| @@ -348,8 +348,27 @@ | ||
| 348 | 348 | 'betterdocs_access_control_repeater' => array(), |
| 349 | 349 | 'internal_knowledge_base_type' => 'basic', |
| 350 | 350 | 'betterdocs_access_control_repeater_kb' => array(), |
| 351 | 351 | 'enable_git_integration' => false, |
| 352 | + 'enable_ai_actions' => true, | |
| 353 | + 'enable_markdown_endpoint' => true, | |
| 354 | + 'ai_actions_button_label' => __( 'Copy page', 'betterdocs' ), | |
| 355 | + 'ai_actions_prompt_template' => __( 'Read from {URL} so I can ask questions about it.', 'betterdocs' ), | |
| 356 | + 'ai_actions_copy_page' => true, | |
| 357 | + 'ai_actions_view_markdown' => true, | |
| 358 | + 'ai_actions_chatgpt' => true, | |
| 359 | + 'ai_actions_claude' => true, | |
| 360 | + 'ai_actions_gemini' => true, | |
| 361 | + 'ai_actions_perplexity' => false, | |
| 362 | + 'ai_actions_grok' => false, | |
| 363 | + 'enable_listen' => true, | |
| 364 | + 'listen_button_label' => __( 'Listen', 'betterdocs' ), | |
| 365 | + 'listen_show_speed' => true, | |
| 366 | + // Words a synthetic voice gets through in a minute at rate 1. | |
| 367 | + // Deliberately lower than the 200 the reading-time pill assumes — that | |
| 368 | + // number describes silent reading. Only the player's duration estimate | |
| 369 | + // uses this; see Core\Listen::WORDS_PER_MINUTE. | |
| 370 | + 'listen_words_per_minute' => 180, | |
| 352 | 371 | /** |
| 353 | 372 | * MCP master switch. Off by default; the toggle lives on the |
| 354 | 373 | * BetterDocs → MCP page, not in the settings tree, and writes |
| 355 | 374 | * through POST betterdocs/v1/settings. |
| @@ -1796,8 +1815,202 @@ | ||
| 1796 | 1815 | 'priority' => 1, |
| 1797 | 1816 | 'label' => __( 'Show Related Docs', 'betterdocs' ), |
| 1798 | 1817 | 'enable_disable_text_active' => true, |
| 1799 | 1818 | 'default' => false |
| 1819 | + ) | |
| 1820 | + ) ) | |
| 1821 | + ), | |
| 1822 | + 'layout_single_doc_ai_actions' => array( | |
| 1823 | + 'id' => 'layout_single_doc_ai_actions', | |
| 1824 | + 'name' => 'layout_single_doc_ai_actions', | |
| 1825 | + 'type' => 'section', | |
| 1826 | + 'label' => __( 'AI Actions', 'betterdocs' ), | |
| 1827 | + 'priority' => 8, | |
| 1828 | + 'fields' => apply_filters( 'betterdocs_single_doc_ai_actions', array( | |
| 1829 | + 'enable_ai_actions' => array( | |
| 1830 | + 'name' => 'enable_ai_actions', | |
| 1831 | + 'type' => 'toggle', | |
| 1832 | + 'priority' => 1, | |
| 1833 | + 'label' => __( 'Enable AI Actions', 'betterdocs' ), | |
| 1834 | + 'label_subtitle' => __( 'Show a "Copy page" button beside the doc title so readers can copy the page as Markdown or open it in an AI assistant.', 'betterdocs' ), | |
| 1835 | + 'enable_disable_text_active' => true, | |
| 1836 | + 'default' => true | |
| 1837 | + ), | |
| 1838 | + 'enable_markdown_endpoint' => array( | |
| 1839 | + 'name' => 'enable_markdown_endpoint', | |
| 1840 | + 'type' => 'toggle', | |
| 1841 | + 'priority' => 2, | |
| 1842 | + 'label' => __( 'Serve Docs as Markdown', 'betterdocs' ), | |
| 1843 | + // Plain text, not `<doc-url>.md`: the subtitle is rendered as HTML, so an | |
| 1844 | + // angle-bracketed placeholder is swallowed as an unknown tag and the | |
| 1845 | + // sentence read "Publish every doc at .md". | |
| 1846 | + 'label_subtitle' => __( 'Publish every doc as Markdown at its address plus .md (for example /docs/getting-started.md), so AI assistants can read a clean copy. The Listen player uses it too. Required by "Copy page", "View as Markdown", and the Claude and Google AI Studio actions.', 'betterdocs' ), | |
| 1847 | + 'enable_disable_text_active' => true, | |
| 1848 | + 'default' => true | |
| 1849 | + ), | |
| 1850 | + // AI Actions and the Markdown endpoint are deliberately independent: the | |
| 1851 | + // `.md` copies also serve AI assistants directly and the Listen player. | |
| 1852 | + // So switching AI Actions off leaves Markdown on — say so where the | |
| 1853 | + // admin is looking, rather than leaving a lone "Enabled" toggle under a | |
| 1854 | + // feature that reads as switched off. | |
| 1855 | + 'ai_actions_markdown_notice' => array( | |
| 1856 | + 'name' => 'ai_actions_markdown_notice', | |
| 1857 | + 'type' => 'html', | |
| 1858 | + 'priority' => 3, | |
| 1859 | + 'html' => sprintf( | |
| 1860 | + '<div class="betterdocs-ai-actions-markdown-notice" style="margin:8px 0 0;padding:12px 16px;background:#f0f6ff;border-left:4px solid #5a6bff;border-radius:4px;font-size:13px;color:#202223;line-height:1.5;">%s</div>', | |
| 1861 | + esc_html__( 'AI Actions is off, but your docs are still published as Markdown (.md) for AI assistants and the Listen player. Turn off "Serve Docs as Markdown" above if you don\'t want that.', 'betterdocs' ) | |
| 1862 | + ), | |
| 1863 | + 'rules' => Rules::logicalRule( array( | |
| 1864 | + Rules::is( 'enable_ai_actions', false ), | |
| 1865 | + Rules::is( 'enable_markdown_endpoint', true ) | |
| 1866 | + ), 'and' ) | |
| 1867 | + ), | |
| 1868 | + 'ai_actions_button_label' => array( | |
| 1869 | + 'name' => 'ai_actions_button_label', | |
| 1870 | + 'type' => 'text', | |
| 1871 | + 'priority' => 4, | |
| 1872 | + 'label' => __( 'Button Label', 'betterdocs' ), | |
| 1873 | + 'default' => __( 'Copy page', 'betterdocs' ), | |
| 1874 | + 'rules' => Rules::is( 'enable_ai_actions', true ) | |
| 1875 | + ), | |
| 1876 | + 'ai_actions_prompt_template' => array( | |
| 1877 | + 'name' => 'ai_actions_prompt_template', | |
| 1878 | + 'type' => 'text', | |
| 1879 | + 'priority' => 5, | |
| 1880 | + 'label' => __( 'AI Prompt Template', 'betterdocs' ), | |
| 1881 | + 'label_subtitle' => __( 'Sent to the AI assistant when a reader opens this doc there. Use {URL} for the doc address.', 'betterdocs' ), | |
| 1882 | + 'default' => __( 'Read from {URL} so I can ask questions about it.', 'betterdocs' ), | |
| 1883 | + 'rules' => Rules::is( 'enable_ai_actions', true ) | |
| 1884 | + ), | |
| 1885 | + 'ai_actions_copy_page' => array( | |
| 1886 | + 'name' => 'ai_actions_copy_page', | |
| 1887 | + 'type' => 'toggle', | |
| 1888 | + 'priority' => 6, | |
| 1889 | + 'label' => __( 'Copy Page', 'betterdocs' ), | |
| 1890 | + 'enable_disable_text_active' => true, | |
| 1891 | + 'default' => true, | |
| 1892 | + 'rules' => Rules::logicalRule( array( | |
| 1893 | + Rules::is( 'enable_ai_actions', true ), | |
| 1894 | + Rules::is( 'enable_markdown_endpoint', true ) | |
| 1895 | + ), 'and' ) | |
| 1896 | + ), | |
| 1897 | + 'ai_actions_view_markdown' => array( | |
| 1898 | + 'name' => 'ai_actions_view_markdown', | |
| 1899 | + 'type' => 'toggle', | |
| 1900 | + 'priority' => 7, | |
| 1901 | + 'label' => __( 'View as Markdown', 'betterdocs' ), | |
| 1902 | + 'enable_disable_text_active' => true, | |
| 1903 | + 'default' => true, | |
| 1904 | + 'rules' => Rules::logicalRule( array( | |
| 1905 | + Rules::is( 'enable_ai_actions', true ), | |
| 1906 | + Rules::is( 'enable_markdown_endpoint', true ) | |
| 1907 | + ), 'and' ) | |
| 1908 | + ), | |
| 1909 | + 'ai_actions_chatgpt' => array( | |
| 1910 | + 'name' => 'ai_actions_chatgpt', | |
| 1911 | + 'type' => 'toggle', | |
| 1912 | + 'priority' => 8, | |
| 1913 | + 'label' => __( 'Open in ChatGPT', 'betterdocs' ), | |
| 1914 | + 'enable_disable_text_active' => true, | |
| 1915 | + 'default' => true, | |
| 1916 | + 'rules' => Rules::is( 'enable_ai_actions', true ) | |
| 1917 | + ), | |
| 1918 | + 'ai_actions_claude' => array( | |
| 1919 | + 'name' => 'ai_actions_claude', | |
| 1920 | + 'type' => 'toggle', | |
| 1921 | + 'priority' => 9, | |
| 1922 | + 'label' => __( 'Open in Claude', 'betterdocs' ), | |
| 1923 | + 'enable_disable_text_active' => true, | |
| 1924 | + 'default' => true, | |
| 1925 | + 'rules' => Rules::logicalRule( array( | |
| 1926 | + Rules::is( 'enable_ai_actions', true ), | |
| 1927 | + Rules::is( 'enable_markdown_endpoint', true ) | |
| 1928 | + ), 'and' ) | |
| 1929 | + ), | |
| 1930 | + 'ai_actions_gemini' => array( | |
| 1931 | + 'name' => 'ai_actions_gemini', | |
| 1932 | + 'type' => 'toggle', | |
| 1933 | + 'priority' => 10, | |
| 1934 | + 'label' => __( 'Open in Google AI Studio', 'betterdocs' ), | |
| 1935 | + 'label_subtitle' => __( 'Gemini has no way to receive a prompt from a link, so this opens Google AI Studio instead. A Google account is required.', 'betterdocs' ), | |
| 1936 | + 'enable_disable_text_active' => true, | |
| 1937 | + 'default' => true, | |
| 1938 | + 'rules' => Rules::logicalRule( array( | |
| 1939 | + Rules::is( 'enable_ai_actions', true ), | |
| 1940 | + Rules::is( 'enable_markdown_endpoint', true ) | |
| 1941 | + ), 'and' ) | |
| 1942 | + ), | |
| 1943 | + 'ai_actions_perplexity' => array( | |
| 1944 | + 'name' => 'ai_actions_perplexity', | |
| 1945 | + 'type' => 'toggle', | |
| 1946 | + 'priority' => 11, | |
| 1947 | + 'label' => __( 'Open in Perplexity', 'betterdocs' ), | |
| 1948 | + 'enable_disable_text_active' => true, | |
| 1949 | + 'default' => false, | |
| 1950 | + 'rules' => Rules::logicalRule( array( | |
| 1951 | + Rules::is( 'enable_ai_actions', true ), | |
| 1952 | + Rules::is( 'enable_markdown_endpoint', true ) | |
| 1953 | + ), 'and' ) | |
| 1954 | + ), | |
| 1955 | + 'ai_actions_grok' => array( | |
| 1956 | + 'name' => 'ai_actions_grok', | |
| 1957 | + 'type' => 'toggle', | |
| 1958 | + 'priority' => 12, | |
| 1959 | + 'label' => __( 'Open in Grok', 'betterdocs' ), | |
| 1960 | + 'enable_disable_text_active' => true, | |
| 1961 | + 'default' => false, | |
| 1962 | + 'rules' => Rules::logicalRule( array( | |
| 1963 | + Rules::is( 'enable_ai_actions', true ), | |
| 1964 | + Rules::is( 'enable_markdown_endpoint', true ) | |
| 1965 | + ), 'and' ) | |
| 1966 | + ) | |
| 1967 | + ) ) | |
| 1968 | + ), | |
| 1969 | + 'layout_single_doc_listen' => array( | |
| 1970 | + 'id' => 'layout_single_doc_listen', | |
| 1971 | + 'name' => 'layout_single_doc_listen', | |
| 1972 | + 'type' => 'section', | |
| 1973 | + 'label' => __( 'Listen', 'betterdocs' ), | |
| 1974 | + 'priority' => 9, | |
| 1975 | + 'fields' => apply_filters( 'betterdocs_single_doc_listen', array( | |
| 1976 | + 'enable_listen' => array( | |
| 1977 | + 'name' => 'enable_listen', | |
| 1978 | + 'type' => 'toggle', | |
| 1979 | + 'priority' => 1, | |
| 1980 | + 'label' => __( 'Enable Listen', 'betterdocs' ), | |
| 1981 | + 'label_subtitle' => __( 'Show a "Listen" button beside the reading time. Pressed, it turns into a small audio player that reads the doc aloud using the visitor\'s browser — no audio files and no third-party service.', 'betterdocs' ), | |
| 1982 | + 'enable_disable_text_active' => true, | |
| 1983 | + 'default' => true | |
| 1984 | + ), | |
| 1985 | + 'listen_button_label' => array( | |
| 1986 | + 'name' => 'listen_button_label', | |
| 1987 | + 'type' => 'text', | |
| 1988 | + 'priority' => 2, | |
| 1989 | + 'label' => __( 'Button Label', 'betterdocs' ), | |
| 1990 | + 'default' => __( 'Listen', 'betterdocs' ), | |
| 1991 | + 'rules' => Rules::is( 'enable_listen', true ) | |
| 1992 | + ), | |
| 1993 | + 'listen_show_speed' => array( | |
| 1994 | + 'name' => 'listen_show_speed', | |
| 1995 | + 'type' => 'toggle', | |
| 1996 | + 'priority' => 3, | |
| 1997 | + 'label' => __( 'Speed Control', 'betterdocs' ), | |
| 1998 | + 'label_subtitle' => __( 'Let readers cycle the playback speed between 0.75× and 2×.', 'betterdocs' ), | |
| 1999 | + 'enable_disable_text_active' => true, | |
| 2000 | + 'default' => true, | |
| 2001 | + 'rules' => Rules::is( 'enable_listen', true ) | |
| 2002 | + ), | |
| 2003 | + 'listen_words_per_minute' => array( | |
| 2004 | + 'name' => 'listen_words_per_minute', | |
| 2005 | + 'type' => 'number', | |
| 2006 | + 'priority' => 4, | |
| 2007 | + 'label' => __( 'Words Per Minute', 'betterdocs' ), | |
| 2008 | + 'label_subtitle' => __( 'Used to estimate the total playing time the player shows. Browser voices read at roughly 180 words a minute at normal speed; raise this if the countdown finishes early.', 'betterdocs' ), | |
| 2009 | + 'default' => 180, | |
| 2010 | + 'min' => 60, | |
| 2011 | + 'max' => 400, | |
| 2012 | + 'rules' => Rules::is( 'enable_listen', true ) | |
| 1800 | 2013 | ) |
| 1801 | 2014 | ) ) |
| 1802 | 2015 | ) |
| 1803 | 2016 | ) |