@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.
- package/AGENTS.md +2 -3
- package/dist/components/activity-orb.d.ts +1 -1
- package/dist/components/activity-orb.d.ts.map +1 -1
- package/dist/components/dropdown-menu.d.ts +4 -1
- package/dist/components/dropdown-menu.d.ts.map +1 -1
- package/dist/components/dropdown-menu.js +6 -3
- package/dist/eslint/index.js +2 -0
- package/dist/eslint/rules/menu-command-owner.js +45 -0
- package/docs/activity.md +4 -5
- package/docs/contract.md +33 -8
- package/package.json +2 -7
- package/dist/components/threshold/canvas-budget.d.ts +0 -10
- package/dist/components/threshold/canvas-budget.d.ts.map +0 -1
- package/dist/components/threshold/canvas-budget.js +0 -54
- package/dist/components/threshold/index.d.ts +0 -6
- package/dist/components/threshold/index.d.ts.map +0 -1
- package/dist/components/threshold/index.js +0 -5
- package/dist/components/threshold/orb.d.ts +0 -14
- package/dist/components/threshold/orb.d.ts.map +0 -1
- package/dist/components/threshold/orb.js +0 -17
- package/dist/components/threshold/presence.d.ts +0 -27
- package/dist/components/threshold/presence.d.ts.map +0 -1
- package/dist/components/threshold/presence.js +0 -159
- package/dist/components/threshold/room.d.ts +0 -33
- package/dist/components/threshold/room.d.ts.map +0 -1
- package/dist/components/threshold/room.js +0 -168
- package/dist/components/threshold/threshold.css +0 -228
- package/dist/components/threshold/typed-line.d.ts +0 -8
- package/dist/components/threshold/typed-line.d.ts.map +0 -1
- package/dist/components/threshold/typed-line.js +0 -43
- package/dist/components/threshold/use-reduced-motion.d.ts +0 -10
- package/dist/components/threshold/use-reduced-motion.d.ts.map +0 -1
- package/dist/components/threshold/use-reduced-motion.js +0 -32
- package/dist/threshold.d.ts +0 -2
- package/dist/threshold.d.ts.map +0 -1
- package/dist/threshold.js +0 -13
- 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
|
-
-
|
|
13
|
-
|
|
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
|
|
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,
|
|
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":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,IAAI,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAM5D,iBAAS,YAAY,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,qBAE3D;AAED,iBAAS,kBAAkB,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,aAAa,CAAC,MAAM,CAAC,KAAK,qBAEnE;AAED,iBAAS,mBAAmB,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,aAAa,CAAC,OAAO,CAAC,KAAK,qBAErE;AAED,iBAAS,mBAAmB,CAAC,EAC3B,KAAe,EACf,WAAe,EACf,
|
|
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 = "
|
|
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-(--
|
|
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, };
|
package/dist/eslint/index.js
CHANGED
|
@@ -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
|
|
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.
|
|
107
|
-
|
|
108
|
-
|
|
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/
|
|
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
|
|
66
|
-
|
|
67
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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": "
|
|
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": "
|
|
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,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
|
-
}
|