@agent-native/core 0.7.55 → 0.7.57

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 (62) hide show
  1. package/dist/a2a/agent-card.d.ts +1 -1
  2. package/dist/a2a/agent-card.d.ts.map +1 -1
  3. package/dist/a2a/agent-card.js +30 -2
  4. package/dist/a2a/agent-card.js.map +1 -1
  5. package/dist/a2a/artifact-response.d.ts +1 -0
  6. package/dist/a2a/artifact-response.d.ts.map +1 -1
  7. package/dist/a2a/artifact-response.js +67 -7
  8. package/dist/a2a/artifact-response.js.map +1 -1
  9. package/dist/a2a/server.d.ts.map +1 -1
  10. package/dist/a2a/server.js +1 -1
  11. package/dist/a2a/server.js.map +1 -1
  12. package/dist/a2a/task-store.d.ts +1 -0
  13. package/dist/a2a/task-store.d.ts.map +1 -1
  14. package/dist/a2a/task-store.js +15 -0
  15. package/dist/a2a/task-store.js.map +1 -1
  16. package/dist/client/AssistantChat.d.ts +15 -0
  17. package/dist/client/AssistantChat.d.ts.map +1 -1
  18. package/dist/client/AssistantChat.js +55 -52
  19. package/dist/client/AssistantChat.js.map +1 -1
  20. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  21. package/dist/client/MultiTabAssistantChat.js +0 -13
  22. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  23. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  24. package/dist/client/composer/TiptapComposer.js +59 -19
  25. package/dist/client/composer/TiptapComposer.js.map +1 -1
  26. package/dist/client/composer/useVoiceDictation.d.ts +4 -1
  27. package/dist/client/composer/useVoiceDictation.d.ts.map +1 -1
  28. package/dist/client/composer/useVoiceDictation.js +246 -8
  29. package/dist/client/composer/useVoiceDictation.js.map +1 -1
  30. package/dist/client/index.d.ts +1 -0
  31. package/dist/client/index.d.ts.map +1 -1
  32. package/dist/client/index.js +1 -0
  33. package/dist/client/index.js.map +1 -1
  34. package/dist/client/resources/ResourcesPanel.js +2 -2
  35. package/dist/client/resources/ResourcesPanel.js.map +1 -1
  36. package/dist/client/settings/VoiceTranscriptionSection.d.ts.map +1 -1
  37. package/dist/client/settings/VoiceTranscriptionSection.js +155 -18
  38. package/dist/client/settings/VoiceTranscriptionSection.js.map +1 -1
  39. package/dist/client/use-chat-models.d.ts +33 -0
  40. package/dist/client/use-chat-models.d.ts.map +1 -0
  41. package/dist/client/use-chat-models.js +183 -0
  42. package/dist/client/use-chat-models.js.map +1 -0
  43. package/dist/integrations/a2a-continuation-processor.js +29 -15
  44. package/dist/integrations/a2a-continuation-processor.js.map +1 -1
  45. package/dist/integrations/adapters/slack.d.ts +2 -2
  46. package/dist/integrations/adapters/slack.js +20 -15
  47. package/dist/integrations/adapters/slack.js.map +1 -1
  48. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  49. package/dist/server/agent-chat-plugin.js +22 -1
  50. package/dist/server/agent-chat-plugin.js.map +1 -1
  51. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  52. package/dist/server/core-routes-plugin.js +6 -0
  53. package/dist/server/core-routes-plugin.js.map +1 -1
  54. package/dist/server/google-realtime-session.d.ts +14 -0
  55. package/dist/server/google-realtime-session.d.ts.map +1 -0
  56. package/dist/server/google-realtime-session.js +155 -0
  57. package/dist/server/google-realtime-session.js.map +1 -0
  58. package/dist/server/voice-providers-status.d.ts +4 -4
  59. package/dist/server/voice-providers-status.d.ts.map +1 -1
  60. package/dist/server/voice-providers-status.js +11 -0
  61. package/dist/server/voice-providers-status.js.map +1 -1
  62. package/package.json +1 -1
@@ -12,7 +12,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
12
12
  */
13
13
  import { useCallback, useEffect, useState } from "react";
14
14
  import { agentNativePath } from "../api-path.js";
15
- import { IconCheck, IconChevronDown, IconChevronRight, IconExternalLink, IconLoader2, IconMicrophone, } from "@tabler/icons-react";
15
+ import { IconAlertCircle, IconCheck, IconChevronDown, IconChevronRight, IconExternalLink, IconLoader2, IconLockOpen, IconMicrophone, } from "@tabler/icons-react";
16
16
  import { useBuilderStatus } from "./useBuilderStatus.js";
17
17
  const PREFS_URL = agentNativePath("/_agent-native/application-state/voice-transcription-prefs");
18
18
  const CLEANUP_PREFS_URL = agentNativePath("/_agent-native/application-state/voice-cleanup-prefs");
@@ -20,7 +20,6 @@ const SECRETS_URL = agentNativePath("/_agent-native/secrets");
20
20
  const PROVIDER_STATUS_URL = agentNativePath("/_agent-native/voice-providers/status");
21
21
  const DEFAULT_TRANSCRIPTION_MODE = "batch";
22
22
  const DEFAULT_BATCH_PROVIDER = "auto";
