Repository navigation
feat(portal): a tabs key in the root nav.json - #408
Conversation
The root nav.json's `pages` decides two things at once: which top-level folders are tabs, and the order of Home's sidebar. A folder cannot be ordered inside Home without becoming a tab. The amendment gives the root file a `tabs` list for the tab bar and leaves `pages` ordering Home, as it orders every other folder. The apimatic: tokens can go in either list, and stay refused below the root. A beta.1 root file that relies on the old reading is refused until 2.0.0, and a portal whose only tab is Home hides the tab bar. Section 5 of portal-config.md states the rules and section 16 the delivery; portal-navigation.md and generated-pages.md point to them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The beta.1 refusal no longer counts a folder serving `/`, which beta.1 itself refused, so no beta.1 file can hold one. The navigation context gains the subfolders with a page in them, since `childNames` cannot tell a page from a folder and both new refusals need to. Section 16 now names the doc comments and existing tests that state the old rule, holds every setting the CLI accepts to the template's schema, and says the e2e suite is red between the template and CLI steps. The serve paragraph speaks of sections rather than tabs, and portal-navigation.md counts `tabs` as the third setting now that `root` is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The root nav.json's `pages` carried two decisions: which top-level folders are tabs, and where Home's nodes go. A folder could not be ordered inside Home without becoming a tab. `tabs` now takes the first decision, and `pages` orders Home's sidebar as every other nav.json orders its folder (.ai/plans/portal-config.md, sections 5 and 16). - navigation-schema.ts extends Fumadocs' metaSchema with `tabs`, typed as `pages`, and the docs collection validates nav.json against it, so the key survives. Unknown keys are still stripped. - A folder is a tab when `tabs` lists it. The API reference and each generated section are tabs unless the root `pages` names them, which places them in Home at the address they always had. - The tabs are ordered by `tabs` through `reorder`, with the defaults the root `pages` gave before for what it leaves unnamed. - portalTabs returns no tab when Home is the only one, so the header shows no switcher over a single choice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The CLI holds `tabs` to the rules the template now reads it by (.ai/plans/portal-config.md, sections 5 and 16). - `tabs` is known in the root nav.json and refused below it with a sentence of its own. Its entries are a folder with a page under it, `api` or `apimatic:api`, `apimatic:sdks` and `apimatic:plugin`. A page, `...`, a folder serving `/`, an unknown name, a duplicate and a node `pages` also names are each refused. - The root `pages` may now name a token, `api` or a folder serving `/`, each placed in Home. Below the root a token is still refused, and the sentence names both places it can go. - A root file with no `tabs` whose `pages` names a folder, `api` or a token is refused, since 2.0.0-beta.1 made tabs of those entries and reading it under the new rule would move them into Home's sidebar. The check goes before 2.0.0 ships. - The content tree takes folder tabs from `tabs`, and leaves a section placed in Home out of the tabs and out of the shared-name warning. - The notice naming the folders the file made tabs of goes: `tabs` says so in its own name. makesSameTabsAs becomes makesSameSectionsAs. - The default fixture moves its tabs from `pages` to `tabs`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… tabs - A third e2e build, from the sections-in-home fixture, whose root nav.json places the API reference, the SDKs and the Context Plugin in Home with `pages` and names no tab. It checks the prerendered sidebar holds Home alone with each section where `pages` names it, that each section keeps its address, and that the header renders no tab bar. - The README's bullet on tabs is rewritten for `tabs` and `pages`, the tokens placeable in either, and the refusal of a 2.0.0-beta.1 file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- entryErrors hands each entry to listEntryError, which brings the function under the cognitive-complexity limit. - The e2e builds share portalBuilt, which registers the before and after hooks and reads the output, and tabPositions, which finds each tab's link in the header, instead of repeating both per describe block. - The tab-order defaults in tabs.test.ts are one table-driven loop. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…can act on Found reviewing the tabs change before handing it over. - The scaffold writes `"tabs": []` beside `pages`, so a folder named in a scaffolded file is placed in Home rather than refused. - The refusal of a root file with no `tabs` no longer leans on beta.1 history: it says the file does not say whether the entries are tabs or in Home's sidebar, and how to say either. - A misspelt setting at the root is answered with `tabs` among the settings, which the list left out. - The README states the real default for a section named in neither list: after the folders `tabs` names, before a reference not named ahead of them. - Comments trimmed to one line each; a dead guard in isTabFolder and a membership scan in the content walk are gone. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
sohail2721
left a comment
There was a problem hiding this comment.
Three findings from a review of this branch, each reproduced against 20543bb with both transformers applied. The first two look like bugs, the third is a usability gap in the error text.
The unnamed sections' tab position reused the sidebar's anchor, which sits after the last named user content. A tabs list naming only the reference and tokens has none, so the anchor fell to the front and the unnamed sections landed ahead of a reference the file deliberately put first: tabs ["apimatic:api"] gave Home, SDKs, Context Plugin, API Reference. In the tab bar every folder is named, so the unnamed band now follows everything `tabs` names, a named reference included, with an unnamed reference still last. The sidebar keeps its own anchor. The README states the rule as one sentence. Found in review (PR #408, first comment). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
With a page at `content/api.md`, naming `api` in `pages` is refused, since the entry positions the mounted reference and the page could never be placed. The same name in `tabs` was accepted before the shared-name test ran, letting through the quietly wrong sidebar the validator exists to refuse. The tab entry now gives the refusal `pages` gives, from one shared sentence. Found in review (PR #408, second comment). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The not-found sentence for a tabs entry borrowed the pages hint, which could point at an entry tabs then refuses: a capitalised page name was answered with "Did you mean 'authentication'?", and following it earned "'authentication' is a page" one run later. A near miss of `api` named only the token, although the folder name is accepted too. The tabs path now has a hint of its own: a folder near miss stays a suggestion, a section is offered as its token, `api` is offered in both spellings, and a page's name is answered in one message with where a page can go. Found in review (PR #408, third comment). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
sohail2721
left a comment
There was a problem hiding this comment.
Second pass on 0f9900a. The three earlier findings are fixed and their tests pin the cases; the full unit suite passes on this head after a build. Three further findings below: one worth fixing before merge (the README line endings), two minor.
An edit on this branch rewrote every line ending from CRLF to LF, so the whole file diffed and hid the 24 lines that actually changed, and any other branch touching the README would conflict on every line. The endings are back as dev stores them; the words are untouched. Found in review (PR #408). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The tab list behind the shared-name warning emitted the folders, then the generated sections, then the reference, ignoring where `tabs` put the tokens: with `tabs` naming a section before a folder, the warning listed them the other way around, and the doc comment still promised the report's order. The list now walks `tabs` and emits each entry in place, then the unnamed sections and an unnamed reference, mirroring what the template renders. Found in review (PR #408). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ne helper The tabs hint matched a folder near miss on the lowered name alone, while the page lookup beside it also stripped a stray extension, so 'Guides.md' got no suggestion in `tabs` although the same entry in `pages` did. The lowering, stripping and matching now live in one nearMiss helper that answers what an entry was meant as, and each list phrases its own sentence over it, so the two cannot drift again. Found in review (PR #408). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
… a folder in Home
A generated section and the API reference were tabs unless the root `pages`
placed them in Home, so `{}` and `"tabs": []` gave Home and three tabs while
every folder went into Home, and the tab bar was not what `tabs` said. Now a
node is a tab exactly when `tabs` names it: a folder by name, the reference
by `api` or its token, a section by its token. Everything else is in Home,
where `pages` orders it and, unnamed, Fumadocs' own order holds, a section
among the folders like any other. `reorder` loses its bands and anchor, and
the root is no longer reordered without a `pages` of its own. A file with no
`tabs`, or no file, leaves Home alone with no tab bar.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… scaffold the sections as tabs With `tabs` the whole tab bar, a root file without it has one reading, no tab, so the refusal of a 2.0.0-beta.1 file goes: there is nothing left to ask. The content tree names a tab for each `tabs` entry and for nothing else, so a section the file does not name is compared with no tab's name. The scaffold writes `apimatic:sdks`, `apimatic:plugin` and `apimatic:api` in `tabs`, so a fresh portal keeps the tabs it had; the sample repository's root file does the same. The default fixture names the plugin's token too, and the branded build, which has no `nav.json`, asserts no tab bar. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The README bullet and the plans said a section named in neither list kept a tab at a default place, and described the beta.1 refusal. Both now say that `tabs` is the whole tab bar after Home, that what it does not name is in Home in Fumadocs' order, and that a file with no `tabs` makes no tab, with a history entry for the review that decided it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
saeedjamshaid
left a comment
There was a problem hiding this comment.
Reviewed at b387807c. The behaviour matches the plan. Two things I'd want before merge: a word to projects that lose their tabs on upgrade (inline at portal-navigation.ts:123), and the prompt text below that still assumes every section is a tab. The rest are code-quality points against the standard #374 set, most of them one rule living in several places.
Two points sit outside the diff:
src/prompts/portal/serve.ts:54still says removing thepluginblock "removes the Context Plugin tab". The plugin is a tab only whentabsnames it.src/prompts/portal/source.ts:315-316still says "the tabs of the SDK pages and the context plugin keep their names".
On #374's sixth principle nothing changes: PortalNavigation worded its errors itself before this PR too. NearMiss is a step toward keying them by kind, though suggestion and tabSuggestion still word their sentences inside the type.
…he tab list and the hints `api` and `apimatic:api` being one node was ruled on in five places. Now `target` says once what an entry names -- the rest entry, an `apimatic:` string that positions nothing here, the API reference, a section, a folder, a page, an empty folder or nothing -- and the duplicate check, each list's rules, the tabs the content tree names and the near-miss hint read that answer. `NavigationSettings.tabs` carries the resolved tabs, undefined for a file with no list. The lists are typed `'pages' | 'tabs'` with a constant each, an entry with its list is one named type, and the sentences a page in `tabs` and an empty folder get are written once. A list of the wrong type no longer hides the other list's entries, and a page named `api` is refused under either spelling of the reference, since it could be positioned under neither. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The scaffold rebuilt `TOKENS` from its parts; the one list now serves both. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…I accepts The test hard-coded `tabs`, so a setting added to `ROOT_FIELDS` would have been validated by the CLI and stripped by the template with nothing failing. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A root file with no `tabs`, or no root file, makes no tab, and a 2.0.0-beta.1 project had the sections as tabs, so the upgrade was silent. The build now says so once, naming the sections in Home and both ways to settle it: the tokens in `tabs` to have them back, or `"tabs": []` to keep the sidebar, which says nothing. Under `portal serve` it is given once, like the other notices, and again when the plugin block changes what is in Home. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A root `title` names the Home tab, and one tab draws no tab bar, so with nothing in `tabs` the name showed nowhere and the CLI accepted a setting that did nothing. It now says so once. The sections-in-home fixture drops the title it never showed, and its build looks for a Home link instead. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…lugin tabs They are tabs only when `tabs` names them. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`portalTabs` builds its list and returns none for a single entry, which says "one choice switches nothing" directly and covers a tab dropped for having no link. `HOME_TAB_ID` goes back to `navigation.ts`. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`groupIntoTabs` matched each folder to a `tabs` entry and `reorder` then matched the entries back to folders. `nodeNamed` now resolves an entry to its node for both, and the tabs are the nodes `tabs` names, in its order; `isTab`, `isTabFolder` and `lists` go. The section check runs the cheap path test before the storage read. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The rule lives in the code and in the plan; the doc comments keep only what the code does not say. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The two notices, the entry resolver, and the alphabetical place of an unnamed section kept on purpose. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
The two points outside the diff are reworded in 0fb23fd: removing the Each finding has a commit of its own from 001ca25 to d7c46c4. On the new head the unit suite passes (1559), the three e2e builds pass (47), and typecheck, ESLint and Prettier are clean. |
… and the schema test The notice built its suggestion inside a nested template literal, and the schema test passed the same field to the assertion twice. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
saeedjamshaid
left a comment
There was a problem hiding this comment.
Second pass at 019ec6f7. 12 of the 13 threads are fixed as asked, and I've resolved them; the alphabetical-order one stands as decided. One fix brought in a new refusal, inline below. Two minor points, neither blocking:
- The no-tabs warning names the generated sections and the API reference, but in beta.1 a user folder the root
pagesnamed was a tab too. A beta.1 project with{"pages": ["index", "tutorials", "..."]}that follows the warning's"tabs"line loses its Tutorials tab without a word. Naming those folders in the suggested line would cover it. TabEntry(portal-navigation.ts:68) andTabOwner(portal-tabs.ts:7) still share three kinds (API reference, section, folder), differing in a folder's name against its directory, plus Home. Much smaller than before; fine to leave if the split is wanted.
…oken Resolving both spellings to the reference had the shared-name refusal fire for `apimatic:api` too, so every scaffolded portal that added `content/api.md` failed to build, though the entry was the token and the page was never meant to be positioned. Only `api` spelled out might have meant the page, so only that spelling is refused, as on dev; `target()` is unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Before `tabs` existed a folder the root `pages` named was a tab as well, so a 2.0.0-beta.1 project that followed the notice's suggested line got its sections back and lost those tabs without a word. The notice now names them, and the line leads with them in `pages` order, as beta.1 showed them; a `(group)` folder serving the home page is left out, since `tabs` refuses it. Under `portal serve` a folder newly named gives the notice again, once. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The third-review entry said the default order is rarely seen; it is seen by every existing portal the moment a release adds a section, since the scaffold writes `nav.json` once, so that release must tell them to name its token. The entry also claimed the `api` page was refused under either spelling, which the fix before this corrects. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
The two minor points and the plan correction, each in its own commit:
On the new head the unit suite passes (1560), and typecheck, ESLint and Prettier are clean. |
saeedjamshaid
left a comment
There was a problem hiding this comment.
Third pass at 30e7885f. The apimatic:api fix and the plan correction are in, and I've resolved that thread; keeping TabEntry and TabOwner apart is fine for the reason given. One change left on the no-tabs notice, inline.
The line the notice offered was refused when followed: a folder or token the root `pages` named went into `tabs` too, and a node is in one list or the other. A folder in `pages` is in Home because that is what `pages` means, so the folders go, and the line names only the sections and the reference that nothing places; once `pages` places them all, the notice says nothing. The validator answers with what `pages` places, so the `api` spelling stays ruled on in one place, and the text that leaned on beta.1 is in present terms: the CLI's messages describe how `nav.json` works now, not what it did. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
QA testing planWhat changed
ScenariosTabs and layout
Errors (build must fail with a clear message)
Upgrade
Also check: 🤖 Generated with Claude Code |
|
🎉 This PR is included in version 2.0.0-beta.3 🎉 The release is available on: Your semantic-release bot 📦🚀 |
…writes Since #408 a portal with no root nav.json has no tab bar, and the API reference is a collapsed folder in Home, so the fixture's home page no longer linked a page under /api/api. The file quickstart scaffolds names the three sections as tabs, which puts that link back and keeps the fixture what a fresh portal looks like. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>



The change
The root
src/content/nav.jsonhad one list,pages, and it carried two decisions: which top-level folders are tabs, and where Home's nodes go. Naming a folder inpagesmade it a tab, so a folder could never be ordered inside Home. Unnamed folders all fell together where...stood, in alphabetical order, and the only way to order them was a parent folder, which moves every address below it.The root file now has two lists. The plan is in
.ai/plans/portal-config.md, sections 5 and 16, added by the first two commits on this branch.{ "title": "Overview", "tabs": ["tutorials", "apimatic:sdks", "apimatic:api"], "pages": ["index", "getting-started", "authentication", "guides", "apimatic:plugin", "..."] }tabslists the tabs after Home, in order, and nothing else is a tab: a folder directly undercontent/,apiorapimatic:api,apimatic:sdksandapimatic:plugin. It is valid only in the root file.pagesorders Home's sidebar, as every othernav.jsonorders its folder. A folder named here is a group inside Home, and a token stands for its section. Whatpagesdoes not name follows in Fumadocs' order: alphabetical, pages before folders, a section among the folders like any other.tabs,"tabs": []or no file at all makes no tab, so Home is the only tab and the header shows no tab bar. Every section keeps its address inside Home.tabs, so a portal fromquickstartlooks as it did.Commits
navigation-schema.tsextends Fumadocs'metaSchemawithtabs, and the docs collection validatesnav.jsonagainst it, so the key is no longer stripped.groupIntoTabsmakes tabs fromtabs, keeps a sectionpagesnames in Home, and orders the tabs throughreorder.portalTabsreturns no tab for Home alone.PortalNavigationvalidatestabs: a page,..., a folder serving/, an unknown name, a duplicate, and a node also inpagesare each refused with their own sentence, andtabsbelow the root is refused. The rootpagesaccepts the tokens and a folder serving/. The content tree names tabs fromtabsand leaves sections placed in Home out of the shared-name warning. The notice naming the folders made into tabs is removed, andmakesSameTabsAsbecomesmakesSameSectionsAs. The default fixture moves its tabs totabs.sections-in-homefixture and build, and the README's tabs bullet rewritten.tabsbesidepages; a misspelt setting at the root is answered withtabsamong the settings; the README states the real default for an unnamed section; unnamed sections follow everythingtabsnames;apiintabsis refused beside a page of that name; a mistyped tab is pointed at whattabsaccepts.tabsdid not name used to keep a tab at a default place, so{}gave Home and three tabs while every folder went into Home. Nowtabsis the whole tab bar: a section it does not name is a folder in Home, in Fumadocs' order, andreorderloses its bands and anchor. A root file with notabsreads as an empty list, so the refusal of a 2.0.0-beta.1 file goes with the ambiguity it guarded. The scaffold and the sample repository (Name the three generated sections as tabs in nav.json sample-docs-as-code-portal#15) name the three sections intabs. README and plans updated.No
BREAKING CHANGE:footer:nav.jsonis new since 1.x, so nothing breaks against the last stable release. The next beta's release notes should say what changed for a beta.1 root file: a folder or token itspagesnamed is in Home untiltabsnames it.Checked
tsc -p test/tsconfig.json, and ESLint and Prettier on the touched files, pass.tabs; the branded build, which has nonav.json, renders no tab bar and serves every section inside Home; the sections-in-home build is unchanged.🤖 Generated with Claude Code