@nadicodeai/ui 4.0.4 → 5.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.
- package/AGENTS.md +4 -0
- package/README.md +4 -0
- package/dist/components/activity-orb.d.ts +21 -0
- package/dist/components/activity-orb.d.ts.map +1 -0
- package/dist/components/activity-orb.js +26 -0
- package/dist/components/threshold/canvas-budget.d.ts +10 -0
- package/dist/components/threshold/canvas-budget.d.ts.map +1 -0
- package/dist/components/threshold/canvas-budget.js +54 -0
- package/dist/components/threshold/index.d.ts +6 -0
- package/dist/components/threshold/index.d.ts.map +1 -0
- package/dist/components/threshold/index.js +5 -0
- package/dist/components/threshold/orb.d.ts +14 -0
- package/dist/components/threshold/orb.d.ts.map +1 -0
- package/dist/components/threshold/orb.js +17 -0
- package/dist/components/threshold/presence.d.ts +27 -0
- package/dist/components/threshold/presence.d.ts.map +1 -0
- package/dist/components/threshold/presence.js +159 -0
- package/dist/components/threshold/room.d.ts +33 -0
- package/dist/components/threshold/room.d.ts.map +1 -0
- package/dist/components/threshold/room.js +168 -0
- package/dist/components/threshold/threshold.css +228 -0
- package/dist/components/threshold/typed-line.d.ts +8 -0
- package/dist/components/threshold/typed-line.d.ts.map +1 -0
- package/dist/components/threshold/typed-line.js +43 -0
- package/dist/components/threshold/use-reduced-motion.d.ts +10 -0
- package/dist/components/threshold/use-reduced-motion.d.ts.map +1 -0
- package/dist/components/threshold/use-reduced-motion.js +32 -0
- package/dist/eslint/index.js +3 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/internal/activity-orb.d.ts +27 -0
- package/dist/internal/activity-orb.d.ts.map +1 -0
- package/dist/internal/activity-orb.js +150 -0
- package/dist/internal/agent-visual/color.d.ts.map +1 -1
- package/dist/internal/agent-visual/color.js +13 -7
- package/dist/threshold.d.ts +2 -0
- package/dist/threshold.d.ts.map +1 -0
- package/dist/threshold.js +13 -0
- package/docs/activity.md +120 -0
- package/docs/contract.md +8 -2
- package/docs/threshold.md +221 -0
- package/package.json +8 -2
package/docs/activity.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# System activity
|
|
2
|
+
|
|
3
|
+
The system activity family has three motions. They describe ongoing system
|
|
4
|
+
operations across the website, Portal and Nadia. They do not represent an
|
|
5
|
+
Agent's identity, attention or personality.
|
|
6
|
+
|
|
7
|
+
| Activity | Use when | Library motion |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| `loading` | Retrieving or revealing content: opening a workspace, fetching records, refreshing a view, retrieving search results. | `composing`: flowing dotted bands wrap around a globe, shown as “Thinking…” in the library's large demo. |
|
|
10
|
+
| `connecting` | Actively establishing or restoring communication with an account, service or device. | `connecting`: links form between nodes and carry signals. |
|
|
11
|
+
| `processing` | Carrying out an operation: saving, submitting, applying changes, validating, importing, preparing an export, reconciling a sync, or other unmeasured work. | `working`: particles travel on tilted orbits. |
|
|
12
|
+
|
|
13
|
+
These are three NadicodeAI activity treatments, not a claim that every design
|
|
14
|
+
system defines these categories. The label names the specific operation;
|
|
15
|
+
people are never expected to decode particle movement alone.
|
|
16
|
+
|
|
17
|
+
## Choose the feedback
|
|
18
|
+
|
|
19
|
+
An operation's purpose selects the orb; the endpoint, worker or request type
|
|
20
|
+
does not. Retrieving search results is `loading`. Establishing the connection
|
|
21
|
+
before that request is `connecting`. A working connection remains a settled
|
|
22
|
+
status while data loads. Synchronizing records after connection is
|
|
23
|
+
`processing`; reconnecting during sync temporarily uses `connecting`.
|
|
24
|
+
|
|
25
|
+
Saving is `processing`, not a fourth activity. Uploading, downloading,
|
|
26
|
+
installing and converting use the existing `Progress` component when the
|
|
27
|
+
completed amount can be measured. For an unmeasured preparation phase, use
|
|
28
|
+
`processing`. When progress will become measurable, reserve a progress
|
|
29
|
+
treatment from the start so the transition does not move the layout. Never
|
|
30
|
+
invent a percentage or surround an indeterminate orb with a simulated fill.
|
|
31
|
+
|
|
32
|
+
Queued, paused, offline, blocked, successful and failed conditions use the
|
|
33
|
+
existing status or action-feedback treatment. A request waiting for a person
|
|
34
|
+
does not keep an orb moving. A retry uses `connecting` only while an actual
|
|
35
|
+
connection attempt is in progress; its failure or recovery replaces activity
|
|
36
|
+
with the appropriate feedback. An account being active is not an activity.
|
|
37
|
+
|
|
38
|
+
Use the indicator beside the operation it describes. Keep the rest of the
|
|
39
|
+
interface available. One operation gets one indicator; separately progressing
|
|
40
|
+
operations can each have one. For content with a predictable layout, prefer
|
|
41
|
+
`Skeleton` or retained content over replacing the region with an orb. Do not
|
|
42
|
+
show both a skeleton and a central orb for the same wait.
|
|
43
|
+
|
|
44
|
+
## Brand treatment
|
|
45
|
+
|
|
46
|
+
All three reuse `thinking-orbs` 0.3.1 (MIT). Their geometry, depth ordering and
|
|
47
|
+
size-specific density remain the library's. NadicodeAI adapts the palette and
|
|
48
|
+
clock, not the rendering engine.
|
|
49
|
+
|
|
50
|
+
The structure inherits the surrounding text's neutral ink. Foreground and
|
|
51
|
+
signal particles carry the existing cobalto `--nc-info` role, mixed with that
|
|
52
|
+
ink to preserve contrast in either appearance. Depth fades into the actual
|
|
53
|
+
surface through transparency. There are no activity-specific colours: cobalto
|
|
54
|
+
means work underway throughout the family. Green, amber and red retain their
|
|
55
|
+
settled feedback meanings. Controls and panel backgrounds remain neutral.
|
|
56
|
+
|
|
57
|
+
The selected clock multipliers are 0.92 for loading, 0.86 for connecting and
|
|
58
|
+
0.92 for processing, applied to each original size-specific preset. The
|
|
59
|
+
connection animation is slightly quieter because its forming links add
|
|
60
|
+
movement as well as its travelling particles. The implementation owns these
|
|
61
|
+
values together; consumers cannot tune speed, density or particle colours.
|
|
62
|
+
|
|
63
|
+
Use 20 CSS pixels inline, including buttons and rows with room for that size;
|
|
64
|
+
64 for a contained region; 88 for an application threshold. The 20 preset is
|
|
65
|
+
separately tuned, and must not be obtained by shrinking the 64 preset. The 88
|
|
66
|
+
extent uses the 64 preset. A 12–16 pixel badge slot keeps its existing compact
|
|
67
|
+
spinner until its surrounding component explicitly accommodates this family.
|
|
68
|
+
|
|
69
|
+
## React
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
import { ActivityOrb } from "@nadicodeai/ui/components/activity-orb";
|
|
73
|
+
|
|
74
|
+
<ActivityOrb activity="loading" label="Caricamento progetti" size={64} />;
|
|
75
|
+
|
|
76
|
+
<div role="status">
|
|
77
|
+
<ActivityOrb activity="connecting" decorative />
|
|
78
|
+
<span>Connessione all’account…</span>
|
|
79
|
+
</div>;
|
|
80
|
+
|
|
81
|
+
<Button disabled={saving} aria-busy={saving}>
|
|
82
|
+
{saving ? <ActivityOrb activity="processing" decorative /> : null}
|
|
83
|
+
Salva modifiche
|
|
84
|
+
</Button>;
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`activity` is required. `size` defaults to 20. A meaningful standalone orb
|
|
88
|
+
requires `label`; `decorative` removes its accessible name when the adjacent
|
|
89
|
+
text or control already supplies the meaning. `paused` freezes the current
|
|
90
|
+
frame for a user-controlled motion pause or a design specimen. It does not
|
|
91
|
+
mean the underlying operation is paused. Product code unmounts the orb when
|
|
92
|
+
the operation ends or cannot proceed.
|
|
93
|
+
|
|
94
|
+
Import `@nadicodeai/ui/globals.css` as described in the
|
|
95
|
+
[consumer guide](consuming-cross-repo.md). The renderer inherits actual CSS
|
|
96
|
+
ink and reads canonical colour tokens. The existing `Orb` threshold adapter
|
|
97
|
+
keeps its public API: `working` maps to `processing`, `connecting` maps to
|
|
98
|
+
`connecting`, and `absent` mounts nothing. Its fixed dark appearance stays at
|
|
99
|
+
the threshold seam, outside the shared renderer.
|
|
100
|
+
|
|
101
|
+
## Accessibility and runtime
|
|
102
|
+
|
|
103
|
+
Reduced motion holds one representative frame and makes no animation requests.
|
|
104
|
+
A preference change takes effect while the component is mounted. The
|
|
105
|
+
operation's readable label remains. Pausing and resuming preserves the phase.
|
|
106
|
+
Offscreen or hidden activity stops rendering. Removing a component removes
|
|
107
|
+
its animation request and observers. DPR is capped at two. The renderer uses
|
|
108
|
+
Canvas 2D and requires no WebGL; without a 2D context, the accessible meaning
|
|
109
|
+
remains and the surrounding operation stays usable.
|
|
110
|
+
|
|
111
|
+
## Ownership and source
|
|
112
|
+
|
|
113
|
+
The component owns rendering and the closed activity choices. Applications
|
|
114
|
+
own requests, retries, progress measurements, copy, completion and recovery.
|
|
115
|
+
Product-design guidance links here for selection and usage. The foundation's
|
|
116
|
+
colour meanings remain in the design-system contract.
|
|
117
|
+
|
|
118
|
+
The source selection follows the [library's public engine and presets](https://libraries.dev/orbs).
|
|
119
|
+
The distinction between measured and unmeasured progress follows
|
|
120
|
+
[Apple's progress guidance](https://developer.apple.com/design/human-interface-guidelines/progress-indicators).
|
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).
|
|
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.
|
|
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
|
### Material surfaces
|
|
@@ -92,6 +92,12 @@ gone, is [`packages/design-system/DESIGN.md`, "## Status Vocabulary"](../../desi
|
|
|
92
92
|
|
|
93
93
|
## Package Shape Invariants
|
|
94
94
|
|
|
95
|
+
System activity uses `ActivityOrb` for loading, connecting and processing;
|
|
96
|
+
[activity.md](activity.md) owns selection, rendering and accessibility. It
|
|
97
|
+
composes with status feedback and measured progress without adding outcome
|
|
98
|
+
states to the orb. The existing threshold `Orb` adapts its public states to
|
|
99
|
+
this same renderer.
|
|
100
|
+
|
|
95
101
|
- **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.
|
|
96
102
|
- **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.
|
|
97
103
|
- **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.
|
|
@@ -101,7 +107,7 @@ gone, is [`packages/design-system/DESIGN.md`, "## Status Vocabulary"](../../desi
|
|
|
101
107
|
|
|
102
108
|
## Admission Rules
|
|
103
109
|
|
|
104
|
-
Assistant conversation and runtime APIs enter through `@nadicodeai/ui/agents`; Agent identity enters through `@nadicodeai/ui/components/agent-card` and `@nadicodeai/ui/components/agent-visual`. 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,
|
|
110
|
+
Assistant conversation and runtime APIs enter through `@nadicodeai/ui/agents`; Agent identity enters through `@nadicodeai/ui/components/agent-card` and `@nadicodeai/ui/components/agent-visual`. 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.
|
|
105
111
|
|
|
106
112
|
### Agent identity presentation
|
|
107
113
|
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# The threshold primitives
|
|
2
|
+
|
|
3
|
+
`@nadicodeai/ui/threshold` holds the four primitives a threshold is drawn from. A
|
|
4
|
+
threshold is the moment before the product is usable: a first run, a sign-in, a
|
|
5
|
+
repair. The Nadia desktop app draws its room, presence, status orb and typed line
|
|
6
|
+
from here, so the brand logic has one home.
|
|
7
|
+
|
|
8
|
+
The entry ships no screen and no state machine. Which view is on, what the
|
|
9
|
+
words are, and when a threshold ends belong to the consuming app.
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
import { NadiaPresence, Orb, ThresholdRoom, TypedLine } from "@nadicodeai/ui/threshold";
|
|
13
|
+
import "@nadicodeai/ui/threshold.css";
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## What each part is for
|
|
17
|
+
|
|
18
|
+
| Part | Its one job | Never |
|
|
19
|
+
| --- | --- | --- |
|
|
20
|
+
| `ThresholdRoom` | the place: a verde field on true black | inside the working app |
|
|
21
|
+
| `NadiaPresence` | the host: her arrival is the progress | a loader, a ring, a bar |
|
|
22
|
+
| `Orb` | status: we are working, or we wait on the Customer | on an error, or beside an actionable button |
|
|
23
|
+
| `TypedLine` | what Nadia is saying while you wait | a control or state machine |
|
|
24
|
+
|
|
25
|
+
The logo is not on this list. It belongs to window chrome, the tray and the
|
|
26
|
+
About row, never to a threshold.
|
|
27
|
+
|
|
28
|
+
## The stylesheet
|
|
29
|
+
|
|
30
|
+
`@nadicodeai/ui/threshold.css` is a separate import rather than a part of
|
|
31
|
+
`globals.css`, because the surface that needs it most does not load Tailwind:
|
|
32
|
+
the Nadia desktop shell takes this file and the design-system token layer and
|
|
33
|
+
nothing else from this package. A Tailwind consumer imports it beside
|
|
34
|
+
`@nadicodeai/ui/globals.css`.
|
|
35
|
+
|
|
36
|
+
Both the stylesheet and the room need the design-system tokens above them, from
|
|
37
|
+
`@nadicodeai/ui/globals.css` or `@nadicodeai/design-system/css`. Without that
|
|
38
|
+
cascade there is no palette, and the room paints its still field rather than
|
|
39
|
+
guessing a colour.
|
|
40
|
+
|
|
41
|
+
## `ThresholdRoom`
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
<div className="relative isolate overflow-hidden">
|
|
45
|
+
<ThresholdRoom energy={arriving ? 1 : 0} dimmed={repairing} ready={connected} />
|
|
46
|
+
{/* the presence and the words, above it */}
|
|
47
|
+
</div>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
| Prop | Type | Default | Meaning |
|
|
51
|
+
| --- | --- | --- | --- |
|
|
52
|
+
| `dimmed` | `boolean` | `false` | a repair or an error quiets the room to 22 percent |
|
|
53
|
+
| `energy` | `number` | `0` | how much of the room's pace runs, 0 to 1 |
|
|
54
|
+
| `ready` | `boolean` | `false` | success: the field takes verde vivo |
|
|
55
|
+
| `className` | `string` | — | added to the root |
|
|
56
|
+
|
|
57
|
+
The room paints absolutely inside its own relative, isolated parent and never
|
|
58
|
+
against the viewport, so a bounded surface can hold one without covering the
|
|
59
|
+
page. It is `aria-hidden`, takes no pointer events, and carries no words.
|
|
60
|
+
|
|
61
|
+
**The recipe.** Paper Shaders 0.0.78 `meshGradientFragmentShader`, its published
|
|
62
|
+
Ink preset adapted to two colours and slowed: `u_colors` are
|
|
63
|
+
`--nc-identity-verde` and `--nc-identity-ink`, `u_colorsCount` 2, `u_distortion`
|
|
64
|
+
1, `u_swirl` 0.2, `u_rotation` 90, `u_grainMixer` and `u_grainOverlay` 0, fit
|
|
65
|
+
contain, scale 1. Mount speed is 0.22 at energy 1, starting frame 12000, minimum
|
|
66
|
+
pixel ratio 1, maximum pixel count 1,024,000, canvas opacity 0.65. The room is
|
|
67
|
+
masked with `radial-gradient(ellipse 65% 58% at 50% 35%, black 10%, transparent
|
|
68
|
+
95%)`. The two colours are shader uniforms, never CSS.
|
|
69
|
+
|
|
70
|
+
`energy` is a scalar so a screen can decide. The captured recipe is energy 1;
|
|
71
|
+
Nadia's first run holds it there while she is still arriving and drops it to 0
|
|
72
|
+
when she has, which stills the field. Sign-in, success and every repair state
|
|
73
|
+
pass 0.
|
|
74
|
+
|
|
75
|
+
**One canvas per window.** While `NadiaPresence` is painting her arrival with
|
|
76
|
+
water, the room mounts nothing and shows this same still field; the moment the
|
|
77
|
+
water leaves, at reveal 1 or when it cannot paint, the room's canvas mounts.
|
|
78
|
+
The budget lives in the package, so a surface that composes both, as first run
|
|
79
|
+
does, keeps the register's one shader canvas without doing anything.
|
|
80
|
+
|
|
81
|
+
**No GPU, no tokens, or a lost context.** The room keeps its still field: a
|
|
82
|
+
token-built radial gradient that needs no WebGL. It carries `data-surface`,
|
|
83
|
+
`"shader"` while the canvas paints and `"still"` otherwise, and a mount that
|
|
84
|
+
fails leaves no dead canvas covering the fallback. A `webglcontextlost` event
|
|
85
|
+
returns it to the still field rather than to a black rectangle.
|
|
86
|
+
|
|
87
|
+
**Reduced motion.** Speed 0: the shader paints frame 12000 once and stops. The
|
|
88
|
+
room still has its colour and its shape; it just holds one frame.
|
|
89
|
+
|
|
90
|
+
## `NadiaPresence`
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
<NadiaPresence label={t("install.progress")} reveal={fraction} src={portrait} state="arriving" />
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
| Prop | Type | Meaning |
|
|
97
|
+
| --- | --- | --- |
|
|
98
|
+
| `label` | `string` | the accessible name of the arrival |
|
|
99
|
+
| `reveal` | `number` | 0 behind the field, 1 standing clear |
|
|
100
|
+
| `src` | `string` | the portrait master's URL |
|
|
101
|
+
| `state` | `"arriving" \| "present" \| "ready" \| "error"` | which threshold she is in |
|
|
102
|
+
|
|
103
|
+
`src` is a prop rather than an import because this package imports no binary
|
|
104
|
+
asset of its own; the consuming surface resolves
|
|
105
|
+
`@nadicodeai/design-system/assets/nadia-solid-surface` with its own bundler and
|
|
106
|
+
passes the URL. Keeping it out of the library is what lets a Node or a
|
|
107
|
+
bundler-free consumer load the module at all.
|
|
108
|
+
|
|
109
|
+
**The arrival.** Opacity is `0.025 + 0.975 × reveal^1.25`, exported as
|
|
110
|
+
`arrivalOpacity` so a screen can read the same curve. At the start she is a
|
|
111
|
+
trace, which gives the arrival somewhere to go. Her lower edge dissolves into
|
|
112
|
+
the room with `linear-gradient(black 55%, transparent 98%)` instead of ending on
|
|
113
|
+
a rectangle. The `error` state multiplies her by 0.62.
|
|
114
|
+
|
|
115
|
+
**The water.** While `state` is `arriving`, `reveal` is below 1 and motion is
|
|
116
|
+
allowed, the portrait is painted by Paper's `waterFragmentShader` at a
|
|
117
|
+
restrained intensity: `caustic`, `edges` and `waves` are `0.35`, `0.4` and `0.3`
|
|
118
|
+
scaled by how much of the arrival is left, with `layering` 0.4 and `highlights`
|
|
119
|
+
0.12, `colorBack` fully transparent and `colorHighlight` on
|
|
120
|
+
`--nc-identity-verde`. Every amplitude reaches zero as she arrives, so her
|
|
121
|
+
geometry holds and the water is gone by the time she stands clear. At reveal 1,
|
|
122
|
+
and in every other state, this is the plain portrait.
|
|
123
|
+
|
|
124
|
+
While the water is wanted it is the window's one shader canvas: the presence
|
|
125
|
+
holds the canvas budget from the moment it asks for the water until the water
|
|
126
|
+
is torn down, and the room keeps its still field for exactly that long.
|
|
127
|
+
|
|
128
|
+
The water canvas is masked to her own silhouette. Water adds its highlight term
|
|
129
|
+
to the whole frame rather than only to the sampled image, so an unmasked canvas
|
|
130
|
+
paints a faintly lit rectangle over the room; the mask matches the shader's own
|
|
131
|
+
contain fit and centred origin.
|
|
132
|
+
|
|
133
|
+
**Reduced motion, no GPU, or an image that will not decode.** The plain
|
|
134
|
+
portrait, at the same reveal. The figure carries `data-surface`, `"water"` or
|
|
135
|
+
`"portrait"`.
|
|
136
|
+
|
|
137
|
+
**Success.** `state="ready"` runs one 360 ms verde vivo beat over her silhouette
|
|
138
|
+
on `--nc-ease-out-strong`, opacity 0 to 0.22 at 35 percent and back to 0, once.
|
|
139
|
+
Verde vivo is Nadia's presence accent and this is the only place a threshold
|
|
140
|
+
spends it, which is why the buttons beside it stay neutral. Reduced motion
|
|
141
|
+
suppresses the beat.
|
|
142
|
+
|
|
143
|
+
**Assistive technology.** A hidden `progressbar` carries `label` and the floored
|
|
144
|
+
percentage, so it reaches 100 only when the arrival is actually finished. The
|
|
145
|
+
portrait itself is decorative; the words are the typed line's.
|
|
146
|
+
|
|
147
|
+
## `Orb`
|
|
148
|
+
|
|
149
|
+
```tsx
|
|
150
|
+
<Orb label={t("install.working")} state="working" />
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
| Prop | Type | Default | Meaning |
|
|
154
|
+
| --- | --- | --- | --- |
|
|
155
|
+
| `label` | `string` | — | what the orb means, in the Customer's language |
|
|
156
|
+
| `size` | `88 \| 64 \| 20` | `88` | CSS pixels: 88 at a threshold, 20 inside a running turn, per the living-elements register |
|
|
157
|
+
| `state` | `"working" \| "connecting" \| "absent"` | — | see below |
|
|
158
|
+
|
|
159
|
+
The threshold adapter preserves its public states: `working` selects the
|
|
160
|
+
shared `processing` activity, `connecting` selects `connecting`, and `absent`
|
|
161
|
+
renders nothing. The threshold defaults to fixed dark ink; `theme="auto"`
|
|
162
|
+
inherits the surrounding product's ink and `theme="light"` fixes light-surface
|
|
163
|
+
ink. There is one renderer, `ActivityOrb`, rather than a second threshold orb.
|
|
164
|
+
|
|
165
|
+
[System activity](activity.md) owns the motion recipes, colour treatment,
|
|
166
|
+
optical sizes, reduced-motion behaviour and connection-versus-person-waiting
|
|
167
|
+
rule. Applications retain their operation state machines and copy.
|
|
168
|
+
|
|
169
|
+
## `TypedLine`
|
|
170
|
+
|
|
171
|
+
```tsx
|
|
172
|
+
<TypedLine text={t("install.step.1")} />
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
| Prop | Type | Default |
|
|
176
|
+
| --- | --- | --- |
|
|
177
|
+
| `cadenceMs` | `number` | `44` |
|
|
178
|
+
| `text` | `string` | — |
|
|
179
|
+
| `className` | `string` | — |
|
|
180
|
+
|
|
181
|
+
The line types itself at 44 ms per character, the cadence the rendered
|
|
182
|
+
comparisons selected for a first-run pause. Three things it does that a naive
|
|
183
|
+
typewriter does not:
|
|
184
|
+
|
|
185
|
+
- the count comes from elapsed time, not from how many callbacks arrived, so a
|
|
186
|
+
software renderer or a busy main thread cannot stretch a one second sentence
|
|
187
|
+
into five;
|
|
188
|
+
- a hidden copy of the whole sentence reserves its width, so the orb beside it
|
|
189
|
+
does not travel sideways with each character;
|
|
190
|
+
- the live region receives the complete sentence at once, so a screen reader is
|
|
191
|
+
never read one letter at a time.
|
|
192
|
+
|
|
193
|
+
A new `text` retypes from a cleared line. The caret is steady while typing and
|
|
194
|
+
hidden afterwards.
|
|
195
|
+
|
|
196
|
+
**Reduced motion.** The whole line at once, and no caret.
|
|
197
|
+
|
|
198
|
+
## Tokens, and the one fixed presentation
|
|
199
|
+
|
|
200
|
+
Everything visual comes from design-system tokens. A threshold is a fixed dark
|
|
201
|
+
surface: it is true black in every theme, so the text ladder reads
|
|
202
|
+
`--nc-dark-ink`, `--nc-dark-body` and `--nc-dark-muted` directly instead of
|
|
203
|
+
branching on the app's mode. That is the fixed-presentation escape
|
|
204
|
+
[`contract.md`](contract.md) names, taken once and coherently for the whole
|
|
205
|
+
surface.
|
|
206
|
+
|
|
207
|
+
The room's two colours are the exception that proves it: they are shader
|
|
208
|
+
uniforms read off `--nc-identity-verde` and `--nc-identity-ink` at mount, not
|
|
209
|
+
CSS values.
|
|
210
|
+
|
|
211
|
+
## Motion budget
|
|
212
|
+
|
|
213
|
+
The foundation's: `--nc-duration-open` for a room fading in or out,
|
|
214
|
+
`--nc-duration-confirm` for the presence, `--nc-ease-emphasized` and
|
|
215
|
+
`--nc-ease-out-strong`. The success beat is the one
|
|
216
|
+
duration written here rather than taken from a token, 360 ms, because it is an
|
|
217
|
+
envelope rather than a state change.
|
|
218
|
+
|
|
219
|
+
Under `prefers-reduced-motion: reduce` the room holds one frame, the water is
|
|
220
|
+
not mounted, the presence jumps to its value, the beat does not run, the orb
|
|
221
|
+
draws once and the line appears whole.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nadicodeai/ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -19,6 +19,11 @@
|
|
|
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",
|
|
22
27
|
"./hooks": {
|
|
23
28
|
"types": "./dist/hooks/index.d.ts",
|
|
24
29
|
"import": "./dist/hooks/index.js"
|
|
@@ -61,7 +66,7 @@
|
|
|
61
66
|
"@assistant-ui/react": "0.15.1",
|
|
62
67
|
"@assistant-ui/react-markdown": "0.14.8",
|
|
63
68
|
"@base-ui/react": "^1.6.0",
|
|
64
|
-
"@nadicodeai/design-system": "
|
|
69
|
+
"@nadicodeai/design-system": "5.1.0",
|
|
65
70
|
"@paper-design/shaders": "0.0.78",
|
|
66
71
|
"class-variance-authority": "^0.7.1",
|
|
67
72
|
"clsx": "^2.1.1",
|
|
@@ -78,6 +83,7 @@
|
|
|
78
83
|
"remark-gfm": "^4.0.1",
|
|
79
84
|
"sonner": "^2.0.7",
|
|
80
85
|
"tailwind-merge": "^3.0.0",
|
|
86
|
+
"thinking-orbs": "0.3.1",
|
|
81
87
|
"tw-animate-css": "^1.4.0",
|
|
82
88
|
"tw-shimmer": "0.4.12",
|
|
83
89
|
"zustand": "^5.0.14"
|