@makefinks/daemon 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/package.json +1 -1
  2. package/src/ai/daemon-ai.ts +8 -35
  3. package/src/ai/message-utils.ts +26 -0
  4. package/src/ai/tools/subagents.ts +8 -7
  5. package/src/app/App.tsx +3 -0
  6. package/src/app/components/AvatarLayer.tsx +96 -24
  7. package/src/app/components/ConversationPane.tsx +9 -8
  8. package/src/avatar/DaemonAvatarRenderable.ts +26 -3
  9. package/src/avatar/daemon-avatar-rig.ts +10 -1159
  10. package/src/avatar/rig/core/rig-engine.ts +202 -0
  11. package/src/avatar/rig/core/rig-types.ts +17 -0
  12. package/src/avatar/rig/scene/create-scene-elements.ts +298 -0
  13. package/src/avatar/rig/state/rig-state.ts +193 -0
  14. package/src/avatar/rig/theme/rig-theme.ts +31 -0
  15. package/src/avatar/rig/tools/rig-tools.ts +8 -0
  16. package/src/avatar/rig/update/update-core.ts +32 -0
  17. package/src/avatar/rig/update/update-eye.ts +31 -0
  18. package/src/avatar/rig/update/update-fragments.ts +46 -0
  19. package/src/avatar/rig/update/update-glitch.ts +64 -0
  20. package/src/avatar/rig/update/update-idle.ts +95 -0
  21. package/src/avatar/rig/update/update-intensity.ts +16 -0
  22. package/src/avatar/rig/update/update-main-anchor.ts +20 -0
  23. package/src/avatar/rig/update/update-particles.ts +49 -0
  24. package/src/avatar/rig/update/update-rings.ts +35 -0
  25. package/src/avatar/rig/update/update-sigils.ts +26 -0
  26. package/src/avatar/rig/update/update-spawn.ts +83 -0
  27. package/src/avatar/rig/utils/math.ts +17 -0
  28. package/src/components/ContentBlockView.tsx +6 -1
  29. package/src/components/ToolCallView.tsx +9 -12
  30. package/src/components/tool-layouts/components.tsx +4 -3
  31. package/src/components/tool-layouts/layouts/subagent.tsx +140 -16
  32. package/src/hooks/daemon-event-handlers.ts +3 -3
  33. package/src/hooks/use-app-controller.ts +51 -1
  34. package/src/hooks/use-app-display-state.ts +1 -1
  35. package/src/hooks/use-glitchy-banner.ts +175 -0
  36. package/src/hooks/use-reasoning-animation.ts +1 -1
  37. package/src/state/daemon-state.ts +3 -3
  38. package/src/ui/reasoning-ticker.tsx +4 -1
  39. package/src/ui/startup.ts +5 -0
  40. package/src/utils/debug-logger.ts +34 -23
