@nadicodeai/ui 10.1.0 → 12.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 (96) hide show
  1. package/AGENTS.md +39 -24
  2. package/README.md +58 -124
  3. package/dist/agent-orb.d.ts +1 -1
  4. package/dist/agent-orb.d.ts.map +1 -1
  5. package/dist/assets/brand/index.json +1 -1
  6. package/dist/components/action-menu.js +1 -1
  7. package/dist/components/agent-card.d.ts.map +1 -1
  8. package/dist/components/agent-card.js +2 -4
  9. package/dist/components/agent-orb.d.ts +11 -5
  10. package/dist/components/agent-orb.d.ts.map +1 -1
  11. package/dist/components/agent-orb.js +8 -6
  12. package/dist/components/alert.js +3 -3
  13. package/dist/components/badge.js +6 -6
  14. package/dist/components/brand-field.d.ts +2 -0
  15. package/dist/components/brand-field.d.ts.map +1 -1
  16. package/dist/components/brand-field.js +3 -1
  17. package/dist/components/button.d.ts +1 -1
  18. package/dist/components/button.d.ts.map +1 -1
  19. package/dist/components/button.js +4 -5
  20. package/dist/components/carousel.js +2 -2
  21. package/dist/components/chart-bar-horizontal.js +1 -1
  22. package/dist/components/chart.js +1 -1
  23. package/dist/components/command.js +1 -1
  24. package/dist/components/context-menu.js +1 -1
  25. package/dist/components/diff-count.js +1 -1
  26. package/dist/components/dropdown-menu.js +3 -3
  27. package/dist/components/footer.d.ts +4 -7
  28. package/dist/components/footer.d.ts.map +1 -1
  29. package/dist/components/footer.js +1 -4
  30. package/dist/components/item.js +1 -1
  31. package/dist/components/kpi-card.js +2 -2
  32. package/dist/components/label-system.d.ts +1 -5
  33. package/dist/components/label-system.d.ts.map +1 -1
  34. package/dist/components/label-system.js +2 -6
  35. package/dist/components/menu-choice-row.js +1 -1
  36. package/dist/components/menubar.js +1 -1
  37. package/dist/components/object-workspace.js +1 -1
  38. package/dist/components/progress.js +1 -1
  39. package/dist/components/scope-switcher.d.ts +24 -0
  40. package/dist/components/scope-switcher.d.ts.map +1 -0
  41. package/dist/components/scope-switcher.js +34 -0
  42. package/dist/components/settings-section.d.ts +2 -1
  43. package/dist/components/settings-section.d.ts.map +1 -1
  44. package/dist/components/settings-section.js +2 -2
  45. package/dist/components/status-dot.js +1 -1
  46. package/dist/components/status-row.js +1 -1
  47. package/dist/components/table.d.ts.map +1 -1
  48. package/dist/components/table.js +1 -1
  49. package/dist/components/tabs.d.ts +11 -5
  50. package/dist/components/tabs.d.ts.map +1 -1
  51. package/dist/components/tabs.js +37 -5
  52. package/dist/components/tooltip.js +1 -1
  53. package/dist/components/trend-badge.d.ts.map +1 -1
  54. package/dist/components/trend-badge.js +1 -2
  55. package/dist/eslint/index.js +4 -0
  56. package/dist/eslint/rules/jsx-class-utility.js +1 -1
  57. package/dist/eslint/rules/no-shadcn-appearance-override.js +3 -3
  58. package/dist/eslint/rules/one-control-height.js +94 -0
  59. package/dist/eslint/rules/settings-section-description.js +45 -0
  60. package/dist/index.d.ts +1 -1
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/index.js +1 -1
  63. package/dist/internal/agent-orb-motion.d.ts +1 -0
  64. package/dist/internal/agent-orb-motion.d.ts.map +1 -1
  65. package/dist/internal/agent-orb-motion.js +6 -3
  66. package/dist/internal/agent-orb.d.ts +10 -2
  67. package/dist/internal/agent-orb.d.ts.map +1 -1
  68. package/dist/internal/agent-orb.js +19 -9
  69. package/dist/internal/assistant-ui/attachment.js +4 -4
  70. package/dist/internal/assistant-ui/follow-up-suggestions.js +1 -1
  71. package/dist/internal/assistant-ui/markdown-text.js +4 -4
  72. package/dist/internal/assistant-ui/reasoning.js +2 -2
  73. package/dist/internal/assistant-ui/thread.d.ts.map +1 -1
  74. package/dist/internal/assistant-ui/thread.js +11 -9
  75. package/dist/internal/assistant-ui/tool-group.js +2 -2
  76. package/dist/styles/components.css +9 -5
  77. package/dist/styles/globals.css +43 -0
  78. package/package.json +3 -4
  79. package/dist/components/handoff-banner.d.ts +0 -9
  80. package/dist/components/handoff-banner.d.ts.map +0 -1
  81. package/dist/components/handoff-banner.js +0 -6
  82. package/docs/activity.md +0 -113
  83. package/docs/agent-orb.md +0 -168
  84. package/docs/component-classification.md +0 -45
  85. package/docs/consuming-cross-repo.md +0 -116
  86. package/docs/contract.md +0 -388
  87. package/docs/icon-assets.md +0 -106
  88. package/docs/migration/1.0.0.md +0 -75
  89. package/docs/migration/10.0.0.md +0 -144
  90. package/docs/migration/2.0.0.md +0 -69
  91. package/docs/migration/6.0.0.md +0 -30
  92. package/docs/migration/9.0.0.md +0 -35
  93. package/docs/migration/base-ui-migration.md +0 -62
  94. package/docs/model-identity.md +0 -18
  95. package/docs/product-compositions.md +0 -96
  96. package/docs/product-sections.md +0 -200
