@nadicodeai/ui 12.1.133 → 12.1.135

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 (32) hide show
  1. package/dist/components/agent-orb.d.ts +8 -5
  2. package/dist/components/agent-orb.d.ts.map +1 -1
  3. package/dist/components/agent-orb.js +8 -5
  4. package/dist/components/data-table.d.ts +1 -1
  5. package/dist/components/data-table.js +2 -5
  6. package/dist/components/record-identity.d.ts +1 -1
  7. package/dist/components/record-identity.d.ts.map +1 -1
  8. package/dist/components/record-identity.js +2 -4
  9. package/dist/components/record-list.d.ts +1 -1
  10. package/dist/components/record-list.js +1 -1
  11. package/dist/components/scope-switcher.d.ts +1 -1
  12. package/dist/components/scope-switcher.d.ts.map +1 -1
  13. package/dist/components/scope-switcher.js +5 -2
  14. package/dist/components/table-layouts.d.ts +7 -7
  15. package/dist/components/table-layouts.js +7 -7
  16. package/dist/components/table.d.ts +2 -2
  17. package/dist/components/table.js +2 -2
  18. package/dist/internal/agent-orb-motion.d.ts +36 -5
  19. package/dist/internal/agent-orb-motion.d.ts.map +1 -1
  20. package/dist/internal/agent-orb-motion.js +153 -37
  21. package/dist/internal/agent-orb.d.ts.map +1 -1
  22. package/dist/internal/agent-orb.js +38 -20
  23. package/dist/internal/table-compact-line.d.ts +0 -5
  24. package/dist/internal/table-compact-line.d.ts.map +1 -1
  25. package/dist/internal/table-compact-line.js +1 -6
  26. package/dist/showcase/specimens/other.js +1 -1
  27. package/dist/showcase/specimens/status.d.ts +2 -1
  28. package/dist/showcase/specimens/status.d.ts.map +1 -1
  29. package/dist/showcase/specimens/status.js +17 -3
  30. package/dist/showcase/usage.generated.js +6 -6
  31. package/dist/standalone/index.js +1 -1
  32. package/package.json +2 -2
@@ -1,19 +1,27 @@
1
+ import { spring } from "motion";
1
2
  const personalities = {
2
- ribbon: { speed: 0.55, tilt: 0.15 },
3
- nadia: { speed: 0.55, tilt: 0.15 },
4
- sofia: { speed: 0.55, tilt: -0.15 },
5
- lattice: { speed: 0.28, tilt: -0.12 },
6
- orbit: { speed: 0.7, tilt: 0.2 },
7
- globe: { speed: 0.35, tilt: -0.16 },
8
- network: { speed: 0.55, tilt: 0.1 },
9
- braid: { speed: 0.65, tilt: -0.2 },
10
- morph: { speed: 0.9, tilt: 0.12 },
3
+ ribbon: { speed: 0.55 },
4
+ nadia: { speed: 0.55 },
5
+ sofia: { speed: 0.55 },
6
+ lattice: { speed: 0.28 },
7
+ orbit: { speed: 0.7 },
8
+ globe: { speed: 0.35 },
9
+ network: { speed: 0.55 },
10
+ braid: { speed: 0.65 },
11
+ morph: { speed: 0.9 },
11
12
  };
12
13
  export function nativeSpeed(form) {
13
14
  return personalities[form].speed;
14
15
  }
