feat(nav): rebuild the documentation navigation around the new information architecture and new home agent pages - #2333
Conversation
The sidebar tree has lived in a 1313-line hand-written nav.ts plus twelve secondary menu files selected by a menu_namespace frontmatter field that falls back silently, so a typo or an unregistered namespace renders the wrong tree with no warning. This adds the data model that replaces it. A tree is one JSON file under src/nav/trees. Rows reference pages by namespace rather than by URL, so the resolver reads each page's live permalink from the content collection and the sidebar renders correctly both before and after the URL migration. Groups and folds may contribute a URL segment, which is how a row's position and its address stay the same statement. Segments and labels are translatable, so the Portuguese tree keeps Portuguese URLs. src/nav/resolve.ts is pure and takes the page facts as input, so the build, the scripts and the linter all share one implementation of the tree walk, the active-row match and the target-permalink derivation. lint:navcheck is the gate the previous model never had: it enforces the schema, unique row ids, a maximum depth, that every page ends in exactly one row, that no two pages claim the same future URL, and that computed permalinks pass the format test:frontmatter already applies. Nothing renders from this model yet; LeftSidebar still reads the legacy menus. The trees, the discard list and the URL map are a proposal for review, not the final placement.
A product section now ships the rows the style guide's information architecture marks required, even where the page behind one is still unwritten: Overview, Quickstart, Glossary, and a Management group holding the Pricing and Changelog links. Pricing is listed, never owned, because that page lives in Fundamentals and the architecture says pricing is a link and never a page. An unwritten row points at one shared page in both languages rather than rendering as a dead or disabled entry, so the reader always lands somewhere that explains itself. A resource group with no landing page of its own, Platform being the one today, resolves there too.
… model The rail, the mobile drawer, the breadcrumb and the previous/next links now all resolve from the tree that owns the current page instead of from the legacy menu files and a URL-segment scan. Rows still point at each page's live permalink, so this lands before any URL moves and can be read on the dev server as it will behave after them. A product tree opens with a row back to the level above it and a section titled with the product name, which is what makes the reader's position legible without a second navigation surface. The breadcrumb is the tree title, the folds above the page, then the page, so it now says where a page sits rather than what its URL spells. Pagination follows the tree's reading order and steps over the rows that are placeholders or are listed here but owned elsewhere. A page the model does not own falls back to the root catalog rather than to an arbitrary tree, and the rows that share the coming-soon page never claim the active state.
The bar carried four dead anchors copied from a sample. It now renders the directory the navigation proposal calls for: a Products panel of four category columns, each product with the one-line description the site already uses and the modules listed under the resource that owns them; a Guides link to the hub; and a Developer tools panel, which is the only place those nine surfaces appear because they are transversal rather than a position in the tree. The same directory leads the mobile drawer, where the bar is hidden, so neither viewport loses half the navigation. The drawer no longer needs the three duplicated rows that every legacy menu carried to fake it. Sign in joins Console in the trailing cluster, and both panels take their labels from the UI dictionary, so the Portuguese bar reads in Portuguese.
The page was a hand-maintained list of 300 markdown links with the same guide repeated under three headings. It now renders from the guides tree, so the hub and the sidebar cannot disagree, and every guide appears once. The tree groups guides by objective across nineteen sub-areas, which is the right shape for someone who knows what they want to achieve and the wrong one for someone who knows only which product they are using. The product filter answers the second reader: it is deep-linkable, so each product section links straight to its own slice instead of repeating the guides inside the product tree, which is what the information architecture asks for. A product with no guide tagged yet gets no row at all rather than a row leading to an empty list. A short curated block leads the page. There is no usage data in the repository, so those eight are a judgement, not a measurement.
A page's address now states where it sits: /documentation/build/cache/,
/documentation/platform/applications/, /documentation/secure/waf/,
/documentation/fundamentals/, /documentation/guides/<area>/<sub-area>/,
/documentation/devtools/<tool>/. The old /documentation/products/
prefix said nothing about position and grouped guides, products,
templates and platform pages under one word.
1167 page/language permalinks move and 9275 links across the corpus are
retargeted to follow them. The retarget is a single pass with a boundary
guard, because replacing paths one after another rewrites the head of a
longer path and corrupts it. A page's own permalink is protected from
that pass by line span rather than by name, since a folded scalar puts
the value on the next line.
85 pages per language leave the navigation: pillar landing pages the
root catalog replaces, journey pages a product tree now carries,
solution pages that belong on the site, template showcase pages whose
guide survives, and legacy product-name twins. Their files and content
stay; only their place in the tree goes.
Dated legal versions and the style guide keep their URLs. Both are
reachable from the tree and neither gains anything from moving.
The de-para is in redirects/: url-map.md to read, and url-map.en.json
and url-map.pt-br.json in the {from, moved} shape for whoever wires the
301s, which this repository does not do. Every page's menu_namespace is
gone; the tree no longer needs the page to name it.
Thirty-three files go: the 1313-line nav.ts and its Portuguese twin, the twelve secondary menus per language, the availableMenu registry, the resolver that mapped a namespace to one of them, the webkit adapter, and the breadcrumb and pagination helpers that scanned URL segments. Two of those menus had no page selecting them at all, and three namespaces the content referenced were never registered, so those pages silently rendered the default tree. That failure mode is gone with the registry. The frontmatter field is gone too. A page no longer names its sidebar; the tree lists the page, which is what makes one page belong to exactly one place and lets the checker prove it. The style guide said otherwise in both languages, so those two passages now describe the tree. Also removed: the per-language nav stub in the add-language script, the forty-four menu.* dictionary keys that titled groups in the deleted menus, four left-sidebar keys left from a tab UI that no longer exists, and two header exports nothing read. astro check drops from 106 errors to 54, all of the difference coming from the deleted files.
navcheck is the guard the previous model never had, and it fails in seconds where the build takes minutes. Running it first means a tree that lists a page twice, points at a page that does not exist, or would land two pages on the same URL stops the pipeline before anything is compiled.
Both were groups with no page of their own, so their row in the root catalog resolved to the shared coming-soon page. That page belongs to no tree, so it renders the root catalog: clicking Platform left the reader exactly where they started and its own navigation never opened, which is the one thing that row exists to do. Each now has a page at its own address, carrying the coming-soon message in both languages. Because the page belongs to the tree, arriving there switches the sidebar to it, and the resources below it get a back row that returns to a real page instead of to a dead end. url-map.ts also warns when the baseline it was handed differs from nothing, which is what happens when it is run against HEAD after the migration has already been committed.
webkit's MenuSubTrigger hides the icon on an inline trigger by design: the row that expands children beneath it leaves the glyph column to them. The docs sidebar groups products under dropdown rows and puts the glyph on the group rather than on the products, so the trigger now renders whatever icon it is handed. The children still carry none. Second hunk in the existing webkit patch, applied the same way as the DocProse teleport fix; a webkit upgrade that touches this file fails loudly rather than silently dropping the behaviour.
Each pillar label now heads dropdown rows that group its products by what they do, in Azion's own words rather than a borrowed taxonomy: Build has Execution and Delivery, Store has Data, Secure has Web protection and Network protection with Edge DNS flat beside them, Observe has Events and logs with Edge Pulse flat. Platform becomes a label over a single Resources dropdown holding the nine resources, and the footer becomes a Manage label over Help and services and Trust. A product inside a dropdown still opens its own tree, and every tree returns to the catalog; the Platform level and its landing pages go, since a dropdown replaces the level. Icons follow one rule: the direct children of a pillar label carry one, nothing else does. That puts a glyph on each dropdown trigger and on the two products that stand flat, and none on Fundamentals, Agent Setup or any row inside a dropdown. Every glyph was checked against the icon fonts for a real codepoint.
Both overlays teleport to <body> without a guard, and an SSR host that does not re-emit Vue's teleport payload leaves the client hydrating the wrong node: the language selector threw on open (insertBefore on a null container) and the sidebar's collapse tooltip logged a hydration mismatch on every page. The DocProse hunk already fixes the same failure the same way; this extends it to the two remaining overlays.
…ration The Products panel stretched to the full viewport because its content was full-width over fluid columns; it now sizes to four fixed columns with a 24px gap, and Developer tools takes the same shape in three labeled columns (Automate, Code, Integrate) instead of one list left in a third of an empty grid. Modules leave the Products panel; a resource's own tree carries them. The portal mounts only after hydration. Its teleport target is null on the server and present at once on the client, so the two renders disagreed and Vue reported a hydration mismatch on every page. The footer comment blaming nested islands for dead controls now states the real causes: a stale optimizer cache in the dev server, and the teleports the webkit patch gates on mount.
A tree page used to carry its way back and its title as rows inside the menu. Both move into the rail's header region: a back arrow to the catalog and the tree title linking to its Overview, with the rows starting at Overview beneath them. The mobile drawer shows the same row above its tree. Below the title sits a filter field, seated in webkit's Sidebar header slot the way its own reference does, with a keycap badge for the `/` shortcut that focuses it from anywhere that is not already a field. Typing prunes the tree to the rows whose label matches and the rows above them, opening every fold that still holds a match; a fold whose own name matches keeps all its children. Clearing the field restores the rows and the folds the reader had open, and the stored fold state is left alone while a filter is active. The header carries no horizontal inset of its own: the Sidebar already pads that region, and adding more put the arrow and the field twelve pixels to the right of the rows below them.
The drawer now opens on the current tree: back arrow and tree title, the filter field with its / shortcut, then the rows. The Products, Guides and Developer tools directory follows below the tree.
Build, Store, Secure and Observe carry the eighteen products the site header lists, in its order. Applications and Firewall leave the panel; they are reached from the sidebar's Resources dropdown.
Search, content type and topic checkboxes with counts in a sticky rail, cards with kind and topic overlines, 24 per page. Filters and page live in the address (?q=, ?kind=, ?product=, ?page=). Rows and folds carry a kind (learning path, tutorial, reference architecture) in place of the retired featured flag; videos from the Azion channel come from src/nav/videos.json and appear in the catalog only.
Names what the group holds: agreements and policies, changelog and status. The Portuguese label becomes Políticas e atualizações.
Each product tree's Guides and tutorials row opens a page in that tree listing the product's guides as a table: name, type and last updated, sorted by name. Opening a guide lands in the Guides tree. Twenty-one products in English and Portuguese; the row no longer deep-links into the global catalog. Last updated comes from one git log pass over the content sources at build time, falling back to the file's modification time.
Hero with Get started and a copy-prompt button carrying the agent marks, then Start by interface, Start by objective, Ready-made templates, Stop attacks, Assess risk and prove compliance and Follow along, as card groups. English and Portuguese, every link resolved to a page that exists in both. A page_header frontmatter switch lets a page open with its own hero instead of the title block. HeroHome aligns to the start, takes a helper line and the prompt, and no longer adds a bottom margin of its own.
An overview with a filterable agent picker, a comparison table and the Workflow, Key concepts and Common tradeoffs cards, plus one page per agent: Claude Code, Cursor, GitHub Copilot, Windsurf, Codex, Gemini CLI and OpenCode. Each carries a quick start, platform access with the nine MCP tools, agent-friendly docs, example prompts, tips, FAQ, troubleshooting and the other agents. English and Portuguese. The copy lives in one data file; thirteen components render it. Agent Setup replaces the coming-soon row in the root sidebar with its own tree, and the home now points at it. AgentMark draws all seven marks in brand colour, mono for the copy button. DocPageHeader takes title and details slots. The webkit patch gains a guard for TabView's requestAnimationFrame call during server rendering.
…me CTA - EN and PT pages built from the sample: three paths (Console template, GitHub import, Azion CLI) with numbered steps, test fences, an AI Assistant prompt, next steps and related products - the home Get started button, the Build your application card and the Also useful lines point at the page; it is not listed in the sidebar and is exempt from the navcheck orphan rule - webkit patch: DocStep takes its number from a CSS counter when it is rendered without a DocSteps provider, as MDX does
- the meta row opens with the page's last git change and closes with an Agent setup link, in the page's language; the link is withheld on the pages the Agent Setup tree lists - commits that swept more than 100 content files (the URL migration, the component refactors) no longer count as a page's last change unless nothing else ever touched it; the guides catalogs share the fix - webkit patch: the masthead takes a label and a locale for the date, so Portuguese pages read it in Portuguese
The intermediate folds (Resources, Execution, Delivery, Data, Web protection, Network protection, Events and logs, Help and services, Policies and updates) are gone; their rows sit directly under the category labels. The two icons that only made sense beside fold rows go with them.
Support, Professional Services, Marketplace and Agreements are not products, so they drop the product skeleton: no Quickstart or Reference slots. Marketplace lists its usage pages in order and gathers the seller pages under their own group; Agreements orders the current documents by weight and keeps every superseded version under Previous versions, one fold per document, newest first. No URL changes.
Twelve cards on both homes went to product overviews or reference pages; they now open the practical page for the task: cache policies, protect a domain, the AI Inference starter kit, access the Console, firewall rules, a WAF rule set, tune WAF, install Bot Manager Lite, IP blocklists, create a certificate, integrate SIEMs, and Activity History. Firewall, WAF and Certificate Manager quickstarts do not exist yet, so those cards take the closest how-to.
Sign in never hid below md: the webkit Button sets its own display class on the same element and wins the cascade over `hidden`. With the extra width the right cluster ran out of room and was laid over the wordmark. The GitHub link and Sign in now sit in breakpoint wrappers (hidden below sm and md, `contents` above), so a 320px bar holds menu, wordmark, search and Console with nothing overlapping.
Every file the navigation rework added or changed loses the comment blocks written along the way (235 blocks, 960 lines across 54 files); comments that predate the branch and lint or compiler directives stay. Six catch blocks that held only a comment keep a one-line note, since an empty catch fails the linter.
The theme defines text-primary and five sibling utilities in its own Tailwind entry, which in dev never scans webkit's sources, so classes used only inside webkit components (the navigation panel's column labels among them) had no rule locally while the production bundle emitted one. The six utilities join the shims main.css already keeps for the same reason.
Nav resolution reads the cached index once per page instead of rebuilding
it four times, and BaseLayout hands the resolved sidebar to LeftSidebar.
The dead `category` and `unlisted` fields leave the tree schema and the 44
tree files; nav and video hrefs must be http(s); gray-matter refuses `js`
frontmatter; exempt.json goes through a schema; url-map reuses the loaded
corpus and the shared tree walk; the retarget script drops its list of
menus this branch deleted.
The guides hub server-renders its first page and fetches the catalog from
a static `/{lang}/guides-catalog.json` after mount, so the island carries
13 KB of props instead of 125 KB.
main.css references the theme instead of keeping 24 hand-copied utility
shims. A plain `@import` of the theme nests its own tailwind import and
cssnano then drops the whole utilities layer, so `@reference` it is, with
the layout still loading the theme's own stylesheet.
The webkit patch gains one `useMounted` composable that doc-prose,
dropdown, tooltip and DocsTopNav share; the two mis-indented hunks are
gone and the lockfile hash follows the regenerated patch.
The agent pages take their hrefs from the navigation through Astro
wrappers, and agent-setup.json is parsed against a zod schema at build
time in a server-only module, so the islands lose their casts without
shipping zod. Inline markdown links only render for same-site, anchor or
http(s) hrefs. The sidebar rail and drawer share one filter component with
the `/` hotkey; the top nav renders its panels from one loop; page-header
strings live in the UI dictionaries with no Spanish table.
|
Agent Setup pages drop the last-updated date and cross-link In Contrast with
|
isaque-bock-azion
left a comment
There was a problem hiding this comment.
Comentários no PR
The agent pages' header never received a date. It now shows the later of the page file's last change and the data file's, since every agent page renders from agent-setup.json. The Agent setup link stays off on these pages by design.
|
What & why
The left sidebar was a 1,300-line hand-written menu plus twelve secondary menus picked by a frontmatter field, with no validation and no working top navigation. This PR replaces it with the information architecture the team validated: a top navigation that is a directory (Products, Guides, Developer tools) and a sidebar that is a tree which switches scope per product, all rendered with
@aziontech/webkit. Every page URL now equals its position in the tree, and every moved address is recorded for the redirect step.What ships, in the order it was built (30 commits on top of
release/new-azion-docs):src/nav/: one JSON tree per product or section, a root catalog, the header directory, a delist list with replacements, and a zod-validated resolver.pnpm lint:navcheckfails the build when a page has no home, a tree nests past three levels, two rows claim one URL, or a reference is unknown; it runs inpr-checks.ymlbefore the build./shortcut as the rail.src/includes. The map of old to new addresses (586 English, 581 Portuguese) lives inurl-map.csv, kept outside the repo by decision; redirects are configured in Console through the Massive Redirect integration, which is a separate step.Related issue: none — the effort is tracked in this PR and the branch name.
Pages affected: every documentation page.
Entry points to review:
/en/documentation/,/en/documentation/build/cache/,/en/documentation/guides/,/en/documentation/build/functions/guides/,/en/documentation/agent-setup/,/en/documentation/first-deploy/,/en/documentation/agreements/, and the Portuguese twins under/pt-br/documentacao/.Type of change
feat) — the hubs, Agent Setup, First deploy and coming-soon pages exist to serve the navigation; no existing page body was rewrittenfix) — typo, broken link, wrong informationdocs) — rewrite, expansion, upkeepi18n)refactor/chore) — reviewed by UXE, no content mixed inAuthor checklist
type(scope): summary(see GOVERNANCE.md §4)title,description,meta_tags,namespace,permalinkon every added page.last_reviewedis not present on any page of the corpus today, so it was not introduced here.curltest and the CLI commands; every agent page carries its install and connect commandspt-brupdated in this PR — every page, label, filter, count and breadcrumb ships in both languagespnpm build:local(build + frontmatter check) without errors — to be ticked by the author after running it; the agent verified on the dev server onlyNotes for reviewers
Why the webkit patch grew
patches/@aziontech__webkit@4.4.0.patchhad one section on the release branch (DocProse). This branch adds seven, all small, all candidates to file upstream. Each exists because a webkit component was either not written for server rendering under Astro or lacked one prop the docs need.dropdown.vue,tooltip.vue392085231<body>at setup. Astro does not re-emit Vue's teleport payload, so the client hydrated the wrong node and the overlays broke after load. The teleport is now enabled only once mounted, which keeps the server and client renders identical.menu-sub-trigger.vue40194f8bbiconit was handed; the sidebar's grouped rows carry glyphs, so the trigger now renders whatever icon it receives.tab-view-panel.vue68710a8c6requestAnimationFrameduring setup, which does not exist on the server, so every page with a TabView failed to render. It now skips the motion wait when the function is absent.doc-step.vue,doc-steps.vue608169fadDocStepnever finds theDocStepsprovider and threw. The step now injects the context with a null fallback and, without a provider, leaves its badge empty for a CSS counter inbase.cssto number.doc-page-header.vuea1e7a6265lastUpdatedLabelandlocale, let Portuguese pages read the date in Portuguese.Two earlier sections for DocFrame were added and then removed within the branch once the screenshot that needed them was dropped, so nothing rides along unused.
Why these Vue components were created or changed
The rule was to render with webkit and add a component only where webkit has no composite for the job or the docs site needs a behaviour of its own (server rendering, two languages, URL state). Modified files:
webkit/DocsSidebar.vue,webkit/DocsSidebarMenu.vue/shortcut, and keep width, collapse and expanded rows across pages.webkit/HeaderRightSidebar.vuewebkit/DocsTopNav.vuetopnav.jsonwith webkit's NavigationMenu; panels are sized to their columns and hydrate correctly.webkit/DocPageHeader.vuewebkit/HeroHome.vueHeader.vuehidden; a 320px bar no longer overlaps the wordmark.Created files:
webkit/GuidesHome.vuewebkit/ProductGuidesTable.vuewebkit/CopyPromptButton.vue,webkit/AgentMark.vueagents/*(11 files)src/data/agent-setup.json: picker, quick start, comparison and tools tables, FAQ, concept cards, docs list, samples, inline text with tooltips, page header. One data file drives 16 pages in two languages through webkit primitives (TabView, Table, Accordion, DocSteps, CodeBlock, DocTooltip).Fifteen other Vue files appear in the diff only because the final commit removed the explanatory comments the branch had introduced; their behaviour is unchanged.
Removed
The legacy menu model (
src/i18n/*/nav.tsand the twelve*Menu.tsfiles,menuToWebkit.ts,getNav.ts, themenu_namespacefield) and the generatedredirects/folder, whose CSV is kept outside the repo.Verification performed
pnpm lint:navcheckpasses;npx astro checkholds at its pre-existing 54 errors; the guides catalog, the agent pages, First deploy, the header at 320, 356, 700 and 900 pixels, and both homes were checked in a browser against the dev server.pnpm build:localand the link check are the author's step.