feat(console): book-driven documentation portal (ADR-0046 §6)#1776
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
The `/docs` portal rendered docs but its navigation was a namespace
heuristic (group by doc-name prefix). ADR-0046 §6 makes `book` the
authored navigation spine — ordered groups whose membership over docs is
derived from each group's include rule (glob/tag), with an optional
explicit `pages` override. §6.4: there is no "flat vs book" fork — a
package with no authored book is browsed as its implicit per-package book
(keyed by packageId). This wires the portal to that model.
URL scheme (doc identity stays single-coordinate per §4; the book segment
is *derived* navigation, not authored path-coupling):
/docs book index (authored + implicit per-package)
/docs/:slug book landing — or a flat-doc permalink that 302s to
its canonical /docs/<homeBook>/<name>
/docs/:slug/:name in-book reader (book sidebar + breadcrumb)
- book-nav.ts: a local port of the framework's resolveBookTree plus portal
helpers — buildPortalBooks (authored + implicit), homeBook (a doc's
canonical book), bookSlug, sortBooks, buildBookCards, scopeDocsToBook,
findBookContainingDoc. Package scoping uses _packageId or, when unstamped,
the name prefix, so it works before the backend stamps provenance. The
synthetic Uncategorized group is flagged and excluded from authored
membership. No dependency on a published spec version or a /tree endpoint.
- use-book-data.ts: fetch book + doc via meta.getItems and synthesize the
portal's implicit books; book is optional (degrades to implicit-only).
- DocsSlug.tsx: the /docs/:slug dispatcher (book landing vs doc redirect).
- BookSidebar.tsx / BookPage.tsx: the resolved spine as nav + a book landing.
- DocsIndex: a book index (no flat/book fork). DocPage: in-book reader with
the book's sidebar, keyed by the :slug segment.
- Routes added top-level (App.tsx) and app-scoped (AppContent.tsx); old
/docs/:name and /apps/:app/docs/:name links redirect via the dispatcher.
Tests: book-nav (17) + BookSidebar (5); existing doc tests unchanged (41
in pages). console type-check + eslint clean.
Note: book/doc must be served by the backend for real data; all states
degrade gracefully (published spec 9.5.1 serves neither yet). Anonymous
`audience:public` access (§6.7) and a book-aware AppDocsIndex are follow-ups;
`doc-groups.ts` is now unused by the portal (left in place, not deleted).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
os-zhuang
force-pushed
the
feat/book-driven-docs-portal
branch
from
June 16, 2026 08:25
c04b47a to
dd8fc4d
Compare
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
Mounts the real DocsIndex / DocsSlug / BookPage / DocPage under the real route table with a mocked metadata adapter (no authored books, so the implicit per-package books are exercised). Verifies the full reader flow end-to-end in jsdom — routing, the /docs/:slug dispatcher, the book landing, the in-book reader + active sidebar, the legacy /docs/:name redirect, the not-found path, and a real click-through navigation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
…abels
Browser-verified against the framework showcase backend. Two polish fixes
found there:
- buildPortalBooks now returns authored books (in their `order`) ahead of
the synthetic per-package books, and buildBookCards preserves that order
instead of re-sorting — so a book with an explicit `order: 0` (e.g. the
showcase manual) is no longer pushed below a fallback implicit book.
- Implicit per-package books label themselves with the humanized package id
("com.objectstack.setup" → "Setup") rather than the raw id; the slug/name
stay the full id so they remain unique and reversible.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
Previously every docs page (index, book landing, reader) called useBookData independently, so a single page issued the doc+book list fetch from several components and every in-section navigation re-fetched. Hoist the fetch to a DocsLayout route that loads once and shares the result via context; the pages read it with useBookData (now a context consumer). Nesting the /docs child routes under the layout (top-level + app-scoped) means navigating within the section issues no further book/doc requests — only the reader's single-doc content fetch. Browser-verified: a full /docs → /docs/manual → reader walk now hits /meta/doc and /meta/book once each (×2 is dev StrictMode only), down from ~8 and ~6 before. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
AppDocsIndex (/apps/:appName/docs) listed the package's docs flat; bring it in line with the portal. It now reads the shared book/doc data from the docs layout and shows the package's book(s): the common single-book case lands straight on that book's grouped reader (redirect), several books list as cards. Links carry the /apps/:appName prefix throughout. Browser-verified inside the showcase app container: /apps/com.example.showcase/docs redirects to .../docs/manual and renders the resolved spine with app-scoped doc links. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
Browser-found polish across the portal: - Book landing no longer duplicates the table of contents (sidebar + a second card list). `/docs/:slug` now opens the book to its overview doc (`*_index`, else the first in order) — a redirect into the reader, whose sidebar already is the TOC. A doc-less book shows an empty state. - Narrow screens (< lg) had no book nav at all (the sidebar is lg-only). The reader now collapses the book nav into a `<details>` disclosure above the content, so mobile/tablet readers can still move within the book. - DocsIndex gains a page heading + intro and even-height cards. - Implicit per-package book cards (no authored description) show their package id as a subtitle for context; AppDocsIndex matches. book-nav gains `firstDoc` + a `Book.implicit` flag / `BookCard.subtitle`. Tests: book-nav 19 (+2) + integration updated for the redirect and the twice-rendered responsive sidebar; 49 in pages. tsc 0, eslint 0. Verified in-browser against the showcase backend. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Makes the documentation portal book-driven (ADR-0046 §6). A
bookis the authored navigation spine — ordered groups whose membership over docs is derived from each group'sincluderule (glob /{ tag }), with an optional explicitpagesoverride. Per §6.4 there is no "flat vs book" fork: a package with no authored book is browsed as its implicit per-package book, so every doc always has a book.URL scheme
Doc identity stays single-coordinate (
<name>, ADR §4 rejects/docs/<book>/<name>as identity); the portal reading URL carries a derived book segment::slug= book.slug (ADR §6 "portal URL segment"); an implicit book uses its packageId. A single/docs/:slugdispatcher resolves a segment to a book landing or redirects a flat doc to its canonical in-book URL. App-scoped mirrors this under/apps/:appName/docs/....Changes (all in
apps/console/src)pages/book-nav.ts— local port ofresolveBookTree+ portal helpers:buildPortalBooks(authored ahead of implicit per-package),homeBook,bookSlug,pkgOf/pkgOfBook(use_packageId, falling back to the name prefix when unstamped),scopeDocsToBook,buildBookCards. Synthetic Uncategorized is flagged and excluded from authored membership. No dependency on a published spec version or a/treeendpoint.pages/DocsLayout.tsx+use-book-data.ts— the/docslayout route fetches book + doc once and shares it via context; pages read it withuseBookData. Navigating within the section issues no further book/doc requests.pages/DocsSlug.tsx— the/docs/:slugdispatcher.BookSidebar.tsx/BookPage.tsx— the resolved spine as nav + a book landing.pages/DocsIndex.tsx— book index (authored books lead).pages/DocPage.tsx— in-book reader with the:slugbook's sidebar.pages/AppDocsIndex.tsx— app-scoped, book-driven (single book → redirect to its reader).com.objectstack.setup→Setup); slug/name keep the full id.App.tsx+ app-scopedAppContent.tsx).Verification
book): the/docsindex (authored Showcase Manual + implicit Setup/Studio), the/docs/manuallanding (Getting Started / Guides resolved frompages+include), the in-book reader (markdown + sidebar), the flat-doc → canonical redirect, and the app-scoped/apps/:pkg/docsredirect inside the app container. The shared-layout fetch was confirmed in the network panel (one book/doc fetch per section, not per page).book-nav(17) +BookSidebar(5) + adocs-portalintegration test (6, mounts the real pages under the real route table); existing doc tests unchanged — 47 inpages.tsc --noEmitexit 0;eslint0 errors.Deployment note & follow-up
book+docmust be served by the backend for real data — published@objectstack/spec@9.5.1serves neither yet, so every surface degrades gracefully and lights up when a framework build withbookis deployed.Open decision (not in this PR): anonymous
audience: publicreading (ADR §6.7)./docsstays behindProtectedRoute; opening it to unauthenticated visitors is a security-gate change to make deliberately.Relationship to the other book PRs
bookadmin/authoring UI. feat: renderelement:metadata_viewer+ ```metadata doc fence (ADR-0051 P1) #1774 (merged) — ADR-0051 inlinemetadata_viewerdoc fence. This — the reader portal.🤖 Generated with Claude Code