23
- const GOOGLE_REALTIME_STREAMING_ENABLED = false;
24
23
  function isProvider(value) {
25
24
  return (value === "auto" ||
26
25
  value === "openai" ||
@@ -71,6 +70,9 @@ export function VoiceTranscriptionSection() {
71
70
  const [showAdvanced, setShowAdvanced] = useState(false);
72
71
  const [cleanupEnabled, setCleanupEnabled] = useState(null);
73
72
  const { status: builderStatus } = useBuilderStatus();
73
+ const builderRealtimeReady = !!builderStatus?.privateKeyConfigured &&
74
+ !!builderStatus?.publicKeyConfigured;
75
+ const googleRealtimeReady = !!googleRealtimeConfigured && builderRealtimeReady;
74
76
  // Read cleanup pref (default: true if Builder is connected).
75
77
  useEffect(() => {
76
78
  let cancelled = false;
@@ -222,10 +224,14 @@ export function VoiceTranscriptionSection() {
222
224
  const chooseSource = (next) => {
223
225
  if (next === transcriptionMode)
224
226
  return;
225
- if (next === "google-realtime" &&
226
- (!googleRealtimeConfigured || !GOOGLE_REALTIME_STREAMING_ENABLED)) {
227
+ if (next === "google-realtime" && !googleRealtimeReady) {
227
228
  setShowAdvanced(true);
228
- focusKey("GOOGLE_APPLICATION_CREDENTIALS");
229
+ if (!googleRealtimeConfigured) {
230
+ focusKey("GOOGLE_APPLICATION_CREDENTIALS");
231
+ }
232
+ else if (!builderRealtimeReady) {
233
+ openBuilderConnect();
234
+ }
229
235
  return;
230
236
  }
231
237
  const previous = { transcriptionMode, provider, instructions };
@@ -234,6 +240,12 @@ export function VoiceTranscriptionSection() {
234
240
  setProvider(nextProvider);
235
241
  void persist(next, nextProvider, instructions, previous);
236
242
  };
243
+ const openBuilderConnect = () => {
244
+ if (typeof window === "undefined")
245
+ return;
246
+ const url = new URL(agentNativePath("/_agent-native/builder/connect"), window.location.origin).href;
247
+ window.open(url, "_blank", "noopener,noreferrer,width=600,height=700");
248
+ };
237
249
  const chooseBatchProvider = (next) => {
238
250
  const nextProvider = batchProvider(normalizeProvider(next));
239
251
  if (transcriptionMode === "batch" && nextProvider === provider)
@@ -253,28 +265,39 @@ export function VoiceTranscriptionSection() {
253
265
  if (transcriptionMode === null) {
254
266
  return (_jsxs("div", { className: "flex items-center gap-1.5 text-[10px] text-muted-foreground", children: [_jsx(IconLoader2, { size: 10, className: "animate-spin" }), "Loading\u2026"] }));
255
267
  }
256
- return (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "rounded-md border border-border bg-background p-2", children: [_jsx("div", { className: "mb-2 flex items-start justify-between gap-3 px-0.5", children: _jsxs("div", { children: [_jsx("div", { className: "text-[11px] font-medium text-foreground", children: "Live transcription" }), _jsx("p", { className: "mt-0.5 text-[10px] text-muted-foreground", children: "Choose where real-time words come from. Batch still runs after recording stops." })] }) }), _jsxs("div", { className: "space-y-2", children: [_jsx(ProviderOption, { id: "mac-native", selected: transcriptionMode === "mac-native", onSelect: () => chooseSource("mac-native"), title: "Mac Native", subtitle: "Free and fast in the macOS Tauri app. Web clients use the existing browser-native path when available.", rightSlot: _jsx("span", { className: "text-[10px] text-muted-foreground", children: "Tauri default" }) }), _jsx(ProviderOption, { id: "google-realtime", selected: transcriptionMode === "google-realtime", onSelect: () => chooseSource("google-realtime"), disabled: !googleRealtimeConfigured || !GOOGLE_REALTIME_STREAMING_ENABLED, title: "Google Realtime (coming soon)", subtitle: googleRealtimeConfigured
257
- ? "Credential detected. The streaming WebSocket path is not enabled yet, so keep using Mac Native or Batch."
258
- : "BYOK only for v1. Configure Google service account before selecting this source.", rightSlot: googleRealtimeConfigured ? (_jsxs("span", { className: "flex items-center gap-1 text-[10px] text-green-500", children: [_jsx(IconCheck, { size: 10 }), "Credential set"] })) : (_jsxs("button", { type: "button", onClick: (e) => {
268
+ return (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "rounded-md border border-border bg-background p-2", children: [_jsx("div", { className: "mb-2 flex items-start justify-between gap-3 px-0.5", children: _jsxs("div", { children: [_jsx("div", { className: "text-[11px] font-medium text-foreground", children: "Live transcription" }), _jsx("p", { className: "mt-0.5 text-[10px] text-muted-foreground", children: "Choose where real-time words come from. Batch still runs after recording stops." })] }) }), _jsxs("div", { className: "space-y-2", children: [_jsx(ProviderOption, { id: "mac-native", selected: transcriptionMode === "mac-native", onSelect: () => chooseSource("mac-native"), title: "Mac Native", subtitle: "Free and fast in the macOS Tauri app. Web clients use the existing browser-native path when available.", rightSlot: _jsx("span", { className: "text-[10px] text-muted-foreground", children: "Tauri default" }) }), _jsx(ProviderOption, { id: "google-realtime", selected: transcriptionMode === "google-realtime", onSelect: () => chooseSource("google-realtime"), disabled: !googleRealtimeReady, title: "Google Realtime", subtitle: googleRealtimeReady
269
+ ? "BYOK only for v1. Streams live partials and finals through Google Speech-to-Text."
270
+ : googleRealtimeConfigured
271
+ ? "Google credentials are set. Connect Builder completely to mint the managed realtime session."
272
+ : "BYOK only for v1. Configure Google service account before selecting this source.", rightSlot: googleRealtimeReady ? (_jsxs("span", { className: "flex items-center gap-1 text-[10px] text-green-500", children: [_jsx(IconCheck, { size: 10 }), "Ready"] })) : googleRealtimeConfigured ? (_jsxs("button", { type: "button", onClick: (e) => {
273
+ e.stopPropagation();
274
+ openBuilderConnect();
275
+ }, className: "inline-flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[10px] text-muted-foreground hover:bg-accent/40 hover:text-foreground", children: ["Connect Builder.io", _jsx(IconExternalLink, { size: 10 })] })) : (_jsxs("button", { type: "button", onClick: (e) => {
259
276
  e.stopPropagation();
260
277
  setShowAdvanced(true);
261
278
  focusKey("GOOGLE_APPLICATION_CREDENTIALS");
262
- }, className: "inline-flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[10px] text-muted-foreground hover:bg-accent/40 hover:text-foreground", children: ["Configure", _jsx(IconExternalLink, { size: 10 })] })) }), _jsx(ProviderOption, { id: "batch", selected: transcriptionMode === "batch", onSelect: () => chooseSource("batch"), title: "Batch", subtitle: "Universal fallback. Sends audio after recording stops through Builder Gemini, Gemini, Groq, then OpenAI." })] })] }), _jsxs("div", { className: "flex items-start justify-between gap-3 rounded-md border border-border bg-accent/30 px-2.5 py-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "text-[11px] font-medium text-foreground", children: "AI cleanup" }), _jsx("p", { className: "text-[10px] text-muted-foreground mt-0.5", children: "Polish punctuation, casing, filler words, titles, and summaries after capture. Builder Gemini is tried first; BYOK Gemini is the fallback." })] }), _jsxs("div", { className: "flex shrink-0 flex-col items-end gap-1", children: [_jsx("button", { type: "button", role: "switch", "aria-checked": !!cleanupEnabled, onClick: () => toggleCleanup(!cleanupEnabled), className: `relative inline-flex h-4 w-7 shrink-0 cursor-pointer items-center rounded-full transition-colors ${cleanupEnabled
263
- ? "bg-[#625DF5]"
279
+ }, className: "inline-flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[10px] text-muted-foreground hover:bg-accent/40 hover:text-foreground", children: ["Configure", _jsx(IconExternalLink, { size: 10 })] })) }), _jsx(ProviderOption, { id: "batch", selected: transcriptionMode === "batch", onSelect: () => chooseSource("batch"), title: "Batch", subtitle: "Universal fallback. Sends audio after recording stops through Builder Gemini, Gemini, Groq, then OpenAI." }), _jsx(SystemAudioStatus, {})] })] }), _jsxs("div", { className: "flex items-start justify-between gap-3 rounded-md border border-border bg-accent/30 px-2.5 py-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "text-[11px] font-medium text-foreground", children: "AI cleanup" }), _jsx("p", { className: "text-[10px] text-muted-foreground mt-0.5", children: "Polish punctuation, casing, filler words, titles, and summaries after capture. Builder Gemini is tried first; BYOK Gemini is the fallback." })] }), _jsxs("div", { className: "flex shrink-0 flex-col items-end gap-1", children: [_jsx("button", { type: "button", role: "switch", "aria-checked": !!cleanupEnabled, onClick: () => toggleCleanup(!cleanupEnabled),
280
+ // Theme tokens; streaming agent owns layout.
281
+ className: `relative inline-flex h-4 w-7 shrink-0 cursor-pointer items-center rounded-full transition-colors ${cleanupEnabled
282
+ ? "bg-primary"
264
283
  : "bg-muted-foreground/30 hover:bg-muted-foreground/50"}`, children: _jsx("span", { className: `inline-block h-3 w-3 transform rounded-full bg-background transition-transform ${cleanupEnabled ? "translate-x-3.5" : "translate-x-0.5"}` }) }), cleanupEnabled && (_jsx("span", { className: "text-[10px] text-muted-foreground", children: builderStatus?.configured
265
284
  ? "Builder ready"
266
285
  : geminiConfigured
267
286
  ? "Gemini key set"
268
- : "Needs key" }))] })] }), _jsxs("div", { className: "rounded-md border border-border bg-background", children: [_jsxs("button", { type: "button", onClick: () => setShowAdvanced((v) => !v), className: "w-full flex items-center justify-between gap-2 px-2.5 py-2 cursor-pointer", children: [_jsxs("span", { className: "text-[11px] font-medium text-foreground inline-flex items-center gap-1", children: [showAdvanced ? (_jsx(IconChevronDown, { size: 12 })) : (_jsx(IconChevronRight, { size: 12 })), "Add API keys"] }), _jsx("span", { className: "text-[10px] text-muted-foreground", children: "Google STT \u00B7 Gemini \u00B7 Groq \u00B7 OpenAI" })] }), showAdvanced && (_jsxs("div", { className: "px-2 pb-2 space-y-2", children: [_jsx(ProviderOption, { id: "google-service-account", selected: transcriptionMode === "google-realtime", onSelect: () => chooseSource("google-realtime"), disabled: !googleRealtimeConfigured || !GOOGLE_REALTIME_STREAMING_ENABLED, title: "Google Speech-to-Text service account", subtitle: "Service-account JSON for the future WebSocket to Google StreamingRecognize path. Saved credentials are detected now; streaming is still coming soon.", rightSlot: googleRealtimeConfigured ===
269
- null ? null : googleRealtimeConfigured ? (_jsxs("span", { className: "flex items-center gap-1 text-[10px] text-green-500", children: [_jsx(IconCheck, { size: 10 }), "Credential set"] })) : (_jsxs("button", { type: "button", onClick: (e) => {
287
+ : "Needs key" }))] })] }), _jsxs("div", { className: "rounded-md border border-border bg-background", children: [_jsxs("button", { type: "button", onClick: () => setShowAdvanced((v) => !v), className: "w-full flex items-center justify-between gap-2 px-2.5 py-2 cursor-pointer", children: [_jsxs("span", { className: "text-[11px] font-medium text-foreground inline-flex items-center gap-1", children: [showAdvanced ? (_jsx(IconChevronDown, { size: 12 })) : (_jsx(IconChevronRight, { size: 12 })), "Add API keys"] }), _jsx("span", { className: "text-[10px] text-muted-foreground", children: "Google STT \u00B7 Gemini \u00B7 Groq \u00B7 OpenAI" })] }), showAdvanced && (_jsxs("div", { className: "px-2 pb-2 space-y-2", children: [_jsx(ProviderOption, { id: "google-service-account", selected: transcriptionMode === "google-realtime", onSelect: () => chooseSource("google-realtime"), disabled: !googleRealtimeReady, title: "Google Speech-to-Text service account", subtitle: googleRealtimeConfigured
288
+ ? "Service-account JSON is set. Connect Builder to mint the managed realtime WebSocket session."
289
+ : "Service-account JSON for the dedicated realtime WebSocket to Google StreamingRecognize.", rightSlot: googleRealtimeConfigured ===
290
+ null ? null : googleRealtimeReady ? (_jsxs("span", { className: "flex items-center gap-1 text-[10px] text-green-500", children: [_jsx(IconCheck, { size: 10 }), "Ready"] })) : googleRealtimeConfigured ? (_jsxs("button", { type: "button", onClick: (e) => {
291
+ e.stopPropagation();
292
+ openBuilderConnect();
293
+ }, className: "inline-flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[10px] text-muted-foreground hover:text-foreground hover:bg-accent/40", children: ["Connect Builder.io", _jsx(IconExternalLink, { size: 10 })] })) : (_jsxs("button", { type: "button", onClick: (e) => {
270
294
  e.stopPropagation();
271
295
  focusKey("GOOGLE_APPLICATION_CREDENTIALS");
272
296
  }, className: "inline-flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[10px] text-muted-foreground hover:text-foreground hover:bg-accent/40", children: ["Configure", _jsx(IconExternalLink, { size: 10 })] })) }), _jsx(ProviderOption, { id: "auto", selected: transcriptionMode === "batch" && provider === "auto", onSelect: () => chooseBatchProvider("auto"), title: "Automatic batch fallback", subtitle: "Keep the current Clips fallback chain: Builder Gemini, Gemini, Groq, then OpenAI." }), _jsx(ProviderOption, { id: "builder-gemini", selected: transcriptionMode === "batch" && provider === "builder-gemini", onSelect: () => chooseBatchProvider("builder-gemini"), disabled: !builderStatus?.configured, title: "Builder.io Connect", subtitle: builderStatus?.configured
273
297
  ? "Use Builder-hosted Gemini Flash-Lite for batch transcription and cleanup."
274
298
  : "One-click connect for Gemini Flash-Lite cleanup and batch transcription. No Google key needed.", rightSlot: builderStatus?.configured ? (_jsxs("span", { className: "flex items-center gap-1 text-[10px] text-green-500", children: [_jsx(IconCheck, { size: 10 }), "Connected"] })) : (_jsxs("button", { type: "button", onClick: (e) => {
275
299
  e.stopPropagation();
276
- const url = new URL(agentNativePath("/_agent-native/builder/connect"), window.location.origin).href;
277
- window.open(url, "_blank", "noopener,noreferrer,width=600,height=700");
300
+ openBuilderConnect();
278
301
  }, className: "inline-flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[10px] text-muted-foreground hover:text-foreground hover:bg-accent/40", children: ["Connect Builder.io", _jsx(IconExternalLink, { size: 10 })] })) }), _jsx(ProviderOption, { id: "gemini", selected: transcriptionMode === "batch" && provider === "gemini", onSelect: () => chooseBatchProvider("gemini"), title: "Google Gemini", subtitle: "BYOK Gemini for AI cleanup and optional strict batch transcription.", rightSlot: geminiConfigured === null ? null : geminiConfigured ? (_jsxs("span", { className: "flex items-center gap-1 text-[10px] text-green-500", children: [_jsx(IconCheck, { size: 10 }), "Key set"] })) : (_jsxs("button", { type: "button", onClick: (e) => {
279
302
  e.stopPropagation();
280
303
  focusKey("GEMINI_API_KEY");
@@ -299,11 +322,125 @@ function ProviderOption({ id, selected, disabled, onSelect, title, subtitle, rig
299
322
  onSelect();
300
323
  }
301
324
  };
302
- return (_jsxs("div", { role: "button", tabIndex: disabled ? -1 : 0, onClick: select, onKeyDown: onKeyDown, "aria-pressed": selected, "aria-disabled": disabled || undefined, className: `w-full text-left rounded-md border px-2.5 py-2 flex items-start gap-2 ${selected
303
- ? "border-[#625DF5] bg-[#625DF5]/10"
325
+ return (_jsxs("div", { role: "button", tabIndex: disabled ? -1 : 0, onClick: select, onKeyDown: onKeyDown, "aria-pressed": selected, "aria-disabled": disabled || undefined,
326
+ // Theme tokens; streaming agent owns layout.
327
+ className: `w-full text-left rounded-md border px-2.5 py-2 flex items-start gap-2 ${selected
328
+ ? "border-primary bg-primary/10"
304
329
  : "border-border bg-accent/30 hover:bg-accent/50"} ${disabled ? "opacity-60 cursor-not-allowed" : ""}`, children: [_jsx("span", { className: `mt-[2px] shrink-0 flex h-3.5 w-3.5 items-center justify-center rounded-full border ${selected
305
- ? "border-[#625DF5] bg-[#625DF5]"
306
- : "border-muted-foreground/40 bg-background"}`, children: selected && (_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-background" })) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("div", { className: "text-[11px] font-medium text-foreground", children: title }), rightSlot && _jsx("div", { className: "shrink-0", children: rightSlot })] }), subtitle && (_jsx("p", { className: "text-[10px] text-muted-foreground mt-0.5", children: subtitle }))] })] }));
330
+ ? "border-primary bg-primary"
331
+ : "border-muted-foreground/40 bg-background"}`, children: selected && (_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-primary-foreground" })) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("div", { className: "text-[11px] font-medium text-foreground", children: title }), rightSlot && _jsx("div", { className: "shrink-0", children: rightSlot })] }), subtitle && (_jsx("p", { className: "text-[10px] text-muted-foreground mt-0.5", children: subtitle }))] })] }));
332
+ }
333
+ function SystemAudioStatus() {
334
+ const [state, setState] = useState(null);
335
+ useEffect(() => {
336
+ let cancelled = false;
337
+ if (typeof window === "undefined")
338
+ return;
339
+ const tauri = window
340
+ .__TAURI_INTERNALS__;
341
+ if (!tauri)
342
+ return; // Web users: render nothing.
343
+ setState({ kind: "loading" });
344
+ // Dynamic import keeps `@tauri-apps/api` out of the web bundle's static
345
+ // graph; it is only resolved inside the desktop shell where the dep is
346
+ // present at build time.
347
+ // Cast to suppress missing types — `@tauri-apps/api` is a desktop-shell
348
+ // dep, not a static dep of `packages/core`. Resolves at runtime only when
349
+ // we're inside Tauri (gated by the `__TAURI_INTERNALS__` check above).
350
+ import(/* @vite-ignore */ "@tauri-apps/api/core")
351
+ .then(async ({ invoke }) => {
352
+ try {
353
+ const status = (await invoke("system_audio_version_status"));
354
+ if (cancelled)
355
+ return;
356
+ if (status && !status.supported) {
357
+ setState({
358
+ kind: "unsupported",
359
+ reason: status.reason ??
360
+ `ScreenCaptureKit is unavailable on ${status.os_version}.`,
361
+ });
362
+ return;
363
+ }
364
+ // Supported — now probe permission. This may prompt; calling it
365
+ // here matches the original on-mount semantics requested in the
366
+ // settings flow.
367
+ try {
368
+ const granted = (await invoke("system_audio_request_permission"));
369
+ if (cancelled)
370
+ return;
371
+ setState(granted ? { kind: "available" } : { kind: "denied" });
372
+ }
373
+ catch (err) {
374
+ if (cancelled)
375
+ return;
376
+ const msg = String(err ?? "");
377
+ if (/macOS\s*1[0-2]|requires macOS 13/i.test(msg)) {
378
+ setState({ kind: "unsupported", reason: msg });
379
+ }
380
+ else {
381
+ setState({ kind: "denied" });
382
+ }
383
+ }
384
+ }
385
+ catch {
386
+ // Older desktop builds may not have the new command yet —
387
+ // fall back to the permission probe.
388
+ if (cancelled)
389
+ return;
390
+ try {
391
+ const granted = (await invoke("system_audio_request_permission"));
392
+ if (cancelled)
393
+ return;
394
+ setState(granted ? { kind: "available" } : { kind: "denied" });
395
+ }
396
+ catch (err) {
397
+ if (cancelled)
398
+ return;
399
+ const msg = String(err ?? "");
400
+ if (/macOS|ScreenCaptureKit/i.test(msg)) {
401
+ setState({ kind: "unsupported", reason: msg });
402
+ }
403
+ else {
404
+ setState({ kind: "denied" });
405
+ }
406
+ }
407
+ }
408
+ })
409
+ .catch(() => {
410
+ // @tauri-apps/api not resolvable -> behave like web.
411
+ if (!cancelled)
412
+ setState(null);
413
+ });
414
+ return () => {
415
+ cancelled = true;
416
+ };
417
+ }, []);
418
+ const openPrivacy = useCallback(() => {
419
+ if (typeof window === "undefined")
420
+ return;
421
+ const tauri = window
422
+ .__TAURI_INTERNALS__;
423
+ if (!tauri)
424
+ return;
425
+ // Cast to suppress missing types — `@tauri-apps/api` is a desktop-shell
426
+ // dep, not a static dep of `packages/core`. Resolves at runtime only when
427
+ // we're inside Tauri (gated by the `__TAURI_INTERNALS__` check above).
428
+ import(/* @vite-ignore */ "@tauri-apps/api/core")
429
+ .then(({ invoke }) => invoke("system_audio_open_privacy_settings"))
430
+ .catch(() => {
431
+ // Older desktop builds without this command — no-op.
432
+ });
433
+ }, []);
434
+ if (!state || state.kind === "loading")
435
+ return null;
436
+ if (state.kind === "available") {
437
+ return (_jsxs("div", { className: "flex items-center gap-1.5 px-0.5 pt-1 text-[10px] text-muted-foreground", children: [_jsx(IconCheck, { size: 11, className: "text-green-500" }), _jsx("span", { children: "System audio capture available." })] }));
438
+ }
439
+ if (state.kind === "unsupported") {
440
+ return (_jsxs("div", { className: "flex items-center gap-1.5 px-0.5 pt-1 text-[10px] text-muted-foreground", children: [_jsx("span", { className: "inline-block h-1.5 w-1.5 shrink-0 rounded-full bg-red-500", "aria-hidden": true }), _jsx("span", { children: "System audio requires macOS 13 or later \u2014 meetings will use mic-only." })] }));
441
+ }
442
+ // denied
443
+ return (_jsxs("div", { className: "flex items-start gap-1.5 px-0.5 pt-1 text-[10px] text-muted-foreground", children: [_jsx(IconAlertCircle, { size: 11, className: "mt-[1px] shrink-0 text-amber-500" }), _jsxs("div", { className: "flex-1", children: [_jsx("span", { children: "Grant Screen Recording permission in System Settings -> Privacy." }), _jsxs("button", { type: "button", onClick: openPrivacy, className: "ml-1 inline-flex items-center gap-1 rounded border border-border px-1.5 py-0.5 text-[10px] text-muted-foreground hover:bg-accent/40 hover:text-foreground", children: [_jsx(IconLockOpen, { size: 10 }), "Open System Settings"] })] })] }));
307
444
  }
308
445
  export function VoiceTranscriptionIcon() {
309
446
  return _jsx(IconMicrophone, { size: 14 });
@@ -1 +1 @@
1
- {"version":3,"file":"VoiceTranscriptionSection.js","sourceRoot":"","sources":["../../../src/client/settings/VoiceTranscriptionSection.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * <VoiceTranscriptionSection /> — source + cleanup settings for voice input.\n *\n * Writes the selection to application_state under `voice-transcription-prefs`\n * so the composer's `useVoiceDictation` hook picks it up on next record. The\n * legacy `provider` field is still written alongside `transcriptionMode` so\n * older clients continue to normalize safely.\n *\n * Provider status comes from `/_agent-native/voice-providers/status`, which\n * mirrors the server transcription route's key/env resolution.\n */\n\nimport React, { useCallback, useEffect, useState } from \"react\";\nimport { agentNativePath } from \"../api-path.js\";\nimport {\n IconCheck,\n IconChevronDown,\n IconChevronRight,\n IconExternalLink,\n IconLoader2,\n IconMicrophone,\n} from \"@tabler/icons-react\";\nimport { useBuilderStatus } from \"./useBuilderStatus.js\";\n\ntype TranscriptionMode = \"mac-native\" | \"google-realtime\" | \"batch\";\n\ntype Provider =\n | \"auto\"\n | \"openai\"\n | \"builder-gemini\"\n | \"builder\"\n | \"browser\"\n | \"gemini\"\n | \"groq\";\n\ninterface Prefs {\n transcriptionMode?: TranscriptionMode;\n provider?: Provider;\n instructions?: string;\n}\n\ninterface SecretStatus {\n key: string;\n status: \"set\" | \"unset\" | \"invalid\";\n}\n\ninterface ProviderStatus {\n builder: boolean;\n gemini: boolean;\n openai: boolean;\n groq: boolean;\n googleRealtime?: boolean;\n browser: true;\n native?: true;\n}\n\nconst PREFS_URL = agentNativePath(\n \"/_agent-native/application-state/voice-transcription-prefs\",\n);\nconst CLEANUP_PREFS_URL = agentNativePath(\n \"/_agent-native/application-state/voice-cleanup-prefs\",\n);\nconst SECRETS_URL = agentNativePath(\"/_agent-native/secrets\");\nconst PROVIDER_STATUS_URL = agentNativePath(\n \"/_agent-native/voice-providers/status\",\n);\nconst DEFAULT_TRANSCRIPTION_MODE: TranscriptionMode = \"batch\";\nconst DEFAULT_BATCH_PROVIDER: Provider = \"auto\";\nconst GOOGLE_REALTIME_STREAMING_ENABLED = false;\n\nfunction isProvider(value: unknown): value is Provider {\n return (\n value === \"auto\" ||\n value === \"openai\" ||\n value === \"builder-gemini\" ||\n value === \"builder\" ||\n value === \"browser\" ||\n value === \"gemini\" ||\n value === \"groq\"\n );\n}\n\nfunction isTranscriptionMode(value: unknown): value is TranscriptionMode {\n return (\n value === \"mac-native\" || value === \"google-realtime\" || value === \"batch\"\n );\n}\n\nfunction normalizeProvider(value: unknown): Provider | null {\n if (!isProvider(value)) return null;\n return value === \"builder\" ? \"builder-gemini\" : value;\n}\n\nfunction legacyModeFromProvider(provider: Provider | null): TranscriptionMode {\n if (provider === \"browser\") return \"mac-native\";\n return \"batch\";\n}\n\nfunction providerForMode(\n mode: TranscriptionMode,\n currentProvider: Provider | null,\n): Provider {\n if (mode === \"mac-native\") return \"browser\";\n if (mode === \"google-realtime\") return \"auto\";\n if (!currentProvider || currentProvider === \"browser\") {\n return DEFAULT_BATCH_PROVIDER;\n }\n return currentProvider;\n}\n\nfunction batchProvider(provider: Provider | null): Provider {\n if (!provider || provider === \"browser\") return DEFAULT_BATCH_PROVIDER;\n return provider;\n}\n\nexport function VoiceTranscriptionSection() {\n const [transcriptionMode, setTranscriptionMode] =\n useState<TranscriptionMode | null>(null);\n const [provider, setProvider] = useState<Provider>(DEFAULT_BATCH_PROVIDER);\n const [instructions, setInstructions] = useState(\"\");\n const [openAiConfigured, setOpenAiConfigured] = useState<boolean | null>(\n null,\n );\n const [geminiConfigured, setGeminiConfigured] = useState<boolean | null>(\n null,\n );\n const [groqConfigured, setGroqConfigured] = useState<boolean | null>(null);\n const [googleRealtimeConfigured, setGoogleRealtimeConfigured] = useState<\n boolean | null\n >(null);\n const [saving, setSaving] = useState(false);\n const [saveError, setSaveError] = useState<string | null>(null);\n const [showAdvanced, setShowAdvanced] = useState(false);\n const [cleanupEnabled, setCleanupEnabled] = useState<boolean | null>(null);\n const { status: builderStatus } = useBuilderStatus();\n\n // Read cleanup pref (default: true if Builder is connected).\n useEffect(() => {\n let cancelled = false;\n fetch(CLEANUP_PREFS_URL)\n .then((r) => (r.ok ? r.json() : null))\n .then(\n (\n body:\n | { enabled?: boolean }\n | { value?: { enabled?: boolean } }\n | null,\n ) => {\n if (cancelled) return;\n const stored =\n (body as { enabled?: boolean } | null)?.enabled ??\n (body as { value?: { enabled?: boolean } } | null)?.value?.enabled;\n if (typeof stored === \"boolean\") setCleanupEnabled(stored);\n else setCleanupEnabled(null); // resolve once builderStatus arrives\n },\n )\n .catch(() => !cancelled && setCleanupEnabled(null));\n return () => {\n cancelled = true;\n };\n }, []);\n\n useEffect(() => {\n if (cleanupEnabled !== null) return;\n if (builderStatus?.configured !== undefined) {\n setCleanupEnabled(!!builderStatus.configured);\n }\n }, [builderStatus?.configured, cleanupEnabled]);\n\n const toggleCleanup = async (next: boolean) => {\n const previous = cleanupEnabled;\n setCleanupEnabled(next);\n try {\n const res = await fetch(CLEANUP_PREFS_URL, {\n method: \"PUT\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ enabled: next }),\n });\n if (!res.ok) {\n throw new Error(`HTTP ${res.status}`);\n }\n } catch {\n setCleanupEnabled(previous);\n }\n };\n\n useEffect(() => {\n let cancelled = false;\n fetch(PREFS_URL)\n .then((r) => (r.ok ? r.json() : null))\n .then((body: Prefs | { value?: Prefs } | null) => {\n if (cancelled) return;\n const value =\n (body as { value?: Prefs } | null)?.value ?? (body as Prefs | null);\n const p = normalizeProvider(\n (body as Prefs | null)?.provider ??\n (body as { value?: Prefs } | null)?.value?.provider,\n );\n const storedMode = isTranscriptionMode(value?.transcriptionMode)\n ? value.transcriptionMode\n : null;\n const mode =\n storedMode ??\n (p ? legacyModeFromProvider(p) : DEFAULT_TRANSCRIPTION_MODE);\n const savedInstructions =\n (body as Prefs | null)?.instructions ??\n (body as { value?: Prefs } | null)?.value?.instructions;\n setTranscriptionMode(mode);\n setProvider(providerForMode(mode, p));\n if (typeof savedInstructions === \"string\") {\n setInstructions(savedInstructions);\n }\n })\n .catch(() => {\n if (!cancelled) {\n setTranscriptionMode(DEFAULT_TRANSCRIPTION_MODE);\n setProvider(DEFAULT_BATCH_PROVIDER);\n }\n });\n return () => {\n cancelled = true;\n };\n }, []);\n\n useEffect(() => {\n let cancelled = false;\n fetch(PROVIDER_STATUS_URL)\n .then((r) => (r.ok ? r.json() : null))\n .then((status: ProviderStatus | null) => {\n if (cancelled) return;\n if (status) {\n setOpenAiConfigured(status.openai);\n setGeminiConfigured(status.gemini);\n setGroqConfigured(status.groq);\n setGoogleRealtimeConfigured(!!status.googleRealtime);\n return;\n }\n return fetch(SECRETS_URL)\n .then((r) => (r.ok ? r.json() : []))\n .then((list: SecretStatus[]) => {\n if (cancelled) return;\n const find = (key: string) =>\n Array.isArray(list) ? list.find((s) => s.key === key) : null;\n setOpenAiConfigured(find(\"OPENAI_API_KEY\")?.status === \"set\");\n setGeminiConfigured(find(\"GEMINI_API_KEY\")?.status === \"set\");\n setGroqConfigured(find(\"GROQ_API_KEY\")?.status === \"set\");\n setGoogleRealtimeConfigured(\n find(\"GOOGLE_APPLICATION_CREDENTIALS\")?.status === \"set\",\n );\n });\n })\n .catch(() => {\n if (!cancelled) {\n setOpenAiConfigured(false);\n setGeminiConfigured(false);\n setGroqConfigured(false);\n setGoogleRealtimeConfigured(false);\n }\n });\n return () => {\n cancelled = true;\n };\n }, []);\n\n const persist = useCallback(\n async (\n nextMode: TranscriptionMode,\n nextProvider: Provider,\n nextInstructions: string,\n previous: {\n transcriptionMode: TranscriptionMode | null;\n provider: Provider;\n instructions: string;\n },\n ) => {\n setSaving(true);\n setSaveError(null);\n try {\n const res = await fetch(PREFS_URL, {\n method: \"PUT\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n transcriptionMode: nextMode,\n provider: nextProvider,\n instructions: nextInstructions.trim(),\n }),\n });\n if (!res.ok) {\n throw new Error(`HTTP ${res.status}`);\n }\n } catch (err) {\n // Revert the optimistic update so the UI matches server state.\n setTranscriptionMode(previous.transcriptionMode);\n setProvider(previous.provider);\n setInstructions(previous.instructions);\n setSaveError(\n `Couldn't save: ${(err as Error)?.message ?? \"network error\"}. Try again.`,\n );\n } finally {\n setSaving(false);\n }\n },\n [],\n );\n\n const focusKey = (key: string) => {\n if (typeof window === \"undefined\") return;\n window.location.hash = `#secrets:${key}`;\n };\n\n const chooseSource = (next: TranscriptionMode) => {\n if (next === transcriptionMode) return;\n if (\n next === \"google-realtime\" &&\n (!googleRealtimeConfigured || !GOOGLE_REALTIME_STREAMING_ENABLED)\n ) {\n setShowAdvanced(true);\n focusKey(\"GOOGLE_APPLICATION_CREDENTIALS\");\n return;\n }\n const previous = { transcriptionMode, provider, instructions };\n const nextProvider = providerForMode(next, provider);\n setTranscriptionMode(next);\n setProvider(nextProvider);\n void persist(next, nextProvider, instructions, previous);\n };\n\n const chooseBatchProvider = (next: Provider) => {\n const nextProvider = batchProvider(normalizeProvider(next));\n if (transcriptionMode === \"batch\" && nextProvider === provider) return;\n const previous = { transcriptionMode, provider, instructions };\n setTranscriptionMode(\"batch\");\n setProvider(nextProvider);\n void persist(\"batch\", nextProvider, instructions, previous);\n };\n\n const updateInstructions = (next: string) => {\n const previous = { transcriptionMode, provider, instructions };\n setInstructions(next);\n if (transcriptionMode) {\n void persist(transcriptionMode, provider, next, previous);\n }\n };\n\n if (transcriptionMode === null) {\n return (\n <div className=\"flex items-center gap-1.5 text-[10px] text-muted-foreground\">\n <IconLoader2 size={10} className=\"animate-spin\" />\n Loading…\n </div>\n );\n }\n\n return (\n <div className=\"space-y-2\">\n <div className=\"rounded-md border border-border bg-background p-2\">\n <div className=\"mb-2 flex items-start justify-between gap-3 px-0.5\">\n <div>\n <div className=\"text-[11px] font-medium text-foreground\">\n Live transcription\n </div>\n <p className=\"mt-0.5 text-[10px] text-muted-foreground\">\n Choose where real-time words come from. Batch still runs after\n recording stops.\n </p>\n </div>\n </div>\n <div className=\"space-y-2\">\n <ProviderOption\n id=\"mac-native\"\n selected={transcriptionMode === \"mac-native\"}\n onSelect={() => chooseSource(\"mac-native\")}\n title=\"Mac Native\"\n subtitle=\"Free and fast in the macOS Tauri app. Web clients use the existing browser-native path when available.\"\n rightSlot={\n <span className=\"text-[10px] text-muted-foreground\">\n Tauri default\n </span>\n }\n />\n <ProviderOption\n id=\"google-realtime\"\n selected={transcriptionMode === \"google-realtime\"}\n onSelect={() => chooseSource(\"google-realtime\")}\n disabled={\n !googleRealtimeConfigured || !GOOGLE_REALTIME_STREAMING_ENABLED\n }\n title=\"Google Realtime (coming soon)\"\n subtitle={\n googleRealtimeConfigured\n ? \"Credential detected. The streaming WebSocket path is not enabled yet, so keep using Mac Native or Batch.\"\n : \"BYOK only for v1. Configure Google service account before selecting this source.\"\n }\n rightSlot={\n googleRealtimeConfigured ? (\n <span className=\"flex items-center gap-1 text-[10px] text-green-500\">\n <IconCheck size={10} />\n Credential set\n </span>\n ) : (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n setShowAdvanced(true);\n focusKey(\"GOOGLE_APPLICATION_CREDENTIALS\");\n }}\n className=\"inline-flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[10px] text-muted-foreground hover:bg-accent/40 hover:text-foreground\"\n >\n Configure\n <IconExternalLink size={10} />\n </button>\n )\n }\n />\n <ProviderOption\n id=\"batch\"\n selected={transcriptionMode === \"batch\"}\n onSelect={() => chooseSource(\"batch\")}\n title=\"Batch\"\n subtitle=\"Universal fallback. Sends audio after recording stops through Builder Gemini, Gemini, Groq, then OpenAI.\"\n />\n </div>\n </div>\n\n <div className=\"flex items-start justify-between gap-3 rounded-md border border-border bg-accent/30 px-2.5 py-2\">\n <div className=\"min-w-0\">\n <div className=\"text-[11px] font-medium text-foreground\">\n AI cleanup\n </div>\n <p className=\"text-[10px] text-muted-foreground mt-0.5\">\n Polish punctuation, casing, filler words, titles, and summaries\n after capture. Builder Gemini is tried first; BYOK Gemini is the\n fallback.\n </p>\n </div>\n <div className=\"flex shrink-0 flex-col items-end gap-1\">\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={!!cleanupEnabled}\n onClick={() => toggleCleanup(!cleanupEnabled)}\n className={`relative inline-flex h-4 w-7 shrink-0 cursor-pointer items-center rounded-full transition-colors ${\n cleanupEnabled\n ? \"bg-[#625DF5]\"\n : \"bg-muted-foreground/30 hover:bg-muted-foreground/50\"\n }`}\n >\n <span\n className={`inline-block h-3 w-3 transform rounded-full bg-background transition-transform ${\n cleanupEnabled ? \"translate-x-3.5\" : \"translate-x-0.5\"\n }`}\n />\n </button>\n {cleanupEnabled && (\n <span className=\"text-[10px] text-muted-foreground\">\n {builderStatus?.configured\n ? \"Builder ready\"\n : geminiConfigured\n ? \"Gemini key set\"\n : \"Needs key\"}\n </span>\n )}\n </div>\n </div>\n\n <div className=\"rounded-md border border-border bg-background\">\n <button\n type=\"button\"\n onClick={() => setShowAdvanced((v) => !v)}\n className=\"w-full flex items-center justify-between gap-2 px-2.5 py-2 cursor-pointer\"\n >\n <span className=\"text-[11px] font-medium text-foreground inline-flex items-center gap-1\">\n {showAdvanced ? (\n <IconChevronDown size={12} />\n ) : (\n <IconChevronRight size={12} />\n )}\n Add API keys\n </span>\n <span className=\"text-[10px] text-muted-foreground\">\n Google STT · Gemini · Groq · OpenAI\n </span>\n </button>\n\n {showAdvanced && (\n <div className=\"px-2 pb-2 space-y-2\">\n <ProviderOption\n id=\"google-service-account\"\n selected={transcriptionMode === \"google-realtime\"}\n onSelect={() => chooseSource(\"google-realtime\")}\n disabled={\n !googleRealtimeConfigured || !GOOGLE_REALTIME_STREAMING_ENABLED\n }\n title=\"Google Speech-to-Text service account\"\n subtitle=\"Service-account JSON for the future WebSocket to Google StreamingRecognize path. Saved credentials are detected now; streaming is still coming soon.\"\n rightSlot={\n googleRealtimeConfigured ===\n null ? null : googleRealtimeConfigured ? (\n <span className=\"flex items-center gap-1 text-[10px] text-green-500\">\n <IconCheck size={10} />\n Credential set\n </span>\n ) : (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n focusKey(\"GOOGLE_APPLICATION_CREDENTIALS\");\n }}\n className=\"inline-flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[10px] text-muted-foreground hover:text-foreground hover:bg-accent/40\"\n >\n Configure\n <IconExternalLink size={10} />\n </button>\n )\n }\n />\n\n <ProviderOption\n id=\"auto\"\n selected={transcriptionMode === \"batch\" && provider === \"auto\"}\n onSelect={() => chooseBatchProvider(\"auto\")}\n title=\"Automatic batch fallback\"\n subtitle=\"Keep the current Clips fallback chain: Builder Gemini, Gemini, Groq, then OpenAI.\"\n />\n\n <ProviderOption\n id=\"builder-gemini\"\n selected={\n transcriptionMode === \"batch\" && provider === \"builder-gemini\"\n }\n onSelect={() => chooseBatchProvider(\"builder-gemini\")}\n disabled={!builderStatus?.configured}\n title=\"Builder.io Connect\"\n subtitle={\n builderStatus?.configured\n ? \"Use Builder-hosted Gemini Flash-Lite for batch transcription and cleanup.\"\n : \"One-click connect for Gemini Flash-Lite cleanup and batch transcription. No Google key needed.\"\n }\n rightSlot={\n builderStatus?.configured ? (\n <span className=\"flex items-center gap-1 text-[10px] text-green-500\">\n <IconCheck size={10} />\n Connected\n </span>\n ) : (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n const url = new URL(\n agentNativePath(\"/_agent-native/builder/connect\"),\n window.location.origin,\n ).href;\n window.open(\n url,\n \"_blank\",\n \"noopener,noreferrer,width=600,height=700\",\n );\n }}\n className=\"inline-flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[10px] text-muted-foreground hover:text-foreground hover:bg-accent/40\"\n >\n Connect Builder.io\n <IconExternalLink size={10} />\n </button>\n )\n }\n />\n\n <ProviderOption\n id=\"gemini\"\n selected={transcriptionMode === \"batch\" && provider === \"gemini\"}\n onSelect={() => chooseBatchProvider(\"gemini\")}\n title=\"Google Gemini\"\n subtitle=\"BYOK Gemini for AI cleanup and optional strict batch transcription.\"\n rightSlot={\n geminiConfigured === null ? null : geminiConfigured ? (\n <span className=\"flex items-center gap-1 text-[10px] text-green-500\">\n <IconCheck size={10} />\n Key set\n </span>\n ) : (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n focusKey(\"GEMINI_API_KEY\");\n }}\n className=\"inline-flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[10px] text-muted-foreground hover:text-foreground hover:bg-accent/40\"\n >\n Add key\n <IconExternalLink size={10} />\n </button>\n )\n }\n />\n\n <ProviderOption\n id=\"openai\"\n selected={transcriptionMode === \"batch\" && provider === \"openai\"}\n onSelect={() => chooseBatchProvider(\"openai\")}\n title=\"OpenAI Whisper\"\n subtitle=\"Batch Whisper provider. Requires an OpenAI API key.\"\n rightSlot={\n openAiConfigured === null ? null : openAiConfigured ? (\n <span className=\"flex items-center gap-1 text-[10px] text-green-500\">\n <IconCheck size={10} />\n Key set\n </span>\n ) : (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n focusKey(\"OPENAI_API_KEY\");\n }}\n className=\"inline-flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[10px] text-muted-foreground hover:text-foreground hover:bg-accent/40\"\n >\n Add key\n <IconExternalLink size={10} />\n </button>\n )\n }\n />\n\n <ProviderOption\n id=\"groq\"\n selected={transcriptionMode === \"batch\" && provider === \"groq\"}\n onSelect={() => chooseBatchProvider(\"groq\")}\n title=\"Groq Whisper\"\n subtitle=\"Fast Whisper batch provider. Requires a Groq API key.\"\n rightSlot={\n groqConfigured === null ? null : groqConfigured ? (\n <span className=\"flex items-center gap-1 text-[10px] text-green-500\">\n <IconCheck size={10} />\n Key set\n </span>\n ) : (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n focusKey(\"GROQ_API_KEY\");\n }}\n className=\"inline-flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[10px] text-muted-foreground hover:text-foreground hover:bg-accent/40\"\n >\n Add key\n <IconExternalLink size={10} />\n </button>\n )\n }\n />\n </div>\n )}\n </div>\n\n {(cleanupEnabled || transcriptionMode === \"batch\") && (\n <div className=\"rounded-md border border-border bg-accent/20 px-2.5 py-2\">\n <label\n htmlFor=\"voice-transcription-instructions\"\n className=\"block text-[10px] font-medium text-foreground\"\n >\n Custom instructions\n </label>\n <textarea\n id=\"voice-transcription-instructions\"\n value={instructions}\n onChange={(event) => updateInstructions(event.target.value)}\n placeholder=\"Names, casing, punctuation, style, or terms to preserve.\"\n className=\"mt-1 min-h-16 w-full resize-y rounded border border-border bg-background px-2 py-1.5 text-[11px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-accent\"\n />\n <p className=\"mt-1 text-[10px] text-muted-foreground\">\n Included with batch transcription and AI cleanup.\n </p>\n </div>\n )}\n\n {saving && <p className=\"text-[10px] text-muted-foreground\">Saving…</p>}\n {saveError && !saving && (\n <p className=\"text-[10px] text-red-500\" role=\"alert\">\n {saveError}\n </p>\n )}\n </div>\n );\n}\n\ninterface ProviderOptionProps {\n id: string;\n selected: boolean;\n disabled?: boolean;\n onSelect: () => void;\n title: string;\n subtitle?: React.ReactNode;\n rightSlot?: React.ReactNode;\n}\n\nfunction ProviderOption({\n id,\n selected,\n disabled,\n onSelect,\n title,\n subtitle,\n rightSlot,\n}: ProviderOptionProps) {\n const select = () => {\n if (!disabled) onSelect();\n };\n\n const onKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (disabled) return;\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n onSelect();\n }\n };\n\n return (\n <div\n role=\"button\"\n tabIndex={disabled ? -1 : 0}\n onClick={select}\n onKeyDown={onKeyDown}\n aria-pressed={selected}\n aria-disabled={disabled || undefined}\n className={`w-full text-left rounded-md border px-2.5 py-2 flex items-start gap-2 ${\n selected\n ? \"border-[#625DF5] bg-[#625DF5]/10\"\n : \"border-border bg-accent/30 hover:bg-accent/50\"\n } ${disabled ? \"opacity-60 cursor-not-allowed\" : \"\"}`}\n >\n <span\n className={`mt-[2px] shrink-0 flex h-3.5 w-3.5 items-center justify-center rounded-full border ${\n selected\n ? \"border-[#625DF5] bg-[#625DF5]\"\n : \"border-muted-foreground/40 bg-background\"\n }`}\n >\n {selected && (\n <span className=\"h-1.5 w-1.5 rounded-full bg-background\" />\n )}\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center justify-between gap-2\">\n <div className=\"text-[11px] font-medium text-foreground\">{title}</div>\n {rightSlot && <div className=\"shrink-0\">{rightSlot}</div>}\n </div>\n {subtitle && (\n <p className=\"text-[10px] text-muted-foreground mt-0.5\">{subtitle}</p>\n )}\n </div>\n </div>\n );\n}\n\nexport function VoiceTranscriptionIcon() {\n return <IconMicrophone size={14} />;\n}\n"]}
1
+ {"version":3,"file":"VoiceTranscriptionSection.js","sourceRoot":"","sources":["../../../src/client/settings/VoiceTranscriptionSection.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * <VoiceTranscriptionSection /> — source + cleanup settings for voice input.\n *\n * Writes the selection to application_state under `voice-transcription-prefs`\n * so the composer's `useVoiceDictation` hook picks it up on next record. The\n * legacy `provider` field is still written alongside `transcriptionMode` so\n * older clients continue to normalize safely.\n *\n * Provider status comes from `/_agent-native/voice-providers/status`, which\n * mirrors the server transcription route's key/env resolution.\n */\n\nimport React, { useCallback, useEffect, useState } from \"react\";\nimport { agentNativePath } from \"../api-path.js\";\nimport {\n IconAlertCircle,\n IconCheck,\n IconChevronDown,\n IconChevronRight,\n IconExternalLink,\n IconLoader2,\n IconLockOpen,\n IconMicrophone,\n} from \"@tabler/icons-react\";\nimport { useBuilderStatus } from \"./useBuilderStatus.js\";\n\ntype TranscriptionMode = \"mac-native\" | \"google-realtime\" | \"batch\";\n\ntype Provider =\n | \"auto\"\n | \"openai\"\n | \"builder-gemini\"\n | \"builder\"\n | \"browser\"\n | \"gemini\"\n | \"groq\";\n\ninterface Prefs {\n transcriptionMode?: TranscriptionMode;\n provider?: Provider;\n instructions?: string;\n}\n\ninterface SecretStatus {\n key: string;\n status: \"set\" | \"unset\" | \"invalid\";\n}\n\ninterface ProviderStatus {\n builder: boolean;\n gemini: boolean;\n openai: boolean;\n groq: boolean;\n googleRealtime?: boolean;\n browser: true;\n native?: true;\n}\n\nconst PREFS_URL = agentNativePath(\n \"/_agent-native/application-state/voice-transcription-prefs\",\n);\nconst CLEANUP_PREFS_URL = agentNativePath(\n \"/_agent-native/application-state/voice-cleanup-prefs\",\n);\nconst SECRETS_URL = agentNativePath(\"/_agent-native/secrets\");\nconst PROVIDER_STATUS_URL = agentNativePath(\n \"/_agent-native/voice-providers/status\",\n);\nconst DEFAULT_TRANSCRIPTION_MODE: TranscriptionMode = \"batch\";\nconst DEFAULT_BATCH_PROVIDER: Provider = \"auto\";\n\nfunction isProvider(value: unknown): value is Provider {\n return (\n value === \"auto\" ||\n value === \"openai\" ||\n value === \"builder-gemini\" ||\n value === \"builder\" ||\n value === \"browser\" ||\n value === \"gemini\" ||\n value === \"groq\"\n );\n}\n\nfunction isTranscriptionMode(value: unknown): value is TranscriptionMode {\n return (\n value === \"mac-native\" || value === \"google-realtime\" || value === \"batch\"\n );\n}\n\nfunction normalizeProvider(value: unknown): Provider | null {\n if (!isProvider(value)) return null;\n return value === \"builder\" ? \"builder-gemini\" : value;\n}\n\nfunction legacyModeFromProvider(provider: Provider | null): TranscriptionMode {\n if (provider === \"browser\") return \"mac-native\";\n return \"batch\";\n}\n\nfunction providerForMode(\n mode: TranscriptionMode,\n currentProvider: Provider | null,\n): Provider {\n if (mode === \"mac-native\") return \"browser\";\n if (mode === \"google-realtime\") return \"auto\";\n if (!currentProvider || currentProvider === \"browser\") {\n return DEFAULT_BATCH_PROVIDER;\n }\n return currentProvider;\n}\n\nfunction batchProvider(provider: Provider | null): Provider {\n if (!provider || provider === \"browser\") return DEFAULT_BATCH_PROVIDER;\n return provider;\n}\n\nexport function VoiceTranscriptionSection() {\n const [transcriptionMode, setTranscriptionMode] =\n useState<TranscriptionMode | null>(null);\n const [provider, setProvider] = useState<Provider>(DEFAULT_BATCH_PROVIDER);\n const [instructions, setInstructions] = useState(\"\");\n const [openAiConfigured, setOpenAiConfigured] = useState<boolean | null>(\n null,\n );\n const [geminiConfigured, setGeminiConfigured] = useState<boolean | null>(\n null,\n );\n const [groqConfigured, setGroqConfigured] = useState<boolean | null>(null);\n const [googleRealtimeConfigured, setGoogleRealtimeConfigured] = useState<\n boolean | null\n >(null);\n const [saving, setSaving] = useState(false);\n const [saveError, setSaveError] = useState<string | null>(null);\n const [showAdvanced, setShowAdvanced] = useState(false);\n const [cleanupEnabled, setCleanupEnabled] = useState<boolean | null>(null);\n const { status: builderStatus } = useBuilderStatus();\n const builderRealtimeReady =\n !!builderStatus?.privateKeyConfigured &&\n !!builderStatus?.publicKeyConfigured;\n const googleRealtimeReady =\n !!googleRealtimeConfigured && builderRealtimeReady;\n\n // Read cleanup pref (default: true if Builder is connected).\n useEffect(() => {\n let cancelled = false;\n fetch(CLEANUP_PREFS_URL)\n .then((r) => (r.ok ? r.json() : null))\n .then(\n (\n body:\n | { enabled?: boolean }\n | { value?: { enabled?: boolean } }\n | null,\n ) => {\n if (cancelled) return;\n const stored =\n (body as { enabled?: boolean } | null)?.enabled ??\n (body as { value?: { enabled?: boolean } } | null)?.value?.enabled;\n if (typeof stored === \"boolean\") setCleanupEnabled(stored);\n else setCleanupEnabled(null); // resolve once builderStatus arrives\n },\n )\n .catch(() => !cancelled && setCleanupEnabled(null));\n return () => {\n cancelled = true;\n };\n }, []);\n\n useEffect(() => {\n if (cleanupEnabled !== null) return;\n if (builderStatus?.configured !== undefined) {\n setCleanupEnabled(!!builderStatus.configured);\n }\n }, [builderStatus?.configured, cleanupEnabled]);\n\n const toggleCleanup = async (next: boolean) => {\n const previous = cleanupEnabled;\n setCleanupEnabled(next);\n try {\n const res = await fetch(CLEANUP_PREFS_URL, {\n method: \"PUT\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ enabled: next }),\n });\n if (!res.ok) {\n throw new Error(`HTTP ${res.status}`);\n }\n } catch {\n setCleanupEnabled(previous);\n }\n };\n\n useEffect(() => {\n let cancelled = false;\n fetch(PREFS_URL)\n .then((r) => (r.ok ? r.json() : null))\n .then((body: Prefs | { value?: Prefs } | null) => {\n if (cancelled) return;\n const value =\n (body as { value?: Prefs } | null)?.value ?? (body as Prefs | null);\n const p = normalizeProvider(\n (body as Prefs | null)?.provider ??\n (body as { value?: Prefs } | null)?.value?.provider,\n );\n const storedMode = isTranscriptionMode(value?.transcriptionMode)\n ? value.transcriptionMode\n : null;\n const mode =\n storedMode ??\n (p ? legacyModeFromProvider(p) : DEFAULT_TRANSCRIPTION_MODE);\n const savedInstructions =\n (body as Prefs | null)?.instructions ??\n (body as { value?: Prefs } | null)?.value?.instructions;\n setTranscriptionMode(mode);\n setProvider(providerForMode(mode, p));\n if (typeof savedInstructions === \"string\") {\n setInstructions(savedInstructions);\n }\n })\n .catch(() => {\n if (!cancelled) {\n setTranscriptionMode(DEFAULT_TRANSCRIPTION_MODE);\n setProvider(DEFAULT_BATCH_PROVIDER);\n }\n });\n return () => {\n cancelled = true;\n };\n }, []);\n\n useEffect(() => {\n let cancelled = false;\n fetch(PROVIDER_STATUS_URL)\n .then((r) => (r.ok ? r.json() : null))\n .then((status: ProviderStatus | null) => {\n if (cancelled) return;\n if (status) {\n setOpenAiConfigured(status.openai);\n setGeminiConfigured(status.gemini);\n setGroqConfigured(status.groq);\n setGoogleRealtimeConfigured(!!status.googleRealtime);\n return;\n }\n return fetch(SECRETS_URL)\n .then((r) => (r.ok ? r.json() : []))\n .then((list: SecretStatus[]) => {\n if (cancelled) return;\n const find = (key: string) =>\n Array.isArray(list) ? list.find((s) => s.key === key) : null;\n setOpenAiConfigured(find(\"OPENAI_API_KEY\")?.status === \"set\");\n setGeminiConfigured(find(\"GEMINI_API_KEY\")?.status === \"set\");\n setGroqConfigured(find(\"GROQ_API_KEY\")?.status === \"set\");\n setGoogleRealtimeConfigured(\n find(\"GOOGLE_APPLICATION_CREDENTIALS\")?.status === \"set\",\n );\n });\n })\n .catch(() => {\n if (!cancelled) {\n setOpenAiConfigured(false);\n setGeminiConfigured(false);\n setGroqConfigured(false);\n setGoogleRealtimeConfigured(false);\n }\n });\n return () => {\n cancelled = true;\n };\n }, []);\n\n const persist = useCallback(\n async (\n nextMode: TranscriptionMode,\n nextProvider: Provider,\n nextInstructions: string,\n previous: {\n transcriptionMode: TranscriptionMode | null;\n provider: Provider;\n instructions: string;\n },\n ) => {\n setSaving(true);\n setSaveError(null);\n try {\n const res = await fetch(PREFS_URL, {\n method: \"PUT\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n transcriptionMode: nextMode,\n provider: nextProvider,\n instructions: nextInstructions.trim(),\n }),\n });\n if (!res.ok) {\n throw new Error(`HTTP ${res.status}`);\n }\n } catch (err) {\n // Revert the optimistic update so the UI matches server state.\n setTranscriptionMode(previous.transcriptionMode);\n setProvider(previous.provider);\n setInstructions(previous.instructions);\n setSaveError(\n `Couldn't save: ${(err as Error)?.message ?? \"network error\"}. Try again.`,\n );\n } finally {\n setSaving(false);\n }\n },\n [],\n );\n\n const focusKey = (key: string) => {\n if (typeof window === \"undefined\") return;\n window.location.hash = `#secrets:${key}`;\n };\n\n const chooseSource = (next: TranscriptionMode) => {\n if (next === transcriptionMode) return;\n if (next === \"google-realtime\" && !googleRealtimeReady) {\n setShowAdvanced(true);\n if (!googleRealtimeConfigured) {\n focusKey(\"GOOGLE_APPLICATION_CREDENTIALS\");\n } else if (!builderRealtimeReady) {\n openBuilderConnect();\n }\n return;\n }\n const previous = { transcriptionMode, provider, instructions };\n const nextProvider = providerForMode(next, provider);\n setTranscriptionMode(next);\n setProvider(nextProvider);\n void persist(next, nextProvider, instructions, previous);\n };\n\n const openBuilderConnect = () => {\n if (typeof window === \"undefined\") return;\n const url = new URL(\n agentNativePath(\"/_agent-native/builder/connect\"),\n window.location.origin,\n ).href;\n window.open(url, \"_blank\", \"noopener,noreferrer,width=600,height=700\");\n };\n\n const chooseBatchProvider = (next: Provider) => {\n const nextProvider = batchProvider(normalizeProvider(next));\n if (transcriptionMode === \"batch\" && nextProvider === provider) return;\n const previous = { transcriptionMode, provider, instructions };\n setTranscriptionMode(\"batch\");\n setProvider(nextProvider);\n void persist(\"batch\", nextProvider, instructions, previous);\n };\n\n const updateInstructions = (next: string) => {\n const previous = { transcriptionMode, provider, instructions };\n setInstructions(next);\n if (transcriptionMode) {\n void persist(transcriptionMode, provider, next, previous);\n }\n };\n\n if (transcriptionMode === null) {\n return (\n <div className=\"flex items-center gap-1.5 text-[10px] text-muted-foreground\">\n <IconLoader2 size={10} className=\"animate-spin\" />\n Loading…\n </div>\n );\n }\n\n return (\n <div className=\"space-y-2\">\n <div className=\"rounded-md border border-border bg-background p-2\">\n <div className=\"mb-2 flex items-start justify-between gap-3 px-0.5\">\n <div>\n <div className=\"text-[11px] font-medium text-foreground\">\n Live transcription\n </div>\n <p className=\"mt-0.5 text-[10px] text-muted-foreground\">\n Choose where real-time words come from. Batch still runs after\n recording stops.\n </p>\n </div>\n </div>\n <div className=\"space-y-2\">\n <ProviderOption\n id=\"mac-native\"\n selected={transcriptionMode === \"mac-native\"}\n onSelect={() => chooseSource(\"mac-native\")}\n title=\"Mac Native\"\n subtitle=\"Free and fast in the macOS Tauri app. Web clients use the existing browser-native path when available.\"\n rightSlot={\n <span className=\"text-[10px] text-muted-foreground\">\n Tauri default\n </span>\n }\n />\n <ProviderOption\n id=\"google-realtime\"\n selected={transcriptionMode === \"google-realtime\"}\n onSelect={() => chooseSource(\"google-realtime\")}\n disabled={!googleRealtimeReady}\n title=\"Google Realtime\"\n subtitle={\n googleRealtimeReady\n ? \"BYOK only for v1. Streams live partials and finals through Google Speech-to-Text.\"\n : googleRealtimeConfigured\n ? \"Google credentials are set. Connect Builder completely to mint the managed realtime session.\"\n : \"BYOK only for v1. Configure Google service account before selecting this source.\"\n }\n rightSlot={\n googleRealtimeReady ? (\n <span className=\"flex items-center gap-1 text-[10px] text-green-500\">\n <IconCheck size={10} />\n Ready\n </span>\n ) : googleRealtimeConfigured ? (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n openBuilderConnect();\n }}\n className=\"inline-flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[10px] text-muted-foreground hover:bg-accent/40 hover:text-foreground\"\n >\n Connect Builder.io\n <IconExternalLink size={10} />\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n setShowAdvanced(true);\n focusKey(\"GOOGLE_APPLICATION_CREDENTIALS\");\n }}\n className=\"inline-flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[10px] text-muted-foreground hover:bg-accent/40 hover:text-foreground\"\n >\n Configure\n <IconExternalLink size={10} />\n </button>\n )\n }\n />\n <ProviderOption\n id=\"batch\"\n selected={transcriptionMode === \"batch\"}\n onSelect={() => chooseSource(\"batch\")}\n title=\"Batch\"\n subtitle=\"Universal fallback. Sends audio after recording stops through Builder Gemini, Gemini, Groq, then OpenAI.\"\n />\n <SystemAudioStatus />\n </div>\n </div>\n\n <div className=\"flex items-start justify-between gap-3 rounded-md border border-border bg-accent/30 px-2.5 py-2\">\n <div className=\"min-w-0\">\n <div className=\"text-[11px] font-medium text-foreground\">\n AI cleanup\n </div>\n <p className=\"text-[10px] text-muted-foreground mt-0.5\">\n Polish punctuation, casing, filler words, titles, and summaries\n after capture. Builder Gemini is tried first; BYOK Gemini is the\n fallback.\n </p>\n </div>\n <div className=\"flex shrink-0 flex-col items-end gap-1\">\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={!!cleanupEnabled}\n onClick={() => toggleCleanup(!cleanupEnabled)}\n // Theme tokens; streaming agent owns layout.\n className={`relative inline-flex h-4 w-7 shrink-0 cursor-pointer items-center rounded-full transition-colors ${\n cleanupEnabled\n ? \"bg-primary\"\n : \"bg-muted-foreground/30 hover:bg-muted-foreground/50\"\n }`}\n >\n <span\n className={`inline-block h-3 w-3 transform rounded-full bg-background transition-transform ${\n cleanupEnabled ? \"translate-x-3.5\" : \"translate-x-0.5\"\n }`}\n />\n </button>\n {cleanupEnabled && (\n <span className=\"text-[10px] text-muted-foreground\">\n {builderStatus?.configured\n ? \"Builder ready\"\n : geminiConfigured\n ? \"Gemini key set\"\n : \"Needs key\"}\n </span>\n )}\n </div>\n </div>\n\n <div className=\"rounded-md border border-border bg-background\">\n <button\n type=\"button\"\n onClick={() => setShowAdvanced((v) => !v)}\n className=\"w-full flex items-center justify-between gap-2 px-2.5 py-2 cursor-pointer\"\n >\n <span className=\"text-[11px] font-medium text-foreground inline-flex items-center gap-1\">\n {showAdvanced ? (\n <IconChevronDown size={12} />\n ) : (\n <IconChevronRight size={12} />\n )}\n Add API keys\n </span>\n <span className=\"text-[10px] text-muted-foreground\">\n Google STT · Gemini · Groq · OpenAI\n </span>\n </button>\n\n {showAdvanced && (\n <div className=\"px-2 pb-2 space-y-2\">\n <ProviderOption\n id=\"google-service-account\"\n selected={transcriptionMode === \"google-realtime\"}\n onSelect={() => chooseSource(\"google-realtime\")}\n disabled={!googleRealtimeReady}\n title=\"Google Speech-to-Text service account\"\n subtitle={\n googleRealtimeConfigured\n ? \"Service-account JSON is set. Connect Builder to mint the managed realtime WebSocket session.\"\n : \"Service-account JSON for the dedicated realtime WebSocket to Google StreamingRecognize.\"\n }\n rightSlot={\n googleRealtimeConfigured ===\n null ? null : googleRealtimeReady ? (\n <span className=\"flex items-center gap-1 text-[10px] text-green-500\">\n <IconCheck size={10} />\n Ready\n </span>\n ) : googleRealtimeConfigured ? (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n openBuilderConnect();\n }}\n className=\"inline-flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[10px] text-muted-foreground hover:text-foreground hover:bg-accent/40\"\n >\n Connect Builder.io\n <IconExternalLink size={10} />\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n focusKey(\"GOOGLE_APPLICATION_CREDENTIALS\");\n }}\n className=\"inline-flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[10px] text-muted-foreground hover:text-foreground hover:bg-accent/40\"\n >\n Configure\n <IconExternalLink size={10} />\n </button>\n )\n }\n />\n\n <ProviderOption\n id=\"auto\"\n selected={transcriptionMode === \"batch\" && provider === \"auto\"}\n onSelect={() => chooseBatchProvider(\"auto\")}\n title=\"Automatic batch fallback\"\n subtitle=\"Keep the current Clips fallback chain: Builder Gemini, Gemini, Groq, then OpenAI.\"\n />\n\n <ProviderOption\n id=\"builder-gemini\"\n selected={\n transcriptionMode === \"batch\" && provider === \"builder-gemini\"\n }\n onSelect={() => chooseBatchProvider(\"builder-gemini\")}\n disabled={!builderStatus?.configured}\n title=\"Builder.io Connect\"\n subtitle={\n builderStatus?.configured\n ? \"Use Builder-hosted Gemini Flash-Lite for batch transcription and cleanup.\"\n : \"One-click connect for Gemini Flash-Lite cleanup and batch transcription. No Google key needed.\"\n }\n rightSlot={\n builderStatus?.configured ? (\n <span className=\"flex items-center gap-1 text-[10px] text-green-500\">\n <IconCheck size={10} />\n Connected\n </span>\n ) : (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n openBuilderConnect();\n }}\n className=\"inline-flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[10px] text-muted-foreground hover:text-foreground hover:bg-accent/40\"\n >\n Connect Builder.io\n <IconExternalLink size={10} />\n </button>\n )\n }\n />\n\n <ProviderOption\n id=\"gemini\"\n selected={transcriptionMode === \"batch\" && provider === \"gemini\"}\n onSelect={() => chooseBatchProvider(\"gemini\")}\n title=\"Google Gemini\"\n subtitle=\"BYOK Gemini for AI cleanup and optional strict batch transcription.\"\n rightSlot={\n geminiConfigured === null ? null : geminiConfigured ? (\n <span className=\"flex items-center gap-1 text-[10px] text-green-500\">\n <IconCheck size={10} />\n Key set\n </span>\n ) : (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n focusKey(\"GEMINI_API_KEY\");\n }}\n className=\"inline-flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[10px] text-muted-foreground hover:text-foreground hover:bg-accent/40\"\n >\n Add key\n <IconExternalLink size={10} />\n </button>\n )\n }\n />\n\n <ProviderOption\n id=\"openai\"\n selected={transcriptionMode === \"batch\" && provider === \"openai\"}\n onSelect={() => chooseBatchProvider(\"openai\")}\n title=\"OpenAI Whisper\"\n subtitle=\"Batch Whisper provider. Requires an OpenAI API key.\"\n rightSlot={\n openAiConfigured === null ? null : openAiConfigured ? (\n <span className=\"flex items-center gap-1 text-[10px] text-green-500\">\n <IconCheck size={10} />\n Key set\n </span>\n ) : (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n focusKey(\"OPENAI_API_KEY\");\n }}\n className=\"inline-flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[10px] text-muted-foreground hover:text-foreground hover:bg-accent/40\"\n >\n Add key\n <IconExternalLink size={10} />\n </button>\n )\n }\n />\n\n <ProviderOption\n id=\"groq\"\n selected={transcriptionMode === \"batch\" && provider === \"groq\"}\n onSelect={() => chooseBatchProvider(\"groq\")}\n title=\"Groq Whisper\"\n subtitle=\"Fast Whisper batch provider. Requires a Groq API key.\"\n rightSlot={\n groqConfigured === null ? null : groqConfigured ? (\n <span className=\"flex items-center gap-1 text-[10px] text-green-500\">\n <IconCheck size={10} />\n Key set\n </span>\n ) : (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n focusKey(\"GROQ_API_KEY\");\n }}\n className=\"inline-flex items-center gap-1 rounded border border-border px-2 py-0.5 text-[10px] text-muted-foreground hover:text-foreground hover:bg-accent/40\"\n >\n Add key\n <IconExternalLink size={10} />\n </button>\n )\n }\n />\n </div>\n )}\n </div>\n\n {(cleanupEnabled || transcriptionMode === \"batch\") && (\n <div className=\"rounded-md border border-border bg-accent/20 px-2.5 py-2\">\n <label\n htmlFor=\"voice-transcription-instructions\"\n className=\"block text-[10px] font-medium text-foreground\"\n >\n Custom instructions\n </label>\n <textarea\n id=\"voice-transcription-instructions\"\n value={instructions}\n onChange={(event) => updateInstructions(event.target.value)}\n placeholder=\"Names, casing, punctuation, style, or terms to preserve.\"\n className=\"mt-1 min-h-16 w-full resize-y rounded border border-border bg-background px-2 py-1.5 text-[11px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-accent\"\n />\n <p className=\"mt-1 text-[10px] text-muted-foreground\">\n Included with batch transcription and AI cleanup.\n </p>\n </div>\n )}\n\n {saving && <p className=\"text-[10px] text-muted-foreground\">Saving…</p>}\n {saveError && !saving && (\n <p className=\"text-[10px] text-red-500\" role=\"alert\">\n {saveError}\n </p>\n )}\n </div>\n );\n}\n\ninterface ProviderOptionProps {\n id: string;\n selected: boolean;\n disabled?: boolean;\n onSelect: () => void;\n title: string;\n subtitle?: React.ReactNode;\n rightSlot?: React.ReactNode;\n}\n\nfunction ProviderOption({\n id,\n selected,\n disabled,\n onSelect,\n title,\n subtitle,\n rightSlot,\n}: ProviderOptionProps) {\n const select = () => {\n if (!disabled) onSelect();\n };\n\n const onKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (disabled) return;\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n onSelect();\n }\n };\n\n return (\n <div\n role=\"button\"\n tabIndex={disabled ? -1 : 0}\n onClick={select}\n onKeyDown={onKeyDown}\n aria-pressed={selected}\n aria-disabled={disabled || undefined}\n // Theme tokens; streaming agent owns layout.\n className={`w-full text-left rounded-md border px-2.5 py-2 flex items-start gap-2 ${\n selected\n ? \"border-primary bg-primary/10\"\n : \"border-border bg-accent/30 hover:bg-accent/50\"\n } ${disabled ? \"opacity-60 cursor-not-allowed\" : \"\"}`}\n >\n <span\n className={`mt-[2px] shrink-0 flex h-3.5 w-3.5 items-center justify-center rounded-full border ${\n selected\n ? \"border-primary bg-primary\"\n : \"border-muted-foreground/40 bg-background\"\n }`}\n >\n {selected && (\n <span className=\"h-1.5 w-1.5 rounded-full bg-primary-foreground\" />\n )}\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center justify-between gap-2\">\n <div className=\"text-[11px] font-medium text-foreground\">{title}</div>\n {rightSlot && <div className=\"shrink-0\">{rightSlot}</div>}\n </div>\n {subtitle && (\n <p className=\"text-[10px] text-muted-foreground mt-0.5\">{subtitle}</p>\n )}\n </div>\n </div>\n );\n}\n\n/**\n * Surfaces the desktop app's system-audio capture readiness inside the live\n * transcription box. Renders nothing in non-Tauri contexts (web users) so the\n * existing browser-only flow is unaffected. The Rust side\n * (`templates/clips/desktop/src-tauri/src/system_audio.rs`) returns either a\n * structured `VersionStatus` via `system_audio_version_status` (preferred,\n * no error-string parsing) or an unsupported-OS error from\n * `system_audio_request_permission`. We surface three states:\n *\n * - macOS 13+ + permission granted -> green check (\"System audio capture available\")\n * - macOS < 13 -> red dot (\"...requires macOS 13 or later\")\n * - permission denied / pending -> amber + \"Open System Settings\" affordance\n */\ntype SystemAudioState =\n | { kind: \"loading\" }\n | { kind: \"available\" }\n | { kind: \"unsupported\"; reason: string }\n | { kind: \"denied\" };\n\ninterface VersionStatusPayload {\n supported: boolean;\n os_version: string;\n reason?: string;\n}\n\nfunction SystemAudioStatus() {\n const [state, setState] = useState<SystemAudioState | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n if (typeof window === \"undefined\") return;\n const tauri = (window as unknown as { __TAURI_INTERNALS__?: unknown })\n .__TAURI_INTERNALS__;\n if (!tauri) return; // Web users: render nothing.\n setState({ kind: \"loading\" });\n // Dynamic import keeps `@tauri-apps/api` out of the web bundle's static\n // graph; it is only resolved inside the desktop shell where the dep is\n // present at build time.\n // Cast to suppress missing types — `@tauri-apps/api` is a desktop-shell\n // dep, not a static dep of `packages/core`. Resolves at runtime only when\n // we're inside Tauri (gated by the `__TAURI_INTERNALS__` check above).\n (\n import(/* @vite-ignore */ \"@tauri-apps/api/core\" as string) as Promise<{\n invoke: (cmd: string, args?: unknown) => Promise<unknown>;\n }>\n )\n .then(async ({ invoke }) => {\n try {\n const status = (await invoke(\"system_audio_version_status\")) as\n | VersionStatusPayload\n | undefined;\n if (cancelled) return;\n if (status && !status.supported) {\n setState({\n kind: \"unsupported\",\n reason:\n status.reason ??\n `ScreenCaptureKit is unavailable on ${status.os_version}.`,\n });\n return;\n }\n // Supported — now probe permission. This may prompt; calling it\n // here matches the original on-mount semantics requested in the\n // settings flow.\n try {\n const granted = (await invoke(\n \"system_audio_request_permission\",\n )) as boolean;\n if (cancelled) return;\n setState(granted ? { kind: \"available\" } : { kind: \"denied\" });\n } catch (err) {\n if (cancelled) return;\n const msg = String(err ?? \"\");\n if (/macOS\\s*1[0-2]|requires macOS 13/i.test(msg)) {\n setState({ kind: \"unsupported\", reason: msg });\n } else {\n setState({ kind: \"denied\" });\n }\n }\n } catch {\n // Older desktop builds may not have the new command yet —\n // fall back to the permission probe.\n if (cancelled) return;\n try {\n const granted = (await invoke(\n \"system_audio_request_permission\",\n )) as boolean;\n if (cancelled) return;\n setState(granted ? { kind: \"available\" } : { kind: \"denied\" });\n } catch (err) {\n if (cancelled) return;\n const msg = String(err ?? \"\");\n if (/macOS|ScreenCaptureKit/i.test(msg)) {\n setState({ kind: \"unsupported\", reason: msg });\n } else {\n setState({ kind: \"denied\" });\n }\n }\n }\n })\n .catch(() => {\n // @tauri-apps/api not resolvable -> behave like web.\n if (!cancelled) setState(null);\n });\n return () => {\n cancelled = true;\n };\n }, []);\n\n const openPrivacy = useCallback(() => {\n if (typeof window === \"undefined\") return;\n const tauri = (window as unknown as { __TAURI_INTERNALS__?: unknown })\n .__TAURI_INTERNALS__;\n if (!tauri) return;\n // Cast to suppress missing types — `@tauri-apps/api` is a desktop-shell\n // dep, not a static dep of `packages/core`. Resolves at runtime only when\n // we're inside Tauri (gated by the `__TAURI_INTERNALS__` check above).\n (\n import(/* @vite-ignore */ \"@tauri-apps/api/core\" as string) as Promise<{\n invoke: (cmd: string, args?: unknown) => Promise<unknown>;\n }>\n )\n .then(({ invoke }) => invoke(\"system_audio_open_privacy_settings\"))\n .catch(() => {\n // Older desktop builds without this command — no-op.\n });\n }, []);\n\n if (!state || state.kind === \"loading\") return null;\n\n if (state.kind === \"available\") {\n return (\n <div className=\"flex items-center gap-1.5 px-0.5 pt-1 text-[10px] text-muted-foreground\">\n <IconCheck size={11} className=\"text-green-500\" />\n <span>System audio capture available.</span>\n </div>\n );\n }\n\n if (state.kind === \"unsupported\") {\n return (\n <div className=\"flex items-center gap-1.5 px-0.5 pt-1 text-[10px] text-muted-foreground\">\n <span\n className=\"inline-block h-1.5 w-1.5 shrink-0 rounded-full bg-red-500\"\n aria-hidden\n />\n <span>\n System audio requires macOS 13 or later — meetings will use mic-only.\n </span>\n </div>\n );\n }\n\n // denied\n return (\n <div className=\"flex items-start gap-1.5 px-0.5 pt-1 text-[10px] text-muted-foreground\">\n <IconAlertCircle size={11} className=\"mt-[1px] shrink-0 text-amber-500\" />\n <div className=\"flex-1\">\n <span>\n Grant Screen Recording permission in System Settings -&gt; Privacy.\n </span>\n <button\n type=\"button\"\n onClick={openPrivacy}\n className=\"ml-1 inline-flex items-center gap-1 rounded border border-border px-1.5 py-0.5 text-[10px] text-muted-foreground hover:bg-accent/40 hover:text-foreground\"\n >\n <IconLockOpen size={10} />\n Open System Settings\n </button>\n </div>\n </div>\n );\n}\n\nexport function VoiceTranscriptionIcon() {\n return <IconMicrophone size={14} />;\n}\n"]}
@@ -0,0 +1,33 @@
1
+ import { type ReasoningEffort } from "../shared/reasoning-effort.js";
2
+ export interface EngineModelGroup {
3
+ engine: string;
4
+ label: string;
5
+ models: string[];
6
+ configured: boolean;
7
+ }
8
+ export interface UseChatModelsResult {
9
+ availableModels: EngineModelGroup[];
10
+ defaultModel: string;
11
+ selectedModel: string;
12
+ selectedEngine: string;
13
+ selectedEffort: ReasoningEffort;
14
+ onModelChange: (model: string, engine: string) => void;
15
+ onEffortChange: (effort: ReasoningEffort) => void;
16
+ refreshEngines: () => void;
17
+ }
18
+ interface Options {
19
+ /**
20
+ * localStorage key used to persist the user's model + effort selection across
21
+ * page loads. Pass `null` to disable persistence.
22
+ */
23
+ storageKey?: string | null;
24
+ }
25
+ /**
26
+ * Fetches available engines/models from the agent server and exposes the same
27
+ * model picker state that `MultiTabAssistantChat` wires up — for surfaces like
28
+ * the Dispatch homepage hero composer that need an identical model picker
29
+ * without mounting the full tabbed chat.
30
+ */
31
+ export declare function useChatModels({ storageKey, }?: Options): UseChatModelsResult;
32
+ export {};
33
+ //# sourceMappingURL=use-chat-models.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-chat-models.d.ts","sourceRoot":"","sources":["../../src/client/use-chat-models.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,+BAA+B,CAAC;AAEvC,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,eAAe,EAAE,gBAAgB,EAAE,CAAC;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,eAAe,CAAC;IAChC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACvD,cAAc,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IAClD,cAAc,EAAE,MAAM,IAAI,CAAC;CAC5B;AAED,UAAU,OAAO;IACf;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AA2BD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,EAC5B,UAAgC,GACjC,GAAE,OAAY,GAAG,mBAAmB,CAgMpC"}