@agent-native/core 0.98.0 → 0.98.1

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 (243) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +24 -0
  3. package/corpus/core/package.json +2 -2
  4. package/corpus/core/src/action-change-marker.ts +8 -1
  5. package/corpus/core/src/changelog/parse.ts +8 -3
  6. package/corpus/core/src/client/AgentPanel.tsx +2 -3
  7. package/corpus/core/src/client/AssistantChat.tsx +83 -39
  8. package/corpus/core/src/client/analytics.ts +29 -0
  9. package/corpus/core/src/client/blocks/library/wireframe-kit.tsx +16 -2
  10. package/corpus/core/src/client/chat-model-groups.ts +28 -0
  11. package/corpus/core/src/client/composer/RealtimeVoiceMode.tsx +442 -85
  12. package/corpus/core/src/client/composer/TiptapComposer.tsx +46 -29
  13. package/corpus/core/src/client/composer/VoiceButton.tsx +2 -1
  14. package/corpus/core/src/client/composer/index.ts +5 -0
  15. package/corpus/core/src/client/composer/useRealtimeVoiceMode.tsx +1206 -67
  16. package/corpus/core/src/client/index.ts +5 -0
  17. package/corpus/core/src/client/progress/RunsTray.tsx +13 -1
  18. package/corpus/core/src/client/settings/SecretsSection.tsx +387 -266
  19. package/corpus/core/src/client/settings/SettingsPanel.tsx +117 -38
  20. package/corpus/core/src/client/use-action.ts +12 -0
  21. package/corpus/core/src/client/use-agent-chat-home-handoff.ts +8 -1
  22. package/corpus/core/src/client/use-agent-engine-configured.ts +17 -3
  23. package/corpus/core/src/client/use-db-sync.ts +84 -34
  24. package/corpus/core/src/client/use-pausing-interval.ts +14 -4
  25. package/corpus/core/src/integrations/scope-store.ts +1 -1
  26. package/corpus/core/src/integrations/usage-budget-store.ts +1 -1
  27. package/corpus/core/src/localization/default-messages.ts +44 -1
  28. package/corpus/core/src/mcp-client/workspace-servers.ts +4 -4
  29. package/corpus/core/src/secrets/crypto.ts +28 -8
  30. package/corpus/core/src/server/action-change.ts +5 -1
  31. package/corpus/core/src/server/action-routes.ts +8 -0
  32. package/corpus/core/src/server/poll.ts +1 -0
  33. package/corpus/core/src/server/realtime-voice.ts +299 -13
  34. package/corpus/core/src/styles/agent-native.css +2 -2
  35. package/corpus/core/src/templates/default/.agents/skills/real-time-sync/SKILL.md +2 -2
  36. package/corpus/core/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +2 -2
  37. package/corpus/core/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +7 -3
  38. package/corpus/core/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +39 -9
  39. package/corpus/core/src/vite/client.ts +9 -1
  40. package/corpus/templates/analytics/.agents/skills/real-time-sync/SKILL.md +2 -2
  41. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +10 -5
  42. package/corpus/templates/analytics/AGENTS.md +9 -13
  43. package/corpus/templates/analytics/actions/navigate.ts +59 -5
  44. package/corpus/templates/analytics/actions/view-screen.ts +3 -6
  45. package/corpus/templates/analytics/app/components/ProviderCorpusJobNotifier.tsx +20 -3
  46. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +2 -1
  47. package/corpus/templates/analytics/app/components/layout/Header.tsx +1 -1
  48. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +21 -6
  49. package/corpus/templates/analytics/app/global.css +19 -20
  50. package/corpus/templates/analytics/app/lib/provider-corpus-job-sync.ts +26 -0
  51. package/corpus/templates/analytics/app/pages/Settings.tsx +35 -341
  52. package/corpus/templates/analytics/app/pages/monitoring/ErrorsPanel.tsx +38 -7
  53. package/corpus/templates/analytics/app/pages/monitoring/MonitoringPage.tsx +1 -1
  54. package/corpus/templates/analytics/app/pages/monitoring/errors/i18n.ts +3 -0
  55. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +333 -272
  56. package/corpus/templates/analytics/app/pages/sessions/SessionDevToolsPanel.tsx +72 -17
  57. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +297 -108
  58. package/corpus/templates/analytics/app/root.tsx +28 -8
  59. package/corpus/templates/analytics/changelog/2026-06-24-first-party-signup-and-pageview-panels-now-honor-dashboard-filters.md +2 -1
  60. package/corpus/templates/analytics/changelog/2026-06-25-referrer-dashboard-panels-show-external-domains.md +2 -1
  61. package/corpus/templates/analytics/changelog/2026-07-06-mobile-chat-header-is-more-compact-and-the-menu-button-opens-navigation.md +1 -0
  62. package/corpus/templates/analytics/changelog/2026-07-08-dashboard-history-and-chat-context.md +1 -0
  63. package/corpus/templates/analytics/changelog/2026-07-08-settings-are-cleaner-searchable-and-alerts-have-their-own-tab.md +1 -0
  64. package/corpus/templates/analytics/changelog/2026-07-08-sidebar-sections-stay-collapsed-until-opened.md +1 -0
  65. package/corpus/templates/analytics/changelog/2026-07-11-analytics-no-longer-repeatedly-restarts-background-requests-.md +6 -0
  66. package/corpus/templates/analytics/changelog/2026-07-11-analytics-only-asks-for-replay-storage-when-you-enable-sessi.md +6 -0
  67. package/corpus/templates/analytics/changelog/2026-07-11-charts-now-show-their-loading-skeleton-while-refreshing.md +6 -0
  68. package/corpus/templates/analytics/changelog/2026-07-11-realtime-voice-now-shows-a-clear-connection-indicator-comple.md +6 -0
  69. package/corpus/templates/analytics/changelog/2026-07-11-replay-event-counts-stay-hidden-until-recordings-finish-load.md +6 -0
  70. package/corpus/templates/analytics/changelog/2026-07-11-replay-storage-key-mismatches-show-actionable-setup-guidance.md +6 -0
  71. package/corpus/templates/analytics/changelog/2026-07-11-replay-timelines-group-continuous-scrolling-and-dev-tools-co.md +6 -0
  72. package/corpus/templates/analytics/changelog/2026-07-11-session-filters-now-stay-compact-in-a-settings-menu-at-mediu.md +6 -0
  73. package/corpus/templates/analytics/changelog/2026-07-11-session-replay-console-errors-now-link-to-matching-issues-or.md +6 -0
  74. package/corpus/templates/analytics/changelog/2026-07-11-session-replays-load-large-recordings-faster-and-surface-fai.md +6 -0
  75. package/corpus/templates/analytics/changelog/2026-07-11-session-replays-preserve-recorded-styles-and-viewport-layout.md +6 -0
  76. package/corpus/templates/analytics/changelog/2026-07-11-session-replays-stay-centered-instead-of-clipping-wide-recor.md +6 -0
  77. package/corpus/templates/analytics/package.json +1 -1
  78. package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +1 -303
  79. package/corpus/templates/analytics/server/handlers/session-replay.ts +57 -0
  80. package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +1 -1
  81. package/corpus/templates/analytics/server/lib/demo-dashboards.ts +16 -3
  82. package/corpus/templates/analytics/server/lib/session-replay-agent-context.ts +12 -0
  83. package/corpus/templates/analytics/server/lib/session-replay.ts +208 -2
  84. package/corpus/templates/analytics/server/plugins/onboarding.ts +7 -79
  85. package/corpus/templates/analytics/server/routes/api/session-replay/recordings/[recordingId]/chunks.get.ts +1 -0
  86. package/corpus/templates/assets/.agents/skills/real-time-sync/SKILL.md +2 -2
  87. package/corpus/templates/assets/app/global.css +4 -4
  88. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +0 -1
  89. package/corpus/templates/assets/app/routes/library.tsx +0 -1
  90. package/corpus/templates/brain/.agents/skills/real-time-sync/SKILL.md +2 -2
  91. package/corpus/templates/brain/app/global.css +5 -5
  92. package/corpus/templates/brain/app/root.tsx +0 -3
  93. package/corpus/templates/calendar/.agents/skills/real-time-sync/SKILL.md +2 -2
  94. package/corpus/templates/calendar/app/root.tsx +1 -0
  95. package/corpus/templates/chat/.agents/skills/real-time-sync/SKILL.md +2 -2
  96. package/corpus/templates/clips/app/hooks/use-auto-title.ts +74 -8
  97. package/corpus/templates/clips/app/hooks/use-library.ts +12 -25
  98. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +20 -22
  99. package/corpus/templates/clips/changelog/2026-07-11-generated-titles-no-longer-cause-continuous-background-refreshes.md +6 -0
  100. package/corpus/templates/clips/changelog/2026-07-11-s3-storage-setup-stays-collapsed-until-you-choose-it-as-the-.md +6 -0
  101. package/corpus/templates/content/.agents/skills/real-time-sync/SKILL.md +2 -2
  102. package/corpus/templates/content/app/hooks/use-comments.ts +0 -1
  103. package/corpus/templates/content/changelog/2026-07-11-comments-now-update-from-live-collaboration-events-without-c.md +6 -0
  104. package/corpus/templates/design/app/components/layout/Header.tsx +1 -1
  105. package/corpus/templates/forms/.agents/skills/real-time-sync/SKILL.md +2 -2
  106. package/corpus/templates/macros/app/components/layout/AppLayout.tsx +1 -2
  107. package/corpus/templates/macros/app/root.tsx +0 -3
  108. package/corpus/templates/mail/.agents/skills/real-time-sync/SKILL.md +2 -2
  109. package/corpus/templates/mail/app/hooks/use-compose-state.ts +1 -0
  110. package/corpus/templates/mail/app/root.tsx +1 -12
  111. package/corpus/templates/plan/.agents/skills/real-time-sync/SKILL.md +2 -2
  112. package/corpus/templates/plan/app/hooks/use-navigation-state.ts +5 -2
  113. package/corpus/templates/plan/app/hooks/use-plans.ts +6 -13
  114. package/corpus/templates/plan/app/pages/PlansPage.tsx +5 -27
  115. package/corpus/templates/plan/changelog/2026-07-11-plan-refreshes-only-when-content-changes.md +6 -0
  116. package/corpus/templates/slides/app/components/comments/SlideCommentsPanel.tsx +4 -1
  117. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +1 -1
  118. package/corpus/templates/slides/app/components/layout/Header.tsx +1 -1
  119. package/corpus/templates/slides/app/context/DeckContext.tsx +1 -0
  120. package/corpus/templates/slides/app/hooks/use-slide-comments.ts +7 -31
  121. package/corpus/templates/slides/changelog/2026-07-11-comments-now-update-from-live-collaboration-events-without-c.md +6 -0
  122. package/dist/action-change-marker.d.ts +1 -0
  123. package/dist/action-change-marker.d.ts.map +1 -1
  124. package/dist/action-change-marker.js +7 -1
  125. package/dist/action-change-marker.js.map +1 -1
  126. package/dist/changelog/parse.d.ts +4 -2
  127. package/dist/changelog/parse.d.ts.map +1 -1
  128. package/dist/changelog/parse.js +5 -3
  129. package/dist/changelog/parse.js.map +1 -1
  130. package/dist/client/AgentPanel.d.ts.map +1 -1
  131. package/dist/client/AgentPanel.js +2 -3
  132. package/dist/client/AgentPanel.js.map +1 -1
  133. package/dist/client/AssistantChat.d.ts +7 -0
  134. package/dist/client/AssistantChat.d.ts.map +1 -1
  135. package/dist/client/AssistantChat.js +62 -40
  136. package/dist/client/AssistantChat.js.map +1 -1
  137. package/dist/client/analytics.d.ts.map +1 -1
  138. package/dist/client/analytics.js +26 -0
  139. package/dist/client/analytics.js.map +1 -1
  140. package/dist/client/blocks/library/wireframe-kit.d.ts +2 -0
  141. package/dist/client/blocks/library/wireframe-kit.d.ts.map +1 -1
  142. package/dist/client/blocks/library/wireframe-kit.js +9 -2
  143. package/dist/client/blocks/library/wireframe-kit.js.map +1 -1
  144. package/dist/client/chat-model-groups.d.ts.map +1 -1
  145. package/dist/client/chat-model-groups.js +24 -0
  146. package/dist/client/chat-model-groups.js.map +1 -1
  147. package/dist/client/composer/RealtimeVoiceMode.d.ts +40 -4
  148. package/dist/client/composer/RealtimeVoiceMode.d.ts.map +1 -1
  149. package/dist/client/composer/RealtimeVoiceMode.js +172 -42
  150. package/dist/client/composer/RealtimeVoiceMode.js.map +1 -1
  151. package/dist/client/composer/TiptapComposer.d.ts +4 -0
  152. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  153. package/dist/client/composer/TiptapComposer.js +47 -29
  154. package/dist/client/composer/TiptapComposer.js.map +1 -1
  155. package/dist/client/composer/VoiceButton.d.ts.map +1 -1
  156. package/dist/client/composer/VoiceButton.js +2 -2
  157. package/dist/client/composer/VoiceButton.js.map +1 -1
  158. package/dist/client/composer/index.d.ts +2 -2
  159. package/dist/client/composer/index.d.ts.map +1 -1
  160. package/dist/client/composer/index.js +1 -1
  161. package/dist/client/composer/index.js.map +1 -1
  162. package/dist/client/composer/useRealtimeVoiceMode.d.ts +100 -3
  163. package/dist/client/composer/useRealtimeVoiceMode.d.ts.map +1 -1
  164. package/dist/client/composer/useRealtimeVoiceMode.js +952 -51
  165. package/dist/client/composer/useRealtimeVoiceMode.js.map +1 -1
  166. package/dist/client/index.d.ts +1 -1
  167. package/dist/client/index.d.ts.map +1 -1
  168. package/dist/client/index.js +1 -1
  169. package/dist/client/index.js.map +1 -1
  170. package/dist/client/progress/RunsTray.d.ts.map +1 -1
  171. package/dist/client/progress/RunsTray.js +10 -1
  172. package/dist/client/progress/RunsTray.js.map +1 -1
  173. package/dist/client/settings/SecretsSection.d.ts +2 -3
  174. package/dist/client/settings/SecretsSection.d.ts.map +1 -1
  175. package/dist/client/settings/SecretsSection.js +46 -22
  176. package/dist/client/settings/SecretsSection.js.map +1 -1
  177. package/dist/client/settings/SettingsPanel.d.ts +9 -0
  178. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  179. package/dist/client/settings/SettingsPanel.js +46 -12
  180. package/dist/client/settings/SettingsPanel.js.map +1 -1
  181. package/dist/client/use-action.d.ts +2 -0
  182. package/dist/client/use-action.d.ts.map +1 -1
  183. package/dist/client/use-action.js +10 -0
  184. package/dist/client/use-action.js.map +1 -1
  185. package/dist/client/use-agent-chat-home-handoff.d.ts +2 -0
  186. package/dist/client/use-agent-chat-home-handoff.d.ts.map +1 -1
  187. package/dist/client/use-agent-chat-home-handoff.js +7 -1
  188. package/dist/client/use-agent-chat-home-handoff.js.map +1 -1
  189. package/dist/client/use-agent-engine-configured.d.ts.map +1 -1
  190. package/dist/client/use-agent-engine-configured.js +17 -3
  191. package/dist/client/use-agent-engine-configured.js.map +1 -1
  192. package/dist/client/use-db-sync.d.ts +8 -2
  193. package/dist/client/use-db-sync.d.ts.map +1 -1
  194. package/dist/client/use-db-sync.js +57 -32
  195. package/dist/client/use-db-sync.js.map +1 -1
  196. package/dist/client/use-pausing-interval.d.ts.map +1 -1
  197. package/dist/client/use-pausing-interval.js +16 -4
  198. package/dist/client/use-pausing-interval.js.map +1 -1
  199. package/dist/integrations/scope-store.js +1 -1
  200. package/dist/integrations/scope-store.js.map +1 -1
  201. package/dist/integrations/usage-budget-store.js +1 -1
  202. package/dist/integrations/usage-budget-store.js.map +1 -1
  203. package/dist/localization/default-messages.d.ts +41 -1
  204. package/dist/localization/default-messages.d.ts.map +1 -1
  205. package/dist/localization/default-messages.js +41 -1
  206. package/dist/localization/default-messages.js.map +1 -1
  207. package/dist/mcp-client/workspace-servers.js +4 -4
  208. package/dist/mcp-client/workspace-servers.js.map +1 -1
  209. package/dist/observability/routes.d.ts +5 -5
  210. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  211. package/dist/secrets/crypto.d.ts +7 -6
  212. package/dist/secrets/crypto.d.ts.map +1 -1
  213. package/dist/secrets/crypto.js +30 -11
  214. package/dist/secrets/crypto.js.map +1 -1
  215. package/dist/secrets/routes.d.ts +9 -9
  216. package/dist/server/action-change.d.ts +1 -0
  217. package/dist/server/action-change.d.ts.map +1 -1
  218. package/dist/server/action-change.js +4 -1
  219. package/dist/server/action-change.js.map +1 -1
  220. package/dist/server/action-routes.d.ts.map +1 -1
  221. package/dist/server/action-routes.js +5 -0
  222. package/dist/server/action-routes.js.map +1 -1
  223. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  224. package/dist/server/poll.d.ts.map +1 -1
  225. package/dist/server/poll.js +1 -0
  226. package/dist/server/poll.js.map +1 -1
  227. package/dist/server/realtime-voice.d.ts +7 -0
  228. package/dist/server/realtime-voice.d.ts.map +1 -1
  229. package/dist/server/realtime-voice.js +222 -12
  230. package/dist/server/realtime-voice.js.map +1 -1
  231. package/dist/styles/agent-native.css +2 -2
  232. package/dist/templates/default/.agents/skills/real-time-sync/SKILL.md +2 -2
  233. package/dist/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +2 -2
  234. package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +7 -3
  235. package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +39 -9
  236. package/dist/vite/client.d.ts.map +1 -1
  237. package/dist/vite/client.js +6 -1
  238. package/dist/vite/client.js.map +1 -1
  239. package/package.json +2 -2
  240. package/src/templates/default/.agents/skills/real-time-sync/SKILL.md +2 -2
  241. package/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +2 -2
  242. package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +7 -3
  243. package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +39 -9
