@nadicodeai/ui 6.6.2 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/AGENTS.md +4 -4
  2. package/LICENSES/LOBEHUB-ICONS-MIT.txt +21 -0
  3. package/NOTICE +14 -0
  4. package/dist/components/activity-orb.d.ts +1 -1
  5. package/dist/components/activity-orb.d.ts.map +1 -1
  6. package/dist/components/brand-icons/claude.d.ts +18 -0
  7. package/dist/components/brand-icons/claude.d.ts.map +1 -0
  8. package/dist/components/brand-icons/claude.js +22 -0
  9. package/dist/components/brand-icons/deepseek.d.ts +18 -0
  10. package/dist/components/brand-icons/deepseek.d.ts.map +1 -0
  11. package/dist/components/brand-icons/deepseek.js +22 -0
  12. package/dist/components/brand-icons/gemini.d.ts +18 -0
  13. package/dist/components/brand-icons/gemini.d.ts.map +1 -0
  14. package/dist/components/brand-icons/gemini.js +25 -0
  15. package/dist/components/brand-icons/grok.d.ts +18 -0
  16. package/dist/components/brand-icons/grok.d.ts.map +1 -0
  17. package/dist/components/brand-icons/grok.js +22 -0
  18. package/dist/components/brand-icons/meta.d.ts +18 -0
  19. package/dist/components/brand-icons/meta.d.ts.map +1 -0
  20. package/dist/components/brand-icons/meta.js +25 -0
  21. package/dist/components/brand-icons/mistral.d.ts +18 -0
  22. package/dist/components/brand-icons/mistral.d.ts.map +1 -0
  23. package/dist/components/brand-icons/mistral.js +22 -0
  24. package/dist/components/brand-icons/moonshot.d.ts +18 -0
  25. package/dist/components/brand-icons/moonshot.d.ts.map +1 -0
  26. package/dist/components/brand-icons/moonshot.js +22 -0
  27. package/dist/components/brand-icons/openai.d.ts +18 -0
  28. package/dist/components/brand-icons/openai.d.ts.map +1 -0
  29. package/dist/components/brand-icons/openai.js +22 -0
  30. package/dist/components/brand-icons/qwen.d.ts +18 -0
  31. package/dist/components/brand-icons/qwen.d.ts.map +1 -0
  32. package/dist/components/brand-icons/qwen.js +25 -0
  33. package/dist/components/brand-icons.d.ts +9 -0
  34. package/dist/components/brand-icons.d.ts.map +1 -1
  35. package/dist/components/brand-icons.js +9 -0
  36. package/dist/components/chart-breakdown-table.d.ts +26 -6
  37. package/dist/components/chart-breakdown-table.d.ts.map +1 -1
  38. package/dist/components/chart-breakdown-table.js +4 -4
  39. package/dist/components/chart-time-series.d.ts +68 -2
  40. package/dist/components/chart-time-series.d.ts.map +1 -1
  41. package/dist/components/chart-time-series.js +112 -11
  42. package/dist/components/chart.d.ts +2 -1
  43. package/dist/components/chart.d.ts.map +1 -1
  44. package/dist/components/chart.js +11 -2
  45. package/dist/components/model-mark.d.ts +19 -0
  46. package/dist/components/model-mark.d.ts.map +1 -0
  47. package/dist/components/model-mark.js +66 -0
  48. package/dist/index.d.ts +1 -0
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +1 -0
  51. package/docs/activity.md +4 -5
  52. package/docs/contract.md +48 -5
  53. package/package.json +3 -7
  54. package/dist/components/threshold/canvas-budget.d.ts +0 -10
  55. package/dist/components/threshold/canvas-budget.d.ts.map +0 -1
  56. package/dist/components/threshold/canvas-budget.js +0 -54
  57. package/dist/components/threshold/index.d.ts +0 -6
  58. package/dist/components/threshold/index.d.ts.map +0 -1
  59. package/dist/components/threshold/index.js +0 -5
  60. package/dist/components/threshold/orb.d.ts +0 -14
  61. package/dist/components/threshold/orb.d.ts.map +0 -1
  62. package/dist/components/threshold/orb.js +0 -17
  63. package/dist/components/threshold/presence.d.ts +0 -27
  64. package/dist/components/threshold/presence.d.ts.map +0 -1
  65. package/dist/components/threshold/presence.js +0 -159
  66. package/dist/components/threshold/room.d.ts +0 -33
  67. package/dist/components/threshold/room.d.ts.map +0 -1
  68. package/dist/components/threshold/room.js +0 -168
  69. package/dist/components/threshold/threshold.css +0 -228
  70. package/dist/components/threshold/typed-line.d.ts +0 -8
  71. package/dist/components/threshold/typed-line.d.ts.map +0 -1
  72. package/dist/components/threshold/typed-line.js +0 -43
  73. package/dist/components/threshold/use-reduced-motion.d.ts +0 -10
  74. package/dist/components/threshold/use-reduced-motion.d.ts.map +0 -1
  75. package/dist/components/threshold/use-reduced-motion.js +0 -32
  76. package/dist/threshold.d.ts +0 -2
  77. package/dist/threshold.d.ts.map +0 -1
  78. package/dist/threshold.js +0 -13
  79. package/docs/threshold.md +0 -221
