diff --git a/AGENTS.md b/AGENTS.md index daf704f02..396439bc3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -33,6 +33,10 @@ yarn watch:demo See `CONTRIBUTING.md` for the full guide. Use clear commit messages so reviewers can understand what each commit does. +## Approved Base Nova Visual Exceptions + +- The shared media-viewer adapter may manage prose flow spacing, responsive action visibility and placement, Dialog viewport bounds, media containment, and overflow. It must reuse the existing `CodeBlockButton` and official Dialog primitive without changing their visual metrics or states. + ## Required Checks Run the following before committing any code changes (except documentation or comment-only updates): diff --git a/README.md b/README.md index 9436a3ce0..54dd1e9d7 100644 --- a/README.md +++ b/README.md @@ -172,7 +172,7 @@ Requirements: Consumers then install a pinned release: ```bash -npm install @aeei/docusaurus-theme@0.1.8 +npm install @aeei/docusaurus-theme@0.1.9 ``` This repository does not publish automatically from an unapproved working tree. diff --git a/docs/base-nova-parity-diagnosis.md b/docs/base-nova-parity-diagnosis.md deleted file mode 100644 index f60a7aed0..000000000 --- a/docs/base-nova-parity-diagnosis.md +++ /dev/null @@ -1,186 +0,0 @@ -# Base Nova 0px Parity — Root Cause Diagnosis - -Status: root causes confirmed and restoration implemented; final full-matrix validation in progress. - -## Oracle - -- Rendered reference: `https://ui.shadcn.com/docs/components/base/*` -- Registry reference: `npx shadcn@4.12.0 add ...` with: - - `style: base-nova` - - Base UI - - `baseColor: neutral` - - Lucide -- Reproducible generated reference: `/tmp/base-nova-exact/src/components/ui` -- Official rendered metric inventory: `/tmp/official-slot-inventory.json` -- Current local metric inventory: `/tmp/local-slot-inventory.json` - -## Root causes - -### 1. Tailwind preflight is missing - -`packages/docusaurus-theme/src/theme/shadcn.css` imports only: - -```css -@import "tailwindcss/theme"; -@import "tailwindcss/utilities"; -``` - -It does not import Tailwind preflight. Official examples use the full official CSS stack. As a result, native browser styles remain active on Base UI elements. - -Observed effect: - -- official ghost/icon Button rest background: transparent -- local hamburger, CodeBlock action, and disclosure trigger rest background: browser `buttonface`, `rgb(239, 239, 239)` -- local buttons also retain native box metrics in some Docusaurus seams - -This is the direct cause of the unexpected gray hamburger and CodeBlock button backgrounds. - -### 2. Official text scale was overwritten (resolved) - -The previous implementation remapped Tailwind UI text tokens to consumer prose tokens. The current theme keeps the official Base Nova/Tailwind text scale intact and owns separate `--theme-text-*` aliases only for Docusaurus shell typography. - -### 3. Official registry files were locally forked - -Examples: - -- `components/ui/button.tsx` - - local additions: `cursor-pointer`, `disabled:cursor-not-allowed` -- `components/ui/card.tsx` - - official: `text-base leading-snug font-medium` - - former local fork: custom heading/line-height classes -- `components/ui/badge.tsx` - - local-only `code` and `codeCompact` variants -- `components/ui/alert.tsx` - - local content-flow behavior inside the official primitive -- `components/ui/sidebar.tsx` - - local reset/behavior classes mixed into registry source - -The official component files are therefore not the actual SSOT. - -### 4. Callsites alter official visual behavior - -Examples: - -- `components/theme-tab-list.tsx` - - forces `variant="line"` - - adds horizontal scroller, border, `rounded-none`, width rules -- `DocCard/Layout/index.tsx` - - adds hover background and layout classes -- `Details/index.tsx` and `TOCCollapsible/*` - - separately compose and style the same disclosure behavior -- `Navbar/MobileSidebar/Layout/index.tsx` - - applies a custom Sheet shell -- `DocSidebar/Desktop/index.tsx` - - forces sticky/custom footer sizing and surface classes -- CodeBlock action adapters pass Docusaurus class names into Button - -The final DOM class list is not the official example class list. - -### 5. Global adapter CSS overrides component slots - -`packages/docusaurus-theme/src/theme/base.scss` visually restyles official seams: - -- mobile Sheet width/padding/header/actions -- Sidebar position/height/background -- Sidebar menu/link/font/radius/padding -- mobile TOC trigger/background -- Table head/cell/row behavior -- dropdown/focus behavior -- global focus outline - -The Sidebar registry source declares a fixed container, but `base.scss` changes it to sticky. This causes the LNB to leave the viewport near the document footer. - -The Sheet uses `z-50`, while the Docusaurus navbar remains above it. The Sheet header is rendered behind the navbar, hiding theme and close actions and making the panel appear to start below the header. - -### 6. The wrong disclosure component is used - -Official rendered reference provides styled `Accordion`: - -- trigger: `384 × 42px` -- trigger padding: `10px 0` -- trigger text: `14px / 20px`, weight `500` -- icon: `16 × 16px` -- transparent rest background - -Local `On this page` and `Expandable details` use separately styled Collapsible + Button compositions: - -- local trigger: `32px` high -- padding: `1px 10px` -- text: `14px / 21px` -- rest background: `rgb(239, 239, 239)` -- bordered/card wrappers differ between the two callsites - -They cannot be identical because they do not share the official Accordion component. - -### 7. Official Tabs are replaced by a custom line-tab composition - -Official default example: - -- Tabs gap: `8px` -- TabsList: height `32px`, padding `3px`, radius `10px`, muted background -- TabsTrigger: height `25px`, padding `2px 6px`, radius `8px`, `14px / 20px` -- selected trigger: background + shadow from official default variant - -Local Docusaurus Tabs: - -- forces line variant -- TabsList radius: `0px` -- TabsList background: transparent -- custom bottom border/indicator -- trigger line-height: `21px` - -The small/inconsistent appearance is caused by intentionally selecting and modifying a different variant. - -### 8. Official animation CSS stack is incomplete - -DropdownMenu and Tooltip use `animate-in`, `fade-in`, `zoom-in`, and related utilities, but the package does not include/import the official animation CSS dependency. Open/close state parity is therefore not guaranteed. - -### 9. Current tests preserve the fork - -Current contract tests assert local inline class fragments and custom variants. They do not compare official registry output or official rendered metrics. A green test run therefore proves internal consistency, not official parity. - -## Measured rest-state deltas - -| Component | Official rendered example | Current local | Confirmed delta/cause | -| ---------------- | -------------------------------------------- | --------------------------------------------- | ------------------------------------------- | -| Button text | Geist, `14/20px` | Inter, `14/21px` | font + line-height token remap | -| Ghost Button bg | transparent | `rgb(239,239,239)` | missing preflight/native buttonface | -| TabsList | `32px`, `p:3px`, `r:10px`, muted bg | `32px`, `p:3px`, `r:0px`, transparent | forced custom line variant | -| TabsTrigger | `25px`, `14/20px` | `25px`, `14/21px` | text token remap | -| Card | `gap:16px`, `r:14px`, `14/20px` | `gap:16px`, `r:12px`, `14/21px` | radius/token and type remap | -| CardTitle | `16/22px` | `17/25.5px` | local CardTitle fork + `text-base` remap | -| Alert | `p:8px 10px`, `r:10px`, `14/20px` | same padding/radius, `14/21px`, tone surfaces | type remap + callsite tone visual overrides | -| TableHead | `40px`, `p:0 8px`, `14/20px` | `40px`, `p:0 8px`, `14/21px` | type remap | -| TableCell | official row `37px`, `14/20px` | local rows `37.5–39.3px`, `14/21px` | type remap/prose content metrics | -| AccordionTrigger | `42px`, `p:10px 0`, transparent | `32px`, `p:1px 10px`, native gray | wrong component + missing preflight | -| Mobile Sheet | viewport overlay with visible header/actions | panel appears below navbar; actions hidden | z-index conflict/custom shell | -| Desktop Sidebar | fixed viewport panel | constrained sticky panel | `base.scss` position override | - -## Required correction order - -1. Restore official CSS stack/preflight and official UI font/text/radius metrics. -2. Replace local UI files with exact generated Base Nova registry files. -3. Use official Accordion for both disclosure callsites. -4. Remove visual callsite classes and `[data-slot]` global overrides. -5. Re-measure all states and iterate until every required delta is zero. - -No implementation is considered complete until the state matrix includes rest, hover, focus, focus-visible, active/pressed, open/expanded, selected/current, disabled, loading, light, and dark. - -## Restoration result - -- Full Tailwind preflight, official Base Nova variants, `tw-animate-css`, Geist Sans/Mono, and Neutral light/dark tokens now ship in compiled package CSS. -- The used `components/ui/**` files match the `shadcn@4.12.0` generated `base-nova` sources after normalizing only import paths and required React imports. -- `Details` and mobile `On this page` now share the official Accordion. -- Docusaurus Tabs use the official default Tabs composition. -- Hamburger, theme, Sheet close, BackToTop, edit, and CodeBlock actions use official Button variants/sizes without incoming visual classes. -- Mobile navigation uses official Sheet and Sidebar composition; Sheet now layers above the Docusaurus navbar. -- Desktop docs navigation retains the official fixed Sidebar container, offset only for the structural Docusaurus navbar. -- The Infima stylesheet is isolated in a lower cascade layer and its generic global table skin is removed before compilation, so the untouched official Table source remains the visual SSOT. -- Tailwind color-mix fallbacks are normalized without reordering state rules; this preserves official selected/focus/dark precedence after Docusaurus production minification. -- Docusaurus CSS minification had shortened Tailwind's exact `calc(1.25 / .875)` `text-sm` line-height ratio to `1.42857`. That produced accumulated `0.015625px`–`0.484375px` component-height drift. The theme now emits exact spacing-derived Tailwind line-height tokens, so production CSS retains the official `16/20/24/28px` metrics. -- `base.scss` contains no direct official `[data-slot]` visual rule. Docs prose selectors explicitly exclude component interiors. -- The live shadcn CodeBlock composition is reproduced directly: semantic `figure`, official surface spacing, code-line grid/padding, and the official Button-based copy action. Docusaurus-only word-wrap UI is omitted because it is absent from the rendered oracle. -- The PostCSS exception is scoped to the packaged `shadcn.scss` and layered Infima input. Consumer CSS continues through its original `postcss-preset-env` options; `src/postcss/preset-env.test.ts` protects this boundary. -- The comparison tool is `scripts/compare-base-nova.cjs`; it uses matched official content/container context, exact trigger alignment for floating surfaces, Chromium's 1/64 CSS-pixel layout quantum, normalized computed colors, and the required geometry/state properties across desktop/tablet/mobile and light/dark. -- The user-facing report is `artifacts/base-nova-parity/report.md` with raw measurements in `results.json`: 406 checks, 406 fully compared exact, 0 contextual exclusions, and 0 differences/errors. -- Automated parity is complete, but user visual approval remains required. diff --git a/docs/superpowers/plans/2026-07-18-exhaustive-rendered-parity.md b/docs/superpowers/plans/2026-07-18-exhaustive-rendered-parity.md deleted file mode 100644 index 108ce7000..000000000 --- a/docs/superpowers/plans/2026-07-18-exhaustive-rendered-parity.md +++ /dev/null @@ -1,210 +0,0 @@ -# Exhaustive Rendered Parity Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Restore every actual starter route surface and prove correct rendering at every horizontal width from 320px through 3840px, including zoom-out/wide-shell behavior. - -**Architecture:** Replace the broken prose exclusion boundary at its source, then make actual-route DOM inventory and responsive sweep the primary acceptance path. Keep synthetic component parity only as supplemental evidence. Full-resolution screenshots and live-oracle comparisons remain mandatory before user approval. - -**Tech Stack:** Docusaurus 3.10, React, TypeScript, Sass, Tailwind CSS 4, Base UI, shadcn Base Nova, Playwright, Jest. - -## Global Constraints - -- Live `ui.shadcn.com` rendered examples win when registry source conflicts. -- Registry-backed UI stays on official Base Nova component source. -- Plain prose/shell surfaces use the live shadcn docs shell oracle. -- Generated OpenAPI content is not modified. -- No commit, push, deployment, or publication before explicit visual approval. -- Card shadow removal remains the only pre-approved visual exception. -- Missing selectors, routes, surfaces, widths, themes, or states fail validation. - ---- - -### Task 1: Actual-route prose regression contract - -**Files:** - -- Create: `scripts/audit-rendered-surfaces.cjs` -- Create: `artifacts/rendered-surfaces/inventory.json` -- Test: `packages/docusaurus-theme/src/theme/prose-contract.test.ts` - -**Interfaces:** - -- Produces: `inventoryRoute(page, route, viewport, theme): Promise`. -- Produces: a JSON row for every visible article element with route, selector, index, text fingerprint, role, computed metrics, and screenshot path. - -- [ ] **Step 1: Write a failing source contract** - -Assert that every prose selector excludes only `.theme-doc-markdown [data-slot]` descendants and never uses bare `:not([data-slot] *)`. - -- [ ] **Step 2: Write failing browser assertions** - -For `/guides/markdown-gfm`, require: - -```js -assert(h1.fontSize !== paragraph.fontSize); -assert(h2.fontSize !== paragraph.fontSize); -assert(ul.listStyleType !== "none"); -assert(ol.listStyleType !== "none"); -assert(li.display === "list-item"); -assert(blockquote.paddingLeft !== "0px"); -assert(image.rect.width <= image.naturalWidth); -``` - -Missing required selectors throw instead of returning. - -- [ ] **Step 3: Run and preserve the red result** - -Run: `node scripts/audit-rendered-surfaces.cjs --route guides/markdown-gfm --width 1440 --theme dark` - -Expected: FAIL for heading scale, list markers, blockquote spacing, and oversized image. - ---- - -### Task 2: Repair prose/component cascade ownership - -**Files:** - -- Modify: `packages/docusaurus-theme/src/theme/base.scss:346-526` -- Modify: `packages/docusaurus-theme/src/theme/base.test.ts` -- Test: `packages/docusaurus-theme/src/theme/prose-contract.test.ts` - -**Interfaces:** - -- Consumes: failing selectors and metrics from Task 1. -- Produces: prose rules that apply to article elements while excluding official components nested inside the article. - -- [ ] **Step 1: Replace the invalid exclusion boundary** - -Use article-local component exclusions equivalent to: - -```scss -:not( - .theme-doc-markdown [data-slot], - .theme-doc-markdown [data-slot] * -) -``` - -Do not match outer shell slots such as `sidebar-inset`. - -- [ ] **Step 2: Restore semantic visual surfaces** - -Set explicit oracle-backed heading scale/rhythm, paragraph rhythm, list markers/indentation/nesting, task-list alignment, blockquote spacing, inline-code treatment, and bounded media behavior. Keep table, Alert, Accordion, Tabs, Card, and CodeBlock interiors outside prose selectors. - -- [ ] **Step 3: Run focused tests** - -Run: - -```bash -yarn jest packages/docusaurus-theme/src/theme/base.test.ts packages/docusaurus-theme/src/theme/prose-contract.test.ts --runInBand -node scripts/audit-rendered-surfaces.cjs --route guides/markdown-gfm --width 1440 --theme dark -``` - -Expected: PASS with no suppressed Markdown surfaces. - ---- - -### Task 3: Exhaustive real-route surface inventory - -**Files:** - -- Modify: `scripts/audit-rendered-surfaces.cjs` -- Create: `artifacts/rendered-surfaces/inventory.json` -- Create: `artifacts/rendered-surfaces/coverage.md` - -**Interfaces:** - -- Produces: `discoverRoutes(): string[]` from docs source, sidebar, navbar, and footer data. -- Produces: coverage counts by route, category, selector, state, viewport, and theme. - -- [ ] **Step 1: Discover routes instead of hard-coding them** - -Map every `examples/docs-starter/docs/**/*.{md,mdx}` file to a route and merge internal routes from Docusaurus output. Fail when any source doc lacks an audited URL. - -- [ ] **Step 2: Enumerate every rendered category** - -Inventory article prose plus Navbar, NavigationMenu, Sidebar, Breadcrumb, TOC, footer, pagination, edit link, BackToTop, Sheet, theme menu, Tooltip, Alert, Accordion, Tabs, Card, Table, CodeBlock, Mermaid, and every visible child instance. - -- [ ] **Step 3: Add semantic and ownership checks** - -Each row records its official component or shell oracle. Unknown ownership fails coverage generation. - ---- - -### Task 4: Continuous responsive sweep and real interactions - -**Files:** - -- Create: `scripts/sweep-responsive-routes.cjs` -- Modify: `scripts/audit-docs-starter.cjs` -- Create: `artifacts/responsive-sweep/results.json` -- Create: `artifacts/responsive-sweep/report.md` - -**Interfaces:** - -- Consumes: discovered routes and surface categories from Task 3. -- Produces: one result per route × theme × width (`320..3840`). - -- [ ] **Step 1: Sweep every integer width** - -At each width, use a fixed representative height and assert: - -```js -scrollWidth <= clientWidth -no visible element has zero width or height -no element overlaps fixed Navbar/Sidebar/Sheet boundaries -all list markers and heading hierarchy remain valid -shell mode matches the observed breakpoint state -``` - -- [ ] **Step 2: Detect breakpoint transitions** - -Record every width where Navbar, Sidebar, TOC, Sheet, table, code, or content geometry changes mode. Re-run `-1 / exact / +1` with hover, keyboard focus, active, open, expanded, selected, disabled, loading, Escape, focus restoration, and scroll states. - -- [ ] **Step 3: Remove blind exceptions** - -Do not exempt table containers or CodeBlocks from overflow checks. Require intended internal scrolling and prohibit page-level overflow/clipping. - ---- - -### Task 5: Oracle comparison, original screenshots, and release validation - -**Files:** - -- Modify: `scripts/compare-base-nova.cjs` -- Create: `artifacts/actual-route-parity/report.md` -- Create: `artifacts/actual-route-parity/results.json` -- Create: original screenshots and focused diffs under `artifacts/actual-route-parity/` -- Modify: `artifacts/visual-approval.md` - -**Interfaces:** - -- Consumes: actual-route inventory and breakpoint results. -- Produces: element-indexed official/local evidence without body/font/width/attribute/floating-context mutation. - -- [ ] **Step 1: Compare actual callsites** - -Use real user routes and interactions. Synthetic fixtures remain supplemental and cannot satisfy completion. - -- [ ] **Step 2: Capture original-resolution evidence** - -Capture every route in light/dark at discovered breakpoints and interaction states. Contact sheets are indexes only. Review original PNGs and store focused official/local comparisons. - -- [ ] **Step 3: Run final validation** - -```bash -yarn jest --runInBand --testPathIgnorePatterns='packages/docusaurus-plugin-openapi-docs/src/markdown/createSchema.test.ts' -yarn tsc -p examples/docs-starter/tsconfig.json --noEmit -yarn workspace @aeei/docusaurus-theme build -yarn workspace @aeei/docs-starter build -node scripts/audit-rendered-surfaces.cjs -node scripts/sweep-responsive-routes.cjs -node scripts/audit-docs-starter.cjs -git diff --check -``` - -Pack `@aeei/docusaurus-theme@0.1.0`, install it into an isolated copy of the starter, and run its production build. - -- [ ] **Step 4: Stop at visual approval** - -Present original screenshots, coverage, responsive sweep, actual-route parity, and residual risks. Do not commit, push, deploy, or publish until the user explicitly approves. diff --git a/docs/superpowers/plans/2026-07-19-actual-ui-parity-recovery.md b/docs/superpowers/plans/2026-07-19-actual-ui-parity-recovery.md deleted file mode 100644 index 0bc9db443..000000000 --- a/docs/superpowers/plans/2026-07-19-actual-ui-parity-recovery.md +++ /dev/null @@ -1,60 +0,0 @@ -# Actual UI parity recovery test plan - -## Source - -- User rejection and screenshots from 2026-07-19. -- `AGENTS.md` Base Nova Visual Parity Contract. -- Oracle: `https://ui.shadcn.com/docs/components/base/*`. - -## Acceptance - -- Card shadow removal is the only visual exception. -- Missing selectors, states, routes, or interaction outcomes fail. -- No contextual exclusions. -- Chromium raw geometry retains 1/64 CSS-pixel values. -- Automation does not replace original-resolution manual review or user approval. - -## Minimal Playwright E2E - -Target: `tests/theme-parity.spec.ts` - -### 1. Desktop navigation controls change real state - -Precondition: Markdown route at desktop width. - -Steps/outcomes: - -- Collapse and expand each LNB category by accessible button. -- `aria-expanded` changes and descendants become hidden/visible. -- Collapse and expand the whole LNB; content allocation changes and focusable restore control appears. -- Open GNB NavigationMenu; menu links become visible; Escape closes it and restores focus. - -### 2. Content controls preserve user-visible behavior - -Precondition: MDX and Markdown routes. - -Steps/outcomes: - -- Select another Tab; selected state and panel content change. -- Expand Details; body becomes visible; keyboard activation works. -- Copy CodeBlock; clipboard receives exact code and feedback state appears. -- Theme menu changes light/dark and persists through navigation. - -### 3. Critical visual relations are viewport-consistent - -Precondition: `390`, `1024`, and `1440`; light/dark. - -Outcomes: - -- GNB branding/menu separation is nonzero and follows the approved official composition. -- Every rendered prose heading has the approved next-sibling flow spacing; component roots are not accidentally excluded. -- Tabs heading-to-root spacing is exact. -- CodeBlock owned background is identical across viewport sizes. -- Desktop shell does not substitute a different content surface from mobile. -- No page-level horizontal overflow. - -## Broader checks - -- Source contracts verify every rendered shared component imports the one official registry SSOT. -- Existing route inventory and integer-width sweep remain supplemental; assertions are strengthened around state change and visual relations. -- Original screenshots are inspected manually after all automated checks pass. diff --git a/docs/superpowers/plans/2026-07-20-search-providers.md b/docs/superpowers/plans/2026-07-20-search-providers.md deleted file mode 100644 index 62d6dedd9..000000000 --- a/docs/superpowers/plans/2026-07-20-search-providers.md +++ /dev/null @@ -1,131 +0,0 @@ -# Optional Search Providers Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Add optional `false | "local" | "algolia"` search providers to `@aeei/docusaurus-theme`, with automatic production indexing for local search and the live shadcn command-menu UI. - -**Architecture:** The theme plugin validates and publishes the selected provider through Docusaurus global data. Local mode extracts deterministic section records from rendered docs during `postBuild`; the browser lazily fetches and searches that static JSON. Algolia mode keeps the Docusaurus provider but uses the same shadcn trigger and the existing token adapter. - -**Tech Stack:** Docusaurus 3.10 plugin lifecycle, React 18/19, TypeScript, Cheerio, official shadcn 4.12.0 Base Nova Dialog/Command/InputGroup, Base UI, cmdk, Playwright, Jest. - -## Global Constraints - -- Search defaults to disabled and emits no trigger, index, or request. -- `search: "local"` automatically indexes every rendered Markdown/MDX route on each production build. -- `search: "algolia"` emits no local index and requires complete standard Docusaurus Algolia config. -- Local queries and content never leave the internal server/browser. -- Search UI follows the live `ui.shadcn.com` command-menu trigger, dialog, list, selected row, and footer geometry. -- No consumer Tailwind/Sass configuration. -- No generated OpenAPI source modification. -- No commit, push, deployment, or publication before explicit visual approval. - ---- - -### Task 1: Provider options and deterministic local index - -**Files:** - -- Create: `packages/docusaurus-theme/src/search/types.ts` -- Create: `packages/docusaurus-theme/src/search/build-index.ts` -- Create: `packages/docusaurus-theme/src/search/build-index.test.ts` -- Modify: `packages/docusaurus-theme/src/index.ts` -- Modify: `packages/docusaurus-theme/package.json` - -**Interfaces:** - -- Produces `SearchProvider = false | "local" | "algolia"`. -- Produces `SearchRecord`, `SearchIndex`, `buildSearchIndex()` and `writeSearchIndex()`. -- Publishes `{search: {provider}}` as plugin global data. - -- [ ] Write failing tests covering invalid options, disabled/Algolia no-index behavior, local route extraction, `noIndex`, fixture exclusion, heading anchors, Unicode normalization, deterministic ordering, and removed-page cleanup. -- [ ] Run `yarn jest packages/docusaurus-theme/src/search/build-index.test.ts --runInBand`; expect missing module/API failures. -- [ ] Add direct `cheerio` dependency and implement deterministic rendered-HTML extraction from `postBuild` routes into `build/search-index.json` only for local mode. -- [ ] Validate Algolia mode against `themeConfig.algolia.appId`, `apiKey`, and `indexName`; throw an actionable build error when incomplete. -- [ ] Re-run the focused Jest test; expect all assertions to pass. - -### Task 2: Official Base Nova command primitives - -**Files:** - -- Create: `packages/docusaurus-theme/src/theme/components/ui/dialog.tsx` -- Create: `packages/docusaurus-theme/src/theme/components/ui/command.tsx` -- Create: `packages/docusaurus-theme/src/theme/components/ui/input-group.tsx` -- Create: `packages/docusaurus-theme/src/theme/components/ui/textarea.tsx` -- Create: `packages/docusaurus-theme/src/theme/components/ui/spinner.tsx` -- Modify: `packages/docusaurus-theme/src/theme/registry-source-contract.test.ts` -- Modify: `packages/docusaurus-theme/VENDORED_SOURCES.md` -- Modify: `packages/docusaurus-theme/THIRD_PARTY_NOTICES.md` -- Modify: `packages/docusaurus-theme/package.json` - -**Interfaces:** - -- Produces official Dialog, Command, InputGroup, Textarea, and Spinner exports. -- Command depends on `cmdk`; placeholder icons are replaced only with corresponding Lucide icons. - -- [ ] Extend registry hash/source tests with the five official Base Nova files and fail before files exist. -- [ ] Copy official shadcn 4.12.0 Base sources, changing only import paths and Lucide placeholder imports. -- [ ] Add direct `cmdk` dependency and attribution/source entries. -- [ ] Run registry contract, theme build, and LSP; expect source identity and types to pass. - -### Task 3: Dependency-free local ranking and live shadcn UI - -**Files:** - -- Create: `packages/docusaurus-theme/src/theme/components/local-search/search.ts` -- Create: `packages/docusaurus-theme/src/theme/components/local-search/search.test.ts` -- Create: `packages/docusaurus-theme/src/theme/components/local-search/index.tsx` -- Modify: `packages/docusaurus-theme/src/theme/Navbar/Content/index.tsx` -- Modify: `packages/docusaurus-theme/src/theme/base.scss` - -**Interfaces:** - -- Produces `searchRecords(records, query, limit = 8)`. -- Produces `` reading `/search-index.json` lazily. -- Consumes plugin global provider data. - -- [ ] Write failing ranking tests for NFKC/lowercase normalization, Unicode tokens, all-token matching, title > section > body ranking, deterministic ties, snippets, and eight-result limit. -- [ ] Implement minimal dependency-free ranking and pass focused tests. -- [ ] Add the live shadcn trigger (`Search documentation…`/`Search…`), Dialog, Command input/list, selected row, loading/empty/error states, footer Enter hint, Mod+K, routing, and focus restoration. -- [ ] Keep Algolia mode behind the same trigger; disabled mode returns `null`. -- [ ] Add Playwright RED tests for disabled, local production index, open/query/select/navigate, keyboard, empty/error, mobile, light/dark, and focus restoration. -- [ ] Build and serve the starter; make Playwright tests green without local visual overrides outside the approved search composition. - -### Task 4: Consumer configuration and search guide - -**Files:** - -- Modify: `examples/docs-starter/docusaurus.config.ts` -- Modify: `examples/docs-starter/config-contract.test.ts` -- Create: `examples/docs-starter/docs/guides/search.md` -- Modify: `examples/docs-starter/sidebars.ts` -- Modify: `packages/docusaurus-theme/README.md` -- Modify: `packages/docusaurus-theme/src/package-contract.test.ts` - -**Interfaces:** - -- Starter registers `["@aeei/docusaurus-theme", {search: "local"}]`. -- Public consumer example documents `{search: "algolia"}` plus standard `themeConfig.algolia`. - -- [ ] Write failing config/package/docs assertions for all three provider modes and automatic MD/MDX indexing. -- [ ] Enable local mode in the starter and add the search guide with security, build/serve, exclusion, refresh, and troubleshooting sections. -- [ ] Document Algolia for public docs/blogs and prohibit Admin API keys. -- [ ] Build the starter and assert `build/search-index.json` contains the new guide route and excludes `/base-nova-parity`. - -### Task 5: Exhaustive verification and evidence - -**Files:** - -- Modify: `scripts/sweep-responsive-routes.cjs` -- Modify: `scripts/capture-actual-route-evidence.cjs` -- Modify: `artifacts/validation/README.md` -- Modify: `artifacts/visual-approval.md` - -**Interfaces:** - -- Search states become mandatory actual-route evidence. - -- [ ] Add search trigger/dialog/result/keyboard/focus assertions to the responsive and critical audits. -- [ ] Capture original-resolution desktop/mobile × light/dark search screenshots and compare them with live shadcn at matching states. -- [ ] Run Playwright, scoped Jest, type/LSP, theme build, starter build, isolated npm tarball build, Base Nova `406/406`, route inventory, rendered audit, shell/TOC audits, and every integer width `320..3840`. -- [ ] Run focused Prettier and `git diff --check`. -- [ ] Update validation counts and present screenshots for explicit user approval; do not commit or publish. diff --git a/docs/superpowers/specs/2026-07-18-exhaustive-rendered-parity-design.md b/docs/superpowers/specs/2026-07-18-exhaustive-rendered-parity-design.md deleted file mode 100644 index c8f84c695..000000000 --- a/docs/superpowers/specs/2026-07-18-exhaustive-rendered-parity-design.md +++ /dev/null @@ -1,68 +0,0 @@ -# Exhaustive rendered parity design - -## Objective - -Restore every rendered starter element—not only synthetic fixtures—to the live shadcn Base Nova oracle, then prove coverage across every horizontal width from 320px through 3840px, including zoom-out/wide-shell behavior, without hiding integration defects. - -## Diagnosis - -The current prose selectors use exclusions such as `:not([data-slot] *)`. Every docs article sits inside the official Sidebar shell, whose ancestors have `data-slot`. Consequently every Markdown descendant matches `[data-slot] *` and is excluded from the prose rules. Tailwind preflight then wins: headings inherit `15px`, lists keep `list-style: none`, margins collapse, and media expands to the content width. - -The existing comparator cannot catch this because it primarily targets a synthetic fixture route, mutates body typography and fixture widths, and injects floating context. The browser audit captures screenshots but neither evaluates them nor asserts Markdown semantics. - -## Considered approaches - -1. **Keep synthetic fixture parity and add isolated prose patches** — rejected. It repeats the blind spot and cannot prove actual-route integration. -2. **Screenshot baseline only** — rejected. It detects raster changes but cannot explain missing semantics, distinguish intended content changes, or prove element coverage. -3. **Actual-route DOM/metric contract + responsive sweep + full-resolution visual review** — selected. It combines deterministic semantic assertions, exact oracle-backed component metrics, continuous responsive coverage, and human-readable visual evidence. - -## Architecture - -### 1. Rendered surface inventory - -Generate the route list from all starter Markdown/MDX docs and Docusaurus navigation data. On each route, enumerate every visible article and shell element with a stable surface category, selector, element index, semantic role, text fingerprint, and owning theme adapter. The validation report fails if a source route or required surface lacks coverage. - -### 2. Prose isolation - -Scope prose exclusions only to official component roots located _inside_ `.theme-doc-markdown`. An outer shell `[data-slot]` must never suppress article typography. Keep component interiors isolated from prose rules. Validate headings, paragraphs, lists, task lists, links, emphasis, blockquotes, inline code, code blocks, images, tables, admonitions, details, Tabs, and Mermaid containers. - -Use the live `ui.shadcn.com` `.typeset` CSS snapshot as the prose SSOT, adapted only where Docusaurus integration requires it: - -- preserve the separate page-title oracle for the synthetic or first content `h1`; -- exclude only article-local `[data-slot]` component roots and `.theme-code-block` interiors; -- never treat raw `svg` as prose media. - -### 3. Oracle ownership - -- Registry-backed UI uses the official Base Nova component source and live rendered component state. -- CodeBlock uses the live shadcn documentation code surface. -- Plain prose, TOC, Mermaid, pagination, footer, and shell-only surfaces use the live shadcn documentation shell because no registry component exists. -- No fixture width/body-font/attribute/floating-context mutation may count as actual-route proof. - -### 4. Responsive verification - -Sweep every integer width from `320` through `3840` for every real route in light and dark mode. At every width assert no unintended horizontal overflow, clipping, overlap, zero-area content, lost list markers, broken heading hierarchy, incorrect responsive shell mode, or uncapped wide-shell drift. Explicitly record breakpoint transitions and test `-1 / exact / +1` widths with interactions. - -Full state screenshots are captured at representative widths and every discovered breakpoint boundary. Screenshots remain full resolution; contact sheets are navigation aids only and never validation evidence. - -### 5. Validation outputs - -Produce: - -- actual-route surface inventory with coverage totals; -- responsive sweep JSON listing all widths/routes/themes; -- semantic/metric failures with selector and element index; -- original screenshots and official/local comparisons; -- focused diff images where a common oracle surface exists; -- standard Jest, TypeScript/LSP, clean builds, browser interaction audit, isolated tarball consumer build, Prettier, and diff-check logs. - -## Boundaries - -- Generated OpenAPI content remains untouched. -- No commit, push, deployment, or npm publication before explicit user visual approval. -- The synthetic parity fixture may remain supplemental but cannot satisfy actual-route acceptance. -- Card shadow remains the only pre-existing visual exception unless the user explicitly changes it. - -## Completion rule - -Work is incomplete if any rendered element is uninventoried, any width in `320–3840` is untested, any actual route/state fails, any screenshot mismatch is unexplained, or the user has not visually approved the original-resolution evidence. diff --git a/docs/superpowers/specs/2026-07-20-local-docs-search-design.md b/docs/superpowers/specs/2026-07-20-local-docs-search-design.md deleted file mode 100644 index 0ed0b3f6e..000000000 --- a/docs/superpowers/specs/2026-07-20-local-docs-search-design.md +++ /dev/null @@ -1,150 +0,0 @@ -# Optional Docs Search Providers Design - -## Objective - -Add optional local and Algolia search providers to `@aeei/docusaurus-theme` without consumer Tailwind/Sass configuration. Internal docs use the self-hosted local index; public docs/blogs may use Docusaurus’ first-class Algolia integration. - -The local feature is intentionally modest: reliable title, heading, and body keyword search with a Base Nova command dialog. It does not provide analytics, typo-tolerant AI search, synonyms, or cross-site federation. - -## Consumer API - -Search is disabled by default. Consumers choose one provider through the existing Docusaurus theme registration. - -Internal/self-hosted docs use the built-in local provider; no separate plugin is required: - -```ts -themes: [ - "@docusaurus/theme-mermaid", - ["@aeei/docusaurus-theme", { search: "local" }], -]; -``` - -Public documentation or blogs can use Docusaurus’ first-class Algolia integration: - -```ts -themes: [ - "@docusaurus/theme-mermaid", - ["@aeei/docusaurus-theme", { search: "algolia" }], -]; - -themeConfig: { - algolia: { - appId: process.env.ALGOLIA_APP_ID, - apiKey: process.env.ALGOLIA_SEARCH_API_KEY, - indexName: process.env.ALGOLIA_INDEX_NAME, - }, -} -``` - -The theme exports this stable option: - -```ts -export type DocusaurusThemeOptions = { - search?: false | "local" | "algolia"; -}; -``` - -Behavior: - -- `search` omitted or `false`: no trigger, no local index, and no search request. -- `search: "local"`: generate the local index and render the Base Nova command dialog. -- `search: "algolia"`: do not generate a local index; use Docusaurus’ Algolia provider behind the same Base Nova trigger and a token-driven Base Nova DocSearch adapter. -- `search: "algolia"` without a complete `themeConfig.algolia` fails the build with an actionable configuration error. -- The included internal starter enables `search: "local"` as the working example. -- Result limits and ranking remain theme-owned defaults; no speculative configuration surface is exposed. - -## Security boundary - -- Search queries and indexed content never leave the browser or the internal server. -- `search-index.json` contains searchable documentation text and must remain behind the same network/auth boundary as the rendered documentation. -- Navigation, footer, test fixtures, and elements explicitly marked for exclusion are not indexed. - -## Index generation - -When `search: "local"`, `@aeei/docusaurus-theme` generates `search-index.json` in the Docusaurus production output during `postBuild`. Algolia and disabled modes never emit this file. - -The generator scans rendered HTML, selects the rendered documentation article, and emits section records containing: - -```ts -type SearchRecord = { - id: string; - url: string; - pageTitle: string; - sectionTitle: string; - text: string; -}; -``` - -Rules: - -- One page-level record plus one record per rendered heading section. -- Index page title, heading text, paragraphs, lists, tables, and inline code. -- Exclude navigation, footer, hidden content, the `/base-nova-parity` fixture, and elements marked `data-search-exclude`. -- Normalize whitespace and Unicode with `NFKC`; preserve original text for snippets. -- Generate deterministic JSON ordering so unchanged docs produce unchanged output. -- Every Markdown/MDX route added to Docusaurus is automatically included on the next production build; consumers never maintain a page list. -- Edited documents update and removed documents disappear on the next clean build. -- Search is supported in production `build` + `serve`; the dialog shows a clear unavailable state if the index is absent during development. - -## Search behavior - -The browser lazily fetches the index the first time the dialog opens and caches it for the session. - -Matching is deliberately simple and dependency-free: - -- Normalize the query with `NFKC` and lowercase it. -- Split on Unicode letter/number boundaries. -- Require every query token to occur in the record. -- Rank page-title matches above section-title matches, and section-title matches above body matches. -- Prefer exact and prefix matches over body substring matches. -- Return at most eight results. -- Generate a short plain-text snippet around the first body match. - -No fuzzy typo correction, stemming, remote requests, or analytics. - -## UI composition - -Use official shadcn 4.12.0 Base Nova registry components for the shared trigger and local provider: - -- Existing official `Button` trigger. -- Official `Dialog`, `Command`, and `InputGroup` sources. -- Lucide `Search`, `FileText`, and `CornerDownLeft` icons. - -Algolia owns its modal DOM. Its adapter must map every visible rest/hover/focus/selected/loading/empty/mobile/dark metric to the corresponding Base Nova Command/Dialog surface without changing Algolia behavior or accessibility. - -Desktop trigger: `Search` plus `⌘ K`/`Ctrl K`. Mobile trigger: icon-only Button with `Search documentation` accessible name. - -Dialog behavior: - -- Open from trigger or Mod+K. -- Autofocus the command input. -- Show loading, empty-query guidance, no-results, unavailable-index, and result states. -- Arrow keys move selection; Enter navigates; Escape closes and restores trigger focus. -- Results show page title, optional section title, and one snippet. -- Use Docusaurus routing for same-site navigation. -- Light/dark, mobile/desktop, focus-visible, selected, loading, empty, error, and open states follow official Base Nova metrics. - -## Documentation - -Add `examples/docs-starter/docs/guides/search.md` and include it in the Guides sidebar. It explains: - -- Choosing `false`, `"local"`, or `"algolia"` in the theme options. -- Local search requires no credentials and keeps content internal. -- Algolia setup for public docs/blogs, including Search-only key usage, crawler requirements, and the prohibition on exposing an Admin API key. -- Production build/serve behavior. -- What content is indexed and excluded. -- `data-search-exclude` usage. -- Index refresh on each deployment. -- The requirement that `search-index.json` remain behind the same auth/network boundary. -- Troubleshooting for a missing or stale index. - -Remove the unused Algolia environment/config branch and DocSearch-only adapter styles from the starter/theme. - -## Validation - -- Unit tests for provider option validation, disabled/local/Algolia output, Algolia configuration failure, automatic new-route indexing, deterministic HTML extraction, normalization, ranking, snippets, exclusions, and result limits. -- Contract tests for exact official Dialog/Command/InputGroup registry sources. -- Playwright tests for trigger, Mod+K, lazy loading, query results, keyboard selection, routing, Escape focus restoration, mobile, light/dark, empty, and unavailable-index states. -- Confirm index generation in starter production build and isolated npm tarball consumer build. -- Re-run the full `320..3840` responsive sweep, route inventory, Base Nova comparison, builds, LSP, Prettier, and `git diff --check`. -- No commit, push, deployment, or publication before explicit user visual approval. diff --git a/examples/docs-starter/docs/guides/markdown-gfm.md b/examples/docs-starter/docs/guides/markdown-gfm.md index dec3aa272..8bd88f10d 100644 --- a/examples/docs-starter/docs/guides/markdown-gfm.md +++ b/examples/docs-starter/docs/guides/markdown-gfm.md @@ -26,6 +26,10 @@ Jump to [paragraph flow](#paragraph-flow-and-emphasis), [lists](#lists-and-tasks ![Hero art](/img/docs-hero.svg) +- Linked image keeps navigation and receives no viewer control: + +[![Linked hero art](/img/docs-hero.svg)](../showcase/mdx-playground) +
![Figure fixture](/img/docs-hero.svg) diff --git a/package.json b/package.json index 40eecc7b4..250b77ff8 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "format": "prettier . --check --ignore-unknown --ignore-path .prettierignore", "lint": "eslint . --ignore-path .gitignore --ext .ts,.tsx,.js", "test": "NODE_OPTIONS=--experimental-vm-modules jest", - "test:e2e:theme": "playwright test tests/theme-parity.spec.ts", + "test:e2e:theme": "playwright test", "test:cypress:dev": "start-server-and-test watch:demo http://localhost:3000 cy:open", "test:cypress": "start-server-and-test serve http://localhost:3000 cy:run", "release:changelog": "scripts/changelog.ts", diff --git a/packages/docusaurus-theme/package.json b/packages/docusaurus-theme/package.json index 41e6e31d5..361502f64 100644 --- a/packages/docusaurus-theme/package.json +++ b/packages/docusaurus-theme/package.json @@ -1,6 +1,6 @@ { "name": "@aeei/docusaurus-theme", - "version": "0.1.8", + "version": "0.1.9", "description": "A shadcn Base Nova theme for Docusaurus docs.", "license": "MIT", "keywords": [ diff --git a/packages/docusaurus-theme/src/package-contract.test.ts b/packages/docusaurus-theme/src/package-contract.test.ts index 8dddbf12c..d3df74ae1 100644 --- a/packages/docusaurus-theme/src/package-contract.test.ts +++ b/packages/docusaurus-theme/src/package-contract.test.ts @@ -20,7 +20,7 @@ it("uses the public docs-only package identity and metadata", () => { const packageJson = JSON.parse(fs.readFileSync(packagePath, "utf8")); expect(packageJson.name).toBe("@aeei/docusaurus-theme"); - expect(packageJson.version).toBe("0.1.8"); + expect(packageJson.version).toBe("0.1.9"); expect(packageJson.description).toBe( "A shadcn Base Nova theme for Docusaurus docs." ); diff --git a/packages/docusaurus-theme/src/theme/DocItem/TOC/Desktop/index.tsx b/packages/docusaurus-theme/src/theme/DocItem/TOC/Desktop/index.tsx index 790cd9802..a922054d3 100644 --- a/packages/docusaurus-theme/src/theme/DocItem/TOC/Desktop/index.tsx +++ b/packages/docusaurus-theme/src/theme/DocItem/TOC/Desktop/index.tsx @@ -1,6 +1,7 @@ import React, { type ReactNode } from "react"; import { useDoc } from "@docusaurus/plugin-content-docs/client"; +import Translate from "@docusaurus/Translate"; import TOCItems from "@theme/TOCItems"; const LINK_CLASS_NAME = "table-of-contents__link toc-highlight"; @@ -14,7 +15,15 @@ export default function DocItemTOCDesktop(): ReactNode {