@@ -18,13 +18,27 @@ export interface RealtimeVoiceModeCopy {
18
18
  showChat: string;
19
19
  hideChat: string;
20
20
  endVoiceMode: string;
21
- microphoneSettings: string;
21
+ voiceSettings: string;
22
+ settings: {
23
+ microphone: string;
24
+ defaultMicrophone: string;
25
+ microphoneSwitchFailed: string;
26
+ language: string;
27
+ autoLanguage: string;
28
+ languages: Record<Exclude<import("./useRealtimeVoiceMode.js").RealtimeVoiceLanguage, "auto">, string>;
29
+ intelligence: string;
30
+ intelligenceLevels: Record<import("./useRealtimeVoiceMode.js").RealtimeVoiceIntelligence, string>;
31
+ voiceStyle: string;
32
+ voiceChangePending: string;
33
+ voiceDescriptions: Record<import("./useRealtimeVoiceMode.js").RealtimeVoice, string>;
34
+ };
22
35
  status: Record<RealtimeVoiceModeState, string>;
23
36
  errors: {
24
37
  unsupported: string;
25
38
  responseFailed: string;
26
39
  sessionFailed: string;
27
40
  channelDisconnected: string;
41
+ connectionTimedOut?: string;
28
42
  connectionFailed: string;
29
43
  offerFailed: string;
30
44
  };
@@ -32,6 +46,7 @@ export interface RealtimeVoiceModeCopy {
32
46
  export interface RealtimeVoiceModeEntryProps {
33
47
  copy: RealtimeVoiceModeCopy;
34
48
  disabled?: boolean;
49
+ providerStatusPending?: boolean;
35
50
  open?: boolean;
36
51
  onOpenChange?: (open: boolean) => void;
37
52
  onStartVoiceMode: () => void;
@@ -48,7 +63,7 @@ export interface RealtimeVoiceModeEntryProps {
48
63
  * The first click offers voice mode without silently changing the existing
49
64
  * editable-dictation behavior.
50
65
  */
51
- export declare function RealtimeVoiceModeEntry({ copy, disabled, open: controlledOpen, onOpenChange, onStartVoiceMode, onKeepDictating, setupRequired, openAiConfigured, connectingBuilder, onConnectBuilder, onUseOpenAiKey, className, }: RealtimeVoiceModeEntryProps): import("react").JSX.Element;
66
+ export declare function RealtimeVoiceModeEntry({ copy, disabled, providerStatusPending, open: controlledOpen, onOpenChange, onStartVoiceMode, onKeepDictating, setupRequired, openAiConfigured, connectingBuilder, onConnectBuilder, onUseOpenAiKey, className, }: RealtimeVoiceModeEntryProps): import("react").JSX.Element;
52
67
  export interface RealtimeVoiceModeDockProps {
53
68
  state: RealtimeVoiceModeState;
54
69
  copy: RealtimeVoiceModeCopy;
@@ -56,13 +71,34 @@ export interface RealtimeVoiceModeDockProps {
56
71
  audioLevels?: RealtimeVoiceAudioLevelStore;
57
72
  onToggleChat: () => void;
58
73
  onEndVoiceMode: () => void;
59
- onOpenMicrophoneSettings?: () => void;
74
+ settings?: RealtimeVoiceModeInlineSettings;
60
75
  errorMessage?: string | null;
61
76
  className?: string;
62
77
  }
78
+ export interface RealtimeVoiceModeSettingOption {
79
+ value: string;
80
+ label: string;
81
+ description?: string;
82
+ }
83
+ export interface RealtimeVoiceModeSelectSetting {
84
+ label: string;
85
+ value: string;
86
+ options: readonly RealtimeVoiceModeSettingOption[];
87
+ onValueChange: (value: string) => void;
88
+ disabled?: boolean;
89
+ }
90
+ export interface RealtimeVoiceModeInlineSettings {
91
+ dialogLabel: string;
92
+ appliesNextConversationNote?: string;
93
+ microphoneError?: string;
94
+ microphone: RealtimeVoiceModeSelectSetting;
95
+ language: RealtimeVoiceModeSelectSetting;
96
+ intelligence: RealtimeVoiceModeSelectSetting;
97
+ voiceStyle: RealtimeVoiceModeSelectSetting;
98
+ }
63
99
  /**
64
100
  * Persistent voice-session control. Toggling the main orb only changes chat
65
101
  * visibility; ending the realtime session is intentionally a separate action.
66
102
  */
67
- export declare function RealtimeVoiceModeDock({ state, copy, chatVisible, audioLevels, onToggleChat, onEndVoiceMode, onOpenMicrophoneSettings, errorMessage, className, }: RealtimeVoiceModeDockProps): import("react").JSX.Element;
103
+ export declare function RealtimeVoiceModeDock({ state, copy, chatVisible, audioLevels, onToggleChat, onEndVoiceMode, settings, errorMessage, className, }: RealtimeVoiceModeDockProps): import("react").JSX.Element;
68
104
  //# sourceMappingURL=RealtimeVoiceMode.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"RealtimeVoiceMode.d.ts","sourceRoot":"","sources":["../../../src/client/composer/RealtimeVoiceMode.tsx"],"names":[],"mappings":"AA4BA,OAAO,EAEL,KAAK,4BAA4B,EAClC,MAAM,iCAAiC,CAAC;AAEzC,MAAM,MAAM,sBAAsB,GAC9B,YAAY,GACZ,WAAW,GACX,UAAU,GACV,SAAS,GACT,OAAO,GACP,QAAQ,CAAC;AAEb;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC,sBAAsB,EAAE,MAAM,CAAC,CAAC;IAC/C,MAAM,EAAE;QACN,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC;QACvB,aAAa,EAAE,MAAM,CAAC;QACtB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,gBAAgB,EAAE,MAAM,CAAC;QACzB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;CACH;AAED,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,qBAAqB,CAAC;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,IAAI,EACJ,QAAQ,EACR,IAAI,EAAE,cAAc,EACpB,YAAY,EACZ,gBAAgB,EAChB,eAAe,EACf,aAAqB,EACrB,gBAAwB,EACxB,iBAAyB,EACzB,gBAAgB,EAChB,cAAc,EACd,SAAS,GACV,EAAE,2BAA2B,+BAmH7B;AAED,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,sBAAsB,CAAC;IAC9B,IAAI,EAAE,qBAAqB,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,4BAA4B,CAAC;IAC3C,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,wBAAwB,CAAC,EAAE,MAAM,IAAI,CAAC;IACtC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAiFD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,KAAK,EACL,IAAI,EACJ,WAAW,EACX,WAAiC,EACjC,YAAY,EACZ,cAAc,EACd,wBAAwB,EACxB,YAAY,EACZ,SAAS,GACV,EAAE,0BAA0B,+BAgL5B"}
1
+ {"version":3,"file":"RealtimeVoiceMode.d.ts","sourceRoot":"","sources":["../../../src/client/composer/RealtimeVoiceMode.tsx"],"names":[],"mappings":"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"}
@@ -1,8 +1,9 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Button } from "@agent-native/toolkit/ui/button";
3
- import { IconAlertTriangle, IconLoader2, IconMicrophone, IconPhoneOff, IconSettings, IconVolume, } from "@tabler/icons-react";
4
- import { useEffect, useId, useState, useSyncExternalStore, } from "react";
5
- import { Popover, PopoverContent, PopoverTrigger, } from "../components/ui/popover.js";
3
+ import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, } from "@agent-native/toolkit/ui/select";
4
+ import { IconAlertTriangle, IconBrain, IconLanguage, IconLoader2, IconMicrophone, IconPhoneOff, IconSettings, IconVolume, } from "@tabler/icons-react";
5
+ import { useCallback, useEffect, useId, useRef, useState, useSyncExternalStore, } from "react";
6
+ import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger, } from "../components/ui/popover.js";
6
7
  import { Tooltip, TooltipContent, TooltipTrigger, } from "../components/ui/tooltip.js";
7
8
  import { cn } from "../utils.js";
8
9
  import { createRealtimeVoiceAudioLevelStore, } from "./realtime-voice-audio-level.js";
@@ -11,11 +12,16 @@ import { createRealtimeVoiceAudioLevelStore, } from "./realtime-voice-audio-leve
11
12
  * The first click offers voice mode without silently changing the existing
12
13
  * editable-dictation behavior.
13
14
  */
14
- export function RealtimeVoiceModeEntry({ copy, disabled, open: controlledOpen, onOpenChange, onStartVoiceMode, onKeepDictating, setupRequired = false, openAiConfigured = false, connectingBuilder = false, onConnectBuilder, onUseOpenAiKey, className, }) {
15
+ export function RealtimeVoiceModeEntry({ copy, disabled, providerStatusPending = false, open: controlledOpen, onOpenChange, onStartVoiceMode, onKeepDictating, setupRequired = false, openAiConfigured = false, connectingBuilder = false, onConnectBuilder, onUseOpenAiKey, className, }) {
15
16
  const [uncontrolledOpen, setUncontrolledOpen] = useState(false);
16
17
  const open = controlledOpen ?? uncontrolledOpen;
17
18
  const titleId = useId();
18
19
  const descriptionId = useId();
20
+ const [collisionBoundary, setCollisionBoundary] = useState(null);
21
+ const setTriggerNode = useCallback((node) => {
22
+ const nextBoundary = node?.closest(".agent-panel-root") ?? null;
23
+ setCollisionBoundary((currentBoundary) => currentBoundary === nextBoundary ? currentBoundary : nextBoundary);
24
+ }, []);
19
25
  const setOpen = (nextOpen) => {
20
26
  if (controlledOpen === undefined)
21
27
  setUncontrolledOpen(nextOpen);
@@ -25,11 +31,11 @@ export function RealtimeVoiceModeEntry({ copy, disabled, open: controlledOpen, o
25
31
  setOpen(false);
26
32
  callback();
27
33
  };
28
- return (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { type: "button", variant: "ghost", size: "icon", disabled: disabled, "aria-label": copy.entryButtonLabel, "aria-expanded": open, className: cn("size-7 shrink-0 text-muted-foreground hover:text-foreground", className), children: _jsx(IconMicrophone, {}) }) }) }), _jsx(TooltipContent, { children: copy.entryButtonLabel })] }), _jsx(PopoverContent, { side: "top", align: "end", sideOffset: 10, className: cn("p-4", setupRequired
29
- ? "w-[min(30rem,calc(100vw-2rem))]"
30
- : "w-[min(22rem,calc(100vw-2rem))]"), "aria-labelledby": titleId, "aria-describedby": descriptionId, children: _jsxs("div", { className: "grid gap-3", children: [_jsxs("div", { className: "grid gap-1", children: [_jsx("h2", { id: titleId, className: "text-sm font-semibold text-foreground", children: setupRequired ? copy.setupTitle : copy.promptTitle }), _jsx("p", { id: descriptionId, className: "text-sm leading-relaxed text-muted-foreground", children: setupRequired ? copy.setupDescription : copy.promptDescription })] }), _jsxs("div", { className: "flex flex-col-reverse gap-2 sm:flex-row sm:flex-nowrap sm:justify-end", children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => choose(onKeepDictating), children: copy.keepDictating }), setupRequired ? (_jsxs(_Fragment, { children: [_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => choose(onUseOpenAiKey ?? onStartVoiceMode), children: openAiConfigured
34
+ return (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { ref: setTriggerNode, type: "button", variant: "ghost", size: "icon", disabled: disabled, "aria-label": copy.entryButtonLabel, "aria-expanded": open, className: cn("size-7 shrink-0 text-muted-foreground hover:text-foreground", className), children: _jsx(IconMicrophone, {}) }) }) }), _jsx(TooltipContent, { children: copy.entryButtonLabel })] }), _jsx(PopoverContent, { side: "top", align: "end", sideOffset: 10, collisionBoundary: collisionBoundary ?? undefined, collisionPadding: 16, "data-collision-boundary": collisionBoundary ? "agent-panel" : "viewport", className: cn("p-4", setupRequired
35
+ ? "w-[min(calc(100vw-2rem),var(--radix-popover-content-available-width,30rem),30rem)]"
36
+ : "w-[min(calc(100vw-2rem),var(--radix-popover-content-available-width,22rem),22rem)]"), "aria-labelledby": titleId, "aria-describedby": descriptionId, children: _jsxs("div", { className: "grid gap-3", children: [_jsxs("div", { className: "grid gap-1", children: [_jsx("h2", { id: titleId, className: "text-sm font-semibold text-foreground", children: setupRequired ? copy.setupTitle : copy.promptTitle }), _jsx("p", { id: descriptionId, className: "text-sm leading-relaxed text-muted-foreground", children: setupRequired ? copy.setupDescription : copy.promptDescription })] }), _jsxs("div", { className: "flex flex-col-reverse gap-2 sm:flex-row sm:flex-wrap sm:justify-end", children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => choose(onKeepDictating), children: copy.keepDictating }), setupRequired ? (_jsxs(_Fragment, { children: [_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => choose(onUseOpenAiKey ?? onStartVoiceMode), children: openAiConfigured
31
37
  ? copy.startWithOpenAiKey
32
- : copy.useOpenAiKey }), _jsxs(Button, { type: "button", size: "sm", className: "whitespace-nowrap", disabled: connectingBuilder, onClick: () => choose(onConnectBuilder ?? onStartVoiceMode), children: [connectingBuilder ? (_jsx(IconLoader2, { className: "animate-spin" })) : null, copy.connectBuilder] })] })) : (_jsxs(Button, { type: "button", size: "sm", onClick: () => choose(onStartVoiceMode), children: [_jsx(IconMicrophone, {}), copy.startVoiceMode] }))] })] }) })] }));
38
+ : copy.useOpenAiKey }), _jsxs(Button, { type: "button", size: "sm", className: "whitespace-nowrap", disabled: connectingBuilder, onClick: () => choose(onConnectBuilder ?? onStartVoiceMode), children: [connectingBuilder ? (_jsx(IconLoader2, { className: "animate-spin" })) : null, copy.connectBuilder] })] })) : (_jsxs(Button, { type: "button", size: "sm", disabled: providerStatusPending, onClick: () => choose(onStartVoiceMode), children: [providerStatusPending ? (_jsx(IconLoader2, { className: "animate-spin" })) : (_jsx(IconMicrophone, {})), copy.startVoiceMode] }))] })] }) })] }));
33
39
  }
