Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions apps/ui/AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Particle Component Development Guide for @coss/ui

For blueprint illustration work, read [Plotter](skills/plotter/SKILL.md). It records the visual direction, reference images, and review feedback for the production heroes and the unlinked `/ui/plotter` showcase.

This guide provides comprehensive instructions for creating new particle components that match the existing library's patterns and best practices. Use this when creating equivalents of origin components or building new particles from scratch.

---
Expand Down Expand Up @@ -1377,3 +1379,13 @@ const items = [
---

This guide should enable autonomous creation of particles that match the existing library's patterns, style, and best practices. Always refer to existing particles in the same category for consistency.

<!-- BEGIN:nextjs-agent-rules -->

## This is NOT the Next.js you know

This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices.

This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean.

<!-- END:nextjs-agent-rules -->
83 changes: 83 additions & 0 deletions apps/ui/app/plotter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Plotter illustrations

Direct route: `/ui/plotter`. This showcase is intentionally absent from site navigation and the sitemap, and its metadata disables indexing. The former `/ui/illustration-lab` and `/ui/illustration-lab-prototype` URLs redirect here with their query parameters.

The gallery presents flat monochrome illustrations, organized by subject with category filters and native-size inline drawings. Stable illustration IDs preserve links and feedback history; `?variant=<study-id>` selects the category and scrolls to the drawing. Discarded drawings have been removed from the collection; their critique remains in the Plotter skill’s feedback ledger. Inclusion in this curated collection does not make every drawing an approved visual anchor.

## Fresh-session examples

The user selected the original versions without added background grids from the 2026-10-31 evaluation:

| Code | Illustration | Study ID |
| --- | --- | --- |
| C04 | Shared frame | `particles-shared-frame` |
| K09 | Appointment buffers | `cal-appointment-buffers` |
| S15 | No matching results | `state-no-matching-results` |

The original SVGs, prompts, and feedback are preserved in [`../../plotter-evals/2026-10-31`](../../plotter-evals/2026-10-31). These catalog additions retain their own review history; they do not change the skill’s earlier approved reference set.

## Production selections

| Illustration | Selected page | Showcase source |
| --- | --- | --- |
| P00 | Main | `./primitives-blueprint.tsx` |
| C03 | Particles | `./particles-blueprint.tsx` |
| E02 | 404 | `./not-found-blueprint.tsx` |

The showcase owns these drawing snapshots and its `BlueprintDrawing` wrapper. It can ship independently of the production illustration integration in PR #912. Original C00 and E00 remain separate examples. When a selected production drawing changes, update its showcase snapshot explicitly if that revision should also appear in the gallery.

## Expanded collection

The initial expansion added 78 studies. After the user removed 36 entries during visual review, 42 additions remain alongside the earlier 58: **100 illustrations** in total. The user explicitly liked the revised registration, delegation, and iris drawings. Retaining the other additions does not approve them individually or change the 27 approved visual references. Existing codes and IDs are preserved without renumbering; removed sources remain in the [review archive](../../plotter-evals/2026-10-31/catalog-expansion/removed/).

Editable sources and metadata live in [`drawings/`](drawings/). Preserve the IDs in [`drawings/manifest.json`](drawings/manifest.json); they also drive category filters and `?variant=` links. Construction and reference choices are recorded in the manifest and SVG metadata. Edit the SVG source, then regenerate the React components from the repository root:

```sh
python3 apps/ui/scripts/build-plotter-drawings.py
```

The generated `expanded-studies.tsx` uses the existing drawing wrapper and gives every rendered instance unique definition IDs. Format the generated file with the repository formatter. Do not edit it directly. The [expansion notes](../../plotter-evals/2026-10-31/catalog-expansion/notes.md) record the feedback and visual review scope.

The visual grammar uses the skill’s editable SVGs as its source. Hover, keyboard focus, or the touch toggle isolates the topic’s marks; `data-grammar-focus` identifies those elements in the source. After editing `skills/plotter/references/grammar/*.svg`, run `python3 apps/ui/scripts/build-plotter-grammar.py` from the repository root and format `visual-grammar-drawings.tsx`. The page explanations live in `visual-grammar-content.ts`.

Isolation preserves the original stroke treatment. The grid’s missing shared segments use `data-grammar-isolation-only` and are hidden in the complete drawing, then revealed at the same grid weight when the surrounding geometry disappears. Callouts are not part of a focused element unless that topic explains callouts.

## Plotter skill

The visible Visual grammar section precedes the illustrations and is reachable from the top navigation at `/ui/plotter#visual-grammar`.

The reusable style instructions, visual anchors, and feedback ledger live in [`../../skills/plotter`](../../skills/plotter). Keep specific user feedback separate from interpretations; a new revision is unreviewed until the user responds. Add illustrations to their subject collection and preserve existing IDs when revising them.

## Shared foundation pilot

AG01 and W05 opt into the [portable drafting foundation](../../skills/plotter/references/foundation.md). AG01 keeps its editable SVG; its transfer arrow and width dimension come from the shared helpers. W05 uses the same arrow helper and shares rectangular hatching between its static and animated drawings. Its timeline and playback controls remain local and unchanged.

From the repository root, regenerate standard styles, AG01's marked fragments, and the SVG-to-JSX output after relevant source changes:

```sh
python3 apps/ui/skills/plotter/scripts/build-standard.py
node apps/ui/scripts/build-plotter-registration.mjs
python3 apps/ui/scripts/build-plotter-drawings.py
```

The registration script and portable example both use the skill-owned [`renderRegistration` recipe](../../skills/plotter/assets/examples/render-registration.mjs). It reads its width witnesses from the SVG's `registry-record` rectangle and local unit, and changes only marked helper fragments and the embedded generated stylesheet. Preserve those markers when editing the surrounding composition.

To produce the two self-contained static SVGs, run:

```sh
bun apps/ui/scripts/export-plotter-pilots.tsx /tmp/plotter-exports
```

`BlueprintDrawing` enables generated role styles only when passed `roles`. Legacy drawings keep their existing inherited defaults. Keep `data-plotter-role` on the original styled groups, and avoid applying opacity twice. The rest of the catalogue and the approved snapshot manifest are outside this pilot.

## Using Plotter from the page

`#use-plotter` provides installation, copyable drawing/animation briefs, and editable output downloads. `#plotter-tokens` shows supported CSS overrides on a separate W05 instance and reads token defaults from the shared standard. Existing catalogue filters, deep links, animation controls, and grammar isolation retain their behavior.

The page serves a complete local skill ZIP, two resolved SVGs, and a portable W05 HTML player from `public/plotter/downloads/`. Regenerate these artifacts after changing the packaged skill or the pilot drawings:

```sh
bun apps/ui/scripts/build-plotter-downloads.tsx
```

The script packages the current sources rather than assuming a branch has been published. It does not run checks or tests. Download URLs use the app's configured base path. The HTML embeds the current W05 motion and static SVGs plus generated role styles; it needs no coss runtime or external font. New presentation code lives in `plotter-intro.tsx`, `plotter-usage.tsx`, `plotter-prompts.tsx`, and `plotter-token-guide.tsx`.
97 changes: 97 additions & 0 deletions apps/ui/app/plotter/appointment-buffers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { BlueprintDrawing } from "./blueprint-drawing";

export function AppointmentBuffersStudy() {
return (
<BlueprintDrawing>
<g strokeWidth="0.6" opacity="0.24">
<path d="M76 180H96M464 180H484M76 260H96M464 260H484" />
</g>
<g strokeWidth="0.65" opacity="0.5">
<path d="M100 120V150M100 170V176M172 120V150M172 170V176M388 120V150M388 170V176M460 120V150M460 170V176" />
<path d="M100 132H113M159 132H172M172 132H255M305 132H388M388 132H401M447 132H460" />
<path d="M96 136l8-8M168 136l8-8M384 136l8-8M456 136l8-8" />
</g>
<g
fill="currentColor"
stroke="none"
fontSize="10"
textAnchor="middle"
opacity="0.5"
>
<text x="136" y="135">
10 min
</text>
<text x="280" y="135">
30 min
</text>
<text x="424" y="135">
10 min
</text>
</g>
<g strokeWidth="0.65" opacity="0.27">
<path d="M100 188L108 180 M100 200L120 180 M100 212L132 180 M100 224L144 180 M100 236L156 180 M100 248L168 180 M100 260L172 188 M172 200L112 260 M172 212L124 260 M172 224L136 260 M172 236L148 260 M172 248L160 260 M388 188L396 180 M388 200L408 180 M388 212L420 180 M388 224L432 180 M388 236L444 180 M388 248L456 180 M388 260L460 188 M460 200L400 260 M460 212L412 260 M460 224L424 260 M460 236L436 260 M460 248L448 260" />
</g>
<g strokeWidth="0.65" opacity="0.5">
<path d="M172 180H100V260H172M388 180H460V260H388" />
</g>
<g strokeWidth="0.95">
<path d="M172 180H388V260H172Z" />
</g>
<g
fill="currentColor"
stroke="none"
fontSize="11"
textAnchor="middle"
opacity="0.72"
>
<text x="136" y="164">
Buffer before
</text>
<text x="280" y="224">
Appointment
</text>
<text x="424" y="164">
Buffer after
</text>
</g>
<g strokeWidth="0.6" opacity="0.24">
<path d="M100 264V274M172 264V274M388 264V274M460 264V274" />
</g>
<g
fill="currentColor"
stroke="none"
fontSize="10"
textAnchor="middle"
opacity="0.5"
>
<text x="100" y="287">
09:50
</text>
<text x="172" y="287">
10:00
</text>
<text x="388" y="287">
10:30
</text>
<text x="460" y="287">
10:40
</text>
</g>
<g strokeWidth="0.65" opacity="0.5">
<path d="M100 300V336M460 300V336M100 328H230M330 328H460" />
<path d="M96 332l8-8M456 332l8-8" />
</g>
<text
x="280"
y="331"
fill="currentColor"
stroke="none"
fontSize="10"
textAnchor="middle"
opacity="0.5"
>
50 min reserved
</text>
</BlueprintDrawing>
);
}
53 changes: 53 additions & 0 deletions apps/ui/app/plotter/blueprint-drawing.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import type { ReactNode } from "react";
import { roleCss } from "../../skills/plotter/assets/foundation/index.mjs";

export function BlueprintDrawing({
children,
roles = false,
}: {
children: ReactNode;
roles?: boolean;
}) {
return (
<svg
aria-hidden="true"
className="h-auto w-full overflow-visible font-sans text-foreground [font-optical-sizing:auto] [font-synthesis:none] [font-variation-settings:'GEOM'_25]"
data-plotter={roles ? "" : undefined}
fill="none"
focusable="false"
stroke="currentColor"
strokeWidth="0.8"
viewBox="0 0 560 440"
>
{roles && <style data-plotter-styles="true">{roleCss}</style>}
{children}
</svg>
);
}

// Draws hero geometry at empty-state size. Strokes stay one pixel at any scale.
export function CompactBlueprintDrawing({
center: [x, y],
scale,
children,
}: {
center: [number, number];
scale: number;
children: ReactNode;
}) {
return (
<svg
aria-hidden="true"
className="size-full overflow-visible text-foreground [&_*]:[vector-effect:non-scaling-stroke]"
fill="none"
focusable="false"
stroke="currentColor"
strokeWidth="1"
viewBox="0 0 96 96"
>
<g transform={`translate(48 48) scale(${scale}) translate(${-x} ${-y})`}>
{children}
</g>
</svg>
);
}
Loading
Loading