@@ -0,0 +1,202 @@
1
+ import { THREE } from "@opentui/core/3d";
2
+ import type { AvatarColorTheme } from "src/types";
3
+ import { type SceneElements, createSceneElements } from "../scene/create-scene-elements";
4
+ import { type RigState, createInitialState } from "../state/rig-state";
5
+ import { updateThemeColors } from "../theme/rig-theme";
6
+ import { TOOL_CATEGORY_COLORS, type ToolCategory } from "../tools/rig-tools";
7
+ import { updateCore } from "../update/update-core";
8
+ import { updateEye } from "../update/update-eye";
9
+ import { updateFragments } from "../update/update-fragments";
10
+ import { updateGlitchBehavior } from "../update/update-glitch";
11
+ import { updateIdleAmbience } from "../update/update-idle";
12
+ import { updateIntensityAndAudio } from "../update/update-intensity";
13
+ import { updateMainAnchor } from "../update/update-main-anchor";
14
+ import { updateParticles } from "../update/update-particles";
15
+ import { updateRings } from "../update/update-rings";
16
+ import { updateSigils } from "../update/update-sigils";
17
+ import { advanceSpawn, applySpawn, resetSpawnState, skipSpawnAnimation } from "../update/update-spawn";
18
+ import { clamp01 } from "../utils/math";
19
+ import type { RigEngineOptions, RigEvent } from "./rig-types";
20
+
21
+ export class RigEngine {
22
+ public readonly scene: THREE.Scene;
23
+ public readonly camera: THREE.PerspectiveCamera;
24
+ private readonly elements: SceneElements;
25
+ private readonly state: RigState;
26
+ private readonly disposables: Array<{ dispose(): void }> = [];
27
+
28
+ constructor(options: RigEngineOptions) {
29
+ this.scene = new THREE.Scene();
30
+ this.camera = new THREE.PerspectiveCamera(28, options.aspectRatio, 0.1, 100);
31
+ this.camera.position.set(0, 0, 8);
32
+ this.camera.lookAt(0, 0, 0);
33
+
34
+ const trackGeo = <T extends THREE.BufferGeometry>(g: T): T => {
35
+ this.disposables.push(g);
36
+ return g;
37
+ };
38
+ const trackMat = <T extends THREE.Material>(m: T): T => {
39
+ this.disposables.push(m);
40
+ return m;
41
+ };
42
+
43
+ this.elements = createSceneElements(this.scene, trackGeo, trackMat);
44
+ this.state = createInitialState();
45
+ }
46
+
47
+ public getScene(): THREE.Scene {
48
+ return this.scene;
49
+ }
50
+
51
+ public getCamera(): THREE.PerspectiveCamera {
52
+ return this.camera;
53
+ }
54
+
55
+ public update(deltaS: number): void {
56
+ const dt = Math.min(0.1, deltaS);
57
+ this.state.glitch.timer += dt;
58
+
59
+ const spawnProgress = advanceSpawn(this.state, dt);
60
+ const isSpawning = spawnProgress < 1;
61
+
62
+ const intensity = updateIntensityAndAudio(this.state, dt);
63
+ const isIdle = intensity < 0.1;
64
+
65
+ // Allow glitch during spawn for effect, or during high intensity
66
+ const allowGlitch =
67
+ (intensity > 0.4 && !this.state.reasoning.active) ||
68
+ (isSpawning && this.state.spawn.glitchIntensity > 0.3);
69
+
70
+ updateMainAnchor(this.elements, this.state, dt, intensity);
71
+ updateCore(this.elements, this.state, dt, intensity);
72
+ updateEye(this.elements, this.state, dt, intensity);
73
+ updateRings(this.elements, this.state, dt, intensity);
74
+ updateFragments(this.elements, this.state, dt, intensity);
75
+ updateSigils(this.elements, this.state, dt, intensity);
76
+ updateParticles(this.elements, this.state, dt, intensity, allowGlitch);
77
+ updateGlitchBehavior(this.elements, this.state, dt, intensity, allowGlitch);
78
+ updateIdleAmbience(this.elements, this.state, dt, isIdle);
79
+ updateThemeColors(this.elements, this.state, dt);
80
+ applySpawn(this.elements, this.state);
81
+ }
82
+
83
+ public handle(event: RigEvent): void {
84
+ switch (event.type) {
85
+ case "theme":
86
+ this.setTheme(event.theme);
87
+ break;
88
+ case "intensity":
89
+ this.setIntensity(event.intensity, { immediate: event.immediate });
90
+ break;
91
+ case "audio":
92
+ this.setAudioLevel(event.level, { immediate: event.immediate });
93
+ break;
94
+ case "tool-active":
95
+ this.setToolActive(event.active, event.category);
96
+ break;
97
+ case "tool-flash":
98
+ this.triggerToolFlash(event.category);
99
+ break;
100
+ case "tool-complete":
101
+ this.triggerToolComplete();
102
+ break;
103
+ case "reasoning":
104
+ this.setReasoningMode(event.active);
105
+ break;
106
+ case "typing":
107
+ this.setTypingMode(event.active);
108
+ break;
109
+ case "typing-pulse":
110
+ this.triggerTypingPulse();
111
+ break;
112
+ default:
113
+ break;
114
+ }
115
+ }
116
+
117
+ public setTheme(theme: AvatarColorTheme): void {
118
+ this.state.theme.target = { ...theme };
119
+ }
120
+
121
+ public setColors(theme: AvatarColorTheme): void {
122
+ this.setTheme(theme);
123
+ }
124
+
125
+ public setIntensity(intensity: number, options?: { immediate?: boolean }): void {
126
+ const next = clamp01(intensity);
127
+ if (options?.immediate) {
128
+ this.state.intensity.target = next;
129
+ this.state.intensity.current = next;
130
+ } else {
131
+ if (next > this.state.intensity.target + 0.1) {
132
+ this.state.intensity.spinBoost = 12.0;
133
+ }
134
+ this.state.intensity.target = next;
135
+ }
136
+ }
137
+
138
+ public setAudioLevel(level: number, options?: { immediate?: boolean }): void {
139
+ const next = clamp01(level);
140
+ if (options?.immediate) {
141
+ this.state.audio.target = next;
142
+ this.state.audio.current = next;
143
+ } else {
144
+ this.state.audio.target = next;
145
+ }
146
+ }
147
+
148
+ public setToolActive(active: boolean, category?: ToolCategory): void {
149
+ this.state.tool.active = active;
150
+ if (active && category) {
151
+ this.elements.sigilMat.color.setHex(TOOL_CATEGORY_COLORS[category]);
152
+ } else {
153
+ this.elements.sigilMat.color.setHex(this.state.theme.current.primary);
154
+ }
155
+ }
156
+
157
+ public triggerToolFlash(category?: ToolCategory): void {
158
+ this.state.tool.flashColor = category ? TOOL_CATEGORY_COLORS[category] : 0xffffff;
159
+ this.state.tool.flashTimer = 0.15;
160
+ this.state.tool.fragmentScatterBoost = 0.3;
161
+ this.state.intensity.spinBoost = Math.max(this.state.intensity.spinBoost, 8);
162
+ }
163
+
164
+ public triggerToolComplete(): void {
165
+ this.state.tool.settleTimer = 0.2;
166
+ }
167
+
168
+ public setReasoningMode(active: boolean): void {
169
+ this.state.reasoning.active = active;
170
+ }
171
+
172
+ public setTypingMode(active: boolean): void {
173
+ this.state.typing.active = active;
174
+ }
175
+
176
+ public triggerTypingPulse(): void {
177
+ this.state.typing.pulse = Math.min(1.0, this.state.typing.pulse + 0.3);
178
+ this.state.intensity.spinBoost = Math.max(this.state.intensity.spinBoost, 1.5);
179
+ }
180
+
181
+ public resetSpawn(): void {
182
+ resetSpawnState(this.state);
183
+ }
184
+
185
+ public skipSpawn(): void {
186
+ skipSpawnAnimation(this.state);
187
+ }
188
+
189
+ /** Returns the current spawn progress (0-1, where 1 = fully spawned) */
190
+ public getSpawnProgress(): number {
191
+ return this.state.spawn.progress;
192
+ }
193
+
194
+ /** Returns true if spawn animation is complete */
195
+ public isSpawnComplete(): boolean {
196
+ return this.state.spawn.complete;
197
+ }
198
+
199
+ public dispose(): void {
200
+ this.disposables.forEach((d) => d.dispose());
201
+ }
202
+ }
@@ -0,0 +1,17 @@
1
+ import type { AvatarColorTheme } from "src/types";
2
+ import type { ToolCategory } from "../tools/rig-tools";
3
+
4
+ export interface RigEngineOptions {
5
+ aspectRatio: number;
6
+ }
7
+
8
+ export type RigEvent =
9
+ | { type: "theme"; theme: AvatarColorTheme }
10
+ | { type: "intensity"; intensity: number; immediate?: boolean }
11
+ | { type: "audio"; level: number; immediate?: boolean }
12
+ | { type: "tool-active"; active: boolean; category?: ToolCategory }
13
+ | { type: "tool-flash"; category?: ToolCategory }
14
+ | { type: "tool-complete" }
15
+ | { type: "reasoning"; active: boolean }
16
+ | { type: "typing"; active: boolean }
17
+ | { type: "typing-pulse" };
@@ -0,0 +1,298 @@
1
+ import { THREE } from "@opentui/core/3d";
2
+
3
+ export interface RingData {
4
+ mesh: THREE.Line;
5
+ material: THREE.LineBasicMaterial;
6
+ speed: number;
7
+ axis: THREE.Vector3;
8
+ phase: number;
9
+ wobblePhase: number;
10
+ }
11
+
12
+ export interface FragmentData {
13
+ mesh: THREE.Mesh;
14
+ material: THREE.MeshBasicMaterial;
15
+ orbitRadius: number;
16
+ orbitSpeed: number;
17
+ orbitAngle: number;
18
+ bobSpeed: number;
19
+ bobPhase: number;
20
+ }
21
+
22
+ export interface ParticleVelocity {
23
+ x: number;
24
+ y: number;
25
+ z: number;
26
+ phase: number;
27
+ }
28
+
29
+ export interface SceneElements {
30
+ mainAnchor: THREE.Group;
31
+ coreGroup: THREE.Group;
32
+ orbitGroup: THREE.Group;
33
+ fragmentGroup: THREE.Group;
34
+ coreMesh: THREE.Mesh;
35
+ glowMesh: THREE.Mesh;
36
+ glowMat: THREE.MeshBasicMaterial;
37
+ eye: THREE.Mesh;
38
+ eyeMat: THREE.MeshBasicMaterial;
39
+ pupil: THREE.Mesh;
40
+ pupilMat: THREE.MeshBasicMaterial;
41
+ rings: RingData[];
42
+ fragments: FragmentData[];
43
+ particleSystem: THREE.Points;
44
+ particleMat: THREE.PointsMaterial;
45
+ particlePos: THREE.BufferAttribute;
46
+ particleVelocities: ParticleVelocity[];
47
+ sigilLines: THREE.LineSegments;
48
+ sigilMat: THREE.LineBasicMaterial;
49
+ sigilPos: THREE.BufferAttribute;
50
+ pointLight: THREE.PointLight;
51
+ }
52
+
53
+ type TrackGeometry = <T extends THREE.BufferGeometry>(g: T) => T;
54
+ type TrackMaterial = <T extends THREE.Material>(m: T) => T;
55
+
56
+ export function createSceneElements(
57
+ scene: THREE.Scene,
58
+ trackGeo: TrackGeometry,
59
+ trackMat: TrackMaterial
60
+ ): SceneElements {
61
+ const mainAnchor = new THREE.Group();
62
+ scene.add(mainAnchor);
63
+
64
+ const coreGroup = new THREE.Group();
65
+ mainAnchor.add(coreGroup);
66
+
67
+ const orbitGroup = new THREE.Group();
68
+ mainAnchor.add(orbitGroup);
69
+
70
+ const fragmentGroup = new THREE.Group();
71
+ mainAnchor.add(fragmentGroup);
72
+
73
+ // Core mesh and glow
74
+ const coreGeo = trackGeo(new THREE.IcosahedronGeometry(0.35, 0));
75
+ const coreMat = trackMat(
76
+ new THREE.MeshBasicMaterial({
77
+ color: 0x000000,
78
+ transparent: true,
79
+ opacity: 0.95,
80
+ })
81
+ );
82
+ const coreMesh = new THREE.Mesh(coreGeo, coreMat);
83
+ coreGroup.add(coreMesh);
84
+
85
+ const glowGeo = trackGeo(new THREE.IcosahedronGeometry(0.38, 1));
86
+ const glowMat = trackMat<THREE.MeshBasicMaterial>(
87
+ new THREE.MeshBasicMaterial({
88
+ color: 0x666666,
89
+ wireframe: true,
90
+ transparent: true,
91
+ opacity: 0.6,
92
+ blending: THREE.AdditiveBlending,
93
+ })
94
+ );
95
+ const glowMesh = new THREE.Mesh(glowGeo, glowMat);
96
+ coreGroup.add(glowMesh);
97
+
98
+ // Eye and pupil
99
+ const eyeGeo = trackGeo(new THREE.RingGeometry(0.08, 0.16, 6));
100
+ const eyeMat = trackMat<THREE.MeshBasicMaterial>(
101
+ new THREE.MeshBasicMaterial({
102
+ color: 0xffffff,
103
+ side: THREE.DoubleSide,
104
+ transparent: true,
105
+ opacity: 1.0,
106
+ blending: THREE.AdditiveBlending,
107
+ })
108
+ );
109
+ const eye = new THREE.Mesh(eyeGeo, eyeMat);
110
+ eye.position.set(0, 0, 0.36);
111
+ coreGroup.add(eye);
112
+
113
+ const pupilGeo = trackGeo(new THREE.CircleGeometry(0.06, 6));
114
+ const pupilMat = trackMat<THREE.MeshBasicMaterial>(
115
+ new THREE.MeshBasicMaterial({
116
+ color: 0xffffff,
117
+ transparent: true,
118
+ opacity: 1.0,
119
+ blending: THREE.AdditiveBlending,
120
+ })
121
+ );
122
+ const pupil = new THREE.Mesh(pupilGeo, pupilMat);
123
+ pupil.position.set(0, 0, 0.37);
124
+ coreGroup.add(pupil);
125
+
126
+ // Orbiting rings
127
+ const rings: RingData[] = [];
128
+ for (let i = 0; i < 3; i++) {
129
+ const radius = 0.7 + i * 0.25;
130
+ const segments = 32;
131
+ const points: THREE.Vector3[] = [];
132
+ for (let j = 0; j <= segments; j++) {
133
+ const theta = (j / segments) * Math.PI * 2;
134
+ if (j % 8 < 6 || i === 1) {
135
+ points.push(new THREE.Vector3(Math.cos(theta) * radius, Math.sin(theta) * radius, 0));
136
+ }
137
+ }
138
+
139
+ const geo = trackGeo(new THREE.BufferGeometry().setFromPoints(points));
140
+ const mat = trackMat<THREE.LineBasicMaterial>(
141
+ new THREE.LineBasicMaterial({
142
+ color: 0x888888,
143
+ transparent: true,
144
+ opacity: 0.5 + i * 0.15,
145
+ blending: THREE.AdditiveBlending,
146
+ })
147
+ );
148
+
149
+ const ring = new THREE.Line(geo, mat);
150
+ ring.rotation.x = (i * Math.PI) / 3 + Math.random() * 0.3;
151
+ ring.rotation.y = (i * Math.PI) / 4;
152
+
153
+ rings.push({
154
+ mesh: ring,
155
+ material: mat,
156
+ speed: 0.3 + i * 0.15,
157
+ axis: new THREE.Vector3(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5).normalize(),
158
+ phase: Math.random() * Math.PI * 2,
159
+ wobblePhase: Math.random() * Math.PI * 2,
160
+ });
161
+ orbitGroup.add(ring);
162
+ }
163
+
164
+ // Floating fragments
165
+ const fragments: FragmentData[] = [];
166
+ const fragmentCount = 12;
167
+ for (let i = 0; i < fragmentCount; i++) {
168
+ let geo: THREE.BufferGeometry;
169
+ const shapeType = i % 4;
170
+ const size = 0.08 + Math.random() * 0.06;
171
+
172
+ switch (shapeType) {
173
+ case 0:
174
+ geo = trackGeo(new THREE.TetrahedronGeometry(size));
175
+ break;
176
+ case 1:
177
+ geo = trackGeo(new THREE.OctahedronGeometry(size));
178
+ break;
179
+ case 2:
180
+ geo = trackGeo(new THREE.BoxGeometry(size, size * 0.3, size * 0.3));
181
+ break;
182
+ default:
183
+ geo = trackGeo(new THREE.IcosahedronGeometry(size * 0.7, 0));
184
+ }
185
+
186
+ const mat = trackMat<THREE.MeshBasicMaterial>(
187
+ new THREE.MeshBasicMaterial({
188
+ color: 0x666666,
189
+ wireframe: Math.random() > 0.5,
190
+ transparent: true,
191
+ opacity: 0.6 + Math.random() * 0.3,
192
+ blending: THREE.AdditiveBlending,
193
+ })
194
+ );
195
+
196
+ const mesh = new THREE.Mesh(geo, mat);
197
+ const orbitRadius = 1.6;
198
+ const orbitOffset = (i / fragmentCount) * Math.PI * 2;
199
+ mesh.position.set(
200
+ Math.cos(orbitOffset) * orbitRadius,
201
+ (Math.random() - 0.5) * 0.4,
202
+ Math.sin(orbitOffset) * orbitRadius
203
+ );
204
+ mesh.rotation.set(Math.random() * Math.PI, Math.random() * Math.PI, Math.random() * Math.PI);
205
+
206
+ fragments.push({
207
+ mesh,
208
+ material: mat,
209
+ orbitRadius,
210
+ orbitSpeed: 0.4,
211
+ orbitAngle: orbitOffset,
212
+ bobSpeed: 1 + Math.random() * 1.5,
213
+ bobPhase: Math.random() * Math.PI * 2,
214
+ });
215
+ fragmentGroup.add(mesh);
216
+ }
217
+
218
+ // Particle system
219
+ const particleCount = 60;
220
+ const pGeo = trackGeo(new THREE.BufferGeometry());
221
+ const pPos = new Float32Array(particleCount * 3);
222
+ const particleVelocities: ParticleVelocity[] = [];
223
+ for (let i = 0; i < particleCount; i++) {
224
+ const r = 1.5 + Math.random() * 2.5;
225
+ const theta = Math.random() * Math.PI * 2;
226
+ const phi = Math.acos(Math.random() * 2 - 1);
227
+ pPos[i * 3] = r * Math.sin(phi) * Math.cos(theta);
228
+ pPos[i * 3 + 1] = r * Math.sin(phi) * Math.sin(theta);
229
+ pPos[i * 3 + 2] = r * Math.cos(phi);
230
+
231
+ particleVelocities.push({
232
+ x: (Math.random() - 0.5) * 0.15,
233
+ y: (Math.random() - 0.5) * 0.15,
234
+ z: (Math.random() - 0.5) * 0.15,
235
+ phase: Math.random() * Math.PI * 2,
236
+ });
237
+ }
238
+
239
+ pGeo.setAttribute("position", new THREE.BufferAttribute(pPos, 3));
240
+ const particleMat = trackMat<THREE.PointsMaterial>(
241
+ new THREE.PointsMaterial({
242
+ color: 0x888888,
243
+ size: 0.025,
244
+ transparent: true,
245
+ opacity: 0.5,
246
+ blending: THREE.AdditiveBlending,
247
+ })
248
+ );
249
+ const particleSystem = new THREE.Points(pGeo, particleMat);
250
+ scene.add(particleSystem);
251
+ const particlePos = particleSystem.geometry.attributes.position as THREE.BufferAttribute;
252
+
253
+ // Sigil lines connecting fragments
254
+ const sigilGeo = trackGeo(new THREE.BufferGeometry());
255
+ const sigilPositions = new Float32Array(fragmentCount * 2 * 3);
256
+ sigilGeo.setAttribute("position", new THREE.BufferAttribute(sigilPositions, 3));
257
+
258
+ const sigilMat = trackMat<THREE.LineBasicMaterial>(
259
+ new THREE.LineBasicMaterial({
260
+ color: 0x444444,
261
+ transparent: true,
262
+ opacity: 0.3,
263
+ blending: THREE.AdditiveBlending,
264
+ })
265
+ );
266
+ const sigilLines = new THREE.LineSegments(sigilGeo, sigilMat);
267
+ scene.add(sigilLines);
268
+ const sigilPos = sigilLines.geometry.attributes.position as THREE.BufferAttribute;
269
+
270
+ // Central light
271
+ const pointLight = new THREE.PointLight(0xffffff, 0.8, 6);
272
+ pointLight.position.set(0, 0, 0.5);
273
+ coreGroup.add(pointLight);
274
+
275
+ return {
276
+ mainAnchor,
277
+ coreGroup,
278
+ orbitGroup,
279
+ fragmentGroup,
280
+ coreMesh,
281
+ glowMesh,
282
+ glowMat,
283
+ eye,
284
+ eyeMat,
285
+ pupil,
286
+ pupilMat,
287
+ rings,
288
+ fragments,
289
+ particleSystem,
290
+ particleMat,
291
+ particlePos,
292
+ particleVelocities,
293
+ sigilLines,
294
+ sigilMat,
295
+ sigilPos,
296
+ pointLight,
297
+ };
298
+ }
@@ -0,0 +1,193 @@
1
+ import type { AvatarColorTheme } from "src/types";
2
+
3
+ export interface PhaseState {
4
+ drift: number;
5
+ corePulse: number;
6
+ eyePulse: number;
7
+ pupilPulse: number;
8
+ sigilPulse: number;
9
+ }
10
+
11
+ export interface IntensityState {
12
+ current: number;
13
+ target: number;
14
+ spinBoost: number;
15
+ }
16
+
17
+ export interface AudioState {
18
+ current: number;
19
+ target: number;
20
+ }
21
+
22
+ export interface GlitchState {
23
+ timer: number;
24
+ isActive: boolean;
25
+ duration: number;
26
+ }
27
+
28
+ export interface ToolState {
29
+ active: boolean;
30
+ flashTimer: number;
31
+ flashColor: number;
32
+ fragmentScatterBoost: number;
33
+ sigilBrightnessBoost: number;
34
+ settleTimer: number;
35
+ }
36
+
37
+ export interface ReasoningState {
38
+ active: boolean;
39
+ blend: number;
40
+ }
41
+
42
+ export interface TypingState {
43
+ active: boolean;
44
+ pulse: number;
45
+ eyeScanTimer: number;
46
+ eyeScanInterval: number;
47
+ }
48
+
49
+ export interface IdleMicroGlitchState {
50
+ timer: number;
51
+ cooldown: number;
52
+ active: boolean;
53
+ duration: number;
54
+ }
55
+
56
+ export interface EyeDriftState {
57
+ x: number;
58
+ y: number;
59
+ targetX: number;
60
+ targetY: number;
61
+ timer: number;
62
+ interval: number;
63
+ }
64
+
65
+ export interface ParticlePulseState {
66
+ timer: number;
67
+ interval: number;
68
+ brightness: number;
69
+ }
70
+
71
+ export interface CoreDriftState {
72
+ x: number;
73
+ y: number;
74
+ z: number;
75
+ phaseX: number;
76
+ phaseY: number;
77
+ phaseZ: number;
78
+ }
79
+
80
+ export interface SpawnState {
81
+ progress: number;
82
+ elapsed: number;
83
+ complete: boolean;
84
+ glitchIntensity: number;
85
+ }
86
+
87
+ export interface ThemeState {
88
+ current: AvatarColorTheme;
89
+ target: AvatarColorTheme;
90
+ }
91
+
92
+ export interface RigState {
93
+ phase: PhaseState;
94
+ intensity: IntensityState;
95
+ audio: AudioState;
96
+ glitch: GlitchState;
97
+ tool: ToolState;
98
+ reasoning: ReasoningState;
99
+ typing: TypingState;
100
+ idleMicroGlitch: IdleMicroGlitchState;
101
+ eyeDrift: EyeDriftState;
102
+ particlePulse: ParticlePulseState;
103
+ coreDrift: CoreDriftState;
104
+ spawn: SpawnState;
105
+ theme: ThemeState;
106
+ }
107
+
108
+ export const DEFAULT_THEME: AvatarColorTheme = {
109
+ primary: 0x9ca3af,
110
+ glow: 0x67e8f9,
111
+ eye: 0xff0000,
112
+ };
113
+
114
+ export function createInitialState(): RigState {
115
+ return {
116
+ phase: {
117
+ drift: 0,
118
+ corePulse: 0,
119
+ eyePulse: 0,
120
+ pupilPulse: 0,
121
+ sigilPulse: 0,
122
+ },
123
+ intensity: {
124
+ current: 0,
125
+ target: 0,
126
+ spinBoost: 0,
127
+ },
128
+ audio: {
129
+ current: 0,
130
+ target: 0,
131
+ },
132
+ glitch: {
133
+ timer: 0,
134
+ isActive: false,
135
+ duration: 0,
136
+ },
137
+ tool: {
138
+ active: false,
139
+ flashTimer: 0,
140
+ flashColor: 0xffffff,
141
+ fragmentScatterBoost: 0,
142
+ sigilBrightnessBoost: 0,
143
+ settleTimer: 0,
144
+ },
145
+ reasoning: {
146
+ active: false,
147
+ blend: 0,
148
+ },
149
+ typing: {
150
+ active: false,
151
+ pulse: 0,
152
+ eyeScanTimer: 0,
153
+ eyeScanInterval: 0.5,
154
+ },
155
+ idleMicroGlitch: {
156
+ timer: 0,
157
+ cooldown: 3 + Math.random() * 5,
158
+ active: false,
159
+ duration: 0,
160
+ },
161
+ eyeDrift: {
162
+ x: 0,
163
+ y: 0,
164
+ targetX: 0,
165
+ targetY: 0,
166
+ timer: 0,
167
+ interval: 1.5 + Math.random() * 2,
168
+ },
169
+ particlePulse: {
170
+ timer: 0,
171
+ interval: 1 + Math.random() * 2,
172
+ brightness: 0,
173
+ },
174
+ coreDrift: {
175
+ x: 0,
176
+ y: 0,
177
+ z: 0,
178
+ phaseX: Math.random() * Math.PI * 2,
179
+ phaseY: Math.random() * Math.PI * 2,
180
+ phaseZ: Math.random() * Math.PI * 2,
181
+ },
182
+ spawn: {
183
+ progress: 0,
184
+ elapsed: 0,
185
+ complete: false,
186
+ glitchIntensity: 1,
187
+ },
188
+ theme: {
189
+ current: { ...DEFAULT_THEME },
190
+ target: { ...DEFAULT_THEME },
191
+ },
192
+ };
193
+ }