34
40
  const SILENT_AUDIO_LEVELS = createRealtimeVoiceAudioLevelStore();
35
41
  const WAVEFORM_WEIGHTS = [0.55, 0.82, 1, 0.82, 0.55];
@@ -49,67 +55,191 @@ function usePrefersReducedMotion() {
49
55
  }
50
56
  function VoiceWaveform({ level, reducedMotion, activity, }) {
51
57
  const visibleLevel = reducedMotion ? 0.45 : level;
52
- return (_jsx("span", { "aria-hidden": "true", className: "flex h-6 items-center justify-center gap-0.5", "data-realtime-voice-waveform": "true", "data-realtime-voice-waveform-activity": activity, children: WAVEFORM_WEIGHTS.map((weight, index) => (_jsx("span", { className: "h-5 w-0.5 origin-center rounded-full bg-current transition-transform duration-75 ease-out motion-reduce:transition-none", style: { transform: `scaleY(${0.2 + visibleLevel * 0.8 * weight})` } }, index))) }));
58
+ return (_jsx("span", { "aria-hidden": "true", className: "flex h-6 items-center justify-center gap-0.5", "data-realtime-voice-waveform": "true", "data-realtime-voice-waveform-activity": activity, children: WAVEFORM_WEIGHTS.map((weight, index) => (_jsx("span", { className: "h-5 w-0.5 origin-center rounded-full bg-current transition-transform duration-75 ease-out motion-reduce:transition-none", style: {
59
+ transform: `scaleY(${0.2 + visibleLevel * 0.8 * weight})`,
60
+ } }, index))) }));
61
+ }
62
+ function VoiceConnectingIndicator() {
63
+ return (_jsx(IconLoader2, { "aria-hidden": "true", className: "size-6 animate-spin motion-reduce:animate-none", "data-realtime-voice-connecting-indicator": "true" }));
53
64
  }
54
65
  const ORB_STATE_CLASSES = {
55
- connecting: "bg-secondary text-secondary-foreground ring-secondary/40 hover:bg-secondary/80",
56
- listening: "bg-primary text-primary-foreground ring-primary/20 hover:bg-primary/90",
57
- speaking: "bg-foreground text-background ring-foreground/20 hover:bg-foreground/90",
58
- working: "bg-secondary text-secondary-foreground ring-secondary/40 hover:bg-secondary/80",
59
- error: "bg-destructive text-destructive-foreground ring-destructive/20 hover:bg-destructive/90",
60
- ending: "cursor-wait bg-muted text-muted-foreground ring-muted/40",
66
+ connecting: "bg-background/65 text-foreground ring-border/60 hover:bg-background/75",
67
+ listening: "bg-background/65 text-foreground ring-border/60 hover:bg-background/75",
68
+ speaking: "bg-background/65 text-foreground ring-border/60 hover:bg-background/75",
69
+ working: "bg-background/65 text-foreground ring-border/60 hover:bg-background/75",
70
+ error: "bg-destructive/20 text-destructive ring-destructive/30 hover:bg-destructive/25",
71
+ ending: "cursor-wait bg-background/65 text-muted-foreground ring-border/60",
61
72
  };
