@makefinks/daemon 0.2.0 → 0.3.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/src/app/App.tsx CHANGED
@@ -1,51 +1,15 @@
1
1
  import { Toaster } from "@opentui-ui/toast/react";
2
- import type { ScrollBoxRenderable, TextareaRenderable } from "@opentui/core";
3
- import { extend, useRenderer } from "@opentui/react";
2
+ import { extend } from "@opentui/react";
4
3
  import "opentui-spinner/react";
5
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
6
- import { setOpenRouterProviderTag, setResponseModel } from "../ai/model-config";
4
+
7
5
  import { DaemonAvatarRenderable } from "../avatar/DaemonAvatarRenderable";
8
- import { useAppAudioDevicesLoader } from "../hooks/use-app-audio-devices-loader";
9
- import { useAppCallbacks } from "../hooks/use-app-callbacks";
10
- import { useAppContextBuilder } from "../hooks/use-app-context-builder";
11
- import { useAppDisplayState } from "../hooks/use-app-display-state";
12
- import { useAppMenus } from "../hooks/use-app-menus";
13
- import { useAppModel } from "../hooks/use-app-model";
14
- import { useAppPreferencesBootstrap } from "../hooks/use-app-preferences-bootstrap";
15
- import { useAppSessions } from "../hooks/use-app-sessions";
16
- import { useAppSettings } from "../hooks/use-app-settings";
17
- import { useConversationManager } from "../hooks/use-conversation-manager";
18
- import { useCopyOnSelect } from "../hooks/use-copy-on-select";
19
- import { useDaemonEvents } from "../hooks/use-daemon-events";
20
- import { useDaemonKeyboard } from "../hooks/use-daemon-keyboard";
21
- import { useGrounding } from "../hooks/use-grounding";
22
- import { useGroundingMenuController } from "../hooks/use-grounding-menu-controller";
23
- import { useInputHistory } from "../hooks/use-input-history";
24
- import { useOverlayController } from "../hooks/use-overlay-controller";
25
- import { usePlaywrightNotification } from "../hooks/use-playwright-notification";
26
- import { useReasoningAnimation } from "../hooks/use-reasoning-animation";
27
- import { useResponseTimer } from "../hooks/use-response-timer";
6
+ import { useAppController } from "../hooks/use-app-controller";
28
7
  import { ToolApprovalProvider } from "../hooks/use-tool-approval";
29
- import { useTypingMode } from "../hooks/use-typing-mode";
30
- import { useVoiceDependenciesNotification } from "../hooks/use-voice-dependencies-notification";
31
8
  import { AppProvider } from "../state/app-context";
32
- import { daemonEvents } from "../state/daemon-events";
33
- import { getDaemonManager } from "../state/daemon-state";
34
- import { deleteSession } from "../state/session-store";
35
- import type { AppPreferences, AudioDevice, OnboardingStep } from "../types";
36
- import { DaemonState } from "../types";
37
9
  import { COLORS } from "../ui/constants";
38
- import { getSoxInstallHint, isSoxAvailable, setAudioDevice } from "../voice/audio-recorder";
39
10
  import { AppOverlays } from "./components/AppOverlays";
40
11
  import { AvatarLayer } from "./components/AvatarLayer";
41
- import {
42
- type ConversationDisplayState,
43
- ConversationPane,
44
- type ProgressDisplayState,
45
- type ReasoningDisplayState,
46
- type StatusDisplayState,
47
- type TypingInputState,
48
- } from "./components/ConversationPane";
12
+ import { ConversationPane } from "./components/ConversationPane";
49
13
 
50
14
  const INITIAL_STATUS_TOP = "70%";
51
15
 
@@ -79,575 +43,7 @@ extend({
79
43
  });
80
44
 
