@nadicodeai/ui 6.6.1 → 7.0.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 (37) hide show
  1. package/AGENTS.md +2 -3
  2. package/dist/components/activity-orb.d.ts +1 -1
  3. package/dist/components/activity-orb.d.ts.map +1 -1
  4. package/dist/components/dropdown-menu.d.ts +4 -1
  5. package/dist/components/dropdown-menu.d.ts.map +1 -1
  6. package/dist/components/dropdown-menu.js +6 -3
  7. package/dist/eslint/index.js +2 -0
  8. package/dist/eslint/rules/menu-command-owner.js +45 -0
  9. package/docs/activity.md +4 -5
  10. package/docs/contract.md +33 -8
  11. package/package.json +2 -7
  12. package/dist/components/threshold/canvas-budget.d.ts +0 -10
  13. package/dist/components/threshold/canvas-budget.d.ts.map +0 -1
  14. package/dist/components/threshold/canvas-budget.js +0 -54
  15. package/dist/components/threshold/index.d.ts +0 -6
  16. package/dist/components/threshold/index.d.ts.map +0 -1
  17. package/dist/components/threshold/index.js +0 -5
  18. package/dist/components/threshold/orb.d.ts +0 -14
  19. package/dist/components/threshold/orb.d.ts.map +0 -1
  20. package/dist/components/threshold/orb.js +0 -17
  21. package/dist/components/threshold/presence.d.ts +0 -27
  22. package/dist/components/threshold/presence.d.ts.map +0 -1
  23. package/dist/components/threshold/presence.js +0 -159
  24. package/dist/components/threshold/room.d.ts +0 -33
  25. package/dist/components/threshold/room.d.ts.map +0 -1
  26. package/dist/components/threshold/room.js +0 -168
  27. package/dist/components/threshold/threshold.css +0 -228
  28. package/dist/components/threshold/typed-line.d.ts +0 -8
  29. package/dist/components/threshold/typed-line.d.ts.map +0 -1
  30. package/dist/components/threshold/typed-line.js +0 -43
  31. package/dist/components/threshold/use-reduced-motion.d.ts +0 -10
  32. package/dist/components/threshold/use-reduced-motion.d.ts.map +0 -1
  33. package/dist/components/threshold/use-reduced-motion.js +0 -32
  34. package/dist/threshold.d.ts +0 -2
  35. package/dist/threshold.d.ts.map +0 -1
  36. package/dist/threshold.js +0 -13
  37. package/docs/threshold.md +0 -221
package/AGENTS.md CHANGED
@@ -9,9 +9,8 @@ exports.
9
9
  - Tokens, CSS, motion, and assets: `../design-system/DESIGN.md`.
10
10
  - Agent visuals: `docs/agent-visual.md`; the Forma avatars: `docs/forma-avatar.md`.
11
11
  - System activity indicators: `docs/activity.md`.
12
- - First run, sign-in, and repair surfaces: `docs/threshold.md`; the
13
- primitives are `@nadicodeai/ui/threshold`, with
14
- `@nadicodeai/ui/threshold.css` beside them.
12
+ - What belongs in this package and what belongs in a product: `docs/contract.md`,
13
+ "Placement". Nadia's entry screen is the worked example that moved out.
15
14
  - Package releases: `../../docs/agents/publish.md`.
16
15
 
17
16
  `npm run dev -w @nadicodeai/ui` watches source and refreshes `dist/`;