62
- function VoiceStateIcon({ state }) {
63
- switch (state) {
64
- case "connecting":
65
- return (_jsx(IconLoader2, { className: "animate-spin motion-reduce:animate-none" }));
66
- case "listening":
67
- return _jsx(IconMicrophone, {});
68
- case "speaking":
69
- return _jsx(IconVolume, {});
70
- case "working":
71
- case "ending":
72
- return (_jsx(IconLoader2, { className: "animate-spin motion-reduce:animate-none" }));
73
- case "error":
74
- return _jsx(IconAlertTriangle, {});
75
- }
73
+ function useChatPanelTranslation(chatVisible) {
74
+ const [translation, setTranslation] = useState(0);
75
+ useEffect(() => {
76
+ if (!chatVisible || typeof window === "undefined") {
77
+ setTranslation(0);
78
+ return;
79
+ }
80
+ const direction = window.getComputedStyle(document.documentElement).direction;
81
+ const inlineEnd = direction === "rtl" ? "left" : "right";
82
+ const update = () => {
83
+ const panels = Array.from(document.querySelectorAll(`.agent-sidebar-panel[data-agent-sidebar-position="${inlineEnd}"][data-agent-sidebar-state="open"]`));
84
+ const panel = panels.find((candidate) => {
85
+ const rect = candidate.getBoundingClientRect();
86
+ return rect.width > 0 && rect.height > 0;
87
+ });
88
+ if (!panel) {
89
+ setTranslation(0);
90
+ return;
91
+ }
92
+ // A fullscreen chat has no unobscured side to move into. Keep the dock
93
+ // at its normal edge so it stays reachable instead of pinning it to the
94
+ // opposite side of the same overlay.
95
+ if (panel.dataset.agentSidebarLayout === "fullscreen") {
96
+ setTranslation(0);
97
+ return;
98
+ }
99
+ const rect = panel.getBoundingClientRect();
100
+ const overlap = inlineEnd === "right"
101
+ ? Math.max(0, window.innerWidth - rect.left)
102
+ : Math.max(0, rect.right);
103
+ const maximumShift = Math.max(0, window.innerWidth - 96);
104
+ const distance = Math.min(overlap, maximumShift);
105
+ setTranslation(inlineEnd === "right" ? -distance : distance);
106
+ };
107
+ update();
108
+ const frame = window.requestAnimationFrame(update);
109
+ window.addEventListener("resize", update);
110
+ const resizeObserver = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(update);
111
+ const observedPanels = new WeakSet();
112
+ const observePanels = () => {
113
+ document
114
+ .querySelectorAll(".agent-sidebar-panel")
115
+ .forEach((panel) => {
116
+ if (!observedPanels.has(panel)) {
117
+ observedPanels.add(panel);
118
+ resizeObserver?.observe(panel);
119
+ }
120
+ });
121
+ };
122
+ observePanels();
123
+ const mutationObserver = new MutationObserver(() => {
124
+ observePanels();
125
+ update();
126
+ });
127
+ mutationObserver.observe(document.body, {
128
+ attributes: true,
129
+ attributeFilter: [
130
+ "data-agent-sidebar-layout",
131
+ "data-agent-sidebar-state",
132
+ "style",
133
+ ],
134
+ childList: true,
135
+ subtree: true,
136
+ });
137
+ return () => {
138
+ window.cancelAnimationFrame(frame);
139
+ window.removeEventListener("resize", update);
140
+ resizeObserver?.disconnect();
141
+ mutationObserver.disconnect();
142
+ };
143
+ }, [chatVisible]);
144
+ return translation;
145
+ }
146
+ function VoiceSettingRow({ icon: Icon, setting, onSelectOpenChange, }) {
147
+ const selected = setting.options.find((option) => option.value === setting.value);
148
+ return (_jsxs(Select, { value: setting.value, onValueChange: setting.onValueChange, onOpenChange: onSelectOpenChange, disabled: setting.disabled, children: [_jsx(SelectTrigger, { "aria-label": `${setting.label}: ${selected?.label ?? setting.value}`, className: "h-11 rounded-none border-0 bg-transparent px-3 shadow-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset", children: _jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-2.5", children: [_jsx(Icon, { className: "size-4 shrink-0 text-muted-foreground" }), _jsx("span", { className: "shrink-0 text-sm font-medium text-foreground", children: setting.label }), _jsx("span", { className: "ms-auto min-w-0 truncate text-end text-sm text-muted-foreground", children: selected?.label ?? setting.value })] }) }), _jsx(SelectContent, { align: "end", className: "min-w-56", "data-realtime-voice-setting-options": "true", children: _jsx(SelectGroup, { children: setting.options.map((option) => (_jsx(SelectItem, { value: option.value, children: _jsxs("span", { className: "grid gap-0.5", children: [_jsx("span", { children: option.label }), option.description ? (_jsx("span", { className: "text-xs text-muted-foreground", children: option.description })) : null] }) }, option.value))) }) })] }));
149
+ }
150
+ function VoiceInlineSettings({ settings, disabled, onSelectOpenChange, }) {
151
+ const selectedVoice = settings.voiceStyle.options.find((option) => option.value === settings.voiceStyle.value);
152
+ return (_jsxs("div", { "data-realtime-voice-settings": "true", children: [_jsx("h2", { className: "sr-only", children: settings.dialogLabel }), _jsxs("div", { className: "grid gap-0.5 px-4 pb-3 pt-4 text-center", children: [_jsx("div", { className: "truncate text-base font-semibold text-foreground", children: selectedVoice?.label ?? settings.voiceStyle.value }), selectedVoice?.description ? (_jsx("div", { className: "truncate text-xs text-muted-foreground", children: selectedVoice.description })) : null] }), _jsxs("div", { className: "mx-2 mb-2 overflow-hidden rounded-xl border border-border/70 bg-background/70", children: [_jsx(VoiceSettingRow, { icon: IconMicrophone, setting: {
153
+ ...settings.microphone,
154
+ disabled: disabled || settings.microphone.disabled,
155
+ }, onSelectOpenChange: onSelectOpenChange }), _jsx("div", { className: "mx-3 h-px bg-border/70" }), _jsx(VoiceSettingRow, { icon: IconLanguage, setting: {
156
+ ...settings.language,
157
+ disabled: disabled || settings.language.disabled,
158
+ }, onSelectOpenChange: onSelectOpenChange }), _jsx("div", { className: "mx-3 h-px bg-border/70" }), _jsx(VoiceSettingRow, { icon: IconBrain, setting: {
159
+ ...settings.intelligence,
160
+ disabled: disabled || settings.intelligence.disabled,
161
+ }, onSelectOpenChange: onSelectOpenChange }), _jsx("div", { className: "mx-3 h-px bg-border/70" }), _jsx(VoiceSettingRow, { icon: IconVolume, setting: {
162
+ ...settings.voiceStyle,
163
+ disabled: disabled || settings.voiceStyle.disabled,
164
+ }, onSelectOpenChange: onSelectOpenChange })] }), settings.microphoneError ? (_jsx("p", { className: "px-4 pb-3 text-center text-xs text-destructive", children: settings.microphoneError })) : null, settings.appliesNextConversationNote ? (_jsx("p", { className: "px-4 pb-3 text-center text-xs text-muted-foreground", children: settings.appliesNextConversationNote })) : null] }));
76
165
  }
77
166
  /**
78
167
  * Persistent voice-session control. Toggling the main orb only changes chat
79
168
  * visibility; ending the realtime session is intentionally a separate action.
80
169
  */
81
- export function RealtimeVoiceModeDock({ state, copy, chatVisible, audioLevels = SILENT_AUDIO_LEVELS, onToggleChat, onEndVoiceMode, onOpenMicrophoneSettings, errorMessage, className, }) {
170
+ export function RealtimeVoiceModeDock({ state, copy, chatVisible, audioLevels = SILENT_AUDIO_LEVELS, onToggleChat, onEndVoiceMode, settings, errorMessage, className, }) {
82
171
  const statusId = useId();
83
172
  const controlsId = useId();
84
173
  const [controlsOpen, setControlsOpen] = useState(false);
174
+ const [settingsOpen, setSettingsOpen] = useState(false);
175
+ const selectInteractionRef = useRef(false);
176
+ const selectInteractionFrameRef = useRef(null);
85
177
  const levels = useSyncExternalStore(audioLevels.subscribe, audioLevels.getSnapshot, audioLevels.getSnapshot);
86
178
  const reducedMotion = usePrefersReducedMotion();
87
- const activity = levels.output > AUDIO_ACTIVITY_THRESHOLD
88
- ? "assistant"
89
- : levels.input > AUDIO_ACTIVITY_THRESHOLD
90
- ? "user"
91
- : "idle";
179
+ // Microphone metering begins while the SDP request is still in flight. Keep
180
+ // the connecting affordance authoritative until WebRTC is established;
181
+ // otherwise speaking into the mic replaces the loader with a waveform and
182
+ // makes a stalled connection look like a live call.
183
+ const connected = state === "listening" || state === "speaking" || state === "working";
184
+ const activity = connected
185
+ ? levels.output > AUDIO_ACTIVITY_THRESHOLD
186
+ ? "assistant"
187
+ : levels.input > AUDIO_ACTIVITY_THRESHOLD
188
+ ? "user"
189
+ : "idle"
190
+ : "idle";
92
191
  const activityLevel = activity === "assistant" ? levels.output : levels.input;
93
192
  const toggleLabel = chatVisible ? copy.hideChat : copy.showChat;
94
193
  const ending = state === "ending";
95
194
  const errorDetailVisible = state === "error" && Boolean(errorMessage);
96
- const orbScale = reducedMotion ? 1 : 1 + Math.min(activityLevel, 1) * 0.07;
195
+ const controlsVisible = controlsOpen || settingsOpen;
196
+ const chatPanelTranslation = useChatPanelTranslation(chatVisible);
197
+ const handleSelectOpenChange = useCallback((open) => {
198
+ if (selectInteractionFrameRef.current !== null) {
199
+ window.cancelAnimationFrame(selectInteractionFrameRef.current);
200
+ selectInteractionFrameRef.current = null;
201
+ }
202
+ selectInteractionRef.current = true;
203
+ if (!open) {
204
+ // Radix closes the portalled Select before its focus/outside events have
205
+ // fully settled. Keep the parent protected through the current frame.
206
+ selectInteractionFrameRef.current = window.requestAnimationFrame(() => {
207
+ selectInteractionRef.current = false;
208
+ selectInteractionFrameRef.current = null;
209
+ });
210
+ }
211
+ }, []);
212
+ useEffect(() => () => {
213
+ if (selectInteractionFrameRef.current !== null) {
214
+ window.cancelAnimationFrame(selectInteractionFrameRef.current);
215
+ }
216
+ }, []);
97
217
  const closeControlsUnlessFocused = (event) => {
218
+ if (settingsOpen)
219
+ return;
98
220
  if (!event.currentTarget.contains(document.activeElement)) {
99
221
  setControlsOpen(false);
100
222
  }
101
223
  };
102
- return (_jsxs("div", { className: cn("pointer-events-none fixed bottom-4 end-4 flex max-w-[calc(100vw-2rem)] flex-col items-end gap-2", className), style: { zIndex: 270 }, "data-realtime-voice-state": state, "data-realtime-voice-activity": activity, children: [errorDetailVisible ? (_jsx("div", { role: "alert", className: "pointer-events-auto max-w-xs rounded-lg border border-destructive/30 bg-background px-3 py-2 text-sm text-destructive shadow-md", children: errorMessage })) : null, _jsxs("div", { className: "group pointer-events-auto flex items-center gap-2", onMouseEnter: () => setControlsOpen(true), onMouseLeave: closeControlsUnlessFocused, onFocusCapture: () => setControlsOpen(true), onBlurCapture: (event) => {
224
+ return (_jsxs("div", { className: cn("pointer-events-none fixed bottom-4 end-4 flex max-w-[calc(100vw-2rem)] flex-col items-end gap-2 transition-transform duration-200 ease-[var(--ease-collapse)] motion-reduce:transition-none", className), style: {
225
+ zIndex: 270,
226
+ transform: `translateX(${chatPanelTranslation}px)`,
227
+ }, "data-realtime-voice-state": state, "data-realtime-voice-activity": activity, "data-realtime-voice-chat-offset": chatPanelTranslation, children: [errorDetailVisible ? (_jsx("div", { role: "alert", className: "pointer-events-auto max-w-xs rounded-lg border border-destructive/30 bg-background px-3 py-2 text-sm text-destructive shadow-md", children: errorMessage })) : null, _jsxs("div", { className: "group pointer-events-auto flex items-center gap-2", onMouseEnter: () => setControlsOpen(true), onMouseLeave: closeControlsUnlessFocused, onFocusCapture: () => setControlsOpen(true), onBlurCapture: (event) => {
103
228
  if (!event.currentTarget.contains(event.relatedTarget)) {
104
229
  setControlsOpen(false);
105
230
  }
106
- }, children: [_jsxs("div", { id: controlsId, "data-realtime-voice-controls": controlsOpen ? "open" : "closed", className: cn("flex items-center gap-1 rounded-full border border-border/70 bg-background/95 p-1 ps-3 shadow-lg backdrop-blur-md transition-[transform,opacity] duration-150 ease-out motion-reduce:transition-none", controlsOpen
231
+ }, children: [_jsxs("div", { id: controlsId, "data-realtime-voice-controls": controlsVisible ? "open" : "closed", className: cn("flex items-center gap-1 rounded-full border border-border/70 bg-background/95 p-1 shadow-lg backdrop-blur-md transition-[transform,opacity] duration-150 ease-out motion-reduce:transition-none", controlsVisible
107
232
  ? "pointer-events-auto translate-x-0 opacity-100"
108
- : "pointer-events-none opacity-0 ltr:translate-x-2 rtl:-translate-x-2 group-hover:pointer-events-auto group-hover:translate-x-0 group-hover:opacity-100 group-focus-within:pointer-events-auto group-focus-within:translate-x-0 group-focus-within:opacity-100"), children: [_jsx("div", { id: statusId, role: state === "error" && !errorDetailVisible ? "alert" : "status", "aria-live": state === "error" && !errorDetailVisible ? "assertive" : "polite", className: "me-1 whitespace-nowrap text-xs font-medium text-foreground", children: copy.status[state] }), onOpenMicrophoneSettings ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { type: "button", variant: "ghost", size: "icon", disabled: ending, onClick: onOpenMicrophoneSettings, "aria-label": copy.microphoneSettings, className: "size-8 rounded-full text-muted-foreground transition-transform duration-150 ease-out active:scale-[0.97]", children: _jsx(IconSettings, {}) }) }), _jsx(TooltipContent, { children: copy.microphoneSettings })] })) : null, _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { type: "button", variant: "ghost", size: "icon", disabled: ending, onClick: onEndVoiceMode, "aria-label": copy.endVoiceMode, className: "size-8 rounded-full text-destructive transition-transform duration-150 ease-out hover:bg-destructive/10 hover:text-destructive active:scale-[0.97]", children: _jsx(IconPhoneOff, {}) }) }), _jsx(TooltipContent, { children: copy.endVoiceMode })] })] }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs(Button, { type: "button", size: "icon", disabled: ending, onClick: () => {
233
+ : "pointer-events-none opacity-0 ltr:translate-x-2 rtl:-translate-x-2 group-hover:pointer-events-auto group-hover:translate-x-0 group-hover:opacity-100 group-focus-within:pointer-events-auto group-focus-within:translate-x-0 group-focus-within:opacity-100"), children: [_jsx("span", { id: statusId, role: state === "error" && !errorDetailVisible ? "alert" : "status", "aria-live": state === "error" && !errorDetailVisible ? "assertive" : "polite", className: "sr-only", children: copy.status[state] }), settings ? (_jsxs(Popover, { open: settingsOpen, onOpenChange: setSettingsOpen, children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(PopoverAnchor, { asChild: true, children: _jsx(Button, { type: "button", variant: "ghost", size: "icon", disabled: ending, onClick: () => setSettingsOpen((open) => !open), "aria-label": copy.voiceSettings, "aria-expanded": settingsOpen, className: "size-8 rounded-full text-muted-foreground transition-transform duration-150 ease-out active:scale-[0.97]", children: _jsx(IconSettings, {}) }) }) }), _jsx(TooltipContent, { children: copy.voiceSettings })] }), _jsx(PopoverContent, { side: "top", align: "end", sideOffset: 10, className: "w-[min(20rem,calc(100vw-2rem))] overflow-hidden p-0", onOpenAutoFocus: (event) => event.preventDefault(), onInteractOutside: (event) => {
234
+ const target = event.target;
235
+ if (selectInteractionRef.current ||
236
+ (target instanceof Element &&
237
+ target.closest('[data-realtime-voice-setting-options="true"]'))) {
238
+ event.preventDefault();
239
+ }
240
+ }, children: _jsx(VoiceInlineSettings, { settings: settings, disabled: ending, onSelectOpenChange: handleSelectOpenChange }) })] })) : null, _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { type: "button", variant: "ghost", size: "icon", disabled: ending, onClick: onEndVoiceMode, "aria-label": copy.endVoiceMode, className: "size-8 rounded-full text-destructive transition-transform duration-150 ease-out hover:bg-destructive/10 hover:text-destructive active:scale-[0.97]", children: _jsx(IconPhoneOff, {}) }) }), _jsx(TooltipContent, { children: copy.endVoiceMode })] })] }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { type: "button", size: "icon", disabled: ending, onClick: () => {
109
241
  setControlsOpen(true);
110
242
  onToggleChat();
111
- }, "aria-label": toggleLabel, "aria-pressed": chatVisible, "aria-describedby": statusId, "aria-controls": controlsId, "aria-expanded": controlsOpen, className: cn("relative isolate size-16 overflow-visible rounded-full ring-4 shadow-xl transition-transform duration-150 ease-out focus-visible:ring-offset-2 active:scale-[0.97] motion-reduce:transition-none", ORB_STATE_CLASSES[state]), children: [_jsx("span", { "aria-hidden": "true", className: cn("pointer-events-none absolute -inset-2 -z-10 rounded-full bg-current opacity-0 blur-md transition-[transform,opacity] duration-150 ease-out motion-reduce:transition-none", activity !== "idle" && "opacity-20"), style: { transform: `scale(${orbScale})` } }), _jsxs("span", { "aria-hidden": "true", className: "pointer-events-none absolute inset-1 overflow-hidden rounded-full bg-gradient-to-br from-background/70 via-background/15 to-transparent opacity-70", children: [_jsx("span", { className: "absolute start-2.5 top-2 size-3 rounded-full bg-background/70 blur-[1px]" }), _jsx("span", { className: "absolute -bottom-2 -end-1 h-8 w-10 rounded-full bg-background/25 blur-md transition-transform duration-150 ease-out motion-reduce:transition-none", style: {
112
- transform: `scale(${reducedMotion ? 1 : 1 + activityLevel * 0.25})`,
113
- } })] }), _jsx("span", { className: "relative z-10 flex items-center justify-center", children: activity === "idle" ? (_jsx(VoiceStateIcon, { state: state })) : (_jsx(VoiceWaveform, { level: activityLevel, reducedMotion: reducedMotion, activity: activity })) })] }) }), _jsx(TooltipContent, { children: toggleLabel })] })] })] }));
243
+ }, "aria-label": toggleLabel, "aria-pressed": chatVisible, "data-realtime-voice-chat-toggle": "orb", "aria-describedby": statusId, "aria-controls": controlsId, "aria-expanded": controlsVisible, className: cn("relative isolate size-16 overflow-visible rounded-full ring-1 backdrop-blur-xl transition-transform duration-150 ease-out focus-visible:ring-offset-2 active:scale-[0.97] motion-reduce:transition-none", ORB_STATE_CLASSES[state]), children: _jsx("span", { className: "relative z-10 flex items-center justify-center", children: state === "connecting" ? (_jsx(VoiceConnectingIndicator, {})) : state !== "error" ? (_jsx(VoiceWaveform, { level: activityLevel, reducedMotion: reducedMotion, activity: activity })) : (_jsx(IconAlertTriangle, {})) }) }) }), _jsx(TooltipContent, { children: toggleLabel })] })] })] }));
114
244
  }
