@nadicodeai/ui 12.1.136 → 12.1.137

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.
@@ -35,7 +35,9 @@ export type AgentOrbProps = AgentOrbBaseProps & ({
35
35
  * @family Status
36
36
  * @when An Agent, anywhere: 20 in the sidebar, 32 in a row, 64 on her Profile card, 96 in an Agent
37
37
  * card, 160 on entry. There is no other Agent picture. The sizes are 20, 24, 32, 40, 64, 96 and
38
- * 160 px, and no other. On a photograph the orb takes the stronger `presence="over-picture"`
38
+ * 160 px, and no other. Detail follows the size the orb is drawn at: up to 32 px it draws the
39
+ * engine's lightest dot profile, at 40 px the middle one and from 64 px the full one, since a small
40
+ * orb cannot show the full profile's dots apart. On a photograph the orb takes the stronger `presence="over-picture"`
39
41
  * weight; everywhere else it is `plain`.
40
42
  * @instead A person is an `Avatar`. The system's own wait is the waiting line, never an orb. An orb
41
43
  * in artwork, such as a sign-in picture, takes `motion="ambient"` and claims no activity. An orb
@@ -47,6 +49,8 @@ export type AgentOrbProps = AgentOrbBaseProps & ({
47
49
  * Done celebrates once over 4.8 s, then rests at 72% of native particle speed with the Idle breath.
48
50
  * A state change interrupts the previous gesture; `motionKey` replays it and `paused` freezes it. A surface that only knows a machine checked in draws idle. No state draws
49
51
  * a circle around the orb. Ambient motion moves the form at its own pace with no state pose or cue.
52
+ * An orb at rest (idle, ambient, or done after its celebration) paints 15 frames a second; every
53
+ * other state, gesture and the celebration paint 30. The pace is the same at either rate.
50
54
  * Reduced motion: movement stops, a thinking or working orb breathes between full ink and 0.45
51
55
  * opacity so busy still reads as busy, an ambient orb holds still, and waiting and blocked keep
52
56
  * their centered signs without an entrance gesture.
@@ -1 +1 @@
1
- {"version":3,"file":"agent-orb.d.ts","sourceRoot":"","sources":["../../src/components/agent-orb.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,aAAa,EAAqB,MAAM,OAAO,CAAC;AAE9D,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EAElB,MAAM,uBAAuB,CAAC;AAG/B,YAAY,EACV,cAAc,EACd,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,YAAY,GACb,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,YAAY,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE,KAAK,iBAAiB,GAAG;IACvB,4FAA4F;IAC5F,IAAI,EAAE,cAAc,CAAC;IACrB,wFAAwF;IACxF,KAAK,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IAC/B,qEAAqE;IACrE,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,wFAAwF;IACxF,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,iBAAiB,GAC3C,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,GACrF,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;AAEhF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,wBAAgB,QAAQ,CAAC,EACvB,IAAI,EACJ,KAAK,EACL,IAAS,EACT,QAAkB,EAClB,MAAc,EACd,KAAc,EACd,MAAiB,EACjB,SAAa,EACb,UAAkB,EAClB,KAAK,EACL,SAAS,GACV,EAAE,aAAa,+BAyDf"}
1
+ {"version":3,"file":"agent-orb.d.ts","sourceRoot":"","sources":["../../src/components/agent-orb.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,aAAa,EAAqB,MAAM,OAAO,CAAC;AAE9D,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EAElB,MAAM,uBAAuB,CAAC;AAG/B,YAAY,EACV,cAAc,EACd,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,YAAY,GACb,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,YAAY,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE,KAAK,iBAAiB,GAAG;IACvB,4FAA4F;IAC5F,IAAI,EAAE,cAAc,CAAC;IACrB,wFAAwF;IACxF,KAAK,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IAC/B,qEAAqE;IACrE,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,wFAAwF;IACxF,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,iBAAiB,GAC3C,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,GACrF,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;AAEhF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,wBAAgB,QAAQ,CAAC,EACvB,IAAI,EACJ,KAAK,EACL,IAAS,EACT,QAAkB,EAClB,MAAc,EACd,KAAc,EACd,MAAiB,EACjB,SAAa,EACb,UAAkB,EAClB,KAAK,EACL,SAAS,GACV,EAAE,aAAa,+BAyDf"}
@@ -9,7 +9,9 @@ import { cn } from "../lib/utils.js";
9
9
  * @family Status
10
10
  * @when An Agent, anywhere: 20 in the sidebar, 32 in a row, 64 on her Profile card, 96 in an Agent
11
11
  * card, 160 on entry. There is no other Agent picture. The sizes are 20, 24, 32, 40, 64, 96 and
12
- * 160 px, and no other. On a photograph the orb takes the stronger `presence="over-picture"`
12
+ * 160 px, and no other. Detail follows the size the orb is drawn at: up to 32 px it draws the
13
+ * engine's lightest dot profile, at 40 px the middle one and from 64 px the full one, since a small
14
+ * orb cannot show the full profile's dots apart. On a photograph the orb takes the stronger `presence="over-picture"`
13
15
  * weight; everywhere else it is `plain`.
14
16
  * @instead A person is an `Avatar`. The system's own wait is the waiting line, never an orb. An orb
15
17
  * in artwork, such as a sign-in picture, takes `motion="ambient"` and claims no activity. An orb
@@ -21,6 +23,8 @@ import { cn } from "../lib/utils.js";
21
23
  * Done celebrates once over 4.8 s, then rests at 72% of native particle speed with the Idle breath.
22
24
  * A state change interrupts the previous gesture; `motionKey` replays it and `paused` freezes it. A surface that only knows a machine checked in draws idle. No state draws
23
25
  * a circle around the orb. Ambient motion moves the form at its own pace with no state pose or cue.
26
+ * An orb at rest (idle, ambient, or done after its celebration) paints 15 frames a second; every
27
+ * other state, gesture and the celebration paint 30. The pace is the same at either rate.
24
28
  * Reduced motion: movement stops, a thinking or working orb breathes between full ink and 0.45
25
29
  * opacity so busy still reads as busy, an ambient orb holds still, and waiting and blocked keep
26
30
  * their centered signs without an entrance gesture.
@@ -1,6 +1,14 @@
1
+ import * as webgpu from "vgpu";
2
+ import type { Gpu, Surface, Target } from "vgpu";
1
3
  import { type FoilColors } from "./scene.js";
4
+ type Webgpu = Pick<typeof webgpu, "clock" | "frameLoop" | "init"> & {
5
+ surface: (gpu: Gpu, canvas: HTMLCanvasElement, options: {
6
+ dpr: [number, number];
7
+ }) => Target & Pick<Surface, "onResize">;
8
+ };
2
9
  type RendererOptions = {
3
10
  colors: FoilColors;
11
+ vgpu?: Webgpu;
4
12
  /** The card's current turn in radians, for the page to turn the card itself. */
5
13
  onTilt: (tiltX: number, tiltY: number) => void;
6
14
  };
@@ -1 +1 @@
1
- {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../../src/components/company-card/renderer.ts"],"names":[],"mappings":"AAQA,OAAO,EAAe,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAEvD,KAAK,eAAe,GAAG;IACrB,MAAM,EAAE,UAAU,CAAC;IACnB,gFAAgF;IAChF,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAChD,CAAC;AAEF,wBAAgB,cAAc,CAAC,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,eAAe;;;;EA+HjF"}
1
+ {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../../src/components/company-card/renderer.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,MAAM,MAAM,CAAC;AAC/B,OAAO,KAAK,EAA0B,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEzE,OAAO,EAAe,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AASvD,KAAK,MAAM,GAAG,IAAI,CAAC,OAAO,MAAM,EAAE,OAAO,GAAG,WAAW,GAAG,MAAM,CAAC,GAAG;IAClE,OAAO,EAAE,CACP,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE;QAAE,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,KAC/B,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;CACzC,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gFAAgF;IAChF,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAChD,CAAC;AAEF,wBAAgB,cAAc,CAAC,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,eAAe;;;;EA+LjF"}
@@ -1,14 +1,18 @@
1
1
  // nadicode: vgpu's holographic card renderer (MIT, https://github.com/vercel-labs/vgpu,
2
- // examples/holographic-card, revision 6fa27bb4), with its WebGPU set-up, frame
3
- // loop and cleanup as they are. The pointer is read on the whole page, as the
4
- // example reads it on its whole stage, so the foil wakes as the pointer comes
5
- // within one card width; the turn it computes is handed to the page, which
6
- // turns the card and its words together.
7
- import { clock, frameLoop, init, surface } from "vgpu";
2
+ // examples/holographic-card, revision 6fa27bb4), with its WebGPU set-up and
3
+ // cleanup as they are.
4
+ import * as webgpu from "vgpu";
8
5
  import { approach, SWEEP_MS, SWEEP_TURN } from "./approach.js";
9
6
  import { createScene } from "./scene.js";
7
+ const AT_REST = 1e-4;
8
+ /**
9
+ * The first frame after the loop starts again counts as one frame of the
10
+ * 60 fps it runs at, not the time it was stopped.
11
+ */
12
+ const FRAME_SECONDS = 1 / 60;
10
13
  export function createRenderer(canvas, options) {
11
- const { colors, onTilt } = options;
14
+ const { colors, onTilt, vgpu = webgpu } = options;
15
+ const { clock, frameLoop, init, surface } = vgpu;
12
16
  let disposed = false;
13
17
  let gpu;
14
18
  let removeInput = () => { };
@@ -33,6 +37,7 @@ export function createRenderer(canvas, options) {
33
37
  if (disposed)
34
38
  return;
35
39
  const motion = window.matchMedia("(prefers-reduced-motion: reduce)");
40
+ const time = clock(context);
36
41
  let targetHover = 0;
37
42
  let pointerX = 0;
38
43
  let pointerY = 0;
@@ -46,52 +51,32 @@ export function createRenderer(canvas, options) {
46
51
  // from its top-left corner to its bottom-right, then rests. It tells a
47
52
  // person at a glance that this is a card that answers the pointer.
48
53
  let sweepStart = null;
49
- // Under reduced motion the card keeps still: the light moves only with the pointer.
50
- requestSweep = () => {
51
- if (!motion.matches)
52
- sweepStart = performance.now();
53
- };
54
- const move = (event) => {
55
- if (!event.isPrimary)
56
- return;
57
- const near = approach(canvas.getBoundingClientRect(), event.clientX, event.clientY);
58
- // A pointer elsewhere on the page leaves a sweep to finish; one that
59
- // comes near takes the card over.
60
- if (near.hover > 0)
61
- sweepStart = null;
62
- if (sweepStart !== null)
63
- return;
64
- pointerX = near.x;
65
- pointerY = near.y;
66
- targetHover = near.hover;
67
- };
68
- const leave = () => {
69
- if (sweepStart === null)
70
- targetHover = 0;
71
- };
72
- const up = (event) => {
73
- if (event.pointerType !== "mouse")
74
- leave();
75
- };
76
- const root = document.documentElement;
77
- window.addEventListener("pointermove", move, { passive: true });
78
- window.addEventListener("pointerdown", move, { passive: true });
79
- window.addEventListener("pointercancel", leave);
80
- window.addEventListener("pointerup", up);
81
- root.addEventListener("pointerleave", leave);
82
- const unsubscribeResize = output.onResize(() => {
83
- shader.set({ params: { resolution: output.size } });
84
- });
85
- removeInput = () => {
86
- window.removeEventListener("pointermove", move);
87
- window.removeEventListener("pointerdown", move);
88
- window.removeEventListener("pointercancel", leave);
89
- window.removeEventListener("pointerup", up);
90
- root.removeEventListener("pointerleave", leave);
91
- unsubscribeResize();
54
+ let pointer = null;
55
+ let needsDraw = true;
56
+ let onScreen = false;
57
+ let loop = null;
58
+ const stop = () => {
59
+ loop?.stop();
60
+ loop = null;
92
61
  };
93
- const time = clock(context);
94
- frameLoop(context, (currentFrame) => {
62
+ const settled = (targetX, targetY, within) => sweepStart === null &&
63
+ Math.abs(targetX - tiltX) <= within &&
64
+ Math.abs(targetY - tiltY) <= within &&
65
+ Math.abs(targetHover - hover) <= within &&
66
+ (targetHover === 0 ||
67
+ (Math.abs(pointerX - lightX) <= within && Math.abs(pointerY - lightY) <= within));
68
+ const draw = (currentFrame) => {
69
+ if (pointer) {
70
+ const near = approach(canvas.getBoundingClientRect(), pointer.clientX, pointer.clientY);
71
+ pointer = null;
72
+ if (near.hover > 0)
73
+ sweepStart = null;
74
+ if (sweepStart === null) {
75
+ pointerX = near.x;
76
+ pointerY = near.y;
77
+ targetHover = near.hover;
78
+ }
79
+ }
95
80
  let turn = null;
96
81
  if (sweepStart !== null) {
97
82
  const progress = (performance.now() - sweepStart) / SWEEP_MS;
@@ -116,6 +101,11 @@ export function createRenderer(canvas, options) {
116
101
  const targetY = motion.matches
117
102
  ? 0
118
103
  : (turn?.[1] ?? -Math.max(-1, Math.min(1, pointerY)) * 0.12 * targetHover);
104
+ if (!needsDraw && settled(targetX, targetY, 0)) {
105
+ currentFrame.cancel();
106
+ stop();
107
+ return;
108
+ }
119
109
  const blend = motion.matches ? 1 : 1 - Math.exp(-10 * Math.min(time.deltaTime, 0.1));
120
110
  tiltX += (targetX - tiltX) * blend;
121
111
  tiltY += (targetY - tiltY) * blend;
@@ -124,10 +114,89 @@ export function createRenderer(canvas, options) {
124
114
  lightX += (pointerX - lightX) * blend;
125
115
  lightY += (pointerY - lightY) * blend;
126
116
  }
117
+ const resting = settled(targetX, targetY, AT_REST);
118
+ if (resting) {
119
+ tiltX = targetX;
120
+ tiltY = targetY;
121
+ hover = targetHover;
122
+ if (targetHover > 0) {
123
+ lightX = pointerX;
124
+ lightY = pointerY;
125
+ }
126
+ }
127
127
  onTilt(tiltX, tiltY);
128
128
  shader.set({ params: { tilt: [tiltX, tiltY], pointer: [lightX, lightY], hover } });
129
129
  currentFrame.pass(output, shader);
130
- }, { fps: 60 });
130
+ needsDraw = false;
131
+ if (resting)
132
+ stop();
133
+ };
134
+ const play = () => {
135
+ if (disposed || !onScreen || document.hidden) {
136
+ stop();
137
+ return;
138
+ }
139
+ if (loop)
140
+ return;
141
+ time.advance(FRAME_SECONDS);
142
+ loop = frameLoop(context, draw, { fps: 60 });
143
+ };
144
+ // Under reduced motion the card keeps still: the light moves only with the pointer.
145
+ requestSweep = () => {
146
+ if (motion.matches)
147
+ return;
148
+ sweepStart = performance.now();
149
+ play();
150
+ };
151
+ const move = (event) => {
152
+ if (!event.isPrimary)
153
+ return;
154
+ pointer = { clientX: event.clientX, clientY: event.clientY };
155
+ play();
156
+ };
157
+ const leave = () => {
158
+ pointer = null;
159
+ if (sweepStart === null)
160
+ targetHover = 0;
161
+ play();
162
+ };
163
+ const up = (event) => {
164
+ if (event.pointerType !== "mouse")
165
+ leave();
166
+ };
167
+ const root = document.documentElement;
168
+ window.addEventListener("pointermove", move, { passive: true });
169
+ window.addEventListener("pointerdown", move, { passive: true });
170
+ window.addEventListener("pointercancel", leave);
171
+ window.addEventListener("pointerup", up);
172
+ root.addEventListener("pointerleave", leave);
173
+ document.addEventListener("visibilitychange", play);
174
+ motion.addEventListener("change", play);
175
+ // vgpu sizes the canvas on a frame, so a card whose box changes takes one to be drawn again.
176
+ const unsubscribeResize = output.onResize(() => {
177
+ shader.set({ params: { resolution: output.size } });
178
+ needsDraw = true;
179
+ });
180
+ const box = new ResizeObserver(play);
181
+ box.observe(canvas);
182
+ const visibility = new IntersectionObserver((entries) => {
183
+ onScreen = entries.at(-1)?.isIntersecting ?? false;
184
+ play();
185
+ });
186
+ visibility.observe(canvas);
187
+ removeInput = () => {
188
+ stop();
189
+ window.removeEventListener("pointermove", move);
190
+ window.removeEventListener("pointerdown", move);
191
+ window.removeEventListener("pointercancel", leave);
192
+ window.removeEventListener("pointerup", up);
193
+ root.removeEventListener("pointerleave", leave);
194
+ document.removeEventListener("visibilitychange", play);
195
+ motion.removeEventListener("change", play);
196
+ unsubscribeResize();
197
+ box.disconnect();
198
+ visibility.disconnect();
199
+ };
131
200
  })().catch((cause) => {
132
201
  if (disposed)
133
202
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"company-card.d.ts","sourceRoot":"","sources":["../../src/components/company-card.tsx"],"names":[],"mappings":"AASA,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,SAAS,CAAC;AAEvD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;AAqBF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,MAAM,EACN,OAAO,EACP,MAAM,EACN,MAAM,EACN,IAAI,EACJ,WAAmB,GACpB,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,kBAAkB,CAAC;IAC5B,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,+BAiSA"}
1
+ {"version":3,"file":"company-card.d.ts","sourceRoot":"","sources":["../../src/components/company-card.tsx"],"names":[],"mappings":"AASA,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,SAAS,CAAC;AAEvD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;AA0BF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,MAAM,EACN,OAAO,EACP,MAAM,EACN,MAAM,EACN,IAAI,EACJ,WAAmB,GACpB,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,kBAAkB,CAAC;IAC5B,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,+BAsTA"}
@@ -17,6 +17,10 @@ const FOIL = {
17
17
  const STRIPE = 34;
18
18
  /** How much of the card must be on screen before it turns, when the caller waits for it. */
19
19
  const IN_VIEW = 0.6;
20
+ function setChanged(element, property, value) {
21
+ if (element.style.getPropertyValue(property) !== value)
22
+ element.style.setProperty(property, value);
23
+ }
20
24
  function reason(cause) {
21
25
  return cause instanceof Error ? cause.message : String(cause);
22
26
  }
@@ -67,7 +71,6 @@ export function CompanyCard({ name, domain, edition, issued, labels, logo, sweep
67
71
  const cardRef = useRef(null);
68
72
  const lightRef = useRef(null);
69
73
  const stripesRef = useRef(null);
70
- const frame = useRef(0);
71
74
  const canvasRef = useRef(null);
72
75
  const [gpuFoil, setGpuFoil] = useState(false);
73
76
  const sweepRef = useRef(() => { });
@@ -102,8 +105,8 @@ export function CompanyCard({ name, domain, edition, issued, labels, logo, sweep
102
105
  const created = createRenderer(canvas, {
103
106
  colors: foilColors(card, edition),
104
107
  onTilt: (tiltX, tiltY) => {
105
- card.style.setProperty("--tilt-x", `${tiltY.toFixed(4)}rad`);
106
- card.style.setProperty("--tilt-y", `${tiltX.toFixed(4)}rad`);
108
+ setChanged(card, "--tilt-x", `${tiltY.toFixed(4)}rad`);
109
+ setChanged(card, "--tilt-y", `${tiltX.toFixed(4)}rad`);
107
110
  },
108
111
  });
109
112
  renderer = created;
@@ -130,16 +133,17 @@ export function CompanyCard({ name, domain, edition, issued, labels, logo, sweep
130
133
  setGpuFoil(false);
131
134
  };
132
135
  }, [edition, seen]);
133
- // The SVG foil behaves as the shader does: it turns once in the light when it
134
- // appears and when it is issued, and wakes as the pointer comes within one
135
- // card width, easing its turn by how near the pointer is.
136
136
  useEffect(() => {
137
137
  const card = cardRef.current;
138
138
  if (!seen || gpuFoil || !card)
139
139
  return;
140
140
  const motion = window.matchMedia("(prefers-reduced-motion: reduce)");
141
+ let onScreen = typeof IntersectionObserver === "undefined";
142
+ let sweepStart = null;
141
143
  let sweepFrame = 0;
142
- let sweeping = false;
144
+ let pointer = null;
145
+ let pointerFrame = 0;
146
+ const playing = () => onScreen && !document.hidden;
143
147
  const light = (x, y) => {
144
148
  lightRef.current?.setAttribute("cx", x.toFixed(3));
145
149
  lightRef.current?.setAttribute("cy", y.toFixed(3));
@@ -162,59 +166,86 @@ export function CompanyCard({ name, domain, edition, issued, labels, logo, sweep
162
166
  }
163
167
  };
164
168
  const stopSweep = () => {
165
- sweeping = false;
169
+ sweepStart = null;
166
170
  cancelAnimationFrame(sweepFrame);
167
171
  };
172
+ const step = (now) => {
173
+ if (sweepStart === null)
174
+ return;
175
+ const progress = Math.min(1, (now - sweepStart) / SWEEP_MS);
176
+ const along = progress * progress * (3 - 2 * progress);
177
+ const rise = Math.sin(Math.PI * progress);
178
+ show(-0.05 + along * 1.1, 0.22 + along * 0.56, rise ** 0.6 * 0.95, rise * MAX_TILT_DEGREES * 0.3, -Math.sin(2 * Math.PI * along) * ((SWEEP_TURN * 180) / Math.PI));
179
+ if (progress < 1)
180
+ sweepFrame = requestAnimationFrame(step);
181
+ else {
182
+ sweepStart = null;
183
+ rest();
184
+ }
185
+ };
186
+ const follow = () => {
187
+ const at = pointer;
188
+ pointer = null;
189
+ if (!at)
190
+ return;
191
+ const near = approach(card.getBoundingClientRect(), at.clientX, at.clientY);
192
+ if (near.hover > 0)
193
+ stopSweep();
194
+ if (sweepStart !== null)
195
+ return;
196
+ if (near.hover === 0)
197
+ return rest();
198
+ const x = Math.min(1, Math.max(0, (near.x + 1) / 2));
199
+ const y = Math.min(1, Math.max(0, (near.y + 1) / 2));
200
+ show(x, y, near.hover, (0.5 - y) * 2 * MAX_TILT_DEGREES * near.hover, (x - 0.5) * 2 * MAX_TILT_DEGREES * near.hover);
201
+ };
202
+ const play = () => {
203
+ cancelAnimationFrame(sweepFrame);
204
+ cancelAnimationFrame(pointerFrame);
205
+ if (!playing())
206
+ return;
207
+ if (sweepStart !== null)
208
+ sweepFrame = requestAnimationFrame(step);
209
+ if (pointer)
210
+ pointerFrame = requestAnimationFrame(follow);
211
+ };
168
212
  const sweep = () => {
169
213
  if (motion.matches)
170
214
  return;
171
- stopSweep();
172
- sweeping = true;
173
- const start = performance.now();
174
- const step = (now) => {
175
- const progress = Math.min(1, (now - start) / SWEEP_MS);
176
- const along = progress * progress * (3 - 2 * progress);
177
- const rise = Math.sin(Math.PI * progress);
178
- show(-0.05 + along * 1.1, 0.22 + along * 0.56, rise ** 0.6 * 0.95, rise * MAX_TILT_DEGREES * 0.3, -Math.sin(2 * Math.PI * along) * ((SWEEP_TURN * 180) / Math.PI));
179
- if (progress < 1)
180
- sweepFrame = requestAnimationFrame(step);
181
- else {
182
- sweeping = false;
183
- rest();
184
- }
185
- };
186
- sweepFrame = requestAnimationFrame(step);
215
+ sweepStart = performance.now();
216
+ play();
187
217
  };
188
218
  const move = (event) => {
189
219
  if (!event.isPrimary)
190
220
  return;
191
- const near = approach(card.getBoundingClientRect(), event.clientX, event.clientY);
192
- if (near.hover > 0)
193
- stopSweep();
194
- if (sweeping)
195
- return;
196
- cancelAnimationFrame(frame.current);
197
- frame.current = requestAnimationFrame(() => {
198
- if (near.hover === 0)
199
- return rest();
200
- const x = Math.min(1, Math.max(0, (near.x + 1) / 2));
201
- const y = Math.min(1, Math.max(0, (near.y + 1) / 2));
202
- show(x, y, near.hover, (0.5 - y) * 2 * MAX_TILT_DEGREES * near.hover, (x - 0.5) * 2 * MAX_TILT_DEGREES * near.hover);
203
- });
221
+ pointer = { clientX: event.clientX, clientY: event.clientY };
222
+ cancelAnimationFrame(pointerFrame);
223
+ if (playing())
224
+ pointerFrame = requestAnimationFrame(follow);
204
225
  };
205
226
  const leave = () => {
206
- if (!sweeping)
227
+ if (sweepStart === null)
207
228
  rest();
208
229
  };
209
230
  window.addEventListener("pointermove", move, { passive: true });
210
231
  document.documentElement.addEventListener("pointerleave", leave);
232
+ document.addEventListener("visibilitychange", play);
233
+ const visibility = onScreen
234
+ ? undefined
235
+ : new IntersectionObserver((entries) => {
236
+ onScreen = entries.at(-1)?.isIntersecting ?? false;
237
+ play();
238
+ });
239
+ visibility?.observe(card);
211
240
  sweepRef.current = sweep;
212
241
  sweep();
213
242
  return () => {
214
243
  window.removeEventListener("pointermove", move);
215
244
  document.documentElement.removeEventListener("pointerleave", leave);
245
+ document.removeEventListener("visibilitychange", play);
246
+ visibility?.disconnect();
216
247
  stopSweep();
217
- cancelAnimationFrame(frame.current);
248
+ cancelAnimationFrame(pointerFrame);
218
249
  sweepRef.current = () => { };
219
250
  rest();
220
251
  };
@@ -1 +1 @@
1
- {"version":3,"file":"hive-scene.d.ts","sourceRoot":"","sources":["../../src/components/hive-scene.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,MAAM,MAAM,SAAS,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjD,mFAAmF;AACnF,MAAM,MAAM,QAAQ,GAAG;IACrB,OAAO,EAAE,SAAS,SAAS,EAAE,CAAC;IAC9B,SAAS,EAAE,SAAS,SAAS,EAAE,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;AAEzD,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,OAAO,CAAC;AAElD,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,GAAG;IAC3E,IAAI,EAAE,aAAa,CAAC;IACpB,qFAAqF;IACrF,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,kFAAkF;IAClF,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACrC,6DAA6D;IAC7D,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,mFAAmF;IACnF,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,QAAQ,CAAC;IAC7D,iEAAiE;IACjE,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,CAAC;AAmLF;;;;;;;;;;;;;GAaG;AACH,iBAAS,SAAS,CAAC,EACjB,IAAI,EACJ,MAAkB,EAClB,IAAI,EACJ,OAAO,EACP,IAAI,EACJ,MAAM,EACN,KAAK,EACL,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,cAAc,qBAsNhB;AAED,2DAA2D;AAC3D,iBAAS,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,qBAM9E;AAED,iEAAiE;AACjE,iBAAS,QAAQ,CAAC,EAChB,QAAQ,EACR,EAAE,EACF,IAAa,GACd,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACzB,qBAMA;AAED,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC"}
1
+ {"version":3,"file":"hive-scene.d.ts","sourceRoot":"","sources":["../../src/components/hive-scene.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,MAAM,MAAM,SAAS,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjD,mFAAmF;AACnF,MAAM,MAAM,QAAQ,GAAG;IACrB,OAAO,EAAE,SAAS,SAAS,EAAE,CAAC;IAC9B,SAAS,EAAE,SAAS,SAAS,EAAE,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;AAEzD,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,OAAO,CAAC;AAElD,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,GAAG;IAC3E,IAAI,EAAE,aAAa,CAAC;IACpB,qFAAqF;IACrF,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,kFAAkF;IAClF,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACrC,6DAA6D;IAC7D,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,mFAAmF;IACnF,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,QAAQ,CAAC;IAC7D,iEAAiE;IACjE,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,CAAC;AAmLF;;;;;;;;;;;;;GAaG;AACH,iBAAS,SAAS,CAAC,EACjB,IAAI,EACJ,MAAkB,EAClB,IAAI,EACJ,OAAO,EACP,IAAI,EACJ,MAAM,EACN,KAAK,EACL,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,cAAc,qBA8PhB;AAED,2DAA2D;AAC3D,iBAAS,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,qBAM9E;AAED,iEAAiE;AACjE,iBAAS,QAAQ,CAAC,EAChB,QAAQ,EACR,EAAE,EACF,IAAa,GACd,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACzB,qBAMA;AAED,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC"}
@@ -239,18 +239,25 @@ function HiveScene({ mode, column = "default", logo, picture, work, chrome, labe
239
239
  drawComb(comb, width, height);
240
240
  context = sizeCanvas(flow, width, height);
241
241
  };
242
+ const observeLayout = (onLayout) => {
243
+ const resize = new ResizeObserver(onLayout);
244
+ resize.observe(scene);
245
+ if (wordsColumn)
246
+ resize.observe(wordsColumn);
247
+ return resize;
248
+ };
242
249
  place();
243
- const resize = new ResizeObserver(place);
244
- resize.observe(scene);
245
- if (wordsColumn)
246
- resize.observe(wordsColumn);
247
- if (still)
250
+ if (still) {
251
+ const resize = observeLayout(place);
248
252
  return () => resize.disconnect();
253
+ }
249
254
  const colours = orbRefs.current.map((canvas) => canvas ? getComputedStyle(canvas).color : "white");
250
255
  const particles = [];
251
256
  let frame = 0;
252
- let last = performance.now();
257
+ let last = null;
253
258
  let nextSpawn = 0;
259
+ // A browser that cannot tell whether the scene is seen shows it as seen.
260
+ let onScreen = typeof IntersectionObserver === "undefined";
254
261
  const spawn = () => {
255
262
  const source = Math.floor(Math.random() * AGENTS.length);
256
263
  const seat = seats[source];
@@ -287,8 +294,11 @@ function HiveScene({ mode, column = "default", logo, picture, work, chrome, labe
287
294
  trail: [],
288
295
  });
289
296
  };
297
+ const flowing = () => context !== null && !reduced.matches && seats.some((seat) => seat !== null);
298
+ const running = () => onScreen && !document.hidden && (particles.length > 0 || flowing());
290
299
  const tick = (now) => {
291
- const dt = Math.min(0.05, (now - last) / 1000);
300
+ frame = 0;
301
+ const dt = last === null ? 0 : Math.min(0.05, (now - last) / 1000);
292
302
  last = now;
293
303
  nextSpawn -= dt;
294
304
  const paint = context;
@@ -322,12 +332,40 @@ function HiveScene({ mode, column = "default", logo, picture, work, chrome, labe
322
332
  }
323
333
  paint.globalAlpha = 1;
324
334
  }
335
+ if (running())
336
+ frame = requestAnimationFrame(tick);
337
+ };
338
+ const play = () => {
339
+ if (!running()) {
340
+ cancelAnimationFrame(frame);
341
+ frame = 0;
342
+ return;
343
+ }
344
+ if (frame)
345
+ return;
346
+ last = null;
325
347
  frame = requestAnimationFrame(tick);
326
348
  };
327
- frame = requestAnimationFrame(tick);
349
+ const resize = observeLayout(() => {
350
+ place();
351
+ play();
352
+ });
353
+ const visibility = onScreen
354
+ ? undefined
355
+ : new IntersectionObserver((entries) => {
356
+ onScreen = entries.at(-1)?.isIntersecting ?? false;
357
+ play();
358
+ });
359
+ visibility?.observe(scene);
360
+ document.addEventListener("visibilitychange", play);
361
+ reduced.addEventListener("change", play);
362
+ play();
328
363
  return () => {
329
364
  cancelAnimationFrame(frame);
330
365
  resize.disconnect();
366
+ visibility?.disconnect();
367
+ document.removeEventListener("visibilitychange", play);
368
+ reduced.removeEventListener("change", play);
331
369
  };
332
370
  }, [mode, still, wordsColumn]);
333
371
  return (_jsxs("div", { ...props, className: cn("nc-hive dark", className), "data-mode": mode, "data-slot": "hive-scene", ref: sceneRef, ...(mode === "loading" ? { "aria-label": label, role: "status" } : {}), children: [chrome, _jsx("img", { alt: logo.label, className: "nc-hive-logo", "data-slot": "hive-logo", src: logo.src }), picture, _jsx("canvas", { "aria-hidden": "true", className: "nc-hive-comb", ref: combRef }), _jsx("canvas", { "aria-hidden": "true", className: "nc-hive-flow", ref: flowRef }), still
@@ -6,9 +6,9 @@ import { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon } fro
6
6
  import { Button } from "./button.js";
7
7
  import { cn } from "../lib/utils.js";
8
8
  const viewportAxis = {
9
- y: "overflow-x-hidden! [mask-image:linear-gradient(to_bottom,transparent,black_min(2.5rem,var(--scroll-area-overflow-y-start,0px)),black_calc(100%_-_min(2.5rem,var(--scroll-area-overflow-y-end,0px))),transparent)]",
10
- x: "overflow-y-hidden! [mask-image:linear-gradient(to_right,transparent,black_min(2.5rem,var(--scroll-area-overflow-x-start,0px)),black_calc(100%_-_min(2.5rem,var(--scroll-area-overflow-x-end,0px))),transparent)]",
11
- both: "[mask-composite:intersect] [mask-image:linear-gradient(to_bottom,transparent,black_min(2.5rem,var(--scroll-area-overflow-y-start,0px)),black_calc(100%_-_min(2.5rem,var(--scroll-area-overflow-y-end,0px))),transparent),linear-gradient(to_right,transparent,black_min(2.5rem,var(--scroll-area-overflow-x-start,0px)),black_calc(100%_-_min(2.5rem,var(--scroll-area-overflow-x-end,0px))),transparent)]",
9
+ y: "overflow-x-hidden! data-overflow-y-start:[mask-image:linear-gradient(to_bottom,transparent,black_2.5rem)] data-overflow-y-end:[mask-image:linear-gradient(to_top,transparent,black_2.5rem)] data-overflow-y-start:data-overflow-y-end:[mask-image:linear-gradient(to_bottom,transparent,black_2.5rem,black_calc(100%_-_2.5rem),transparent)]",
10
+ x: "overflow-y-hidden! data-overflow-x-start:[mask-image:linear-gradient(to_right,transparent,black_2.5rem)] data-overflow-x-end:[mask-image:linear-gradient(to_left,transparent,black_2.5rem)] data-overflow-x-start:data-overflow-x-end:[mask-image:linear-gradient(to_right,transparent,black_2.5rem,black_calc(100%_-_2.5rem),transparent)]",
11
+ both: "[--fade-x:linear-gradient(black,black)] [--fade-y:linear-gradient(black,black)] data-overflow-x-start:[--fade-x:linear-gradient(to_right,transparent,black_2.5rem)] data-overflow-x-end:[--fade-x:linear-gradient(to_left,transparent,black_2.5rem)] data-overflow-x-start:data-overflow-x-end:[--fade-x:linear-gradient(to_right,transparent,black_2.5rem,black_calc(100%_-_2.5rem),transparent)] data-overflow-y-start:[--fade-y:linear-gradient(to_bottom,transparent,black_2.5rem)] data-overflow-y-end:[--fade-y:linear-gradient(to_top,transparent,black_2.5rem)] data-overflow-y-start:data-overflow-y-end:[--fade-y:linear-gradient(to_bottom,transparent,black_2.5rem,black_calc(100%_-_2.5rem),transparent)] [mask-composite:intersect] [mask-image:var(--fade-y),var(--fade-x)]",
12
12
  };
13
13
  function scrollToNeighbour(viewport, content, axis, direction) {
14
14
  const edges = (box) => axis === "y" ? { start: box.top, end: box.bottom } : { start: box.left, end: box.right };
@@ -10,6 +10,10 @@ export declare function stateSpeed(state: AgentOrbState, form: AgentOrbFigure, a
10
10
  gestureAge: (age: number) => number;
11
11
  settle: (age: number, delay?: number) => number;
12
12
  }): number;
13
+ export declare const ORB_FRAME_RATE: {
14
+ readonly resting: 15;
15
+ readonly busy: 30;
16
+ };
13
17
  /**
14
18
  * Reduced motion turns movement off and keeps opacity and colour, so a
15
19
  * thinking or working orb breathes between full ink and 0.45 over 1.6 s,
@@ -1 +1 @@
1
- {"version":3,"file":"agent-orb-motion.d.ts","sourceRoot":"","sources":["../../src/internal/agent-orb-motion.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;AAc7F,wBAAgB,WAAW,CAAC,IAAI,EAAE,cAAc,UAE/C;AAED,wBAAgB,UAAU,CACxB,KAAK,EAAE,aAAa,EACpB,IAAI,EAAE,cAAc,EACpB,GAAG,SAAI,EACP,MAAM;;;;;;sBA2Cc,MAAM;kBACV,MAAM;CA5CA,UAWvB;AAKD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,UAIlE;AAED,wBAAgB,aAAa,CAAC,KAAK,CAAC,EAAE,mBAAmB;;;;;;sBAgBnC,MAAM;kBACV,MAAM;EAGvB;AAUD,wBAAgB,UAAU,CACxB,KAAK,EAAE,aAAa,EACpB,GAAG,EAAE,MAAM,EACX,MAAM;;;;;;sBAjBc,MAAM;kBACV,MAAM;CAgBA,EACtB,OAAO,UAAQ;;;;;;EA+ChB;AA6ED,wBAAgB,WAAW,CACzB,OAAO,EAAE,wBAAwB,EACjC,KAAK,EAAE,aAAa,EACpB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,KAAK,EAAE,MAAM,GAAG,OAAO;AACvB,mFAAmF;AACnF,GAAG,SAAI,EACP,MAAM;;;;;;sBAxJc,MAAM;kBACV,MAAM;CAuJA,EACtB,WAAW,SAA8B,QA4E1C"}
1
+ {"version":3,"file":"agent-orb-motion.d.ts","sourceRoot":"","sources":["../../src/internal/agent-orb-motion.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;AAc7F,wBAAgB,WAAW,CAAC,IAAI,EAAE,cAAc,UAE/C;AAED,wBAAgB,UAAU,CACxB,KAAK,EAAE,aAAa,EACpB,IAAI,EAAE,cAAc,EACpB,GAAG,SAAI,EACP,MAAM;;;;;;sBA6Cc,MAAM;kBACV,MAAM;CA9CA,UAWvB;AAED,eAAO,MAAM,cAAc;;;CAAqC,CAAC;AAKjE;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,UAIlE;AAED,wBAAgB,aAAa,CAAC,KAAK,CAAC,EAAE,mBAAmB;;;;;;sBAgBnC,MAAM;kBACV,MAAM;EAGvB;AAUD,wBAAgB,UAAU,CACxB,KAAK,EAAE,aAAa,EACpB,GAAG,EAAE,MAAM,EACX,MAAM;;;;;;sBAjBc,MAAM;kBACV,MAAM;CAgBA,EACtB,OAAO,UAAQ;;;;;;EA+ChB;AA6ED,wBAAgB,WAAW,CACzB,OAAO,EAAE,wBAAwB,EACjC,KAAK,EAAE,aAAa,EACpB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,KAAK,EAAE,MAAM,GAAG,OAAO;AACvB,mFAAmF;AACnF,GAAG,SAAI,EACP,MAAM;;;;;;sBAxJc,MAAM;kBACV,MAAM;CAuJA,EACtB,WAAW,SAA8B,QA4E1C"}
@@ -24,6 +24,7 @@ export function stateSpeed(state, form, age = 0, timing = defaultTiming) {
24
24
  };
25
25
  return speed[state] * nativeSpeed(form);
26
26
  }
27
+ export const ORB_FRAME_RATE = { resting: 15, busy: 30 };
27
28
  /** The breath's period, in seconds. */
28
29
  const BREATH_SECONDS = 1.6;
29
30
  /**
@@ -44,6 +44,7 @@ export type AgentOrbMountOptions = AgentOrbMountBase & ({
44
44
  motion: "ambient";
45
45
  state?: never;
46
46
  });
47
+ export declare const LONGEST_FRAME_SECONDS = 0.1;
47
48
  export declare function mountAgentOrb(canvas: HTMLCanvasElement, options: AgentOrbMountOptions): {
48
49
  destroy(): void;
49
50
  setPaused(_value: boolean): void;
@@ -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,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
+ {"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,EAQnB,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;AA+DlD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAuDpF;AA2ID,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;AAmIxF,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC,wBAAgB,aAAa,CAAC,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,oBAAoB;;sBAIzC,OAAO;qBAAuB,aAAa;EA2LvF"}