@@ -7,7 +7,7 @@ export type ActivityOrbProps = {
7
7
  style?: CSSProperties;
8
8
  /** Freeze the animation for a preview or a user-controlled motion pause. */
9
9
  paused?: boolean;
10
- /** 20 inline, 64 within a region, 88 at an application threshold. */
10
+ /** 20 inline, 64 within a region, 88 on an application's entry screen. */
11
11
  size?: ActivityOrbSize;
12
12
  } & ({
13
13
  decorative: true;
@@ -1 +1 @@
1
- {"version":3,"file":"activity-orb.d.ts","sourceRoot":"","sources":["../../src/components/activity-orb.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,aAAa,EAAqB,MAAM,OAAO,CAAC;AAE9D,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,eAAe,EAAoB,MAAM,yBAAyB,CAAC;AAGpG,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE7E,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,YAAY,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,qEAAqE;IACrE,IAAI,CAAC,EAAE,eAAe,CAAC;CACxB,GAAG,CAAC;IAAE,UAAU,EAAE,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,GAAG;IAAE,UAAU,CAAC,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAElF,iFAAiF;AACjF,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,SAAS,EACT,UAAkB,EAClB,KAAK,EACL,MAAc,EACd,IAAS,EACT,KAAK,EACL,GAAG,KAAK,EACT,EAAE,gBAAgB,+BAmClB"}
1
+ {"version":3,"file":"activity-orb.d.ts","sourceRoot":"","sources":["../../src/components/activity-orb.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,aAAa,EAAqB,MAAM,OAAO,CAAC;AAE9D,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,eAAe,EAAoB,MAAM,yBAAyB,CAAC;AAGpG,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE7E,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,YAAY,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,0EAA0E;IAC1E,IAAI,CAAC,EAAE,eAAe,CAAC;CACxB,GAAG,CAAC;IAAE,UAAU,EAAE,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,GAAG;IAAE,UAAU,CAAC,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAElF,iFAAiF;AACjF,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,SAAS,EACT,UAAkB,EAClB,KAAK,EACL,MAAc,EACd,IAAS,EACT,KAAK,EACL,GAAG,KAAK,EACT,EAAE,gBAAgB,+BAmClB"}
@@ -15,6 +15,9 @@ declare function DropdownMenuItem({ className, iconOnly, inset, variant, ...prop
15
15
  inset?: boolean;
16
16
  variant?: "default" | "destructive";
17
17
  }): React.JSX.Element;
18
+ declare function DropdownMenuCommand({ icon, children, className, ...props }: Omit<React.ComponentProps<typeof DropdownMenuItem>, "iconOnly"> & {
19
+ icon: React.ReactElement;
20
+ }): React.JSX.Element;
18
21
  declare function DropdownMenuSub({ ...props }: MenuPrimitive.SubmenuRoot.Props): React.JSX.Element;
19
22
  declare function DropdownMenuSubTrigger({ className, inset, children, ...props }: MenuPrimitive.SubmenuTrigger.Props & {
20
23
  inset?: boolean;
@@ -30,5 +33,5 @@ declare function DropdownMenuRadioItem({ className, iconOnly, children, inset, .
30
33
  }): React.JSX.Element;
31
34
  declare function DropdownMenuSeparator({ className, ...props }: MenuPrimitive.Separator.Props): React.JSX.Element;
32
35
  declare function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<"span">): React.JSX.Element;
33
- export { DropdownMenu, DropdownMenuPortal, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, };
36
+ export { DropdownMenu, DropdownMenuPortal, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuItem, DropdownMenuCommand, DropdownMenuCheckboxItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, };
34
37
  //# sourceMappingURL=dropdown-menu.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dropdown-menu.d.ts","sourceRoot":"","sources":["../../src/components/dropdown-menu.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dropdown-menu.d.ts","sourceRoot":"","sources":["../../src/components/dropdown-menu.tsx"],"names":[],"mappings":"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"}
@@ -12,8 +12,8 @@ function DropdownMenuPortal({ ...props }) {
12
12
  function DropdownMenuTrigger({ ...props }) {
13
13
  return _jsx(MenuPrimitive.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
14
14
  }
15
- function DropdownMenuContent({ align = "start", alignOffset = 0, density = "compact", side = "bottom", sideOffset = 4, className, ...props }) {
16
- return (_jsx(MenuPrimitive.Portal, { children: _jsx(MenuPrimitive.Positioner, { className: "isolate z-50 outline-none", align: align, alignOffset: alignOffset, side: side, sideOffset: sideOffset, children: _jsx(MenuPrimitive.Popup, { "data-slot": "dropdown-menu-content", "data-density": density, className: cn("group/menu z-50 max-h-(--available-height) w-(--anchor-width) min-w-32 overflow-x-hidden overflow-y-auto rounded-2xl border-0 nc-glass p-nadicode-xxs text-popover-foreground nc-elevation-floating outline-none data-closed:overflow-hidden", anchoredOverlayMotion, className), ...props }) }) }));
15
+ function DropdownMenuContent({ align = "start", alignOffset = 0, density = "comfortable", side = "bottom", sideOffset = 6, className, ...props }) {
16
+ return (_jsx(MenuPrimitive.Portal, { children: _jsx(MenuPrimitive.Positioner, { className: "isolate z-50 outline-none", align: align, alignOffset: alignOffset, side: side, sideOffset: sideOffset, collisionPadding: 8, children: _jsx(MenuPrimitive.Popup, { "data-slot": "dropdown-menu-content", "data-density": density, className: cn("group/menu z-50 max-h-(--available-height) w-max min-w-[min(14rem,var(--available-width))] max-w-(--available-width) overflow-x-hidden overflow-y-auto rounded-2xl border-0 bg-popover p-nadicode-xxs text-popover-foreground nc-elevation-floating outline-none data-closed:overflow-hidden", anchoredOverlayMotion, className), ...props }) }) }));
17
17
  }
18
18
  function DropdownMenuGroup({ ...props }) {
19
19
  return _jsx(MenuPrimitive.Group, { "data-slot": "dropdown-menu-group", ...props });
@@ -25,6 +25,9 @@ function DropdownMenuItem({ className, iconOnly = false, inset, variant = "defau
25
25
  return (_jsx(MenuPrimitive.Item, { "data-slot": "dropdown-menu-item", "data-icon-only": iconOnly || undefined, "data-inset": inset, "data-variant": variant, className: cn("group/dropdown-menu-item relative flex min-h-touch-target cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 group-data-[density=comfortable]/menu:px-3 group-data-[density=comfortable]/menu:py-2 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-[var(--nc-error-deep)] data-[variant=destructive]:focus:bg-[var(--nc-error-soft)] data-[variant=destructive]:focus:text-[var(--nc-error-deep)] data-disabled:pointer-events-none data-disabled:opacity-50 [@media(hover:hover)_and_(pointer:fine)]:min-h-0 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-[var(--nc-error-deep)]", iconOnly &&
26
26
  "size-8 min-w-touch-target shrink-0 justify-center rounded-full p-0 group-data-[density=comfortable]/menu:p-0 [@media(hover:hover)_and_(pointer:fine)]:min-w-0", className), ...props }));
27
27
  }
28
+ function DropdownMenuCommand({ icon, children, className, ...props }) {
29
+ return (_jsxs(DropdownMenuItem, { ...props, className: cn(className, "h-auto w-full min-w-0 justify-start gap-3 text-left font-normal whitespace-normal"), children: [_jsx("span", { className: "min-w-0 flex-1 [overflow-wrap:anywhere]", children: children }), _jsx("span", { "aria-hidden": "true", className: "flex shrink-0 items-center justify-center", children: icon })] }));
30
+ }
28
31
  function DropdownMenuSub({ ...props }) {
29
32
  return _jsx(MenuPrimitive.SubmenuRoot, { "data-slot": "dropdown-menu-sub", ...props });
30
33
  }
@@ -50,4 +53,4 @@ function DropdownMenuSeparator({ className, ...props }) {
50
53
  function DropdownMenuShortcut({ className, ...props }) {
51
54
  return (_jsx("span", { "data-slot": "dropdown-menu-shortcut", className: cn("ml-auto text-xs text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground", className), ...props }));
52
55
  }
53
- export { DropdownMenu, DropdownMenuPortal, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, };
56
+ export { DropdownMenu, DropdownMenuPortal, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuItem, DropdownMenuCommand, DropdownMenuCheckboxItem, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, };
@@ -1,5 +1,6 @@
1
1
  import { readFileSync } from "node:fs";
2
2
 
3
+ import { menuCommandOwner } from "./rules/menu-command-owner.js";
3
4
  import { cardRowOwner } from "./rules/card-row-owner.js";
4
5
  import { confirmDestructiveAction } from "./rules/confirm-destructive-action.js";
5
6
  import { iconControlAccessibleName } from "./rules/icon-control-accessible-name.js";
@@ -78,6 +79,7 @@ const plugin = {
78
79
  configs: {},
79
80
  rules: {
80
81
  "card-row-owner": cardRowOwner,
82
+ "menu-command-owner": menuCommandOwner,
81
83
  "confirm-destructive-action": confirmDestructiveAction,
82
84
  "icon-control-accessible-name": iconControlAccessibleName,
83
85
  "no-app-feature-component": noAppFeatureComponent,
@@ -0,0 +1,45 @@
1
+ import { jsxComponentName } from "./jsx-class-utility.js";
2
+
3
+ export const menuCommandOwner = {
4
+ meta: {
5
+ type: "problem",
6
+ docs: { description: "Keep command labels and icons in the shared menu composition." },
7
+ schema: [],
8
+ messages: {
9
+ command:
10
+ "Use DropdownMenuCommand with its required icon; it owns readable labels and trailing icon layout. DropdownMenuItem is only for an iconOnly control here.",
11
+ trigger:
12
+ "Use ActionMenu for three-dot commands so placement, naming, and popup behavior have one owner.",
13
+ },
14
+ },
15
+ create(context) {
16
+ return {
17
+ JSXOpeningElement(node) {
18
+ const name = jsxComponentName(node, context);
19
+ if (
20
+ name === "DropdownMenuItem" &&
21
+ !node.attributes.some(
22
+ (attribute) =>
23
+ attribute.type === "JSXAttribute" &&
24
+ attribute.name.name === "iconOnly" &&
25
+ (attribute.value === null ||
26
+ (attribute.value.type === "JSXExpressionContainer" &&
27
+ attribute.value.expression.value === true)),
28
+ )
29
+ )
30
+ context.report({ node, messageId: "command" });
31
+ if (
32
+ [
33
+ "Ellipsis",
34
+ "EllipsisIcon",
35
+ "EllipsisVertical",
36
+ "MoreHorizontal",
37
+ "MoreVertical",
38
+ ].includes(name)
39
+ ) {
40
+ context.report({ node, messageId: "trigger" });
41
+ }
42
+ },
43
+ };
44
+ },
45
+ };
package/docs/activity.md CHANGED
@@ -61,7 +61,7 @@ movement as well as its travelling particles. The implementation owns these
61
61
  values together; consumers cannot tune speed, density or particle colours.
62
62
 
63
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
64
+ 64 for a contained region; 88 on an application's entry screen. The 20 preset is
65
65
  separately tuned, and must not be obtained by shrinking the 64 preset. The 88
66
66
  extent uses the 64 preset. A 12–16 pixel badge slot keeps its existing compact
67
67
  spinner until its surrounding component explicitly accommodates this family.
@@ -103,10 +103,9 @@ the operation fails or React replaces the startup surface.
103
103
 
104
104
  Import `@nadicodeai/ui/globals.css` as described in the
105
105
  [consumer guide](consuming-cross-repo.md). The renderer inherits actual CSS
106
- ink and reads canonical colour tokens. The existing `Orb` threshold adapter
107
- keeps its public API: `working` maps to `processing`, `connecting` maps to
108
- `connecting`, and `absent` mounts nothing. Its fixed dark appearance stays at
109
- the threshold seam, outside the shared renderer.
106
+ ink and reads canonical colour tokens. Nadia's entry screen wraps this renderer
107
+ in its own `Orb`, which fixes a dark appearance the shared renderer knows
108
+ nothing about; that wrapper lives in the desktop, not here.
110
109
 
111
110
  ## Accessibility and runtime
112
111
 
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
@@ -62,9 +62,10 @@ restates those three as separate utilities: an `outline-none` or
62
62
  `outline-hidden` on the same element leaves Tailwind's own outline utilities
63
63
  painting nothing.
64
64
 
65
- `nc-glass` is the one glass material, and glass is floating chrome: menu,
66
- popover, select, combobox, and navigation-menu popups, the command palette,
67
- and `GlassControl` bars carry it over our own content. Content surfaces stay
65
+ `nc-glass` is the one glass material for floating chrome: popover, select,
66
+ combobox, and navigation-menu popups, the command palette, and `GlassControl`
67
+ bars carry it over our own content. Command dropdowns use a solid popover fill
68
+ so labels stay readable over dense rows; account dropdowns share that surface. Content surfaces stay
68
69
  solid: cards, KPIs, tables, inspectors, and dialog, sheet, and drawer bodies.
69
70
  Glass keeps exactly one separator, the elevation falloff's integrated edge
70
71
  ring. Reduced transparency falls back to the same solid floating surface.
@@ -107,7 +108,7 @@ Its caption names the table. Callers supply data and labels, not column-hiding C
107
108
  `ActionMenu` is the shared three-dot composition of `Button` and `DropdownMenu`:
108
109
  a ghost icon trigger, horizontal ellipsis, and end-aligned popup. The shared
109
110
  stylesheet gives enabled buttons a pointer cursor, including this trigger. It inherits the
110
- menu's glass, elevation, motion, keyboard behavior, and focus return. Supply
111
+ menu's opaque surface, elevation, motion, keyboard behavior, and focus return. Supply
111
112
  `label` as a localized, record-specific accessible name; `triggerId` supports
112
113
  focus return from an app-owned confirmation, and `children` supplies menu items.
113
114
  Apps own action selection, permission, placement, and confirmation. Reuse this
@@ -138,8 +139,7 @@ gone, is [`packages/design-system/DESIGN.md`, "## Status Vocabulary"](../../desi
138
139
  System activity uses `ActivityOrb` for loading, connecting and processing;
139
140
  [activity.md](activity.md) owns selection, rendering and accessibility. It
140
141
  composes with status feedback and measured progress without adding outcome
141
- states to the orb. The existing threshold `Orb` adapts its public states to
142
- this same renderer.
142
+ states to the orb.
143
143
 
144
144
  - **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.
145
145
  - **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.
@@ -151,7 +151,19 @@ this same renderer.
151
151
 
152
152
  ## Admission Rules
153
153
 
154
- 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.
154
+ 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.
155
+
156
+ ### Placement
157
+
158
+ `@nadicodeai/ui` and `@nadicodeai/design-system` hold the Foundation and the
159
+ components, assets and styles at least two products use; a component, asset or
160
+ style one product alone needs lives in that product's own directory. Nadia's
161
+ entry screen is the worked example: the room, the presence, the typed line and
162
+ their stylesheet were a package surface no other product imported, and they now
163
+ live in the desktop at `apps/nadia/hermes/apps/desktop/src/plugins/nadia/entry/`,
164
+ where [`apps/nadia/docs/entry-screen.md`](../../../apps/nadia/docs/entry-screen.md)
165
+ owns their contract. `packages/ui/tests/guards/product-placement.test.ts`
166
+ refuses a product's name in either package.
155
167
 
156
168
  ### Agent identity presentation
157
169
 
@@ -272,6 +284,19 @@ Portal placement is owned by its [context contract](../../../apps/portal/docs/in
272
284
  `DataTable.heading`, `CardHeader` and `SettingsSection` are its composition
273
285
  seams. Empty states and operational notices remain distinct message owners.
274
286
 
287
+ ### Command popup composition
288
+
289
+ `DropdownMenuContent` uses comfortable spacing by default. Its content-based
290
+ width is bounded by Base UI's available width; collision padding keeps it off
291
+ screen edges and available height allows vertical scrolling. A command popup
292
+ uses solid `bg-popover` so underlying table text cannot compete with labels.
293
+ Do not size command menus from an icon trigger. `DropdownMenuCommand` requires
294
+ an `icon` element and owns the wrapping label and trailing, decorative icon slot.
295
+ Use it for links, callbacks, native form buttons, and dialog triggers. It retains
296
+ Menu.Item behavior; it is not a new positioning or focus implementation.
297
+ `DropdownMenuItem iconOnly` remains available for the account-settings gear;
298
+ checkbox and radio items retain their selection semantics.
299
+
275
300
  ### Account popup composition
276
301
 
277
302
  `DropdownMenuContent density="comfortable"` supplies roomier command rows
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nadicodeai/ui",
3
- "version": "6.6.1",
3
+ "version": "7.0.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.1",
68
+ "@nadicodeai/design-system": "7.0.0",
74
69
  "@paper-design/shaders": "0.0.80",
75
70
  "class-variance-authority": "^0.7.1",
76
71
  "clsx": "^2.1.1",
@@ -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
- }