15
- export function stateSpeed(state, form) {
16
- const speed = { idle: 1, working: 1, thinking: 1, waiting: 0, blocked: 0, done: 0.65 };
16
+ export function stateSpeed(state, form, age = 0, timing = defaultTiming) {
17
+ const speed = {
18
+ idle: 0.864,
19
+ working: 1,
20
+ thinking: 0.55,
21
+ waiting: 0,
22
+ blocked: 0,
23
+ done: age >= timing.doneDuration ? 0.72 : 0.65,
24
+ };
17
25
  return speed[state] * nativeSpeed(form);
18
26
  }
19
27
  /** The breath's period, in seconds. */
@@ -29,58 +37,166 @@ export function reducedBreath(state, seconds) {
29
37
  const phase = (1 - Math.cos((seconds * Math.PI * 2) / BREATH_SECONDS)) / 2;
30
38
  return 1 - 0.55 * phase;
31
39
  }
32
- export function motionPose(form, state, age) {
33
- const personality = personalities[form];
40
+ export function readOrbTiming(style) {
41
+ const seconds = (name, fallback) => {
42
+ const token = style?.getPropertyValue(`--nc-duration-${name}`).trim() ?? "";
43
+ return token ? Number.parseFloat(token) * (token.endsWith("ms") ? 0.001 : 1) : fallback;
44
+ };
45
+ const wait = seconds("wait", 1.6);
46
+ const confirm = seconds("confirm", 0.18);
47
+ const transition = seconds("transition", 0.24);
48
+ const period = (beats) => Math.round(wait * beats * 1000) / 1000;
49
+ const gesture = spring({ keyframes: [0, 1], duration: transition * 2000, bounce: 0.1 });
50
+ return {
51
+ wait,
52
+ confirm,
53
+ transition,
54
+ doneDuration: period(3),
55
+ blockedDuration: period(1.25),
56
+ gestureAge: (age) => (age * transition * 2) / wait,
57
+ settle: (age, delay = 0) => age < delay ? 0 : gesture.next((age - delay) * 1000).value,
58
+ };
59
+ }
60
+ const defaultTiming = readOrbTiming();
61
+ const clamp = (value) => Math.min(1, Math.max(0, value));
62
+ const smooth = (value) => {
63
+ const t = clamp(value);
64
+ return t * t * (3 - 2 * t);
65
+ };
66
+ export function motionPose(state, age, timing = defaultTiming, reduced = false) {
34
67
  let sx = 0.82;
35
68
  let sy = 0.82;
36
69
  let y = 0;
37
- const x = 0;
70
+ let x = 0;
38
71
  let rotation = 0;
39
- const split = 0;
40
- let opacity = 1;
41
72
  if (state === "idle") {
42
- const breath = Math.sin((age * Math.PI) / 2);
73
+ const breath = Math.sin(((reduced ? 0.6 : age) * Math.PI * 2) / (timing.wait * 4));
43
74
  sx += breath * 0.01;
44
75
  sy += breath * 0.01;
45
76
  y = -breath * 0.004;
46
77
  }
47
- else if (state === "waiting") {
48
- sx = 0.8;
49
- sy = 0.84;
50
- y = -0.015;
51
- opacity = 0.75;
78
+ else if (!reduced && state === "waiting") {
79
+ const gestureAge = timing.gestureAge(age);
80
+ const breath = Math.sin((age * Math.PI * 2) / (timing.wait * 3));
81
+ const anticipation = Math.sin(clamp(gestureAge / timing.confirm) * Math.PI);
82
+ const lean = timing.settle(gestureAge, timing.confirm * 0.55);
83
+ const scale = 1 - anticipation * 0.018 + breath * 0.006;
84
+ sx *= scale;
85
+ sy *= scale;
86
+ y = -lean * 0.018;
87
+ rotation = (-lean * 4.5 * Math.PI) / 180;
52
88
  }
53
- else if (state === "blocked") {
54
- const stop = Math.min(1, age / 0.25);
55
- const effort = Math.sin(stop * Math.PI);
56
- sx = 0.8 + effort * 0.015;
57
- sy = 0.82 - stop * 0.08;
58
- y = stop * 0.025 - effort * 0.018;
59
- rotation = personality.tilt * stop * 0.4;
60
- opacity = 1 - stop * 0.4;
89
+ else if (!reduced && state === "blocked") {
90
+ const gestureAge = timing.gestureAge(age);
91
+ const strikeAt = timing.confirm * 0.85;
92
+ const recoil = smooth(gestureAge / strikeAt) * (1 - timing.settle(gestureAge, strikeAt));
93
+ sx *= 1 - recoil * 0.018;
94
+ sy *= 1 + recoil * 0.012;
95
+ x = -recoil * 0.034;
96
+ y = recoil * 0.014;
97
+ rotation = (-recoil * 4 * Math.PI) / 180;
61
98
  }
62
- else if (state === "done") {
63
- if (age < 0.18) {
64
- const anticipation = Math.sin((Math.PI * age) / 0.18);
99
+ else if (!reduced && state === "done") {
100
+ const phase = (age * 1.3) / (timing.wait * 2.25);
101
+ if (phase < 0.18) {
102
+ const anticipation = Math.sin((Math.PI * phase) / 0.18);
65
103
  sx += 0.03 * anticipation;
66
104
  sy -= 0.04 * anticipation;
67
105
  y = 0.012 * anticipation;
68
106
  }
69
- else if (age < 1.3) {
70
- const leap = (age - 0.18) / 1.12;
107
+ else if (phase < 1.3) {
108
+ const leap = (phase - 0.18) / 1.12;
71
109
  y = -0.07 * Math.sin(Math.PI * leap);
72
110
  sx += 0.025 * Math.sin(Math.PI * leap);
73
111
  sy += 0.035 * Math.sin(Math.PI * leap);
74
112
  rotation = Math.sin(leap * Math.PI * 2) * 0.07;
75
113
  }
76
114
  }
77
- return { sx, sy, x, y, rotation, split, opacity };
115
+ return { sx, sy, x, y, rotation };
116
+ }
117
+ // Lucide Pause and Ban geometry, lucide-static 1.58.0, ISC licence.
118
+ function paintHeldCue(context, state, size, age, reduced, color, timing) {
119
+ const gestureAge = timing.gestureAge(age);
120
+ const enter = reduced ? 1 : smooth((gestureAge - timing.confirm * 0.35) / timing.confirm);
121
+ const landed = reduced ? 1 : timing.settle(gestureAge, timing.confirm * 0.35);
122
+ const side = Math.max(7, Math.min(36, size * 0.26));
123
+ context.save();
124
+ context.fillStyle = color;
125
+ context.strokeStyle = color;
126
+ context.globalAlpha = enter * 0.95;
127
+ context.translate(size / 2, size / 2);
128
+ if (state === "waiting") {
129
+ const lean = reduced ? 0 : timing.settle(gestureAge, timing.confirm * 0.55);
130
+ const breath = reduced ? 0 : Math.sin((age * Math.PI * 2) / (timing.wait * 3));
131
+ context.translate(0, (1 - landed) * -0.12 * side);
132
+ context.rotate((-lean * 2 * Math.PI) / 180);
133
+ const scale = 0.94 + landed * 0.06;
134
+ context.scale(scale, scale);
135
+ context.scale(side / 24, side / 24);
136
+ for (const [index, x] of [14, 5].entries()) {
137
+ const arrival = reduced
138
+ ? 1
139
+ : timing.settle(gestureAge, timing.confirm * (index === 0 ? 0.65 : 0.2));
140
+ const direction = index === 0 ? 1 : -1;
141
+ context.save();
142
+ context.globalAlpha = enter * 0.95 * clamp(arrival) * (reduced ? 1 : 0.96 + breath * 0.04);
143
+ context.translate(x - 9.5 + direction * (1 - arrival) * 1.45 * 5, (1 - arrival) * -0.24 * 18);
144
+ context.rotate((direction * (1 - arrival) * 14 * Math.PI) / 180);
145
+ context.scale(1, 1 + (1 - arrival) * 0.12);
146
+ context.beginPath();
147
+ context.roundRect(-2.5, -9, 5, 18, 1);
148
+ context.fill();
149
+ context.restore();
150
+ }
151
+ }
152
+ else {
153
+ context.translate(0, (1 - landed) * -0.1 * side);
154
+ context.rotate(((1 - landed) * -12 * Math.PI) / 180);
155
+ const scale = 1 + (1 - landed) * 0.18;
156
+ context.scale(scale, scale);
157
+ context.scale(side / 24, side / 24);
158
+ context.lineWidth = Math.max(1.6, 24 / side);
159
+ context.lineCap = "round";
160
+ context.beginPath();
161
+ context.arc(0, 0, 10, 0, Math.PI * 2);
162
+ context.stroke();
163
+ const slash = reduced ? 1 : smooth((gestureAge - timing.confirm * 0.8) / timing.confirm);
164
+ context.beginPath();
165
+ context.moveTo(-7.071, -7.071);
166
+ context.lineTo(-7.071 + 14.141 * slash, -7.071 + 14.142 * slash);
167
+ context.stroke();
168
+ if (!reduced && size >= 40) {
169
+ const impact = clamp((gestureAge - timing.confirm * 0.85) / timing.transition);
170
+ context.globalAlpha *= Math.sin(impact * Math.PI) * 0.65;
171
+ context.lineWidth = (1.4 * 24) / side;
172
+ for (const direction of [-1, 1]) {
173
+ context.beginPath();
174
+ const x = direction * (14.52 + impact * 5.472);
175
+ const y = direction * (10.34 + impact * 1.68);
176
+ context.moveTo(x - 1.612, y - 1.612);
177
+ context.lineTo(x + 1.612, y + 1.612);
178
+ context.stroke();
179
+ }
180
+ }
181
+ }
182
+ context.restore();
78
183
  }
79
184
  export function paintOrbCue(context, state, size, seconds, reduced, colors, depth,
80
185
  /** The orb's whole ink, so the cue breathes with the body under reduced motion. */
81
- ink = 1) {
186
+ ink = 1, timing = defaultTiming, statusColor = colors[0] ?? "currentColor") {
187
+ if (state === "waiting" || state === "blocked") {
188
+ if (depth === "front")
189
+ paintHeldCue(context, state, size, seconds, reduced, statusColor, timing);
190
+ return;
191
+ }
82
192
  if (state !== "working" && state !== "thinking" && state !== "done")
83
193
  return;
194
+ seconds *=
195
+ state === "done"
196
+ ? 3.1 / timing.doneDuration
197
+ : state === "working"
198
+ ? 7.2 / (timing.wait * 2)
199
+ : 3.6 / (timing.wait * 1.5);
84
200
  context.save();
85
201
  if (state === "thinking") {
86
202
  if (depth === "front") {
@@ -1 +1 @@
1
- {"version":3,"file":"agent-orb.d.ts","sourceRoot":"","sources":["../../src/internal/agent-orb.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,QAAQ,EAEb,KAAK,QAAQ,EAId,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,KAAK,aAAa,EAMnB,MAAM,oBAAoB,CAAC;AAE5B,QAAA,MAAM,aAAa;;;;;;;;CAQ0B,CAAC;AAE9C,MAAM,MAAM,YAAY,GAAG,MAAM,OAAO,aAAa,CAAC;AACtD;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,YAAY,GAAG,OAAO,GAAG,OAAO,CAAC;AAC9D,MAAM,MAAM,YAAY,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC;AAE7D,kDAAkD;AAClD,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,cAAc,CAAC;AACxD,+FAA+F;AAC/F,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,SAAS,CAAC;AAiDlD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAuDpF;AAqGD,KAAK,iBAAiB,GAAG;IACvB,IAAI,EAAE,cAAc,CAAC;IACrB;;;OAGG;IACH,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,gBAAgB,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,iBAAiB,GAClD,CAAC;IAAE,MAAM,CAAC,EAAE,QAAQ,CAAC;IAAC,KAAK,CAAC,EAAE,aAAa,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC,CAAC;AAExF,wBAAgB,aAAa,CAAC,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,oBAAoB;;sBAIzC,OAAO;qBAAuB,aAAa;EAoMvF"}
1
+ {"version":3,"file":"agent-orb.d.ts","sourceRoot":"","sources":["../../src/internal/agent-orb.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,QAAQ,EAEb,KAAK,QAAQ,EAId,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,KAAK,aAAa,EAOnB,MAAM,oBAAoB,CAAC;AAE5B,QAAA,MAAM,aAAa;;;;;;;;CAQ0B,CAAC;AAE9C,MAAM,MAAM,YAAY,GAAG,MAAM,OAAO,aAAa,CAAC;AACtD;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,YAAY,GAAG,OAAO,GAAG,OAAO,CAAC;AAC9D,MAAM,MAAM,YAAY,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC;AAE7D,kDAAkD;AAClD,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,cAAc,CAAC;AACxD,+FAA+F;AAC/F,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,SAAS,CAAC;AAyDlD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAuDpF;AA6HD,KAAK,iBAAiB,GAAG;IACvB,IAAI,EAAE,cAAc,CAAC;IACrB;;;OAGG;IACH,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,gBAAgB,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,iBAAiB,GAClD,CAAC;IAAE,MAAM,CAAC,EAAE,QAAQ,CAAC;IAAC,KAAK,CAAC,EAAE,aAAa,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC,CAAC;AAExF,wBAAgB,aAAa,CAAC,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,oBAAoB;;sBAIzC,OAAO;qBAAuB,aAAa;EA0MvF"}
@@ -1,5 +1,5 @@
1
1
  import { finalizeFrame, MODE_FRAMES, makeProj, radiusScale, resolvePreset, } from "thinking-orbs/engine";
2
- import { motionPose, nativeSpeed, paintOrbCue, reducedBreath, stateSpeed, } from "./agent-orb-motion.js";
2
+ import { motionPose, nativeSpeed, paintOrbCue, reducedBreath, readOrbTiming, stateSpeed, } from "./agent-orb-motion.js";
3
3
  const agentOrbForms = {
4
4
  orbit: "working",
5
5
  globe: "searching",
@@ -27,7 +27,15 @@ const presenceWeights = {
27
27
  */
28
28
  const nadiaStripes = ["--nc-identity-azzurro", "--nc-identity-verde", null, "--nc-identity-rosso"];
29
29
  const celebrationTokens = ["--nc-acqua-700", "--nc-viola-700", "--nc-warning", "--nc-rosso-700"];
30
- const appearanceTokens = [...celebrationTokens, ...nadiaStripes.filter((token) => token !== null)];
30
+ const statusTokens = { waiting: "--nc-warning-deep", blocked: "--nc-error" };
31
+ const appearanceTokens = [
32
+ ...celebrationTokens,
33
+ ...nadiaStripes.filter((token) => token !== null),
34
+ ...Object.values(statusTokens),
35
+ "--nc-duration-wait",
36
+ "--nc-duration-confirm",
37
+ "--nc-duration-transition",
38
+ ];
31
39
  /**
32
40
  * Sofia's four stripes: cobalto, giallo, the orb's own pigment and arancio, the
33
41
  * identity hues Nadia does not wear, so side by side the two carry all six.
@@ -122,22 +130,25 @@ function paintAgentOrb(context, input) {
122
130
  ? facing(stripedRibbonFrame(size, seconds, preset.opts), size)
123
131
  : MODE_FRAMES[preset.mode](size, seconds, preset.opts);
124
132
  context.clearRect(0, 0, size, size);
125
- const pose = motionPose(form, input.motion === "ambient" ? "idle" : state, reduced ? (state === "done" ? 2 : 0.6) : age);
126
- const gestureSeconds = reduced ? 0.8 : age / (state === "done" ? 0.575 : 0.2875);
133
+ const done = state === "done" && age >= input.timing.doneDuration;
134
+ const visualState = input.motion === "ambient" || done ? "idle" : state;
135
+ const poseAge = input.motion === "ambient" ? 0 : done ? age - input.timing.doneDuration : age;
136
+ const pose = motionPose(visualState, poseAge, input.timing, reduced);
137
+ const cueAge = done ? age - input.timing.doneDuration : age;
138
+ const statusColor = visualState === "waiting" || visualState === "blocked"
139
+ ? input.statusColors[visualState]
140
+ : color;
127
141
  const project = (x, y) => {
128
142
  const dx = (x - size / 2) * pose.sx;
129
143
  const dy = (y - size / 2) * pose.sy;
130
144
  return [
131
- size / 2 +
132
- dx * Math.cos(pose.rotation) -
133
- dy * Math.sin(pose.rotation) +
134
- (pose.x + Math.sign(dx) * pose.split) * size,
145
+ size / 2 + dx * Math.cos(pose.rotation) - dy * Math.sin(pose.rotation) + pose.x * size,
135
146
  size / 2 + dx * Math.sin(pose.rotation) + dy * Math.cos(pose.rotation) + pose.y * size,
136
147
  ];
137
148
  };
138
- const ink = pose.opacity * breath;
149
+ const ink = breath;
139
150
  if (input.motion === "status") {
140
- paintOrbCue(context, state, size, gestureSeconds, reduced, celebrationColors, "back", breath);
151
+ paintOrbCue(context, visualState, size, cueAge, reduced, celebrationColors, "back", breath, input.timing, statusColor);
141
152
  }
142
153
  context.fillStyle = color;
143
154
  context.strokeStyle = color;
@@ -159,7 +170,7 @@ function paintAgentOrb(context, input) {
159
170
  }
160
171
  context.globalAlpha = 1;
161
172
  if (input.motion === "status") {
162
- paintOrbCue(context, state, size, gestureSeconds, reduced, celebrationColors, "front", breath);
173
+ paintOrbCue(context, visualState, size, cueAge, reduced, celebrationColors, "front", breath, input.timing, statusColor);
163
174
  }
164
175
  }
165
176
  export function mountAgentOrb(canvas, options) {
@@ -185,6 +196,8 @@ export function mountAgentOrb(canvas, options) {
185
196
  let celebrationColors = [color];
186
197
  let stripeColors = [color];
187
198
  let appearanceKey = "";
199
+ let timing = readOrbTiming();
200
+ let statusColors = { waiting: color, blocked: color };
188
201
  /**
189
202
  * The box the orb is displayed at, in CSS pixels. A canvas stretched by
190
203
  * layout reads as pixels unless the engine paints at that size, so the box
@@ -213,6 +226,8 @@ export function mountAgentOrb(canvas, options) {
213
226
  motion: orbMotion,
214
227
  celebrationColors,
215
228
  stripeColors,
229
+ statusColors,
230
+ timing,
216
231
  });
217
232
  }
218
233
  function running() {
@@ -223,7 +238,7 @@ export function mountAgentOrb(canvas, options) {
223
238
  return orbMotion === "status" && (state === "thinking" || state === "working");
224
239
  if (orbMotion === "ambient")
225
240
  return true;
226
- return state !== "waiting" && !(state === "blocked" && age >= 0.3);
241
+ return !(state === "blocked" && age >= timing.blockedDuration);
227
242
  }
228
243
  function tick(now) {
229
244
  request = 0;
@@ -235,21 +250,19 @@ export function mountAgentOrb(canvas, options) {
235
250
  breath += elapsed;
236
251
  else {
237
252
  seconds +=
238
- elapsed * (orbMotion === "ambient" ? nativeSpeed(form) : stateSpeed(state, form));
253
+ elapsed *
254
+ (orbMotion === "ambient" ? nativeSpeed(form) : stateSpeed(state, form, age, timing));
239
255
  if (orbMotion === "status")
240
- age += elapsed * (state === "done" ? 0.575 : 0.2875);
241
- if (state === "done" && age >= 1.8) {
242
- state = "idle";
243
- age = 0;
244
- }
256
+ age += elapsed;
245
257
  }
246
258
  }
247
259
  previous = now;
248
- if (now - lastPaint >= 1000 / 30) {
260
+ if (now - lastPaint >= 1000 / 30 || !running()) {
249
261
  paint();
250
262
  lastPaint = now;
251
263
  }
252
- request = requestAnimationFrame(tick);
264
+ if (running())
265
+ request = requestAnimationFrame(tick);
253
266
  }
254
267
  function play() {
255
268
  cancelAnimationFrame(request);
@@ -293,6 +306,11 @@ export function mountAgentOrb(canvas, options) {
293
306
  const appearance = getComputedStyle(canvas);
294
307
  appearanceKey = readAppearanceKey(appearance, painted);
295
308
  color = appearance.color;
309
+ timing = readOrbTiming(appearance);
310
+ statusColors = {
311
+ waiting: computedColor(appearance.getPropertyValue(statusTokens.waiting).trim() || color),
312
+ blocked: computedColor(appearance.getPropertyValue(statusTokens.blocked).trim() || color),
313
+ };
296
314
  celebrationColors = celebrationTokens.map((token) => computedColor(`color-mix(in oklch, ${appearance.getPropertyValue(token).trim() || color} 75%, white)`));
297
315
  stripeColors = (figureStripes[form] ?? []).map((token) => (token && appearance.getPropertyValue(token).trim()) || color);
298
316
  play();
@@ -17,11 +17,6 @@ type TableCompactLineValue = {
17
17
  layout: TableCompactLayout;
18
18
  facts: readonly TableDetailFact[];
19
19
  };
20
- /**
21
- * The facts an entity row moves onto the line under its name on a card narrower than its
22
- * columns, so the record stays one 72 px row: the name over one muted line. `RecordIdentity`
23
- * draws them after its own line.
24
- */
25
20
  export declare function TableCompactLine({ children, ...value }: TableCompactLineValue & {
26
21
  children: React.ReactNode;
27
22
  }): React.JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"table-compact-line.d.ts","sourceRoot":"","sources":["../../src/internal/table-compact-line.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAuB,KAAK,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAG1F,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,4HAA4H;IAC5H,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,wGAAwG;IACxG,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,gBAAwB,GACzB,EAAE;IACD,KAAK,EAAE,SAAS,eAAe,EAAE,CAAC;IAClC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,uBAQA;AAED,KAAK,qBAAqB,GAAG;IAAE,MAAM,EAAE,kBAAkB,CAAC;IAAC,KAAK,EAAE,SAAS,eAAe,EAAE,CAAA;CAAE,CAAC;AAI/F;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,qBAAqB,GAAG;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,qBAEvD;AAED,4IAA4I;AAC5I,wBAAgB,iBAAiB,6BAiBhC;AAED,2KAA2K;AAC3K,wBAAgB,qBAAqB,6BAkBpC;AAID;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,OAAO,EACP,QAAQ,GACT,EAAE;IACD,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,qBAEA;AAED,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD"}
1
+ {"version":3,"file":"table-compact-line.d.ts","sourceRoot":"","sources":["../../src/internal/table-compact-line.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAuB,KAAK,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAG1F,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,4HAA4H;IAC5H,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,wGAAwG;IACxG,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,gBAAwB,GACzB,EAAE;IACD,KAAK,EAAE,SAAS,eAAe,EAAE,CAAC;IAClC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,uBAQA;AAED,KAAK,qBAAqB,GAAG;IAAE,MAAM,EAAE,kBAAkB,CAAC;IAAC,KAAK,EAAE,SAAS,eAAe,EAAE,CAAA;CAAE,CAAC;AAI/F,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,qBAAqB,GAAG;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,qBAEvD;AAED,4IAA4I;AAC5I,wBAAgB,iBAAiB,6BAgBhC;AAED,2KAA2K;AAC3K,wBAAgB,qBAAqB,6BAkBpC;AAID;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,OAAO,EACP,QAAQ,GACT,EAAE;IACD,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,qBAEA;AAED,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD"}
@@ -7,11 +7,6 @@ export function DetailFacts({ facts, leadingSeparator = false, }) {
7
7
  return facts.map((fact, index) => (_jsxs("span", { children: [index > 0 || leadingSeparator ? _jsx("span", { "aria-hidden": "true", children: " \u00B7 " }) : null, _jsxs("span", { className: "sr-only", children: [fact.label, ": "] }), fact.value] }, fact.id)));
8
8
  }
9
9
  const TableCompactLineContext = React.createContext(null);
10
- /**
11
- * The facts an entity row moves onto the line under its name on a card narrower than its
12
- * columns, so the record stays one 72 px row: the name over one muted line. `RecordIdentity`
13
- * draws them after its own line.
14
- */
15
10
  export function TableCompactLine({ children, ...value }) {
16
11
  return _jsx(TableCompactLineContext, { value: value, children: children });
17
12
  }
@@ -23,7 +18,7 @@ export function TableCompactFacts() {
23
18
  const facts = line.facts.filter((fact) => !fact.besideName);
24
19
  if (facts.length === 0)
25
20
  return null;
26
- return (_jsx("span", { className: cn("[&_[data-slot=tag]]:contents [&>span>:is(div,p,.block,.grid,.flex)]:inline", tableCompactLayouts[line.layout].details), "data-slot": "table-detail-line", "data-wraps": facts.some((fact) => fact.wraps) || undefined, children: _jsx(DetailFacts, { facts: facts, leadingSeparator: true }) }));
21
+ return (_jsx("span", { className: cn("[&_[data-slot=tag]]:contents [&>span>:is(div,p,.block,.grid,.flex)]:inline", tableCompactLayouts[line.layout].details), "data-slot": "table-detail-line", children: _jsx(DetailFacts, { facts: facts, leadingSeparator: true }) }));
27
22
  }
28
23
  /** The compact facts that stand beside the name of the row this stands in, which no ellipsis cuts; nothing outside such a row or on a card wide enough for the columns. */
29
24
  export function TableCompactNameFacts() {
@@ -37,7 +37,7 @@ function CompanyCardSpecimen() {
37
37
  }
38
38
  function HiveSceneSpecimen() {
39
39
  const art = useEmptyArt();
40
- return (_jsx("div", { className: "h-96 overflow-hidden rounded-md", children: _jsx(HiveScene, { className: "size-full", logo: { src: HIVE_LOGO, label: "Alveare" }, mode: "join", picture: _jsx("img", { alt: "", className: "size-full object-cover", src: art.teams }), children: _jsx("p", { className: "text-lg font-semibold", children: "Join Acme's workspace" }) }) }));
40
+ return (_jsx("div", { className: "h-96 overflow-hidden rounded-md", style: { contain: "paint" }, children: _jsx(HiveScene, { className: "size-full", logo: { src: HIVE_LOGO, label: "Alveare" }, mode: "join", picture: _jsx("img", { alt: "", className: "size-full object-cover", src: art.teams }), children: _jsx("p", { className: "text-lg font-semibold", children: "Join Acme's workspace" }) }) }));
41
41
  }
42
42
  function NadiaPresenceSpecimen() {
43
43
  const [reveal, setReveal] = useState(0.6);
@@ -1,8 +1,9 @@
1
1
  declare function ActionStatusSpecimen(): import("react").JSX.Element;
2
2
  declare function GaugeSpecimen(): import("react").JSX.Element;
3
+ declare function AgentOrbSpecimen(): import("react").JSX.Element;
3
4
  export declare const STATUS: {
4
5
  ActionStatus: typeof ActionStatusSpecimen;
5
- AgentOrb: () => import("react").JSX.Element;
6
+ AgentOrb: typeof AgentOrbSpecimen;
6
7
  AppMark: () => import("react").JSX.Element;
7
8
  ArchIcon: () => import("react").JSX.Element;
8
9
  Avatar: () => import("react").JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/showcase/specimens/status.tsx"],"names":[],"mappings":"AAwDA,iBAAS,oBAAoB,gCAiB5B;AAYD,iBAAS,aAAa,gCA2DrB;AAED,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;CAoQQ,CAAC"}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/showcase/specimens/status.tsx"],"names":[],"mappings":"AAyDA,iBAAS,oBAAoB,gCAiB5B;AAYD,iBAAS,aAAa,gCA2DrB;AAWD,iBAAS,gBAAgB,gCAkExB;AAED,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;CAuPQ,CAAC"}
@@ -1,12 +1,13 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useId } from "react";
3
+ import { useId, useState } from "react";
4
4
  import { Building2, Check, Clock, CreditCard, Database, Lock, Mail, MonitorDown, Pause, Save, Wrench, } from "lucide-react";
5
5
  import { ActionStatus } from "../../components/action-status.js";
6
6
  import { AgentOrb } from "../../components/agent-orb.js";
7
7
  import { AppMark, composioLogo } from "../../components/app-mark.js";
8
8
  import { Avatar, AvatarFallback, AvatarGroup, AvatarWithIcon } from "../../components/avatar.js";
9
9
  import { Badge } from "../../components/badge.js";
10
+ import { Button } from "../../components/button.js";
10
11
  import { BrandMark, BrandWordmark } from "../../components/brand.js";
11
12
  import { CheckCounts, CheckState } from "../../components/check-counts.js";
12
13
  import { FileTypeIcon } from "../../components/file-type-icon.js";
@@ -21,7 +22,7 @@ import { RecordIdentity } from "../../components/record-identity.js";
21
22
  import { StatusBadge } from "../../components/status-badge.js";
22
23
  import { Tag } from "../../components/tag.js";
23
24
  import { ObjectFactValue } from "../../components/object-fact-value.js";
24
- import { ARANCIO, COBALTO, nothing, VERDE } from "../samples.js";
25
+ import { COBALTO, nothing } from "../samples.js";
25
26
  const SAMPLE_LOGO = `data:image/svg+xml,${encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 16"><rect width="64" height="16" fill="white"/><text x="0" y="13" fill="navy" font-family="sans-serif" font-size="14" font-weight="700">ROSSI</text></svg>')}`;
26
27
  const SAMPLE_ICON = `data:image/svg+xml,${encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><rect width="32" height="32" rx="7" fill="royalblue"/><path d="M10 9h8a5 5 0 0 1 0 10h-4v5h-4z" fill="white"/></svg>')}`;
27
28
  const MEMBERS = [
@@ -41,9 +42,22 @@ function GaugeRow({ label, gauge }) {
41
42
  function GaugeSpecimen() {
42
43
  return (_jsxs("div", { className: "grid gap-6", children: [_jsxs("div", { className: "flex flex-wrap items-end gap-6", children: [_jsx(GaugeRow, { gauge: (id) => _jsx(Gauge, { "aria-labelledby": id, size: "xs", value: 42 }), label: "Storage used" }), _jsx(GaugeRow, { gauge: (id) => _jsx(Gauge, { "aria-labelledby": id, size: "sm", value: 64 }), label: "Plan used" }), _jsx(GaugeRow, { gauge: (id) => _jsx(Gauge, { "aria-labelledby": id, size: "md", value: 87 }), label: "Tasks done" }), _jsx(GaugeRow, { gauge: (id) => _jsx(Gauge, { "aria-labelledby": id, size: "lg", value: 99.97 }), label: "Cache hit rate" })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-6", children: [_jsx(GaugeRow, { gauge: (id) => _jsx(Gauge, { "aria-labelledby": id, showValue: true, size: "sm", value: 64 }), label: "Plan used, 64%" }), _jsx(GaugeRow, { gauge: (id) => _jsx(Gauge, { "aria-labelledby": id, showValue: true, size: "md", value: 87 }), label: "Tasks done, 87%" }), _jsx(GaugeRow, { gauge: (id) => _jsx(Gauge, { "aria-labelledby": id, showValue: true, size: "lg", value: 99.97 }), label: "Cache hit rate, 99.97%" })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-6", children: [_jsx(GaugeRow, { gauge: (id) => (_jsx(Gauge, { "aria-labelledby": id, showValue: true, size: "md", tone: "warning", value: 82 })), label: "Model budget, 82%: close to the limit" }), _jsx(GaugeRow, { gauge: (id) => (_jsx(Gauge, { "aria-labelledby": id, showValue: true, size: "md", tone: "critical", value: 97 })), label: "Mailbox space, 97%: almost full" }), _jsx(GaugeRow, { gauge: (id) => (_jsx(Gauge, { "aria-labelledby": id, size: "md", value: 58, children: _jsx(Database, { "aria-hidden": "true" }) })), label: "Memory used" })] })] }));
43
44
  }
45
+ const ORB_STATES = [
46
+ "idle",
47
+ "working",
48
+ "thinking",
49
+ "waiting",
50
+ "blocked",
51
+ "done",
52
+ ];
53
+ function AgentOrbSpecimen() {
54
+ const [replay, setReplay] = useState(0);
55
+ const [state, setState] = useState("waiting");
56
+ return (_jsxs("div", { className: "grid gap-6", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [ORB_STATES.map((option) => (_jsx(Button, { variant: state === option ? "default" : "outline", size: "sm", "aria-pressed": state === option, onClick: () => setState(option), children: option }, option))), _jsx(Button, { variant: "outline", size: "sm", onClick: () => setReplay((value) => value + 1), children: "Replay motion" })] }), _jsx("div", { className: "flex flex-wrap items-end gap-6", children: ORB_STATES.map((status) => (_jsxs("div", { className: "grid justify-items-center gap-2 text-sm", children: [_jsx(AgentOrb, { form: "nadia", label: `Nadia, ${status}`, size: 96, state: status, motionKey: replay }), _jsx("span", { children: status })] }, status))) }), _jsx("div", { className: "flex flex-wrap items-end gap-6", children: [20, 24, 32, 40, 64, 96, 160].map((size) => (_jsxs("div", { className: "grid justify-items-center gap-2 text-sm", children: [_jsx(AgentOrb, { form: "nadia", label: `Nadia, ${state}, ${size} px`, size: size, state: state, motionKey: replay }), _jsxs("span", { children: [size, " px"] })] }, size))) }), _jsx("div", { className: "flex flex-wrap items-end gap-6", children: ["sofia", "ribbon", "lattice", "orbit", "globe", "network", "braid", "morph"].map((form) => (_jsx(AgentOrb, { form: form, label: `${form}, ${state}`, color: form === "sofia" ? undefined : COBALTO, size: 64, state: state, motionKey: replay }, form))) })] }));
57
+ }
44
58
  export const STATUS = {
45
59
  ActionStatus: ActionStatusSpecimen,
46
- AgentOrb: () => (_jsxs("div", { className: "flex flex-wrap items-end gap-6", children: [_jsx(AgentOrb, { color: COBALTO, form: "orbit", label: "Invoices, working", size: 64, state: "working" }), _jsx(AgentOrb, { color: VERDE, form: "lattice", label: "Orders, waiting for you", size: 64, state: "waiting" }), _jsx(AgentOrb, { color: ARANCIO, form: "braid", label: "Support", size: 32 }), _jsx(AgentOrb, { form: "nadia", label: "Nadia", size: 96 })] })),
60
+ AgentOrb: AgentOrbSpecimen,
47
61
  AppMark: () => (_jsxs("div", { className: "flex flex-wrap items-center gap-3 text-sm", children: [_jsx(AppMark, { app: "gmail", logo: composioLogo("gmail") }), "Gmail", _jsx(AppMark, { app: "telegram", platform: "telegram" }), "Telegram", _jsx(AppMark, { app: "Fatture in Cloud" }), "Fatture in Cloud"] })),
48
62
  ArchIcon: () => (_jsxs("div", { className: "flex flex-wrap items-center gap-4 text-sm", children: [_jsxs("span", { className: "flex items-center gap-2", children: [_jsx(ArchIcon, { "aria-hidden": "true", className: "size-5" }), "Company"] }), _jsxs("span", { className: "flex items-center gap-2", children: [_jsx(ApprovalsIcon, { "aria-hidden": "true", className: "size-5" }), "Approvals"] }), _jsxs("span", { className: "flex items-center gap-2", children: [_jsx(AgentIcon, { "aria-hidden": "true", className: "size-5" }), "Agents"] })] })),
49
63
  Avatar: () => (_jsxs("div", { className: "grid gap-6", children: [_jsx("div", { className: "flex flex-wrap items-center gap-4", children: ["xs", "sm", "default", "lg"].map((size) => (_jsx(Avatar, { label: "Ada Rossi", size: size, children: _jsx(AvatarFallback, { children: size === "xs" ? "A" : "AR" }) }, size))) }), _jsxs("div", { className: "flex flex-wrap items-center gap-6", children: [_jsx(AvatarGroup, { label: "5 members", limit: 3, members: MEMBERS }), _jsx(AvatarGroup, { label: "5 members, last on top", limit: 3, members: MEMBERS, reverse: true }), _jsx(AvatarGroup, { label: "5 members, tight", limit: 3, members: MEMBERS, overlap: 6 })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-6 text-sm", children: [_jsxs("span", { className: "flex items-center gap-2", children: [_jsx(AvatarWithIcon, { icon: _jsx(Check, {}), iconBackground: true, label: "Ada Rossi, approved", name: "Ada Rossi" }), "Ada Rossi"] }), _jsxs("span", { className: "flex items-center gap-2", children: [_jsx(AvatarWithIcon, { icon: _jsx(Pause, {}), iconBackground: true, label: "Marco Bianchi, paused", name: "Marco Bianchi" }), "Marco Bianchi"] }), _jsx(AvatarWithIcon, { icon: _jsx(Clock, {}), iconBackground: true, label: "Luca Conti, waiting for approval", name: "Luca Conti" })] })] })),