@agent-native/core 0.7.54 → 0.7.56

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 (60) hide show
  1. package/dist/a2a/artifact-response.d.ts +1 -0
  2. package/dist/a2a/artifact-response.d.ts.map +1 -1
  3. package/dist/a2a/artifact-response.js +111 -13
  4. package/dist/a2a/artifact-response.js.map +1 -1
  5. package/dist/a2a/task-store.d.ts +1 -0
  6. package/dist/a2a/task-store.d.ts.map +1 -1
  7. package/dist/a2a/task-store.js +15 -0
  8. package/dist/a2a/task-store.js.map +1 -1
  9. package/dist/cli/templates-meta.js +1 -1
  10. package/dist/cli/templates-meta.js.map +1 -1
  11. package/dist/client/AssistantChat.d.ts +15 -0
  12. package/dist/client/AssistantChat.d.ts.map +1 -1
  13. package/dist/client/AssistantChat.js +55 -52
  14. package/dist/client/AssistantChat.js.map +1 -1
  15. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  16. package/dist/client/MultiTabAssistantChat.js +0 -13
  17. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  18. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  19. package/dist/client/composer/TiptapComposer.js +59 -19
  20. package/dist/client/composer/TiptapComposer.js.map +1 -1
  21. package/dist/client/composer/useVoiceDictation.d.ts +8 -3
  22. package/dist/client/composer/useVoiceDictation.d.ts.map +1 -1
  23. package/dist/client/composer/useVoiceDictation.js +278 -22
  24. package/dist/client/composer/useVoiceDictation.js.map +1 -1
  25. package/dist/client/index.d.ts +1 -0
  26. package/dist/client/index.d.ts.map +1 -1
  27. package/dist/client/index.js +1 -0
  28. package/dist/client/index.js.map +1 -1
  29. package/dist/client/resources/ResourcesPanel.js +2 -2
  30. package/dist/client/resources/ResourcesPanel.js.map +1 -1
  31. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  32. package/dist/client/settings/SettingsPanel.js +7 -5
  33. package/dist/client/settings/SettingsPanel.js.map +1 -1
  34. package/dist/client/settings/VoiceTranscriptionSection.d.ts +4 -2
  35. package/dist/client/settings/VoiceTranscriptionSection.d.ts.map +1 -1
  36. package/dist/client/settings/VoiceTranscriptionSection.js +164 -60
  37. package/dist/client/settings/VoiceTranscriptionSection.js.map +1 -1
  38. package/dist/client/use-chat-models.d.ts +33 -0
  39. package/dist/client/use-chat-models.d.ts.map +1 -0
  40. package/dist/client/use-chat-models.js +183 -0
  41. package/dist/client/use-chat-models.js.map +1 -0
  42. package/dist/integrations/a2a-continuation-processor.js +29 -15
  43. package/dist/integrations/a2a-continuation-processor.js.map +1 -1
  44. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  45. package/dist/server/agent-chat-plugin.js +22 -1
  46. package/dist/server/agent-chat-plugin.js.map +1 -1
  47. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  48. package/dist/server/core-routes-plugin.js +6 -0
  49. package/dist/server/core-routes-plugin.js.map +1 -1
  50. package/dist/server/google-realtime-session.d.ts +14 -0
  51. package/dist/server/google-realtime-session.d.ts.map +1 -0
  52. package/dist/server/google-realtime-session.js +155 -0
  53. package/dist/server/google-realtime-session.js.map +1 -0
  54. package/dist/server/voice-providers-status.d.ts +7 -0
  55. package/dist/server/voice-providers-status.d.ts.map +1 -1
  56. package/dist/server/voice-providers-status.js +14 -1
  57. package/dist/server/voice-providers-status.js.map +1 -1
  58. package/docs/content/sharing.md +155 -0
  59. package/docs/content/template-clips.md +8 -5
  60. package/package.json +1 -1
