From 14fb2df43dea011d9e5c0c8a96106b92d4456e5c Mon Sep 17 00:00:00 2001 From: Val Alexander Date: Fri, 11 Sep 2026 06:52:17 -0500 Subject: [PATCH] feat(specimens): refine library and Lab documentation UI Align the catalog with shadcn documentation conventions while preserving OpenCoven's neutral docs palette and Cave accents. Center and slim the header, simplify preview chrome, improve source readability and search recovery, and connect stateful Lab scenes through shareable URLs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- README.md | 52 +- apps/specimens/index.html | 31 +- apps/specimens/src/app.tsx | 284 +++---- apps/specimens/src/component-preview.tsx | 8 +- apps/specimens/src/coven-theme.css | 114 +++ apps/specimens/src/lab-scenes.ts | 53 ++ apps/specimens/src/lab.css | 76 +- apps/specimens/src/lab.tsx | 101 +-- apps/specimens/src/main.tsx | 1 + apps/specimens/src/specimens-fixes.css | 45 +- apps/specimens/src/specimens.css | 693 ++++++++---------- packages/ui/tests/catalog-navigation.test.tsx | 178 ++++- packages/ui/tests/specimens.test.tsx | 86 +++ public/fonts/eb-garamond/OFL.txt | 93 +++ .../fonts/eb-garamond/eb-garamond-latin.woff2 | Bin 0 -> 44336 bytes public/fonts/inter/OFL.txt | 93 +++ public/fonts/inter/inter-latin.woff2 | Bin 0 -> 48432 bytes public/fonts/jetbrains-mono/OFL.txt | 93 +++ .../jetbrains-mono/jetbrains-mono-latin.woff2 | Bin 0 -> 31432 bytes scripts/mobile-quality-review.mjs | 15 + scripts/verify-contracts.mjs | 72 +- scripts/visual-review.mjs | 368 +++++++++- scripts/zoom-probe.mjs | 41 +- 23 files changed, 1781 insertions(+), 716 deletions(-) create mode 100644 apps/specimens/src/coven-theme.css create mode 100644 apps/specimens/src/lab-scenes.ts create mode 100644 public/fonts/eb-garamond/OFL.txt create mode 100644 public/fonts/eb-garamond/eb-garamond-latin.woff2 create mode 100644 public/fonts/inter/OFL.txt create mode 100644 public/fonts/inter/inter-latin.woff2 create mode 100644 public/fonts/jetbrains-mono/OFL.txt create mode 100644 public/fonts/jetbrains-mono/jetbrains-mono-latin.woff2 diff --git a/README.md b/README.md index f39de46..2639ceb 100644 --- a/README.md +++ b/README.md @@ -23,10 +23,10 @@ quietly drift back into copied HTML. | Icons | Lucide | | Distribution | `@opencoven/ui` package and shadcn registry | -Zinc supplies scaffold structure only. Colors, display/reading/mono font roles, +Zinc supplies scaffold structure only. The package's colors and display/reading/mono font roles, and the 4/8/12/16/24/32/48/64/96px spacing scale map to the pinned canonical -brand profile. Fonts use its local/system fallback stacks; no font files or -third-party font requests are bundled. Components consume semantic tokens such +brand profile. Package fonts use its local/system fallback stacks without +bundled font files or third-party font requests. Components consume semantic tokens such as `background`, `card`, `presence`, `success`, and the canonical `tool-read`/`tool-write`/`tool-exec`/`tool-net` mappings. @@ -69,15 +69,55 @@ direct navigation, while the selected entry follows page scrolling. Library and Lab use one fixed compact layout, without density controls or density labels. Public component density props remain available to consumers. -The library presents one specimen per row, with bounded live surfaces, -responsive block layouts, and a compact installation strip. It keeps live previews above syntax-highlighted install commands, +The app uses the [docs UI](https://docs.opencoven.ai/docs/guide/getting-started) +neutral backgrounds (`#121212` dark, `#f5f5f5` light), with Cave's **Coven** +default lavender accents, Inter for documentation headings and interface text, +and JetBrains Mono for code and numeric labels. EB Garamond remains available +through the Cave editorial token but is not preloaded for documentation pages. +The Cave reference is +[`foundations.css` at `47afd756`](https://github.com/OpenCoven/coven-cave/blob/47afd75644ad9e28136f480b4185409541adb68f/src/styles/globals/foundations.css). +Color conveys interaction or semantic state, not catalog categories. +Dark is the initial scheme; saved light preferences are respected. +`coven-theme.css` scopes these aliases to the app, including portaled menus. +The official mark, shared spacing scale, and public package/registry tokens stay +unchanged. + +The three Latin variable fonts are self-hosted, with no runtime font requests +to third parties. Their SIL Open Font License 1.1 files are included alongside +each font in `public/fonts/`. + +Component headings link directly to their specimens. Search announces the +result count and provides a clear action beside the query; Escape clears the +query while the search field is focused. +The slim desktop header centers search between balanced side columns, with +navigation on the left and an icon-only theme action on the right. Below +1088px, search occupies its own centered row with touch-sized controls. + +The presentation follows [shadcn's component documentation](https://ui.shadcn.com/docs/components/button): +sans-serif headings, quiet grouped navigation, a unified neutral shell, and +text-only Preview/Source tabs above a bordered canvas. The OpenCoven mark, +original column widths, complete catalog, and stateful demos remain intact. + +The library uses a compact documentation header and one specimen per row, with +a single bounded preview canvas rather than nested outer cards. Small controls +retain their intrinsic width on mobile; composed surfaces use the available +space. Install/Import tabs and their code panels use the full content width +beneath the preview. It keeps live previews above syntax-highlighted install commands, imports, and full component source, with copy controls and explicit clipboard-failure feedback. The shared code renderer supports Bash, TypeScript/TSX, and CSS. Each specimen has Preview/Source tabs in a fixed-size preview area, so opening source does not change the card or page height. Preview or Escape returns to the same component state; source scrolls inside that area. Supported states live in a separate, always-visible footer rather -than a tab. The Lab +than a tab. + +Related specimens expose **Open in Lab** links to their matching scene. +Scene URLs such as `/lab#run-rail` are shareable; scene changes replace the +current fragment without adding history entries. Browser hash history preserves +mounted drafts, and unknown scene fragments normalize to Composer. +**View in library** returns to the relevant specimen. + +The Lab fits the viewport and offers six scenes through tabs, previous/next controls, and touch swipes. Only the active scene scrolls when space is limited or text is enlarged; the page and navigation stay fixed. Demo actions are local and diff --git a/apps/specimens/index.html b/apps/specimens/index.html index 3d576d3..670933f 100644 --- a/apps/specimens/index.html +++ b/apps/specimens/index.html @@ -1,14 +1,41 @@ - + + + - + + OpenCoven UI — Built for agents. Made for humans. diff --git a/apps/specimens/src/app.tsx b/apps/specimens/src/app.tsx index 1a15c94..df60ffd 100644 --- a/apps/specimens/src/app.tsx +++ b/apps/specimens/src/app.tsx @@ -3,7 +3,6 @@ import { ActivityItem, AttachmentChip, - Badge, BudgetPill, Button, Card, @@ -23,13 +22,14 @@ import { TranscriptTurn, type ComposerMode, } from "@opencoven/ui"; -import { Box, Braces, Layers3, Moon, Sparkles, Sun } from "lucide-react"; +import { ArrowUpRight, Link2, Moon, SearchX, Sun, X } from "lucide-react"; import { type ReactNode, useEffect, useMemo, useRef, useState } from "react"; import { ComposerDemo, RunRailDemo } from "./block-demos"; import { CodeSnippet } from "./code-snippet"; import { ComponentPreview } from "./component-preview"; import { Lab } from "./lab"; +import { labScenes } from "./lab-scenes"; import { CompletionPaletteExample, FailureSurfaceExample, @@ -44,7 +44,6 @@ type Specimen = { id: string; title: string; group: SpecimenGroup; - primitive: string; description: string; states: string; preview: ReactNode; @@ -64,26 +63,21 @@ const componentSources = import.meta.glob( { query: "?raw", import: "default", eager: true }, ); -const groupDetails: Record< - SpecimenGroup, - { id: string; eyebrow: string; description: string } -> = { - Composer: { - id: "group-composer", - eyebrow: "Input layer", - description: "Intent, authority, attachments, and send readiness.", - }, - "Run rail": { - id: "group-run-rail", - eyebrow: "Evidence layer", - description: "Execution evidence, limits, resources, and failure states.", - }, - Blocks: { - id: "group-blocks", - eyebrow: "Complete surfaces", - description: "Public components assembled into reusable agent workflows.", - }, -}; +const groupDetails: Record = + { + Composer: { + id: "group-composer", + description: "Intent, authority, attachments, and send readiness.", + }, + "Run rail": { + id: "group-run-rail", + description: "Execution evidence, limits, resources, and failure states.", + }, + Blocks: { + id: "group-blocks", + description: "Public components assembled into reusable agent workflows.", + }, + }; function SpecimenCard({ specimen }: { specimen: Specimen }) { const sourceKind = specimen.group === "Blocks" ? "blocks" : "components"; @@ -95,6 +89,7 @@ function SpecimenCard({ specimen }: { specimen: Specimen }) { const registryUrl = `https://ui.opencoven.ai/r/${specimen.id}.json`; const packagePath = `@opencoven/ui/${sourceKind}/${specimen.id}`; const importCode = `import { ${exportName} } from "${packagePath}";`; + const labScene = labScenes.find((scene) => scene.source === specimen.id); const source = componentSources[ `../../../packages/ui/src/${sourceKind}/${specimen.id}.tsx` @@ -110,15 +105,25 @@ function SpecimenCard({ specimen }: { specimen: Specimen }) { >
+

+ + {specimen.title} + +

- {sourceKind === "blocks" ? : } {sourceKind === "blocks" ? "Block" : "Component"} - {specimen.primitive}
-

- {specimen.title} -

+ {labScene ? ( + + Open in Lab + ) : null}

{specimen.description}

- CLI - React API + Install + Import
- - + Supported states
    {specimen.states.split(", ").map((state) => (
  • {state}
  • @@ -175,7 +178,6 @@ function useSpecimens() { id: "mode-switch", title: "Mode switch", group: "Composer", - primitive: "native", description: "A pressed-state control for Chat, Do, and Plan authority.", states: "default, selected, focus, disabled", @@ -187,7 +189,6 @@ function useSpecimens() { id: "send-control", title: "Send control", group: "Composer", - primitive: "Base UI", description: "The surface's one filled action, with a stable stop state.", states: "ready, running, disabled", @@ -197,7 +198,6 @@ function useSpecimens() { id: "completion-palette", title: "Completion palette", group: "Composer", - primitive: "Base UI Menu", description: "Keyboard-ready slash commands in a collision-aware overlay.", states: "closed, open, focused, disabled", @@ -207,7 +207,6 @@ function useSpecimens() { id: "attachment-chip", title: "Attachment chip", group: "Composer", - primitive: "native", description: "A file attachment with explicit operational state.", states: "ready, uploading, failed", preview: ( @@ -226,7 +225,6 @@ function useSpecimens() { id: "metric-display", title: "Metric display", group: "Run rail", - primitive: "native", description: "Comparable run figures in stable tabular numeric typography.", states: "neutral, success, warning, information", @@ -242,7 +240,6 @@ function useSpecimens() { id: "plan-row", title: "Plan row", group: "Run rail", - primitive: "native", description: "A task step whose icon, text treatment, and label carry state.", states: "pending, active, complete, blocked", @@ -262,7 +259,6 @@ function useSpecimens() { id: "activity-item", title: "Activity item", group: "Run rail", - primitive: "native", description: "One append-only tool event using the canonical class mapping.", states: "read, write, exec, net, running", @@ -279,7 +275,6 @@ function useSpecimens() { id: "resource-row", title: "Resource row", group: "Run rail", - primitive: "native", description: "A direction-aware path row with operation and diff evidence.", states: "modified, added, deleted, renamed", @@ -304,7 +299,6 @@ function useSpecimens() { id: "tool-mix", title: "Tool mix", group: "Run rail", - primitive: "native", description: "A readable summary with one fixed read/exec/write/net order.", states: "populated, empty", @@ -323,7 +317,6 @@ function useSpecimens() { id: "failure-surface", title: "Failure surface", group: "Run rail", - primitive: "Base UI Button", description: "A durable failure receipt with quiet output and explicit next moves.", states: "failed, retried", @@ -333,7 +326,6 @@ function useSpecimens() { id: "context-meter", title: "Context meter", group: "Run rail", - primitive: "native progress", description: "Window consumption with a fixed threshold and stable numbers.", states: "normal, warning, full", @@ -348,7 +340,6 @@ function useSpecimens() { id: "budget-pill", title: "Budget pill", group: "Run rail", - primitive: "native", description: "Spend against a limit with icon, text weight, and semantic tone.", states: "normal, warning, over", @@ -364,7 +355,6 @@ function useSpecimens() { id: "composer", title: "Composer block", group: "Blocks", - primitive: "composition", description: "The full intent-taking surface, built only from public modules.", states: "empty, ready, running, disabled", @@ -374,7 +364,6 @@ function useSpecimens() { id: "run-rail", title: "Run rail block", group: "Blocks", - primitive: "composition", description: "Metrics, activity, context, and budget as one operational report.", states: "populated, loading, empty, error", @@ -384,7 +373,6 @@ function useSpecimens() { id: "transcript-turn", title: "Transcript turn", group: "Blocks", - primitive: "composition", description: "Familiar identity and provenance lead an editorial response.", states: "streaming, complete, with artifacts", @@ -414,7 +402,6 @@ function useSpecimens() { id: "session-header", title: "Session header", group: "Blocks", - primitive: "composition", description: "The session task, branch, execution state, and spend in one line.", states: "pending, active, complete, blocked", @@ -435,18 +422,22 @@ function useSpecimens() { function Library({ specimens }: { specimens: Specimen[] }) { if (specimens.length === 0) { return ( -
    +
    -

    No matching specimens

    -

    Try a component name, block, state, or operational concept.

    +

    No matches found

    +

    Try a component like Composer, or a state like running.

    ); } return ( -
    +
    {groupOrder.map((group) => { const groupedSpecimens = specimens.filter( (specimen) => specimen.group === group, @@ -462,16 +453,9 @@ function Library({ specimens }: { specimens: Specimen[] }) {
    -

    - {detail.eyebrow} -

    {group}

    {detail.description}

    - - {groupedSpecimens.length}{" "} - {groupedSpecimens.length === 1 ? "specimen" : "specimens"} -
    {groupedSpecimens.map((specimen) => ( @@ -601,10 +585,7 @@ function CatalogNavigation({ data-group={group} key={group} > -

    - {group} - {items.length} -

    +

    {group}

      {items.map((specimen) => (
    • @@ -655,7 +636,6 @@ function CatalogNavigation({
      Install @opencoven/ui -

      Semantic source, package exports, and registry remain aligned.

      ); @@ -668,25 +648,40 @@ function App() { const [query, setQuery] = useState(""); const searchRef = useRef(null); const topbarRef = useRef(null); + const mainRef = useRef(null); const normalizedPath = window.location.pathname.replace(/\/+$/, "") || "/"; const isLab = normalizedPath === "/lab"; const specimens = useSpecimens(); + const normalizedQuery = query.trim().toLowerCase(); + const isSearching = normalizedQuery.length > 0; const filteredSpecimens = useMemo(() => { - const normalizedQuery = query.trim().toLowerCase(); return specimens.filter((specimen) => `${specimen.title} ${specimen.group} ${specimen.description} ${specimen.states}` .toLowerCase() .includes(normalizedQuery), ); - }, [query, specimens]); + }, [normalizedQuery, specimens]); const activeId = useCatalogLocation(filteredSpecimens, !isLab); const activeGroup = filteredSpecimens.find( (specimen) => specimen.id === activeId, )?.group; + function clearSearch() { + setQuery(""); + searchRef.current?.focus(); + } + useEffect(() => { document.documentElement.classList.toggle("dark", scheme === "dark"); document.documentElement.dataset.density = density; + document + .querySelector('meta[name="theme-color"]') + ?.setAttribute( + "content", + getComputedStyle(document.documentElement) + .getPropertyValue("--oc-bg") + .trim(), + ); try { localStorage.setItem("coven-ui:scheme", scheme); localStorage.removeItem("coven-ui:density"); @@ -731,47 +726,85 @@ function App() { return (
      - + { + if (isLab) { + event.preventDefault(); + mainRef.current?.focus({ preventScroll: true }); + } + }} + > Skip to specimens
      - - - -
      - {!isLab ? ( + +
      + {!isLab ? ( +
      setQuery(event.target.value)} + onKeyDown={(event) => { + if ( + event.key === "Escape" && + !event.nativeEvent.isComposing && + query + ) { + event.preventDefault(); + clearSearch(); + } + }} + aria-label="Search components" + aria-controls="component-catalog" + aria-keyshortcuts="Control+K Meta+K" placeholder="Search components…" - shortcut="⌘K" - className="specimen-search" + shortcut={query ? undefined : "⌘K"} /> - ) : null} + {query ? ( + + ) : null} +
      + ) : null} +
      @@ -793,7 +825,12 @@ function App() { /> ) : null} -
      +
      -

      - {isLab - ? "The component workbench" - : "Sixteen public building blocks"} -

      -

      - {isLab ? ( - <> - A little room to experiment. - - ) : ( - <> - Agent surfaces. -
      - Made tangible. - - )} -

      +

      {isLab ? "Component lab" : "Component library"}

      {isLab - ? "Six scenes. Real components. One focused canvas." - : "Try the interaction, then take the source. Composable inputs, execution evidence, and complete agent workflows."} + ? `${labScenes.length} interactive scenes, built from the library.` + : `${specimens.length} components and blocks. Preview, inspect, and reuse.`}

      -
      -
      -
      {isLab ? "Views" : "Specimens"}
      -
      {isLab ? "06" : "16"}
      -
      -
      -
      Schemes
      -
      02
      -
      -
      {isLab ? ( ) : ( - + <> +
      +

      + {isSearching + ? `${filteredSpecimens.length} ${filteredSpecimens.length === 1 ? "result" : "results"}` + : `All ${specimens.length} components`} +

      +
      + + )}
      diff --git a/apps/specimens/src/component-preview.tsx b/apps/specimens/src/component-preview.tsx index 853f3aa..27d5696 100644 --- a/apps/specimens/src/component-preview.tsx +++ b/apps/specimens/src/component-preview.tsx @@ -1,5 +1,4 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@opencoven/ui"; -import { Braces, Eye } from "lucide-react"; import { type ReactNode, useRef, useState } from "react"; import { CodeSnippet } from "./code-snippet"; @@ -35,16 +34,15 @@ function ComponentPreview({ className="specimen-preview__tabs" > - - - + Source
      {/* Keep the live component's dimensions and state behind its source. */} diff --git a/apps/specimens/src/coven-theme.css b/apps/specimens/src/coven-theme.css new file mode 100644 index 0000000..aa7188e --- /dev/null +++ b/apps/specimens/src/coven-theme.css @@ -0,0 +1,114 @@ +@font-face { + font-family: "Inter"; + font-style: normal; + font-weight: 100 900; + font-display: swap; + src: url("/fonts/inter/inter-latin.woff2") format("woff2"); +} + +@font-face { + font-family: "EB Garamond"; + font-style: normal; + font-weight: 400 700; + font-display: swap; + src: url("/fonts/eb-garamond/eb-garamond-latin.woff2") format("woff2"); +} + +@font-face { + font-family: "JetBrains Mono"; + font-style: normal; + font-weight: 400 700; + font-display: swap; + src: url("/fonts/jetbrains-mono/jetbrains-mono-latin.woff2") format("woff2"); +} + +/* Cave's Coven default typography/accents, with the docs UI's neutral surfaces. */ +:root[data-presentation="coven"] { + --oc-bg: #f5f5f5; + --oc-surface-1: #f1f1f1; + --oc-surface-2: #ededed; + --oc-surface-3: #e3e3e3; + --coven-panel: var(--oc-surface-1); + --oc-text: #0a0a0a; + --oc-text-muted: oklch(0.42 0.009 291); + --oc-text-subtle: color-mix(in oklch, var(--oc-text) 76%, transparent); + --oc-border: color-mix(in oklch, var(--oc-text) 12%, transparent); + --oc-border-strong: color-mix(in oklch, var(--oc-text) 60%, transparent); + --oc-presence: #6859ac; + --oc-on-presence: var(--oc-bg); + --oc-action: var(--oc-presence); + --oc-action-hover: color-mix(in oklch, var(--oc-presence) 88%, black); + --oc-on-action: var(--oc-on-presence); + --oc-focus: var(--oc-presence); + /* Strengthen Cave's light-mode state ink for small labels on docs panels. */ + --oc-success-text: color-mix( + in oklch, + oklch(0.52 0.16 158) 88%, + var(--oc-text) + ); + --oc-pending-text: color-mix( + in oklch, + oklch(0.58 0.16 78) 80%, + var(--oc-text) + ); + --oc-blocked-text: oklch(0.52 0.2 24); + --oc-evidence-text: var(--oc-presence); + --oc-success-bg: color-mix( + in oklch, + var(--oc-success-text) 8%, + var(--oc-surface-1) + ); + --oc-pending-bg: color-mix( + in oklch, + var(--oc-pending-text) 8%, + var(--oc-surface-1) + ); + --oc-blocked-bg: color-mix( + in oklch, + var(--oc-blocked-text) 8%, + var(--oc-surface-1) + ); + --oc-evidence-bg: color-mix( + in oklch, + var(--oc-presence) 8%, + var(--oc-surface-1) + ); + --oc-font-display: Inter, "SF Pro Display", system-ui, sans-serif; + --oc-font-reading: Inter, "SF Pro Text", system-ui, sans-serif; + --oc-font-mono: + "JetBrains Mono", "SFMono-Regular", ui-monospace, Menlo, Monaco, Consolas, + monospace; + --oc-leading-heading: 1.05; + --oc-shadow-1: none; + --oc-shadow-2: 0 8px 24px + color-mix(in oklch, oklch(0.45 0.01 291) 40%, transparent); + + --font-editorial: "EB Garamond", "Iowan Old Style", Georgia, serif; + --popover: #fafafa; + --accent: var(--oc-surface-2); + --input: var(--oc-border-strong); + --tool-read: var(--oc-evidence-text); + --tool-write: var(--oc-success-text); + --tool-exec: var(--oc-pending-text); + --tool-net: var(--oc-blocked-text); + --overlay: color-mix(in oklch, var(--oc-bg) 96%, transparent); +} + +:root[data-presentation="coven"].dark { + --oc-bg: #121212; + --oc-surface-1: #191919; + --oc-surface-2: #212121; + --oc-surface-3: #2c2c2c; + --oc-text: #ebebeb; + --oc-text-muted: oklch(0.66 0.01 291); + --oc-text-subtle: color-mix(in oklch, var(--oc-text) 72%, transparent); + --oc-border-strong: color-mix(in oklch, var(--oc-text) 48%, transparent); + --oc-presence: #9386d0; + --oc-on-presence: oklch(0.16 0.008 291); + --oc-action-hover: color-mix(in oklch, var(--oc-presence) 88%, white); + --oc-success-text: oklch(0.78 0.14 158); + --oc-pending-text: oklch(0.83 0.13 78); + --oc-blocked-text: oklch(0.74 0.18 24); + --oc-shadow-2: 0 8px 24px rgb(0 0 0 / 40%); + --popover: #1e1e1e; +} diff --git a/apps/specimens/src/lab-scenes.ts b/apps/specimens/src/lab-scenes.ts new file mode 100644 index 0000000..9a557a0 --- /dev/null +++ b/apps/specimens/src/lab-scenes.ts @@ -0,0 +1,53 @@ +import { + Activity, + FileCode2, + Layers3, + MessageSquare, + PenLine, + Wand2, +} from "lucide-react"; + +export const labScenes = [ + { + id: "composer", + title: "Composer", + subtitle: "From intent to action", + icon: PenLine, + source: "composer", + }, + { + id: "run-rail", + title: "Run rail", + subtitle: "Every step, accounted for", + icon: Activity, + source: "run-rail", + }, + { + id: "messages", + title: "Messages", + subtitle: "A familiar voice, with evidence", + icon: MessageSquare, + source: "transcript-turn", + }, + { + id: "context", + title: "Context", + subtitle: "The right files, within reach", + icon: FileCode2, + source: "resource-row", + }, + { + id: "actions", + title: "Actions", + subtitle: "Small controls, clear intent", + icon: Wand2, + source: "completion-palette", + }, + { + id: "cards", + title: "Cards", + subtitle: "Outcomes you can open", + icon: Layers3, + source: "session-header", + }, +] as const; diff --git a/apps/specimens/src/lab.css b/apps/specimens/src/lab.css index f75764b..3fc9853 100644 --- a/apps/specimens/src/lab.css +++ b/apps/specimens/src/lab.css @@ -11,6 +11,11 @@ flex: none; } +.specimen-app--lab .specimen-topbar__inner { + grid-template-columns: minmax(0, 1fr) auto; + grid-template-areas: "leading actions"; +} + .specimen-app--lab .specimen-shell { display: block; flex: 1; @@ -44,31 +49,19 @@ .specimen-app--lab .specimen-hero h1 { max-width: none; - font-size: clamp(1.6rem, 3vw, 2.5rem); + font-size: clamp(1.5rem, 3vw, 1.75rem); } -.specimen-app--lab .specimen-hero__copy > p:last-child, -.specimen-app--lab .specimen-stats { +.specimen-app--lab .specimen-hero__copy > p:last-child { display: none; } .assembled-lab { - --scene-color: var(--presence); display: flex; flex-direction: column; min-width: 0; min-height: 0; - border: 1px solid color-mix(in srgb, var(--scene-color) 28%, var(--border)); -} - -.assembled-lab[data-tone="information"] { - --scene-color: var(--information); -} -.assembled-lab[data-tone="success"] { - --scene-color: var(--success); -} -.assembled-lab[data-tone="warning"] { - --scene-color: var(--warning); + border: 1px solid var(--border); } .lab-scene-header { @@ -95,10 +88,10 @@ height: 2.5rem; flex: none; place-items: center; - border: 1px solid color-mix(in srgb, var(--scene-color) 28%, var(--border)); + border: 1px solid var(--border); border-radius: var(--radius-2); - background: color-mix(in srgb, var(--scene-color) 8%, var(--card)); - color: var(--scene-color); + background: var(--muted); + color: var(--foreground); } .lab-scene-icon svg { @@ -107,9 +100,9 @@ } .lab-scene-heading h2 { margin: 0; - color: var(--scene-color); + color: var(--foreground); font-size: 1rem; - font-weight: 700; + font-weight: 500; } .lab-scene-heading p { margin: 0.2rem 0 0; @@ -165,7 +158,7 @@ min-height: 100%; align-content: safe center; padding: clamp(1rem, 3vh, 2.5rem) clamp(1rem, 4vw, 4rem); - background: var(--oc-surface-2); + background: var(--background); } .lab-scene-surface { @@ -179,7 +172,7 @@ } .lab-scene-surface [data-slot="composer"] { - box-shadow: var(--elevation-3); + box-shadow: none; } .lab-scene-surface .demo-status { text-align: center; @@ -200,7 +193,7 @@ overflow: visible; border-block: 1px solid var(--border); padding: 0.625rem; - background: var(--card); + background: var(--background); } .assembled-lab .assembled-lab__tabs { @@ -230,10 +223,14 @@ height: 1.125rem; } .assembled-lab__tabs [role="tab"][data-active] { - border-color: color-mix(in srgb, var(--scene-color) 35%, var(--border)); - background: color-mix(in srgb, var(--scene-color) 10%, var(--card)); - color: var(--scene-color); - box-shadow: inset 0 -2px 0 var(--scene-color); + border-color: transparent; + background: var(--muted); + color: var(--foreground); + box-shadow: none; +} + +.assembled-lab__tabs [role="tab"][data-active] svg { + color: var(--foreground); } .lab-footer { @@ -252,7 +249,7 @@ display: inline-flex; align-items: center; gap: 0.375rem; - color: var(--scene-color); + color: var(--muted-foreground); text-decoration: none; } .lab-footer a:hover { @@ -401,6 +398,10 @@ margin-block-start: 0.5rem; } + .lab-message-stack .specimen-command { + padding-block: 0.5rem; + } + .lab-scene-surface [data-slot="run-rail"] { gap: 0.75rem; } @@ -417,35 +418,18 @@ margin: 0; font-size: 1.5rem; } - - .specimen-app--lab .specimen-hero .specimen-kicker { - display: none; - } } @media (max-width: 68rem) { .specimen-app--lab .specimen-topbar__inner { - flex-wrap: nowrap; - padding-block: 0.5rem; - gap: 0.5rem; - } - .specimen-app--lab .specimen-topbar__actions { - flex: 0 1 auto; - width: auto; + padding-block: 0.25rem; } .specimen-app--lab .specimen-brand > span:last-child { display: none; } - .specimen-app--lab .surface-switcher { - margin-inline-start: 0; - margin-inline-end: auto; - } } @media (max-width: 48rem) { - .specimen-app--lab .scheme-control span { - display: none; - } .specimen-app--lab .specimen-hero { padding-block-end: 0.75rem; } diff --git a/apps/specimens/src/lab.tsx b/apps/specimens/src/lab.tsx index f467df4..4a6dad8 100644 --- a/apps/specimens/src/lab.tsx +++ b/apps/specimens/src/lab.tsx @@ -9,74 +9,33 @@ import { TabsTrigger, TranscriptTurn, } from "@opencoven/ui"; -import { - Activity, - ArrowLeft, - ArrowRight, - FileCode2, - Layers3, - MessageSquare, - PenLine, - Wand2, -} from "lucide-react"; -import { useRef, useState } from "react"; +import { ArrowLeft, ArrowRight, FileCode2, Wand2 } from "lucide-react"; +import { useEffect, useRef, useState } from "react"; import { ComposerDemo, RunRailDemo } from "./block-demos"; import { CodeSnippet, HighlightedCode } from "./code-snippet"; +import { labScenes as scenes } from "./lab-scenes"; -const scenes = [ - { - id: "composer", - title: "Composer", - subtitle: "From intent to action", - icon: PenLine, - source: "composer", - tone: "presence", - }, - { - id: "run-rail", - title: "Run rail", - subtitle: "Every step, accounted for", - icon: Activity, - source: "run-rail", - tone: "information", - }, - { - id: "messages", - title: "Messages", - subtitle: "A familiar voice, with evidence", - icon: MessageSquare, - source: "transcript-turn", - tone: "presence", - }, - { - id: "context", - title: "Context", - subtitle: "The right files, within reach", - icon: FileCode2, - source: "resource-row", - tone: "success", - }, - { - id: "actions", - title: "Actions", - subtitle: "Small controls, clear intent", - icon: Wand2, - source: "completion-palette", - tone: "warning", - }, - { - id: "cards", - title: "Cards", - subtitle: "Outcomes you can open", - icon: Layers3, - source: "session-header", - tone: "information", - }, -] as const; +function sceneIndexFromLocation() { + return Math.max( + 0, + scenes.findIndex((scene) => `#${scene.id}` === window.location.hash), + ); +} + +function replaceSceneHash(index: number) { + const hash = `#${scenes[index]!.id}`; + if (window.location.hash !== hash) { + window.history.replaceState( + window.history.state, + "", + `${window.location.pathname}${window.location.search}${hash}`, + ); + } +} function Lab({ density }: { density: "default" | "compact" }) { - const [activeIndex, setActiveIndex] = useState(0); + const [activeIndex, setActiveIndex] = useState(sceneIndexFromLocation); const [actionStatus, setActionStatus] = useState( "Choose an action to try its local preview.", ); @@ -88,6 +47,18 @@ function Lab({ density }: { density: "default" | "compact" }) { const navigate = (offset: number) => setActiveIndex((index) => (index + offset + scenes.length) % scenes.length); + useEffect(() => { + const onHashChange = () => { + const index = sceneIndexFromLocation(); + setActiveIndex(index); + replaceSceneHash(index); + }; + window.addEventListener("hashchange", onHashChange); + return () => window.removeEventListener("hashchange", onHashChange); + }, []); + + useEffect(() => replaceSceneHash(activeIndex), [activeIndex]); + const views = { composer: , "run-rail": , @@ -251,7 +222,6 @@ function Lab({ density }: { density: "default" | "compact" }) { id="assembled-lab" aria-label="Component scenes" aria-roledescription="carousel" - data-tone={scene.tone} >
      @@ -266,7 +236,8 @@ function Lab({ density }: { density: "default" | "compact" }) {
      {scene.title}, scene - {String(activeIndex + 1).padStart(2, "0")} / 06 + {String(activeIndex + 1).padStart(2, "0")} /{" "} + {String(scenes.length).padStart(2, "0")}
diff --git a/apps/specimens/src/main.tsx b/apps/specimens/src/main.tsx index dbd0dfe..79b400a 100644 --- a/apps/specimens/src/main.tsx +++ b/apps/specimens/src/main.tsx @@ -2,6 +2,7 @@ import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import "@opencoven/ui/globals.css"; +import "./coven-theme.css"; import "./specimens.css"; import "./specimens-fixes.css"; import "./lab.css"; diff --git a/apps/specimens/src/specimens-fixes.css b/apps/specimens/src/specimens-fixes.css index 4b704de..2364bf0 100644 --- a/apps/specimens/src/specimens-fixes.css +++ b/apps/specimens/src/specimens-fixes.css @@ -1,28 +1,4 @@ /* Responsive guards for the catalog and its source panels. */ -@media (max-width: 68rem) { - .specimen-topbar__inner { - display: flex; - flex-wrap: wrap; - } - - .surface-switcher { - margin-inline-start: auto; - } - - .specimen-topbar__actions { - display: flex; - flex: 1 0 100%; - flex-wrap: wrap; - } - - .specimen-search { - width: auto; - max-width: none; - min-width: 7rem; - flex: 1 1 10rem; - } -} - @media (max-width: 48rem) { html { scroll-padding-top: 1rem; @@ -44,14 +20,11 @@ .specimen-hero, .specimen-hero__copy, - .specimen-stats, - .specimen-stats div, .specimen-rail > * { min-width: 0; max-width: 100%; } - .specimen-stats :where(dt, dd), .specimen-rail__package { overflow-wrap: anywhere; } @@ -72,8 +45,7 @@ grid-template-columns: repeat(2, minmax(5.25rem, 1fr)); gap: 0; overflow-x: auto; - border-block-end: 1px solid var(--border); - padding: 0 0.75rem; + padding: 0; scrollbar-width: thin; } @@ -92,18 +64,18 @@ .specimen-card__header { min-height: 0; - padding: 16px; + padding: 0 0 16px; overflow-wrap: anywhere; } .specimen-preview { - margin-inline: 16px; + margin-inline: 0; } .specimen-stage { min-width: 0; min-height: 10rem; - justify-items: stretch; + justify-items: center; padding: 16px; } @@ -121,7 +93,6 @@ .specimen-stage > * { box-sizing: border-box; - width: 100%; min-width: 0; max-width: 100%; } @@ -140,7 +111,7 @@ .specimen-documentation { min-width: 0; max-width: 100%; - padding: 0.75rem 1rem 1rem; + padding: 0.75rem 0 0; } } @@ -161,16 +132,12 @@ } .specimen-hero h1 { - font-size: 2rem; + font-size: 1.5rem; } .catalog-group__header { gap: 0.375rem; } - - .catalog-group__count { - display: none; - } } @media (prefers-reduced-motion: reduce) { diff --git a/apps/specimens/src/specimens.css b/apps/specimens/src/specimens.css index 93dbcf1..54d59b0 100644 --- a/apps/specimens/src/specimens.css +++ b/apps/specimens/src/specimens.css @@ -1,6 +1,6 @@ html { min-width: 320px; - --specimen-topbar-height: 3.75rem; + --specimen-topbar-height: 3.25rem; scroll-padding-top: calc(var(--specimen-topbar-height) + 1rem); } @@ -34,21 +34,33 @@ body { inset-block-start: 0; z-index: 50; border-block-end: 1px solid var(--border); - background: color-mix(in srgb, var(--background) 92%, transparent); - backdrop-filter: blur(18px); + background: var(--background); } .specimen-topbar__inner { - display: flex; - flex-wrap: wrap; - min-height: 3.75rem; + display: grid; + min-height: 3.25rem; max-width: 1680px; + grid-template-columns: minmax(0, 1fr) minmax(0, min(22rem, 40vw)) minmax( + 0, + 1fr + ); + grid-template-areas: "leading search actions"; align-items: center; gap: 0.75rem; margin-inline: auto; padding-inline: clamp(1rem, 3vw, 1.5rem); } +.specimen-topbar__leading { + display: flex; + grid-area: leading; + min-width: 0; + flex-wrap: wrap; + align-items: center; + gap: var(--oc-space-3); +} + .specimen-brand { display: inline-flex; align-items: center; @@ -60,47 +72,39 @@ body { .specimen-brand__mark { display: grid; - width: 2rem; - height: 2rem; + width: 1.5rem; + height: 1.5rem; flex: none; place-items: center; color: var(--foreground); } .specimen-brand__mark > span { - width: 1.875rem; - height: 1.875rem; + width: 1.5rem; + height: 1.5rem; background: currentColor; mask: url("/opencoven-mark.svg") center / contain no-repeat; } .specimen-brand > span:last-child { - display: grid; font-size: 0.875rem; - font-weight: 750; + font-weight: 600; letter-spacing: -0.015em; - line-height: 1.1; + line-height: 1.5; font-family: var(--font-display); } -.specimen-brand small { - margin-block-start: 0.2rem; - color: var(--muted-foreground); - font-family: var(--font-numeric); - font-size: 0.625rem; - font-weight: 600; - letter-spacing: 0.08em; - text-transform: uppercase; -} - .surface-switcher { display: inline-flex; + min-width: 0; + max-width: 100%; + flex-wrap: wrap; align-items: center; gap: 0.125rem; - padding: 0.1875rem; - border: 1px solid var(--border); + padding: 0; + border: 0; border-radius: var(--radius-2); - background: var(--card); + background: transparent; } .surface-switcher a { @@ -108,9 +112,9 @@ body { border-radius: calc(var(--radius-2) - 2px); background: transparent; color: var(--muted-foreground); - padding: 0.375rem 0.75rem; - font-size: 0.75rem; - font-weight: 700; + padding: 0.375rem 0.5rem; + font-size: 0.8125rem; + font-weight: 500; line-height: 1.25rem; text-decoration: none; white-space: nowrap; @@ -122,15 +126,14 @@ body { } .surface-switcher a[aria-current="page"] { - background: var(--foreground); - color: var(--background); - box-shadow: var(--elevation-1); + background: transparent; + color: var(--foreground); + font-weight: 600; } .specimen-topbar__actions { display: flex; - flex: 1 1 34rem; - flex-wrap: wrap; + grid-area: actions; min-width: 0; align-items: center; justify-content: flex-end; @@ -138,13 +141,29 @@ body { } .specimen-search { - width: min(20rem, 34vw); - max-width: 20rem; - flex: 1 1 16rem; + position: relative; + grid-area: search; + width: 100%; + max-width: 22rem; + min-width: 0; + justify-self: center; +} + +.specimen-search input { + height: 2.25rem; + font-size: 0.8125rem; +} + +.specimen-search input::-webkit-search-cancel-button { + -webkit-appearance: none; } .scheme-control { flex: none; + width: 2.25rem; + height: 2.25rem; + min-height: 2.25rem; + padding: 0; white-space: nowrap; } @@ -167,19 +186,19 @@ body { align-self: start; height: calc(100vh - var(--specimen-topbar-height)); overflow-y: auto; - border-inline-end: 1px solid var(--border); - background: color-mix(in srgb, var(--card) 72%, var(--background)); + border-inline-end: 0; + background: var(--background); padding: 28px 16px 40px 20px; scrollbar-width: thin; } .specimen-kicker { margin: 0; - color: var(--presence); - font-size: 0.625rem; - font-weight: 750; - letter-spacing: 0.14em; - text-transform: uppercase; + color: var(--muted-foreground); + font-family: var(--font-display); + font-size: 0.75rem; + font-weight: 500; + letter-spacing: 0; } .specimen-rail__nav { @@ -188,29 +207,18 @@ body { } .specimen-rail__group { - --nav-color: var(--presence); min-width: 0; } -.specimen-rail__group[data-group="Run rail"] { - --nav-color: var(--information); -} - -.specimen-rail__group[data-group="Blocks"] { - --nav-color: var(--success); -} - .specimen-rail__group h2 { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin: 0 12px 8px; - color: var(--nav-color); - font-size: 0.625rem; - font-weight: 650; - letter-spacing: 0.1em; - text-transform: uppercase; + color: var(--foreground); + font-size: 0.875rem; + font-weight: 500; } .specimen-rail__group ul { @@ -221,12 +229,12 @@ body { .specimen-rail__nav a { display: block; - border-inline-start: 2px solid transparent; - border-radius: 0 var(--radius-1) var(--radius-1) 0; + border: 0; + border-radius: var(--radius-2); color: var(--muted-foreground); padding: 6px 12px; - font-size: 0.84375rem; - font-weight: 450; + font-size: 0.875rem; + font-weight: 400; line-height: 1.4; text-decoration: none; overflow-wrap: anywhere; @@ -238,15 +246,9 @@ body { } .specimen-rail__nav a[aria-current] { - border-inline-start-color: var(--nav-color); - background: color-mix(in srgb, var(--nav-color) 9%, var(--card)); + background: var(--muted); color: var(--foreground); - font-weight: 650; -} - -.specimen-rail__nav small { - color: color-mix(in srgb, var(--muted-foreground) 76%, transparent); - font-size: 0.625rem; + font-weight: 500; } .specimen-mobile-nav { @@ -288,9 +290,8 @@ body { } .specimen-toc a { - border-inline-start: 1px solid var(--border); color: var(--muted-foreground); - padding: 6px 12px; + padding: 6px 0; font-size: 0.75rem; text-decoration: none; overflow-wrap: anywhere; @@ -298,8 +299,7 @@ body { .specimen-toc a:hover, .specimen-toc a[aria-current] { - border-inline-start-color: var(--presence); - color: var(--presence); + color: var(--foreground); } .specimen-rail__package { @@ -318,13 +318,6 @@ body { white-space: nowrap; } -.specimen-rail__package p { - margin: 0.625rem 0 0; - color: var(--muted-foreground); - font-size: 0.7rem; - line-height: 1.5; -} - .specimen-main { min-width: 0; } @@ -336,126 +329,98 @@ body { } [data-density="compact"] .specimen-main__inner { - padding-block-start: var(--oc-space-8); + padding-block-start: var(--oc-space-6); } .specimen-hero { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 1.5rem; - margin-block-end: clamp(2rem, 4vw, 3rem); - border-block-end: 1px solid var(--border); - padding-block-end: clamp(1.5rem, 3vw, 2rem); + display: block; + margin-block-end: var(--oc-space-6); + border: 0; + padding: 0; } .specimen-hero__copy { min-width: 0; - flex: 1 1 24rem; } .specimen-hero h1 { - max-width: 19ch; + max-width: 22ch; overflow-wrap: anywhere; - margin: 0.5rem 0 0; + margin: 0; font-family: var(--font-display); - font-size: var(--oc-type-3); - font-weight: 700; - letter-spacing: var(--oc-tracking-heading); - line-height: var(--oc-leading-heading); + font-size: 1.75rem; + font-weight: 600; + letter-spacing: -0.025em; + line-height: 1.2; color: var(--foreground); text-wrap: balance; } -.specimen-hero h1 em { - color: var(--presence); - font-style: normal; -} - -.specimen-stats div:nth-child(2) dd { - color: var(--information); -} - .specimen-hero__copy > p:last-child { max-width: var(--oc-reading-measure); - margin: var(--oc-space-4) 0 0; + margin: var(--oc-space-2) 0 0; color: var(--muted-foreground); font-size: var(--oc-type--1); line-height: var(--oc-leading-reading); } -.specimen-stats { +.catalog { display: grid; - flex: 0 1 16rem; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 0.5rem; - margin: 0; + gap: clamp(2.75rem, 5vw, 4.5rem); } -.specimen-stats div { - min-width: 0; - border-inline-start: 1px solid var(--border); - padding-inline-start: 0.875rem; +.catalog-results { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + gap: var(--oc-space-2) var(--oc-space-4); } -.specimen-stats dt { - color: var(--muted-foreground); - font-size: 0.625rem; - font-weight: 700; - letter-spacing: 0.08em; - text-transform: uppercase; +.catalog-results[data-active] { + margin-block-end: var(--oc-space-4); } -.specimen-stats dd { - margin: 0.25rem 0 0; - color: var(--foreground); - font-size: 1.25rem; - font-weight: 650; +.catalog-results__count { + margin: 0; + color: var(--muted-foreground); + font-size: var(--oc-type--1); } -.catalog { - display: grid; - gap: clamp(2.75rem, 5vw, 4.5rem); +.catalog-clear-search { + position: absolute; + inset-inline-end: 0.125rem; + inset-block-start: 50%; + width: 2rem; + height: 2rem; + min-height: 2rem; + padding: 0; + transform: translateY(-50%); } .catalog-group { - --section-color: var(--presence); scroll-margin-block-start: 0; } -#group-run-rail { - --section-color: var(--information); -} - -#group-blocks { - --section-color: var(--success); -} - .catalog-group__header { display: flex; align-items: end; justify-content: space-between; gap: 2rem; - margin-block-end: 0.875rem; -} - -.catalog-group__eyebrow { - margin: 0; - color: var(--section-color); - font-size: 0.625rem; - font-weight: 750; - letter-spacing: 0.14em; - text-transform: uppercase; + margin-block-end: var(--oc-space-6); + border-block-end: 1px solid var(--border); + padding-block-end: var(--oc-space-4); } .catalog-group__header h2 { max-width: 34rem; - margin: 0.35rem 0 0; - font-size: clamp(1.25rem, 2vw, 1.6rem); - font-weight: 700; + margin: 0; + font-family: var(--font-display); + font-size: 1.25rem; + font-weight: 600; letter-spacing: -0.025em; line-height: 1.2; - color: var(--section-color); + color: var(--foreground); text-wrap: balance; } @@ -463,18 +428,10 @@ body { max-width: 52ch; margin: 0.35rem 0 0; color: var(--muted-foreground); - font-size: 0.75rem; + font-size: 0.875rem; line-height: 1.5; } -.catalog-group__count { - flex: none; - color: var(--muted-foreground); - font-size: 0.6875rem; - letter-spacing: 0.04em; - text-transform: uppercase; -} - .specimen-grid { display: grid; grid-template-columns: minmax(0, 1fr); @@ -485,66 +442,113 @@ body { display: flex; flex-direction: column; min-width: 0; - overflow: hidden; + overflow: visible; scroll-margin-block-start: 0; - border: 1px solid var(--border); - border-radius: var(--radius-4); - background: var(--card); + border: 0; + background: transparent; color: var(--card-foreground); - box-shadow: var(--elevation-1); - transition: - border-color var(--motion-standard) var(--motion-ease), - box-shadow var(--motion-standard) var(--motion-ease), - translate var(--motion-standard) var(--motion-ease); -} - -.specimen-card:hover, -.specimen-card:focus-within { - border-color: color-mix(in srgb, var(--section-color) 48%, var(--border)); - box-shadow: var(--elevation-2); } .specimen-card__header { - min-height: 8.25rem; - padding: var(--oc-space-6) var(--oc-space-6) var(--oc-space-4); + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + align-items: center; + gap: var(--oc-space-2) var(--oc-space-3); + min-height: 0; + padding: 0 0 var(--oc-space-4); } .specimen-card__meta { display: flex; flex-wrap: wrap; - align-items: center; - justify-content: space-between; - gap: 1rem; + min-width: 0; + align-items: baseline; + gap: var(--oc-space-2) var(--oc-space-3); } .specimen-card__kind { display: inline-flex; align-items: center; gap: 0.45rem; - color: var(--section-color); - font-size: 0.625rem; - font-weight: 700; - letter-spacing: 0.12em; - text-transform: uppercase; + color: var(--muted-foreground); + font-size: 0.6875rem; + font-weight: 500; } -.specimen-card__kind svg { +.specimen-card__title { + min-width: 0; + max-width: 100%; + margin: 0; + color: var(--foreground); + font-family: var(--font-display); + font-size: 1.25rem; + font-weight: 600; + letter-spacing: var(--oc-tracking-heading); +} + +.specimen-lab-link { + display: inline-flex; + min-height: 2.5rem; + align-items: center; + justify-self: end; + gap: var(--oc-space-1); + border-radius: var(--radius-1); + color: var(--muted-foreground); + font-size: 0.75rem; + text-decoration: none; +} + +.specimen-lab-link svg { width: 0.875rem; height: 0.875rem; + flex: none; } -.specimen-card__title { - margin: 0.75rem 0 0; - color: color-mix(in srgb, var(--section-color) 55%, var(--foreground)); - font-family: var(--font-display); - font-size: var(--oc-type-1); - font-weight: 650; - letter-spacing: var(--oc-tracking-heading); +.specimen-lab-link:hover, +.specimen-lab-link:focus-visible { + color: var(--foreground); + text-decoration: underline; + text-underline-offset: 0.2em; +} + +.specimen-permalink { + display: inline-flex; + max-width: 100%; + align-items: baseline; + gap: var(--oc-space-2); + border-radius: var(--radius-1); + color: inherit; + text-decoration: none; +} + +.specimen-permalink span { + min-width: 0; + overflow-wrap: anywhere; +} + +.specimen-permalink svg { + width: 0.65em; + height: 0.65em; + flex: none; + color: var(--muted-foreground); +} + +.specimen-permalink:hover span, +.specimen-permalink:focus-visible span { + text-decoration: underline; + text-decoration-thickness: 1px; + text-underline-offset: 0.2em; +} + +.specimen-permalink:hover svg, +.specimen-permalink:focus-visible svg { + color: var(--foreground); } .specimen-card__description { - max-width: 46ch; - margin: var(--oc-space-2) 0 0; + grid-column: 1 / -1; + max-width: var(--oc-reading-measure); + margin: 0; color: var(--muted-foreground); font-size: var(--oc-type--1); line-height: var(--oc-leading-reading); @@ -556,28 +560,25 @@ body { flex: 1; flex-direction: column; min-width: 0; - margin: 0 var(--oc-space-4) var(--oc-space-4); - overflow: hidden; - border: 1px solid color-mix(in srgb, var(--section-color) 24%, var(--border)); - border-radius: var(--radius-2); - background: var(--oc-surface-2); - box-shadow: inset 0 1px 0 - color-mix(in srgb, var(--section-color) 18%, transparent); + margin: 0; + overflow: visible; + border: 0; + background: transparent; } .specimen-preview__tabs { flex: 1; min-width: 0; - gap: 0; + gap: var(--oc-space-3); } .specimen-view-tabs { width: 100%; - gap: var(--oc-space-1); + gap: var(--oc-space-4); border-block-end: 1px solid var(--border); border-radius: 0; - background: var(--card); - padding: var(--oc-space-2); + background: transparent; + padding: 0; justify-content: flex-start; overflow-x: auto; scrollbar-width: thin; @@ -588,20 +589,23 @@ body { min-width: 0; min-height: 2.75rem; gap: var(--oc-space-2); - padding-inline: var(--oc-space-4); - font-size: 0.8125rem; + padding-inline: var(--oc-space-2); + font-size: 0.875rem; + font-weight: 500; + color: var(--muted-foreground); + border: 0; + border-radius: 0; } .specimen-view-tabs [role="tab"][data-active] { - border-color: var(--border); - background: var(--oc-surface-2); - color: var(--presence); - box-shadow: inset 0 -2px 0 var(--presence); + background: transparent; + color: var(--foreground); + box-shadow: none; } -.specimen-view-tabs svg { - width: 0.875rem; - height: 0.875rem; +.specimen-view-tabs [role="tab"][data-active]::after { + inset-block-end: 0; + background: var(--foreground); } .specimen-view-tabs [role="tab"]:focus-visible { @@ -613,6 +617,10 @@ body { display: grid; flex: 1; min-width: 0; + overflow: hidden; + border: 1px solid var(--border); + border-radius: var(--radius-2); + background: var(--background); } .specimen-live-panel { @@ -647,6 +655,8 @@ body { flex: 1; min-height: 0; max-height: none; + border: 0; + background: var(--card); overscroll-behavior: contain; } @@ -657,7 +667,7 @@ body { .specimen-stage { display: grid; flex: 1; - min-height: 14rem; + min-height: 10rem; align-content: center; justify-items: center; padding: var(--oc-space-6); @@ -676,7 +686,7 @@ body { .demo-status { margin: 0.75rem 0 0; color: var(--muted-foreground); - font-size: 0.6875rem; + font-size: 0.75rem; line-height: 1.5; } @@ -700,10 +710,10 @@ body { .specimen-documentation { display: grid; - min-height: 7.5rem; + min-height: 0; align-content: start; gap: var(--oc-space-3); - padding: var(--oc-space-3) var(--oc-space-4) var(--oc-space-4); + padding: var(--oc-space-3) 0 0; font-size: 0.8125rem; line-height: 1.6; } @@ -711,8 +721,7 @@ body { .specimen-states { display: grid; gap: var(--oc-space-2); - border-block-start: 1px solid var(--border); - padding: var(--oc-space-4); + padding: var(--oc-space-3) 0 0; font-size: 0.6875rem; } @@ -721,70 +730,67 @@ body { align-items: center; gap: var(--oc-space-2); color: var(--muted-foreground); - font-weight: 600; + font-weight: 500; } -.specimen-states svg { - width: 0.875rem; - height: 0.875rem; -} .specimen-states ul { display: flex; flex-wrap: wrap; - gap: var(--oc-space-2); + gap: var(--oc-space-3); margin: 0; padding: 0; list-style: none; } .specimen-states li { - border: 1px solid var(--border); - border-radius: var(--radius-1); - background: var(--oc-surface-2); + border: 0; + background: transparent; color: var(--oc-text-muted); - padding: var(--oc-space-1) var(--oc-space-2); + padding: 0; } .specimen-card > [data-slot="tabs"] { flex: none; gap: 0; - border-block-start: 1px solid var(--border); - background: color-mix(in srgb, var(--background) 70%, var(--card)); + margin-block-start: var(--oc-space-4); + background: transparent; } .specimen-code-tabs { + width: 100%; + justify-content: flex-start; + gap: var(--oc-space-4); margin: 0; - padding: 0.375rem 0.75rem; + border-block-end: 1px solid var(--border); + padding: 0; } -@media (min-width: 64rem) { - .specimen-card__header { - display: grid; - grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr); - align-items: center; - column-gap: var(--oc-space-8); - row-gap: var(--oc-space-4); - padding: var(--oc-space-6); - } +.specimen-code-tabs [role="tab"] { + flex: none; +} - .specimen-card__meta { - grid-column: 1 / -1; - } +.specimen-documentation .specimen-code-snippet { + gap: 0; + overflow: hidden; + border: 1px solid var(--border); + border-radius: var(--radius-2); + background: var(--card); +} - .specimen-card__title, - .specimen-card__description { - margin: 0; - } +.specimen-documentation .specimen-code-snippet__header { + border-block-end: 1px solid var(--border); + padding-inline: var(--oc-space-3); +} - .specimen-card__description { - max-width: 60ch; - justify-self: end; - } +.specimen-documentation .specimen-command { + border: 0; + background: var(--card); +} - .specimen-preview { - margin-inline: var(--oc-space-6); - margin-block-end: var(--oc-space-6); - } +.specimen-documentation .code-copy-error { + padding: 0 var(--oc-space-3) var(--oc-space-3); +} +@media (min-width: 64rem) { .specimen-stage { padding: var(--oc-space-8); } @@ -802,44 +808,26 @@ body { grid-column: 1 / -1; } - .specimen-card > [data-slot="tabs"] { - display: grid; - grid-template-columns: auto minmax(0, 1fr); - align-items: start; - gap: var(--oc-space-6); - padding-inline: var(--oc-space-6); - } - - .specimen-code-tabs { - margin-block-start: var(--oc-space-4); - padding-inline: 0; - } - - .specimen-documentation { - min-height: 0; - padding: var(--oc-space-4) 0; - } - .specimen-states { display: flex; flex-wrap: wrap; align-items: center; gap: var(--oc-space-4); - padding-inline: var(--oc-space-6); } } .specimen-code-tabs [role="tab"] { min-height: 2.75rem; - font-size: 0.6875rem; + font-size: 0.875rem; } .specimen-code-tabs [role="tab"][data-active] { - color: var(--section-color); + color: var(--foreground); } .specimen-code-tabs [role="tab"][data-active]::after { - background: var(--section-color); + inset-block-end: 0; + background: var(--foreground); } .specimen-documentation p { @@ -863,10 +851,8 @@ body { align-items: center; gap: 0.4rem; color: var(--muted-foreground); - font-size: 0.6rem; - font-weight: 750; - letter-spacing: 0.08em; - text-transform: uppercase; + font-size: 0.6875rem; + font-weight: 500; } .specimen-code-snippet__header svg { @@ -876,7 +862,7 @@ body { .specimen-code-snippet__header button { min-height: 2.75rem; - font-size: 0.625rem; + font-size: 0.75rem; } .code-copy-error { @@ -896,7 +882,7 @@ body { background: var(--background); color: var(--foreground); padding: 0.75rem; - font-size: 0.7rem; + font-size: 0.75rem; line-height: 1.6; white-space: pre-wrap; word-break: break-word; @@ -966,9 +952,9 @@ p > .hljs { } .specimen-install-meta span { - color: var(--presence); + color: var(--muted-foreground); font-size: 0.625rem; - font-weight: 750; + font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase; } @@ -993,12 +979,12 @@ p > .hljs { .catalog-empty { display: grid; - min-height: 24rem; + min-height: min(20rem, 320px); place-items: center; align-content: center; border: 1px dashed var(--border); border-radius: var(--radius-3); - padding: 2rem; + padding: min(var(--oc-space-8), 32px); text-align: center; } @@ -1033,9 +1019,9 @@ p > .hljs { scroll-margin-block-start: 0; border: 1px solid var(--border); border-radius: var(--radius-3); - background: var(--card); + background: var(--background); color: var(--card-foreground); - box-shadow: var(--elevation-2); + box-shadow: none; } .assembled-lab__nav { @@ -1091,23 +1077,48 @@ p > .hljs { @media (max-width: 68rem) { .specimen-topbar__inner { - grid-template-columns: auto 1fr; - padding-block: 0.75rem; + grid-template-columns: minmax(0, 1fr) auto; + grid-template-areas: "leading actions" "search search"; + gap: var(--oc-space-1) var(--oc-space-3); + padding-block: var(--oc-space-1) var(--oc-space-2); } - .surface-switcher { - justify-self: end; + .specimen-topbar__leading { + min-height: 2.75rem; } - .specimen-topbar__actions { - display: grid; - width: 100%; - grid-column: 1 / -1; - grid-template-columns: minmax(0, 1fr) auto; + .specimen-search input { + height: 2.75rem; + } + + .scheme-control { + width: 2.75rem; + height: 2.75rem; + min-height: 2.75rem; + } + + .catalog-clear-search { + width: 2.5rem; + height: 2.5rem; + min-height: 2.5rem; + } +} + +@media (pointer: coarse) and (min-width: 68.01rem) { + .specimen-search input, + .scheme-control { + height: 2.75rem; + min-height: 2.75rem; + } + + .scheme-control { + width: 2.75rem; } - .specimen-search { - width: 100%; + .catalog-clear-search { + width: 2.5rem; + height: 2.5rem; + min-height: 2.5rem; } } @@ -1153,18 +1164,8 @@ p > .hljs { @media (max-width: 48rem) { .specimen-hero { - align-items: start; - } - - .specimen-stats { - flex-basis: 100%; - width: 100%; - } - - .specimen-hero { - gap: 1.25rem; - margin-block-end: 2rem; - padding-block-end: 1.25rem; + margin-block-end: 1.5rem; + padding-block-end: 0; } .catalog-group__header { @@ -1181,23 +1182,7 @@ p > .hljs { } @media (max-width: 36rem) { - .specimen-topbar__inner { - gap: 0.75rem; - } - - .specimen-brand small { - display: none; - } - - .specimen-topbar__actions { - grid-template-columns: minmax(0, 1fr) auto; - } - - .specimen-search { - grid-column: auto; - } - - .scheme-control span { + .specimen-brand > span:last-child { display: none; } @@ -1206,11 +1191,7 @@ p > .hljs { } .specimen-hero { - margin-block-end: 2rem; - } - - .specimen-hero h1 { - font-size: clamp(2.25rem, 11vw, 3.25rem); + margin-block-end: 1.5rem; } .catalog { @@ -1222,10 +1203,6 @@ p > .hljs { gap: 0.75rem; } - .catalog-group__count { - justify-self: start; - } - .specimen-stage, .assembled-lab__stage { padding: 1rem; @@ -1249,28 +1226,6 @@ p > .hljs { position: static; } - .specimen-brand > span:last-child { - display: none; - } - - .specimen-brand__mark { - width: 2rem; - height: 2rem; - } - - .surface-switcher a { - padding-inline: 0.45rem; - font-size: 0.6875rem; - } - - .specimen-topbar__actions { - gap: 0.375rem; - } - - .specimen-hero h1 { - font-size: 2rem; - } - .specimen-main__inner { padding-block-start: 16px; padding-inline: 16px; @@ -1286,17 +1241,7 @@ p > .hljs { line-height: 1.5; } - .specimen-stats div { - min-width: 0; - padding-inline-start: 0.625rem; - } - - .specimen-stats dt { - font-size: 0.55rem; - } - .specimen-stage { - min-height: 14rem; padding: 0.875rem; } } diff --git a/packages/ui/tests/catalog-navigation.test.tsx b/packages/ui/tests/catalog-navigation.test.tsx index b869ba2..a5398fa 100644 --- a/packages/ui/tests/catalog-navigation.test.tsx +++ b/packages/ui/tests/catalog-navigation.test.tsx @@ -31,6 +31,126 @@ describe("catalog navigation", () => { window.history.replaceState(null, "", "/"); }); + it("defaults to dark and retains an explicitly chosen light scheme", () => { + const { unmount } = render(); + expect(document.documentElement).toHaveClass("dark"); + fireEvent.click(screen.getByRole("button", { name: "Use light scheme" })); + expect(document.documentElement).not.toHaveClass("dark"); + expect(localStorage.getItem("coven-ui:scheme")).toBe("light"); + unmount(); + render(); + expect(document.documentElement).not.toHaveClass("dark"); + expect( + screen.getByRole("button", { name: "Use dark scheme" }), + ).toBeVisible(); + }); + + it("keeps the catalog header free of decorative stats and framework badges", () => { + const { container } = render(); + expect(container.querySelector(".specimen-hero dl")).toBeNull(); + expect( + container.querySelector(".specimen-card__meta [data-slot=badge]"), + ).toBeNull(); + expect(container.querySelectorAll(".specimen-card")).toHaveLength(16); + }); + + it("keeps documentation headings free of repeated decorative labels", () => { + const { container } = render(); + expect(screen.queryByText("Reference lab")).toBeNull(); + expect(screen.queryByText("Input layer")).toBeNull(); + expect(screen.queryByText("Evidence layer")).toBeNull(); + expect(screen.queryByText("Complete surfaces")).toBeNull(); + expect(container.querySelector(".catalog-group__count")).toBeNull(); + expect(container.querySelector(".specimen-rail__nav small")).toBeNull(); + expect(container.querySelectorAll(".catalog-group h2")).toHaveLength(3); + }); + + it.each([ + ["/", "Component library"], + ["/lab", "Component lab"], + ])("uses a task-focused page title on %s", (path, title) => { + window.history.replaceState(null, "", path); + render(); + expect( + screen.getByRole("heading", { level: 1, name: title }), + ).toBeVisible(); + }); + + it("names the installation and import actions accurately", () => { + render(); + const specimen = within( + screen.getByRole("article", { name: "Mode switch" }), + ); + expect(specimen.getByRole("tab", { name: "Install" })).toBeVisible(); + fireEvent.click(specimen.getByRole("tab", { name: "Import" })); + expect(specimen.getByLabelText("Import Mode switch")).toHaveTextContent( + 'import { ModeSwitch } from "@opencoven/ui/components/mode-switch";', + ); + expect(specimen.queryByRole("tab", { name: "React API" })).toBeNull(); + }); + + it("keeps search clearing beside the query and restores input focus", () => { + render(); + const input = screen.getByRole("searchbox", { name: "Search components" }); + fireEvent.change(input, { target: { value: "metric" } }); + const search = within( + screen.getByRole("search", { name: "Component search" }), + ); + fireEvent.click(search.getByRole("button", { name: "Clear search" })); + expect(input).toHaveValue(""); + expect(input).toHaveFocus(); + expect(document.querySelectorAll(".specimen-card")).toHaveLength(16); + expect(search.queryByRole("button", { name: "Clear search" })).toBeNull(); + }); + + it("places search between navigation and an accessible icon-only theme action", () => { + render(); + const search = screen.getByRole("search", { name: "Component search" }); + expect(search.previousElementSibling).toHaveClass( + "specimen-topbar__leading", + ); + expect(search.nextElementSibling).toHaveClass("specimen-topbar__actions"); + const theme = screen.getByRole("button", { name: "Use light scheme" }); + expect(theme.textContent).toBe(""); + expect(theme).toHaveAttribute("title", "Use light scheme"); + fireEvent.click(theme); + expect( + screen.getByRole("button", { name: "Use dark scheme" }), + ).toBeVisible(); + }); + + it("links each assembled example to its matching Lab scene", () => { + render(); + for (const [name, scene] of [ + ["Composer block", "composer"], + ["Run rail block", "run-rail"], + ["Transcript turn", "messages"], + ["Resource row", "context"], + ["Completion palette", "actions"], + ["Session header", "cards"], + ]) { + const specimen = within(screen.getByRole("article", { name })); + expect( + specimen.getByRole("link", { name: "Open in Lab" }), + ).toHaveAttribute("href", `/lab#${scene}`); + } + expect(screen.getAllByRole("link", { name: "Open in Lab" })).toHaveLength( + 6, + ); + }); + + it("keeps the linked Lab scene when skipping to its main content", () => { + window.history.replaceState(null, "", "/lab#run-rail"); + render(); + fireEvent.click(screen.getByRole("link", { name: "Skip to specimens" })); + expect(screen.getByRole("main")).toHaveFocus(); + expect(window.location.hash).toBe("#run-rail"); + expect(screen.getByRole("tab", { name: "Run rail" })).toHaveAttribute( + "aria-selected", + "true", + ); + }); + it.each([ ["/", "default"], ["/", "compact"], @@ -109,11 +229,67 @@ describe("catalog navigation", () => { fireEvent.change(screen.getByPlaceholderText("Search components…"), { target: { value: "no-such-specimen" }, }); - expect(screen.getByText("No matching specimens")).toBeVisible(); + expect(screen.getByText("No matches found")).toBeVisible(); expect(within(navigation).getAllByRole("link")).toHaveLength(1); expect(screen.getAllByRole("option")).toHaveLength(1); }); + it("announces search results and restores the inventory and focus when cleared", () => { + const { container } = render(); + const search = screen.getByPlaceholderText("Search components…"); + const status = screen.getByRole("status", { name: "Search results" }); + expect(status).toHaveTextContent("All 16 components"); + fireEvent.change(search, { target: { value: "Completion palette" } }); + expect(status).toHaveTextContent("1 result"); + fireEvent.change(search, { target: { value: "no-such-component" } }); + expect(status).toHaveTextContent("0 results"); + expect(screen.getByText("No matches found")).toBeVisible(); + fireEvent.click(screen.getByRole("button", { name: "Clear search" })); + expect(search).toHaveValue(""); + expect(search).toHaveFocus(); + expect(container.querySelectorAll(".specimen-card")).toHaveLength(16); + expect(status).toHaveTextContent("All 16 components"); + expect( + screen.queryByRole("button", { name: "Clear search" }), + ).not.toBeInTheDocument(); + }); + + it("clears search with Escape while retaining keyboard focus", () => { + render(); + const search = screen.getByPlaceholderText("Search components…"); + act(() => search.focus()); + fireEvent.change(search, { target: { value: "Context meter" } }); + fireEvent.keyDown(search, { key: "Escape" }); + expect(search).toHaveValue(""); + expect(search).toHaveFocus(); + expect( + screen.getByRole("status", { name: "Search results" }), + ).toHaveTextContent("All 16 components"); + }); + + it("does not clear an in-progress text composition with Escape", () => { + render(); + const search = screen.getByPlaceholderText("Search components…"); + fireEvent.change(search, { target: { value: "Context" } }); + fireEvent.keyDown(search, { key: "Escape", isComposing: true }); + expect(search).toHaveValue("Context"); + }); + + it("provides a permalink from every component heading without changing its name", () => { + const { container } = render(); + const articles = [ + ...container.querySelectorAll(".specimen-card"), + ]; + expect(articles).toHaveLength(16); + for (const article of articles) { + const heading = within(article).getByRole("heading", { level: 3 }); + expect( + within(heading).getByRole("link", { name: heading.textContent! }), + ).toHaveAttribute("href", `#${article.id}`); + } + expect(screen.getByRole("article", { name: "Mode switch" })).toBeVisible(); + }); + it("clears a stale catalog fragment when filtering removes its target", () => { window.history.replaceState({ retained: true }, "", "/?navigation=keep"); render(); diff --git a/packages/ui/tests/specimens.test.tsx b/packages/ui/tests/specimens.test.tsx index 35934a2..a97abc4 100644 --- a/packages/ui/tests/specimens.test.tsx +++ b/packages/ui/tests/specimens.test.tsx @@ -11,6 +11,74 @@ import { Lab } from "../../../apps/specimens/src/lab"; import { ComponentPreview } from "../../../apps/specimens/src/component-preview"; describe("specimen source and scenes", () => { + beforeEach(() => window.history.replaceState(null, "", "/lab")); + afterEach(() => window.history.replaceState(null, "", "/")); + + it("opens a linked Lab scene and exposes its library return path", () => { + window.history.replaceState( + { from: "library" }, + "", + "/lab?view=demo#run-rail", + ); + render(); + expect(screen.getByRole("tab", { name: "Run rail" })).toHaveAttribute( + "aria-selected", + "true", + ); + expect( + screen.getByRole("link", { name: "View in library" }), + ).toHaveAttribute("href", "/#run-rail"); + expect(window.location.search).toBe("?view=demo"); + expect(window.history.state).toEqual({ from: "library" }); + }); + + it("keeps scene URLs current without adding history entries", async () => { + const user = userEvent.setup(); + render(); + const length = window.history.length; + expect(window.location.hash).toBe("#composer"); + await user.click(screen.getByRole("button", { name: "Next scene" })); + expect(window.location.hash).toBe("#run-rail"); + await user.click(screen.getByRole("tab", { name: "Context" })); + expect(window.location.hash).toBe("#context"); + expect(window.history.length).toBe(length); + }); + + it("follows scene hash history without replacing a Composer draft", () => { + render(); + const draft = screen.getByRole("textbox", { name: "Message" }); + fireEvent.change(draft, { target: { value: "Keep this local draft" } }); + act(() => { + window.history.replaceState(null, "", "/lab#run-rail"); + window.dispatchEvent(new HashChangeEvent("hashchange")); + }); + expect(screen.getByRole("tab", { name: "Run rail" })).toHaveAttribute( + "aria-selected", + "true", + ); + act(() => { + window.history.replaceState(null, "", "/lab#composer"); + window.dispatchEvent(new HashChangeEvent("hashchange")); + }); + expect(screen.getByRole("textbox", { name: "Message" })).toBe(draft); + expect(draft).toHaveValue("Keep this local draft"); + }); + + it("canonicalizes unknown scene links to Composer", () => { + window.history.replaceState(null, "", "/lab#unknown"); + render(); + expect(window.location.hash).toBe("#composer"); + act(() => { + window.history.replaceState(null, "", "/lab#still-unknown"); + window.dispatchEvent(new HashChangeEvent("hashchange")); + }); + expect(window.location.hash).toBe("#composer"); + expect(screen.getByRole("tab", { name: "Composer" })).toHaveAttribute( + "aria-selected", + "true", + ); + }); + it("covers rather than unmounts the preview and returns focus on Escape", async () => { const user = userEvent.setup(); render( @@ -43,6 +111,24 @@ describe("specimen source and scenes", () => { expect(toggle).toHaveAttribute("aria-selected", "false"); }); + it("uses text-only line tabs ahead of the preview canvas", () => { + const { container } = render( + + + , + ); + const tabs = screen.getByRole("tablist", { name: "Demo view" }); + expect(tabs).toHaveAttribute("data-variant", "line"); + expect(tabs.querySelector("svg")).toBeNull(); + expect(tabs.nextElementSibling).toBe( + container.querySelector(".specimen-preview__canvas"), + ); + }); + it("highlights TypeScript, JSX and shell source without interpreting HTML", () => { const source = 'const label = "";\n'; diff --git a/public/fonts/eb-garamond/OFL.txt b/public/fonts/eb-garamond/OFL.txt new file mode 100644 index 0000000..d4143a8 --- /dev/null +++ b/public/fonts/eb-garamond/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2017 The EB Garamond Project Authors (https://github.com/octaviopardo/EBGaramond12) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/public/fonts/eb-garamond/eb-garamond-latin.woff2 b/public/fonts/eb-garamond/eb-garamond-latin.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..ed33cd8246458c410a0c1dddb02817bb4c3c836f GIT binary patch literal 44336 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 Tp~gD%=1~4;zp%;UV+#NP+I>lo literal 0 HcmV?d00001 diff --git a/public/fonts/jetbrains-mono/OFL.txt b/public/fonts/jetbrains-mono/OFL.txt new file mode 100644 index 0000000..821a3da --- /dev/null +++ b/public/fonts/jetbrains-mono/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. + +This license is copied below, and is also available with a FAQ at: https://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/public/fonts/jetbrains-mono/jetbrains-mono-latin.woff2 b/public/fonts/jetbrains-mono/jetbrains-mono-latin.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..4d09cda4a4c3f4c08ae253dd8a9c5133a89b31b7 GIT binary patch literal 31432 zcmV)AK*YayPew8T0RR910D8y(6951J0W!b<0D4*g0RR9100000000000000000000 z0000QfjAq78XSR|at2^OQ&d4zfgmRy0EHqiOc4kQf!sKOtS$?N7634ToL&Jo0we>B z8U!E(heijZ3=9WbP!@4Q(d&)~x|7`bT&-&Hs*sy!%I%P-;oo1;Y=qU7#TGc(|No!1 zbc}(6-Unc&W>x(v|6ijW{&ED~YM3;NqM5 zD<4T=wzMtYPh95^;}_`JX3AL0=NBm|MNs~INH-XEao=Y_!%kt?!D?ulQ*qD)i|;=0 zMyAsL)zobjez|7GMeZu8at$|Jo3^}sqW#2F7lDn-7+EK2-y_J)W;gi_;R_)kVhkb1 z@EBtRjEDgvMvOGZh?u5`h4a~06@o%&8UDS7oz}`%!%COc>2GbR=-fClBJSu z$%zAY6Al2JX6Z=RIUM)hBUiG^wKKuLGI!99r^Op>gURXk6BZ=U=9c!Uy= z(kU7ZShUpfU<4|*4i;_&W3j4#ci)e0^#z!WFkf7+C^}$IN-at)+PhLr)>vf-kRUJe zNJ3G!+YeIc%#aM)S>=F-{(CK_-vEs{rBDja+e4WX+tW#o>^Lo?1ppNQS_FPR@#}TI zziSz0_DII?^dF%DESi_(f^0po^f1JZ)L5BNal$OQ+^H zlbOHRZR(~&+NBbGYi`?s4bhza4?Q?Y>p*FdKuAcC05RLFWRs1P$tFP-k|2Q+AXtS8 z{41qSdsV0yv^}9>Q1dgRe)QgbPRyv{?pp>yapXY|Lk20<|Ns2uPHPkWedDd0E21dS zEXBYR??=1$Wlh@c_(6B04mE~F(@J7qo%VKH1In>wkS)s~$8lOtO8+iLFnyS4nEx`= z)xn=eeEXk)pmbEg98u<-t1n&b%wj3wLZE5Wv_so|#^=|VJ4ud&MB(n8Fr)zGmj{YS zo&M+hGqrtV0g^b%Rg=>Dnq|mKIy5{Sx_w`UI0-@5M5)^e@OHmxB_=0k-L+R#*ZNRy z*3dfbZrCjqD#nS}>v6y5rMdIR^ZsI9Q7olsLNYVSL?$LcyF|d>n`)K*hX$#psM`Xh zHQ-wCEHrZlvl}GN`25W8?hkbLZ-8htK&lB+5&%-GCM4Z7KuQ8QGgpM;03f+ba-7C_ zj`s~v1{@2y9iFi+8*|=f>ngt>obbZS!mHf7AV@C>uP#W>|1z~qyZ>}+-=NuY=Xiy^ zl{MkT4)Q+rLG~Ndvj8!Jhf`nn3h7f`*LAJl2@9+0jeG`z< zF8ibnC0L4riq7og{kOwd>-#~onxnVvHn%xtN=itAh)4*?IPUrX|I5v&BK`sv#n$HG z#1r)D071aYm)!F(HV{<$=?I5YMC5ek=Tz9_R8lzu1v=G&ojN+FUbr(_j5AiE^N>T% z!&2qH+liQUAlaV=1c^jZI1ivWXDUi_WT31gfdH^~G9cGOz)U5%bQT~wKrS47;&k{V z@XTMO1?ES4ljDF15dgT%_@9PhpaOvg5hSl)7tR|e1J-9Sh;FP196bU6ego_ICy%#^ zpTYiQKZ@mRkrXVJ30N({{PNXjRd$Rhd7fcUDf&zV5df|+5r5|;aPZ}Zi~xG7;WfR0 z@q6&GqcP&S;`lj%TGXtYx(cB0j^_n{;0sx$0g@PWVaz^86(ZB4FaZde5Dc0XA!{R40Q^A? zvUY!Ae*g*7s}k^GcZ~q-F#?-_M-ry&?^~|)Sak5>Bz+2pehg(^zOH;iXqwyn@5vRO z`f9o0IQzMk3l<$}^XG;DDPV};>{y%t?VkMR-q>W2&+R*y0A3#x|MwTq0+M;NFBV)C zfomlO%mIkgv4nRL01VH+^+JLM6HC@?*s`;jBOboxL|nO(SYxeqeE8W!X{&A2cGzhb zoe-hIM2L34K?x2yELFM;nX=_L!64shXPj58%te<}t5v7oRoC3qq}d(c`9Yg@4|VF& z?WsQfe)rNVe;6@p+=RbO`^TJx0CI?oXo!KB$P!s2HsTPh+##T5KsCj>+g` zODK)XQGx!Pey{#nOqO6`ZTHkr4!@JC#8O>+uvSmPS5`iy$J zdXj2X*_*g3Tt%}irBV5!;v3=y{j=gc@m%t7@fLLp!@_R0zIm0ktPG5Jh}Nv$j-R8!Xl%VMjL#f{}M zN@F=$=9g)ukjpIY74?OtP!t}HJ$ZAk%w+?Quf>v4(IwJF+%UzBeEiN~p3Y;g6|gxr zYCV`Kjs(3=8#tC3@VveJ21G?DDLK0gJy9%C%xsW|=W;1J( zZX_^Zecvuv&!DmVFK2DudSfk)sPSOe+G()V@_JjoncK=Nxfa*0XfxLoP+~s#?s})2 zhndrvldsHI7}LUR=Bl|2mpx;C*YCpZ!{9G!Jc~}nA`$)BYFUAFG3~`{n@O}_8ksuY zWu4hVDwA^Ws5*Qod~(ld9uZ^}X|4tmR4p@x_ z^d4aFpalSVq6O#6Z(+M&n`?)#ptkGsUpxUNq9k+(C8NVA#mkMq@DUKWM@rTXd1P~~!|BsJOsJ^E6fjeRl585QKN(dk9Ziu~1C`=XM_)e+Zdd2-y< zzVeyjQ6=A9OF@daGadc2CUkC|Poss?KqK=!rfr)uZ$zy_-8gnl1+KP6kDfVXXo%gm}fCn%C(u)Hm#Q)cWp#$|KVRzfkRO|QF4#OJi%n(G+f*~>14tJ zzZmhAnH{*e0Rd&JE?rHrvb!z@W&+oX3rQGz<4LEC{PKt{T$25he15NHzqoi69iC*Xl zlc-7p;i7T|m;+@v%ccf}$XlXmKsqr~u8rCNIob?~bqp{E@^WNF74jBOU@8zxaKg1s zOO)gAg;?i+bHMgKR*2P9AP)fMJHw|!at|}!SeOE6*vD|uzeFTV)srx4L|>!e5AYr^ z?(}sMNSoK?MgSe6^Bs|)S&r7}u+{5cefQOOIIn_cS+-=6IdIJFde%9k&MD3%o(!W0`yYHSO)0(V5r7m19QikDk6tzDmyf&$x1B z24$evA&b&uzs?Oa#YW}%Vc2d|jto7i(I)H4(25eo4HBvpuD2*witoMAt`t}NxNEc) za_cDX_Eh9?+12mKj7OicxhHg)u0tI$<|H!rLAtU>McU12o2K0GKa)X{P=RDn zLp<$F?!RyACG{@U;?m8$dtbw)-5g&k?{G}7dNJ)&#wp<465Zdz0ZC!pg zP4=R0wkf)LfHhq3?)&Tx&e@(6ABS_}WaXmKjCaqB#Rb^+{;rLPG)?G9*h@t>omk$0 zMobO;jfW_S3@+xB8(tAom~nywHozE6N&D+L=Rpk}szSfiderfm)`brXI7{lU>n1KK zT$sW7yqg%Lgz&t1U>(%I%83X zdVCzWM$iM}kW}DmJ9V`V^q`e`?cyRpm$6dq06M_>Ds--^sWg{$NeAq-nL~%L#SE&p zZd6ow#dIwtxj&dpbFSdH4iuplDWdZo#r~LW#`{LI4>iCRU!lmP9#P?gz8M|ySwcmq zqQ2LuBHG2O4&L9XY}%34`7mC^*&U@5@3>eswNfd?Gt_~@_XZH5dnStI3PqpM;pZr zFrwq|6rR?#eM6?gS^90z&%jMHt5V#U&DaZe+ubAAPZveY2O9CYh`X0m&7PQiNK`JV zx;E8TS6K=3bG(>xczU!DpsMpxd>9WX9CtMVn1%@-62p#*FsiEPE_nx?Z##_+R*mB_ z^#C4pBjOy^5Iy)8ix@a+uj}c-tSo{Yj<$sxFpI8U+QG%HOQuUyiL_nnn7hh*5L|_e z45k9jU>Gf>qEOR&8F~}2-uAr`igYlmre-U)NV+<3c60y z)->arNi&6JK%CEFMW*JF&ard#_wqzEc)^1^Qy%!j^k5SG_{Lzh0q-!t=>Z1V>nE}3 zrc$&ok^iP2CaTwnd+;avOf-&h69J=;hLMPDr23hWUDZ-8?s8=RR`gYjbBH7{TWQ5wW_rnYI*bj92Q_Srk2 zcPy?NN~NRc=>=TBr`hHOO8zEIFcEdAS>w>uhAmN7sJd#OSB!(1FIl_*ez|yr01d$D zebH>5qo`%LBKL|Sc8vZgkpZk;QxFj>u8Q;<+Lv2opv;+U{egiS^V{yv9OZR9EDv{4t3LUNTb>$5M;Oitbs2eXN*gn3nXw7=(?JNLO6qr<=T)PrHG!aw;sV3C%QO2&? z@j2LQ@fBkGl%hkEm4l;U^pn(6Vmu8rKS59PWMwL2(nC}Pp+a?72E--!7bJ9p!%6f2 zb*{&u<^i-paG+|7HceNtkLZf6W#hSYHMBoFVLIqX)DF-Rhi0gCv!>QmkA_SuG@TLD zRgsVZP_wLz?jflWD10=kZvwm2nwVdZA!ZDPs$;kkiY48LNryViN+<~Qy_g7xr|_JR zTy)E=1f!O4-X#Js4O6nl#K{H3s$DkTRNJf-Tkwe;W$+RYn*?bI&r}qE?m-VA&2dE< z-4)%@j4vY0U8H)p6dFaQG=K^pTgcyvIsE)(6m%{VQu^(r1@;Zyh7JU&H;`_NmL(i# zX>KFwdK1Zp08?^LPr@W2n|S5YqsqtP>jO!qBU1V=RJ$iU^Is(TO=4kFP={Kz2gJJd z)>S-tVXeW3%XuSVvbZYJw@{iK&oMxaqI}Hwc*NX7+`}F+GR60)FzZ5N)Q#uf6FJ>@ zfBEP=zWxeu`ma$ef&YUw_;9J%Ic92r8X&bknm7MJG{)@lK_Kd9xFYwWN9@$vMZt%5 zWr?WFB$X-!ak|LeA>Pq|C1B6zZDhmDpBv$|YE25!~K zJzoM;j=jmgw=!R$2WHQ5%ESK7Bw(@~m%W`qbl~FISe8er7^MAH_FDqTF2jD-P94aG zNnc{m?ETgQY~t}lIbge=k*!J=yS^gIT>~mSTSZt}q1Kwhyra-v%Z$|?T6!SCr<3$0 zL=A);+;7`=Fey|iay6ox4o^_YX726i?Qy>;0dAMbuy{l!+^BOLrMlb7GV7werXrjaAD-rI~t&K z5dQwO&YZ<*5fetV+Ro*I7hUnor*S4DPE`)&Q6cyS?|JR|@r7>`?;_b-qOf zzVI+|Q2C2710*}xiz`Ns>giO;?x`MkR79tQ)rIj#+2&M<=I8ZC%|s+Lrpzal995J7 zcS-el3)CptSSnB+m{-pFG(abX@y+H91=FMAvzkYElnw+h4?W#;yEe?_$ zs;4ZFJda9D77o;caue;5MWh3H$Hh>`YO#&bNt$bebgH&6{^bE^xVVR3yG9o*x@0i# zaA6T010L{UjyZ;Tjf6>w$n*1hbQp$n9<}1I5ew$`EM zUo$%AYu5N#YA-EOe>CpEWuVzOc=t1AeJrku^cm1-p0zSSX@Yvq_*sMb6tgn+kdbMY zM}=8k%ergAT4*Z;pk(SdDFYb z0yb^RyYVZNS>1R*;?seKrJAlt%!tlRb~=opBy3tBi*?BuCCbKpGd}4viO3Xep|q-Y z(@3cBF6H{7!{=PU{3xY-`THoz!SJdrZZB1R$3@!LPqY;6xwzif3D1fYFB_U>eBx^2 zke1C74kA*Tr*oW;wTQ)kKaJ{GBUQOIGR0#3h3}?KEK;F#L)0y(_((Zt@0mHJ>T1{8 zQbf+$WEko|J77buD=cDkl+7?PrL|lcn-OPg(4#y3o1stZr2JuN{Z42lq)4n8bH_q} zqGWBX8Q+14g!xE2&Lq@6ZnW#9pS7RHUgx;&(4zYiZN5Z-H$!L!X03r0Xdl)>&?fU_ zRiyp0rX)N~LbMVJpRGwGc&en?hbAYt5=NvZL2o6rj$HI!=A|2cYuNyR#{J&NBUXU+ zqPvO6bUvT^^i`&6?R_0)bC$#5c4J*X$)(3=`3Rx_Xxbw$7IORHj!;-hSJ&RFaj zTbS`~%H?sG&7V^Z8|-+b##yUJz`ocs$t^=@YcIxn?pzdTGy^@ztF97}W@S z$jDUnQejqy{pt=o{~Lu=QGzLX5nv@~E94#kcB5J#BmAf^B`ycbNE)0aQeXf`VWMkHiG6l!Nb%a@j2-T&%#%Y6mc( zJnNKLXcfoHl+~)Z>ts-aZ3vH(?$)zguP9NyxN}iDzC#mEup(H|b~F`e0!8OD+L`0V zz&#)x(TYmL>NUxs&zeuSkA%-`huARi9jU9-+|-8%^=#Ctd%fu#$D%FdFq&#^jPncsFcVX9n`dwQBt8!^U^m}9lv!q zSlaNbLw*06#vP}xk89Ql-(CSh&ck8VERBiN^n-`8!IF}CG;ETVpF?8K19GCOLg&!>WiNl zK8m)b^2-UY=z9gVj}$0%@M4#?(ncx$&G@vzY=o!s6l$3wlbfN!E1idR+yK9g3$C5T zHDnKbQ4C~vlS~ifQgoQvaOE4Y0nWf4vNmo~sW7X{pACV9U=1~RjA7ZyG(hW7XuJ~| zZ#Xt{Kcx-`HF-?#D0@01i-y4IgWsfbv*(~K4h6}O_jx2nwxVU%bykCnaO&J;p!f{8 zQX{sqxGItt^4J_37$8r{gU$F@W;_sc*+WLAm`sIP9sYLI!Rg?as=zhd9KAF^Qe?wE z3e0Lm786FaI!9z@{_W7mRIo+|@~jbVvbieKDr8AHlrunL$wJKdU@$8YQrJUArVvhr zS=}lZHB0_BG9b9R0OW>r!utv1hCn^z2Z{EdCEC zW+&7I5+HHdawNF}a^6g+6snh6;|!f8>@Z1*u7csiMvQdAVtk}~Aj9OD2<;yG$zV}$6LFrtTT z6)#x)a}IAJkB#-^1}AckPxc7dJFA!4zdEY#y12Q?;=50MDb}jZX$x`Si@aX7991D& zmKxJSJX9;5Cvg$PhUa+IjEOHnwGP;UIJ;nZ$w%9(7yG!U`h|k?GS#u!{$%fUq12vVx9# zB*qF%GNWJE*Mm14yg|k9rB%wH>iNE@Y|4o1=SZNV;9kPs%1@Jv#yn|L>1L`MS8W1W zT&oJKIHe==nYw@dy)fp68@Ou>r|hXJiS_fPmpXc_90CSxNG#9cqav9>>L5WZRz+e| zm+EaueJ7@0c=5GDRafxFbg zkil8@@DZ8Fq{bJ^{0$^G{_8za67l`XdCv(KD`{$LbLHjGs5A?gfN@qiD&wa%iV5 zH14thDBu?C1P&r9?7-gx?go?)xacXq#Vw6TQT*JVkB)$*qv%&~h-sqe74`x$(MdlO zfIV8U9u$M~$~nlz_YkftVRaII-b z=q$nvb69M!*>bRiqoufbL|nOXx02Wz9z1!GsZ^#aM7W$pSZ5;ze@fc~`iR;tyXkyP zFGPeW(PG3pD9Ito4oh_@)=&`)lg|3vKc>xwg%~ISnZc(TLV%huQ3|p^2y&fB5+b2R za3M$Fb7`(IIO&x0$Y6+2Awhu#18Wv6Sy_z3jy)#J2sq&rayD-{sZ}IaTg$}(8+cpK zmk&8Vo2YCNV5=Y+J7{h8i7sX&6igQR~1Aee<&a&V26NbZhG zu3gl+%DHvHe=+Rn29y@b)c9xTDB&tg)DOs^!Z1PUWc2T{hA1l@r%HZ0t zU|B^r7Fv~GGnl??&WDFYXg4-k1qy+^r_Dng^Y>DHg+(v&CB;jfV^UvbE9F)`YB6E2 z8r8%-^z@?gdVc7)+-()>xQc0fMl^1Hr`aXCJisH6mJNtB>qaI%bf?^PS9<6@-kOwZ zLh_cjn9t`^wOzl%&6YZWGI@OUBbJ|8@x}#FsS{W5of9$oDiz7#hDw-dW41<32-5IH zO!_J>B2LD@DSjKcAbKX87!@q+aCKB>42&7moTq->XBz!+yT18jd>?)p#B|PxfxtNq zXq4?|f`17K!j*lGuj3LH#E1%DF3n7T)M3dZA&}%iE+4%oM^Hd-g5~&`&HqjBDmI%P z7nIIe803c8{EeJ+`KiNJ&es{)2-=;pWSnP3Mtb%YTfB4ias-5@asmM-wjsS3b)w4- z*dsrcMIr2xqTGbIz{EXMy+uW~$#Y5{$>+D3~w6-w4IF}DEPLNLusXmpKB1u;yBSFVzSBP2L(j^|u zFNXB7v0Di{w1stW!$N+sL2^!GafKkQuvtOZGBler0OOt~S4U!bia>=bM*xBbX5mzg zm?nS_1VyT26Af!J{0!!o0|n%P@Q$hOBAOQ`ezq6`%r6#Mn2nL(V7!zRT-dh4!qB0k2XBgS*1n@IPD)1m5?_Y zR9Lol*(`CBY+>2K_#$L=6(DKmWr@eketXwn#qKYZ!x~!0~aVw>iA-S?#a|QfOp4U9DD)dSRb@tm%6v!bk!mc7n8UY^f8cZH!Q-ji~YXdR#GR?3>MO%unpC0*IW3F&SSj>7) zK`ll>ONn;h)b4#|os{iQ94gwj&U|Iqm{9CO zsQ%+qB~W8DgYj3bcOHB}x?^@K@{1K>?4PX;4*Huckt%j+FH@YjP{mFe~+5+!h$X)Nizs)C)Xq$Qq{?AX#`kgp2b%uLMznzVQbxh<`--H32; zo=iH&-?MMZVGwU?0YPB-HYn8Y`$sl*R-dq2u0_`tWA)W^kKQ`v#u0ys7lCWasAbVV$pcBS89V*Hpnf@NtO#CZL^HLC3 z{X3f>w$dS^y7EN+5ceQ@}2y<@|)R_ml3B((XdD2N#y-Bv`pOhHVsSD+U zQRwx>k;1yPP>N4->5vu;NPaisaD{Y8g#mn*mxaLSWn-bVyFBu;HPFf>X^M@Fb;BSC zAqjY-!76e&CcuMTLem#Jg4vjpA;f5>%oPX4W6mnqsCigj=LT0C2YXv*hngb{FPrKd z3-$R~_T+R2HA;Uhslu8idH@K&JaVkn{5E_vh@z1UB!U`##P z+wwaK6(oQXnmTyA$S;$5`FhL_BTK{~b)=z2OE$_B|r)_DxkKa(yh4{z{96PKjYX3{I+VH;#d`6{!NGpe!NcqRwoYnHPrKQJ1?ne-)U zj=8vnHXg|`;W@1YyC{m_BB7{^IEAogogLy^(V`(2h$x(%bww~`jH-N;<)aouUa>&C zEPiP1AS@xfzd~{I{B};D@nc&Q&PYXEql40zo}d$RLMp^k8=KV;Y70Bv$c1;9nq21k z9j=s6tk8aLP&iHBSTOMxb7jBz0XtgME<5#5>MPA|ef>mwA9kmju+Vil9rcxJn1xmg3RateS9*XE8Q~OFl4MKAW23iS@*dDQ?r8(pee1XHGNLb*M&j1;3kYaW~7G>(R$$JvnfT#f-~4~W~%=*ViJE{NFk+J>3=I; z4$0io7#Wa|4<|o1O>g_@Z0BT={Z~O zg$W}*)Ed@jEJD|{Yn{D_m`A49c+C|fe~;tf)1tQHFTP%bvkuOxXZE%n&aUT<4ie#s z?(L5xUA>&W{pto+^9_`4Bj#GN3f)b3*)kL&J|bPILS|VouY)+EP^>6F+$daJlN@UV zn?Wq(A=si}2^{YV@72=upv`5m7CGURj@e^|QeuXjqNELhp-pfLP1Z9E^=EfAgP>ep zLNRcVo7Wk)n`g!LjbEdq_Jd{Tmsii)mnRMRi#jeJ*c&$zP}Ab_?G;qW^&dTw*NRgs z7Rj8bc3!uax6o-%}2Pv}`xu&9GujFdDSf2gm@c?y%LsZ-25jxE4*K>B5S=LTd z+WGTL(+l{D1sdzEb>igp0qbe)h}-##spP?{Q~X9@lP}oXk5^0o9E^Ijpx3J{5Oyg( z%u5q?1eSzV%RQxFLUIZRr@3lM=EKMbXsXyMGmdhDIv|?QrvZ1hHl_f6^Gu=E^z1&w{pnj?IU0%krfo3j&5S*V4gB2<5^i1(`+0wdK=32bi6L&*~H0Xocw zDE5>1n{Jk}#3(wIM=ZM$?~z;*m1O08hcqImzYkCq`T(S>efz5uik0;d$5^Z-6w(6; zjzA0*XpGDR>WPLo6l$Q_b;K3&Xj+Wc$D4Cn)z*(vbn|S!JvdQR9cWcn`Y0bm(G*s$ zBs*GaR+m$izZN+bQu2}DBh)DeX-!viUv0?$&iMx~c(VO44Qpo5KDXx^>9fTz@6lTV zhi*8JT%B-Z$eCx_R7Z3#aWNm$C4mFNA_x=iHkpKDAW@XHCxIwh zc(>P@$S>C7VV=-YBV94Y(XfQJy9K}CaY_TD9E3_xz3E1L*1xZ)4nPiR^s+P1(-#gN z%&RlBa`IqmZCE_Kjdv7gXLn6HO5F$~CR%yLOBBCA3~_?ONW{2&0*ix6K}uuV9<2hQ z)TM;`(Aoc`kQI<-vg`5j{{MdDI)z9%@~ZNd`{~{pTMRsY%U4QzzLiHI`t+bRKifP5 zq&i?^Tvyp%%jV~&uSS_`TLL}Uy*VtjrJN_fVJ?syymp0t*+s&>oLx@^AIXy7Rl>EUFW+XnV% z-|odwx)LsN&im)1qQ$p2qX#&BYxmpQnjAn0T_5(^3`*Fv|Rn- z%75LvZ+!IG{~f+T@BQ-X`}tL~)>tcV{ytdvnq5$hn~*OpO@DSxfb`Ap_tNJ+-AC^O z=jf?#ev$pg8k=X@)xbaYZYZIqOqy$TWWyPoy#<9n9V#miipf>56SEk!h{c9=7A?{m z^a=%`*LHxFEJLV79w2N6xxZ3kFa)JCKf$25L8mhis2w##yf9D(mj&{Fa08=nsQik3>N{&-b_gF?SjUi*1;_PAIeG3R&5El_T?q7J%-W_wb@fQ#+R}ML?l27wjWKyP z8mPt=ZT;qCx)`6>Z2o#te<>}W)IwW8Z}42$6kOCqvFa%8q_$Nopc`BKqLmYE!H~bZ zWNh*8t+r3i}V6cd&68$d9l#1WkqS4j&*#M?DXElIA34N;YaB;d)XuhC<4$2uTn)g5T9if7^x ztx1pQOh`NvU)|6GCN974|Llj`vx1e%nWeXvzAL}?K8SeUFZM{Cjpj~guPEY^seXMb zzsNdmT5jNMbt>(9zimA~$*!0(#k5Xf;Deme#P)entq+$A7@J^u`@@>{s$V)HJHJ>i zu-o~|yS@ZZ+Q%LKLg9rwb8J6u-YE1xer*=`7)ybXKEPNCJT&;g`rB9PaMqOgj;Lg> zc&#FG3``t#$Ws!GRC$+{hG%^5tv4wt+>J?#UM!FZ3qOS+7OJ%42ufQhn=StjURoyG zhglp*w&pM=nsc7TS2%pOEgy!Ro#`Am+lRRv1V4EtCz{h6&316Q_+d*%d^*C;JQuaj zE^uUKAOc`#8Mj-o#P~2X##BUgvJz3+N_uJ{!NEXHq}8b*NW32>NR0|Nq5UXs0xMaD zFo_~S^cxiZYKb8@zjjiIE(=$ybtVJ412vI4HLl??lS&TNg6`e~u_mYR*L~4}4vz!| zgEbM4(QQQ&6QepSm@y_gJ`rJy)NH~Q=0lZjmd>OTtq$>|3v5$B|@!%3yTPq$pctFF86~H zdxjscDpovxj2iXwUqeisF&SnX_ylf3^@NFF$AE!-1N1eHJovGKMxKfRq@Br9J-(GQqj=p!B2HIgyH)=HK&wftJW2=s|)q*`^OmV}3 zkLNYVT+Rp|__gsj&>=SSvrVG!Zqo@Ex0rL127UsUA^xE|DbHO(qR-`J7xMGuYA1fs zbBO!0!{SeQHI_rXPaPaaxQ9W7fT@D~y#X@m5^LC_h+(TO$V%kuHe!Qg7U9wn1ilsb zF`d=`Fpdq}&NY^wZ#jBK4V_@9gvG?<#3*Ex7FyW z+#IZGqR*c-`iG#}P=p?Ayr48ZI}I;$FpiSCCVwWAOrS=shQ~jp>|@m$?hL!Gg2>NP zXtQ`<_E{G}vmzyB^TU9^6}0YW$|iCJvd;v(XP&oCDK&#zEso0KYM@U-WfcJ_jtBGP zG$PG&76}c7A1!)?KPV>*v>Gu{TCI`L=NILvQIkUZN z$dsC1m#q1J(T``M3qrE9yCk(awJERKu#?lWhcl)WEZQRi!YlHxR;oo`Nnb}im&n*q z2{>IKWZk?ZVq>nB8ytwm(W!b%@JBhi?-!%A#UOR~woHoxI{Fp;gL9$7OP8%ZNCSPv zhu?0H1@iRT=5qk9$$)u zg=9}7^av^YRp_p=ai1ylW)vZQW&_5 zpVgI)t23E3_4BkQ@>bQG=bh@W%+HFl?RZb#l44gS6%5zo;Op%rgNuV3k_5YXyxjt7 z67i7}H#!{zFV}2)CNPG>;`K(C9XIiI@pn=2pli5B4QRq^aAbi@sH|hVE0F5MM&0bo~5T z)$FuGsFI%8PMw5ZCrqoa%$#3_jJ85lkN>jH+N3pUL`bqTBi)(&Iy82=2BLeV4?Sb( zconG;iAr};O*tkZ^GdNF6D}DB@pi7KjQHoISXFHuCF;)??qwg zbZ)jLb5|zk2q#gwsV?}6mGoUQm3%d6&hf|Gg4L6K_gP zMB%(8mp>LP`*rf|t_(JiVhQ#AR{`q!x7JSHpU77Gfi03GI~LS+;P~c8 z+ov60aQD64H&SiczdBN$^?}V=+X;s9s zzj~x^-`n7RWSv(awDDMnvI0aiY$gI(Rux(^W?ca+5T&Ff*-zn{9C!@o?~J&^D@jz^ z2(*!O+=a22T``~qs2*p{>cE?wJfUrI@K)sNZ*b4lJrG=oyqSMH26{6g4(X#Oy0BXw zEQMfQjeXn>GO>i6|m^-t~t`I+zl+^H&H zPN}Of2w=lkb?LjU4WA3%2%@ik&nwLg;6Tj}Z}kx)FnT)wY0B%}kS8+o1b9B;LpVN8YLm9N6Rd0G*F7K3$ECX)ImR3V#>la5 zm_0RHeKOG#XtwnWmxHF1>skI$ zp&e~pWidD5(Ei72g>*0B+64trMszUo?;8FcJ;a^wDwh#2&|P%5|IR-*j+y}~lK8yR z4xg_(Fb(>)o?uvC2!}|Y35_{xpqG^NE~TC?>imt$^m;3`cPY7B$1>`L6~F(+1bW}f zTDwq}1JC~eo1t{`kCkg>A7++V8*gRFzY#3^jdh!n%cfQAK~{H_cCm)PeaBTL3pO=t zYsAb06n9<~x6U_Euop;AyYBOC;e#XHckOGrN?bUj@}I2_rIh1%K>YIB=83r8NtlSi zi>vL>zIA_Ap4HczsCjwGV!RE;wwTizw}AEH)8vuar$l>@*)GSqIGZQr?^&jRE7YlZ z^nuEd)X^|lSFJC5oKk7Eb|!Y_4l z7I;#p#-OKW#^vYBzX+PnGDU@g^9SKpfM#YKF!|?*RY7xE&e~Fg9N@YMo8kGt{8@&- z_gbSTB{eExSUUXZ-0?ZPb}+I&^Xh?6bSnxefxtG(2s|2T@}kvR@uSg8oSyKYWehNvi?PIA2X=@2Yg7*Eg2L_1uds z7klaUJgl=$I~OCAV$=nI*;N6JI&#G$Ic~xo(m;-VwO^>eP+i=__>71OF`2QP=ZNP* zFy&xIvtE_aeN59H-fs!mL+yz<$wKt@6SY7iql5Vd1Ai;Yyi!#TU?JxW62|09(za8VrD;L+{_1mQ{2H3NPw?WJsxf?fZ-pq4& zlw^xr1T_NtX8Y)S=} zLFvqtmam~6{FOLoSt{jQ3{qxYl1=LUo(K2dTG0XDx&vIEGpoF(R=HcE=C|dpy?4Al z3hW*pW5N+Nj$#@d2{EC{aAQz}&m~>aQA@*TOnx3r|NmnfGDAdcs~IC!{`Yj$NEir` zpwlsoyE1T&|NT3J1Goei08`3%Po2E~l?(gA+}}CBg)i=-kD4Z@xhBN{5i9Awte89% zk(jDgu4(^N1&U!`7iC;g>Sgfm#U`&KqSZzmC(Noeo?yS2Fcvu$JB&qycz^ai4$VZ; zI6E!bN~UIhy*Jd*?x*b0xhtyVEuzNUoKDeVQ73rKqV*-tE``!k82&1CbCx*Y3q0%dijny5BO)dLBg{{6gknU=7 zvG6@jJ=y89UIC}@{oPj7boWlZ*5X29Z@fDxD~d4H)9GK|T+8E)2&~iZKU&2VJf8%F z5l6cYYqa!Ru*PY9lINA@{mkO~xD| z6{Mts)D>!^fK)=hE*a(RH?0@jsWN0>>)GrEC#SZkUiCj~j(m!zCud2Wj59B1bjRYXoP_^@@a9I1iy*Y0 zG`a)EJsG9B33n6e z__pnJ6<21#L9{%e$`ja?l7Xqbfvq%g2p$@sz>|6mulc88^)=uS|7XoZ4+7aXK30zx@P%`mOY3UOYil<#2YvOjx)4M}#>)m`J?Bz*n-2{e1fkeUFwEgr)rFGoru? z#*U=ev#yzt0QhA9|EO{QTN2P{_v*LK<+t|*?s6MemvR8C;IK4NF%!VGkLE(=hCJBt zuptcdQlaIE#jxMm2aAi_`PpsU@ah`KM_u1KABJY1MPGHox?ju~8QKr;)LqiRh;`~1 zg!i2YY@PiJw=~~~!7wns$4lvaTXPzN^02VW2T!yn?qj}sA?CACO6BXAuj)&y#zT8= zFfYm!yqPiQvE?!0)dQj>4JI32d~gtq8AM$`GBeM(S0{dkKfgB7wJx~;$m`H+mn{m4v&J@C)SZSCi0Tc z&Bc}AQQEnRA$WKqbMUo^@aO!ID#`=dK7Zu}eoqm^$Ysx3FMbUsU?$uQuS2HQE+~H* zwpi%%V===o(-flH7_w|$>y=p*b=$Xv{NRYUZz^__ zaV_OUlR{tPl^MG5ZQE;gS7y(%X(9EN7P|Qi&OPWvq3`fvZ{C$zk*=l8Qb73h+-$L< zQobYX*G%@5Ic-0lsYtAfeZiXKZm^l|FWzK-4H0o{`o|t9Y)t21d@5 z%Zg?FxV#Pdi{r<|Hu4+V8xvtz|N4aNhUq;B>!P?8Vbur9ZT_7J&0bZK1^dcsJWbpu zpLASeyYDAZ;_mC3s*hUwQCk;p0RcuQgohq{?KI#y1(2dsVqb>0>-4Z66R#A1Mq0-E zHF1wGBfX;=;6(%+^M9*&J{Ns{*-5K8u38T=2g0kvU+~|k0eS$?f#0$Qks=^`^Id4* z+~NMT**T^jSp;kKXqg|JFoR<|A%Gs_FIn?b2B@3?T?XdIuq_}Wd09zDI!a8 z8GY9;a9&ZfTN|q<@7;Dj{NW#9_&HayPYD*0MuXs^58o#zmty~nc|g+7B+IM15p_w$ zAJr_oDP^~n2x#wDYxCPjY&+*TkL2Q)$_E1cELvUkG-Ij{2SSK+WUa^!2)?)QnmV;zRNUxQSPow_&}oDTPy# z9uuz~wDfd@NLYaj{*YX9? z=`HtW)1UrMAiQS>1U}<)J#d%%owK$0VRihRGa}oAg1Ed2y_g`mhzN{jzuzXm{rnyB zT6|^KtA6e7X(!=liw3tHPfB{bdf7PdT@r$pjn(e8>`Zn#m(Av;$I5?V0|?h zkguZ}?$5i>lLK^V2uG-Xpf6Z3H_9hC^8SS=i_{M|5tgg${&3z67yDqj)}0HUO#V5x zZ4!3uS77U@0h3=xTZ|~Z-E9#}9h}2LMUH=ul|};v-P!#}!8xMsVU26s(K0)eQ-llFtWEn=q#b&UA-m<(b~&*BK0jC{iTSvB+3}6WQ$q@MXrgmL+P@(57%OU?+O<~xGeI+L!C;3uWP7| zK(Uc@tJxH&$S=KL1gi1-S(of%eEJMp2^Tx3fyI!}y$SCyW;M=@{(D}1A)vAQevAu? z=O2dQCC628OIAc0Z$VCXxWp5V7*&UAmC}q4{(#o~k_QCc1-8lI6Z)?1-%Lm`BjAO9 z(^;H83x6X5YxH5YcFbF&3#3KxTYpZf@ef!_SmWLfyIVWSEyr-AQAFhVU+kDa$hQz~ zaZlR3v*`++Elwt2^vkE=;4VoGe%5xLFA8-kdz$x_!^m8x|`Kj^7m$b@6O82jSQ zcpKhe-hJn#|G)q0e|ljOXe^p1Z6_^?7Dr2_9iI)ebN;*N zbQk(|dKmrGy0PxBh0}4-%0+>T;ujrRRDX7!)0@m-GF%ycj6}w%UArIe_j~@L((-#9 zJ=2<~#H!u~J4AFyRy0PndhI%`M&MXVi_;&rmE<0XgtY`}(W=R6-RwG>#oSxPOVqWGvKYKsN$+4s_-IOMz zrRlHf^K?9&&n#_hZA3O=n{_rDfB*K1$}VNkpc$00wNMA6VLZ%+weTivhy8FIS=id! zy4Y?(NALcD-GXK?!de`U^Kmn7!=1PfzrdqJXm^@A+3%$XX%20qt+a#o(RU1(?P9@V z1>4Qa*)7(^Cb*4*kAsqHc^uE<4g6>Rn$HQlCEO)~C9)+t5h{|!7vh|_EbfX<@w=E1 z@AGAjiFs%KDxWPZmr9qW7VX7s$>DA)cbD;Hc3E0pFMldum2b+~idnIi>8cx&$MfRp zWw!jETH>TrKdOE;q~_ zFqY0v&Kr%&gqnDB*nDQP%n9>_IcG{u>CLx@1priV+U6i3-5vCwwp@^n*kUk7o^H%F z1eDs}|Mk<`U%uerfbeIiwG|`L7QY4vZxKqzsMc?1*Z5#&^OiOnlkm&j+GE^z-+}y( zIy4ICUtZ=(Bj`bVZWUvS@Y3x}t&2&OM1;74xi;9Muh;0>D|wU(JLQuOsUV3yQz63^ zCm$3|WJta;S|Do&#w3jCXQKIpVYZ@u5tAFYkECY5KQwuRGgqb(ldy!{awH#A1+52) zHp6lZgmChL54iLj5#0~~C_@H|Z9nv(<(v*vdCS?j76*N~PosU}rZC!3b$n&8mr&C@ zm7&M%Mv+{f8565g&`oUMp5BJgLXcNjI}e=188B%SEm_UWK^(8KL_Y&}2}b|!D_jAP zPOFYCCrx9S7DMbPXHAu0!|bW=o4^M9Jh$-#=aF@U(M6mNnBz2N#C~z^e~^wg&Ku`B zLYaWnjvlMBk=-O7I$T$6VS*o(8#`-jYdcHi2n#neeo+`8Qizf8z|Y{*2kL`_dd&l} zVa|Si&_$&XC%*68BK)@mgYp^$zo$UFq3mx#;C@#(_=#ZM=1g@bs zvyqubFmc-r9}+g;sTv_!Q|J}NJF-BP8HQ#z?~5m|EsME(8`L*ISAt`&u3@I5?eCsm zUGj1ITfJUA`Wq$U=RgW!;SqG#MqMMDSxw5sn$6xy6$?dyM{m{l;06T3u z9AW8(GE8lC1yDN@HFWz$Fs?8##4dxDE41Sd93LMF1vX!Y5$Z=K;12)2CmiHeoVB`c z0Th&4TjMYSYhjEK#r*^!M^=f|y)dQpp4k?ijP1zKveTPjm#d~CcRZD$*Ws57n~!l^ z)Q6LF%K=Xkg&3`f`Zc8d{WpvtT)P=A&OmK?E5H9=C+z7-i9`_P4~0`w(5g7JQ`*HP zzX`cOc*&!IF+khw0o`BiL4F%uoh}C8lLq%Qu7u(OP~$xar?r z9)u4q!_Y{aO^z8eqf6T=MCu3!RzK^n)S?7QPkWcT2ti`*P!xOUMdX1g{LecQInXL& z-extox__7%YxYH;$W>HwS>ZOY+j9C^GL;l*W6-Q4f$8LAv_wuSmb`!4M#9@jnB?{( z8rY)t6I4jK*4tTWw%l(vpc9s!+y-E$w3nVWP%$kMOIL%)LZd`Jj!pWW>I}a%`eFsk z;}~W+qs>8^yYp@(YY$G42({k9O(6ye1n#CNg1y;ZA@`e{;C?GuFHlyA|M{hk8^+<1VjE*h#X6w&AFtz>e7yU|m z|5up>>4A4p(SB4E6c&{J{caBb92V9QM$YomG7(R7=U1NV}*W5khFU>BoO9UY#5s`VxHn)Ex?pv{$q+ z*@@CaiRAoGoaKG@o^QPnZE$RNHzxi{xY2}j`^)+GQiz4?n!`pzB0*0ZMMm_r_eh53 z1uEnk6VV-_c3Z~9)NR=Jdt|(h-Zloe2IXb(KtA~Jz7rr8{p9Hh@BYPx+c+fXdmXJr$7 zM*2poBFMH<>#yQ<$sOh6&<)GyzV%2UBWX%=AvR0In-o$9!wFD==MdhE-s8rmQ^hb) zrxwRJDYk;L_yDGKJYxL^iAuIP2-Rd!Tnz(m6J*3UsLQp%@dGfmr`$_*3Z_kn+)9G_ zqZoFO+}?$K#(XAQL9{FhZOI7IIv8zR(d?h@nw6%d(Iw&zG&ITGMuPm#GaI^)(yzO4 zxjHg9z+y5xq9h$HeL^m*$JjOG;|mljg^*1&q6>In>)=Rc&Jv@K+$JJKyiGD*7nTgt zSHjX;y3Kh`9GNPcb}kfjUA)wScc@ASi2160acD8+x)5AUIU&*P0PH=Fh67Gj^VE_d zfAils&Fww)DXEvj8!vt84fJWVF8{v^*|Um}f>q3UNtto0ifg>?S5HSsV6Ya9xWJKB^N_?86(Cn94#gv~fICBY6Z z=i8>L8?=A+M&MX6y$756up`zi2u`{k8U9-D@<1IofF7va7b|ip1pdsDbB9zfrsI{m zTcN8Ar(%8Dz?JVnKSqf+S8=|8vV>yFO`}=T$;f>Kgp+f3Q`AXiTq=}+jljUCMDe#ErORXWa8Y+ngLDB>Q)}xM`sT7KKE$XH1 zP!vskRbTe2HlE~VAsol)jn@SSjB<Dn{eJ)9VjlkM-=J?(ogLNsEymklRhk!S~r_ zk{vx6ttoa;X#;KrKE$26TA9sOZrVVIP^sq`WY#sd6PZxDr-hCL3nRg@rb5$2=PEPR zC`sf&{5g#iPwc2tEfX(KS}Z-B?x%&=dOVGBHxGPZt{>b9Od1s+2kk68JpxnOJn!<= zMS~dEyprncpzU^T@gyMR)N+))p(i<{a1+>vqLLt@-?8+A;+**!Pcx%^Lc1;#jW^J0 zj|J3j7c>Dc3J!Ml*%e$AaE~p_#(Hkz&z{xGR6-yc6{h|kVE~IaVNM>;(YjIo-o>IH zb)K+qUdZ~jtB^nWD$^aUiwc}3UuSyazu)_%zv3sDtQm!d^aoxHb#p`f%~Blvmw`op z0;dRJ&u5O!c4m+D5c%RCSNTi6S^`INbf}=+2eLK|X0pFgIW)hWUuO_$@14-<+U1+B z4{3O8=TAc6%+KJP2OK5M47XM>I8FFaCp|=pBbDH3$#4i{^kfy@Gf&giwageB8t;_Y zOd*UZDqMtm^{Dd407;V}gOClm5+N32stT#I5wl#%5^14W=su+BS&NQ3KX9E;3~8RS zQW(PGVA}U05plHpOqsF=!;?)o1OKR%Spt>qA!d-cr6`6qD}p&ZJJW`2d9K(~@;T~o z6aC&XPY=VXxdX)qK0O}?F#xdE)NL5SwWc(rH(^=sXfy%l_iatg=PZk1^k4y|eDefS zx7&wbq_T28ts{K~8|w~CSAA_iGU|zGPK!m{;6K6Sygx#FQbBM{l~4S5g4_*GO#b&8 z|FX`iZ(DPlTrfE_V4rRF1VWnI2nDSBWEk93AE|?;=Up=&8w_V|_ee1{;m_`TkM?NU zNw@WtAW3PGL}=@+X`h6IR2_FDuzPX+51}o}*~vj_QeGRy!FRQ+?f`I&MeL z)P28U(O=d{pgf0LGB(jbd7NHmHzP!te{hVxi*m`k@r#EiL3 zm*UXM%Q8ChGe@Hi34l794#%RG*?$KW=TS-G~_OG`6|Xp)LLrRHbK~&PGAbWM>2R<^_60T;jS48 zI*mhQ)-ADKYAiErg3)m zG0UJkiPr#WO@L3{w3^`YtIDa}O*S>>wMt+5AqP{4_5G?cTK44p7XNiW|l(2+6G zPX#-ZQzI>Mt@d1%oXF5VQQkbUKyY~?RF1hBGs;-6sm%A>NaJHnd8S30%p}$#c zCPOIeflD+6a#JBcuWBnU{0vN&9I0GVGL6~s4Y|l29=bE*MbA4vS8&F%X&4GjzD{0= z8j#6aj67YPv00_Mt!QQ@9*aKH(Si$cx8A#MZ?QruJcA#U#&Fyg5Z=T~;Ud`^?|2Rm z^H4;kJ9I)fG`SY@9hl7|SFj;HflCGL7bR}S^(M^NycbU*>xi@o8pz2+4iVu<{_YV$ z8zL7%5v82h2+U@q^wwb|chKnQi5Yd#CI(}=h)eJASk^NuPF5oZpFJSph5>ivsJ>7oX;h43}qiqGK1Gh4L}mlIK^9)6->d= zs!X_@LV=3Or~RPr20&UsQm!G(_I3~@kl%%}wiYE)#N1~xaR!K?)9 zqDU~ZF?AG>v(TyaJZKD7+dvy$?x5{4%`U}x(TwtDgk&?DIgZK!+L0K@a1Rm%xk*YO z>eoW_jcrE!ESBcV2c`sJnZ0E_lQ}9v*3U|j-xsgF)by>7m!OU9jL|73w`i%k-_-_m zuSXn&ORs^DMVd5_1;wS{na_D}hES&>f?v`DDq>lxF_@d*go?pc;EZz-iepl=`jTzASH9`|#bQ^Z1}sju>4oa zs^pUo*sQ#SiT^I|bUl*1LIjG8rN93?IPlkY{F`f3H&zX=AVirH13p6!HRcK##J`xE=ttWNYa6@&%JP1~2gc?~M)E+2?$>oW2X~HBk$2uZ`$cDs`03#TxlOmza z>y@l&$i3})Y9yX6!KK%hoyCEg2kB%(J^FEul9!Psc;@rHx=QBNY8!#ZJQ0-3{!yhR zDNto|dm@Wek+zwg$TP2fzW=s*d@A{Wtp${;2@XbQjvpkSg2)DWzP9q5(#iwqa{@t| znA$@qvM4D%tzL9W<)%^?a1RsQm^c!$+P8WIH)qlIzh+`N6$WXX^LJah}vi9?JX5|gpn$o~5Sd>s#p zeIJ|3zL+Rl^y>x39b69>_K#S&Q)20KV|BU)d9DVs!cew9zCqPmDh|8_LgWY5o8gws z!X#&PwtL5wHY*A1cT~BGlpC_1l$lv0aa^Tv1{zE_;2Ed*iA%F%&6%h`yd=P;#PrXR z681C+UJo@BAR9K%JiZWyYHZ`B!LE4SYaQ70O`LjSN3WHAjVo%w=^8yF16cc<5Mo-VKI*p5#713-c!zRh|mTibkffUV)h>U0Xu@F-1A+q+6KOyEMz5*s@<_F_dbbc1Nb z4Cx%wRjLN}?HHI!iQvSlG$PtMeJYSly%J!T+l)xdXyFs88Sh{P_a9r>mTCHJm(ol7 zuk~~R9MMA5$F1j8k z)NM3?uvm+_Dl_X1dWFT5XZf7@@XssJ89KdNHUd%(V0;&gD=?Gj9F_wVR{f|{;bBhS zGoH?CHLVeA^2m6-j9^}mB+B>T3b~t?=0ih5h7v8jYHzkQzQu={8?`Q4d$J>>_(8!+ z)CL*^AqcvQ6Cm;dYHOC5%OxP65DKEQt5m#d`8(830mug;2chrbamP|dQBv~iW*a>D zd4E1Bf#Bpy`eFc2y)`OXnnashBj^NhJ(YLj%f1vJRjW0a)S8Gv&Hfq{uNef+@X*fd zNsQT<1EGg4;UWEENu@?qxeN!}*GlS0v)Q(|K>V~ z&Hzaz6mF8+c`H9v6-%v^nKP`Ci?cg!he!4V$ZkNFfY^b-?mkyuZ7jqS%AMi^(+!x4 zACmW9CEG&@&Kvk$k)c#sJHKFNt+@mcVPz_7Jw%KV6Rdjv0x`rG!MyO(bfYptIs7{s zWAwpkh7@-s9aKxMN~)|LQ3|gGqjZ3DcdpUs2xgpCgyaH95RxG}dnFngg%qhCgda0W zI%38wHj)WpCX=Lq8i%|8;QPOih4G+<8Y6^+l-;@6m7g`m(k;7;U0kfsXR?-Tq8^%x zG+CDSzua!?S$)4P01##@jAS86G|6^NBs~THNR!;qQmCyVs!E_@y{6$I+-xR>_7M@% zeBp<2{0szhPgX$KI1viJXb_)JI}5b6O-P_H5w@ES)(XDYgSD0bQ*C@(b{9uh#1&`F zDjq34xYqKkXrJk35H{orgy1Ue%41dgkk~LbK1F)lfsp@y)^tENx&%;-^YS>ekA} z^nBV51t-8zm6iG6bTcg`XFx`gH}|f~sl++9>$i&{ywx4EZy(Zq;Dq3uT`6#F;o}Ch{H#HFCH%j42=e=W+-dMY1B!o3XAC zyyyTm%@6`puoWyE)!^}-5)v%UY=)BF1--W=!0iDFPI&PxPX-h8TP2Y}OL0I{LbI%d z3L5vC_r_Qd1xZmnk}i%yT4Cv_?$eF=C9{7+19n&Id+XA=&%BAM{-Wdk)zaOmN#jCP zmeRhGLvM-*E8+t+DH8hwIP*~Bd4YBBOsm4BFz3=R&agOcF7gS=t;2no}gcqbEU~G~wpcD9$}HgrYe0vr7c{D zRTp7b+s$yNyzTdT&N`;k*$kR%CXp9SQD_5?e*@}z&1HmQ>sxHK7~0A*j1W-~+GHd5 zMBty~p^$m`X%u7M04ygmrBap~|3@L!wT_Tmc-)e*BU^FXHCjC+>RYzn>h?E5A z25wmZV_F%5NSa6i0DYtatdfaiOw_cXls}WNbdsl{YJ6KQbU2SH>pY=I(+DGx=%Q=H zreN2||3CWeFSq)ZR* zIMi^pqdC6(G7dFXCVl?-sj`0|07^wMZB2H&BKO<0-CdA6C(p7^_|B;62M1CL+Lec) zVP~W&6I2QR;!3N)bIY$a*lVK|ZqD}?-{d-P1f3h;oXTQ&r87b;`m6DBkX3BVSz+VP zv)&{=-N~33A+k0plOrU2c59@r)5YHy^vvf*eI*Nq`?Clk?wLJU(bF#L-^jjriqOQw zmWm)$^~c#U)7k+~&k2Gp^j{#Ywl@+|{Y}2ZBNK)tWCPB&>{oY>J_KP^-IPT^knuz8 z097qEVF*x$h5@jeI%X^HuhmbN<&(la{p6VQ&3o^?d%Y?iVig>xSE4-uvh|cIwKB^2D}R+r193(^64pF(cpsYve)s94(nUC+t{KX&uu^5i zugHpMt~m&A6(7>K!VV1IN?ajN6TnrOrRv&BYL_<_DNc0IR1~HJrX=$3Yb|2jga|@D zXsqKgyh*Y)*G_;q79HCJaYJI>!2>r|z$1+}q7$7B1HwTY)s*ox4R%63RUZ`TrfmA( zN}FIOlUDvbdBAT640}qA0Jx^ud!PP;!qn#DB=FHh(yne#XisNPkzR+lDcHYVM1lDL zUERHV=R~~5xQ))>yWe0HKfll<7OZk~eE6RJ=G`;?>OQ&j2Vq3|-&9dmka9%|Pga4e zIY;M|2D|FjzqU08A-Mm<%3W_h@IC?e;pcCE^x;SEJpIA?9%Yl$42jWd+C~TztobE& z4gOK6NlAC`V)gaC-m!1!#WT>NsFDQo>49ob-vIX;=bbn9+ZcED|M>uu`+0mwGd|t+M@08DCpX&IVH1-ll z4ADQ{@XSYuALiX+Sm-Ln)c5i=0DFV+tcy-p?PC>ehK_>hp~C@t@hE00QEK4uR4uN_ zI9@}CTv-=df8JgjUYf?D|$%J6;MEgM@3~FOd@wS2_B`h z8xm2u6D;=Vd8vF`nE+_GQ7-2W8g=~r57VFTiWjipZ}|4Y@WECYC^yWYZ%h|@VR|JTTH@EBWsDsxac@p0#uCn-k|a20v2U6IX>F28b}uXP z!lKm#*;*eAY9fij%f07o-X~>A&j$h6inDo5|NY^gWqG*TQ4(Es<~7SC$Kx)Soz6u0|aCoj_z5jw;`>o^GO4#MJ{X1x}b-~YQ(s@T)Z!>KiK#<{|z`3~!r z->9;N81sa$tkao;RSD)qzGxQ!Mg2}_y9vfjGb`2ga9fq7$%a@G(_eCH7d6K>uyI~M zoH1_pPOn|kTg%+%Nk|wbj6Lvld*fIiaWbL4bhCeYyxEF8<}WUrr0?Iu`41o)C&*5BJ7s0J5fblUxhFrt%=Twi%z#2cye{$e!gh%mU?jc zq2q3JEF(7SH0epZ7A{^H+AfCW0+m`I41%vM#wNM&{H7NXpl1z3sbU`*4k}=%iisL3 z?g_}*8_z_gVccENk|5Gjd&-MkXFDy&sav&=2pB?HKcP)e=XeDD)*H`sXXh4AoZ<1# zATq(r+{t#?EGQwFZ-#syg~+uX*8$SuQ-5|9Ot*9$&qh&i*z&jN>e z3u@Hy#u7uX?#BdU5~zYM zs;zALwCLIv2qcGp1VcjWlCRe;%Az-@oa6+s;Be=TZJFJRY_6e~0b;(;i(o7RT+i)Q z#|9ED*7B|&;=DOJAxOR3IwD0$v1ciaA~0EdoUN(E;NCba$gWdv;;GPPqd%v^@U#+x zR7h7%HQ+`Y)Z(!wO!`-*kL7RzZATEo+q!XBLM^C}fhnPrHGpW2sp zurK)pc_#wqKE*A(5!`o6-H`mR79?DOh@=q1h|F-?-4&I^^_O$UPMiQdO-L$hU0(RH zO$H_eZ_*hztnSfj<7Y-x@5It^(i&cOknE^Q!t=aY!3&0YKI$9D`iKhwV7*kCgkgK7AY2)XN2L%I}W6bL>m6^UCB1cmFbqxy!8s4P~86O#$lHdU+PX6Gy26jnDMdEpkrobEtw--(_{Fbz7E9P@cm2(m6yRe| zi%wfYNfvthrRZTQzT7g`5{ap`5&(TsqYwT<5s4r{rQJRiSO1jnE52*)~&MVN%SPF zB7|P*U`hA>CwCaJXK$l&VyDqcTt~{!U?eGeujTmo$=TRz)1*|dH>cKnyJ2H^>ED;+ z!fzREl0~Z1G;7Ov1K{Z7HBIz#gvN2cdX5fyhNCpm%aQPzyMeqLjZXi0Ng9wCkTf^1jCdW2zciXL|jt<3Hu~NMwEcg#3QT$ z0WgGGi0Kw0JvHVb1C?ngU}cC!Ak_c^B+@YhcY!d$3%femoHn{_@%iKj(1>@?UU9Za zwpXlZ`vll8nV%$kMT>LDHu2)b#~N-nNqr$aWWR^AAXfO9$&AFOyubBmhkG)jK_f?& z9J^1EENv{{B@WNOb!} zq^Hif=DOAXB$aD;w_d-B(q}E%<6dw7x1NIi2R<)GoP#QC;xFE2W0Vra5ffzwepcmw zlBGDpTZe56Q5xdiwo9e)sn4X(KUV3e8+MqWwNsFjb_tdtQ9{lpYogc!&SF_Ag4h~gy8Ai{()F7l$Rq|&CgP3t<}4~yk$y*V4F z3l}e4zH;^2^&2-43`j<%2>h8@%5hb!Eq$&(wwN`z1V^Rtma1wH>`|#Wb294{7sY*qVP3=iq}g8C0wrc>8< z)c<<`7#x8_p)pt-ouZ>JN`u=>%t|t9P%csZXMtwHiI@or!5fMWrR1nMYtBdcy4bcOK zneFcC8uxb79)db#{iS}_w{Qbo#dbQSp#Cs882D&6V&1_a5gvxQ+9haF%MxIif5Kmc z{r<8)igbTJ4)ivW>q&w6wG-LQLJ3&FK^+Jm zwhV9;A+AR{Yp8>QM#G2{Wx>fr>ZHpI?Kwdh+b}I<9U|yK2ZvNLMP=^Cn)wirSlpBg zJ?@ioX1<5@HXQk5b)a@Y*VOSgL8MTVh7A%j5(>sz$i_*rmb%y4_nSiNG^4ZXlGzBu zxRwNHEk^Iq%jzUQ1f@yrJn!fr-iL1}c_1l}<+mr&+^5Lac%f|J27Fzy1b78ZZ=G$vgdvKD`$4AI0rQ^NW zUpjkE`r~Q1<`+tJre*z+Z9GcTh_8bm7u5X~fhrZD(STO*N+~3_uJ%D^gr@CfDXip>}M{w@3~kWC+|NwYdy}rq^>-SWJOe?bb;Y ziGkPKNFSu?CQP2vljH5+2N7;A&WMZzL2w+5zZ(M7R7#6{n{O z#Gg2m_0_4i$&Rh`aPMho@_w~no*f+BY=4#a0l=5TrDx304-D6ZQe>7&SxLrA zEy%TZtdmJRCoLU{O+hlSe# n2(9rG{s=Adns=eIFL+JUL&fp`E2sX)l3(vv|4&*@ZUg`T;X1QK literal 0 HcmV?d00001 diff --git a/scripts/mobile-quality-review.mjs b/scripts/mobile-quality-review.mjs index 4483c1c..df2c6e4 100644 --- a/scripts/mobile-quality-review.mjs +++ b/scripts/mobile-quality-review.mjs @@ -297,6 +297,7 @@ try { )}; if (${Boolean(scenario.wideDisplayFont)}) { document.documentElement.style.setProperty("--oc-font-display", "ui-monospace, monospace"); + document.documentElement.style.setProperty("--font-editorial", "ui-monospace, monospace"); } })()`); @@ -437,6 +438,17 @@ try { rect(rail)?.bottom ?? 0, ), firstCardTop: rect(firstCard)?.top ?? null, + headingWordLines: (() => { + const heading = document.querySelector("h1"); + let start = 0; + return heading.textContent.split(" ").map(word => { + const range = document.createRange(); + range.setStart(heading.firstChild, start); + range.setEnd(heading.firstChild, start + word.length); + start += word.length + 1; + return range.getClientRects().length; + }); + })(), railNavOverflow: clipped( document.querySelector(".specimen-mobile-nav"), ), @@ -574,6 +586,9 @@ try { if (measurement.cardCount !== 16) { failures.push(`expected 16 cards, got ${measurement.cardCount}`); } + if (measurement.headingWordLines.some((lines) => lines !== 1)) { + failures.push("page heading breaks inside a word"); + } if (measurement.tabRootCount !== 16) { failures.push( `expected 16 card tab roots, got ${measurement.tabRootCount}`, diff --git a/scripts/verify-contracts.mjs b/scripts/verify-contracts.mjs index 8863282..664ea64 100644 --- a/scripts/verify-contracts.mjs +++ b/scripts/verify-contracts.mjs @@ -124,10 +124,53 @@ const assertions = [ specimenApp.includes("

{group}

"), ], [ - "CLI and React API tabs stay separate", - specimenApp.includes('CLI') && + "specimens frame the canvas rather than the preview toolbar", + /\.specimen-card\s*\{[^}]*border:\s*0;[^}]*background:\s*transparent;/.test( + specimenCss, + ) && + /\.specimen-preview\s*\{[^}]*border:\s*0;/.test(specimenCss) && + /\.specimen-preview__canvas\s*\{[^}]*border:\s*1px solid var\(--border\);[^}]*background:\s*var\(--background\);/.test( + specimenCss, + ), + ], + [ + "documentation headings use the interface font", + /\.specimen-hero h1\s*\{[^}]*font-family:\s*var\(--font-display\);/.test( + specimenCss, + ) && + /\.catalog-group__header h2\s*\{[^}]*font-family:\s*var\(--font-display\);/.test( + specimenCss, + ), + ], + [ + "documentation chrome uses neutral surfaces", + /\.specimen-topbar\s*\{[^}]*background:\s*var\(--background\);/.test( + specimenCss, + ) && + /\.specimen-rail\s*\{[^}]*background:\s*var\(--background\);/.test( + specimenCss, + ), + ], + [ + "topbar centers search between balanced side columns", + /\.specimen-topbar__inner\s*\{[^}]*display:\s*grid;[^}]*min-height:\s*3\.25rem;[^}]*grid-template-columns:\s*minmax\(0, 1fr\) minmax\(0, min\(22rem, 40vw\)\) minmax\(\s*0,\s*1fr\s*\);/.test( + specimenCss, + ), + ], + [ + "mobile examples retain intrinsic control widths", + /\.specimen-stage\s*\{[^}]*justify-items:\s*center;/.test(specimenFixes) && + !/\.specimen-stage > \*\s*\{[^}]*\n\s*width:\s*100%;/.test(specimenFixes), + ], + [ + "code snippets use a readable base size", + /\.specimen-command\s*\{[^}]*font-size:\s*0\.75rem;/.test(specimenCss), + ], + [ + "Install and Import tabs stay separate", + specimenApp.includes('Install') && specimenApp.includes( - 'React API', + 'Import', ) && !specimenApp.includes('API') && specimenApp.includes('language="typescript"') && @@ -236,19 +279,19 @@ const assertions = [ ], [ "text resize keeps shell chrome and hero contained", - specimenFixes.includes( - ".specimen-topbar__inner {\n display: flex;\n flex-wrap: wrap;", + /\.specimen-topbar__leading\s*\{[^}]*min-width:\s*0;[^}]*flex-wrap:\s*wrap;/.test( + specimenCss, ) && - /\.specimen-topbar__actions\s*\{[^}]*display:\s*flex;[^}]*flex:\s*1 0 100%;[^}]*flex-wrap:\s*wrap;/.test( - specimenFixes, + specimenCss.includes( + 'grid-template-areas: "leading actions" "search search";', ) && - /\.specimen-search\s*\{[^}]*width:\s*auto;[^}]*min-width:\s*7rem;[^}]*flex:\s*1 1 10rem;/.test( - specimenFixes, + /\.specimen-search\s*\{[^}]*width:\s*100%;[^}]*min-width:\s*0;/.test( + specimenCss, ) && /\.specimen-main__inner[^{}]*\{[^}]*box-sizing:\s*border-box;/.test( specimenFixes, ) && - /\.specimen-stats\s*\{[^}]*grid-template-columns:\s*repeat\(2,\s*minmax\(0,\s*1fr\)\);/.test( + /\.specimen-hero h1\s*\{[^}]*overflow-wrap:\s*anywhere;/.test( specimenStyles, ), ], @@ -306,12 +349,7 @@ const specimenSelectorPairs = [ ], ["hero", 'className="specimen-hero"', ".specimen-hero"], ["hero copy", 'className="specimen-hero__copy"', ".specimen-hero__copy"], - ["hero stats", 'className="specimen-stats"', ".specimen-stats"], - [ - "catalog eyebrow", - 'className="catalog-group__eyebrow numeric"', - ".catalog-group__eyebrow", - ], + ["heading links", 'className="specimen-permalink"', ".specimen-permalink"], [ "catalog summary", 'className="catalog-group__summary"', @@ -334,6 +372,8 @@ const obsoleteSpecimenSelectors = [ ".specimen-rail__intro", ".specimen-eyebrow", ".catalog-group__grid", + ".catalog-group__eyebrow", + ".catalog-group__count", ]; for (const selector of obsoleteSpecimenSelectors) { diff --git a/scripts/visual-review.mjs b/scripts/visual-review.mjs index 7da0397..644fe06 100644 --- a/scripts/visual-review.mjs +++ b/scripts/visual-review.mjs @@ -284,7 +284,7 @@ async function pressKey(client, key, code, keyCode, modifiers = 0) { } } -async function auditInteractions(client, scenario) { +async function auditNavigation(client, scenario) { await navigate( client, new URL("/?navigation-audit#session-header", baseUrl).href, @@ -319,6 +319,13 @@ async function auditInteractions(client, scenario) { document.querySelector('.specimen-rail__nav a[href="#mode-switch"]').getAttribute("aria-current") === "location"`, "component navigation", ); + await clickElement(client, "#send-control .specimen-permalink"); + await waitForValue( + client, + `location.hash === "#send-control" && + document.querySelector('.specimen-rail__nav a[href="#send-control"]').getAttribute("aria-current") === "location"`, + "component heading permalink", + ); await evaluateValue( client, `document.getElementById("plan-row").scrollIntoView({ block: "start" })`, @@ -328,6 +335,9 @@ async function auditInteractions(client, scenario) { `document.querySelector('.specimen-rail__nav a[href="#plan-row"]').getAttribute("aria-current") === "location"`, "scroll-synchronized component navigation", ); +} + +async function auditSearchRecovery(client, scenario) { await pressKey(client, "k", "KeyK", 75, 2); await waitForValue( client, @@ -341,6 +351,68 @@ async function auditInteractions(client, scenario) { 'location.hash === ""', "filtered-out component fragment cleared", ); + await waitForValue( + client, + `document.querySelector('[role="status"][aria-label="Search results"]').textContent === "0 results"`, + "empty search feedback", + ); + const searchLayout = await evaluateValue( + client, + `(() => { + const root = document.documentElement; + const emptyStyle = getComputedStyle(document.querySelector(".catalog-empty")); + const elements = [...document.querySelectorAll(".catalog-results, .catalog-clear-search, .catalog-empty, .catalog-empty h2, .catalog-empty p")]; + return { + fontSize: getComputedStyle(root).fontSize, + emptyPadding: parseFloat(emptyStyle.paddingLeft), + emptyMinHeight: parseFloat(emptyStyle.minHeight), + documentOverflow: Math.max(0, root.scrollWidth - root.clientWidth), + clipped: elements.filter(element => element.scrollWidth > element.clientWidth + 1) + .map(element => ({ element: element.className || element.tagName, overflow: element.scrollWidth - element.clientWidth })), + }; + })()`, + ); + if (scenario.textScale) { + await evaluateValue( + client, + `(async () => { + document.querySelector(".catalog-results").scrollIntoView({ block: "start" }); + await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve))); + })()`, + true, + ); + } + const emptySearch = await client.send("Page.captureScreenshot", { + format: "png", + fromSurface: true, + captureBeyondViewport: false, + }); + await writeFile( + path.join(outputDir, `${scenario.name}-search-empty.png`), + Buffer.from(emptySearch.data, "base64"), + ); + if ( + searchLayout.documentOverflow > 1 || + searchLayout.clipped.length || + searchLayout.emptyPadding > 32 || + searchLayout.emptyMinHeight > 320 + ) { + throw new Error( + `${scenario.name}: search layout ${JSON.stringify(searchLayout)}`, + ); + } + await clickElement(client, ".catalog-clear-search"); + await waitForValue( + client, + `(() => { + const input = document.querySelector(".specimen-search input"); + const bounds = input.getBoundingClientRect(); + return document.activeElement === input && input.value === "" && + document.querySelectorAll(".specimen-card").length === 16 && + bounds.top >= 0 && bounds.bottom <= innerHeight; + })()`, + "clear search restores visible input focus", + ); await pressKey(client, "a", "KeyA", 65, selectAllModifier); await client.send("Input.insertText", { text: "Context meter" }); await waitForValue( @@ -351,14 +423,19 @@ async function auditInteractions(client, scenario) { document.querySelectorAll("#component-picker option").length === 2`, "filtered specimen", ); - await pressKey(client, "a", "KeyA", 65, selectAllModifier); - await pressKey(client, "Backspace", "Backspace", 8); + await pressKey(client, "Escape", "Escape", 27); await waitForValue( client, - `document.querySelectorAll(".specimen-card").length === 16`, + `document.querySelectorAll(".specimen-card").length === 16 && + document.activeElement === document.querySelector(".specimen-search input")`, "restored catalog", ); + return searchLayout; +} +async function auditInteractions(client, scenario) { + await auditNavigation(client, scenario); + await auditSearchRecovery(client, scenario); await clickElement(client, ".scheme-control"); await evaluateValue( client, @@ -455,6 +532,30 @@ async function auditInteractions(client, scenario) { "stop control", ); + const sceneLoaded = client.waitForEvent("Page.loadEventFired"); + await clickElement(client, "#run-rail .specimen-lab-link"); + await sceneLoaded; + await waitForValue( + client, + `location.pathname === "/lab" && location.hash === "#run-rail" && document.querySelector(".assembled-lab__tabs [aria-selected=true]")?.dataset.sceneId === "run-rail"`, + "direct Library-to-Lab scene link", + ); + await evaluateValue(client, 'document.querySelector(".skip-link").focus()'); + await pressKey(client, "Enter", "Enter", 13); + await waitForValue( + client, + `document.activeElement?.id === "specimen-main" && location.hash === "#run-rail" && document.querySelector(".assembled-lab__tabs [aria-selected=true]")?.dataset.sceneId === "run-rail"`, + "Lab skip link preserves the active scene", + ); + const libraryLoaded = client.waitForEvent("Page.loadEventFired"); + await clickElement(client, ".lab-footer a"); + await libraryLoaded; + await waitForValue( + client, + `location.pathname === "/" && location.hash === "#run-rail" && Boolean(document.querySelector("#run-rail .specimen-lab-link"))`, + "return to the matching library specimen", + ); + const loaded = client.waitForEvent("Page.loadEventFired"); await clickElement(client, '.surface-switcher a[href="/lab"]'); await loaded; @@ -465,15 +566,27 @@ async function auditInteractions(client, scenario) { await clickElement(client, 'button[aria-label="Next scene"]'); await waitForValue( client, - `document.querySelector(".assembled-lab__tabs [aria-selected=true]").dataset.sceneId === "run-rail"`, + `location.hash === "#run-rail" && document.querySelector(".assembled-lab__tabs [aria-selected=true]").dataset.sceneId === "run-rail"`, "next scene", ); await clickElement(client, 'button[aria-label="Previous scene"]'); await waitForValue( client, - `document.querySelector(".lab-scene-panel:not([hidden]) textarea")?.value === "Keep this carousel draft"`, + `location.hash === "#composer" && document.querySelector(".lab-scene-panel:not([hidden]) textarea")?.value === "Keep this carousel draft"`, "carousel draft preservation", ); + await evaluateValue(client, 'location.hash = "#messages"'); + await waitForValue( + client, + `document.querySelector(".assembled-lab__tabs [aria-selected=true]")?.dataset.sceneId === "messages"`, + "external scene hash", + ); + await evaluateValue(client, "history.back()"); + await waitForValue( + client, + `location.hash === "#composer" && document.querySelector(".lab-scene-panel:not([hidden]) textarea")?.value === "Keep this carousel draft"`, + "browser Back restores the scene without losing its draft", + ); await clickElement(client, ".assembled-lab__tabs [data-scene-id=composer]"); await pressKey(client, "ArrowRight", "ArrowRight", 39); await waitForValue( @@ -578,11 +691,15 @@ async function auditInventory(client, scenario) { const overlay = card.querySelector(".specimen-source-overlay"); const canvas = card.querySelector(".specimen-preview__canvas"); const source = overlay.querySelector("code").textContent; + const codeViewport = overlay.querySelector("pre"); + const codeStyle = getComputedStyle(codeViewport); const result = { source, + visibleCodeLines: (codeViewport.clientHeight - parseFloat(codeStyle.paddingTop) - parseFloat(codeStyle.paddingBottom)) / parseFloat(codeStyle.lineHeight), + permalink: card.querySelector(".specimen-permalink")?.getAttribute("href") === "#" + card.id, stable: Math.abs(before.page - document.documentElement.scrollHeight) < 1 && Math.abs(before.card - card.getBoundingClientRect().height) < 1 && Math.abs(before.scroll - scrollY) < 1, - covers: Math.abs(overlay.getBoundingClientRect().height - canvas.getBoundingClientRect().height) < 1 && + covers: Math.abs(overlay.getBoundingClientRect().height - canvas.clientHeight) < 1 && card.querySelector(".specimen-live-panel").inert, overflow: Math.max(card.scrollWidth - card.clientWidth, stage.scrollWidth - stage.clientWidth), }; @@ -601,6 +718,9 @@ async function auditInventory(client, scenario) { if ( !result.stable || !result.covers || + !result.permalink || + !Number.isFinite(result.visibleCodeLines) || + result.visibleCodeLines < 2 || result.overflow > 1 || result.source !== expected ) { @@ -631,6 +751,7 @@ async function auditInventory(client, scenario) { screenshot, stable: result.stable, overflow: result.overflow, + visibleCodeLines: result.visibleCodeLines, }); } return { @@ -683,7 +804,7 @@ const scenarios = [ legacyDensity: "default", mobile: false, expected: "library", - revealCodeTab: "CLI", + revealCodeTab: "Install", codeTargetId: "mode-switch", expectedCode: 'pnpm dlx shadcn@latest add "https://ui.opencoven.ai/r/mode-switch.json"', @@ -703,7 +824,7 @@ const scenarios = [ legacyDensity: "compact", mobile: false, expected: "library", - revealCodeTab: "React API", + revealCodeTab: "Import", codeTargetId: "session-header", expectedCode: 'import { SessionHeader } from "@opencoven/ui/blocks/session-header";', @@ -719,7 +840,7 @@ const scenarios = [ mobile: true, expected: "library", textScale: 2, - revealCodeTab: "React API", + revealCodeTab: "Import", codeTargetId: "session-header", expectedCode: 'import { SessionHeader } from "@opencoven/ui/blocks/session-header";', @@ -1188,7 +1309,11 @@ try { [a.left + 8, a.bottom - 8], [a.right - 8, a.bottom - 8], [a.left + a.width / 2, a.top + a.height / 2], ]; - return ["top","left","width","height"].every(key => Math.abs(a[key] - b[key]) < 1) && + const interior = { + top: b.top + canvas.clientTop, left: b.left + canvas.clientLeft, + width: canvas.clientWidth, height: canvas.clientHeight, + }; + return ["top","left","width","height"].every(key => Math.abs(a[key] - interior[key]) < 1) && live.inert && live.getAttribute("aria-hidden") === "true" && samplePoints.every(([x, y]) => overlay.contains(document.elementFromPoint(x, y))); })(), @@ -1208,6 +1333,22 @@ try { previewVisible: isVisible(codeCard?.querySelector(".specimen-preview")), logoMask: getComputedStyle(document.querySelector(".specimen-brand__mark > span")).maskImage, topbarVisible: isVisible(topbar), + headerPresentation: (() => { + const search = document.querySelector(".specimen-search"); + const header = document.querySelector(".specimen-topbar__inner"); + const searchBox = bounds(search); + const headerBox = bounds(header); + const theme = document.querySelector(".scheme-control"); + return { + searchCenterOffset: searchBox + ? Math.abs(searchBox.left + searchBox.width / 2 - (headerBox.left + headerBox.width / 2)) + : null, + height: bounds(topbar).height, + searchHeight: searchBox?.height ?? null, + themeText: theme.textContent.trim(), + titleSize: parseFloat(getComputedStyle(document.querySelector("h1")).fontSize), + }; + })(), railVisible: isVisible(rail), catalogLayout: (() => { const inner = document.querySelector(".specimen-main__inner"); @@ -1238,6 +1379,27 @@ try { heroHeight: heroBounds?.height ?? null, contentTop: contentBounds?.top ?? null, cardCount: cards.length, + smallPreviewHeight: bounds(document.querySelector("#mode-switch .specimen-stage"))?.height, + previewPresentation: (() => { + const card = document.querySelector("#mode-switch"); + if (!card) return null; + const preview = card.querySelector(".specimen-preview"); + const canvas = card.querySelector(".specimen-preview__canvas"); + const tabs = card.querySelector(".specimen-view-tabs"); + return { + top: bounds(preview).top, + bottom: bounds(preview).bottom, + articleBorder: parseFloat(getComputedStyle(card).borderTopWidth), + previewBorder: parseFloat(getComputedStyle(preview).borderTopWidth), + canvasBorder: parseFloat(getComputedStyle(canvas).borderTopWidth), + tabsAboveCanvas: bounds(tabs).bottom <= bounds(canvas).top, + textOnlyTabs: tabs.querySelectorAll("svg").length === 0, + neutralCanvas: getComputedStyle(canvas).backgroundColor === getComputedStyle(document.body).backgroundColor, + stageWidth: bounds(card.querySelector(".specimen-stage")).width, + controlWidth: bounds(card.querySelector('[data-slot="mode-switch"]')).width, + codeSize: parseFloat(getComputedStyle(card.querySelector(".specimen-command")).fontSize), + }; + })(), stateFooterCount: document.querySelectorAll(".specimen-states").length, libraryColumns: [...document.querySelectorAll(".specimen-grid")].map(grid => getComputedStyle(grid).gridTemplateColumns.split(" ").length), runRailColumns: (() => { @@ -1246,8 +1408,84 @@ try { })(), statesTabCount: [...document.querySelectorAll(".specimen-card [role=tab]")].filter(tab => tab.textContent.trim() === "States").length, brandAction: getComputedStyle(root).getPropertyValue("--oc-action").trim(), + backgroundColors: ["body", ".specimen-topbar", ".specimen-rail", ".specimen-card", ".specimen-preview__canvas", ".assembled-lab__stage"] + .flatMap(selector => { + let element = document.querySelector(selector); + if (!element) return []; + let color = getComputedStyle(element).backgroundColor; + while (color === "rgba(0, 0, 0, 0)" && element.parentElement) { + element = element.parentElement; + color = getComputedStyle(element).backgroundColor; + } + return [{ selector, color }]; + }), + presentation: root.dataset.presentation, + presentationBackground: getComputedStyle(root).getPropertyValue("--oc-bg").trim(), + neutralHeading: getComputedStyle(document.querySelector("h1")).color === getComputedStyle(document.body).color, + documentationChrome: { + unifiedSurfaces: [topbar, rail].filter(Boolean).every(element => + getComputedStyle(element).backgroundColor === getComputedStyle(document.body).backgroundColor), + sansSectionHeadings: [...document.querySelectorAll(".catalog-group h2")].every(element => + getComputedStyle(element).fontFamily.includes("Inter")), + sidebarBorder: rail ? parseFloat(getComputedStyle(rail).borderInlineEndWidth) : 0, + decorativeLabels: document.querySelectorAll(".catalog-group__eyebrow, .catalog-group__count, .specimen-rail__nav small, .specimen-brand small").length, + }, + presentationContrast: (() => { + const style = getComputedStyle(root); + const value = name => style.getPropertyValue(name).trim(); + const canvas = document.createElement("canvas"); + canvas.width = canvas.height = 1; + const context = canvas.getContext("2d"); + const luminance = rgb => { + const linear = [...rgb].slice(0, 3).map(channel => { + const value = channel / 255; + return value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4; + }); + return linear[0] * 0.2126 + linear[1] * 0.7152 + linear[2] * 0.0722; + }; + const pairs = [ + ["text", "--foreground", "--background", 4.5], + ["muted text", "--muted-foreground", "--background", 4.5], + ["preview captions", "--muted-foreground", "--oc-surface-2", 4.5], + ["primary action", "--primary-foreground", "--primary", 4.5], + ["selected controls", "--presence", "--oc-surface-2", 4.5], + ["focus ring", "--ring", "--background", 3], + ["input boundary", "--input", "--card", 3], + ["success", "--success", "--card", 4.5], + ["success surface", "--success", "--oc-success-bg", 4.5], + ["warning", "--warning", "--card", 4.5], + ["warning surface", "--warning", "--oc-pending-bg", 4.5], + ["error", "--destructive", "--card", 4.5], + ["information", "--information", "--card", 4.5], + ]; + const resolvedPairs = pairs.map(([name, foreground, background, minimum]) => + [name, value(foreground), value(background), minimum] + ); + const count = document.querySelector(".specimen-rail__nav small"); + if (count) { + resolvedPairs.push(["navigation count", getComputedStyle(count).color, getComputedStyle(document.body).backgroundColor, 4.5]); + } + for (const token of visibleSyntaxTokens) { + const command = token.closest(".specimen-command"); + if (command) { + resolvedPairs.push([token.className, getComputedStyle(token).color, getComputedStyle(command).backgroundColor, 4.5]); + } + } + return resolvedPairs.map(([name, foreground, background, minimum]) => { + context.clearRect(0, 0, 1, 1); + context.fillStyle = background; + context.fillRect(0, 0, 1, 1); + const back = luminance(context.getImageData(0, 0, 1, 1).data); + context.fillStyle = foreground; + context.fillRect(0, 0, 1, 1); + const front = luminance(context.getImageData(0, 0, 1, 1).data); + return { name, minimum, ratio: (Math.max(front, back) + 0.05) / (Math.min(front, back) + 0.05) }; + }); + })(), brandSpacing: getComputedStyle(root).getPropertyValue("--oc-space-6").trim(), displayFont: getComputedStyle(document.querySelector("h1")).fontFamily, + interfaceFont: getComputedStyle(document.body).fontFamily, + loadedFonts: [...document.fonts].filter(face => face.status === "loaded").map(face => face.family), forcedSelectionVisible: (() => { const tab = document.querySelector(".specimen-view-tabs [aria-selected=true]"); if (!tab) return false; @@ -1319,6 +1557,78 @@ try { ); const failures = []; + const header = layout.headerPresentation; + if ( + (header.searchCenterOffset !== null && header.searchCenterOffset > 1) || + header.themeText !== "" || + (!scenario.textScale && + scenario.width > 1088 && + (header.height > 53 || + header.titleSize > 28 || + (header.searchHeight !== null && header.searchHeight > 36))) + ) { + failures.push( + `centered slim header regressed: ${JSON.stringify(header)}`, + ); + } + if (layout.previewPresentation) { + const preview = layout.previewPresentation; + if ( + preview.articleBorder !== 0 || + preview.previewBorder !== 0 || + preview.canvasBorder !== 1 || + !preview.tabsAboveCanvas || + !preview.textOnlyTabs || + !preview.neutralCanvas || + preview.codeSize < 12 + ) { + failures.push( + `preview hierarchy or source readability regressed: ${JSON.stringify(preview)}`, + ); + } + if ( + !layout.documentationChrome.unifiedSurfaces || + !layout.documentationChrome.sansSectionHeadings || + layout.documentationChrome.sidebarBorder !== 0 || + layout.documentationChrome.decorativeLabels !== 0 + ) { + failures.push( + `documentation chrome regressed: ${JSON.stringify(layout.documentationChrome)}`, + ); + } + if (!scenario.textScale) { + if (preview.controlWidth > preview.stageWidth - 64) { + failures.push("small controls are stretched across their preview"); + } + if (layout.scrollY < 1 && scenario.height >= 800) { + const maximumTop = + layout.viewportWidth > 880 + ? layout.topbarHeight + 340 + : layout.mobileChromeBottom + 380; + if (preview.top > maximumTop || preview.bottom > scenario.height) { + failures.push( + `first preview is too far below the fold: ${preview.top}-${preview.bottom}`, + ); + } + } + } + } + for (const pair of layout.presentationContrast) { + if (!Number.isFinite(pair.ratio) || pair.ratio < pair.minimum) { + failures.push( + `${pair.name} contrast ${pair.ratio.toFixed(2)} is below ${pair.minimum}`, + ); + } + } + if ( + scenario.expected === "library" && + !scenario.textScale && + layout.smallPreviewHeight > 161 + ) { + failures.push( + `small preview has excessive empty space: ${layout.smallPreviewHeight}px`, + ); + } if (scenario.expected === "library") { const nav = layout.catalogLayout; const desktop = layout.viewportWidth > 880; @@ -1361,16 +1671,43 @@ try { ) { failures.push("run rail does not use the available preview width"); } + if (!scenario.forcedColors) { + for (const surface of layout.backgroundColors) { + const channels = surface.color + .match(/^rgb\(([\d.]+),\s*([\d.]+),\s*([\d.]+)\)$/) + ?.slice(1) + .map(Number); + if (!channels || Math.max(...channels) - Math.min(...channels) > 1) { + failures.push( + `non-neutral background at ${surface.selector}: ${surface.color}`, + ); + } + } + } if ( layout.brandAction !== - (scenario.scheme === "dark" ? "#8e3dff" : "#7a22ee") || + (scenario.scheme === "dark" ? "#9386d0" : "#6859ac") || + layout.presentation !== "coven" || + layout.presentationBackground !== + (scenario.scheme === "dark" ? "#121212" : "#f5f5f5") || + !layout.neutralHeading || layout.brandSpacing !== "1.5rem" || - !layout.displayFont.startsWith("Geist") + !layout.displayFont.includes("Inter") || + !layout.interfaceFont.includes("Inter") ) { failures.push( - `canonical brand mismatch: ${layout.brandAction}, ${layout.brandSpacing}, ${layout.displayFont}`, + `Coven presentation mismatch: ${layout.brandAction}, ${layout.presentationBackground}, ${layout.brandSpacing}, ${layout.displayFont}`, ); } + const requiredFonts = ["Inter"]; + if (layout.codeSnippetCount > 0) requiredFonts.push("JetBrains Mono"); + for (const family of requiredFonts) { + if (!layout.loadedFonts.some((loaded) => loaded.includes(family))) { + failures.push( + `visible content has not loaded the bundled ${family} font`, + ); + } + } if ( !layout.topbarVisible || (scenario.expected !== "lab" && !layout.railVisible) || @@ -1645,6 +1982,9 @@ try { if (scenario.auditInventory) { layout.inventory = await auditInventory(client, scenario); } + if (scenario.expected === "library" && scenario.textScale) { + layout.searchRecovery = await auditSearchRecovery(client, scenario); + } if (scenario.auditInteractions) { layout.interactions = await auditInteractions(client, scenario); } diff --git a/scripts/zoom-probe.mjs b/scripts/zoom-probe.mjs index 3c61b38..5d1141b 100644 --- a/scripts/zoom-probe.mjs +++ b/scripts/zoom-probe.mjs @@ -1,10 +1,9 @@ /* - * Detail probe for the specimen shell's ritual-tech finish. + * Detail probe for the specimen shell's documentation frame and typography. * * visual-review.mjs captures whole pages at 1x, which is the right scale for - * layout regressions but too coarse to confirm a 1px circuitry trace actually - * paints. This grabs one card at 3x and asserts the decorative layers resolve - * to real colours rather than to an unresolved custom property. + * layout regressions. This grabs one card at 3x and records the canvas border, + * heading typography, and background for closer visual inspection. * * The evaluate callbacks below run in the page, not in Node. */ @@ -39,25 +38,27 @@ await card.scrollIntoViewIfNeeded(); await page.waitForTimeout(300); await card.screenshot({ path: `${outDir}/zoom-card.png` }); -// Prove the circuitry trace is painted, not merely declared. -const trace = await card.evaluate((node) => { - const style = getComputedStyle(node, "::before"); - return { - content: style.content, - height: style.height, - backgroundImage: style.backgroundImage.slice(0, 160), - display: style.display, - }; -}); +const frame = await card + .locator(".specimen-preview__canvas") + .evaluate((node) => { + const style = getComputedStyle(node); + return { + borderWidth: style.borderTopWidth, + borderColor: style.borderTopColor, + borderRadius: style.borderRadius, + background: style.backgroundColor, + }; + }); -const eyebrow = await page - .locator(".catalog-group__eyebrow") +const heading = await page + .locator(".catalog-group h2") .first() .evaluate((node) => { - const style = getComputedStyle(node, "::before"); + const style = getComputedStyle(node); return { - width: style.width, - backgroundImage: style.backgroundImage.slice(0, 120), + fontFamily: style.fontFamily, + fontSize: style.fontSize, + fontWeight: style.fontWeight, }; }); @@ -79,7 +80,7 @@ const animations = await page.evaluate(() => console.log( JSON.stringify( - { trace, eyebrow, bodyLayers, runningAnimations: animations }, + { frame, heading, bodyLayers, runningAnimations: animations }, null, 2, ),