@makefinks/daemon 0.5.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.
- package/package.json +1 -1
- package/src/app/App.tsx +3 -0
- package/src/app/components/AvatarLayer.tsx +96 -24
- package/src/app/components/ConversationPane.tsx +3 -2
- package/src/avatar/DaemonAvatarRenderable.ts +26 -3
- package/src/avatar/daemon-avatar-rig.ts +10 -1159
- package/src/avatar/rig/core/rig-engine.ts +202 -0
- package/src/avatar/rig/core/rig-types.ts +17 -0
- package/src/avatar/rig/scene/create-scene-elements.ts +298 -0
- package/src/avatar/rig/state/rig-state.ts +193 -0
- package/src/avatar/rig/theme/rig-theme.ts +31 -0
- package/src/avatar/rig/tools/rig-tools.ts +8 -0
- package/src/avatar/rig/update/update-core.ts +32 -0
- package/src/avatar/rig/update/update-eye.ts +31 -0
- package/src/avatar/rig/update/update-fragments.ts +46 -0
- package/src/avatar/rig/update/update-glitch.ts +64 -0
- package/src/avatar/rig/update/update-idle.ts +95 -0
- package/src/avatar/rig/update/update-intensity.ts +16 -0
- package/src/avatar/rig/update/update-main-anchor.ts +20 -0
- package/src/avatar/rig/update/update-particles.ts +49 -0
- package/src/avatar/rig/update/update-rings.ts +35 -0
- package/src/avatar/rig/update/update-sigils.ts +26 -0
- package/src/avatar/rig/update/update-spawn.ts +83 -0
- package/src/avatar/rig/utils/math.ts +17 -0
- package/src/hooks/use-app-controller.ts +51 -1
- package/src/hooks/use-app-display-state.ts +1 -1
- package/src/hooks/use-glitchy-banner.ts +175 -0
- package/src/ui/startup.ts +5 -0
package/package.json
CHANGED
package/src/app/App.tsx
CHANGED
|
@@ -70,6 +70,9 @@ export function App() {
|
|
|
70
70
|
width={controller.avatarLayerProps.width}
|
|
71
71
|
height={controller.avatarLayerProps.height}
|
|
72
72
|
zIndex={controller.avatarLayerProps.zIndex}
|
|
73
|
+
showBanner={controller.avatarLayerProps.showBanner}
|
|
74
|
+
animateBanner={controller.avatarLayerProps.animateBanner}
|
|
75
|
+
startupAnimationActive={controller.avatarLayerProps.startupAnimationActive}
|
|
73
76
|
/>
|
|
74
77
|
|
|
75
78
|
{controller.isListeningDim ? (
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { memo, useCallback } from "react";
|
|
1
|
+
import { memo, useCallback, useEffect, useRef } from "react";
|
|
2
2
|
import type { RefObject } from "react";
|
|
3
3
|
import type { DaemonAvatarRenderable } from "../../avatar/DaemonAvatarRenderable";
|
|
4
|
+
import { BANNER_GRADIENT, DAEMON_BANNER_LINES, useGlitchyBanner } from "../../hooks/use-glitchy-banner";
|
|
4
5
|
import { DaemonState } from "../../types";
|
|
5
6
|
|
|
6
7
|
export interface AvatarLayerProps {
|
|
@@ -10,41 +11,112 @@ export interface AvatarLayerProps {
|
|
|
10
11
|
width: number;
|
|
11
12
|
height: number;
|
|
12
13
|
zIndex?: number;
|
|
14
|
+
showBanner?: boolean;
|
|
15
|
+
animateBanner?: boolean;
|
|
16
|
+
startupAnimationActive?: boolean;
|
|
13
17
|
}
|
|
14
18
|
|
|
15
19
|
function AvatarLayerImpl(props: AvatarLayerProps) {
|
|
16
|
-
const {
|
|
20
|
+
const {
|
|
21
|
+
avatarRef,
|
|
22
|
+
daemonState,
|
|
23
|
+
applyAvatarForState,
|
|
24
|
+
width,
|
|
25
|
+
height,
|
|
26
|
+
zIndex = 0,
|
|
27
|
+
showBanner = false,
|
|
28
|
+
animateBanner = false,
|
|
29
|
+
startupAnimationActive = false,
|
|
30
|
+
} = props;
|
|
31
|
+
|
|
32
|
+
// Use glitchy banner animation when animateBanner is true
|
|
33
|
+
const glitchyBanner = useGlitchyBanner(showBanner && animateBanner);
|
|
34
|
+
|
|
35
|
+
// Determine which lines/colors to use
|
|
36
|
+
const bannerLines = animateBanner ? glitchyBanner.lines : DAEMON_BANNER_LINES;
|
|
37
|
+
const bannerColors = animateBanner ? glitchyBanner.colors : BANNER_GRADIENT;
|
|
38
|
+
|
|
39
|
+
// Keep a stable callback ref so we don't detach/reattach on daemonState changes.
|
|
40
|
+
const daemonStateRef = useRef(daemonState);
|
|
41
|
+
const applyAvatarForStateRef = useRef(applyAvatarForState);
|
|
42
|
+
const startupAnimationActiveRef = useRef(startupAnimationActive);
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
daemonStateRef.current = daemonState;
|
|
46
|
+
}, [daemonState]);
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
applyAvatarForStateRef.current = applyAvatarForState;
|
|
49
|
+
}, [applyAvatarForState]);
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
startupAnimationActiveRef.current = startupAnimationActive;
|
|
52
|
+
}, [startupAnimationActive]);
|
|
17
53
|
|
|
18
54
|
const handleAvatarRef = useCallback(
|
|
19
55
|
(ref: DaemonAvatarRenderable | null) => {
|
|
56
|
+
if (ref === avatarRef.current) return;
|
|
20
57
|
avatarRef.current = ref;
|
|
21
|
-
if (ref)
|
|
22
|
-
|
|
58
|
+
if (!ref) return;
|
|
59
|
+
|
|
60
|
+
applyAvatarForStateRef.current(daemonStateRef.current);
|
|
61
|
+
if (startupAnimationActiveRef.current) {
|
|
62
|
+
ref.resetSpawn();
|
|
63
|
+
} else {
|
|
64
|
+
ref.skipSpawn();
|
|
23
65
|
}
|
|
24
66
|
},
|
|
25
|
-
[avatarRef
|
|
67
|
+
[avatarRef]
|
|
26
68
|
);
|
|
27
69
|
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
const ref = avatarRef.current;
|
|
72
|
+
if (!ref) return;
|
|
73
|
+
if (startupAnimationActive) {
|
|
74
|
+
ref.resetSpawn();
|
|
75
|
+
} else {
|
|
76
|
+
ref.skipSpawn();
|
|
77
|
+
}
|
|
78
|
+
}, [avatarRef, startupAnimationActive]);
|
|
79
|
+
|
|
28
80
|
return (
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
81
|
+
<>
|
|
82
|
+
{showBanner && (
|
|
83
|
+
<box
|
|
84
|
+
position="absolute"
|
|
85
|
+
top={6}
|
|
86
|
+
left={0}
|
|
87
|
+
width="100%"
|
|
88
|
+
alignItems="center"
|
|
89
|
+
justifyContent="center"
|
|
90
|
+
flexDirection="column"
|
|
91
|
+
zIndex={10}
|
|
92
|
+
>
|
|
93
|
+
{bannerLines.map((line, i) => (
|
|
94
|
+
<text key={i}>
|
|
95
|
+
<span fg={bannerColors[i]}>{line}</span>
|
|
96
|
+
</text>
|
|
97
|
+
))}
|
|
98
|
+
</box>
|
|
99
|
+
)}
|
|
100
|
+
<box
|
|
101
|
+
position="absolute"
|
|
102
|
+
top={0}
|
|
103
|
+
left={0}
|
|
104
|
+
width="100%"
|
|
105
|
+
height="100%"
|
|
106
|
+
alignItems="center"
|
|
107
|
+
justifyContent="center"
|
|
108
|
+
zIndex={zIndex}
|
|
109
|
+
>
|
|
110
|
+
<daemon-avatar
|
|
111
|
+
id="daemon-avatar"
|
|
112
|
+
live
|
|
113
|
+
width={width}
|
|
114
|
+
height={height}
|
|
115
|
+
respectAlpha={true}
|
|
116
|
+
ref={handleAvatarRef}
|
|
117
|
+
/>
|
|
118
|
+
</box>
|
|
119
|
+
</>
|
|
48
120
|
);
|
|
49
121
|
}
|
|
50
122
|
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
isLastTextBlockInList,
|
|
8
8
|
shouldHideContentBlock,
|
|
9
9
|
} from "../../components/ContentBlockView";
|
|
10
|
-
import { DaemonText } from "../../components/DaemonText";
|
|
11
10
|
import { GroundingBadge } from "../../components/GroundingBadge";
|
|
12
11
|
import { InlineStatusIndicator } from "../../components/InlineStatusIndicator";
|
|
13
12
|
import { StatusBar } from "../../components/StatusBar";
|
|
@@ -78,6 +77,7 @@ export interface ConversationPaneProps {
|
|
|
78
77
|
modelName?: string;
|
|
79
78
|
sessionTitle?: string;
|
|
80
79
|
isVoiceOutputEnabled?: boolean;
|
|
80
|
+
startupIntroDone?: boolean;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
function ConversationPaneImpl(props: ConversationPaneProps) {
|
|
@@ -98,6 +98,7 @@ function ConversationPaneImpl(props: ConversationPaneProps) {
|
|
|
98
98
|
modelName,
|
|
99
99
|
sessionTitle,
|
|
100
100
|
isVoiceOutputEnabled,
|
|
101
|
+
startupIntroDone = true,
|
|
101
102
|
} = props;
|
|
102
103
|
|
|
103
104
|
const { conversationHistory, currentTranscription, currentContentBlocks } = conversation;
|
|
@@ -193,7 +194,7 @@ function ConversationPaneImpl(props: ConversationPaneProps) {
|
|
|
193
194
|
/>
|
|
194
195
|
)}
|
|
195
196
|
|
|
196
|
-
{!hasInteracted && (
|
|
197
|
+
{!hasInteracted && startupIntroDone && (
|
|
197
198
|
<box
|
|
198
199
|
position="absolute"
|
|
199
200
|
left={0}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
|
+
type CliRenderer,
|
|
2
3
|
FrameBufferRenderable,
|
|
4
|
+
OptimizedBuffer,
|
|
3
5
|
RGBA,
|
|
4
6
|
TextAttributes,
|
|
5
|
-
OptimizedBuffer,
|
|
6
|
-
type CliRenderer,
|
|
7
7
|
} from "@opentui/core";
|
|
8
8
|
import { SuperSampleType, ThreeCliRenderer } from "@opentui/core/3d";
|
|
9
9
|
import {
|
|
10
|
-
createDaemonRig,
|
|
11
10
|
type DaemonColorTheme,
|
|
12
11
|
type DaemonRig,
|
|
13
12
|
type ToolCategory,
|
|
13
|
+
createDaemonRig,
|
|
14
14
|
} from "./daemon-avatar-rig";
|
|
15
15
|
|
|
16
16
|
export type { ToolCategory } from "./daemon-avatar-rig";
|
|
@@ -30,6 +30,7 @@ export class DaemonAvatarRenderable extends FrameBufferRenderable {
|
|
|
30
30
|
private pendingTheme: DaemonColorTheme | null = null;
|
|
31
31
|
private pendingIntensity: { value: number; immediate: boolean } | null = null;
|
|
32
32
|
private pendingAudioLevel: { value: number; immediate: boolean } | null = null;
|
|
33
|
+
private pendingSpawnAction: "reset" | "skip" | null = null;
|
|
33
34
|
|
|
34
35
|
private getDesiredAspectRatio(width: number, height: number): number {
|
|
35
36
|
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return 1;
|
|
@@ -138,6 +139,14 @@ export class DaemonAvatarRenderable extends FrameBufferRenderable {
|
|
|
138
139
|
if (this.pendingAudioLevel) {
|
|
139
140
|
this.rig.setAudioLevel(this.pendingAudioLevel.value, { immediate: this.pendingAudioLevel.immediate });
|
|
140
141
|
}
|
|
142
|
+
if (this.pendingSpawnAction) {
|
|
143
|
+
if (this.pendingSpawnAction === "reset") {
|
|
144
|
+
this.rig.resetSpawn();
|
|
145
|
+
} else {
|
|
146
|
+
this.rig.skipSpawn();
|
|
147
|
+
}
|
|
148
|
+
this.pendingSpawnAction = null;
|
|
149
|
+
}
|
|
141
150
|
this.renderBuffer = OptimizedBuffer.create(
|
|
142
151
|
this.frameBuffer.width,
|
|
143
152
|
this.frameBuffer.height,
|
|
@@ -340,4 +349,18 @@ export class DaemonAvatarRenderable extends FrameBufferRenderable {
|
|
|
340
349
|
this.rig.triggerTypingPulse();
|
|
341
350
|
}
|
|
342
351
|
}
|
|
352
|
+
|
|
353
|
+
public resetSpawn(): void {
|
|
354
|
+
this.pendingSpawnAction = "reset";
|
|
355
|
+
if (this.rig) {
|
|
356
|
+
this.rig.resetSpawn();
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
public skipSpawn(): void {
|
|
361
|
+
this.pendingSpawnAction = "skip";
|
|
362
|
+
if (this.rig) {
|
|
363
|
+
this.rig.skipSpawn();
|
|
364
|
+
}
|
|
365
|
+
}
|
|
343
366
|
}
|