@nadicodeai/ui 1.0.0 → 1.0.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"agent-visual.d.ts","sourceRoot":"","sources":["../../src/components/agent-visual.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAKzE,KAAK,uBAAuB,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE9D,KAAK,wBAAwB,GACzB;IAAE,UAAU,CAAC,EAAE,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,GACpC;IAAE,UAAU,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzC,KAAK,gBAAgB,GAAG,wBAAwB,GAAG;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,uBAAuB,CAAC;IACvC,MAAM,EAAE,iBAAiB,CAAC;CAC3B,CAAC;AAkCF,iBAAS,WAAW,CAAC,EACnB,SAAS,EACT,UAAiB,EACjB,KAAK,EACL,YAAqB,EACrB,MAAM,GACP,EAAE,gBAAgB,+BAmElB;AAED,OAAO,EAAE,WAAW,EAAE,KAAK,uBAAuB,EAAE,KAAK,gBAAgB,EAAE,KAAK,iBAAiB,EAAE,CAAC"}
1
+ {"version":3,"file":"agent-visual.d.ts","sourceRoot":"","sources":["../../src/components/agent-visual.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAKzE,KAAK,uBAAuB,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE9D,KAAK,wBAAwB,GACzB;IAAE,UAAU,CAAC,EAAE,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,GACpC;IAAE,UAAU,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzC,KAAK,gBAAgB,GAAG,wBAAwB,GAAG;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,uBAAuB,CAAC;IACvC,MAAM,EAAE,iBAAiB,CAAC;CAC3B,CAAC;AAuBF,iBAAS,WAAW,CAAC,EACnB,SAAS,EACT,UAAiB,EACjB,KAAK,EACL,YAAqB,EACrB,MAAM,GACP,EAAE,gBAAgB,+BAoFlB;AAED,OAAO,EAAE,WAAW,EAAE,KAAK,uBAAuB,EAAE,KAAK,gBAAgB,EAAE,KAAK,iBAAiB,EAAE,CAAC"}
@@ -16,21 +16,10 @@ class AgentVisualErrorBoundary extends Component {
16
16
  return this.state.failed ? null : this.props.children;
17
17
  }
18
18
  }