115
245
  //# sourceMappingURL=RealtimeVoiceMode.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"RealtimeVoiceMode.js","sourceRoot":"","sources":["../../../src/client/composer/RealtimeVoiceMode.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Button } from \"@agent-native/toolkit/ui/button\";\nimport {\n IconAlertTriangle,\n IconLoader2,\n IconMicrophone,\n IconPhoneOff,\n IconSettings,\n IconVolume,\n} from \"@tabler/icons-react\";\nimport {\n type MouseEvent,\n useEffect,\n useId,\n useState,\n useSyncExternalStore,\n} from \"react\";\n\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"../components/ui/popover.js\";\nimport {\n Tooltip,\n TooltipContent,\n TooltipTrigger,\n} from \"../components/ui/tooltip.js\";\nimport { cn } from \"../utils.js\";\nimport {\n createRealtimeVoiceAudioLevelStore,\n type RealtimeVoiceAudioLevelStore,\n} from \"./realtime-voice-audio-level.js\";\n\nexport type RealtimeVoiceModeState =\n | \"connecting\"\n | \"listening\"\n | \"speaking\"\n | \"working\"\n | \"error\"\n | \"ending\";\n\n/**\n * User-visible copy stays outside the shared component so host catalogs remain\n * the source of truth. Callers should provide these values through `useT()`.\n */\nexport interface RealtimeVoiceModeCopy {\n entryButtonLabel: string;\n promptTitle: string;\n promptDescription: string;\n setupTitle: string;\n setupDescription: string;\n connectBuilder: string;\n useOpenAiKey: string;\n startWithOpenAiKey: string;\n startVoiceMode: string;\n keepDictating: string;\n showChat: string;\n hideChat: string;\n endVoiceMode: string;\n microphoneSettings: string;\n status: Record<RealtimeVoiceModeState, string>;\n errors: {\n unsupported: string;\n responseFailed: string;\n sessionFailed: string;\n channelDisconnected: string;\n connectionFailed: string;\n offerFailed: string;\n };\n}\n\nexport interface RealtimeVoiceModeEntryProps {\n copy: RealtimeVoiceModeCopy;\n disabled?: boolean;\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n onStartVoiceMode: () => void;\n onKeepDictating: () => void;\n setupRequired?: boolean;\n openAiConfigured?: boolean;\n connectingBuilder?: boolean;\n onConnectBuilder?: () => void;\n onUseOpenAiKey?: () => void;\n className?: string;\n}\n\n/**\n * Composer mic entry point for apps that support a full-duplex voice session.\n * The first click offers voice mode without silently changing the existing\n * editable-dictation behavior.\n */\nexport function RealtimeVoiceModeEntry({\n copy,\n disabled,\n open: controlledOpen,\n onOpenChange,\n onStartVoiceMode,\n onKeepDictating,\n setupRequired = false,\n openAiConfigured = false,\n connectingBuilder = false,\n onConnectBuilder,\n onUseOpenAiKey,\n className,\n}: RealtimeVoiceModeEntryProps) {\n const [uncontrolledOpen, setUncontrolledOpen] = useState(false);\n const open = controlledOpen ?? uncontrolledOpen;\n const titleId = useId();\n const descriptionId = useId();\n\n const setOpen = (nextOpen: boolean) => {\n if (controlledOpen === undefined) setUncontrolledOpen(nextOpen);\n onOpenChange?.(nextOpen);\n };\n\n const choose = (callback: () => void) => {\n setOpen(false);\n callback();\n };\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <Tooltip>\n <TooltipTrigger asChild>\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n disabled={disabled}\n aria-label={copy.entryButtonLabel}\n aria-expanded={open}\n className={cn(\n \"size-7 shrink-0 text-muted-foreground hover:text-foreground\",\n className,\n )}\n >\n <IconMicrophone />\n </Button>\n </PopoverTrigger>\n </TooltipTrigger>\n <TooltipContent>{copy.entryButtonLabel}</TooltipContent>\n </Tooltip>\n\n <PopoverContent\n side=\"top\"\n align=\"end\"\n sideOffset={10}\n className={cn(\n \"p-4\",\n setupRequired\n ? \"w-[min(30rem,calc(100vw-2rem))]\"\n : \"w-[min(22rem,calc(100vw-2rem))]\",\n )}\n aria-labelledby={titleId}\n aria-describedby={descriptionId}\n >\n <div className=\"grid gap-3\">\n <div className=\"grid gap-1\">\n <h2 id={titleId} className=\"text-sm font-semibold text-foreground\">\n {setupRequired ? copy.setupTitle : copy.promptTitle}\n </h2>\n <p\n id={descriptionId}\n className=\"text-sm leading-relaxed text-muted-foreground\"\n >\n {setupRequired ? copy.setupDescription : copy.promptDescription}\n </p>\n </div>\n\n <div className=\"flex flex-col-reverse gap-2 sm:flex-row sm:flex-nowrap sm:justify-end\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => choose(onKeepDictating)}\n >\n {copy.keepDictating}\n </Button>\n {setupRequired ? (\n <>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => choose(onUseOpenAiKey ?? onStartVoiceMode)}\n >\n {openAiConfigured\n ? copy.startWithOpenAiKey\n : copy.useOpenAiKey}\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n className=\"whitespace-nowrap\"\n disabled={connectingBuilder}\n onClick={() => choose(onConnectBuilder ?? onStartVoiceMode)}\n >\n {connectingBuilder ? (\n <IconLoader2 className=\"animate-spin\" />\n ) : null}\n {copy.connectBuilder}\n </Button>\n </>\n ) : (\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={() => choose(onStartVoiceMode)}\n >\n <IconMicrophone />\n {copy.startVoiceMode}\n </Button>\n )}\n </div>\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n\nexport interface RealtimeVoiceModeDockProps {\n state: RealtimeVoiceModeState;\n copy: RealtimeVoiceModeCopy;\n chatVisible: boolean;\n audioLevels?: RealtimeVoiceAudioLevelStore;\n onToggleChat: () => void;\n onEndVoiceMode: () => void;\n onOpenMicrophoneSettings?: () => void;\n errorMessage?: string | null;\n className?: string;\n}\n\nconst SILENT_AUDIO_LEVELS = createRealtimeVoiceAudioLevelStore();\nconst WAVEFORM_WEIGHTS = [0.55, 0.82, 1, 0.82, 0.55];\nconst AUDIO_ACTIVITY_THRESHOLD = 0.035;\n\nfunction usePrefersReducedMotion(): boolean {\n const [reduced, setReduced] = useState(false);\n useEffect(() => {\n if (typeof window === \"undefined\" || !window.matchMedia) return;\n const query = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n const update = () => setReduced(query.matches);\n update();\n query.addEventListener?.(\"change\", update);\n return () => query.removeEventListener?.(\"change\", update);\n }, []);\n return reduced;\n}\n\nfunction VoiceWaveform({\n level,\n reducedMotion,\n activity,\n}: {\n level: number;\n reducedMotion: boolean;\n activity: \"user\" | \"assistant\";\n}) {\n const visibleLevel = reducedMotion ? 0.45 : level;\n return (\n <span\n aria-hidden=\"true\"\n className=\"flex h-6 items-center justify-center gap-0.5\"\n data-realtime-voice-waveform=\"true\"\n data-realtime-voice-waveform-activity={activity}\n >\n {WAVEFORM_WEIGHTS.map((weight, index) => (\n <span\n key={index}\n className=\"h-5 w-0.5 origin-center rounded-full bg-current transition-transform duration-75 ease-out motion-reduce:transition-none\"\n style={{ transform: `scaleY(${0.2 + visibleLevel * 0.8 * weight})` }}\n />\n ))}\n </span>\n );\n}\n\nconst ORB_STATE_CLASSES: Record<RealtimeVoiceModeState, string> = {\n connecting:\n \"bg-secondary text-secondary-foreground ring-secondary/40 hover:bg-secondary/80\",\n listening:\n \"bg-primary text-primary-foreground ring-primary/20 hover:bg-primary/90\",\n speaking:\n \"bg-foreground text-background ring-foreground/20 hover:bg-foreground/90\",\n working:\n \"bg-secondary text-secondary-foreground ring-secondary/40 hover:bg-secondary/80\",\n error:\n \"bg-destructive text-destructive-foreground ring-destructive/20 hover:bg-destructive/90\",\n ending: \"cursor-wait bg-muted text-muted-foreground ring-muted/40\",\n};\n\nfunction VoiceStateIcon({ state }: { state: RealtimeVoiceModeState }) {\n switch (state) {\n case \"connecting\":\n return (\n <IconLoader2 className=\"animate-spin motion-reduce:animate-none\" />\n );\n case \"listening\":\n return <IconMicrophone />;\n case \"speaking\":\n return <IconVolume />;\n case \"working\":\n case \"ending\":\n return (\n <IconLoader2 className=\"animate-spin motion-reduce:animate-none\" />\n );\n case \"error\":\n return <IconAlertTriangle />;\n }\n}\n\n/**\n * Persistent voice-session control. Toggling the main orb only changes chat\n * visibility; ending the realtime session is intentionally a separate action.\n */\nexport function RealtimeVoiceModeDock({\n state,\n copy,\n chatVisible,\n audioLevels = SILENT_AUDIO_LEVELS,\n onToggleChat,\n onEndVoiceMode,\n onOpenMicrophoneSettings,\n errorMessage,\n className,\n}: RealtimeVoiceModeDockProps) {\n const statusId = useId();\n const controlsId = useId();\n const [controlsOpen, setControlsOpen] = useState(false);\n const levels = useSyncExternalStore(\n audioLevels.subscribe,\n audioLevels.getSnapshot,\n audioLevels.getSnapshot,\n );\n const reducedMotion = usePrefersReducedMotion();\n const activity =\n levels.output > AUDIO_ACTIVITY_THRESHOLD\n ? \"assistant\"\n : levels.input > AUDIO_ACTIVITY_THRESHOLD\n ? \"user\"\n : \"idle\";\n const activityLevel = activity === \"assistant\" ? levels.output : levels.input;\n const toggleLabel = chatVisible ? copy.hideChat : copy.showChat;\n const ending = state === \"ending\";\n const errorDetailVisible = state === \"error\" && Boolean(errorMessage);\n const orbScale = reducedMotion ? 1 : 1 + Math.min(activityLevel, 1) * 0.07;\n\n const closeControlsUnlessFocused = (event: MouseEvent<HTMLDivElement>) => {\n if (!event.currentTarget.contains(document.activeElement)) {\n setControlsOpen(false);\n }\n };\n\n return (\n <div\n className={cn(\n \"pointer-events-none fixed bottom-4 end-4 flex max-w-[calc(100vw-2rem)] flex-col items-end gap-2\",\n className,\n )}\n style={{ zIndex: 270 }}\n data-realtime-voice-state={state}\n data-realtime-voice-activity={activity}\n >\n {errorDetailVisible ? (\n <div\n role=\"alert\"\n className=\"pointer-events-auto max-w-xs rounded-lg border border-destructive/30 bg-background px-3 py-2 text-sm text-destructive shadow-md\"\n >\n {errorMessage}\n </div>\n ) : null}\n\n <div\n className=\"group pointer-events-auto flex items-center gap-2\"\n onMouseEnter={() => setControlsOpen(true)}\n onMouseLeave={closeControlsUnlessFocused}\n onFocusCapture={() => setControlsOpen(true)}\n onBlurCapture={(event) => {\n if (\n !event.currentTarget.contains(event.relatedTarget as Node | null)\n ) {\n setControlsOpen(false);\n }\n }}\n >\n <div\n id={controlsId}\n data-realtime-voice-controls={controlsOpen ? \"open\" : \"closed\"}\n className={cn(\n \"flex items-center gap-1 rounded-full border border-border/70 bg-background/95 p-1 ps-3 shadow-lg backdrop-blur-md transition-[transform,opacity] duration-150 ease-out motion-reduce:transition-none\",\n controlsOpen\n ? \"pointer-events-auto translate-x-0 opacity-100\"\n : \"pointer-events-none opacity-0 ltr:translate-x-2 rtl:-translate-x-2 group-hover:pointer-events-auto group-hover:translate-x-0 group-hover:opacity-100 group-focus-within:pointer-events-auto group-focus-within:translate-x-0 group-focus-within:opacity-100\",\n )}\n >\n <div\n id={statusId}\n role={state === \"error\" && !errorDetailVisible ? \"alert\" : \"status\"}\n aria-live={\n state === \"error\" && !errorDetailVisible ? \"assertive\" : \"polite\"\n }\n className=\"me-1 whitespace-nowrap text-xs font-medium text-foreground\"\n >\n {copy.status[state]}\n </div>\n\n {onOpenMicrophoneSettings ? (\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n disabled={ending}\n onClick={onOpenMicrophoneSettings}\n aria-label={copy.microphoneSettings}\n className=\"size-8 rounded-full text-muted-foreground transition-transform duration-150 ease-out active:scale-[0.97]\"\n >\n <IconSettings />\n </Button>\n </TooltipTrigger>\n <TooltipContent>{copy.microphoneSettings}</TooltipContent>\n </Tooltip>\n ) : null}\n\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n disabled={ending}\n onClick={onEndVoiceMode}\n aria-label={copy.endVoiceMode}\n className=\"size-8 rounded-full text-destructive transition-transform duration-150 ease-out hover:bg-destructive/10 hover:text-destructive active:scale-[0.97]\"\n >\n <IconPhoneOff />\n </Button>\n </TooltipTrigger>\n <TooltipContent>{copy.endVoiceMode}</TooltipContent>\n </Tooltip>\n </div>\n\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n type=\"button\"\n size=\"icon\"\n disabled={ending}\n onClick={() => {\n setControlsOpen(true);\n onToggleChat();\n }}\n aria-label={toggleLabel}\n aria-pressed={chatVisible}\n aria-describedby={statusId}\n aria-controls={controlsId}\n aria-expanded={controlsOpen}\n className={cn(\n \"relative isolate size-16 overflow-visible rounded-full ring-4 shadow-xl transition-transform duration-150 ease-out focus-visible:ring-offset-2 active:scale-[0.97] motion-reduce:transition-none\",\n ORB_STATE_CLASSES[state],\n )}\n >\n <span\n aria-hidden=\"true\"\n className={cn(\n \"pointer-events-none absolute -inset-2 -z-10 rounded-full bg-current opacity-0 blur-md transition-[transform,opacity] duration-150 ease-out motion-reduce:transition-none\",\n activity !== \"idle\" && \"opacity-20\",\n )}\n style={{ transform: `scale(${orbScale})` }}\n />\n <span\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute inset-1 overflow-hidden rounded-full bg-gradient-to-br from-background/70 via-background/15 to-transparent opacity-70\"\n >\n <span className=\"absolute start-2.5 top-2 size-3 rounded-full bg-background/70 blur-[1px]\" />\n <span\n className=\"absolute -bottom-2 -end-1 h-8 w-10 rounded-full bg-background/25 blur-md transition-transform duration-150 ease-out motion-reduce:transition-none\"\n style={{\n transform: `scale(${reducedMotion ? 1 : 1 + activityLevel * 0.25})`,\n }}\n />\n </span>\n <span className=\"relative z-10 flex items-center justify-center\">\n {activity === \"idle\" ? (\n <VoiceStateIcon state={state} />\n ) : (\n <VoiceWaveform\n level={activityLevel}\n reducedMotion={reducedMotion}\n activity={activity}\n />\n )}\n </span>\n </Button>\n </TooltipTrigger>\n <TooltipContent>{toggleLabel}</TooltipContent>\n </Tooltip>\n </div>\n </div>\n );\n}\n"]}