@@ -1,9 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  /**
3
- * <VoiceTranscriptionSection /> — provider picker for composer voice input.
3
+ * <VoiceTranscriptionSection /> — source + cleanup settings for voice input.
4
4
  *
5
5
  * Writes the selection to application_state under `voice-transcription-prefs`
6
- * so the composer's `useVoiceDictation` hook picks it up on next record.
6
+ * so the composer's `useVoiceDictation` hook picks it up on next record. The
7
+ * legacy `provider` field is still written alongside `transcriptionMode` so
8
+ * older clients continue to normalize safely.
7
9
  *
8
10
  * Provider status comes from `/_agent-native/voice-providers/status`, which
9
11
  * mirrors the server transcription route's key/env resolution.
@@ -16,27 +18,61 @@ const PREFS_URL = agentNativePath("/_agent-native/application-state/voice-transc
16
18
  const CLEANUP_PREFS_URL = agentNativePath("/_agent-native/application-state/voice-cleanup-prefs");
17
19
  const SECRETS_URL = agentNativePath("/_agent-native/secrets");
18
20
  const PROVIDER_STATUS_URL = agentNativePath("/_agent-native/voice-providers/status");
19
- const DEFAULT_PROVIDER = "browser";
21
+ const DEFAULT_TRANSCRIPTION_MODE = "batch";
22
+ const DEFAULT_BATCH_PROVIDER = "auto";
20
23
  function isProvider(value) {
21
- return (value === "openai" ||
24
+ return (value === "auto" ||
25
+ value === "openai" ||
22
26
  value === "builder-gemini" ||
23
27
  value === "builder" ||
24
28
  value === "browser" ||
25
29
  value === "gemini" ||
26
30
  value === "groq");
27
31
  }
32
+ function isTranscriptionMode(value) {
33
+ return (value === "mac-native" || value === "google-realtime" || value === "batch");
34
+ }
35
+ function normalizeProvider(value) {
36
+ if (!isProvider(value))
37
+ return null;
38
+ return value === "builder" ? "builder-gemini" : value;
39
+ }
40
+ function legacyModeFromProvider(provider) {
41
+ if (provider === "browser")
42
+ return "mac-native";
43
+ return "batch";
44
+ }
45
+ function providerForMode(mode, currentProvider) {
46
+ if (mode === "mac-native")
47
+ return "browser";
48
+ if (mode === "google-realtime")
49
+ return "auto";
50
+ if (!currentProvider || currentProvider === "browser") {
51
+ return DEFAULT_BATCH_PROVIDER;
52
+ }
53
+ return currentProvider;
54
+ }
55
+ function batchProvider(provider) {
56
+ if (!provider || provider === "browser")
57
+ return DEFAULT_BATCH_PROVIDER;
58
+ return provider;
59
+ }
28
60
  export function VoiceTranscriptionSection() {
29
- const [provider, setProvider] = useState(null);
61
+ const [transcriptionMode, setTranscriptionMode] = useState(null);
62
+ const [provider, setProvider] = useState(DEFAULT_BATCH_PROVIDER);
30
63
  const [instructions, setInstructions] = useState("");
31
- const [hasStoredProvider, setHasStoredProvider] = useState(false);
32
64
  const [openAiConfigured, setOpenAiConfigured] = useState(null);
33
65
  const [geminiConfigured, setGeminiConfigured] = useState(null);
34
66
  const [groqConfigured, setGroqConfigured] = useState(null);
67
+ const [googleRealtimeConfigured, setGoogleRealtimeConfigured] = useState(null);
35
68
  const [saving, setSaving] = useState(false);
36
69
  const [saveError, setSaveError] = useState(null);
37
70
  const [showAdvanced, setShowAdvanced] = useState(false);
38
71
  const [cleanupEnabled, setCleanupEnabled] = useState(null);
39
72
  const { status: builderStatus } = useBuilderStatus();
73
+ const builderRealtimeReady = !!builderStatus?.privateKeyConfigured &&
74
+ !!builderStatus?.publicKeyConfigured;
75
+ const googleRealtimeReady = !!googleRealtimeConfigured && builderRealtimeReady;
40
76
  // Read cleanup pref (default: true if Builder is connected).
41
77
  useEffect(() => {
42
78
  let cancelled = false;
@@ -64,14 +100,22 @@ export function VoiceTranscriptionSection() {
64
100
  setCleanupEnabled(!!builderStatus.configured);
65
101
  }
66
102
  }, [builderStatus?.configured, cleanupEnabled]);
67
- const toggleCleanup = (next) => {
103
+ const toggleCleanup = async (next) => {
68
104
  const previous = cleanupEnabled;
69
105
  setCleanupEnabled(next);
70
- fetch(CLEANUP_PREFS_URL, {
71
- method: "PUT",
72
- headers: { "Content-Type": "application/json" },
73
- body: JSON.stringify({ enabled: next }),
74
- }).catch(() => setCleanupEnabled(previous));
106
+ try {
107
+ const res = await fetch(CLEANUP_PREFS_URL, {
108
+ method: "PUT",
109
+ headers: { "Content-Type": "application/json" },
110
+ body: JSON.stringify({ enabled: next }),
111
+ });
112
+ if (!res.ok) {
113
+ throw new Error(`HTTP ${res.status}`);
114
+ }
115
+ }
116
+ catch {
117
+ setCleanupEnabled(previous);
118
+ }
75
119
  };
76
120
  useEffect(() => {
77
121
  let cancelled = false;
@@ -80,31 +124,32 @@ export function VoiceTranscriptionSection() {
80
124
  .then((body) => {
81
125
  if (cancelled)
82
126
  return;
83
- const p = body?.provider ??
84
- body?.value?.provider;
127
+ const value = body?.value ?? body;
128
+ const p = normalizeProvider(body?.provider ??
129
+ body?.value?.provider);
130
+ const storedMode = isTranscriptionMode(value?.transcriptionMode)
131
+ ? value.transcriptionMode
132
+ : null;
133
+ const mode = storedMode ??
134
+ (p ? legacyModeFromProvider(p) : DEFAULT_TRANSCRIPTION_MODE);
85
135
  const savedInstructions = body?.instructions ??
86
136
  body?.value?.instructions;
87
- setHasStoredProvider(isProvider(p));
88
- setProvider(isProvider(p)
89
- ? p === "builder"
90
- ? "builder-gemini"
91
- : p
92
- : DEFAULT_PROVIDER);
137
+ setTranscriptionMode(mode);
138
+ setProvider(providerForMode(mode, p));
93
139
  if (typeof savedInstructions === "string") {
94
140
  setInstructions(savedInstructions);
95
141
  }
96
142
  })
97
- .catch(() => !cancelled && setProvider(DEFAULT_PROVIDER));
143
+ .catch(() => {
144
+ if (!cancelled) {
145
+ setTranscriptionMode(DEFAULT_TRANSCRIPTION_MODE);
146
+ setProvider(DEFAULT_BATCH_PROVIDER);
147
+ }
148
+ });
98
149
  return () => {
99
150
  cancelled = true;
100
151
  };
101
152
  }, []);
102
- useEffect(() => {
103
- if (hasStoredProvider || provider === null)
104
- return;
105
- if (builderStatus?.configured)
106
- setProvider("builder-gemini");
107
- }, [builderStatus?.configured, hasStoredProvider, provider]);
108
153
  useEffect(() => {
109
154
  let cancelled = false;
110
155
  fetch(PROVIDER_STATUS_URL)
@@ -116,6 +161,7 @@ export function VoiceTranscriptionSection() {
116
161
  setOpenAiConfigured(status.openai);
117
162
  setGeminiConfigured(status.gemini);
118
163
  setGroqConfigured(status.groq);
164
+ setGoogleRealtimeConfigured(!!status.googleRealtime);
119
165
  return;
120
166
  }
121
167
  return fetch(SECRETS_URL)
@@ -127,6 +173,7 @@ export function VoiceTranscriptionSection() {
127
173
  setOpenAiConfigured(find("OPENAI_API_KEY")?.status === "set");
128
174
  setGeminiConfigured(find("GEMINI_API_KEY")?.status === "set");
129
175
  setGroqConfigured(find("GROQ_API_KEY")?.status === "set");
176
+ setGoogleRealtimeConfigured(find("GOOGLE_APPLICATION_CREDENTIALS")?.status === "set");
130
177
  });
131
178
  })
132
179
  .catch(() => {
@@ -134,13 +181,14 @@ export function VoiceTranscriptionSection() {
134
181
  setOpenAiConfigured(false);
135
182
  setGeminiConfigured(false);
136
183
  setGroqConfigured(false);
184
+ setGoogleRealtimeConfigured(false);
137
185
  }
138
186
  });
139
187
  return () => {
140
188
  cancelled = true;
141
189
  };
142
190
  }, []);
143
- const persist = useCallback(async (nextProvider, nextInstructions, previous) => {
191
+ const persist = useCallback(async (nextMode, nextProvider, nextInstructions, previous) => {
144
192
  setSaving(true);
145
193
  setSaveError(null);
146
194
  try {
@@ -148,6 +196,7 @@ export function VoiceTranscriptionSection() {
148
196
  method: "PUT",
149
197
  headers: { "Content-Type": "application/json" },
150
198
  body: JSON.stringify({
199
+ transcriptionMode: nextMode,
151
200
  provider: nextProvider,
152
201
  instructions: nextInstructions.trim(),
153
202
  }),
@@ -158,6 +207,7 @@ export function VoiceTranscriptionSection() {
158
207
  }
159
208
  catch (err) {
160
209
  // Revert the optimistic update so the UI matches server state.
210
+ setTranscriptionMode(previous.transcriptionMode);
161
211
  setProvider(previous.provider);
162
212
  setInstructions(previous.instructions);
163
213
  setSaveError(`Couldn't save: ${err?.message ?? "network error"}. Try again.`);
@@ -166,46 +216,98 @@ export function VoiceTranscriptionSection() {
166
216
  setSaving(false);
167
217
  }
168
218
  }, []);
169
- const choose = (next) => {
170
- if (next === provider)
219
+ const focusKey = (key) => {
220
+ if (typeof window === "undefined")
221
+ return;
222
+ window.location.hash = `#secrets:${key}`;
223
+ };
224
+ const chooseSource = (next) => {
225
+ if (next === transcriptionMode)
226
+ return;
227
+ if (next === "google-realtime" && !googleRealtimeReady) {
228
+ setShowAdvanced(true);
229
+ if (!googleRealtimeConfigured) {
230
+ focusKey("GOOGLE_APPLICATION_CREDENTIALS");
231
+ }
232
+ else if (!builderRealtimeReady) {
233
+ openBuilderConnect();
234
+ }
235
+ return;
236
+ }
237
+ const previous = { transcriptionMode, provider, instructions };
238
+ const nextProvider = providerForMode(next, provider);
239
+ setTranscriptionMode(next);
240
+ setProvider(nextProvider);
241
+ void persist(next, nextProvider, instructions, previous);
242
+ };
243
+ const openBuilderConnect = () => {
244
+ if (typeof window === "undefined")
171
245
  return;
172
- const previous = { provider, instructions };
173
- setHasStoredProvider(true);
174
- setProvider(next);
175
- void persist(next, instructions, previous);
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
+ };
249
+ const chooseBatchProvider = (next) => {
250
+ const nextProvider = batchProvider(normalizeProvider(next));
251
+ if (transcriptionMode === "batch" && nextProvider === provider)
252
+ return;
253
+ const previous = { transcriptionMode, provider, instructions };
254
+ setTranscriptionMode("batch");
255
+ setProvider(nextProvider);
256
+ void persist("batch", nextProvider, instructions, previous);
176
257
  };
177
258
  const updateInstructions = (next) => {
178
- const previous = { provider, instructions };
259
+ const previous = { transcriptionMode, provider, instructions };
179
260
  setInstructions(next);
180
- if (provider)
181
- void persist(provider, next, previous);
261
+ if (transcriptionMode) {
262
+ void persist(transcriptionMode, provider, next, previous);
263
+ }
182
264
  };
183
- if (provider === null) {
265
+ if (transcriptionMode === null) {
184
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"] }));
185
267
  }
186
- const focusKey = (key) => {
187
- if (typeof window === "undefined")
188
- return;
189
- window.location.hash = `#secrets:${key}`;
190
- };
191
- return (_jsxs("div", { className: "space-y-2", children: [_jsx(ProviderOption, { id: "builder-gemini", selected: provider === "builder-gemini", onSelect: () => choose("builder-gemini"), disabled: !builderStatus?.configured, title: "Builder.io Connect (recommended)", subtitle: builderStatus?.configured
192
- ? "Fast Gemini Flash-Lite transcription and cleanup through Builder.io. No Google key needed."
193
- : "One-click connect — uses Gemini Flash-Lite without a Google key.", 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) => {
194
- e.stopPropagation();
195
- const url = new URL(agentNativePath("/_agent-native/builder/connect"), window.location.origin).href;
196
- window.open(url, "_blank", "noopener,noreferrer,width=600,height=700");
197
- }, 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("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: "Cleanup transcripts with AI" }), _jsx("p", { className: "text-[10px] text-muted-foreground mt-0.5", children: "Polish punctuation, casing, and remove filler words after each recording." })] }), _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
198
- ? "bg-[#625DF5]"
199
- : "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"}` }) })] }), _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: "OpenAI \u00B7 Gemini \u00B7 Groq \u00B7 browser" })] }), showAdvanced && (_jsxs("div", { className: "px-2 pb-2 space-y-2", children: [_jsx(ProviderOption, { id: "openai", selected: provider === "openai", onSelect: () => choose("openai"), title: "OpenAI Whisper", subtitle: "Best quality. Requires an OpenAI API key.", rightSlot: openAiConfigured === null ? null : openAiConfigured ? (_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) => {
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) => {
200
273
  e.stopPropagation();
201
- focusKey("OPENAI_API_KEY");
202
- }, 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: ["Add key", _jsx(IconExternalLink, { size: 10 })] })) }), _jsx(ProviderOption, { id: "gemini", selected: provider === "gemini", onSelect: () => choose("gemini"), title: "Google Gemini", subtitle: "Fast transcription via Gemini Flash Lite. Requires a Gemini API key.", 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) => {
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) => {
276
+ e.stopPropagation();
277
+ setShowAdvanced(true);
278
+ focusKey("GOOGLE_APPLICATION_CREDENTIALS");
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." })] })] }), _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"
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
284
+ ? "Builder ready"
285
+ : geminiConfigured
286
+ ? "Gemini key set"
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) => {
294
+ e.stopPropagation();
295
+ focusKey("GOOGLE_APPLICATION_CREDENTIALS");
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
297
+ ? "Use Builder-hosted Gemini Flash-Lite for batch transcription and cleanup."
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) => {
299
+ e.stopPropagation();
300
+ openBuilderConnect();
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) => {
203
302
  e.stopPropagation();
204
303
  focusKey("GEMINI_API_KEY");
205
- }, 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: ["Add key", _jsx(IconExternalLink, { size: 10 })] })) }), _jsx(ProviderOption, { id: "groq", selected: provider === "groq", onSelect: () => choose("groq"), title: "Groq Whisper", subtitle: "Fastest Whisper inference. Requires a Groq API key.", rightSlot: groqConfigured === null ? null : groqConfigured ? (_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) => {
304
+ }, 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: ["Add key", _jsx(IconExternalLink, { size: 10 })] })) }), _jsx(ProviderOption, { id: "openai", selected: transcriptionMode === "batch" && provider === "openai", onSelect: () => chooseBatchProvider("openai"), title: "OpenAI Whisper", subtitle: "Batch Whisper provider. Requires an OpenAI API key.", rightSlot: openAiConfigured === null ? null : openAiConfigured ? (_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) => {
305
+ e.stopPropagation();
306
+ focusKey("OPENAI_API_KEY");
307
+ }, 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: ["Add key", _jsx(IconExternalLink, { size: 10 })] })) }), _jsx(ProviderOption, { id: "groq", selected: transcriptionMode === "batch" && provider === "groq", onSelect: () => chooseBatchProvider("groq"), title: "Groq Whisper", subtitle: "Fast Whisper batch provider. Requires a Groq API key.", rightSlot: groqConfigured === null ? null : groqConfigured ? (_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) => {
206
308
  e.stopPropagation();
207
309
  focusKey("GROQ_API_KEY");
208
- }, 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: ["Add key", _jsx(IconExternalLink, { size: 10 })] })) }), _jsx(ProviderOption, { id: "browser", selected: provider === "browser", onSelect: () => choose("browser"), title: "Native browser", subtitle: "Free built-in speech recognition. No key required." })] }))] }), provider !== "browser" && (_jsxs("div", { className: "rounded-md border border-border bg-accent/20 px-2.5 py-2", children: [_jsx("label", { htmlFor: "voice-transcription-instructions", className: "block text-[10px] font-medium text-foreground", children: "Custom instructions" }), _jsx("textarea", { id: "voice-transcription-instructions", value: instructions, onChange: (event) => updateInstructions(event.target.value), placeholder: "Names, casing, punctuation, style, or terms to preserve.", 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" }), _jsx("p", { className: "mt-1 text-[10px] text-muted-foreground", children: "Included with LLM-based transcription and cleanup." })] })), saving && _jsx("p", { className: "text-[10px] text-muted-foreground", children: "Saving\u2026" }), saveError && !saving && (_jsx("p", { className: "text-[10px] text-red-500", role: "alert", children: saveError }))] }));
310
+ }, 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: ["Add key", _jsx(IconExternalLink, { size: 10 })] })) })] }))] }), (cleanupEnabled || transcriptionMode === "batch") && (_jsxs("div", { className: "rounded-md border border-border bg-accent/20 px-2.5 py-2", children: [_jsx("label", { htmlFor: "voice-transcription-instructions", className: "block text-[10px] font-medium text-foreground", children: "Custom instructions" }), _jsx("textarea", { id: "voice-transcription-instructions", value: instructions, onChange: (event) => updateInstructions(event.target.value), placeholder: "Names, casing, punctuation, style, or terms to preserve.", 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" }), _jsx("p", { className: "mt-1 text-[10px] text-muted-foreground", children: "Included with batch transcription and AI cleanup." })] })), saving && _jsx("p", { className: "text-[10px] text-muted-foreground", children: "Saving\u2026" }), saveError && !saving && (_jsx("p", { className: "text-[10px] text-red-500", role: "alert", children: saveError }))] }));
209
311
  }
210
312
  function ProviderOption({ id, selected, disabled, onSelect, title, subtitle, rightSlot, }) {
211
313
  const select = () => {
@@ -220,11 +322,13 @@ function ProviderOption({ id, selected, disabled, onSelect, title, subtitle, rig
220
322
  onSelect();
221
323
  }
222
324
  };
223
- 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
224
- ? "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"
225
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
226
- ? "border-[#625DF5] bg-[#625DF5]"
227
- : "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 }))] })] }));
228
332
  }
229
333
  export function VoiceTranscriptionIcon() {
230
334
  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 /> — provider picker for composer 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.\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 Provider =\n | \"openai\"\n | \"builder-gemini\"\n | \"builder\"\n | \"browser\"\n | \"gemini\"\n | \"groq\";\n\ninterface Prefs {\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 browser: 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_PROVIDER: Provider = \"browser\";\n\nfunction isProvider(value: unknown): value is Provider {\n return (\n value === \"openai\" ||\n value === \"builder-gemini\" ||\n value === \"builder\" ||\n value === \"browser\" ||\n value === \"gemini\" ||\n value === \"groq\"\n );\n}\n\nexport function VoiceTranscriptionSection() {\n const [provider, setProvider] = useState<Provider | null>(null);\n const [instructions, setInstructions] = useState(\"\");\n const [hasStoredProvider, setHasStoredProvider] = useState(false);\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 [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 = (next: boolean) => {\n const previous = cleanupEnabled;\n setCleanupEnabled(next);\n fetch(CLEANUP_PREFS_URL, {\n method: \"PUT\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ enabled: next }),\n }).catch(() => setCleanupEnabled(previous));\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 p =\n (body as Prefs | null)?.provider ??\n (body as { value?: Prefs } | null)?.value?.provider;\n const savedInstructions =\n (body as Prefs | null)?.instructions ??\n (body as { value?: Prefs } | null)?.value?.instructions;\n setHasStoredProvider(isProvider(p));\n setProvider(\n isProvider(p)\n ? p === \"builder\"\n ? \"builder-gemini\"\n : p\n : DEFAULT_PROVIDER,\n );\n if (typeof savedInstructions === \"string\") {\n setInstructions(savedInstructions);\n }\n })\n .catch(() => !cancelled && setProvider(DEFAULT_PROVIDER));\n return () => {\n cancelled = true;\n };\n }, []);\n\n useEffect(() => {\n if (hasStoredProvider || provider === null) return;\n if (builderStatus?.configured) setProvider(\"builder-gemini\");\n }, [builderStatus?.configured, hasStoredProvider, provider]);\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 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 });\n })\n .catch(() => {\n if (!cancelled) {\n setOpenAiConfigured(false);\n setGeminiConfigured(false);\n setGroqConfigured(false);\n }\n });\n return () => {\n cancelled = true;\n };\n }, []);\n\n const persist = useCallback(\n async (\n nextProvider: Provider,\n nextInstructions: string,\n previous: { provider: Provider | null; instructions: string },\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 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 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 choose = (next: Provider) => {\n if (next === provider) return;\n const previous = { provider, instructions };\n setHasStoredProvider(true);\n setProvider(next);\n void persist(next, instructions, previous);\n };\n\n const updateInstructions = (next: string) => {\n const previous = { provider, instructions };\n setInstructions(next);\n if (provider) void persist(provider, next, previous);\n };\n\n if (provider === 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 const focusKey = (key: string) => {\n if (typeof window === \"undefined\") return;\n window.location.hash = `#secrets:${key}`;\n };\n\n return (\n <div className=\"space-y-2\">\n <ProviderOption\n id=\"builder-gemini\"\n selected={provider === \"builder-gemini\"}\n onSelect={() => choose(\"builder-gemini\")}\n disabled={!builderStatus?.configured}\n title=\"Builder.io Connect (recommended)\"\n subtitle={\n builderStatus?.configured\n ? \"Fast Gemini Flash-Lite transcription and cleanup through Builder.io. No Google key needed.\"\n : \"One-click connect — uses Gemini Flash-Lite without a Google key.\"\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 {/* Cleanup-with-AI toggle — defaults to on if Builder is connected. */}\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 Cleanup transcripts with AI\n </div>\n <p className=\"text-[10px] text-muted-foreground mt-0.5\">\n Polish punctuation, casing, and remove filler words after each\n recording.\n </p>\n </div>\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 </div>\n\n {/* Single \"Add API keys\" disclosure — collapses BYOK rows + browser. */}\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 OpenAI · Gemini · Groq · browser\n </span>\n </button>\n\n {showAdvanced && (\n <div className=\"px-2 pb-2 space-y-2\">\n <ProviderOption\n id=\"openai\"\n selected={provider === \"openai\"}\n onSelect={() => choose(\"openai\")}\n title=\"OpenAI Whisper\"\n subtitle=\"Best quality. 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=\"gemini\"\n selected={provider === \"gemini\"}\n onSelect={() => choose(\"gemini\")}\n title=\"Google Gemini\"\n subtitle=\"Fast transcription via Gemini Flash Lite. Requires a Gemini API key.\"\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=\"groq\"\n selected={provider === \"groq\"}\n onSelect={() => choose(\"groq\")}\n title=\"Groq Whisper\"\n subtitle=\"Fastest Whisper inference. 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\n <ProviderOption\n id=\"browser\"\n selected={provider === \"browser\"}\n onSelect={() => choose(\"browser\")}\n title=\"Native browser\"\n subtitle=\"Free built-in speech recognition. No key required.\"\n />\n </div>\n )}\n </div>\n\n {provider !== \"browser\" && (\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 LLM-based transcription and 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 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\";\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 </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\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"}