@nadicodeai/ui 6.6.2 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/AGENTS.md +4 -4
  2. package/LICENSES/LOBEHUB-ICONS-MIT.txt +21 -0
  3. package/NOTICE +14 -0
  4. package/dist/components/activity-orb.d.ts +1 -1
  5. package/dist/components/activity-orb.d.ts.map +1 -1
  6. package/dist/components/brand-icons/claude.d.ts +18 -0
  7. package/dist/components/brand-icons/claude.d.ts.map +1 -0
  8. package/dist/components/brand-icons/claude.js +22 -0
  9. package/dist/components/brand-icons/deepseek.d.ts +18 -0
  10. package/dist/components/brand-icons/deepseek.d.ts.map +1 -0
  11. package/dist/components/brand-icons/deepseek.js +22 -0
  12. package/dist/components/brand-icons/gemini.d.ts +18 -0
  13. package/dist/components/brand-icons/gemini.d.ts.map +1 -0
  14. package/dist/components/brand-icons/gemini.js +25 -0
  15. package/dist/components/brand-icons/grok.d.ts +18 -0
  16. package/dist/components/brand-icons/grok.d.ts.map +1 -0
  17. package/dist/components/brand-icons/grok.js +22 -0
  18. package/dist/components/brand-icons/meta.d.ts +18 -0
  19. package/dist/components/brand-icons/meta.d.ts.map +1 -0
  20. package/dist/components/brand-icons/meta.js +25 -0
  21. package/dist/components/brand-icons/mistral.d.ts +18 -0
  22. package/dist/components/brand-icons/mistral.d.ts.map +1 -0
  23. package/dist/components/brand-icons/mistral.js +22 -0
  24. package/dist/components/brand-icons/moonshot.d.ts +18 -0
  25. package/dist/components/brand-icons/moonshot.d.ts.map +1 -0
  26. package/dist/components/brand-icons/moonshot.js +22 -0
  27. package/dist/components/brand-icons/openai.d.ts +18 -0
  28. package/dist/components/brand-icons/openai.d.ts.map +1 -0
  29. package/dist/components/brand-icons/openai.js +22 -0
  30. package/dist/components/brand-icons/qwen.d.ts +18 -0
  31. package/dist/components/brand-icons/qwen.d.ts.map +1 -0
  32. package/dist/components/brand-icons/qwen.js +25 -0
  33. package/dist/components/brand-icons.d.ts +9 -0
  34. package/dist/components/brand-icons.d.ts.map +1 -1
  35. package/dist/components/brand-icons.js +9 -0
  36. package/dist/components/chart-breakdown-table.d.ts +26 -6
  37. package/dist/components/chart-breakdown-table.d.ts.map +1 -1
  38. package/dist/components/chart-breakdown-table.js +4 -4
  39. package/dist/components/chart-time-series.d.ts +68 -2
  40. package/dist/components/chart-time-series.d.ts.map +1 -1
  41. package/dist/components/chart-time-series.js +112 -11
  42. package/dist/components/chart.d.ts +2 -1
  43. package/dist/components/chart.d.ts.map +1 -1
  44. package/dist/components/chart.js +11 -2
  45. package/dist/components/model-mark.d.ts +19 -0
  46. package/dist/components/model-mark.d.ts.map +1 -0
  47. package/dist/components/model-mark.js +66 -0
  48. package/dist/index.d.ts +1 -0
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +1 -0
  51. package/docs/activity.md +4 -5
  52. package/docs/contract.md +48 -5
  53. package/package.json +3 -7
  54. package/dist/components/threshold/canvas-budget.d.ts +0 -10
  55. package/dist/components/threshold/canvas-budget.d.ts.map +0 -1
  56. package/dist/components/threshold/canvas-budget.js +0 -54
  57. package/dist/components/threshold/index.d.ts +0 -6
  58. package/dist/components/threshold/index.d.ts.map +0 -1
  59. package/dist/components/threshold/index.js +0 -5
  60. package/dist/components/threshold/orb.d.ts +0 -14
  61. package/dist/components/threshold/orb.d.ts.map +0 -1
  62. package/dist/components/threshold/orb.js +0 -17
  63. package/dist/components/threshold/presence.d.ts +0 -27
  64. package/dist/components/threshold/presence.d.ts.map +0 -1
  65. package/dist/components/threshold/presence.js +0 -159
  66. package/dist/components/threshold/room.d.ts +0 -33
  67. package/dist/components/threshold/room.d.ts.map +0 -1
  68. package/dist/components/threshold/room.js +0 -168
  69. package/dist/components/threshold/threshold.css +0 -228
  70. package/dist/components/threshold/typed-line.d.ts +0 -8
  71. package/dist/components/threshold/typed-line.d.ts.map +0 -1
  72. package/dist/components/threshold/typed-line.js +0 -43
  73. package/dist/components/threshold/use-reduced-motion.d.ts +0 -10
  74. package/dist/components/threshold/use-reduced-motion.d.ts.map +0 -1
  75. package/dist/components/threshold/use-reduced-motion.js +0 -32
  76. package/dist/threshold.d.ts +0 -2
  77. package/dist/threshold.d.ts.map +0 -1
  78. package/dist/threshold.js +0 -13
  79. package/docs/threshold.md +0 -221
