/** * The documentation sidebar — PLAN.md §10 "Documentation": five core groups, then one group * per game after them (§16, D60). * * The pages themselves land in phases 7 and 8. This file exists in phase 1 because the * shape of the journey is a design decision that is already made, and because a sidebar * written up front is what stops the docs from being organised by repository. A reader * should never need to know that `link`, `servuo-plugins` and `installer` are three * repositories in order to connect a game server (§10). * * `autogenerate` is deliberately NOT used: the order of "Getting started" is the * installation path from §10, and alphabetical order would scramble it. * * Entries are added as their pages are written — Starlight fails the build on a link to a * page that does not exist, which is the behaviour we want. */ export const docsSidebar = [ { label: 'Getting started', items: [ { label: 'What is Runic Gateway?', slug: 'docs' }, { label: 'Requirements', slug: 'docs/getting-started/requirements' }, { label: 'Install the site', slug: 'docs/getting-started/install-the-site' }, { label: 'First run', slug: 'docs/getting-started/first-run' }, { label: 'Install a game module', slug: 'docs/getting-started/install-a-game-module' }, { label: 'Connect your game server', slug: 'docs/getting-started/connect-a-game-server' }, { label: 'Verify the whole stack', slug: 'docs/getting-started/verify-the-whole-stack' }, ], }, { label: 'Administration', items: [ { label: 'Configuration', slug: 'docs/administration/configuration' }, { label: 'Branding and theming', slug: 'docs/administration/branding-and-theming' }, { label: 'Navigation and pages', slug: 'docs/administration/navigation-and-pages' }, { label: 'Content', slug: 'docs/administration/content' }, { label: 'Users and roles', slug: 'docs/administration/users-and-roles' }, { label: 'Authentication', slug: 'docs/administration/authentication' }, { label: 'Teams', slug: 'docs/administration/teams' }, { label: 'Scheduled events', slug: 'docs/administration/events' }, { label: 'Moderation', slug: 'docs/administration/moderation' }, { label: 'Notifications and email', slug: 'docs/administration/notifications-and-email' }, { label: 'Engagement rules', slug: 'docs/administration/engagement-rules' }, { label: 'Message templates', slug: 'docs/administration/message-templates' }, { label: 'Managing modules', slug: 'docs/administration/managing-modules' }, { label: 'Maintenance and upgrades', slug: 'docs/administration/maintenance-and-upgrades' }, { label: 'Troubleshooting', slug: 'docs/administration/troubleshooting' }, ], }, { label: 'Modules', items: [ { label: 'The module system', slug: 'docs/modules/the-module-system' }, { label: 'Installing modules', slug: 'docs/modules/installing-modules' }, { label: 'Module lifecycle', slug: 'docs/modules/module-lifecycle' }, { label: 'The module manifest', slug: 'docs/modules/the-module-manifest' }, { label: 'The module API', slug: 'docs/modules/the-module-api' }, { label: 'Building a module', slug: 'docs/modules/building-a-module' }, { label: 'The Integration Kit', slug: 'docs/modules/the-integration-kit' }, { label: 'Testing and release', slug: 'docs/modules/testing-and-release' }, ], }, { label: 'Architecture', items: [ { label: 'System architecture', slug: 'docs/architecture/system-architecture' }, { label: 'The bridge', slug: 'docs/architecture/the-bridge' }, { label: 'Authentication architecture', slug: 'docs/architecture/authentication-architecture' }, { label: 'Teams architecture', slug: 'docs/architecture/teams-architecture' }, { label: 'Events architecture', slug: 'docs/architecture/events-architecture' }, { label: 'Protocol versions', slug: 'docs/architecture/protocol-versions' }, ], }, { label: 'Reference', items: [ { label: 'Environment variables', slug: 'docs/reference/environment-variables' }, { label: 'Installer CLI', slug: 'docs/reference/installer-cli' }, { label: 'HTTP API', slug: 'docs/reference/http-api' }, { label: 'Canonical documents', slug: 'docs/reference/canonical-documents' }, ], }, // One group per game, after Reference (PLAN.md §16, D60). Everything above this line is // core and is held to core's words by checkTerms.mjs (D63, D64). A group whose label is a // game's `name` in platform.json is that game's group, and may use that game's words — so // the label is load-bearing: renaming it here without renaming the game turns it core. { label: 'Ultima Online', items: [ { label: 'The Ultima Online module', slug: 'docs/ultima-online' }, { label: 'Connect a ServUO shard', slug: 'docs/ultima-online/connect-a-servuo-shard' }, { label: 'Verify the bridge', slug: 'docs/ultima-online/verify-the-bridge' }, { label: 'The shard connection', slug: 'docs/ultima-online/the-shard-connection' }, { label: 'Client files', slug: 'docs/ultima-online/client-files' }, { label: 'Troubleshooting', slug: 'docs/ultima-online/troubleshooting' }, { label: 'Bridge.cfg', slug: 'docs/ultima-online/bridge-cfg' }, { label: 'sidecar.toml', slug: 'docs/ultima-online/sidecar-toml' }, { label: 'Shard event catalog', slug: 'docs/ultima-online/event-catalog' }, ], }, { label: 'Rust', items: [ { label: 'The Rust module', slug: 'docs/rust' }, { label: 'Install the bridge', slug: 'docs/rust/install-the-bridge' }, { label: 'Connect the site', slug: 'docs/rust/connect-the-site' }, { label: 'Verify the bridge', slug: 'docs/rust/verify-the-bridge' }, { label: 'Linking Steam accounts', slug: 'docs/rust/linking-steam-accounts' }, { label: 'Permissions and groups', slug: 'docs/rust/permissions-and-groups' }, { label: 'Mod config from the site', slug: 'docs/rust/mod-config' }, { label: 'Who sees who is online', slug: 'docs/rust/who-sees-who-is-online' }, { label: 'Events, zones and rewards', slug: 'docs/rust/events-zones-and-rewards' }, { label: 'Optional plugins', slug: 'docs/rust/optional-plugins' }, { label: 'The live map', slug: 'docs/rust/the-live-map' }, { label: 'Troubleshooting', slug: 'docs/rust/troubleshooting' }, { label: 'sidecar.toml', slug: 'docs/rust/sidecar-toml' }, { label: 'RunicGateway.json', slug: 'docs/rust/runicgateway-json' }, { label: 'Event kinds', slug: 'docs/rust/event-kinds' }, // §16, D61/D72: RunicNPC is a subgroup of Rust, nested so it reads as part of the game. { label: 'RunicNPC', items: [ { label: 'RunicNPC', slug: 'docs/rust/runicnpc' }, { label: 'Install RunicNPC', slug: 'docs/rust/runicnpc/install' }, { label: 'Commands', slug: 'docs/rust/runicnpc/commands' }, { label: 'NPC profiles', slug: 'docs/rust/runicnpc/npc-profiles' }, { label: 'NPC placements', slug: 'docs/rust/runicnpc/npc-placements' }, { label: 'Cost and caps', slug: 'docs/rust/runicnpc/cost-and-caps' }, { label: 'Profile fields', slug: 'docs/rust/runicnpc/profile-fields' }, { label: 'The API', slug: 'docs/rust/runicnpc/api' }, ], }, ], }, ]; /** * The tree §10 planned, kept as the record of what was intended — every page it names now * exists, as of phase 8. * * It was the phases 7/8 checklist, and a checklist with nothing left on it is no longer * pulling its weight: it is a second copy of the tree above, maintained by hand, and it had * already drifted once (phase 7 added `Content` under D37 and this list was not updated, * which nothing caught because nothing reads it). `checkSidebar.mjs` now asserts the two * agree, which is what makes keeping it safe. * * Not exported into the Starlight config. */ export const plannedSidebar = { 'Getting started': [ 'What is Runic Gateway?', 'Requirements', 'Install the site', 'First run', 'Install a game module', 'Connect your game server', 'Verify the whole stack', ], Administration: [ 'Configuration', 'Branding and theming', 'Navigation and pages', 'Content', 'Users and roles', 'Authentication', 'Teams', 'Scheduled events', 'Moderation', 'Notifications and email', 'Engagement rules', 'Message templates', 'Managing modules', 'Maintenance and upgrades', 'Troubleshooting', ], Modules: [ 'The module system', 'Installing modules', 'Module lifecycle', 'The module manifest', 'The module API', 'Building a module', 'The Integration Kit', 'Testing and release', ], Architecture: [ 'System architecture', 'The bridge', 'Authentication architecture', 'Teams architecture', 'Events architecture', 'Protocol versions', ], Reference: [ 'Environment variables', 'Installer CLI', 'HTTP API', 'Canonical documents', ], 'Ultima Online': [ 'The Ultima Online module', 'Connect a ServUO shard', 'Verify the bridge', 'The shard connection', 'Client files', 'Troubleshooting', 'Bridge.cfg', 'sidecar.toml', 'Shard event catalog', ], // PLAN.md §16, D70 (phase 15). RunicNPC's subgroup lands in phase 16 (D61). Rust: [ 'The Rust module', 'Install the bridge', 'Connect the site', 'Verify the bridge', 'Linking Steam accounts', 'Permissions and groups', 'Mod config from the site', 'Who sees who is online', 'Events, zones and rewards', 'Optional plugins', 'The live map', 'Troubleshooting', 'sidecar.toml', 'RunicGateway.json', 'Event kinds', { RunicNPC: [ 'RunicNPC', 'Install RunicNPC', 'Commands', 'NPC profiles', 'NPC placements', 'Cost and caps', 'Profile fields', 'The API', ], }, ], }; /** * A group's items as flat labels, a nested group's pages written `Group › Page` after the * group's own label. Both trees go through this, so `checkSidebar.mjs` compares nested * groups as strictly as top-level ones, and `checkTerms.mjs` reads every label. * * In `docsSidebar` a nested group is `{ label, items }`; in `plannedSidebar` it is * `{ Label: [pages] }`. */ export function flatLabels(items) { return items.flatMap((item) => { if (typeof item === 'string') return [item]; if (Array.isArray(item.items)) return [item.label, ...flatLabels(item.items).map((l) => `${item.label} › ${l}`)]; if ('label' in item) return [item.label]; return Object.entries(item).flatMap(([label, pages]) => [label, ...flatLabels(pages).map((l) => `${label} › ${l}`)]); }); }