@@ -27,13 +27,17 @@
27
27
  * The one glass material (DESIGN.md "Elevation & Depth"): floating chrome —
28
28
  * menus, popovers, selects, comboboxes, the command palette, and bars — may
29
29
  * carry it; content surfaces stay solid. Glass composites only over our own
30
- * content, and its elevation falloff's edge ring stays its one separator.
31
- * Declared as a utility so variant prefixes can gate it (navigation-menu's
32
- * non-viewport popup). Reduced transparency falls back to the same solid
33
- * surface at the same elevation.
30
+ * content. Its edge is the floating rung's recipe, whose inset top highlight
31
+ * and inner hairline only show once the fill turns translucent. A fine grain
32
+ * over the fill keeps the blurred colour behind it from banding. Declared as a
33
+ * utility so variant prefixes can gate it (navigation-menu's non-viewport
34
+ * popup). Reduced transparency falls back to the same solid surface at the
35
+ * same elevation.
34
36
  */
35
37
  @utility nc-glass {
36
- background: color-mix(in srgb, var(--popover) var(--nc-material-glass-opacity), transparent);
38
+ background:
39
+ url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxNDAnIGhlaWdodD0nMTQwJz48ZmlsdGVyIGlkPSdnJz48ZmVUdXJidWxlbmNlIHR5cGU9J2ZyYWN0YWxOb2lzZScgYmFzZUZyZXF1ZW5jeT0nLjg1JyBudW1PY3RhdmVzPScyJyBzdGl0Y2hUaWxlcz0nc3RpdGNoJy8+PGZlQ29sb3JNYXRyaXggdHlwZT0nc2F0dXJhdGUnIHZhbHVlcz0nMCcvPjwvZmlsdGVyPjxyZWN0IHdpZHRoPScxMDAlJyBoZWlnaHQ9JzEwMCUnIGZpbHRlcj0ndXJsKCNnKScgb3BhY2l0eT0nLjA1Jy8+PC9zdmc+"),
40
+ color-mix(in srgb, var(--popover) var(--nc-material-glass-opacity), transparent);
37
41
  backdrop-filter: blur(var(--nc-material-glass-blur)) saturate(var(--nc-material-glass-saturation));
38
42
 
39
43
  @media (prefers-reduced-transparency: reduce) {
@@ -29,6 +29,49 @@
29
29
  */
30
30
  @custom-variant dark (&:where(.dark, .dark *));
31
31
 
32
+ /*
33
+ * Dark mode carries height in tone (DESIGN.md "Elevation & Depth"): the page
34
+ * stays true black and each rung's surface is one fixed step lighter. The rung
35
+ * sets the card and popover roles on its own element, so whichever role that
36
+ * surface paints with takes the rung's tone, whatever order the classes land
37
+ * in. A rung behind a variant prefix keeps the page's roles. In light every
38
+ * tone is the solid white. This belongs to the application
39
+ * stylesheet, not components.css, because a host keeps its own roles.
40
+ */
41
+ @layer components {
42
+ .nc-elevation-resting {
43
+ --card: var(--nc-material-resting);
44
+ --popover: var(--nc-material-resting);
45
+ }
46
+
47
+ .nc-elevation-raised {
48
+ --card: var(--nc-material-raised);
49
+ --popover: var(--nc-material-raised);
50
+ }
51
+
52
+ .nc-elevation-floating {
53
+ --card: var(--nc-material-floating);
54
+ --popover: var(--nc-material-floating);
55
+ }
56
+
57
+ .nc-elevation-modal {
58
+ --card: var(--nc-material-modal);
59
+ --popover: var(--nc-material-modal);
60
+ }
61
+
62
+ /*
63
+ * A lifted dark surface is lighter than the soft steps the hover, selected
64
+ * and keycap fills take on the black page, so on it those fills step up
65
+ * from its own tone instead: 8% of the foreground over the surface. Light
66
+ * surfaces are white, where the soft steps already read.
67
+ */
68
+ :where(.dark)
69
+ :is(.nc-elevation-resting, .nc-elevation-raised, .nc-elevation-floating, .nc-elevation-modal) {
70
+ --accent: color-mix(in oklab, var(--popover) 92%, var(--foreground));
71
+ --muted: color-mix(in oklab, var(--popover) 92%, var(--foreground));
72
+ }
73
+ }
74
+
32
75
  @layer base {
33
76
  * {
34
77
  @apply border-border outline-ring;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nadicodeai/ui",
3
- "version": "10.1.0",
3
+ "version": "12.1.0",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -52,8 +52,7 @@
52
52
  "LICENSES",
53
53
  "NOTICE",
54
54
  "README.md",
55
- "AGENTS.md",
56
- "docs"
55
+ "AGENTS.md"
57
56
  ],
58
57
  "scripts": {
59
58
  "dev": "node scripts/dev-build.mjs --watch",
@@ -72,7 +71,7 @@
72
71
  "@assistant-ui/react": "0.15.21",
73
72
  "@assistant-ui/react-markdown": "0.14.16",
74
73
  "@base-ui/react": "^1.8.0",
75
- "@nadicodeai/design-system": "10.1.0",
74
+ "@nadicodeai/design-system": "12.1.0",
76
75
  "class-variance-authority": "^0.7.1",
77
76
  "clsx": "^2.1.1",
78
77
  "cmdk": "^1.1.1",
@@ -1,9 +0,0 @@
1
- import * as React from "react";
2
- declare function HandoffBanner({ className, reason, destination, children, ...props }: Omit<React.ComponentProps<"div">, "children"> & {
3
- reason?: string;
4
- destination?: string;
5
- /** Escape hatch: rich banner content that replaces the serializable model. */
6
- children?: React.ReactNode;
7
- }): React.JSX.Element;
8
- export { HandoffBanner };
9
- //# sourceMappingURL=handoff-banner.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"handoff-banner.d.ts","sourceRoot":"","sources":["../../src/components/handoff-banner.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,iBAAS,aAAa,CAAC,EACrB,SAAS,EACT,MAAM,EACN,WAAW,EACX,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,GAAG;IACjD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,qBAWA;AAED,OAAO,EAAE,aAAa,EAAE,CAAC"}
@@ -1,6 +0,0 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { cn } from "../lib/utils.js";
3
- function HandoffBanner({ className, reason, destination, children, ...props }) {
4
- return (_jsx("div", { "data-slot": "handoff-banner", className: cn("nc-handoff-banner", className), ...props, children: children ?? (_jsxs(_Fragment, { children: [destination != null && _jsx("span", { "data-slot": "handoff-banner-destination", children: destination }), reason != null && _jsx("span", { "data-slot": "handoff-banner-reason", children: reason })] })) }));
5
- }
6
- export { HandoffBanner };
package/docs/activity.md DELETED
@@ -1,113 +0,0 @@
1
- # System activity
2
-
3
- Who does the work decides how it looks.
4
-
5
- An Agent's work shows that Agent's orb in its state and a sentence saying what
6
- it is doing; [AgentOrb](agent-orb.md) owns it, and Nadia connecting is her
7
- ribbon, working. Everything on this page is the *system's* own work: a save, a
8
- sign-in, a refresh, a page load. An orb means an Agent, so system work never
9
- takes one.
10
-
11
- | The wait | What it shows |
12
- | --- | --- |
13
- | Under about a second | The control's own label changes to its pending word. Nothing else. |
14
- | Past a second | One `WaitingLine` beside the work, and nothing else moves. |
15
- | Measurable | `Progress` with a written count ("120 di 240 fatture"). |
16
- | A page load | `Skeleton` placeholders in the shape of the content, still. |
17
-
18
- One wait gets one indicator. Separately progressing operations may each have
19
- one. A request waiting for a person is not a wait: queued, paused, offline,
20
- blocked, successful and failed conditions take the status or feedback
21
- treatment. An account being active is not an activity.
22
-
23
- ## The pending label
24
-
25
- The control keeps its place, its size and its icon; only its word changes, and
26
- the caller marks the control `disabled` and `aria-busy` so the change is
27
- announced as well as read.
28
-
29
- ```tsx
30
- import { PendingLabel } from "@nadicodeai/ui/components/pending-label";
31
-
32
- <Button aria-busy={saving || undefined} disabled={saving} type="submit">
33
- <PendingLabel pending={saving} pendingLabel="Salvataggio…">
34
- Salva modifiche
35
- </PendingLabel>
36
- </Button>;
37
- ```
38
-
39
- `pending` and `pendingLabel` are required; the children are the idle label. It
40
- renders a `span` carrying `data-slot="pending-label"` and, while pending,
41
- `data-pending="true"`. It adds no mark of its own: a control that had an icon
42
- keeps that icon, unchanged, throughout the wait.
43
-
44
- ## The waiting line
45
-
46
- A 2 px track of ink with a 32% segment crossing it linearly on the `wait`
47
- token, after one second of waiting. Before that second the track is there and
48
- empty, unannounced, so the line arriving moves nothing around it; a wait the
49
- person barely notices does not deserve movement, and a control that flashed an
50
- indicator for 200 ms reads as a glitch.
51
-
52
- ```tsx
53
- import { WaitingLine } from "@nadicodeai/ui/components/waiting-line";
54
-
55
- <WaitingLine label="Caricamento delle fatture" />;
56
-
57
- <div>
58
- <p>Stiamo preparando il documento.</p>
59
- <WaitingLine decorative />
60
- </div>;
61
- ```
62
-
63
- A meaningful standalone line takes `label` and announces itself as a status;
64
- `decorative` removes its accessible name when the text beside it already says
65
- what is happening. Width comes from the caller (`className="w-24"`, or the
66
- full width of the region it belongs to); height, ink, segment and timing are
67
- the component's and are not tunable. The one-second delay is
68
- `WAITING_LINE_DELAY_MS`, exported so a test can name it rather than guess it.
69
-
70
- Reduced motion drops the travel and breathes the full line in opacity, so a
71
- wait still reads as a wait. The rule lives in
72
- `packages/design-system/src/css/motion.css` as `.nc-anim-wait`, with the rest
73
- of the motion vocabulary.
74
-
75
- Product code unmounts the line when the operation ends or cannot proceed.
76
-
77
- ## Measurable and still
78
-
79
- `Progress` carries work whose completed amount is known, always beside a
80
- written count. When work will become measurable, reserve the progress
81
- treatment from the start so the transition does not move the layout. Never
82
- invent a percentage.
83
-
84
- `Skeleton` is a still placeholder shaped like the content behind it. It does
85
- not pulse. Do not show a skeleton and a waiting line for the same wait: the
86
- placeholders already say the page is arriving.
87
-
88
- ## What retired
89
-
90
- `ActivityOrb` and `mountActivityOrb`, `Spinner`, `GlyphSpinner`, Sonner's
91
- spinning loading icon, the raw `LoaderCircle` spinners in `feedback-state`,
92
- `installer-shell`, `automation-section` and `confirm-dialog`, the assistant-ui
93
- attachment, tool-fallback and tool-group spinners, and the shimmer text on the
94
- reasoning, tool-fallback and tool-group triggers. With them went the
95
- `thinking-orbs` system-activity palette, the `tw-shimmer` dependency and the
96
- duplicate `@utility shimmer`, and the unused `nc-anim-typing` and
97
- `nc-anim-shimmer` hooks.
98
-
99
- Where one of those marked a running state inside a component, the state is now
100
- said in words and marked with a still glyph: an installer step and an
101
- automation row take `CircleDotIcon`, and a running tool call says "Running
102
- tool". A status badge never moves; its word and its mark carry the state.
103
-
104
- ## Ownership and source
105
-
106
- The components own the delay, the geometry, the ink and the motion. The
107
- application owns the request, the retry, the copy, the completion and the
108
- recovery. The decision behind this page is
109
- [the nadicode design system ADR](../../../docs/adr/2026-09-23-the-nadicode-design-system.md);
110
- the design-system side of it is
111
- [`DESIGN.md`, "## System activity"](../../design-system/DESIGN.md#system-activity).
112
- The distinction between measured and unmeasured progress follows
113
- [Apple's progress guidance](https://developer.apple.com/design/human-interface-guidelines/progress-indicators).
package/docs/agent-orb.md DELETED
@@ -1,168 +0,0 @@
1
- # Agent orbs
2
-
3
- `AgentOrb` is the one Agent identity in every product: seven structures from
4
- `thinking-orbs` 0.3.1, each painted in one chosen colour, and Nadia's own
5
- striped ribbon. These are persistent appearance choices, not activity states.
6
- No other Agent form, avatar or portrait exists; a custom picture a person
7
- uploads in Nadia is theirs, not an identity this package draws.
8
-
9
- | Form | Structure | Upstream preset |
10
- | --- | --- | --- |
11
- | `ribbon` | Flowing dotted bands | `composing` |
12
- | `lattice` | Rotating particle bands | `solving` |
13
- | `orbit` | Particles on tilted orbits | `working` |
14
- | `globe` | Dotted globe and scanning meridian | `searching` |
15
- | `network` | Connected constellation | `connecting` |
16
- | `braid` | Interwoven strands | `weaving` |
17
- | `morph` | Changing geometric contour | `shaping` |
18
-
19
-
20
- ## Nadia
21
-
22
- `form="nadia"` is the ribbon striped along its whole length, across its width:
23
- azzurro (`--nc-identity-azzurro`), verde, the orb's own pigment, and rosso.
24
- The third stripe is the white of the tricolore; it takes the inherited text
25
- colour so it stays visible as ink on a white ground. The stripes are fixed:
26
- the same four at every size, in both themes and in every state, and nobody
27
- chooses them. Each dot keeps its lane through the engine's depth sort, so a
28
- stripe never changes colour as the ribbon turns. Her motion is the ribbon's.
29
-
30
- ## Stored appearance
31
-
32
- Nadia stores an Agent's look in its profile and reports it to the Portal on
33
- every check-in; the Portal renders it and never writes it.
34
- `@nadicodeai/ui/components/agent-orb-appearance` is the one reading of those
35
- strings, server-safe so a server can resolve a row before it renders:
36
-
37
- | Field | Stored as |
38
- | --- | --- |
39
- | `shape` | `orb:orbit`, `orb:ribbon`, `orb:lattice`, `orb:globe`, `orb:network`, `orb:braid`, `orb:morph` |
40
- | `color` | The design system's identity hex of cobalto, verde or arancio, or null; or a rosso an Agent chose before rosso left the picker |
41
-
42
- `resolveAgentOrb(stored, { root })` returns the form and colour to draw. The
43
- root Agent is Nadia and draws `nadia` whatever is stored. An unknown or empty
44
- shape draws `orbit`; an unknown or empty colour draws the inherited text
45
- colour, which is also the default a picker's reset restores. Paint a colour
46
- with `agentOrbColorTokens[color]`. Each retired `forma:` spelling resolves to
47
- its own orb, so an Agent keeps a distinct look until someone changes it.
48
-
49
- ## The three colours
50
-
51
- A person chooses cobalto, verde or arancio, or nothing, which leaves the orb
52
- the inherited text colour. All three clear 3:1 against both the white and the
53
- black canvas, so a chosen orb stays visible in either theme; giallo does not
54
- (1.6:1 on white) and is not offered.
55
-
56
- Rosso is not offered either. It is exactly the error red, so a red Agent beside
57
- a red "Bloccato" badge read as a broken Agent rather than a chosen colour.
58
- Nadia keeps rosso in her own stripes, where it is one of four and says nothing
59
- on its own. An Agent that chose rosso before it left the picker keeps a colour:
60
- `resolveAgentOrb` maps a stored `rosso`, by name or by the `#d3302f` hex, to
61
- arancio, the nearest hue that is not a status. That is a mapping on the way in,
62
- not an offer on the way out — `agentOrbColors` is what a picker shows, and it
63
- has three entries.
64
-
65
- The appearance is a person's choice, never derived: do not infer it from a name,
66
- an id hash or a role. A machine check-in is not Agent activity, so a surface that
67
- only knows check-ins draws the orb idle. The form does not select a job, tools or runtime status. Do not switch it when
68
- an Agent starts searching or solving. Applications own the persisted form,
69
- colour, name and status; names beside avatars provide exact identification.
70
-
71
- ## Component
72
-
73
- ```tsx
74
- import { AgentOrb } from "@nadicodeai/ui/components/agent-orb";
75
-
76
- <AgentOrb form="ribbon" color="var(--nc-identity-cobalto)" size={32} label="Research" />;
77
- <AgentOrb form="nadia" size={32} label="Nadia" />;
78
- <AgentOrb form="globe" presence="over-picture" size={96} state="working" label="Ricerca" />;
79
- ```
80
-
81
- `form` is required. `color` accepts one CSS colour, including a customer-chosen
82
- colour or a canonical token. Omit it to inherit text colour. This configurable
83
- artwork is not a new palette for controls or status. Every particle and line
84
- of a chosen form uses the same pigment, and on Nadia each stripe keeps its own;
85
- opacity, not another colour, creates depth. Working
86
- releases occasional coloured flourishes; Done repeats the same arcs,
87
- arrowheads, rings and sparkles in a short celebration around
88
- that single-colour identity, using the existing chart and warning tokens. Choose a
89
- colour visible on the intended background. A deliberately black pigment on a
90
- black surface is not automatically recoloured.
91
-
92
- A native startup screen paints an orb before React loads through
93
- `mountAgentOrb(canvas, { form, size, presence, state, paused })` from
94
- `@nadicodeai/ui/agent-orb`, the React-free entry beside
95
- `@nadicodeai/ui/activity`. It is the renderer `AgentOrb` itself mounts, with
96
- the same motion, states and presence; the caller supplies the canvas and the
97
- adjacent name, and calls `destroy()` when React replaces the startup surface.
98
- Import `@nadicodeai/ui/globals.css` with it: the renderer inherits the CSS ink
99
- and reads the colour tokens from the element.
100
-
101
- Sizes are 20, 24, 32, 40, 64, 96 and 160 CSS pixels. Menu sizes through 32 use
102
- the upstream compact density; larger sizes use its detailed profile. `paused`
103
- holds the current frame and resumes without resetting the appearance. An
104
- accessible image requires `label`; use `decorative` beside an existing name.
105
- The component is Canvas 2D, not WebGL. It reuses the vendor's portable geometry
106
- API because the stock React component only offers monochrome light/dark ink,
107
- not a configurable pigment.
108
-
109
- Motion is limited to 30 painted frames per second. Offscreen and hidden orbs
110
- stop scheduling animation. Unmount removes the frame request and observers.
111
- DPR is capped at two. Without Canvas 2D, the accessible name remains
112
- available.
113
-
114
- The engine paints the box the canvas occupies, not the `size` it was given: a
115
- `ResizeObserver` follows the element, and the backing store and the geometry's
116
- density profile both come from the measured CSS pixels. `size` sets that box
117
- through `--nc-agent-orb-size` in a class, so a caller whose layout decides the
118
- size overrides it with its own sizing class and the orb stays sharp at
119
- whatever width it lands on. The declared size still stands in before layout
120
- has measured anything, on a server render and in a test environment without
121
- layout.
122
-
123
- `presence` decides how loudly one orb reads on its ground, per mount, never
124
- as a module setting: two orbs on one page can want different weights.
125
- `plain`, the default, is the interface's own ink. `over-picture` multiplies
126
- ink by 2.3 and every dot and line radius by 1.9, for an orb drawn on a
127
- photograph, where the plain weights sink into the image. Nadia on open sky
128
- keeps `plain`.
129
-
130
- Reduced motion turns movement off and keeps opacity and colour. A thinking or
131
- working orb breathes between full ink and 0.45 over 1.6 s, ease-in-out, so
132
- busy still reads as busy; its geometry does not turn, and the state cue
133
- breathes with the body. Every other state, idle, waiting, blocked and done
134
- included, holds one still frame. The preference is observed live.
135
-
136
- `state` accepts idle, working, waiting, blocked, thinking and done, and every
137
- surface passes the Agent's real one: only Agents move on their own, so motion
138
- on a screen means an Agent is working and a still orb means it is not. Idle
139
- holds one still pose and schedules no frame after its first paint; a surface
140
- that knows only check-ins draws idle, because a machine check-in is not Agent
141
- activity. Geometry and state gestures have separate clocks. Each form has its
142
- own geometry pace, shared across avatar sizes, which Working and Thinking
143
- keep; Morph holds a contour for about 1.6 seconds and changes it over one
144
- second. Waiting holds its pose; Blocked makes one gentle interrupted effort
145
- and stops.
146
- V7 keeps the material free-standing and intact:
147
- no gear, thought bubble, pause, exclamation mark or check is cut into the form.
148
- Working and Thinking preserve the native geometry: do not stretch, flatten,
149
- fold or emit copies of the orb to convey either state. Working uses one open,
150
- tapered multicoloured ribbon passing behind and in front of the orb on a
151
- 7.2-second loop. Thinking uses three persistent coloured dots above the orb,
152
- with sequential opacity and slight size pulses on a 3.6-second loop.
153
- The distinct symbols remain visible throughout both cycles. Done repeats
154
- the ribbon in three celebratory sweeps and then rests. Changing `motionKey`
155
- replays it. These exterior cues use the celebration palette; the body keeps
156
- its configured pigment.
157
- No state has an enclosing circle. Keep a nearby status label where exact
158
- state identification matters, including reduced motion and compact sizes.
159
-
160
- In the Portal, [`agent-identity.tsx`](../../../apps/portal/src/components/agent-identity.tsx)
161
- draws every Agent, in its lists, Agent workspace and the sign-in, sign-up and
162
- Get Nadia pages, and `src/lib/agent-orb.ts` maps what a machine reported to
163
- the state drawn. It also gives an orb a `travelId`, which wraps it in React's
164
- `<ViewTransition>` so the same orb travels from a list row into the Agent's
165
- page; `apps/portal/docs/design-doctrine.md` owns that rule. Inspect them in
166
- the running local Portal for compact and full-size appearances.
167
-
168
- [Upstream library and presets](https://libraries.dev/orbs), MIT licensed.
@@ -1,45 +0,0 @@
1
- # Component Classification
2
-
3
- This file records the ownership class for U4 product compositions. The package may render standard product UI, but consuming apps own app behavior, data, persistence, and product-specific promises.
4
-
5
- If first-party shadcn or assistant-ui registry source already covers the same
6
- generic product UI, do not add a parallel nadicode package API. Use the
7
- registry item in the consuming app and adapt it there with app-owned data,
8
- copy, and behavior.
9
-
10
- ## Removed Direct Reinventions
11
-
12
- | Removed API | Registry source to use instead | Reason |
13
- | --- | --- | --- |
14
- | Package-level `DataTable`, `DataTableColumnControls`, `DataTableEmptyState`, `DataTableState` | [shadcn Data Table guide](https://ui.shadcn.com/docs/components/base/data-table), `@shadcn/table`, and the table in `@shadcn/dashboard-01` | These are first-party composition sources for sorting, visibility, filtering, and pagination. Portal retains its app-owned `DataTable` adapter for registry and URL behavior. |
15
- | `CommandPalette`, `ModelPickerCommand`, `SessionPickerCommand` | `@shadcn/command` and its `CommandDialog` export ([Command guide](https://ui.shadcn.com/docs/components/base/command)) | The command primitive and dialog composition are already supplied; model/session execution is app behavior. |
16
- | `ChartCard`, `MetricChart`, `semanticChartColors` | The package chart kit, owned by [`contract.md`](contract.md) | shadcn already ships the chart primitives and blocks the kit is pulled from; metric aggregation and chart choice are app behavior. |
17
-
18
- ## Retained Package Compositions
19
-
20
- | Component | Classification | Built on | Required states | Package-owned accessibility | Fixture |
21
- | --- | --- | --- | --- | --- | --- |
22
- | `ActionMenu` | Canonical record-action composition. | shadcn `Button` and `DropdownMenu`. | closed, open, keyboard selection, focus return. | Required accessible trigger name; inherited menu mechanics. | `../src/components/action-menu.test.tsx` |
23
- | `SettingsSection` | nadicode product composition. | shadcn Card slots, Separator, and FieldGroup. | header, field group, and divided action footer. | Section structure; consumer owns the form transaction. | `../src/components/settings-section.test.tsx` |
24
- | `SettingsField` | nadicode product composition. | shadcn Field, FieldLabel, FieldDescription, and FieldError. | valid, invalid, required, and disabled field rows. | Render callback supplies the control's label and message associations in server and client components. | `../src/components/settings-section.test.tsx` |
25
- | `FeedbackState` | Net-new central product state composition adapted from shadcn empty-state patterns. | shadcn `Empty` and lucide status icons. | `empty`, `loading`, `saving`, `error`, `permission`, `offline`, `no-results`, `fatal`. | `role="status"` for loading/saving; `role="alert"` for error, permission, offline, and fatal. | `../tests/guards/product-compositions.test.ts` |
26
-
27
- ## App-Owned Boundaries
28
-
29
- Keep these out of `@nadicodeai/ui`:
30
-
31
- - data fetching, React Query, routers, server actions, desktop IPC, terminal runtime code, and persistence;
32
- - login/signup routes, OTP/two-factor flows, auth provider choices, recovery flows, session mutation, and analytics;
33
- - permission decisions, tenant/customer scoping, and policy enforcement;
34
- - app-specific product copy, page IA, and route-specific ordering;
35
- - fake proof, invented customer names, invented metrics, or placeholder testimonials.
36
-
37
- ## Fixture Guidance
38
-
39
- U4 fixtures should use nadicode-relevant work objects:
40
-
41
- - briefing agents, review agents, approval queues, sessions, models, workspace defaults, files, pairing codes, and weekly approvals;
42
- - controlled state examples for sorting, selection, visibility, validation, loading, saving, offline, permission, and fatal states;
43
- - no external network, browser, customer PII, signed contracts, financials, or fake proof.
44
-
45
- Browser-driven tests stay out of the default Vitest suite. U4 fixtures render through static and SSR-friendly tests only.
@@ -1,116 +0,0 @@
1
- # Consuming `@nadicodeai/ui` Cross-Repo
2
-
3
- This is the contract for an external repo — the Nadia repo, sibling Nadia web,
4
- desktop, or any consumer outside this monorepo — installing `@nadicodeai/ui`
5
- from published npm rather than the workspace source.
6
-
7
- ## Version pinning
8
-
9
- Pin the UI package to an exact version, never `latest` or a caret/tilde range:
10
-
11
- ```bash
12
- npm view @nadicodeai/ui version
13
- npm install @nadicodeai/ui@<exact-version> react@^19 react-dom@^19 tailwindcss@^4 @tailwindcss/postcss@^4
14
- ```
15
-
16
- `@nadicodeai/ui` pins and installs its `@nadicodeai/design-system` runtime
17
- dependency exactly. A React consumer does not declare that dependency directly
18
- just to use UI components or the UI stylesheet.
19
-
20
- The package installs its Agent UI implementation exactly. Consumers import
21
- presentation, runtime primitives, providers, hooks, and types through
22
- `@nadicodeai/ui/agents`; they do not install or import assistant-ui packages
23
- directly. A protocol-specific runtime adapter remains a direct consumer
24
- dependency because the application owns transport and message state.
25
-
26
- Declare `@nadicodeai/design-system` directly only when the app intentionally
27
- uses a separate public design-system API, such as a font/logo asset or the
28
- static CSS interface for a separate non-React surface. Pin that direct
29
- dependency to the version required by the installed
30
- UI release:
31
-
32
- ```bash
33
- npm view @nadicodeai/ui@<exact-version> dependencies.@nadicodeai/design-system
34
- ```
35
-
36
- ## CSS interface
37
-
38
- Import one stylesheet once at the React app root:
39
-
40
- ```ts
41
- import "@nadicodeai/ui/globals.css";
42
- ```
43
-
44
- `@nadicodeai/ui/globals.css` is the complete React style interface. It composes
45
- Tailwind v4, shadcn, and the complete design-system CSS module, and it declares
46
- the four `nc-elevation-*` utilities over the generated elevation tokens.
47
- Consumers do not add a second border or ring to package-owned elevated roots.
48
- Do not import `@nadicodeai/design-system/css`, generated theme files, or CSS
49
- partials beside it. The package declares its own built component sources; Tailwind's
50
- normal automatic detection covers the consumer application. The app still owns
51
- its PostCSS configuration, theme provider, and persisted mode preference.
52
-
53
- That stylesheet also delivers the brand typeface. The design-system CSS module
54
- declares the `@font-face` rules for Geist and Geist Mono and loads the packaged
55
- woff2 files, so a consuming app must not add its own font loader for those two
56
- families. A framework loader on top registers a second, differently sourced face
57
- for the same family names, which downloads the fonts twice and leaves no defined
58
- winner. Import files under `@nadicodeai/design-system/assets/fonts/*` only when
59
- the consumer wants the faces without the CSS module; that is one of the
60
- intentional direct-API cases described above.
61
-
62
- ## Strict ESLint interface
63
-
64
- The package exports the same design-system rules used by this repository. Add
65
- the flat config after the consumer's framework and TypeScript configs:
66
-
67
- ```js
68
- import nadicodeai from "@nadicodeai/ui/eslint";
69
-
70
- export default [
71
- // Framework, parser, and TypeScript configs first.
72
- ...nadicodeai.configs["flat/strict"],
73
- ];
74
- ```
75
-
76
- Every rule in this config is an error. The config rejects generated `nc-*`
77
- utilities that do not exist in the installed design-system release, arbitrary
78
- radius values that exactly duplicate a generated token, source deep imports,
79
- raw design values, native visible controls, and appearance overrides on shared
80
- UI primitives. An arbitrary radius remains valid when no generated radius has
81
- the same value. Consumers may compose domain-specific components from the
82
- public UI package; the rules govern package boundaries and design values, not
83
- the shape of a consumer's JSX tree.
84
-
85
- ## nadicode logo
86
-
87
- React surfaces name the company with the wordmark from the UI package:
88
-
89
- ```tsx
90
- import { BrandWordmark } from "@nadicodeai/ui/components/brand";
91
-
92
- <BrandWordmark alt="nadicode" />;
93
- ```
94
-
95
- Use `BrandMark`, the plain n, where the name does not fit. The n is cut from the
96
- wordmark, so never place the two together. Static consumers that need files
97
- instead of React declare the design-system dependency directly and use the
98
- documented SVG or PNG package routes in `packages/design-system/README.md`.
99
-
100
- ## Base UI API breaks (migrating from Radix-era components)
101
-
102
- `@nadicodeai/ui@0.5.0+` is Base UI only; the Radix-era compatibility layer is
103
- gone. Full catalog and rationale: `docs/migration/base-ui-migration.md`. What
104
- a consumer actually hits when porting call sites:
105
-
106
- - No `asChild`. Use Base UI's `render` prop; when the rendered element isn't
107
- a native `<button>`, also pass `nativeButton={false}`.
108
- - Menu items (`DropdownMenuItem`, `ContextMenuItem`, `MenubarItem`) use
109
- `onClick`, not `onSelect`.
110
- - State attributes are `data-checked`/`data-unchecked` and
111
- `data-open`/`data-closed`, not a single `data-state` value.
112
- - `Accordion` and `ToggleGroup` values are always arrays — pass `multiple`
113
- for multi-select instead of `type="single" | "multiple"`.
114
- - `TooltipProvider` takes `delay`, not `delayDuration`.
115
- - Disclosure panels (`CollapsibleContent`, etc.) take `keepMounted`, not
116
- `forceMount`.