@divinci-ai/robot-avatar 0.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.
- package/LICENSE +21 -0
- package/README.md +52 -0
- package/dist/blend.d.ts +8 -0
- package/dist/blend.d.ts.map +1 -0
- package/dist/blend.js +17 -0
- package/dist/blend.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/render-gate.d.ts +32 -0
- package/dist/render-gate.d.ts.map +1 -0
- package/dist/render-gate.js +72 -0
- package/dist/render-gate.js.map +1 -0
- package/dist/scene/AnimatedHeart.d.ts +18 -0
- package/dist/scene/AnimatedHeart.d.ts.map +1 -0
- package/dist/scene/AnimatedHeart.js +63 -0
- package/dist/scene/AnimatedHeart.js.map +1 -0
- package/dist/scene/LogoRobot.d.ts +24 -0
- package/dist/scene/LogoRobot.d.ts.map +1 -0
- package/dist/scene/LogoRobot.js +285 -0
- package/dist/scene/LogoRobot.js.map +1 -0
- package/dist/scene/RobotScene.d.ts +43 -0
- package/dist/scene/RobotScene.d.ts.map +1 -0
- package/dist/scene/RobotScene.js +43 -0
- package/dist/scene/RobotScene.js.map +1 -0
- package/dist/scene/index.d.ts +16 -0
- package/dist/scene/index.d.ts.map +1 -0
- package/dist/scene/index.js +15 -0
- package/dist/scene/index.js.map +1 -0
- package/dist/scene/useGroupMotion.d.ts +12 -0
- package/dist/scene/useGroupMotion.d.ts.map +1 -0
- package/dist/scene/useGroupMotion.js +47 -0
- package/dist/scene/useGroupMotion.js.map +1 -0
- package/dist/scene/useRobotSignals.d.ts +28 -0
- package/dist/scene/useRobotSignals.d.ts.map +1 -0
- package/dist/scene/useRobotSignals.js +101 -0
- package/dist/scene/useRobotSignals.js.map +1 -0
- package/dist/types.d.ts +15 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/package.json +46 -0
- package/src/blend.ts +16 -0
- package/src/index.ts +14 -0
- package/src/render-gate.ts +102 -0
- package/src/scene/AnimatedHeart.tsx +95 -0
- package/src/scene/LogoRobot.tsx +361 -0
- package/src/scene/RobotScene.tsx +102 -0
- package/src/scene/index.ts +16 -0
- package/src/scene/useGroupMotion.ts +61 -0
- package/src/scene/useRobotSignals.ts +126 -0
- package/src/types.ts +16 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Divinci AI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# @divinci-ai/robot-avatar
|
|
2
|
+
|
|
3
|
+
The Divinci robot mascot — the procedural, rigged react-three-fiber avatar
|
|
4
|
+
shared by every surface that shows him:
|
|
5
|
+
|
|
6
|
+
| Surface | Wrapper |
|
|
7
|
+
|---|---|
|
|
8
|
+
| SDK docs hero (sdk.divinci.ai) | `workspace/sdk/docs/src/components/RobotHero/RobotHero.tsx` |
|
|
9
|
+
| Web app + Divinci Agent panel | `workspace/clients/web/src/components/RobotAvatar/RobotAvatar.tsx` |
|
|
10
|
+
| Chrome extension (iframe) | via the web app / a dedicated robot page (planned) |
|
|
11
|
+
|
|
12
|
+
## Entry points
|
|
13
|
+
|
|
14
|
+
- **`@divinci-ai/robot-avatar`** (light, no three.js): `AvatarState`,
|
|
15
|
+
`RobotAvatarColors`, `blendHex`, `useRenderGate`. Safe for main bundles.
|
|
16
|
+
- **`@divinci-ai/robot-avatar/scene`** (heavy): `RobotScene`, `LogoRobot`,
|
|
17
|
+
`AnimatedHeart`, `useRobotSignals`, `useGroupMotion`. **Always lazy-import**
|
|
18
|
+
so three.js stays in its own chunk:
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
import { lazy } from "react";
|
|
22
|
+
const RobotScene = lazy(() =>
|
|
23
|
+
import("@divinci-ai/robot-avatar/scene").then((m) => ({ default: m.RobotScene }))
|
|
24
|
+
);
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Built-in hardening (every consumer gets these)
|
|
28
|
+
|
|
29
|
+
- `powerPreference: "low-power"` — never spins up the discrete GPU.
|
|
30
|
+
- `failIfMajorPerformanceCaveat: true` — refuses software WebGL (SwiftShader)
|
|
31
|
+
instead of burning CPU; your error boundary shows the static fallback.
|
|
32
|
+
- `paused` prop → R3F `frameloop: "never"`. Pair with `useRenderGate` (pauses
|
|
33
|
+
when scrolled off-screen, tab hidden, or user idle).
|
|
34
|
+
- `onContextLost` callback — `webglcontextlost` does not throw through React;
|
|
35
|
+
listen here and swap to your static fallback.
|
|
36
|
+
- `prefers-reduced-motion` honored live: autonomous animation freezes to a
|
|
37
|
+
rest pose; only the gentle cursor-driven gaze lean remains.
|
|
38
|
+
- `dpr` capped at `[1, 1.5]` by default (the mascot renders small).
|
|
39
|
+
|
|
40
|
+
## Consumer responsibilities
|
|
41
|
+
|
|
42
|
+
Each wrapper owns: color resolution (theme vars vs brand constants), the
|
|
43
|
+
static fallback image (Suspense placeholder + WebGL-failure fallback +
|
|
44
|
+
context-loss swap), and any surface-specific gaze target (`speakerTarget`).
|
|
45
|
+
|
|
46
|
+
## Version skew
|
|
47
|
+
|
|
48
|
+
Compiled against the lowest consumer versions (react 18 / three 0.169 /
|
|
49
|
+
r3f 8 / drei 9); peer ranges span through the docs site's react 19 / three
|
|
50
|
+
0.185 / r3f 9 / drei 10. Only skew-stable APIs are used (`Canvas`, `useFrame`,
|
|
51
|
+
`Environment`, `Lightformer`, core three geometry/materials) — check both
|
|
52
|
+
consumers when touching imports.
|
package/dist/blend.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tiny hex-color blend used by consumers to derive the robot's palette from a
|
|
3
|
+
* theme (web client) or brand constants (docs hero). Lives in the light entry
|
|
4
|
+
* so wrappers can compute colors without touching three.js.
|
|
5
|
+
*/
|
|
6
|
+
/** Blend two hex colors; t=0 → a, t=1 → b. */
|
|
7
|
+
export declare function blendHex(a: string, b: string, t: number): string;
|
|
8
|
+
//# sourceMappingURL=blend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blend.d.ts","sourceRoot":"","sources":["../src/blend.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,8CAA8C;AAC9C,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAQhE"}
|
package/dist/blend.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tiny hex-color blend used by consumers to derive the robot's palette from a
|
|
3
|
+
* theme (web client) or brand constants (docs hero). Lives in the light entry
|
|
4
|
+
* so wrappers can compute colors without touching three.js.
|
|
5
|
+
*/
|
|
6
|
+
/** Blend two hex colors; t=0 → a, t=1 → b. */
|
|
7
|
+
export function blendHex(a, b, t) {
|
|
8
|
+
const m = a.replace("#", "").trim();
|
|
9
|
+
const n = b.replace("#", "").trim();
|
|
10
|
+
if (m.length < 6 || n.length < 6)
|
|
11
|
+
return a;
|
|
12
|
+
const ra = [parseInt(m.slice(0, 2), 16), parseInt(m.slice(2, 4), 16), parseInt(m.slice(4, 6), 16)];
|
|
13
|
+
const rb = [parseInt(n.slice(0, 2), 16), parseInt(n.slice(2, 4), 16), parseInt(n.slice(4, 6), 16)];
|
|
14
|
+
const mix = ra.map((v, i) => Math.round(v + (rb[i] - v) * t));
|
|
15
|
+
return "#" + mix.map((v) => Math.max(0, Math.min(255, v)).toString(16).padStart(2, "0")).join("");
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=blend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blend.js","sourceRoot":"","sources":["../src/blend.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,8CAA8C;AAC9C,MAAM,UAAU,QAAQ,CAAC,CAAS,EAAE,CAAS,EAAE,CAAS;IACtD,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACpC,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACpC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,CAAC;IAC3C,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IACnG,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IACnG,MAAM,GAAG,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC9D,OAAO,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACpG,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @divinci-ai/robot-avatar — light entry (NO three.js).
|
|
3
|
+
*
|
|
4
|
+
* Import this from main bundles; lazy-import "@divinci-ai/robot-avatar/scene"
|
|
5
|
+
* for the WebGL chunk:
|
|
6
|
+
*
|
|
7
|
+
* const RobotScene = lazy(() =>
|
|
8
|
+
* import("@divinci-ai/robot-avatar/scene").then((m) => ({ default: m.RobotScene }))
|
|
9
|
+
* );
|
|
10
|
+
*/
|
|
11
|
+
export type { AvatarState, RobotAvatarColors } from "./types.js";
|
|
12
|
+
export { blendHex } from "./blend.js";
|
|
13
|
+
export { useRenderGate, type RenderGate, type RenderGateOptions } from "./render-gate.js";
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,KAAK,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @divinci-ai/robot-avatar — light entry (NO three.js).
|
|
3
|
+
*
|
|
4
|
+
* Import this from main bundles; lazy-import "@divinci-ai/robot-avatar/scene"
|
|
5
|
+
* for the WebGL chunk:
|
|
6
|
+
*
|
|
7
|
+
* const RobotScene = lazy(() =>
|
|
8
|
+
* import("@divinci-ai/robot-avatar/scene").then((m) => ({ default: m.RobotScene }))
|
|
9
|
+
* );
|
|
10
|
+
*/
|
|
11
|
+
export { blendHex } from "./blend.js";
|
|
12
|
+
export { useRenderGate } from "./render-gate.js";
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,aAAa,EAA2C,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useRenderGate — shared "should the robot render right now?" gate.
|
|
3
|
+
*
|
|
4
|
+
* Returns `active: false` when any of these hold:
|
|
5
|
+
* - the container is scrolled out of the viewport (IntersectionObserver)
|
|
6
|
+
* - the tab is hidden (visibilitychange)
|
|
7
|
+
* - the user has been idle for `idleMs` (optional; pass 0/undefined to skip)
|
|
8
|
+
*
|
|
9
|
+
* Consumers feed `active` into RobotScene's `paused` prop, which sets the R3F
|
|
10
|
+
* frameloop to "never" — the WebGL context stays alive but stops burning
|
|
11
|
+
* GPU/battery. This generalizes the SDK docs hero's gate controller so the web
|
|
12
|
+
* app / agent panel / extension iframe get the same behavior for free.
|
|
13
|
+
*
|
|
14
|
+
* Lives in the light entry: React-only, no three.js.
|
|
15
|
+
*/
|
|
16
|
+
export interface RenderGateOptions {
|
|
17
|
+
/** Pause after this many ms without pointer/key/scroll activity. 0 = never. */
|
|
18
|
+
idleMs?: number;
|
|
19
|
+
/** IntersectionObserver threshold for "visible" (default 0.05). */
|
|
20
|
+
threshold?: number;
|
|
21
|
+
}
|
|
22
|
+
export interface RenderGate<T extends HTMLElement> {
|
|
23
|
+
/**
|
|
24
|
+
* Attach to the element wrapping the canvas. A callback ref (not a ref
|
|
25
|
+
* object) so the type is identical under react 18 and 19 typings.
|
|
26
|
+
*/
|
|
27
|
+
ref: (node: T | null) => void;
|
|
28
|
+
/** True when the robot should animate. */
|
|
29
|
+
active: boolean;
|
|
30
|
+
}
|
|
31
|
+
export declare function useRenderGate<T extends HTMLElement = HTMLDivElement>(options?: RenderGateOptions): RenderGate<T>;
|
|
32
|
+
//# sourceMappingURL=render-gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-gate.d.ts","sourceRoot":"","sources":["../src/render-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,MAAM,WAAW,iBAAiB;IAChC,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU,CAAC,CAAC,SAAS,WAAW;IAC/C;;;OAGG;IACH,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;IAC9B,0CAA0C;IAC1C,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,wBAAgB,aAAa,CAAC,CAAC,SAAS,WAAW,GAAG,cAAc,EAClE,OAAO,GAAE,iBAAsB,GAC9B,UAAU,CAAC,CAAC,CAAC,CAgEf"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useRenderGate — shared "should the robot render right now?" gate.
|
|
3
|
+
*
|
|
4
|
+
* Returns `active: false` when any of these hold:
|
|
5
|
+
* - the container is scrolled out of the viewport (IntersectionObserver)
|
|
6
|
+
* - the tab is hidden (visibilitychange)
|
|
7
|
+
* - the user has been idle for `idleMs` (optional; pass 0/undefined to skip)
|
|
8
|
+
*
|
|
9
|
+
* Consumers feed `active` into RobotScene's `paused` prop, which sets the R3F
|
|
10
|
+
* frameloop to "never" — the WebGL context stays alive but stops burning
|
|
11
|
+
* GPU/battery. This generalizes the SDK docs hero's gate controller so the web
|
|
12
|
+
* app / agent panel / extension iframe get the same behavior for free.
|
|
13
|
+
*
|
|
14
|
+
* Lives in the light entry: React-only, no three.js.
|
|
15
|
+
*/
|
|
16
|
+
import { useEffect, useState } from "react";
|
|
17
|
+
export function useRenderGate(options = {}) {
|
|
18
|
+
const { idleMs = 0, threshold = 0.05 } = options;
|
|
19
|
+
const [node, setNode] = useState(null);
|
|
20
|
+
const [active, setActive] = useState(true);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
let visible = true;
|
|
23
|
+
let shown = !document.hidden;
|
|
24
|
+
let idle = false;
|
|
25
|
+
let idleTimer;
|
|
26
|
+
const recompute = () => {
|
|
27
|
+
setActive(visible && shown && !idle);
|
|
28
|
+
};
|
|
29
|
+
const bumpIdle = () => {
|
|
30
|
+
if (!idleMs)
|
|
31
|
+
return;
|
|
32
|
+
if (idle) {
|
|
33
|
+
idle = false;
|
|
34
|
+
recompute();
|
|
35
|
+
}
|
|
36
|
+
window.clearTimeout(idleTimer);
|
|
37
|
+
idleTimer = window.setTimeout(() => {
|
|
38
|
+
idle = true;
|
|
39
|
+
recompute();
|
|
40
|
+
}, idleMs);
|
|
41
|
+
};
|
|
42
|
+
let io;
|
|
43
|
+
if (node && typeof IntersectionObserver !== "undefined") {
|
|
44
|
+
io = new IntersectionObserver((entries) => {
|
|
45
|
+
visible = entries.some((e) => e.isIntersecting);
|
|
46
|
+
recompute();
|
|
47
|
+
}, { threshold });
|
|
48
|
+
io.observe(node);
|
|
49
|
+
}
|
|
50
|
+
const onVisibility = () => {
|
|
51
|
+
shown = !document.hidden;
|
|
52
|
+
recompute();
|
|
53
|
+
};
|
|
54
|
+
document.addEventListener("visibilitychange", onVisibility);
|
|
55
|
+
const activityEvents = ["pointermove", "pointerdown", "keydown", "wheel", "touchstart", "scroll"];
|
|
56
|
+
if (idleMs) {
|
|
57
|
+
activityEvents.forEach((ev) => window.addEventListener(ev, bumpIdle, { passive: true }));
|
|
58
|
+
bumpIdle();
|
|
59
|
+
}
|
|
60
|
+
recompute();
|
|
61
|
+
return () => {
|
|
62
|
+
io?.disconnect();
|
|
63
|
+
document.removeEventListener("visibilitychange", onVisibility);
|
|
64
|
+
if (idleMs) {
|
|
65
|
+
activityEvents.forEach((ev) => window.removeEventListener(ev, bumpIdle));
|
|
66
|
+
window.clearTimeout(idleTimer);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}, [node, idleMs, threshold]);
|
|
70
|
+
return { ref: setNode, active };
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=render-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-gate.js","sourceRoot":"","sources":["../src/render-gate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AnimatedHeart — the Divinci heart rendered in code (not baked into the GLB)
|
|
3
|
+
* so it can be alive: it slowly phases through brand colors, is slightly
|
|
4
|
+
* transparent, and gently pulses.
|
|
5
|
+
*/
|
|
6
|
+
import React from "react";
|
|
7
|
+
import type { MutableRefObject } from "react";
|
|
8
|
+
import type { RobotSignals } from "./useRobotSignals.js";
|
|
9
|
+
export interface AnimatedHeartProps {
|
|
10
|
+
position?: [number, number, number];
|
|
11
|
+
scale?: number;
|
|
12
|
+
cycleSeconds?: number;
|
|
13
|
+
opacity?: number;
|
|
14
|
+
stretchY?: number;
|
|
15
|
+
sig?: MutableRefObject<RobotSignals>;
|
|
16
|
+
}
|
|
17
|
+
export declare function AnimatedHeart({ position, scale, cycleSeconds, opacity, stretchY, sig, }: AnimatedHeartProps): React.ReactElement;
|
|
18
|
+
//# sourceMappingURL=AnimatedHeart.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AnimatedHeart.d.ts","sourceRoot":"","sources":["../../src/scene/AnimatedHeart.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAA0B,MAAM,OAAO,CAAC;AAG/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,gBAAgB,CAAC,YAAY,CAAC,CAAC;CACtC;AAwBD,wBAAgB,aAAa,CAAC,EAC5B,QAAyB,EACzB,KAAW,EACX,YAAgB,EAChB,OAAa,EACb,QAAY,EACZ,GAAG,GACJ,EAAE,kBAAkB,GAAG,KAAK,CAAC,YAAY,CA4CzC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* AnimatedHeart — the Divinci heart rendered in code (not baked into the GLB)
|
|
4
|
+
* so it can be alive: it slowly phases through brand colors, is slightly
|
|
5
|
+
* transparent, and gently pulses.
|
|
6
|
+
*/
|
|
7
|
+
import { useMemo, useRef } from "react";
|
|
8
|
+
import { useFrame } from "@react-three/fiber";
|
|
9
|
+
import * as THREE from "three";
|
|
10
|
+
function makeHeartGeometry() {
|
|
11
|
+
const s = new THREE.Shape();
|
|
12
|
+
s.moveTo(0, 0.3);
|
|
13
|
+
s.bezierCurveTo(0, 0.3, -0.2, 0, -0.5, 0);
|
|
14
|
+
s.bezierCurveTo(-0.95, 0, -0.95, 0.5, -0.95, 0.5);
|
|
15
|
+
s.bezierCurveTo(-0.95, 0.78, -0.6, 1.05, 0, 1.35);
|
|
16
|
+
s.bezierCurveTo(0.6, 1.05, 0.95, 0.78, 0.95, 0.5);
|
|
17
|
+
s.bezierCurveTo(0.95, 0.5, 0.95, 0, 0.5, 0);
|
|
18
|
+
s.bezierCurveTo(0.2, 0, 0, 0.3, 0, 0.3);
|
|
19
|
+
const geo = new THREE.ExtrudeGeometry(s, {
|
|
20
|
+
depth: 0.55,
|
|
21
|
+
bevelEnabled: true,
|
|
22
|
+
bevelThickness: 0.14,
|
|
23
|
+
bevelSize: 0.14,
|
|
24
|
+
bevelSegments: 5,
|
|
25
|
+
curveSegments: 28,
|
|
26
|
+
});
|
|
27
|
+
geo.center();
|
|
28
|
+
geo.rotateZ(Math.PI);
|
|
29
|
+
return geo;
|
|
30
|
+
}
|
|
31
|
+
export function AnimatedHeart({ position = [0, 0.2, 0.45], scale = 0.5, cycleSeconds = 6, opacity = 0.8, stretchY = 1, sig, }) {
|
|
32
|
+
const geometry = useMemo(makeHeartGeometry, []);
|
|
33
|
+
const mesh = useRef(null);
|
|
34
|
+
const material = useRef(null);
|
|
35
|
+
const color = useMemo(() => new THREE.Color(), []);
|
|
36
|
+
useFrame(() => {
|
|
37
|
+
const t = performance.now() / 1000;
|
|
38
|
+
const s = sig?.current;
|
|
39
|
+
const reduced = s?.reduced ?? false;
|
|
40
|
+
// Reduced motion: hold a static mid-cycle color, no hue drift / pulse / bob.
|
|
41
|
+
const phase = reduced ? 0 : Math.sin((t * 2 * Math.PI) / cycleSeconds);
|
|
42
|
+
const hue = (0.89 + 0.11 * phase) % 1;
|
|
43
|
+
color.setHSL(hue, 0.7, 0.56);
|
|
44
|
+
if (material.current) {
|
|
45
|
+
material.current.color.copy(color);
|
|
46
|
+
material.current.emissive.copy(color);
|
|
47
|
+
}
|
|
48
|
+
if (mesh.current) {
|
|
49
|
+
if (reduced) {
|
|
50
|
+
mesh.current.scale.set(scale, scale * stretchY, scale);
|
|
51
|
+
mesh.current.position.y = position[1];
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
const reacting = s ? t < s.reactUntil : false;
|
|
55
|
+
const pulse = 1 + (reacting ? 0.2 : 0.05) * Math.sin(t * (reacting ? 9 : 2.4));
|
|
56
|
+
mesh.current.scale.set(scale * pulse, scale * stretchY * pulse, scale * pulse);
|
|
57
|
+
mesh.current.position.y = position[1] + (reacting ? Math.abs(Math.sin(t * 9)) * 0.06 : 0);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
return (_jsx("mesh", { ref: mesh, geometry: geometry, position: position, scale: scale, children: _jsx("meshStandardMaterial", { ref: material, transparent: true, opacity: opacity, roughness: 0.2, metalness: 0.0, emissiveIntensity: 0.35, depthWrite: false }) }));
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=AnimatedHeart.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AnimatedHeart.js","sourceRoot":"","sources":["../../src/scene/AnimatedHeart.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LogoRobot — the Divinci mascot, hand-built in code and fully rigged.
|
|
3
|
+
*
|
|
4
|
+
* Structure mirrors the flat logo (open frame, NO torso):
|
|
5
|
+
* - rounded-corner head with two dark eyes + two ball-tipped antennae
|
|
6
|
+
* - two "4"-shaped arm/leg forms (mirrored): diagonal + crossbar + vertical
|
|
7
|
+
* stem, one sphere hand; a foot floats below each
|
|
8
|
+
* - the AnimatedHeart floating in the chest center
|
|
9
|
+
*
|
|
10
|
+
* Every part is rigged and animates independently off shared interaction
|
|
11
|
+
* signals (page-wide gaze, keystroke reactions, idle fidgets).
|
|
12
|
+
*/
|
|
13
|
+
import React from "react";
|
|
14
|
+
import { type SpeakerTarget } from "./useRobotSignals.js";
|
|
15
|
+
import type { AvatarState, RobotAvatarColors } from "../types.js";
|
|
16
|
+
export interface LogoRobotProps {
|
|
17
|
+
state: AvatarState;
|
|
18
|
+
colors: RobotAvatarColors;
|
|
19
|
+
autoRotate?: boolean;
|
|
20
|
+
/** Optional extra gaze target (see useRobotSignals). */
|
|
21
|
+
speakerTarget?: SpeakerTarget;
|
|
22
|
+
}
|
|
23
|
+
export declare function LogoRobot({ state, colors, autoRotate, speakerTarget }: LogoRobotProps): React.ReactElement;
|
|
24
|
+
//# sourceMappingURL=LogoRobot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LogoRobot.d.ts","sourceRoot":"","sources":["../../src/scene/LogoRobot.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAA0B,MAAM,OAAO,CAAC;AAI/C,OAAO,EAAqD,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE7G,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AA6PlE,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,wDAAwD;IACxD,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,UAAkB,EAAE,aAAa,EAAE,EAAE,cAAc,GAAG,KAAK,CAAC,YAAY,CAgFlH"}
|