package/docs/contract.md CHANGED
@@ -6,7 +6,7 @@ The normative invariants for the `@nadicodeai/ui` package: what may live here, h
6
6
 
7
7
  - **Token-only, no raw literals or shadow aliases.** Components style through semantic shadcn utilities (`bg-background`, `text-foreground`, `border-input`, and the rest), or a canonical generated design-system token when no standard role is precise enough. The UI package does not mint screen-, component-, or product-specific color aliases. No raw `#hex` / `rgb()` / `hsl()` may appear in `.tsx`; a new source value goes to `packages/design-system/DESIGN.md` first and flows through generated output. A narrow protocol adapter may translate third-party classes directly to canonical tokens in `globals.css`, but it must not create another token vocabulary. Enforced by the raw-colour `no-restricted-syntax` selectors in the library ESLint config, `packages/ui/tests/guards/dark-semantic-roles.test.ts`, and `packages/ui/tests/guards/token-reference-integrity.test.ts`.
8
8
  - **Dark-ready semantics, no component dark implementation.** Components use the same semantic role or canonical mode-aware token in every mode. They must not ship Tailwind `dark:` color utilities, component-local `.dark` blocks, OS dark-mode branching, or raw dark palette utilities such as `dark:bg-slate-950` and `dark:text-white`. An intentionally fixed-presentation technology surface, such as the dark terminal console, selects canonical fixed-mode tokens as one coherent component treatment rather than branching by app theme. Enforced by `packages/ui/tests/guards/no-dark.test.ts`, `packages/ui/tests/guards/dark-semantic-roles.test.ts`, and `packages/ui/tests/guards/token-reference-integrity.test.ts`. The generated mode and theme architecture lives only in [`packages/design-system/DESIGN.md`](../../design-system/DESIGN.md#css-architecture--token-pipeline).
9
- - **Components inherit one complete React style interface.** `@nadicodeai/ui/globals.css` composes every style dependency owned by this package, including the complete design-system CSS module. Components carry no theme of their own, and React callers do not compose design-system stylesheets or partials beside it. Installation and source-scanning setup live in the [consumer setup guide](consuming-cross-repo.md). `@nadicodeai/ui/threshold.css` is the one stylesheet outside it, because the surface that needs it most loads no Tailwind layer at all: the Nadia desktop shell takes that file and the design system's token artifact and nothing else. A React caller imports it beside globals.css, never instead of it.
9
+ - **Components inherit one complete React style interface.** `@nadicodeai/ui/globals.css` composes every style dependency owned by this package, including the complete design-system CSS module. Components carry no theme of their own, and React callers do not compose design-system stylesheets or partials beside it. Installation and source-scanning setup live in the [consumer setup guide](consuming-cross-repo.md). `@nadicodeai/ui/globals.css` is the only stylesheet this package ships; a surface that loads no Tailwind layer takes the design system's token artifact and writes its own rules against it.
10
10
  - **Design-system geometry remains the foundation.** React components compose the kept `nc-*` page grammar and display CSS components; they do not redefine frame math, marker placement, token values, or raw CSS geometry. Use parent-paints-plus-gap for bordered cell grids and keep every visible seam single-owner (doctrine home: `packages/design-system/DESIGN.md` "## Layout").
11
11
 
12
12
  ### Persistent notices
@@ -102,6 +102,8 @@ page, not in repeated panels surrounding identical child tables.
102
102
  `BreakdownTable` owns its responsive comparison layout: identity and value stay
103
103
  in the main row; share moves into a labeled detail row when space is tight.
104
104
  Its caption names the table. Callers supply data and labels, not column-hiding CSS.
105
+ An entity's optional `mark` prints between its color dot and its label, so a
106
+ ranked model row shows the maker's mark beside the name.
105
107
 
106
108
  ### Record action menus
107
109
 
@@ -139,20 +141,31 @@ gone, is [`packages/design-system/DESIGN.md`, "## Status Vocabulary"](../../desi
139
141
  System activity uses `ActivityOrb` for loading, connecting and processing;
140
142
  [activity.md](activity.md) owns selection, rendering and accessibility. It
141
143
  composes with status feedback and measured progress without adding outcome
142
- states to the orb. The existing threshold `Orb` adapts its public states to
143
- this same renderer.
144
+ states to the orb.
144
145
 
145
146
  - **Source-authored, built package.** Components are authored as shadcn-style source in `src/`, then emitted to `dist/` for consumption by other apps. Package exports must point at built JS, declarations, and CSS, never raw `src/*.tsx`, and `npm pack --dry-run` must include only intentional consumer artifacts.
146
147
  - **Package surface is source-backed.** Public components stay flat under `src/components/`. Reviewed registry source under `src/internal/` is implementation, not a package namespace. Do not add top-level exports for recipes, blocks, agent, terminal, or vendor groupings unless a later package API decision adds concrete public source, built files, docs, and tests in the same change.
147
148
  - **Agents is the client-only conversation interface.** `@nadicodeai/ui/agents` re-exports the complete `@assistant-ui/react` runtime and primitive API plus the reviewed, branded registry composition held under `src/internal/assistant-ui/`. Its built entry retains `"use client"`. Consumers receive Thread, attachments, Markdown, reasoning, tools, suggestions, providers, runtimes, hooks, and types from that one domain namespace. Agent identity components use their direct component subpaths so server compositions do not import the conversation runtime. The vendor name is not a public NadicodeAI subpath, and the package does not rename, wrap, or rebuild assistant-ui behavior.
148
149
  - **Base UI is the active primitive base.** `components.json` must resolve through a Base style, currently `base-nova`, and `npx shadcn@latest info --json` must report `base: "base"`. Do not reintroduce `radix-ui`, `@radix-ui/*`, or `vaul` to package source, dependencies, or app optimizer configuration. Registry-owned shadcn primitives stay aligned with current `shadcn@latest` Base UI output; use Base UI `render` composition instead of Radix `asChild`. `cmdk` is allowed only for the stock shadcn Command component while the current `base-nova` registry declares that dependency; do not use it elsewhere or replace it without a shadcn/compatibility decision and tests.
149
- - **Charts are shadcn charts.** `chart.tsx` is shadcn's `chart` component over Recharts (`ChartContainer`, `ChartTooltip`, `ChartLegend`, theme-keyed `ChartConfig`), and the chart kit (`chart-time-series`, `chart-breakdown-table`, `chart-sparkline`, `chart-bullet-bar`, `chart-kpi-stat`, `chart-delta`, `chart-date-range-control`, `chart-entity-colors`) is built on it. An app composes these and never imports `recharts` or draws SVG charts of its own; `recharts` is declared by this package alone. A chart the kit lacks is first looked up in the shadcn registry through the CLI or MCP, then added to the kit with NadicodeAI tokens and the chart accessibility contract. What a chart shows is decided by the product; how it is drawn comes from here.
150
+ - **Charts are shadcn charts.** `chart.tsx` is shadcn's `chart` component over Recharts (`ChartContainer`, `ChartTooltip`, `ChartLegend`, theme-keyed `ChartConfig`), and the chart kit (`chart-time-series`, `chart-breakdown-table`, `chart-sparkline`, `chart-bullet-bar`, `chart-kpi-stat`, `chart-delta`, `chart-date-range-control`, `chart-entity-colors`) is built on it. An app composes these and never imports `recharts` or draws SVG charts of its own; `recharts` is declared by this package alone. A chart the kit lacks is first looked up in the shadcn registry through the CLI or MCP, then added to the kit with NadicodeAI tokens and the chart accessibility contract. What a chart shows is decided by the product; how it is drawn comes from here. `TimeSeriesComposed` is that lookup performed: the registry holds no composed item, so the bars-plus-line composition for a rate over its measures is Recharts' `ComposedChart` through this same foundation, with the second axis existing only for a series that names it.
150
151
  - **Named shadcn blocks stay named.** When a user, plan, or issue selects a shadcn block/example such as `login-02`, `signup-02`, `dashboard-01`, or `sidebar-*`, use that registry item as the source of truth through shadcn CLI/MCP. Do not replace it with a package-authored abstraction unless the plan explicitly records the substitution, the reason, and the true consumer route that proves it.
151
152
  - **A change to what the package ships bumps the version.** Any change to exported components, `src/index.ts`, package `exports`, `globals.css`, theme behavior, package docs, or design-system token, CSS, or theme output this package consumes bumps both package versions in lockstep before handoff, and `npm pack --dry-run -w @nadicodeai/ui` shows exactly what consumers receive (procedure: `../../../docs/agents/publish.md`).
152
153
 
153
154
  ## Admission Rules
154
155
 
155
- Assistant conversation and runtime APIs enter through `@nadicodeai/ui/agents`; Agent identity enters through `@nadicodeai/ui/components/agent-card`, `@nadicodeai/ui/components/agent-visual`, and the Forma avatars in `@nadicodeai/ui/components/forma-avatar` (owned by [`forma-avatar.md`](forma-avatar.md)). The consuming application still constructs the runtime and owns transport, persistence, permissions, tools, approvals, and failure policy. Shared non-Agent product components (a shell, a settings surface, a generic section, a diff, or a file preview) are admitted only with a named consuming surface landing in the same change. App-bespoke UI and behavior (window chrome, PTY/xterm embeds, queues, approvals, and persistence) stay in the consuming app; they never become package abstractions. `@nadicodeai/ui/threshold` is the plan-approved package surface for the room, Nadia's presence, the status orb and the typed line used by the Nadia desktop app. The screen, its copy and its state machine stay in the consuming app, and the entry ships no screen. Its contract is [`threshold.md`](threshold.md). A component left unconsumed after its target migration ships gets removed.
156
+ Assistant conversation and runtime APIs enter through `@nadicodeai/ui/agents`; Agent identity enters through `@nadicodeai/ui/components/agent-card`, `@nadicodeai/ui/components/agent-visual`, and the Forma avatars in `@nadicodeai/ui/components/forma-avatar` (owned by [`forma-avatar.md`](forma-avatar.md)). The consuming application still constructs the runtime and owns transport, persistence, permissions, tools, approvals, and failure policy. Shared non-Agent product components (a shell, a settings surface, a generic section, a diff, or a file preview) are admitted only with a named consuming surface landing in the same change. App-bespoke UI and behavior (window chrome, PTY/xterm embeds, queues, approvals, and persistence) stay in the consuming app; they never become package abstractions. A component left unconsumed after its target migration ships gets removed.
157
+
158
+ ### Placement
159
+
160
+ `@nadicodeai/ui` and `@nadicodeai/design-system` hold the Foundation and the
161
+ components, assets and styles at least two products use; a component, asset or
162
+ style one product alone needs lives in that product's own directory. Nadia's
163
+ entry screen is the worked example: the room, the presence, the typed line and
164
+ their stylesheet were a package surface no other product imported, and they now
165
+ live in the desktop at `apps/nadia/hermes/apps/desktop/src/plugins/nadia/entry/`,
166
+ where [`apps/nadia/docs/entry-screen.md`](../../../apps/nadia/docs/entry-screen.md)
167
+ owns their contract. `packages/ui/tests/guards/product-placement.test.ts`
168
+ refuses a product's name in either package.
156
169
 
157
170
  ### Agent identity presentation
158
171
 
@@ -179,8 +192,38 @@ package.
179
192
  - **Reusable sections and compositions live here after proof.** Bento grids, media mounts, demo surfaces, page frames, section intros, CTA bands, proof slots, benefits grids, generic shells, and homepage/use-case sections belong in `@nadicodeai/ui` when a second concrete consumer, a plan-approved shared component, or a named validation slice proves the package boundary. Website, portal, and sibling Nadia routes pass data and media children into existing compositions instead of rebuilding layout locally.
180
193
  - **Brand renderers live here for React consumers.** Logo geometry, asset generation, and usage semantics are owned by [`packages/design-system/DESIGN.md`, "Brand media authority"](../../design-system/DESIGN.md#brand-media-authority). `BrandLockup` supplies the approved horizontal lockup. `BrandMark` serves tight spaces and `BrandWordmark` serves placements where the name must read by itself. All three render the package's public neutral geometry seam with React-scoped SVG ids, dynamic theme paint, and accessible names. Consuming routes use these adapters; they never import raw logo assets, draw brand SVG inline, mask or recolor assets, or create another lockup rule.
181
194
  - **Third-party brand marks are generated, never hand-drawn and never hand-edited.** Other companies' marks live in `packages/ui/src/components/brand-icons/`, emitted from an explicit allow-list by `packages/ui/scripts/build-brand-icons.mjs` and exported through the `@nadicodeai/ui/components/brand-icons` subpath. They are identity images, not UI icons: a brand mark is exempt from the currentColor stroke system and from the raw-literal rule, it never stands in for a UI icon, and a UI icon never stands in for a brand. Marks drawn as one silhouette are stripped to `currentColor` by the generator; every other mark keeps its official colors verbatim. To add a brand, edit the generator's allow-list and run `npm run generate:brand-icons -w @nadicodeai/ui`; hand edits to the generated directory are lost and `packages/ui/tests/guards/brand-icons-codegen.test.ts` fails on them. Use is nominative only — naming a platform, an integration, or a provider — never adjacent to endorsement or partnership wording; that nominative posture is the licensing and trademark position, so a mark never appears where the copy implies endorsement.
195
+
196
+ The one script reads two explicit allow-lists: platform marks out of the Iconify `logos` set (CC0-1.0), and model makers' marks out of the LobeHub icon set (MIT, `@lobehub/icons-static-svg`), both as `devDependencies`. The emitted components are plain SVG, so neither icon data package belongs in the runtime dependency set or reaches a consumer bundle. The artwork itself does ship, so every upstream whose licence asks for one travels with a notice in `LICENSES/` and `NOTICE`, and `tests/guards/brand-icons-codegen.test.ts` holds the allow-list to that: a new source cannot ship unattributed.
182
197
  - **No fake proof APIs.** Components may support future testimonials, logos, stats, and proof slots, but they must render nothing public when the proof is absent. Do not ship placeholder customer names, metrics, or logos.
183
198
 
199
+ ### Model identity
200
+
201
+ `ModelMark` is a model's identity: its maker's mark, plus the model's name and
202
+ author when a caller supplies them. It takes `author` — the slug a usage read
203
+ reports — and resolves it against the generated model-maker marks (rule
204
+ `model-shown-with-logo`). An author off that allow-list gets the neutral tile,
205
+ so a model never falls back to a bare colour swatch. The tile owns the box and
206
+ scales the artwork inside it; `size` states that box, and the author slug prints
207
+ as reported.
208
+
209
+ One component has two readings: with `name`, the row a table cell or ranked
210
+ list reads; without one, the bare mark that stands in a chart legend beside that
211
+ legend's own text. `BreakdownTable` entities and the `chart-time-series` series
212
+ (`TimeSeriesBars`, `TimeSeriesArea`, `TimeSeriesComposed`) take an optional
213
+ `mark` for that second reading, placed beside the entity's or series' name. A
214
+ chart places it on each surface itself: the legend through the explicit `marks`
215
+ path on `ChartLegendContent`, and the tooltip row through the kit's own row
216
+ renderer, because `rule/model-shown-with-logo` names tooltips among the places a
217
+ model appears. A series' mark never travels through `ChartConfig`, whose label
218
+ the legend and the default tooltip item both read, so an identity parked there
219
+ would appear on a surface that never asked for it. No legend is forked. A mark is
220
+ decoration beside the label that already names it; one standing alone takes
221
+ `label` (`role="img"` with an accessible name) or stays hidden.
222
+
223
+ What this package does not own: which authors exist, which mark a maker gets
224
+ (the generator's allow-list), or what a surface shows a model for. The consuming
225
+ app supplies the author slug, the name, and the colors it pairs with them.
226
+
184
227
  ## Accessibility Invariant
185
228
 
186
229
  **Tier-1 accessibility is tested, browser-free.** Rendering component tests use the package `expectAccessible` helper to check the rendered DOM. The jsdom tier is structural: rendered contrast, landmark context, focus, and keyboard behavior belong to real-browser Playwright coverage. The shared library ESLint configuration is the executable authority for static JSX accessibility rules, and the repository quality doctrine owns their policy; this contract does not copy the rule inventory.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nadicodeai/ui",
3
- "version": "6.6.2",
3
+ "version": "7.1.0",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -19,11 +19,6 @@
19
19
  "types": "./dist/agents.d.ts",
20
20
  "import": "./dist/agents.js"
21
21
  },
22
- "./threshold": {
23
- "types": "./dist/threshold.d.ts",
24
- "import": "./dist/threshold.js"
25
- },
26
- "./threshold.css": "./dist/components/threshold/threshold.css",
27
22
  "./activity": {
28
23
  "types": "./dist/activity.d.ts",
29
24
  "import": "./dist/activity.js"
@@ -70,7 +65,7 @@
70
65
  "@assistant-ui/react": "0.15.18",
71
66
  "@assistant-ui/react-markdown": "0.14.14",
72
67
  "@base-ui/react": "^1.8.0",
73
- "@nadicodeai/design-system": "6.6.2",
68
+ "@nadicodeai/design-system": "7.1.0",
74
69
  "@paper-design/shaders": "0.0.80",
75
70
  "class-variance-authority": "^0.7.1",
76
71
  "clsx": "^2.1.1",
@@ -101,6 +96,7 @@
101
96
  "devDependencies": {
102
97
  "@iconify-json/logos": "^1.2.14",
103
98
  "@iconify/utils": "^3.1.7",
99
+ "@lobehub/icons-static-svg": "^1.95.0",
104
100
  "@nadicodeai/eslint-plugin": "*",
105
101
  "@stryker-mutator/core": "10.0.0",
106
102
  "@stryker-mutator/vitest-runner": "10.0.0",
@@ -1,10 +0,0 @@
1
- /** True while a primitive that outranks the room owns the window's canvas. */
2
- export declare function canvasHeld(): boolean;
3
- /**
4
- * Take the window's canvas. Returns the release, which is idempotent: a
5
- * teardown that runs twice releases once.
6
- */
7
- export declare function holdCanvas(): () => void;
8
- /** Whether the room must yield its canvas right now. Live. */
9
- export declare function useCanvasHeld(): boolean;
10
- //# sourceMappingURL=canvas-budget.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"canvas-budget.d.ts","sourceRoot":"","sources":["../../../src/components/threshold/canvas-budget.ts"],"names":[],"mappings":"AA0BA,8EAA8E;AAC9E,wBAAgB,UAAU,IAAI,OAAO,CAEpC;AAED;;;GAGG;AACH,wBAAgB,UAAU,IAAI,MAAM,IAAI,CAWvC;AAWD,8DAA8D;AAC9D,wBAAgB,aAAa,IAAI,OAAO,CAEvC"}
@@ -1,54 +0,0 @@
1
- "use client";
2
- import { useSyncExternalStore } from "react";
3
- /*
4
- * One shader canvas per window: the living-elements register's rule, held
5
- * here so no screen can break it by composition.
6
- *
7
- * Two threshold primitives can paint with a shader, the room and the arriving
8
- * presence, and a threshold mounts both. The presence's water outranks the
9
- * room: while Nadia is arriving her water is the window's one live canvas and
10
- * the room shows its still field; when she stands clear the water leaves and
11
- * the room's canvas is the one that paints. The presence takes a hold for as
12
- * long as its water is wanted, and the room yields while any hold is out.
13
- *
14
- * The budget is per window, so it is module state rather than a context: a
15
- * provider would let a surface forget to wrap both primitives, and the rule
16
- * would then hold only where someone remembered it.
17
- */
18
- let holds = 0;
19
- const listeners = new Set();
20
- const notify = () => {
21
- for (const listener of listeners)
22
- listener();
23
- };
24
- /** True while a primitive that outranks the room owns the window's canvas. */
25
- export function canvasHeld() {
26
- return holds > 0;
27
- }
28
- /**
29
- * Take the window's canvas. Returns the release, which is idempotent: a
30
- * teardown that runs twice releases once.
31
- */
32
- export function holdCanvas() {
33
- holds += 1;
34
- notify();
35
- let released = false;
36
- return () => {
37
- if (released)
38
- return;
39
- released = true;
40
- holds -= 1;
41
- notify();
42
- };
43
- }
44
- function subscribe(listener) {
45
- listeners.add(listener);
46
- return () => {
47
- listeners.delete(listener);
48
- };
49
- }
50
- const serverSnapshot = () => false;
51
- /** Whether the room must yield its canvas right now. Live. */
52
- export function useCanvasHeld() {
53
- return useSyncExternalStore(subscribe, canvasHeld, serverSnapshot);
54
- }
@@ -1,6 +0,0 @@
1
- export { Orb, type OrbProps, type OrbSize, type OrbState, type OrbTheme } from "./orb.js";
2
- export { arrivalOpacity, NadiaPresence, type NadiaPresenceProps, type PresenceState, } from "./presence.js";
3
- export { ThresholdRoom, type ThresholdRoomProps } from "./room.js";
4
- export { TypedLine, type TypedLineProps } from "./typed-line.js";
5
- export { useReducedMotion } from "./use-reduced-motion.js";
6
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/threshold/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,KAAK,QAAQ,EAAE,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,MAAM,OAAO,CAAC;AACvF,OAAO,EACL,cAAc,EACd,aAAa,EACb,KAAK,kBAAkB,EACvB,KAAK,aAAa,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC"}
@@ -1,5 +0,0 @@
1
- export { Orb } from "./orb.js";
2
- export { arrivalOpacity, NadiaPresence, } from "./presence.js";
3
- export { ThresholdRoom } from "./room.js";
4
- export { TypedLine } from "./typed-line.js";
5
- export { useReducedMotion } from "./use-reduced-motion.js";
@@ -1,14 +0,0 @@
1
- import { type ActivityOrbSize } from "../activity-orb.js";
2
- export type OrbState = "working" | "connecting" | "absent";
3
- export type OrbTheme = "auto" | "dark" | "light";
4
- export type OrbSize = ActivityOrbSize;
5
- export type OrbProps = {
6
- className?: string;
7
- label: string;
8
- size?: OrbSize;
9
- state: OrbState;
10
- theme?: OrbTheme;
11
- };
12
- /** Preserve the threshold API while using the one shared activity renderer. */
13
- export declare function Orb({ className, label, size, state, theme }: OrbProps): import("react").JSX.Element | null;
14
- //# sourceMappingURL=orb.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"orb.d.ts","sourceRoot":"","sources":["../../../src/components/threshold/orb.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAe,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAG9E,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,YAAY,GAAG,QAAQ,CAAC;AAC3D,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AACjD,MAAM,MAAM,OAAO,GAAG,eAAe,CAAC;AAEtC,MAAM,MAAM,QAAQ,GAAG;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,QAAQ,CAAC;IAChB,KAAK,CAAC,EAAE,QAAQ,CAAC;CAClB,CAAC;AAUF,+EAA+E;AAC/E,wBAAgB,GAAG,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,IAAS,EAAE,KAAK,EAAE,KAAc,EAAE,EAAE,QAAQ,sCAYnF"}
@@ -1,17 +0,0 @@
1
- "use client";
2
- import { jsx as _jsx } from "react/jsx-runtime";
3
- import { ActivityOrb } from "../activity-orb.js";
4
- import { cn } from "../../lib/utils.js";
5
- // A threshold deliberately fixes its appearance. Normal product surfaces
6
- // inherit their ink; the shared renderer needs no knowledge of app themes.
7
- const THEME_INK = {
8
- auto: "inherit",
9
- dark: "var(--nc-dark-ink)",
10
- light: "var(--nc-ink)",
11
- };
12
- /** Preserve the threshold API while using the one shared activity renderer. */
13
- export function Orb({ className, label, size = 88, state, theme = "dark" }) {
14
- if (state === "absent")
15
- return null;
16
- return (_jsx(ActivityOrb, { activity: state === "working" ? "processing" : "connecting", className: cn("nc-threshold-orb", className), "data-state": state, label: label, size: size, style: { color: THEME_INK[theme] } }));
17
- }
@@ -1,27 +0,0 @@
1
- export type PresenceState = "arriving" | "present" | "ready" | "error";
2
- export type NadiaPresenceProps = {
3
- className?: string;
4
- /** The accessible name of the arrival, read by the hidden progress bar. */
5
- label: string;
6
- /** 0 while she is behind the field, 1 when she stands clear. */
7
- reveal: number;
8
- /**
9
- * The portrait master. The package imports no binary asset of its own, so
10
- * the consuming surface resolves `@nadicodeai/design-system/assets/nadia-solid-surface`
11
- * with its own bundler and passes the URL here.
12
- */
13
- src: string;
14
- state: PresenceState;
15
- };
16
- /** The arrival curve: a trace at the start, not a face already half-built. */
17
- export declare function arrivalOpacity(reveal: number): number;
18
- /**
19
- * Nadia at a threshold.
20
- *
21
- * `reveal` drives one opacity, so nothing re-renders per frame and the value
22
- * a Customer sees is the value assistive technology is told. The lower edge
23
- * dissolves into the room instead of ending on a rectangle. At `state="ready"`
24
- * she takes one short verde vivo beat, her presence accent, and settles.
25
- */
26
- export declare function NadiaPresence({ className, label, reveal, src, state }: NadiaPresenceProps): import("react").JSX.Element;
27
- //# sourceMappingURL=presence.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"presence.d.ts","sourceRoot":"","sources":["../../../src/components/threshold/presence.tsx"],"names":[],"mappings":"AA+CA,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC;AAEvE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,aAAa,CAAC;CACtB,CAAC;AAIF,8EAA8E;AAC9E,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAErD;AAgBD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,kBAAkB,+BAsIzF"}
@@ -1,159 +0,0 @@
1
- "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { defaultObjectSizing, ShaderFitOptions, ShaderMount, waterFragmentShader, } from "@paper-design/shaders";
4
- import { useEffect, useRef, useState } from "react";
5
- import { resolveIdentityColor } from "../../internal/agent-visual/color.js";
6
- import { cn } from "../../lib/utils.js";
7
- import { holdCanvas } from "./canvas-budget.js";
8
- import { useReducedMotion } from "./use-reduced-motion.js";
9
- /*
10
- * Nadia's presence: the portrait master itself, arriving through the room. No
11
- * medallion, no ring, no bar. How much of her you can see is the progress.
12
- *
13
- * While she is arriving the portrait carries Paper Shaders' Water at a
14
- * restrained intensity, so the field she is coming through moves across her
15
- * without taking her geometry apart. Every amplitude is scaled by how much of
16
- * the arrival is left, so the water is gone by the time she stands clear. At
17
- * reveal 1, and in every other state, this is the plain portrait.
18
- *
19
- * The water is the window's one shader canvas while it is wanted. The presence
20
- * holds the canvas budget from the moment the water is asked for until it is
21
- * torn down, and the room keeps its still field for exactly that long
22
- * (`canvas-budget.ts`).
23
- */
24
- const ARRIVAL_FLOOR = 0.025;
25
- const ARRIVAL_CURVE = 1.25;
26
- const WATER_CAUSTIC = 0.35;
27
- const WATER_EDGES = 0.4;
28
- const WATER_WAVES = 0.3;
29
- const WATER_LAYERING = 0.4;
30
- const WATER_HIGHLIGHTS = 0.12;
31
- const WATER_SIZE = 1;
32
- const WATER_SPEED = 1;
33
- const WATER_FRAME = 0;
34
- const WATER_MIN_PIXEL_RATIO = 1;
35
- const WATER_MAX_PIXEL_COUNT = 1_024_000;
36
- const PRESENCE_FIELD_TOKEN = "--nc-identity-verde";
37
- const TRANSPARENT = [0, 0, 0, 0];
38
- const clampUnit = (value) => Math.min(Math.max(value, 0), 1);
39
- /** The arrival curve: a trace at the start, not a face already half-built. */
40
- export function arrivalOpacity(reveal) {
41
- return ARRIVAL_FLOOR + (1 - ARRIVAL_FLOOR) * clampUnit(reveal) ** ARRIVAL_CURVE;
42
- }
43
- function waterUniforms(reveal, field) {
44
- const remaining = 1 - clampUnit(reveal);
45
- return {
46
- u_caustic: WATER_CAUSTIC * remaining,
47
- u_colorBack: TRANSPARENT,
48
- u_colorHighlight: field,
49
- u_edges: WATER_EDGES * remaining,
50
- u_highlights: WATER_HIGHLIGHTS,
51
- u_layering: WATER_LAYERING,
52
- u_size: WATER_SIZE,
53
- u_waves: WATER_WAVES * remaining,
54
- };
55
- }
56
- /**
57
- * Nadia at a threshold.
58
- *
59
- * `reveal` drives one opacity, so nothing re-renders per frame and the value
60
- * a Customer sees is the value assistive technology is told. The lower edge
61
- * dissolves into the room instead of ending on a rectangle. At `state="ready"`
62
- * she takes one short verde vivo beat, her presence accent, and settles.
63
- */
64
- export function NadiaPresence({ className, label, reveal, src, state }) {
65
- const still = useReducedMotion();
66
- const hostRef = useRef(null);
67
- const mountRef = useRef(null);
68
- const [surface, setSurface] = useState("portrait");
69
- const value = clampUnit(reveal);
70
- const arriving = state === "arriving";
71
- const wet = arriving && value < 1 && !still;
72
- useEffect(() => {
73
- const host = hostRef.current;
74
- if (!host || !wet)
75
- return;
76
- const field = resolveIdentityColor(host, PRESENCE_FIELD_TOKEN);
77
- if (!field)
78
- return;
79
- let cancelled = false;
80
- let canvas = null;
81
- // Taken before the image decodes: the room stays still while her water is
82
- // on its way, not only once it paints.
83
- const releaseCanvas = holdCanvas();
84
- const clearCanvases = () => {
85
- for (const element of host.querySelectorAll("canvas"))
86
- element.remove();
87
- host.removeAttribute("data-paper-shader");
88
- };
89
- const teardown = () => {
90
- canvas?.removeEventListener("webglcontextlost", handleContextLost);
91
- canvas = null;
92
- try {
93
- mountRef.current?.dispose();
94
- }
95
- catch {
96
- // The plain portrait is what stays; vendor cleanup failing is not
97
- // allowed to remove Nadia from the screen.
98
- }
99
- mountRef.current = null;
100
- clearCanvases();
101
- // After disposal, so the room's canvas is never alive beside this one.
102
- releaseCanvas();
103
- };
104
- function handleContextLost(event) {
105
- event.preventDefault();
106
- teardown();
107
- setSurface("portrait");
108
- }
109
- const image = new Image();
110
- image.decoding = "async";
111
- image.src = src;
112
- const mount = async () => {
113
- try {
114
- await image.decode();
115
- if (cancelled)
116
- return;
117
- mountRef.current = new ShaderMount(host, waterFragmentShader, {
118
- ...waterUniforms(value, field),
119
- u_fit: ShaderFitOptions.contain,
120
- u_image: image,
121
- u_offsetX: defaultObjectSizing.offsetX,
122
- u_offsetY: defaultObjectSizing.offsetY,
123
- u_originX: defaultObjectSizing.originX,
124
- u_originY: defaultObjectSizing.originY,
125
- u_rotation: defaultObjectSizing.rotation,
126
- u_scale: defaultObjectSizing.scale,
127
- u_worldHeight: defaultObjectSizing.worldHeight,
128
- u_worldWidth: defaultObjectSizing.worldWidth,
129
- }, undefined, WATER_SPEED, WATER_FRAME, WATER_MIN_PIXEL_RATIO, WATER_MAX_PIXEL_COUNT, ["u_image"]);
130
- canvas = mountRef.current.canvasElement;
131
- canvas.addEventListener("webglcontextlost", handleContextLost);
132
- setSurface("water");
133
- }
134
- catch {
135
- // No WebGL, no palette, or an image that will not decode: she arrives
136
- // as the plain portrait, which is the whole design at reveal 1 anyway.
137
- teardown();
138
- setSurface("portrait");
139
- }
140
- };
141
- void mount();
142
- return () => {
143
- cancelled = true;
144
- teardown();
145
- setSurface("portrait");
146
- };
147
- }, [src, wet]);
148
- useEffect(() => {
149
- const host = hostRef.current;
150
- const mount = mountRef.current;
151
- if (!host || !mount)
152
- return;
153
- const field = resolveIdentityColor(host, PRESENCE_FIELD_TOKEN);
154
- if (!field)
155
- return;
156
- mount.setUniforms(waterUniforms(value, field));
157
- }, [value]);
158
- return (_jsxs("div", { className: cn("nc-threshold-presence", className), "data-state": state, "data-still": still || undefined, style: { "--threshold-portrait": `url("${src}")` }, children: [_jsx("div", { "aria-hidden": "true", className: "nc-threshold-presence-figure", "data-surface": surface, ref: hostRef, style: { opacity: arriving ? arrivalOpacity(value) : 1 }, children: _jsx("img", { alt: "", className: "nc-threshold-presence-portrait", draggable: false, src: src }) }), _jsx("div", { "aria-label": label, "aria-valuemax": 100, "aria-valuemin": 0, "aria-valuenow": Math.floor(value * 100), className: "nc-threshold-sr", role: "progressbar" })] }));
159
- }
@@ -1,33 +0,0 @@
1
- export type ThresholdRoomProps = {
2
- className?: string;
3
- /** Repair and error states quiet the room instead of hiding it. */
4
- dimmed?: boolean;
5
- /**
6
- * How much of the room's pace is running, 0 to 1. The captured recipe is
7
- * energy 1; the first-run screen holds it there while Nadia is still
8
- * arriving and drops it to 0 when she has, which stills the field. Sign-in,
9
- * success and every repair state pass 0.
10
- */
11
- energy?: number;
12
- /** Success: the field's colour becomes verde vivo, Nadia's presence accent. */
13
- ready?: boolean;
14
- };
15
- /**
16
- * The threshold's background field.
17
- *
18
- * It paints absolutely inside its own relative, isolated parent and never
19
- * against the viewport, so a Portal panel can hold one without covering the
20
- * page. It takes no pointer events and carries no words.
21
- *
22
- * When WebGL is unavailable, when the design-system tokens are not in the
23
- * cascade above it, or when the context is lost after mounting, the element
24
- * keeps its still field: a token-built radial gradient that needs no GPU. The
25
- * room is never blank.
26
- *
27
- * The window carries one shader canvas. While Nadia's water is painting her
28
- * arrival, the room holds this same still field and mounts nothing; the
29
- * moment she stands clear the water leaves and the room's canvas is the one
30
- * that paints (`canvas-budget.ts`).
31
- */
32
- export declare function ThresholdRoom({ className, dimmed, energy, ready, }: ThresholdRoomProps): import("react").JSX.Element;
33
- //# sourceMappingURL=room.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"room.d.ts","sourceRoot":"","sources":["../../../src/components/threshold/room.tsx"],"names":[],"mappings":"AAwCA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAIF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAAC,EAC5B,SAAS,EACT,MAAc,EACd,MAAU,EACV,KAAa,GACd,EAAE,kBAAkB,+BA+IpB"}