19
- function supportsAmbientAgentVisual() {
20
- try {
21
- const context = document.createElement("canvas").getContext("webgl2");
22
- if (!context)
23
- return false;
24
- context.getExtension("WEBGL_lose_context")?.loseContext();
25
- return true;
26
- }
27
- catch {
28
- return false;
29
- }
30
- }
31
19
  function AgentVisual({ className, decorative = true, label, presentation = "card", recipe, }) {
32
20
  const hostRef = useRef(null);
33
21
  const [canAnimate, setCanAnimate] = useState(false);
22
+ const [isInViewport, setIsInViewport] = useState(false);
34
23
  const [rendererReady, setRendererReady] = useState(false);
35
24
  const handleReady = useCallback(() => setRendererReady(true), []);
36
25
  const handleUnavailable = useCallback(() => {
@@ -40,20 +29,35 @@ function AgentVisual({ className, decorative = true, label, presentation = "card
40
29
  useEffect(() => {
41
30
  setCanAnimate(false);
42
31
  setRendererReady(false);
43
- if (presentation !== "detail")
44
- return;
45
32
  if (typeof window.matchMedia !== "function")
46
33
  return;
47
34
  const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
48
35
  const update = () => {
49
36
  setRendererReady(false);
50
- setCanAnimate(!reducedMotion.matches && supportsAmbientAgentVisual());
37
+ setCanAnimate(!reducedMotion.matches);
51
38
  };
52
39
  update();
53
40
  reducedMotion.addEventListener("change", update);
54
41
  return () => reducedMotion.removeEventListener("change", update);
55
- }, [presentation, recipe]);
56
- const hasAmbientRenderer = presentation === "detail" && canAnimate;
42
+ }, [recipe]);
43
+ useEffect(() => {
44
+ const host = hostRef.current;
45
+ if (!host)
46
+ return;
47
+ if (typeof IntersectionObserver !== "function") {
48
+ setIsInViewport(true);
49
+ return;
50
+ }
51
+ const observer = new IntersectionObserver(([entry]) => {
52
+ const isIntersecting = entry?.isIntersecting ?? false;
53
+ setIsInViewport(isIntersecting);
54
+ if (!isIntersecting)
55
+ setRendererReady(false);
56
+ });
57
+ observer.observe(host);
58
+ return () => observer.disconnect();
59
+ }, []);
60
+ const hasAmbientRenderer = canAnimate && isInViewport;
57
61
  return (_jsxs("div", { "aria-hidden": decorative || undefined, "aria-label": decorative ? undefined : label, className: cn("relative isolate size-full overflow-hidden rounded-xl bg-nc-campo-inchiostro", presentation === "card" && "min-h-20", presentation === "detail" && "min-h-48", className), "data-agent-visual-motion": hasAmbientRenderer ? "ambient" : "static", "data-agent-visual-presentation": presentation, "data-agent-visual-recipe": recipe, ref: hostRef, role: decorative ? undefined : "img", children: [_jsx(AgentVisualPoster, { recipe: recipe }), hasAmbientRenderer && hostRef.current ? (_jsx("div", { "aria-hidden": "true", className: cn("absolute inset-0 opacity-0 transition-opacity duration-[var(--nc-duration-close)] ease-[var(--nc-ease-out-strong)]", rendererReady && "opacity-100"), "data-agent-visual-renderer": "paper-metaballs", children: _jsx(AgentVisualErrorBoundary, { onUnavailable: handleUnavailable, children: _jsx(Suspense, { fallback: null, children: _jsx(LazyAgentVisualRenderer, { host: hostRef.current, onReady: handleReady, onUnavailable: handleUnavailable, recipe: recipe }) }) }) })) : null] }));
58
62
  }
59
63
  export { AgentVisual };
@@ -1 +1 @@
1
- {"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../src/components/table.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,iBAAS,KAAK,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,qBAUpE;AAED,iBAAS,WAAW,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,qBAE1E;AAED,iBAAS,SAAS,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,qBAQxE;AAED,iBAAS,WAAW,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,qBAW1E;AAED,iBAAS,QAAQ,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,qBAWpE;AAED,iBAAS,SAAS,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,qBAWrE;AAED,iBAAS,SAAS,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,qBAQrE;AAED,iBAAS,YAAY,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,SAAS,CAAC,qBAQ7E;AAED,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC"}
1
+ {"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../src/components/table.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,iBAAS,KAAK,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,qBAUpE;AAED,iBAAS,WAAW,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,qBAQ1E;AAED,iBAAS,SAAS,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,qBAQxE;AAED,iBAAS,WAAW,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,qBAW1E;AAED,iBAAS,QAAQ,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,qBAWpE;AAED,iBAAS,SAAS,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,qBAWrE;AAED,iBAAS,SAAS,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,qBAQrE;AAED,iBAAS,YAAY,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,SAAS,CAAC,qBAQ7E;AAED,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC"}
@@ -5,10 +5,10 @@ function Table({ className, ...props }) {
5
5
  return (_jsx("div", { "data-slot": "table-container", className: "relative w-full overflow-x-auto bg-background", children: _jsx("table", { "data-slot": "table", className: cn("w-full caption-bottom bg-background text-sm", className), ...props }) }));
6
6
  }
7
7
  function TableHeader({ className, ...props }) {
8
- return _jsx("thead", { "data-slot": "table-header", className: cn("[&_tr]:border-b", className), ...props });
8
+ return (_jsx("thead", { "data-slot": "table-header", className: cn("[&_tr]:h-9 [&_tr]:border-b", className), ...props }));
9
9
  }
10
10
  function TableBody({ className, ...props }) {
11
- return (_jsx("tbody", { "data-slot": "table-body", className: cn("[&_tr:last-child]:border-0", className), ...props }));
11
+ return (_jsx("tbody", { "data-slot": "table-body", className: cn("[&_tr]:h-10 [&_tr:last-child]:border-0", className), ...props }));
12
12
  }
13
13
  function TableFooter({ className, ...props }) {
14
14
  return (_jsx("tfoot", { "data-slot": "table-footer", className: cn("border-t border-border bg-muted/50 font-medium [&>tr]:last:border-b-0", className), ...props }));
@@ -17,7 +17,7 @@ function TableRow({ className, ...props }) {
17
17
  return (_jsx("tr", { "data-slot": "table-row", className: cn("border-b border-border transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted", className), ...props }));
18
18
  }
19
19
  function TableHead({ className, ...props }) {
20
- return (_jsx("th", { "data-slot": "table-head", className: cn("h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0", className), ...props }));
20
+ return (_jsx("th", { "data-slot": "table-head", className: cn("h-9 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0", className), ...props }));
21
21
  }
22
22
  function TableCell({ className, ...props }) {
23
23
  return (_jsx("td", { "data-slot": "table-cell", className: cn("p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0", className), ...props }));
@@ -1 +1 @@
1
- {"version":3,"file":"color.d.ts","sourceRoot":"","sources":["../../../src/internal/agent-visual/color.ts"],"names":[],"mappings":"AAAA,KAAK,WAAW,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAyCpD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAExF"}
1
+ {"version":3,"file":"color.d.ts","sourceRoot":"","sources":["../../../src/internal/agent-visual/color.ts"],"names":[],"mappings":"AAAA,KAAK,WAAW,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AA4EpD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAGxF"}
@@ -34,6 +34,39 @@ function parseOklch(value) {
34
34
  clamp(alpha),
35
35
  ];
36
36
  }
37
+ function parseLab(value) {
38
+ if (!value.startsWith("lab"))
39
+ return null;
40
+ const start = value.indexOf("(");
41
+ const end = value.lastIndexOf(")");
42
+ if (start < 0 || end <= start)
43
+ return null;
44
+ const [channels, alphaValue] = value.slice(start + 1, end).split("/");
45
+ const [lightnessValue, aValue, bValue] = channels?.trim().split(/\s+/) ?? [];
46
+ const lightness = Number.parseFloat(lightnessValue ?? "");
47
+ const a = Number.parseFloat(aValue ?? "");
48
+ const b = Number.parseFloat(bValue ?? "");
49
+ const alpha = alphaValue ? Number.parseFloat(alphaValue.trim()) : 1;
50
+ if (![lightness, a, b, alpha].every(Number.isFinite))
51
+ return null;
52
+ const delta = 6 / 29;
53
+ const inverseLab = (channel) => channel > delta ? channel ** 3 : 3 * delta ** 2 * (channel - 4 / 29);
54
+ const f1 = (lightness + 16) / 116;
55
+ const x50 = 0.96422 * inverseLab(f1 + a / 500);
56
+ const y50 = inverseLab(f1);
57
+ const z50 = 0.82521 * inverseLab(f1 - b / 200);
58
+ // CSS Lab uses D50; WebGL colors use sRGB's D65 white point.
59
+ const x65 = 0.9555766 * x50 - 0.0230393 * y50 + 0.0631636 * z50;
60
+ const y65 = -0.0282895 * x50 + 1.0099416 * y50 + 0.0210077 * z50;
61
+ const z65 = 0.0122982 * x50 - 0.020483 * y50 + 1.3299098 * z50;
62
+ return [
63
+ linearToSrgb(3.2404542 * x65 - 1.5371385 * y65 - 0.4985314 * z65),
64
+ linearToSrgb(-0.969266 * x65 + 1.8760108 * y65 + 0.041556 * z65),
65
+ linearToSrgb(0.0556434 * x65 - 0.2040259 * y65 + 1.0572252 * z65),
66
+ clamp(alpha),
67
+ ];
68
+ }
37
69
  export function resolveCampoColor(element, variable) {
38
- return parseOklch(getComputedStyle(element).getPropertyValue(variable).trim());
70
+ const value = getComputedStyle(element).getPropertyValue(variable).trim();
71
+ return parseOklch(value) ?? parseLab(value);
39
72
  }
package/docs/contract.md CHANGED
@@ -44,6 +44,14 @@ falls back to the same solid floating surface.
44
44
  Page and region states own one opaque dashed boundary. A Card-scoped Empty is
45
45
  transparent and unframed because the Card already owns containment.
46
46
 
47
+ ### Tables
48
+
49
+ `Table` owns the native table structure, solid semantic background, horizontal
50
+ overflow, and one compact density: 36px header rows and 40px body rows. Consumers
51
+ compose `TableHeader`, `TableBody`, `TableRow`, `TableHead`, and `TableCell`; they
52
+ do not replace table layout with grid or flex, hide the header to create another
53
+ presentation, or reconstruct the native semantics with ARIA.
54
+
47
55
  ## Status Vocabulary
48
56
 
49
57
  `Badge` is the product's only status carrier. It ships five semantic tone
@@ -83,12 +91,14 @@ Assistant conversation and runtime APIs enter through `@nadicodeai/ui/agents`; A
83
91
  freedom are deliberately closed: one of `general`, `reasoning`,
84
92
  `coordination`, `communication`, or `execution`, plus the structural
85
93
  presentation `registry`, `card`, or `detail`. Consumers cannot supply colours,
86
- shader code, speed, count, density, frame, or material. Registry and Card
87
- presentations are deterministic posters. Detail may mount the Paper Metaballs
88
- renderer only after WebGL2 and reduced-motion checks; SSR, reduced motion,
89
- missing WebGL, initialization failure, and context loss retain the same poster.
90
- The visual is decorative by default. A meaningful selectable image opts into
91
- `decorative={false}` and supplies `label`.
94
+ shader code, speed, count, density, frame, or material. Every presentation
95
+ mounts the same ambient Paper Metaballs renderer after the reduced-motion
96
+ check. The shared component mounts it only while the visual intersects the
97
+ viewport, releasing its WebGL context offscreen; the vendor pauses it when the
98
+ document is hidden. SSR, first paint, reduced motion,
99
+ missing WebGL, initialization failure, and context loss retain the same
100
+ deterministic poster. The visual is decorative by default. A meaningful
101
+ selectable image opts into `decorative={false}` and supplies `label`.
92
102
 
93
103
  `AgentCard` composes the fundamental `Card` with explicit `visual`, `name`,
94
104
  `job`, `status`, `meta`, and `action` slots. It owns presentation only. The app
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nadicodeai/ui",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -63,7 +63,7 @@
63
63
  "@assistant-ui/react": "0.15.1",
64
64
  "@assistant-ui/react-markdown": "0.14.8",
65
65
  "@base-ui/react": "^1.6.0",
66
- "@nadicodeai/design-system": "1.0.0",
66
+ "@nadicodeai/design-system": "1.0.2",
67
67
  "@paper-design/shaders": "0.0.78",
68
68
  "class-variance-authority": "^0.7.1",
69
69
  "clsx": "^2.1.1",
@@ -49,7 +49,7 @@ import { Thread } from "@nadicodeai/ui/agents";
49
49
 
50
50
  - Use `@nadicodeai/ui/agents` for assistant-ui conversation primitives, providers, hooks, runtimes, and types. It is a client-only entry; do not import assistant-ui packages directly or recreate their behavior.
51
51
  - Import Agent identity from `@nadicodeai/ui/components/agent-card` and `@nadicodeai/ui/components/agent-visual`. Direct subpaths keep the Card composition server-compatible and the animated visual at a client leaf.
52
- - Use `AgentVisual` with an explicit closed recipe and `registry`, `card`, or `detail` presentation; never infer or randomize identity from a free-form role. Compose it through `AgentCard` when the Agent is a recognizable content object. There is no `AgentGlyph` or `AgentRow`.
52
+ - Use `AgentVisual` with an explicit closed recipe and `registry`, `card`, or `detail` presentation; every role renders the living identity when motion and WebGL are available. The poster is first paint and fallback. Never infer or randomize identity from a free-form role. Compose it through `AgentCard` when the Agent is a recognizable content object. There is no `AgentGlyph` or `AgentRow`.
53
53
  - `Card` defaults to the resting material. Select `surface="raised"` for physically raised/selected work and `surface="plain"` for an intentional structural/auth/website escape. Do not add border, ring, or shadow classes at call sites.
54
54
  - Use `Empty scope="page|region|card"` so only the owning container draws a frame. Use `GlassControl shape="control"` for compact floating controls and `shape="bar"` for full-width header chrome, never content or modal bodies.
55
55
  - Use first-party shadcn registry source in the consuming app for generic data tables, command palettes, and chart cards; do not import a NadicodeAI package abstraction for those patterns.