@@ -1,168 +0,0 @@
1
- "use client";
2
- import { jsx as _jsx } from "react/jsx-runtime";
3
- import { defaultObjectSizing, meshGradientFragmentShader, ShaderFitOptions, ShaderMount, } 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 { useCanvasHeld } from "./canvas-budget.js";
8
- import { useReducedMotion } from "./use-reduced-motion.js";
9
- /*
10
- * The room is the place a threshold happens in: one field of verde ink on true
11
- * black, behind Nadia and behind the words. It is Paper Shaders' published
12
- * Mesh Gradient "Ink" preset, adapted to two colours and slowed, mounted
13
- * through the core `ShaderMount` the package already depends on. There is no
14
- * React wrapper and no second shader engine.
15
- *
16
- * Every number below was selected from rendered captures (the study is
17
- * `apps/nadia/build/design/threshold/`), so this module is the one place they
18
- * are written.
19
- */
20
- const ROOM_SPEED = 0.22;
21
- const ROOM_FRAME = 12_000;
22
- const ROOM_MIN_PIXEL_RATIO = 1;
23
- const ROOM_MAX_PIXEL_COUNT = 1_024_000;
24
- const ROOM_DISTORTION = 1;
25
- const ROOM_SWIRL = 0.2;
26
- const ROOM_ROTATION = 90;
27
- /** The field's ink, and the two colours it carries. Uniforms, never CSS. */
28
- const ROOM_INK_TOKEN = "--nc-identity-ink";
29
- const ROOM_FIELD_TOKEN = "--nc-identity-verde";
30
- const ROOM_READY_TOKEN = "--nc-dark-verde-vivo";
31
- const clampUnit = (value) => Math.min(Math.max(value, 0), 1);
32
- /**
33
- * The threshold's background field.
34
- *
35
- * It paints absolutely inside its own relative, isolated parent and never
36
- * against the viewport, so a Portal panel can hold one without covering the
37
- * page. It takes no pointer events and carries no words.
38
- *
39
- * When WebGL is unavailable, when the design-system tokens are not in the
40
- * cascade above it, or when the context is lost after mounting, the element
41
- * keeps its still field: a token-built radial gradient that needs no GPU. The
42
- * room is never blank.
43
- *
44
- * The window carries one shader canvas. While Nadia's water is painting her
45
- * arrival, the room holds this same still field and mounts nothing; the
46
- * moment she stands clear the water leaves and the room's canvas is the one
47
- * that paints (`canvas-budget.ts`).
48
- */
49
- export function ThresholdRoom({ className, dimmed = false, energy = 0, ready = false, }) {
50
- const still = useReducedMotion();
51
- const hostRef = useRef(null);
52
- const mountRef = useRef(null);
53
- const [surface, setSurface] = useState("still");
54
- const held = useCanvasHeld();
55
- const speed = still ? 0 : ROOM_SPEED * clampUnit(energy);
56
- // The deferred mount reads the values current when it runs, not the ones of
57
- // the render that scheduled it.
58
- const speedRef = useRef(speed);
59
- const readyRef = useRef(ready);
60
- useEffect(() => {
61
- speedRef.current = speed;
62
- readyRef.current = ready;
63
- });
64
- // The window's one canvas. While Nadia's water holds it the room keeps its
65
- // still field; when the hold is released the room mounts. Every other prop
66
- // change is carried into the live mount by the two effects below, so a prop
67
- // change never rebuilds a WebGL context.
68
- //
69
- // The mount is deferred by one microtask. Effects run in tree order, so a
70
- // presence mounted after the room takes its hold after this effect has run;
71
- // deferring lets that hold cancel a mount that has not happened yet, and
72
- // the window never carries two live contexts, not even for one tick.
73
- useEffect(() => {
74
- const host = hostRef.current;
75
- if (!host || held)
76
- return;
77
- let cancelled = false;
78
- let canvas = null;
79
- const clearCanvases = () => {
80
- for (const element of host.querySelectorAll("canvas"))
81
- element.remove();
82
- host.removeAttribute("data-paper-shader");
83
- };
84
- const handleContextLost = (event) => {
85
- event.preventDefault();
86
- canvas?.removeEventListener("webglcontextlost", handleContextLost);
87
- canvas = null;
88
- try {
89
- mountRef.current?.dispose();
90
- }
91
- catch {
92
- // The still field is already behind the canvas; vendor cleanup failing
93
- // must not take the room down with it.
94
- }
95
- mountRef.current = null;
96
- clearCanvases();
97
- setSurface("still");
98
- };
99
- const mount = () => {
100
- if (cancelled)
101
- return;
102
- const ink = resolveIdentityColor(host, ROOM_INK_TOKEN);
103
- const field = resolveIdentityColor(host, readyRef.current ? ROOM_READY_TOKEN : ROOM_FIELD_TOKEN);
104
- // Without the design-system cascade there is no palette to paint with, and
105
- // a guessed colour would be a second source for a token value.
106
- if (!ink || !field)
107
- return;
108
- try {
109
- mountRef.current = new ShaderMount(host, meshGradientFragmentShader, {
110
- u_colors: [field, ink],
111
- u_colorsCount: 2,
112
- u_distortion: ROOM_DISTORTION,
113
- u_fit: ShaderFitOptions.contain,
114
- u_grainMixer: 0,
115
- u_grainOverlay: 0,
116
- u_offsetX: defaultObjectSizing.offsetX,
117
- u_offsetY: defaultObjectSizing.offsetY,
118
- u_originX: defaultObjectSizing.originX,
119
- u_originY: defaultObjectSizing.originY,
120
- u_rotation: ROOM_ROTATION,
121
- u_scale: defaultObjectSizing.scale,
122
- u_swirl: ROOM_SWIRL,
123
- u_worldHeight: defaultObjectSizing.worldHeight,
124
- u_worldWidth: defaultObjectSizing.worldWidth,
125
- }, undefined, speedRef.current, ROOM_FRAME, ROOM_MIN_PIXEL_RATIO, ROOM_MAX_PIXEL_COUNT);
126
- canvas = mountRef.current.canvasElement;
127
- canvas.addEventListener("webglcontextlost", handleContextLost);
128
- setSurface("shader");
129
- }
130
- catch {
131
- // The constructor prepends its canvas before it reaches the context, so
132
- // a failed mount leaves one behind that would cover the still field.
133
- mountRef.current = null;
134
- clearCanvases();
135
- setSurface("still");
136
- }
137
- };
138
- queueMicrotask(mount);
139
- return () => {
140
- cancelled = true;
141
- canvas?.removeEventListener("webglcontextlost", handleContextLost);
142
- try {
143
- mountRef.current?.dispose();
144
- }
145
- catch {
146
- // Same reason as above: disposal is best effort.
147
- }
148
- mountRef.current = null;
149
- clearCanvases();
150
- setSurface("still");
151
- };
152
- }, [held]);
153
- useEffect(() => {
154
- mountRef.current?.setSpeed(speed);
155
- }, [speed]);
156
- useEffect(() => {
157
- const host = hostRef.current;
158
- const mount = mountRef.current;
159
- if (!host || !mount)
160
- return;
161
- const ink = resolveIdentityColor(host, ROOM_INK_TOKEN);
162
- const field = resolveIdentityColor(host, ready ? ROOM_READY_TOKEN : ROOM_FIELD_TOKEN);
163
- if (!ink || !field)
164
- return;
165
- mount.setUniforms({ u_colors: [field, ink] });
166
- }, [ready]);
167
- return (_jsx("div", { "aria-hidden": "true", className: cn("nc-threshold-room", className), "data-dimmed": dimmed || undefined, "data-ready": ready || undefined, "data-surface": surface, ref: hostRef }));
168
- }
@@ -1,228 +0,0 @@
1
- /*
2
- * @nadicodeai/ui/threshold.css
3
- *
4
- * The threshold primitives' stylesheet. It is a separate import rather than a
5
- * part of globals.css because the surface that needs it most, the Nadia
6
- * desktop app, runs inside a shell that does not load Tailwind: it takes this
7
- * file and the design-system token layer, and nothing else from this package.
8
- *
9
- * A threshold is a fixed dark surface. It is true black in every theme, so the
10
- * ladders below read the dark-mode tokens directly instead of branching on the
11
- * app's mode; that is the fixed-presentation escape the package contract
12
- * names, taken once, coherently, for the whole surface.
13
- *
14
- * The room's two colours are shader uniforms, not CSS, and live in room.tsx.
15
- * The only colour written here is the still field the room falls back to.
16
- */
17
-
18
- /* The room ---------------------------------------------------------------- */
19
-
20
- /*
21
- * Absolute inside its own relative, isolated parent, never fixed against the
22
- * viewport: a Portal panel holds a room without covering the page. Its still
23
- * field is a token-built radial that needs no GPU, and the canvas covers it
24
- * once the shader is painting.
25
- */
26
- .nc-threshold-room {
27
- position: absolute;
28
- inset: 0;
29
- z-index: 0;
30
- overflow: hidden;
31
- pointer-events: none;
32
- background: radial-gradient(
33
- ellipse 55% 46% at 50% 36%,
34
- color-mix(in oklab, var(--nc-identity-verde) 30%, var(--nc-identity-ink)),
35
- transparent 90%
36
- );
37
- mask-image: radial-gradient(ellipse 65% 58% at 50% 35%, black 10%, transparent 95%);
38
- transition: opacity var(--nc-duration-open) var(--nc-ease-emphasized);
39
- }
40
-
41
- .nc-threshold-room canvas {
42
- opacity: 0.65;
43
- }
44
-
45
- /* Repair and error states quiet the room; they never remove it. */
46
- .nc-threshold-room[data-dimmed] {
47
- opacity: 0.22;
48
- }
49
-
50
- /* The presence ------------------------------------------------------------ */
51
-
52
- .nc-threshold-presence {
53
- position: relative;
54
- flex: none;
55
- width: clamp(180px, 38vh, 320px);
56
- aspect-ratio: 462 / 520;
57
- transition: opacity var(--nc-duration-confirm) var(--nc-ease-emphasized);
58
- }
59
-
60
- /* Her lower edge dissolves into the room rather than ending on a rectangle. */
61
- .nc-threshold-presence-figure {
62
- position: relative;
63
- width: 100%;
64
- height: 100%;
65
- mask-image: linear-gradient(black 55%, transparent 98%);
66
- transition: opacity var(--nc-duration-confirm) var(--nc-ease-emphasized);
67
- }
68
-
69
- .nc-threshold-presence-portrait {
70
- display: block;
71
- width: 100%;
72
- height: 100%;
73
- object-fit: contain;
74
- }
75
-
76
- /* While the water is painting her, the plain portrait beneath it is redundant. */
77
- .nc-threshold-presence-figure[data-surface="water"] .nc-threshold-presence-portrait {
78
- visibility: hidden;
79
- }
80
-
81
- /*
82
- * The water canvas is cut to her own silhouette. Water's highlight term is
83
- * added to the whole frame, not only to the sampled image, so an unmasked
84
- * canvas paints a faintly lit rectangle over the room: the visible tile the
85
- * rendered comparisons rejected. The mask matches the shader's own `contain`
86
- * fit and centred origin, so the two agree pixel for pixel.
87
- */
88
- .nc-threshold-presence-figure canvas {
89
- position: absolute;
90
- inset: 0;
91
- mask-image: var(--threshold-portrait);
92
- mask-position: center;
93
- mask-repeat: no-repeat;
94
- mask-size: contain;
95
- }
96
-
97
- .nc-threshold-presence[data-state="error"] {
98
- opacity: 0.62;
99
- }
100
-
101
- /*
102
- * Success: one short verde vivo beat over her own silhouette, then nothing.
103
- * Verde vivo is Nadia's presence accent and this is the only place a threshold
104
- * spends it, which is why the buttons beside it stay neutral.
105
- */
106
- .nc-threshold-presence::after {
107
- content: "";
108
- position: absolute;
109
- inset: 0;
110
- background: var(--nc-dark-verde-vivo);
111
- mask-image: var(--threshold-portrait);
112
- mask-position: center;
113
- mask-repeat: no-repeat;
114
- mask-size: contain;
115
- opacity: 0;
116
- pointer-events: none;
117
- }
118
-
119
- .nc-threshold-presence[data-state="ready"]::after {
120
- animation: nc-threshold-arrival 360ms var(--nc-ease-out-strong) 1;
121
- }
122
-
123
- @keyframes nc-threshold-arrival {
124
- 0% {
125
- opacity: 0;
126
- }
127
- 35% {
128
- opacity: 0.22;
129
- }
130
- 100% {
131
- opacity: 0;
132
- }
133
- }
134
-
135
- /* The orb ----------------------------------------------------------------- */
136
-
137
- .nc-threshold-orb {
138
- display: block;
139
- flex: none;
140
- }
141
-
142
- /* The line ---------------------------------------------------------------- */
143
-
144
- /*
145
- * A hidden copy of the whole sentence holds the width while the visible one
146
- * fills in, so the orb beside it does not travel with each character.
147
- */
148
- .nc-threshold-line {
149
- display: grid;
150
- position: relative;
151
- margin: 0;
152
- font-family: var(--nc-type-display-md-font-family);
153
- font-size: var(--nc-type-display-md-font-size);
154
- font-weight: var(--nc-type-display-md-font-weight);
155
- line-height: var(--nc-type-display-md-line-height);
156
- letter-spacing: -0.01em;
157
- text-align: center;
158
- text-wrap: balance;
159
- color: var(--nc-dark-ink);
160
- }
161
-
162
- .nc-threshold-line-measure {
163
- grid-area: 1 / 1;
164
- visibility: hidden;
165
- }
166
-
167
- .nc-threshold-line-spoken {
168
- grid-area: 1 / 1;
169
- }
170
-
171
- .nc-threshold-caret {
172
- display: inline-block;
173
- width: 2px;
174
- height: 1em;
175
- margin-left: 2px;
176
- vertical-align: -0.15em;
177
- background: currentcolor;
178
- visibility: hidden;
179
- }
180
-
181
- .nc-threshold-line[data-typing] .nc-threshold-caret {
182
- visibility: visible;
183
- }
184
-
185
- /* Shared ------------------------------------------------------------------ */
186
-
187
- /*
188
- * The threshold ships its own visually-hidden helper: the desktop shell does
189
- * not load this package's Tailwind layer, and a progress value nobody can read
190
- * is not an accessible progress value.
191
- */
192
- .nc-threshold-sr {
193
- position: absolute;
194
- width: 1px;
195
- height: 1px;
196
- overflow: hidden;
197
- clip-path: inset(50%);
198
- white-space: nowrap;
199
- }
200
-
201
- @media (prefers-reduced-motion: reduce) {
202
- .nc-threshold-room,
203
- .nc-threshold-presence,
204
- .nc-threshold-presence-figure {
205
- transition: none;
206
- }
207
-
208
- .nc-threshold-presence[data-state="ready"]::after {
209
- animation: none;
210
- }
211
-
212
- .nc-threshold-caret {
213
- visibility: hidden;
214
- }
215
- }
216
-
217
- /*
218
- * The components also carry the preference as `data-still`, which is what a
219
- * shell that toggles reduced motion without the media query (an in-app
220
- * setting) sets. Same result, one rule.
221
- */
222
- .nc-threshold-presence[data-still] {
223
- transition: none;
224
- }
225
-
226
- .nc-threshold-presence[data-still][data-state="ready"]::after {
227
- animation: none;
228
- }
@@ -1,8 +0,0 @@
1
- export type TypedLineProps = {
2
- /** Milliseconds per character. The threshold's cadence is 44. */
3
- cadenceMs?: number;
4
- className?: string;
5
- text: string;
6
- };
7
- export declare function TypedLine({ cadenceMs, className, text }: TypedLineProps): import("react").JSX.Element;
8
- //# sourceMappingURL=typed-line.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"typed-line.d.ts","sourceRoot":"","sources":["../../../src/components/threshold/typed-line.tsx"],"names":[],"mappings":"AAwBA,MAAM,MAAM,cAAc,GAAG;IAC3B,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,wBAAgB,SAAS,CAAC,EAAE,SAAsB,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,cAAc,+BAwCpF"}
@@ -1,43 +0,0 @@
1
- "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useEffect, useState } from "react";
4
- import { cn } from "../../lib/utils.js";
5
- import { useReducedMotion } from "./use-reduced-motion.js";
6
- /*
7
- * The line Nadia says while a threshold is open. It types itself, because a
8
- * first run is the one moment in the product where waiting is the content.
9
- *
10
- * Three decisions the captures settled:
11
- *
12
- * - the count comes from elapsed time, not from how many callbacks arrived, so
13
- * a software renderer or a busy main thread cannot stretch a one second
14
- * sentence into five;
15
- * - a hidden copy of the whole sentence reserves its width, so nothing beside
16
- * the line travels sideways with each character;
17
- * - the live region receives the complete sentence at once, so a screen reader
18
- * is never read one letter at a time.
19
- */
20
- const CADENCE_MS = 44;
21
- export function TypedLine({ cadenceMs = CADENCE_MS, className, text }) {
22
- const still = useReducedMotion();
23
- const [shown, setShown] = useState("");
24
- useEffect(() => {
25
- if (still) {
26
- setShown(text);
27
- return;
28
- }
29
- setShown("");
30
- const startedAt = performance.now();
31
- let timer = 0;
32
- const tick = () => {
33
- const count = Math.min(text.length, Math.floor((performance.now() - startedAt) / cadenceMs));
34
- setShown(text.slice(0, count));
35
- if (count < text.length)
36
- timer = window.setTimeout(tick, cadenceMs);
37
- };
38
- timer = window.setTimeout(tick, cadenceMs);
39
- return () => window.clearTimeout(timer);
40
- }, [cadenceMs, still, text]);
41
- const typing = shown.length < text.length;
42
- return (_jsxs("p", { className: cn("nc-threshold-line", className), "data-typing": typing || undefined, children: [_jsx("span", { "aria-hidden": "true", className: "nc-threshold-line-measure", children: text }), _jsxs("span", { "aria-hidden": "true", className: "nc-threshold-line-spoken", children: [_jsx("span", { children: shown }), _jsx("span", { className: "nc-threshold-caret" })] }), _jsx("span", { "aria-live": "polite", className: "nc-threshold-sr", children: text })] }));
43
- }
@@ -1,10 +0,0 @@
1
- /**
2
- * Live `prefers-reduced-motion`, the one reader the threshold primitives share.
3
- *
4
- * The value is read from the media query on every render rather than cached in
5
- * state, so a preference changed while a threshold is open reaches the room,
6
- * the presence, the orb and the typed line in the same commit. Server renders
7
- * report false and the first client render corrects it.
8
- */
9
- export declare function useReducedMotion(): boolean;
10
- //# sourceMappingURL=use-reduced-motion.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"use-reduced-motion.d.ts","sourceRoot":"","sources":["../../../src/components/threshold/use-reduced-motion.ts"],"names":[],"mappings":"AA2BA;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C"}
@@ -1,32 +0,0 @@
1
- "use client";
2
- import { useSyncExternalStore } from "react";
3
- const REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
4
- function query() {
5
- return typeof window === "undefined" || typeof window.matchMedia !== "function"
6
- ? undefined
7
- : window.matchMedia(REDUCED_MOTION_QUERY);
8
- }
9
- function subscribe(onChange) {
10
- const list = query();
11
- if (!list)
12
- return () => { };
13
- list.addEventListener("change", onChange);
14
- return () => list.removeEventListener("change", onChange);
15
- }
16
- function getSnapshot() {
17
- return query()?.matches ?? false;
18
- }
19
- function getServerSnapshot() {
20
- return false;
21
- }
22
- /**
23
- * Live `prefers-reduced-motion`, the one reader the threshold primitives share.
24
- *
25
- * The value is read from the media query on every render rather than cached in
26
- * state, so a preference changed while a threshold is open reaches the room,
27
- * the presence, the orb and the typed line in the same commit. Server renders
28
- * report false and the first client render corrects it.
29
- */
30
- export function useReducedMotion() {
31
- return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
32
- }
@@ -1,2 +0,0 @@
1
- export * from "./components/threshold/index.js";
2
- //# sourceMappingURL=threshold.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"threshold.d.ts","sourceRoot":"","sources":["../src/threshold.ts"],"names":[],"mappings":"AAYA,cAAc,8BAA8B,CAAC"}
package/dist/threshold.js DELETED
@@ -1,13 +0,0 @@
1
- /*
2
- * `@nadicodeai/ui/threshold`: the primitives every threshold is drawn from.
3
- *
4
- * A threshold is the moment before the product is usable: a first run, a
5
- * sign-in, a repair. The Nadia desktop app draws its room, presence, status
6
- * orb and typed line from here, so the brand logic lives in one place.
7
- *
8
- * The screens themselves, and the state machines behind them, stay in the
9
- * consuming app. This entry ships no screen.
10
- *
11
- * The stylesheet is a separate import: `@nadicodeai/ui/threshold.css`.
12
- */
13
- export * from "./components/threshold/index.js";