*/ function novasira_help_steps(): array { return [ [ 'title' => __('Connect an agent', 'novasira'), 'detail' => __('One endpoint and one application password. The Connect screen shows both, checks that this site can serve them, and gives you a prompt that proves the connection works before you trust it.', 'novasira'), 'url' => novasira_page_url('novasira-connect'), 'cta' => __('Open Connect', 'novasira'), ], [ 'title' => __('Scan the site', 'novasira'), 'detail' => __('Ask the agent for a full audit, or run it from the dashboard. The scan reads the database rather than the published pages, so the first result usually includes things no crawler had ever reported.', 'novasira'), 'url' => novasira_page_url('novasira'), 'cta' => __('Open the dashboard', 'novasira'), ], [ 'title' => __('Work one area', 'novasira'), 'detail' => __('Pick the worst finding and open the area it belongs to. Each area carries the method, the findings, and a prompt that hands the whole thing to the agent. One area at a time beats one enormous audit nobody acts on.', 'novasira'), 'url' => novasira_page_url('novasira-areas'), 'cta' => __('Open SEO areas', 'novasira'), ], ]; } /** Is the MCP endpoint actually being served? */ function novasira_help_server_up(): bool { if (function_exists('novasira_settings_bool') && !novasira_settings_bool('novasira_mcp_enabled')) { return false; } return novasira_is_connectable(); } /** The MCP endpoint for this site. */ function novasira_help_endpoint(): string { // Preferring the Connect screen's own accessor keeps the two screens from // ever quoting different URLs, but this screen has to work on a request // where that file was not loaded. if (function_exists('novasira_mcp_endpoint')) { return (string) novasira_mcp_endpoint(); } return rest_url('mcp/novasira'); } /** * The block somebody pastes into a support request. * * Deliberately short. The Status screen builds a full diagnostic; this is the * eight lines that answer most questions without anybody having to find that * screen first. Nothing here is a secret: no password, no token, no client id. */ function novasira_help_support_block(): string { $registered = 0; $names = novasira_ability_names(); foreach ($names as $name) { if (novasira_has_ability($name)) { ++$registered; } } $lines = []; $lines[] = 'Novasira ' . (defined('NOVASIRA_VERSION') ? NOVASIRA_VERSION : 'unknown'); $lines[] = 'WordPress ' . get_bloginfo('version') . ' | PHP ' . PHP_VERSION; $lines[] = 'Site: ' . home_url(); $lines[] = 'Endpoint: ' . novasira_help_endpoint(); $lines[] = 'MCP server: ' . (novasira_help_server_up() ? 'up' : 'not answering'); $lines[] = 'Abilities API: ' . (function_exists('wp_register_ability') ? 'present' : 'missing'); $lines[] = 'MCP adapter: ' . (class_exists('WP\\MCP\\Core\\McpAdapter') ? 'present' : 'missing'); $lines[] = 'Scan engine: ' . (function_exists('novasira_scan_all') ? 'loaded' : 'not loaded'); $lines[] = 'Tools registered: ' . $registered . ' of ' . count($names); $lines[] = 'Permalinks: ' . (get_option('permalink_structure') === '' ? 'plain' : 'pretty'); return implode("\n", $lines); } /** A code block with a copy button, in the shell's own markup. */ function novasira_help_echo_code(string $code): void { echo '
'; echo '
' . esc_html($code) . '
'; echo ''; echo '
'; } /** The three steps. */ function novasira_help_echo_steps(): void { novasira_card_open(__('Getting started', 'novasira')); echo '
    '; $n = 0; foreach (novasira_help_steps() as $step) { ++$n; echo '
  1. '; echo '' . esc_html((string) $n) . ''; echo ''; echo '' . esc_html((string) $step['title']) . ''; echo '' . esc_html((string) $step['detail']) . ''; echo '' . esc_html((string) $step['cta']) . ''; echo '
  2. '; } echo '
'; novasira_card_close(); } /** All twenty-six areas, one line each. */ function novasira_help_echo_areas(): void { $areas = novasira_areas(); $groups = novasira_area_groups(); novasira_card_open(sprintf( /* translators: %d: how many SEO areas exist. */ __('The %d areas', 'novasira'), count($areas) )); echo '

' . esc_html__( 'Each area is a discipline with its own method, its own checks and its own prompt. Open one and the method is on the screen next to what this site actually looks like.', 'novasira' ) . '

'; echo '' . '' . '' . '' . '' . ''; foreach ($areas as $slug => $area) { $live = (string) ($area['mode'] ?? '') === 'live'; $group = (string) ($area['group'] ?? ''); echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; } echo '
' . esc_html__('Area', 'novasira') . '' . esc_html__('Group', 'novasira') . '' . esc_html__('What it covers', 'novasira') . '' . esc_html__('Data', 'novasira') . '
' . esc_html((string) ($area['name'] ?? $slug)) . ''; echo '
' . esc_html((string) $slug) . '
' . esc_html((string) ($groups[$group] ?? $group)) . '' . esc_html((string) ($area['summary'] ?? '')) . '' . esc_html($live ? __('This site', 'novasira') : __('Elsewhere', 'novasira')) . '
'; novasira_card_close(); } /** What this plugin will not do, said once and plainly. */ function novasira_help_echo_limits(): void { $advisory = []; foreach (novasira_areas() as $slug => $area) { if ((string) ($area['mode'] ?? '') === 'advisory') { $advisory[(string) $slug] = (string) ($area['name'] ?? $slug); } } novasira_card_open(__('What Novasira does not do', 'novasira')); echo '

' . esc_html__( 'Most support requests this plugin will ever get are about something it deliberately does not have. So, plainly:', 'novasira' ) . '

'; $limits = [ __('It makes no outbound requests.', 'novasira') => __('Nothing in this plugin calls another server. There is no update check of its own, no analytics, no error reporting, and no "anonymous usage data". The build is tested for it: anything that would phone home fails the check before a release is made.', 'novasira'), __('There is no account and no key.', 'novasira') => __('No sign-up, no licence, no trial, nothing to activate and nothing to expire. It is GPL, and it works the same on the day you install it as on any other day.', 'novasira'), __('It does not crawl your site.', 'novasira') => __('It reads the database directly. That is the point — it sees drafts, scheduled posts, private pages and empty alt attributes that a crawler never reaches. It also means it reports what is stored, not what a visitor is served after a theme, a cache and a CDN have had their turn.', 'novasira'), __('It changes almost nothing on its own.', 'novasira') => __('Two of the nine tools can write, both only when asked, and both check that the calling user may edit that specific post first. Everything else reads.', 'novasira'), ]; echo ''; if ($advisory !== []) { echo '

' . esc_html(sprintf( /* translators: %d: how many areas are advisory. */ _n( '%d area has no numbers in this database', '%d areas have no numbers in this database', count($advisory), 'novasira' ), count($advisory) )) . '

'; echo '

' . esc_html__( 'Impressions, positions, who links to you, what a competitor published, keyword volumes, a Maps profile — none of that is in WordPress, and Novasira ships no API key and no crawler to go and get it. These areas carry the method and the prompt instead, and say so on their own screens. The agent brings the numbers; a panel that invented them would be worse than one that admits it cannot.', 'novasira' ) . '

'; echo '
'; } novasira_card_close(); } /** Support, the source, and the block to paste into a request. */ function novasira_help_echo_support(): void { $up = novasira_help_server_up(); novasira_card_open(__('Getting help', 'novasira')); echo '

' . esc_html__( 'Novasira is open source. Bugs, questions and feature requests all go to the support forum, where they are public and searchable — which means the next person with your problem finds the answer instead of asking again.', 'novasira' ) . '

'; echo '

' . '' . esc_html__('Report a problem or ask a question', 'novasira') . '' . '  ·  ' . '' . esc_html__('Read the source', 'novasira') . '' . '

'; echo '

' . esc_html__('Paste this with your report', 'novasira') . '

'; echo '

' . esc_html__( 'Versions, whether the server is answering, and how many tools registered. No password, no token and no client id is in it.', 'novasira' ) . '

'; novasira_help_echo_code(novasira_help_support_block()); echo '

' . sprintf( /* translators: %s: a link to the Status screen. */ esc_html__('%s runs the full set of checks and explains each failure, if the ten lines above are not enough.', 'novasira'), '' . esc_html__('Status', 'novasira') . '' ) . '

'; if (!$up) { echo '

' . esc_html__( 'The MCP server is not answering on this request, so an agent cannot reach this site. Status will say which of the three reasons it is.', 'novasira' ) . '

'; } novasira_card_close(); } /** * The Docs & support screen. */ function novasira_render_help_page(): void { novasira_header( __('Docs & support', 'novasira'), __('How to start, where the areas are, and what to paste into a support request.', 'novasira') ); // Lower than the other screens on purpose: the paste block names versions and // the site URL, neither of which is a secret, and somebody who can see a // Novasira menu item should be able to read its documentation. if (!current_user_can(novasira_read_cap())) { echo '

' . esc_html__('You do not have permission to view Novasira.', 'novasira') . '

'; novasira_footer(); return; } novasira_help_echo_steps(); novasira_help_echo_areas(); novasira_help_echo_support(); novasira_help_echo_limits(); novasira_footer(); }