81
45
  export function App() {
82
- const renderer = useRenderer();
83
-
84
- const [onboardingActive, setOnboardingActive] = useState(false);
85
- usePlaywrightNotification({ enabled: !onboardingActive });
86
- useVoiceDependenciesNotification({ enabled: !onboardingActive });
87
- const { handleCopyOnSelectMouseUp } = useCopyOnSelect();
88
-
89
- const {
90
- reasoningQueue,
91
- reasoningDisplay,
92
- fullReasoning,
93
- setReasoningQueue,
94
- setFullReasoning,
95
- fullReasoningRef,
96
- clearReasoningState,
97
- clearReasoningTicker,
98
- } = useReasoningAnimation();
99
-
100
- const [preferencesLoaded, setPreferencesLoaded] = useState(false);
101
-
102
- const menus = useAppMenus();
103
- const {
104
- showDeviceMenu,
105
- setShowDeviceMenu,
106
- showSettingsMenu,
107
- setShowSettingsMenu,
108
- showModelMenu,
109
- setShowModelMenu,
110
- showProviderMenu,
111
- setShowProviderMenu,
112
- showSessionMenu,
113
- setShowSessionMenu,
114
- showHotkeysPane,
115
- setShowHotkeysPane,
116
- showGroundingMenu,
117
- setShowGroundingMenu,
118
- showUrlMenu,
119
- setShowUrlMenu,
120
- } = menus;
121
-
122
- const {
123
- currentSessionId,
124
- setCurrentSessionIdSafe,
125
- currentSessionIdRef,
126
- ensureSessionId,
127
- setSessions,
128
- sessionMenuItems,
129
- handleFirstMessage,
130
- } = useAppSessions({ showSessionMenu });
131
-
132
- const { latestGroundingMap, hasGrounding } = useGrounding(currentSessionId);
133
- const {
134
- groundingInitialIndex,
135
- groundingSelectedIndex,
136
- setGroundingSelectedIndex,
137
- onGroundingSelect,
138
- onGroundingIndexChange,
139
- } = useGroundingMenuController({ sessionId: currentSessionId, latestGroundingMap });
140
-
141
- const appSettings = useAppSettings();
142
- const {
143
- interactionMode,
144
- setInteractionMode,
145
- voiceInteractionType,
146
- setVoiceInteractionType,
147
- speechSpeed,
148
- setSpeechSpeed,
149
- reasoningEffort,
150
- setReasoningEffort,
151
- bashApprovalLevel,
152
- setBashApprovalLevel,
153
- showFullReasoning,
154
- setShowFullReasoning,
155
- showToolOutput,
156
- setShowToolOutput,
157
- canEnableVoiceOutput,
158
- } = appSettings;
159
-
160
- const appModel = useAppModel({
161
- preferencesLoaded,
162
- showProviderMenu,
163
- });
164
- const {
165
- currentModelId,
166
- setCurrentModelId,
167
- currentOpenRouterProviderTag,
168
- setCurrentOpenRouterProviderTag,
169
- modelsWithPricing,
170
- openRouterModels,
171
- openRouterModelsLoading,
172
- openRouterModelsUpdatedAt,
173
- providerMenuItems,
174
- refreshOpenRouterModels,
175
- } = appModel;
176
-
177
- const { addToHistory, navigateUp, navigateDown, resetNavigation } = useInputHistory();
178
-
179
- const {
180
- daemonState,
181
- conversationHistory,
182
- currentTranscription,
183
- currentResponse,
184
- currentContentBlocks,
185
- error,
186
- sessionUsage,
187
- modelMetadata,
188
- avatarRef,
189
- currentUserInputRef,
190
- hydrateConversationHistory,
191
- setCurrentTranscription,
192
- setCurrentResponse,
193
- clearCurrentContentBlocks,
194
- resetSessionUsage,
195
- setSessionUsage,
196
- applyAvatarForState,
197
- } = useDaemonEvents({
198
- currentModelId,
199
- preferencesLoaded,
200
- setReasoningQueue,
201
- setFullReasoning,
202
- clearReasoningState,
203
- clearReasoningTicker,
204
- fullReasoningRef,
205
- sessionId: currentSessionId,
206
- sessionIdRef: currentSessionIdRef,
207
- ensureSessionId,
208
- addToHistory,
209
- onFirstMessage: handleFirstMessage,
210
- });
211
-
212
- const {
213
- setTypingInput,
214
- typingTextareaRef,
215
- handleTypingContentChange,
216
- handleTypingSubmit,
217
- prefillTypingInput,
218
- handleHistoryUp,
219
- handleHistoryDown,
220
- } = useTypingMode({
221
- daemonState,
222
- currentUserInputRef,
223
- setCurrentTranscription,
224
- onTypingActivity: useCallback(() => {
225
- avatarRef.current?.triggerTypingPulse();
226
- }, [avatarRef]),
227
- navigateUp,
228
- navigateDown,
229
- resetNavigation,
230
- });
231
-
232
- const { responseElapsedMs } = useResponseTimer({ daemonState });
233
-
234
- const [loadedPreferences, setLoadedPreferences] = useState<AppPreferences | null>(null);
235
- const [onboardingStep, setOnboardingStep] = useState<OnboardingStep>("intro");
236
- const [devices, setDevices] = useState<AudioDevice[]>([]);
237
- const [currentDevice, setCurrentDevice] = useState<string | undefined>(undefined);
238
- const [currentOutputDevice, setCurrentOutputDevice] = useState<string | undefined>(undefined);
239
- const [resetNotification, setResetNotification] = useState<string>("");
240
- const [apiKeyMissingError, setApiKeyMissingError] = useState<string>("");
241
- const [escPendingCancel, setEscPendingCancel] = useState(false);
242
- const [deviceLoadTimedOut, setDeviceLoadTimedOut] = useState(false);
243
- const soxAvailable = useMemo(() => isSoxAvailable(), []);
244
- const soxInstallHint = useMemo(() => getSoxInstallHint(), []);
245
- const apiKeyTextareaRef = useRef<TextareaRenderable | null>(null);
246
- const conversationScrollRef = useRef<ScrollBoxRenderable | null>(null);
247
-
248
- const manager = getDaemonManager();
249
- const supportsReasoning = modelMetadata?.supportsReasoning ?? false;
250
-
251
- useEffect(() => {
252
- const handleTranscriptionReady = (text: string) => {
253
- prefillTypingInput(text);
254
- };
255
- daemonEvents.on("transcriptionReady", handleTranscriptionReady);
256
- return () => {
257
- daemonEvents.off("transcriptionReady", handleTranscriptionReady);
258
- };
259
- }, [manager, prefillTypingInput]);
260
-
261
- useEffect(() => {
262
- manager.setEnsureSessionId(() => ensureSessionId());
263
- return () => manager.setEnsureSessionId(null);
264
- }, [manager, ensureSessionId]);
265
-
266
- const { persistPreferences } = useAppPreferencesBootstrap({
267
- manager,
268
- setCurrentModelId,
269
- setCurrentOpenRouterProviderTag,
270
- setCurrentDevice,
271
- setCurrentOutputDevice,
272
- setInteractionMode,
273
- setVoiceInteractionType,
274
- setSpeechSpeed,
275
- setReasoningEffort,
276
- setBashApprovalLevel,
277
- setShowFullReasoning,
278
- setShowToolOutput,
279
- setLoadedPreferences,
280
- setOnboardingActive,
281
- setOnboardingStep,
282
- setPreferencesLoaded,
283
- });
284
-
285
- useAppAudioDevicesLoader({
286
- preferencesLoaded,
287
- showDeviceMenu,
288
- onboardingStep,
289
- setDevices,
290
- setCurrentDevice,
291
- setDeviceLoadTimedOut,
292
- });
293
-
294
- const conversationManager = useConversationManager({
295
- conversationHistory,
296
- sessionUsage,
297
- currentSessionId,
298
- ensureSessionId,
299
- setCurrentSessionIdSafe,
300
- currentSessionIdRef,
301
- setSessions,
302
- hydrateConversationHistory,
303
- setCurrentTranscription,
304
- setCurrentResponse,
305
- clearCurrentContentBlocks,
306
- clearReasoningState,
307
- resetSessionUsage,
308
- setSessionUsage,
309
- currentUserInputRef,
310
- });
311
- const { clearConversationState, loadSessionById, startNewSession, undoLastTurn } = conversationManager;
312
- const startNewSessionAndReset = useCallback(() => {
313
- startNewSession();
314
- applyAvatarForState(DaemonState.IDLE);
315
- }, [startNewSession, applyAvatarForState]);
316
-
317
- const {
318
- handleDeviceSelect,
319
- handleOutputDeviceSelect,
320
- handleModelSelect,
321
- handleProviderSelect,
322
- toggleInteractionMode,
323
- completeOnboarding,
324
- handleApiKeySubmit,
325
- } = useAppCallbacks({
326
- currentModelId,
327
- setCurrentModelId,
328
- setCurrentDevice,
329
- setCurrentOutputDevice,
330
- setCurrentOpenRouterProviderTag,
331
- setInteractionMode,
332
- setVoiceInteractionType,
333
- setSpeechSpeed,
334
- setReasoningEffort,
335
- persistPreferences,
336
- loadedPreferences,
337
- onboardingStep,
338
- setOnboardingStep,
339
- apiKeyTextareaRef,
340
- setShowDeviceMenu,
341
- setShowModelMenu,
342
- setShowProviderMenu,
343
- setShowSettingsMenu,
344
- setShowSessionMenu,
345
- setOnboardingActive,
346
- });
347
-
348
- const handleSessionSelect = useCallback(
349
- (selectedIdx: number) => {
350
- const item = sessionMenuItems[selectedIdx];
351
- if (!item) return;
352
- void loadSessionById(item.id);
353
- },
354
- [sessionMenuItems, loadSessionById]
355
- );
356
-
357
- const handleSessionDelete = useCallback(
358
- (selectedIdx: number) => {
359
- const item = sessionMenuItems[selectedIdx];
360
- if (!item) return;
361
-
362
- void (async () => {
363
- await deleteSession(item.id);
364
- setSessions((prev) => prev.filter((s) => s.id !== item.id));
365
-
366
- if (currentSessionIdRef.current === item.id) {
367
- clearConversationState();
368
- setCurrentSessionIdSafe(null);
369
- setResetNotification("SESSION DELETED");
370
- setTimeout(() => setResetNotification(""), 2000);
371
- }
372
- })();
373
- },
374
- [sessionMenuItems, setSessions, currentSessionIdRef, clearConversationState, setCurrentSessionIdSafe]
375
- );
376
-
377
- useEffect(() => {
378
- setGroundingSelectedIndex(0);
379
- }, [currentSessionId]);
380
-
381
- const keyboardActions = useMemo(
382
- () => ({
383
- setShowDeviceMenu,
384
- setShowSettingsMenu,
385
- setShowModelMenu,
386
- setShowProviderMenu,
387
- setShowSessionMenu,
388
- setShowHotkeysPane,
389
- setShowGroundingMenu,
390
- setShowUrlMenu,
391
- setTypingInput,
392
- setCurrentTranscription,
393
- setCurrentResponse,
394
- setApiKeyMissingError,
395
- setEscPendingCancel,
396
- setShowFullReasoning,
397
- setShowToolOutput,
398
- persistPreferences,
399
- clearReasoningState,
400
- currentUserInputRef,
401
- conversationScrollRef,
402
- startNewSession: startNewSessionAndReset,
403
- undoLastTurn,
404
- }),
405
- [
406
- setShowDeviceMenu,
407
- setShowSettingsMenu,
408
- setShowModelMenu,
409
- setShowProviderMenu,
410
- setShowSessionMenu,
411
- setShowHotkeysPane,
412
- setShowGroundingMenu,
413
- setShowUrlMenu,
414
- setTypingInput,
415
- setCurrentTranscription,
416
- setCurrentResponse,
417
- setApiKeyMissingError,
418
- setEscPendingCancel,
419
- setShowFullReasoning,
420
- setShowToolOutput,
421
- persistPreferences,
422
- clearReasoningState,
423
- currentUserInputRef,
424
- conversationScrollRef,
425
- startNewSessionAndReset,
426
- undoLastTurn,
427
- ]
428
- );
429
-
430
- const hasInteracted =
431
- conversationHistory.length > 0 || currentTranscription.length > 0 || currentContentBlocks.length > 0;
432
-
433
- const { isOverlayOpen } = useOverlayController(
434
- {
435
- showDeviceMenu,
436
- showSettingsMenu,
437
- showModelMenu,
438
- showProviderMenu,
439
- showSessionMenu,
440
- showHotkeysPane,
441
- showGroundingMenu,
442
- showUrlMenu,
443
- onboardingActive,
444
- },
445
- {
446
- setShowDeviceMenu,
447
- setShowSettingsMenu,
448
- setShowModelMenu,
449
- setShowProviderMenu,
450
- setShowSessionMenu,
451
- setShowHotkeysPane,
452
- setShowGroundingMenu,
453
- setShowUrlMenu,
454
- }
455
- );
456
-
457
- useDaemonKeyboard(
458
- {
459
- isOverlayOpen,
460
- escPendingCancel,
461
- hasInteracted,
462
- hasGrounding,
463
- showFullReasoning,
464
- showToolOutput,
465
- },
466
- keyboardActions
467
- );
468
-
469
- const displayState = useAppDisplayState({
470
- daemonState,
471
- currentContentBlocks,
472
- currentResponse,
473
- reasoningDisplay,
474
- reasoningQueue,
475
- responseElapsedMs,
476
- hasInteracted,
477
- currentModelId,
478
- modelMetadata,
479
- preferencesLoaded,
480
- currentSessionId,
481
- sessionMenuItems,
482
- terminalWidth: renderer.terminalWidth,
483
- terminalHeight: renderer.terminalHeight,
484
- });
485
-
486
- const {
487
- isToolCalling,
488
- isReasoning,
489
- statusText,
490
- statusColor,
491
- showWorkingSpinner,
492
- workingSpinnerLabel,
493
- modelName,
494
- sessionTitle,
495
- avatarWidth,
496
- avatarHeight,
497
- frostColor,
498
- isListening,
499
- isListeningDim,
500
- } = displayState;
501
-
502
- const statusBarHeight = hasInteracted ? (apiKeyMissingError ? 5 : 3) : 0;
503
-
504
- useEffect(() => {
505
- if (daemonState === DaemonState.IDLE) {
506
- setEscPendingCancel(false);
507
- }
508
- }, [daemonState]);
509
-
510
- const conversationDisplayState: ConversationDisplayState = {
511
- conversationHistory,
512
- currentTranscription,
513
- currentResponse,
514
- currentContentBlocks,
515
- };
516
-
517
- const statusDisplayState: StatusDisplayState = {
518
- daemonState,
519
- statusText,
520
- statusColor,
521
- apiKeyMissingError,
522
- error,
523
- resetNotification,
524
- escPendingCancel,
525
- };
526
-
527
- const reasoningDisplayState: ReasoningDisplayState = {
528
- showFullReasoning,
529
- showToolOutput,
530
- reasoningQueue,
531
- reasoningDisplay,
532
- fullReasoning,
533
- };
534
-
535
- const progressDisplayState: ProgressDisplayState = {
536
- showWorkingSpinner,
537
- workingSpinnerLabel,
538
- isToolCalling,
539
- responseElapsedMs,
540
- };
541
-
542
- const typingInputState: TypingInputState = {
543
- typingTextareaRef,
544
- conversationScrollRef,
545
- onTypingContentChange: handleTypingContentChange,
546
- onTypingSubmit: handleTypingSubmit,
547
- onHistoryUp: handleHistoryUp,
548
- onHistoryDown: handleHistoryDown,
549
- };
550
-
551
- const appContextValue = useAppContextBuilder({
552
- menus: {
553
- showDeviceMenu,
554
- setShowDeviceMenu,
555
- showSettingsMenu,
556
- setShowSettingsMenu,
557
- showModelMenu,
558
- setShowModelMenu,
559
- showProviderMenu,
560
- setShowProviderMenu,
561
- showSessionMenu,
562
- setShowSessionMenu,
563
- showHotkeysPane,
564
- setShowHotkeysPane,
565
- showGroundingMenu,
566
- setShowGroundingMenu,
567
- showUrlMenu,
568
- setShowUrlMenu,
569
- },
570
- device: {
571
- devices,
572
- currentDevice,
573
- setCurrentDevice,
574
- currentOutputDevice,
575
- setCurrentOutputDevice,
576
- deviceLoadTimedOut,
577
- soxAvailable,
578
- soxInstallHint,
579
- },
580
- settings: {
581
- interactionMode,
582
- voiceInteractionType,
583
- speechSpeed,
584
- reasoningEffort,
585
- bashApprovalLevel,
586
- supportsReasoning,
587
- canEnableVoiceOutput,
588
- showFullReasoning,
589
- setShowFullReasoning,
590
- showToolOutput,
591
- setShowToolOutput,
592
- setBashApprovalLevel,
593
- persistPreferences,
594
- },
595
- model: {
596
- curatedModels: modelsWithPricing,
597
- openRouterModels,
598
- openRouterModelsLoading,
599
- openRouterModelsUpdatedAt,
600
- currentModelId,
601
- setCurrentModelId,
602
- providerMenuItems,
603
- currentOpenRouterProviderTag,
604
- },
605
- session: {
606
- sessionMenuItems,
607
- currentSessionId,
608
- },
609
- grounding: {
610
- latestGroundingMap,
611
- groundingInitialIndex,
612
- groundingSelectedIndex,
613
- setGroundingSelectedIndex,
614
- },
615
- onboarding: {
616
- onboardingActive,
617
- onboardingStep,
618
- setOnboardingStep,
619
- onboardingPreferences: loadedPreferences,
620
- apiKeyTextareaRef,
621
- },
622
- deviceCallbacks: {
623
- onDeviceSelect: handleDeviceSelect,
624
- onOutputDeviceSelect: handleOutputDeviceSelect,
625
- },
626
- settingsCallbacks: {
627
- onToggleInteractionMode: toggleInteractionMode,
628
- onSetVoiceInteractionType: setVoiceInteractionType,
629
- onSetSpeechSpeed: setSpeechSpeed,
630
- onSetReasoningEffort: setReasoningEffort,
631
- onSetBashApprovalLevel: setBashApprovalLevel,
632
- },
633
- modelCallbacks: {
634
- onModelSelect: handleModelSelect,
635
- onModelRefresh: refreshOpenRouterModels,
636
- onProviderSelect: handleProviderSelect,
637
- },
638
- sessionCallbacks: {
639
- onSessionSelect: handleSessionSelect,
640
- onSessionDelete: handleSessionDelete,
641
- },
642
- groundingCallbacks: {
643
- onGroundingSelect,
644
- onGroundingIndexChange,
645
- },
646
- onboardingCallbacks: {
647
- onKeySubmit: handleApiKeySubmit,
648
- completeOnboarding,
649
- },
650
- });
46
+ const controller = useAppController({ initialStatusTop: INITIAL_STATUS_TOP });
651
47
 
652
48
  return (
653
49
  <ToolApprovalProvider>
@@ -656,7 +52,7 @@ export function App() {
656
52
  width="100%"
657
53
  height="100%"
658
54
  backgroundColor={COLORS.BACKGROUND}
659
- onMouseUp={handleCopyOnSelectMouseUp}
55
+ onMouseUp={controller.handleCopyOnSelectMouseUp}
660
56
  >
661
57
  <>
662
58
  <Toaster
@@ -668,18 +64,18 @@ export function App() {
668
64
  />
669
65
 
670
66
  <AvatarLayer
671
- avatarRef={avatarRef}
672
- daemonState={daemonState}
673
- applyAvatarForState={applyAvatarForState}
674
- width={avatarWidth}
675
- height={avatarHeight}
676
- zIndex={isListening && hasInteracted ? 2 : 0}
67
+ avatarRef={controller.avatarLayerProps.avatarRef}
68
+ daemonState={controller.avatarLayerProps.daemonState}
69
+ applyAvatarForState={controller.avatarLayerProps.applyAvatarForState}
70
+ width={controller.avatarLayerProps.width}
71
+ height={controller.avatarLayerProps.height}
72
+ zIndex={controller.avatarLayerProps.zIndex}
677
73
  />
678
74
 
679
- {isListeningDim ? (
75
+ {controller.isListeningDim ? (
680
76
  <box
681
77
  position="absolute"
682
- top={statusBarHeight}
78
+ top={controller.listeningDimTop}
683
79
  left={0}
684
80
  width="100%"
685
81
  height="100%"
@@ -688,31 +84,17 @@ export function App() {
688
84
  />
689
85
  ) : null}
690
86
 
691
- <box flexDirection="column" width="100%" height="100%" zIndex={isListening ? 0 : 1}>
692
- <ConversationPane
693
- conversation={conversationDisplayState}
694
- status={statusDisplayState}
695
- reasoning={reasoningDisplayState}
696
- progress={progressDisplayState}
697
- typing={typingInputState}
698
- sessionUsage={sessionUsage}
699
- modelMetadata={modelMetadata}
700
- hasInteracted={hasInteracted}
701
- frostColor={frostColor}
702
- initialStatusTop={INITIAL_STATUS_TOP}
703
- hasGrounding={hasGrounding}
704
- groundingCount={latestGroundingMap?.items.length}
705
- modelName={modelName}
706
- sessionTitle={sessionTitle}
707
- isVoiceOutputEnabled={interactionMode === "voice"}
708
- />
87
+ <box
88
+ flexDirection="column"
89
+ width="100%"
90
+ height="100%"
91
+ zIndex={controller.conversationContainerZIndex}
92
+ >
93
+ <ConversationPane {...controller.conversationPaneProps} />
709
94
  </box>
710
95
 
711
- <AppProvider value={appContextValue}>
712
- <AppOverlays
713
- conversationHistory={conversationHistory}
714
- currentContentBlocks={currentContentBlocks}
715
- />
96
+ <AppProvider value={controller.appContextValue}>
97
+ <AppOverlays {...controller.overlaysProps} />
716
98
  </AppProvider>
717
99
  </>
718
100
  </box>