1
+ {"version":3,"file":"RealtimeVoiceMode.js","sourceRoot":"","sources":["../../../src/client/composer/RealtimeVoiceMode.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Button } from \"@agent-native/toolkit/ui/button\";\nimport {\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectTrigger,\n} from \"@agent-native/toolkit/ui/select\";\nimport {\n IconAlertTriangle,\n IconBrain,\n IconLanguage,\n IconLoader2,\n IconMicrophone,\n IconPhoneOff,\n IconSettings,\n IconVolume,\n} from \"@tabler/icons-react\";\nimport {\n type MouseEvent,\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n useSyncExternalStore,\n} from \"react\";\n\nimport {\n Popover,\n PopoverAnchor,\n PopoverContent,\n PopoverTrigger,\n} from \"../components/ui/popover.js\";\nimport {\n Tooltip,\n TooltipContent,\n TooltipTrigger,\n} from \"../components/ui/tooltip.js\";\nimport { cn } from \"../utils.js\";\nimport {\n createRealtimeVoiceAudioLevelStore,\n type RealtimeVoiceAudioLevelStore,\n} from \"./realtime-voice-audio-level.js\";\n\nexport type RealtimeVoiceModeState =\n | \"connecting\"\n | \"listening\"\n | \"speaking\"\n | \"working\"\n | \"error\"\n | \"ending\";\n\n/**\n * User-visible copy stays outside the shared component so host catalogs remain\n * the source of truth. Callers should provide these values through `useT()`.\n */\nexport interface RealtimeVoiceModeCopy {\n entryButtonLabel: string;\n promptTitle: string;\n promptDescription: string;\n setupTitle: string;\n setupDescription: string;\n connectBuilder: string;\n useOpenAiKey: string;\n startWithOpenAiKey: string;\n startVoiceMode: string;\n keepDictating: string;\n showChat: string;\n hideChat: string;\n endVoiceMode: string;\n voiceSettings: string;\n settings: {\n microphone: string;\n defaultMicrophone: string;\n microphoneSwitchFailed: string;\n language: string;\n autoLanguage: string;\n languages: Record<\n Exclude<\n import(\"./useRealtimeVoiceMode.js\").RealtimeVoiceLanguage,\n \"auto\"\n >,\n string\n >;\n intelligence: string;\n intelligenceLevels: Record<\n import(\"./useRealtimeVoiceMode.js\").RealtimeVoiceIntelligence,\n string\n >;\n voiceStyle: string;\n voiceChangePending: string;\n voiceDescriptions: Record<\n import(\"./useRealtimeVoiceMode.js\").RealtimeVoice,\n string\n >;\n };\n status: Record<RealtimeVoiceModeState, string>;\n errors: {\n unsupported: string;\n responseFailed: string;\n sessionFailed: string;\n channelDisconnected: string;\n connectionTimedOut?: string;\n connectionFailed: string;\n offerFailed: string;\n };\n}\n\nexport interface RealtimeVoiceModeEntryProps {\n copy: RealtimeVoiceModeCopy;\n disabled?: boolean;\n providerStatusPending?: boolean;\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n onStartVoiceMode: () => void;\n onKeepDictating: () => void;\n setupRequired?: boolean;\n openAiConfigured?: boolean;\n connectingBuilder?: boolean;\n onConnectBuilder?: () => void;\n onUseOpenAiKey?: () => void;\n className?: string;\n}\n\n/**\n * Composer mic entry point for apps that support a full-duplex voice session.\n * The first click offers voice mode without silently changing the existing\n * editable-dictation behavior.\n */\nexport function RealtimeVoiceModeEntry({\n copy,\n disabled,\n providerStatusPending = false,\n open: controlledOpen,\n onOpenChange,\n onStartVoiceMode,\n onKeepDictating,\n setupRequired = false,\n openAiConfigured = false,\n connectingBuilder = false,\n onConnectBuilder,\n onUseOpenAiKey,\n className,\n}: RealtimeVoiceModeEntryProps) {\n const [uncontrolledOpen, setUncontrolledOpen] = useState(false);\n const open = controlledOpen ?? uncontrolledOpen;\n const titleId = useId();\n const descriptionId = useId();\n const [collisionBoundary, setCollisionBoundary] =\n useState<HTMLElement | null>(null);\n\n const setTriggerNode = useCallback((node: HTMLButtonElement | null) => {\n const nextBoundary =\n node?.closest<HTMLElement>(\".agent-panel-root\") ?? null;\n setCollisionBoundary((currentBoundary) =>\n currentBoundary === nextBoundary ? currentBoundary : nextBoundary,\n );\n }, []);\n\n const setOpen = (nextOpen: boolean) => {\n if (controlledOpen === undefined) setUncontrolledOpen(nextOpen);\n onOpenChange?.(nextOpen);\n };\n\n const choose = (callback: () => void) => {\n setOpen(false);\n callback();\n };\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <Tooltip>\n <TooltipTrigger asChild>\n <PopoverTrigger asChild>\n <Button\n ref={setTriggerNode}\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n disabled={disabled}\n aria-label={copy.entryButtonLabel}\n aria-expanded={open}\n className={cn(\n \"size-7 shrink-0 text-muted-foreground hover:text-foreground\",\n className,\n )}\n >\n <IconMicrophone />\n </Button>\n </PopoverTrigger>\n </TooltipTrigger>\n <TooltipContent>{copy.entryButtonLabel}</TooltipContent>\n </Tooltip>\n\n <PopoverContent\n side=\"top\"\n align=\"end\"\n sideOffset={10}\n collisionBoundary={collisionBoundary ?? undefined}\n collisionPadding={16}\n data-collision-boundary={collisionBoundary ? \"agent-panel\" : \"viewport\"}\n className={cn(\n \"p-4\",\n setupRequired\n ? \"w-[min(calc(100vw-2rem),var(--radix-popover-content-available-width,30rem),30rem)]\"\n : \"w-[min(calc(100vw-2rem),var(--radix-popover-content-available-width,22rem),22rem)]\",\n )}\n aria-labelledby={titleId}\n aria-describedby={descriptionId}\n >\n <div className=\"grid gap-3\">\n <div className=\"grid gap-1\">\n <h2 id={titleId} className=\"text-sm font-semibold text-foreground\">\n {setupRequired ? copy.setupTitle : copy.promptTitle}\n </h2>\n <p\n id={descriptionId}\n className=\"text-sm leading-relaxed text-muted-foreground\"\n >\n {setupRequired ? copy.setupDescription : copy.promptDescription}\n </p>\n </div>\n\n <div className=\"flex flex-col-reverse gap-2 sm:flex-row sm:flex-wrap sm:justify-end\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => choose(onKeepDictating)}\n >\n {copy.keepDictating}\n </Button>\n {setupRequired ? (\n <>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => choose(onUseOpenAiKey ?? onStartVoiceMode)}\n >\n {openAiConfigured\n ? copy.startWithOpenAiKey\n : copy.useOpenAiKey}\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n className=\"whitespace-nowrap\"\n disabled={connectingBuilder}\n onClick={() => choose(onConnectBuilder ?? onStartVoiceMode)}\n >\n {connectingBuilder ? (\n <IconLoader2 className=\"animate-spin\" />\n ) : null}\n {copy.connectBuilder}\n </Button>\n </>\n ) : (\n <Button\n type=\"button\"\n size=\"sm\"\n disabled={providerStatusPending}\n onClick={() => choose(onStartVoiceMode)}\n >\n {providerStatusPending ? (\n <IconLoader2 className=\"animate-spin\" />\n ) : (\n <IconMicrophone />\n )}\n {copy.startVoiceMode}\n </Button>\n )}\n </div>\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n\nexport interface RealtimeVoiceModeDockProps {\n state: RealtimeVoiceModeState;\n copy: RealtimeVoiceModeCopy;\n chatVisible: boolean;\n audioLevels?: RealtimeVoiceAudioLevelStore;\n onToggleChat: () => void;\n onEndVoiceMode: () => void;\n settings?: RealtimeVoiceModeInlineSettings;\n errorMessage?: string | null;\n className?: string;\n}\n\nexport interface RealtimeVoiceModeSettingOption {\n value: string;\n label: string;\n description?: string;\n}\n\nexport interface RealtimeVoiceModeSelectSetting {\n label: string;\n value: string;\n options: readonly RealtimeVoiceModeSettingOption[];\n onValueChange: (value: string) => void;\n disabled?: boolean;\n}\n\nexport interface RealtimeVoiceModeInlineSettings {\n dialogLabel: string;\n appliesNextConversationNote?: string;\n microphoneError?: string;\n microphone: RealtimeVoiceModeSelectSetting;\n language: RealtimeVoiceModeSelectSetting;\n intelligence: RealtimeVoiceModeSelectSetting;\n voiceStyle: RealtimeVoiceModeSelectSetting;\n}\n\nconst SILENT_AUDIO_LEVELS = createRealtimeVoiceAudioLevelStore();\nconst WAVEFORM_WEIGHTS = [0.55, 0.82, 1, 0.82, 0.55];\nconst AUDIO_ACTIVITY_THRESHOLD = 0.035;\n\nfunction usePrefersReducedMotion(): boolean {\n const [reduced, setReduced] = useState(false);\n useEffect(() => {\n if (typeof window === \"undefined\" || !window.matchMedia) return;\n const query = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n const update = () => setReduced(query.matches);\n update();\n query.addEventListener?.(\"change\", update);\n return () => query.removeEventListener?.(\"change\", update);\n }, []);\n return reduced;\n}\n\nfunction VoiceWaveform({\n level,\n reducedMotion,\n activity,\n}: {\n level: number;\n reducedMotion: boolean;\n activity: \"idle\" | \"user\" | \"assistant\";\n}) {\n const visibleLevel = reducedMotion ? 0.45 : level;\n return (\n <span\n aria-hidden=\"true\"\n className=\"flex h-6 items-center justify-center gap-0.5\"\n data-realtime-voice-waveform=\"true\"\n data-realtime-voice-waveform-activity={activity}\n >\n {WAVEFORM_WEIGHTS.map((weight, index) => (\n <span\n key={index}\n className=\"h-5 w-0.5 origin-center rounded-full bg-current transition-transform duration-75 ease-out motion-reduce:transition-none\"\n style={{\n transform: `scaleY(${0.2 + visibleLevel * 0.8 * weight})`,\n }}\n />\n ))}\n </span>\n );\n}\n\nfunction VoiceConnectingIndicator() {\n return (\n <IconLoader2\n aria-hidden=\"true\"\n className=\"size-6 animate-spin motion-reduce:animate-none\"\n data-realtime-voice-connecting-indicator=\"true\"\n />\n );\n}\n\nconst ORB_STATE_CLASSES: Record<RealtimeVoiceModeState, string> = {\n connecting:\n \"bg-background/65 text-foreground ring-border/60 hover:bg-background/75\",\n listening:\n \"bg-background/65 text-foreground ring-border/60 hover:bg-background/75\",\n speaking:\n \"bg-background/65 text-foreground ring-border/60 hover:bg-background/75\",\n working:\n \"bg-background/65 text-foreground ring-border/60 hover:bg-background/75\",\n error:\n \"bg-destructive/20 text-destructive ring-destructive/30 hover:bg-destructive/25\",\n ending: \"cursor-wait bg-background/65 text-muted-foreground ring-border/60\",\n};\n\nfunction useChatPanelTranslation(chatVisible: boolean): number {\n const [translation, setTranslation] = useState(0);\n\n useEffect(() => {\n if (!chatVisible || typeof window === \"undefined\") {\n setTranslation(0);\n return;\n }\n\n const direction = window.getComputedStyle(\n document.documentElement,\n ).direction;\n const inlineEnd = direction === \"rtl\" ? \"left\" : \"right\";\n\n const update = () => {\n const panels = Array.from(\n document.querySelectorAll<HTMLElement>(\n `.agent-sidebar-panel[data-agent-sidebar-position=\"${inlineEnd}\"][data-agent-sidebar-state=\"open\"]`,\n ),\n );\n const panel = panels.find((candidate) => {\n const rect = candidate.getBoundingClientRect();\n return rect.width > 0 && rect.height > 0;\n });\n\n if (!panel) {\n setTranslation(0);\n return;\n }\n\n // A fullscreen chat has no unobscured side to move into. Keep the dock\n // at its normal edge so it stays reachable instead of pinning it to the\n // opposite side of the same overlay.\n if (panel.dataset.agentSidebarLayout === \"fullscreen\") {\n setTranslation(0);\n return;\n }\n\n const rect = panel.getBoundingClientRect();\n const overlap =\n inlineEnd === \"right\"\n ? Math.max(0, window.innerWidth - rect.left)\n : Math.max(0, rect.right);\n const maximumShift = Math.max(0, window.innerWidth - 96);\n const distance = Math.min(overlap, maximumShift);\n setTranslation(inlineEnd === \"right\" ? -distance : distance);\n };\n\n update();\n const frame = window.requestAnimationFrame(update);\n window.addEventListener(\"resize\", update);\n\n const resizeObserver =\n typeof ResizeObserver === \"undefined\" ? null : new ResizeObserver(update);\n const observedPanels = new WeakSet<HTMLElement>();\n const observePanels = () => {\n document\n .querySelectorAll<HTMLElement>(\".agent-sidebar-panel\")\n .forEach((panel) => {\n if (!observedPanels.has(panel)) {\n observedPanels.add(panel);\n resizeObserver?.observe(panel);\n }\n });\n };\n observePanels();\n\n const mutationObserver = new MutationObserver(() => {\n observePanels();\n update();\n });\n mutationObserver.observe(document.body, {\n attributes: true,\n attributeFilter: [\n \"data-agent-sidebar-layout\",\n \"data-agent-sidebar-state\",\n \"style\",\n ],\n childList: true,\n subtree: true,\n });\n\n return () => {\n window.cancelAnimationFrame(frame);\n window.removeEventListener(\"resize\", update);\n resizeObserver?.disconnect();\n mutationObserver.disconnect();\n };\n }, [chatVisible]);\n\n return translation;\n}\n\nfunction VoiceSettingRow({\n icon: Icon,\n setting,\n onSelectOpenChange,\n}: {\n icon: typeof IconLanguage;\n setting: RealtimeVoiceModeSelectSetting;\n onSelectOpenChange: (open: boolean) => void;\n}) {\n const selected = setting.options.find(\n (option) => option.value === setting.value,\n );\n\n return (\n <Select\n value={setting.value}\n onValueChange={setting.onValueChange}\n onOpenChange={onSelectOpenChange}\n disabled={setting.disabled}\n >\n <SelectTrigger\n aria-label={`${setting.label}: ${selected?.label ?? setting.value}`}\n className=\"h-11 rounded-none border-0 bg-transparent px-3 shadow-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset\"\n >\n <div className=\"flex min-w-0 flex-1 items-center gap-2.5\">\n <Icon className=\"size-4 shrink-0 text-muted-foreground\" />\n <span className=\"shrink-0 text-sm font-medium text-foreground\">\n {setting.label}\n </span>\n <span className=\"ms-auto min-w-0 truncate text-end text-sm text-muted-foreground\">\n {selected?.label ?? setting.value}\n </span>\n </div>\n </SelectTrigger>\n <SelectContent\n align=\"end\"\n className=\"min-w-56\"\n data-realtime-voice-setting-options=\"true\"\n >\n <SelectGroup>\n {setting.options.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n <span className=\"grid gap-0.5\">\n <span>{option.label}</span>\n {option.description ? (\n <span className=\"text-xs text-muted-foreground\">\n {option.description}\n </span>\n ) : null}\n </span>\n </SelectItem>\n ))}\n </SelectGroup>\n </SelectContent>\n </Select>\n );\n}\n\nfunction VoiceInlineSettings({\n settings,\n disabled,\n onSelectOpenChange,\n}: {\n settings: RealtimeVoiceModeInlineSettings;\n disabled: boolean;\n onSelectOpenChange: (open: boolean) => void;\n}) {\n const selectedVoice = settings.voiceStyle.options.find(\n (option) => option.value === settings.voiceStyle.value,\n );\n\n return (\n <div data-realtime-voice-settings=\"true\">\n <h2 className=\"sr-only\">{settings.dialogLabel}</h2>\n <div className=\"grid gap-0.5 px-4 pb-3 pt-4 text-center\">\n <div className=\"truncate text-base font-semibold text-foreground\">\n {selectedVoice?.label ?? settings.voiceStyle.value}\n </div>\n {selectedVoice?.description ? (\n <div className=\"truncate text-xs text-muted-foreground\">\n {selectedVoice.description}\n </div>\n ) : null}\n </div>\n <div className=\"mx-2 mb-2 overflow-hidden rounded-xl border border-border/70 bg-background/70\">\n <VoiceSettingRow\n icon={IconMicrophone}\n setting={{\n ...settings.microphone,\n disabled: disabled || settings.microphone.disabled,\n }}\n onSelectOpenChange={onSelectOpenChange}\n />\n <div className=\"mx-3 h-px bg-border/70\" />\n <VoiceSettingRow\n icon={IconLanguage}\n setting={{\n ...settings.language,\n disabled: disabled || settings.language.disabled,\n }}\n onSelectOpenChange={onSelectOpenChange}\n />\n <div className=\"mx-3 h-px bg-border/70\" />\n <VoiceSettingRow\n icon={IconBrain}\n setting={{\n ...settings.intelligence,\n disabled: disabled || settings.intelligence.disabled,\n }}\n onSelectOpenChange={onSelectOpenChange}\n />\n <div className=\"mx-3 h-px bg-border/70\" />\n <VoiceSettingRow\n icon={IconVolume}\n setting={{\n ...settings.voiceStyle,\n disabled: disabled || settings.voiceStyle.disabled,\n }}\n onSelectOpenChange={onSelectOpenChange}\n />\n </div>\n {settings.microphoneError ? (\n <p className=\"px-4 pb-3 text-center text-xs text-destructive\">\n {settings.microphoneError}\n </p>\n ) : null}\n {settings.appliesNextConversationNote ? (\n <p className=\"px-4 pb-3 text-center text-xs text-muted-foreground\">\n {settings.appliesNextConversationNote}\n </p>\n ) : null}\n </div>\n );\n}\n\n/**\n * Persistent voice-session control. Toggling the main orb only changes chat\n * visibility; ending the realtime session is intentionally a separate action.\n */\nexport function RealtimeVoiceModeDock({\n state,\n copy,\n chatVisible,\n audioLevels = SILENT_AUDIO_LEVELS,\n onToggleChat,\n onEndVoiceMode,\n settings,\n errorMessage,\n className,\n}: RealtimeVoiceModeDockProps) {\n const statusId = useId();\n const controlsId = useId();\n const [controlsOpen, setControlsOpen] = useState(false);\n const [settingsOpen, setSettingsOpen] = useState(false);\n const selectInteractionRef = useRef(false);\n const selectInteractionFrameRef = useRef<number | null>(null);\n const levels = useSyncExternalStore(\n audioLevels.subscribe,\n audioLevels.getSnapshot,\n audioLevels.getSnapshot,\n );\n const reducedMotion = usePrefersReducedMotion();\n // Microphone metering begins while the SDP request is still in flight. Keep\n // the connecting affordance authoritative until WebRTC is established;\n // otherwise speaking into the mic replaces the loader with a waveform and\n // makes a stalled connection look like a live call.\n const connected =\n state === \"listening\" || state === \"speaking\" || state === \"working\";\n const activity = connected\n ? levels.output > AUDIO_ACTIVITY_THRESHOLD\n ? \"assistant\"\n : levels.input > AUDIO_ACTIVITY_THRESHOLD\n ? \"user\"\n : \"idle\"\n : \"idle\";\n const activityLevel = activity === \"assistant\" ? levels.output : levels.input;\n const toggleLabel = chatVisible ? copy.hideChat : copy.showChat;\n const ending = state === \"ending\";\n const errorDetailVisible = state === \"error\" && Boolean(errorMessage);\n const controlsVisible = controlsOpen || settingsOpen;\n const chatPanelTranslation = useChatPanelTranslation(chatVisible);\n\n const handleSelectOpenChange = useCallback((open: boolean) => {\n if (selectInteractionFrameRef.current !== null) {\n window.cancelAnimationFrame(selectInteractionFrameRef.current);\n selectInteractionFrameRef.current = null;\n }\n selectInteractionRef.current = true;\n if (!open) {\n // Radix closes the portalled Select before its focus/outside events have\n // fully settled. Keep the parent protected through the current frame.\n selectInteractionFrameRef.current = window.requestAnimationFrame(() => {\n selectInteractionRef.current = false;\n selectInteractionFrameRef.current = null;\n });\n }\n }, []);\n\n useEffect(\n () => () => {\n if (selectInteractionFrameRef.current !== null) {\n window.cancelAnimationFrame(selectInteractionFrameRef.current);\n }\n },\n [],\n );\n\n const closeControlsUnlessFocused = (event: MouseEvent<HTMLDivElement>) => {\n if (settingsOpen) return;\n if (!event.currentTarget.contains(document.activeElement)) {\n setControlsOpen(false);\n }\n };\n\n return (\n <div\n className={cn(\n \"pointer-events-none fixed bottom-4 end-4 flex max-w-[calc(100vw-2rem)] flex-col items-end gap-2 transition-transform duration-200 ease-[var(--ease-collapse)] motion-reduce:transition-none\",\n className,\n )}\n style={{\n zIndex: 270,\n transform: `translateX(${chatPanelTranslation}px)`,\n }}\n data-realtime-voice-state={state}\n data-realtime-voice-activity={activity}\n data-realtime-voice-chat-offset={chatPanelTranslation}\n >\n {errorDetailVisible ? (\n <div\n role=\"alert\"\n className=\"pointer-events-auto max-w-xs rounded-lg border border-destructive/30 bg-background px-3 py-2 text-sm text-destructive shadow-md\"\n >\n {errorMessage}\n </div>\n ) : null}\n\n <div\n className=\"group pointer-events-auto flex items-center gap-2\"\n onMouseEnter={() => setControlsOpen(true)}\n onMouseLeave={closeControlsUnlessFocused}\n onFocusCapture={() => setControlsOpen(true)}\n onBlurCapture={(event) => {\n if (\n !event.currentTarget.contains(event.relatedTarget as Node | null)\n ) {\n setControlsOpen(false);\n }\n }}\n >\n <div\n id={controlsId}\n data-realtime-voice-controls={controlsVisible ? \"open\" : \"closed\"}\n className={cn(\n \"flex items-center gap-1 rounded-full border border-border/70 bg-background/95 p-1 shadow-lg backdrop-blur-md transition-[transform,opacity] duration-150 ease-out motion-reduce:transition-none\",\n controlsVisible\n ? \"pointer-events-auto translate-x-0 opacity-100\"\n : \"pointer-events-none opacity-0 ltr:translate-x-2 rtl:-translate-x-2 group-hover:pointer-events-auto group-hover:translate-x-0 group-hover:opacity-100 group-focus-within:pointer-events-auto group-focus-within:translate-x-0 group-focus-within:opacity-100\",\n )}\n >\n <span\n id={statusId}\n role={state === \"error\" && !errorDetailVisible ? \"alert\" : \"status\"}\n aria-live={\n state === \"error\" && !errorDetailVisible ? \"assertive\" : \"polite\"\n }\n className=\"sr-only\"\n >\n {copy.status[state]}\n </span>\n\n {settings ? (\n <Popover open={settingsOpen} onOpenChange={setSettingsOpen}>\n <Tooltip>\n <TooltipTrigger asChild>\n <PopoverAnchor asChild>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n disabled={ending}\n onClick={() => setSettingsOpen((open) => !open)}\n aria-label={copy.voiceSettings}\n aria-expanded={settingsOpen}\n className=\"size-8 rounded-full text-muted-foreground transition-transform duration-150 ease-out active:scale-[0.97]\"\n >\n <IconSettings />\n </Button>\n </PopoverAnchor>\n </TooltipTrigger>\n <TooltipContent>{copy.voiceSettings}</TooltipContent>\n </Tooltip>\n <PopoverContent\n side=\"top\"\n align=\"end\"\n sideOffset={10}\n className=\"w-[min(20rem,calc(100vw-2rem))] overflow-hidden p-0\"\n onOpenAutoFocus={(event) => event.preventDefault()}\n onInteractOutside={(event) => {\n const target = event.target;\n if (\n selectInteractionRef.current ||\n (target instanceof Element &&\n target.closest(\n '[data-realtime-voice-setting-options=\"true\"]',\n ))\n ) {\n event.preventDefault();\n }\n }}\n >\n <VoiceInlineSettings\n settings={settings}\n disabled={ending}\n onSelectOpenChange={handleSelectOpenChange}\n />\n </PopoverContent>\n </Popover>\n ) : null}\n\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n disabled={ending}\n onClick={onEndVoiceMode}\n aria-label={copy.endVoiceMode}\n className=\"size-8 rounded-full text-destructive transition-transform duration-150 ease-out hover:bg-destructive/10 hover:text-destructive active:scale-[0.97]\"\n >\n <IconPhoneOff />\n </Button>\n </TooltipTrigger>\n <TooltipContent>{copy.endVoiceMode}</TooltipContent>\n </Tooltip>\n </div>\n\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n type=\"button\"\n size=\"icon\"\n disabled={ending}\n onClick={() => {\n setControlsOpen(true);\n onToggleChat();\n }}\n aria-label={toggleLabel}\n aria-pressed={chatVisible}\n data-realtime-voice-chat-toggle=\"orb\"\n aria-describedby={statusId}\n aria-controls={controlsId}\n aria-expanded={controlsVisible}\n className={cn(\n \"relative isolate size-16 overflow-visible rounded-full ring-1 backdrop-blur-xl transition-transform duration-150 ease-out focus-visible:ring-offset-2 active:scale-[0.97] motion-reduce:transition-none\",\n ORB_STATE_CLASSES[state],\n )}\n >\n <span className=\"relative z-10 flex items-center justify-center\">\n {state === \"connecting\" ? (\n <VoiceConnectingIndicator />\n ) : state !== \"error\" ? (\n <VoiceWaveform\n level={activityLevel}\n reducedMotion={reducedMotion}\n activity={activity}\n />\n ) : (\n <IconAlertTriangle />\n )}\n </span>\n </Button>\n </TooltipTrigger>\n <TooltipContent>{toggleLabel}</TooltipContent>\n </Tooltip>\n </div>\n </div>\n );\n}\n"]}
@@ -173,6 +173,10 @@ export interface TiptapComposerProps {
173
173
  */
174
174
  onAttachmentError?: (message: string) => void;
175
175
  }
176
+ /** Tiptap keeps the Editor object truthy after destroy but clears commandManager. */
177
+ export declare function isComposerEditorUsable<T extends {
178
+ isDestroyed?: boolean;
179
+ }>(editor: T | null | undefined): editor is T;
176
180
  export declare function createTiptapComposerExtensions(getPlaceholder: () => string | undefined): (import("@tiptap/core").Extension<import("@tiptap/extension-placeholder").PlaceholderOptions, any> | import("@tiptap/core").Extension<import("@tiptap/starter-kit").StarterKitOptions, any> | import("@tiptap/core").Node<any, any>)[];
177
181
  export declare const MODEL_SELECTOR_POPOVER_STYLE: {
178
182
  fontSize: number;