@agent-native/toolkit 0.14.3 → 0.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent-native.eject.json +19 -1
- package/dist/chat-history.css +3 -1
- package/dist/composer/ComposerPlusMenu.d.ts.map +1 -1
- package/dist/composer/ComposerPlusMenu.js +131 -35
- package/dist/composer/ComposerPlusMenu.js.map +1 -1
- package/dist/composer/MentionPopover.d.ts.map +1 -1
- package/dist/composer/MentionPopover.js +40 -5
- package/dist/composer/MentionPopover.js.map +1 -1
- package/dist/composer/PastedTextChip.d.ts.map +1 -1
- package/dist/composer/PastedTextChip.js +26 -2
- package/dist/composer/PastedTextChip.js.map +1 -1
- package/dist/composer/PastedTextChip.spec.d.ts +2 -0
- package/dist/composer/PastedTextChip.spec.d.ts.map +1 -0
- package/dist/composer/PastedTextChip.spec.js +42 -0
- package/dist/composer/PastedTextChip.spec.js.map +1 -0
- package/dist/composer/PromptComposer.d.ts.map +1 -1
- package/dist/composer/PromptComposer.js +26 -5
- package/dist/composer/PromptComposer.js.map +1 -1
- package/dist/composer/TiptapComposer.d.ts +5 -3
- package/dist/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/composer/TiptapComposer.js +289 -79
- package/dist/composer/TiptapComposer.js.map +1 -1
- package/dist/composer/TiptapComposer.spec.js +57 -0
- package/dist/composer/TiptapComposer.spec.js.map +1 -1
- package/dist/composer/VoiceButton.d.ts.map +1 -1
- package/dist/composer/VoiceButton.js +24 -5
- package/dist/composer/VoiceButton.js.map +1 -1
- package/dist/composer/runtime-adapters.d.ts +1 -0
- package/dist/composer/runtime-adapters.d.ts.map +1 -1
- package/dist/composer/runtime-adapters.js +8 -1
- package/dist/composer/runtime-adapters.js.map +1 -1
- package/dist/composer/runtime-adapters.spec.js +15 -0
- package/dist/composer/runtime-adapters.spec.js.map +1 -1
- package/dist/composer/useRealtimeVoiceMode.d.ts.map +1 -1
- package/dist/composer/useRealtimeVoiceMode.js +204 -68
- package/dist/composer/useRealtimeVoiceMode.js.map +1 -1
- package/dist/composer/useRealtimeVoiceMode.spec.js +27 -1
- package/dist/composer/useRealtimeVoiceMode.spec.js.map +1 -1
- package/dist/context-ui/ContextMeter.d.ts +3 -2
- package/dist/context-ui/ContextMeter.d.ts.map +1 -1
- package/dist/context-ui/ContextMeter.js +23 -3
- package/dist/context-ui/ContextMeter.js.map +1 -1
- package/dist/context-ui/ContextMeter.spec.d.ts +2 -0
- package/dist/context-ui/ContextMeter.spec.d.ts.map +1 -0
- package/dist/context-ui/ContextMeter.spec.js +52 -0
- package/dist/context-ui/ContextMeter.spec.js.map +1 -0
- package/dist/context-ui/ContextSegmentRow.d.ts +5 -3
- package/dist/context-ui/ContextSegmentRow.d.ts.map +1 -1
- package/dist/context-ui/ContextSegmentRow.js +74 -7
- package/dist/context-ui/ContextSegmentRow.js.map +1 -1
- package/dist/context-ui/ContextTreemap.d.ts +3 -2
- package/dist/context-ui/ContextTreemap.d.ts.map +1 -1
- package/dist/context-ui/ContextTreemap.js +5 -2
- package/dist/context-ui/ContextTreemap.js.map +1 -1
- package/dist/context-ui/ContextXRayPanel.d.ts +3 -2
- package/dist/context-ui/ContextXRayPanel.d.ts.map +1 -1
- package/dist/context-ui/ContextXRayPanel.js +112 -23
- package/dist/context-ui/ContextXRayPanel.js.map +1 -1
- package/dist/context-ui/types.d.ts +2 -0
- package/dist/context-ui/types.d.ts.map +1 -1
- package/dist/context-ui/types.js +7 -1
- package/dist/context-ui/types.js.map +1 -1
- package/dist/design-system/default-adapter.d.ts.map +1 -1
- package/dist/design-system/default-adapter.js +4 -1
- package/dist/design-system/default-adapter.js.map +1 -1
- package/dist/design-system/default-adapter.spec.js +12 -0
- package/dist/design-system/default-adapter.spec.js.map +1 -1
- package/dist/design-system/types.d.ts +2 -0
- package/dist/design-system/types.d.ts.map +1 -1
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +5 -2
- package/dist/provider.js.map +1 -1
- package/dist/sharing/SharePrimitives.d.ts +31 -0
- package/dist/sharing/SharePrimitives.d.ts.map +1 -0
- package/dist/sharing/SharePrimitives.js +45 -0
- package/dist/sharing/SharePrimitives.js.map +1 -0
- package/dist/sharing/index.d.ts +1 -0
- package/dist/sharing/index.d.ts.map +1 -1
- package/dist/sharing/index.js +1 -0
- package/dist/sharing/index.js.map +1 -1
- package/package.json +2 -2
- package/src/chat-history.css +3 -1
- package/src/composer/ComposerPlusMenu.tsx +170 -52
- package/src/composer/MentionPopover.tsx +43 -7
- package/src/composer/PastedTextChip.spec.tsx +58 -0
- package/src/composer/PastedTextChip.tsx +35 -7
- package/src/composer/PromptComposer.tsx +28 -7
- package/src/composer/TiptapComposer.spec.ts +85 -0
- package/src/composer/TiptapComposer.tsx +338 -81
- package/src/composer/VoiceButton.tsx +36 -11
- package/src/composer/runtime-adapters.spec.tsx +19 -0
- package/src/composer/runtime-adapters.tsx +12 -2
- package/src/composer/useRealtimeVoiceMode.spec.ts +34 -0
- package/src/composer/useRealtimeVoiceMode.tsx +219 -69
- package/src/context-ui/ContextMeter.spec.tsx +73 -0
- package/src/context-ui/ContextMeter.tsx +34 -4
- package/src/context-ui/ContextSegmentRow.tsx +111 -22
- package/src/context-ui/ContextTreemap.tsx +7 -1
- package/src/context-ui/ContextXRayPanel.tsx +138 -24
- package/src/context-ui/types.ts +14 -0
- package/src/design-system/default-adapter.spec.tsx +15 -0
- package/src/design-system/default-adapter.tsx +5 -1
- package/src/design-system/types.ts +2 -0
- package/src/provider.tsx +9 -1
- package/src/sharing/SharePrimitives.tsx +185 -0
- package/src/sharing/index.ts +8 -0
|
@@ -66,6 +66,7 @@ export function isRealtimeVoiceSetupRequired(
|
|
|
66
66
|
|
|
67
67
|
export function VoiceButton({ voice, isMac, disabled }: VoiceButtonProps) {
|
|
68
68
|
const adapters = useComposerRuntimeAdapters();
|
|
69
|
+
const t = adapters.translate!;
|
|
69
70
|
const { state, start, stop, supported } = voice;
|
|
70
71
|
const realtimeVoice = useRealtimeVoiceModeOptional();
|
|
71
72
|
const realtimeCopy = useRealtimeVoiceModeCopy();
|
|
@@ -144,10 +145,17 @@ export function VoiceButton({ voice, isMac, disabled }: VoiceButtonProps) {
|
|
|
144
145
|
}
|
|
145
146
|
|
|
146
147
|
const label = recording
|
|
147
|
-
? "
|
|
148
|
+
? t("agentChat.voice.dictation.stopRecording", {
|
|
149
|
+
defaultValue: "Stop recording",
|
|
150
|
+
})
|
|
148
151
|
: transcribing
|
|
149
|
-
? "
|
|
150
|
-
|
|
152
|
+
? t("agentChat.voice.dictation.transcribing", {
|
|
153
|
+
defaultValue: "Transcribing…",
|
|
154
|
+
})
|
|
155
|
+
: t("agentChat.voice.dictation.start", {
|
|
156
|
+
shortcut: isMac ? "⌘⇧M" : "Ctrl+Shift+M",
|
|
157
|
+
defaultValue: `Dictate (${isMac ? "⌘⇧M" : "Ctrl+Shift+M"})`,
|
|
158
|
+
});
|
|
151
159
|
|
|
152
160
|
const onClick = () => {
|
|
153
161
|
if (recording) stop();
|
|
@@ -190,6 +198,7 @@ export interface VoiceRecordingOverlayProps {
|
|
|
190
198
|
export function VoiceRecordingOverlay({ voice }: VoiceRecordingOverlayProps) {
|
|
191
199
|
const { state, amplitude, durationMs, errorMessage, cancel } = voice;
|
|
192
200
|
const { dismissError, start } = voice;
|
|
201
|
+
const t = useComposerRuntimeAdapters().translate!;
|
|
193
202
|
|
|
194
203
|
if (state === "error" && errorMessage) {
|
|
195
204
|
return (
|
|
@@ -207,12 +216,16 @@ export function VoiceRecordingOverlay({ voice }: VoiceRecordingOverlayProps) {
|
|
|
207
216
|
void start();
|
|
208
217
|
}}
|
|
209
218
|
className="shrink-0 cursor-pointer rounded px-1.5 py-0.5 text-[11px] font-medium text-red-500 hover:bg-red-500/20"
|
|
210
|
-
aria-label="
|
|
219
|
+
aria-label={t("agentChat.common.retry", {
|
|
220
|
+
defaultValue: "Try again",
|
|
221
|
+
})}
|
|
211
222
|
>
|
|
212
|
-
Try again
|
|
223
|
+
{t("agentChat.common.retry", { defaultValue: "Try again" })}
|
|
213
224
|
</button>
|
|
214
225
|
</TooltipTrigger>
|
|
215
|
-
<TooltipContent>
|
|
226
|
+
<TooltipContent>
|
|
227
|
+
{t("agentChat.common.retry", { defaultValue: "Try again" })}
|
|
228
|
+
</TooltipContent>
|
|
216
229
|
</Tooltip>
|
|
217
230
|
<Tooltip>
|
|
218
231
|
<TooltipTrigger asChild>
|
|
@@ -220,12 +233,16 @@ export function VoiceRecordingOverlay({ voice }: VoiceRecordingOverlayProps) {
|
|
|
220
233
|
type="button"
|
|
221
234
|
onClick={dismissError}
|
|
222
235
|
className="shrink-0 flex h-4 w-4 cursor-pointer items-center justify-center rounded text-red-500 hover:bg-red-500/20"
|
|
223
|
-
aria-label="
|
|
236
|
+
aria-label={t("agentChat.common.dismiss", {
|
|
237
|
+
defaultValue: "Dismiss",
|
|
238
|
+
})}
|
|
224
239
|
>
|
|
225
240
|
<IconX className="h-3 w-3" />
|
|
226
241
|
</button>
|
|
227
242
|
</TooltipTrigger>
|
|
228
|
-
<TooltipContent>
|
|
243
|
+
<TooltipContent>
|
|
244
|
+
{t("agentChat.common.dismiss", { defaultValue: "Dismiss" })}
|
|
245
|
+
</TooltipContent>
|
|
229
246
|
</Tooltip>
|
|
230
247
|
</div>
|
|
231
248
|
);
|
|
@@ -245,18 +262,26 @@ export function VoiceRecordingOverlay({ voice }: VoiceRecordingOverlayProps) {
|
|
|
245
262
|
type="button"
|
|
246
263
|
onClick={cancel}
|
|
247
264
|
className="shrink-0 flex h-5 w-5 items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-accent/40"
|
|
248
|
-
aria-label="
|
|
265
|
+
aria-label={t("agentChat.voice.dictation.cancelRecording", {
|
|
266
|
+
defaultValue: "Cancel recording",
|
|
267
|
+
})}
|
|
249
268
|
>
|
|
250
269
|
<IconX className="h-3 w-3" />
|
|
251
270
|
</button>
|
|
252
271
|
</TooltipTrigger>
|
|
253
|
-
<TooltipContent>
|
|
272
|
+
<TooltipContent>
|
|
273
|
+
{t("agentChat.voice.dictation.cancel", {
|
|
274
|
+
defaultValue: "Cancel (Esc)",
|
|
275
|
+
})}
|
|
276
|
+
</TooltipContent>
|
|
254
277
|
</Tooltip>
|
|
255
278
|
|
|
256
279
|
<div className="flex-1 flex items-center gap-[2px] min-w-0 h-4">
|
|
257
280
|
{state === "transcribing" ? (
|
|
258
281
|
<span className="text-[11px] text-muted-foreground">
|
|
259
|
-
|
|
282
|
+
{t("agentChat.voice.dictation.transcribing", {
|
|
283
|
+
defaultValue: "Transcribing…",
|
|
284
|
+
})}
|
|
260
285
|
</span>
|
|
261
286
|
) : (
|
|
262
287
|
<AmplitudeBars amplitude={amplitude} />
|
|
@@ -25,6 +25,25 @@ afterEach(() => {
|
|
|
25
25
|
});
|
|
26
26
|
|
|
27
27
|
describe("ComposerRuntimeAdaptersProvider", () => {
|
|
28
|
+
it("interpolates fallback translations without a provider", () => {
|
|
29
|
+
let translated = "";
|
|
30
|
+
|
|
31
|
+
function Consumer() {
|
|
32
|
+
const runtime = useComposerRuntimeAdapters();
|
|
33
|
+
translated = runtime.translate!("attachment.tooLarge", {
|
|
34
|
+
defaultValue: '"{{name}}" is {{size}} MB (max {{maxSize}} MB)',
|
|
35
|
+
name: "report.pdf",
|
|
36
|
+
size: 12.5,
|
|
37
|
+
maxSize: 10,
|
|
38
|
+
});
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
act(() => root.render(<Consumer />));
|
|
43
|
+
|
|
44
|
+
expect(translated).toBe('"report.pdf" is 12.5 MB (max 10 MB)');
|
|
45
|
+
});
|
|
46
|
+
|
|
28
47
|
it("keeps the context value stable so consumer effects do not refire", () => {
|
|
29
48
|
const readAppState = vi.fn(() => undefined);
|
|
30
49
|
const adapters: ComposerRuntimeAdapters = { voice: { readAppState } };
|
|
@@ -110,6 +110,7 @@ export interface VoiceContextPack {
|
|
|
110
110
|
export interface ComposerRuntimeAdapters {
|
|
111
111
|
resolvePath?: (path: string) => string;
|
|
112
112
|
translate?: ComposerTranslate;
|
|
113
|
+
formatNumber?: (value: number, options?: Intl.NumberFormatOptions) => string;
|
|
113
114
|
models?: {
|
|
114
115
|
useChatModels?: (options: { enabled: boolean }) => ComposerModelState;
|
|
115
116
|
useAgentEngineConfigured?: (enabled: boolean) => {
|
|
@@ -178,8 +179,15 @@ export interface ComposerRuntimeAdapters {
|
|
|
178
179
|
}
|
|
179
180
|
|
|
180
181
|
const identityPath = (path: string) => path;
|
|
181
|
-
const fallbackTranslate: ComposerTranslate = (key, options) =>
|
|
182
|
-
|
|
182
|
+
const fallbackTranslate: ComposerTranslate = (key, options) => {
|
|
183
|
+
const template =
|
|
184
|
+
typeof options?.defaultValue === "string" ? options.defaultValue : key;
|
|
185
|
+
|
|
186
|
+
return template.replace(/{{\s*([\w$.-]+)\s*}}/g, (match, name: string) => {
|
|
187
|
+
const value = options?.[name];
|
|
188
|
+
return value == null ? match : String(value);
|
|
189
|
+
});
|
|
190
|
+
};
|
|
183
191
|
const fallbackModels = {
|
|
184
192
|
useChatModels: () => ({
|
|
185
193
|
selectedModel: "auto",
|
|
@@ -210,6 +218,8 @@ const fallbackAdapters: Required<Pick<ComposerRuntimeAdapters, "resolvePath">> &
|
|
|
210
218
|
ComposerRuntimeAdapters = {
|
|
211
219
|
resolvePath: identityPath,
|
|
212
220
|
translate: fallbackTranslate,
|
|
221
|
+
formatNumber: (value, options) =>
|
|
222
|
+
new Intl.NumberFormat("en-US", options).format(value),
|
|
213
223
|
models: fallbackModels,
|
|
214
224
|
agentChat: {
|
|
215
225
|
sendToAgentChat: () => {},
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
// @vitest-environment happy-dom
|
|
2
2
|
|
|
3
|
+
import { act, createElement } from "react";
|
|
4
|
+
import { createRoot } from "react-dom/client";
|
|
3
5
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
4
6
|
|
|
7
|
+
import { ComposerRuntimeAdaptersProvider } from "./runtime-adapters.js";
|
|
5
8
|
import {
|
|
6
9
|
createRealtimeVoiceGreetingEvent,
|
|
7
10
|
createRealtimeVoiceGreetingStarter,
|
|
@@ -28,6 +31,7 @@ import {
|
|
|
28
31
|
realtimeVoiceReasoningEffort,
|
|
29
32
|
resolveRealtimeVoiceLanguage,
|
|
30
33
|
shouldRestoreRealtimeVoiceTranscriptThread,
|
|
34
|
+
useRealtimeVoiceModeCopy,
|
|
31
35
|
} from "./useRealtimeVoiceMode.js";
|
|
32
36
|
import type { RealtimeVoiceFunctionTool } from "./useRealtimeVoiceMode.js";
|
|
33
37
|
|
|
@@ -45,6 +49,36 @@ function realtimeTool(name: string): RealtimeVoiceFunctionTool {
|
|
|
45
49
|
};
|
|
46
50
|
}
|
|
47
51
|
|
|
52
|
+
describe("Realtime voice localization", () => {
|
|
53
|
+
it("uses app overrides from the modern agentChat namespace", () => {
|
|
54
|
+
const container = document.createElement("div");
|
|
55
|
+
const root = createRoot(container);
|
|
56
|
+
let entryButtonLabel = "";
|
|
57
|
+
|
|
58
|
+
function Consumer() {
|
|
59
|
+
entryButtonLabel = useRealtimeVoiceModeCopy().entryButtonLabel;
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
act(() => {
|
|
64
|
+
root.render(
|
|
65
|
+
createElement(ComposerRuntimeAdaptersProvider, {
|
|
66
|
+
adapters: {
|
|
67
|
+
translate: (key, options) =>
|
|
68
|
+
key === "agentChat.voiceMode.entryButtonLabel"
|
|
69
|
+
? "Modern voice override"
|
|
70
|
+
: String(options?.defaultValue ?? key),
|
|
71
|
+
},
|
|
72
|
+
children: createElement(Consumer),
|
|
73
|
+
}),
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
expect(entryButtonLabel).toBe("Modern voice override");
|
|
78
|
+
act(() => root.unmount());
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
48
82
|
describe("Realtime voice client transport", () => {
|
|
49
83
|
it("normalizes persisted preferences and resolves Auto from the browser language", () => {
|
|
50
84
|
expect(
|
|
@@ -22,7 +22,10 @@ import {
|
|
|
22
22
|
type RealtimeVoiceModeCopy,
|
|
23
23
|
type RealtimeVoiceModeState,
|
|
24
24
|
} from "./RealtimeVoiceMode.js";
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
type ComposerTranslate,
|
|
27
|
+
useComposerRuntimeAdapters,
|
|
28
|
+
} from "./runtime-adapters.js";
|
|
26
29
|
|
|
27
30
|
const REALTIME_VOICE_STATE_KEY = "realtime-voice-session";
|
|
28
31
|
const REALTIME_VOICE_PREFERENCES_KEY = "realtime-voice-prefs";
|
|
@@ -1097,81 +1100,203 @@ export function listenForRealtimeVoicePageHide(
|
|
|
1097
1100
|
return () => window.removeEventListener("pagehide", cleanup);
|
|
1098
1101
|
}
|
|
1099
1102
|
|
|
1100
|
-
function voiceCopy(t:
|
|
1103
|
+
function voiceCopy(t: ComposerTranslate): RealtimeVoiceModeCopy {
|
|
1101
1104
|
return {
|
|
1102
|
-
entryButtonLabel: t("
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1105
|
+
entryButtonLabel: t("agentChat.voiceMode.entryButtonLabel", {
|
|
1106
|
+
defaultValue: "Use microphone",
|
|
1107
|
+
}),
|
|
1108
|
+
promptTitle: t("agentChat.voiceMode.promptTitle", {
|
|
1109
|
+
defaultValue: "Use your voice",
|
|
1110
|
+
}),
|
|
1111
|
+
promptDescription: t("agentChat.voiceMode.promptDescription", {
|
|
1112
|
+
defaultValue:
|
|
1113
|
+
"Voice mode keeps listening while the agent navigates and takes actions.",
|
|
1114
|
+
}),
|
|
1115
|
+
setupTitle: t("agentChat.voiceMode.setupTitle", {
|
|
1116
|
+
defaultValue: "Set up voice mode",
|
|
1117
|
+
}),
|
|
1118
|
+
setupDescription: t("agentChat.voiceMode.setupDescription", {
|
|
1119
|
+
defaultValue:
|
|
1120
|
+
"Connect Builder.io to use managed voice with free credits, or add your own keys.",
|
|
1121
|
+
}),
|
|
1122
|
+
connectBuilder: t("agentChat.voiceMode.connectBuilder", {
|
|
1123
|
+
defaultValue: "Connect Builder.io",
|
|
1124
|
+
}),
|
|
1125
|
+
useOpenAiKey: t("agentChat.voiceMode.useOpenAiKey", {
|
|
1126
|
+
defaultValue: "Add your own keys",
|
|
1127
|
+
}),
|
|
1128
|
+
startWithOpenAiKey: t("agentChat.voiceMode.startWithOpenAiKey", {
|
|
1129
|
+
defaultValue: "Start with OpenAI key",
|
|
1130
|
+
}),
|
|
1131
|
+
startVoiceMode: t("agentChat.voiceMode.start", {
|
|
1132
|
+
defaultValue: "Real-time voice",
|
|
1133
|
+
}),
|
|
1134
|
+
keepDictating: t("agentChat.voiceMode.keepDictating", {
|
|
1135
|
+
defaultValue: "Dictate",
|
|
1136
|
+
}),
|
|
1137
|
+
rememberPreference: t("agentChat.voiceMode.rememberPreference", {
|
|
1138
|
+
defaultValue: "Remember my preference",
|
|
1139
|
+
}),
|
|
1140
|
+
showChat: t("agentChat.voiceMode.showChat", {
|
|
1141
|
+
defaultValue: "Show chat",
|
|
1142
|
+
}),
|
|
1143
|
+
hideChat: t("agentChat.voiceMode.hideChat", {
|
|
1144
|
+
defaultValue: "Hide chat",
|
|
1145
|
+
}),
|
|
1146
|
+
endVoiceMode: t("agentChat.voiceMode.end", {
|
|
1147
|
+
defaultValue: "End voice mode",
|
|
1148
|
+
}),
|
|
1149
|
+
voiceSettings: t("agentChat.voiceMode.voiceSettings", {
|
|
1150
|
+
defaultValue: "Voice settings",
|
|
1151
|
+
}),
|
|
1117
1152
|
settings: {
|
|
1118
|
-
language: t("
|
|
1119
|
-
|
|
1153
|
+
language: t("agentChat.voiceMode.settings.language", {
|
|
1154
|
+
defaultValue: "Language",
|
|
1155
|
+
}),
|
|
1156
|
+
autoLanguage: t("agentChat.voiceMode.settings.autoLanguage", {
|
|
1157
|
+
defaultValue: "Auto",
|
|
1158
|
+
}),
|
|
1120
1159
|
languages: {
|
|
1121
|
-
en: t("
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1160
|
+
en: t("agentChat.voiceMode.settings.languages.en", {
|
|
1161
|
+
defaultValue: "English",
|
|
1162
|
+
}),
|
|
1163
|
+
es: t("agentChat.voiceMode.settings.languages.es", {
|
|
1164
|
+
defaultValue: "Spanish",
|
|
1165
|
+
}),
|
|
1166
|
+
fr: t("agentChat.voiceMode.settings.languages.fr", {
|
|
1167
|
+
defaultValue: "French",
|
|
1168
|
+
}),
|
|
1169
|
+
de: t("agentChat.voiceMode.settings.languages.de", {
|
|
1170
|
+
defaultValue: "German",
|
|
1171
|
+
}),
|
|
1172
|
+
it: t("agentChat.voiceMode.settings.languages.it", {
|
|
1173
|
+
defaultValue: "Italian",
|
|
1174
|
+
}),
|
|
1175
|
+
pt: t("agentChat.voiceMode.settings.languages.pt", {
|
|
1176
|
+
defaultValue: "Portuguese",
|
|
1177
|
+
}),
|
|
1178
|
+
ja: t("agentChat.voiceMode.settings.languages.ja", {
|
|
1179
|
+
defaultValue: "Japanese",
|
|
1180
|
+
}),
|
|
1181
|
+
ko: t("agentChat.voiceMode.settings.languages.ko", {
|
|
1182
|
+
defaultValue: "Korean",
|
|
1183
|
+
}),
|
|
1184
|
+
zh: t("agentChat.voiceMode.settings.languages.zh", {
|
|
1185
|
+
defaultValue: "Chinese",
|
|
1186
|
+
}),
|
|
1130
1187
|
},
|
|
1131
|
-
intelligence: t("
|
|
1188
|
+
intelligence: t("agentChat.voiceMode.settings.intelligence", {
|
|
1189
|
+
defaultValue: "Intelligence",
|
|
1190
|
+
}),
|
|
1132
1191
|
intelligenceLevels: {
|
|
1133
|
-
instant: t("
|
|
1192
|
+
instant: t("agentChat.voiceMode.settings.intelligenceLevels.instant", {
|
|
1193
|
+
defaultValue: "Instant",
|
|
1194
|
+
}),
|
|
1134
1195
|
balanced: t(
|
|
1135
|
-
"
|
|
1196
|
+
"agentChat.voiceMode.settings.intelligenceLevels.balanced",
|
|
1197
|
+
{ defaultValue: "Balanced" },
|
|
1136
1198
|
),
|
|
1137
|
-
deep: t("
|
|
1199
|
+
deep: t("agentChat.voiceMode.settings.intelligenceLevels.deep", {
|
|
1200
|
+
defaultValue: "Deep",
|
|
1201
|
+
}),
|
|
1138
1202
|
},
|
|
1139
|
-
voiceStyle: t("
|
|
1140
|
-
|
|
1141
|
-
|
|
1203
|
+
voiceStyle: t("agentChat.voiceMode.settings.voiceStyle", {
|
|
1204
|
+
defaultValue: "Voice style",
|
|
1205
|
+
}),
|
|
1206
|
+
microphone: t("agentChat.voiceMode.settings.microphone", {
|
|
1207
|
+
defaultValue: "Microphone",
|
|
1208
|
+
}),
|
|
1209
|
+
defaultMicrophone: t("agentChat.voiceMode.settings.defaultMicrophone", {
|
|
1210
|
+
defaultValue: "System default",
|
|
1211
|
+
}),
|
|
1142
1212
|
microphoneSwitchFailed: t(
|
|
1143
|
-
"
|
|
1213
|
+
"agentChat.voiceMode.settings.microphoneSwitchFailed",
|
|
1214
|
+
{
|
|
1215
|
+
defaultValue:
|
|
1216
|
+
"Could not switch microphones. Your current microphone is still active.",
|
|
1217
|
+
},
|
|
1144
1218
|
),
|
|
1145
|
-
voiceChangePending: t("
|
|
1219
|
+
voiceChangePending: t("agentChat.voiceMode.settings.voiceChangePending", {
|
|
1220
|
+
defaultValue:
|
|
1221
|
+
"Your new voice will apply next time you start voice mode.",
|
|
1222
|
+
}),
|
|
1146
1223
|
voiceDescriptions: {
|
|
1147
|
-
marin: t("
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1224
|
+
marin: t("agentChat.voiceMode.settings.voiceDescriptions.marin", {
|
|
1225
|
+
defaultValue: "Warm and natural",
|
|
1226
|
+
}),
|
|
1227
|
+
cedar: t("agentChat.voiceMode.settings.voiceDescriptions.cedar", {
|
|
1228
|
+
defaultValue: "Clear and grounded",
|
|
1229
|
+
}),
|
|
1230
|
+
coral: t("agentChat.voiceMode.settings.voiceDescriptions.coral", {
|
|
1231
|
+
defaultValue: "Friendly and bright",
|
|
1232
|
+
}),
|
|
1233
|
+
sage: t("agentChat.voiceMode.settings.voiceDescriptions.sage", {
|
|
1234
|
+
defaultValue: "Calm and thoughtful",
|
|
1235
|
+
}),
|
|
1236
|
+
verse: t("agentChat.voiceMode.settings.voiceDescriptions.verse", {
|
|
1237
|
+
defaultValue: "Expressive and versatile",
|
|
1238
|
+
}),
|
|
1239
|
+
alloy: t("agentChat.voiceMode.settings.voiceDescriptions.alloy", {
|
|
1240
|
+
defaultValue: "Balanced and neutral",
|
|
1241
|
+
}),
|
|
1242
|
+
ash: t("agentChat.voiceMode.settings.voiceDescriptions.ash", {
|
|
1243
|
+
defaultValue: "Smooth and confident",
|
|
1244
|
+
}),
|
|
1245
|
+
ballad: t("agentChat.voiceMode.settings.voiceDescriptions.ballad", {
|
|
1246
|
+
defaultValue: "Warm and expressive",
|
|
1247
|
+
}),
|
|
1248
|
+
echo: t("agentChat.voiceMode.settings.voiceDescriptions.echo", {
|
|
1249
|
+
defaultValue: "Clear and direct",
|
|
1250
|
+
}),
|
|
1251
|
+
shimmer: t("agentChat.voiceMode.settings.voiceDescriptions.shimmer", {
|
|
1252
|
+
defaultValue: "Light and upbeat",
|
|
1253
|
+
}),
|
|
1157
1254
|
},
|
|
1158
1255
|
},
|
|
1159
1256
|
status: {
|
|
1160
|
-
connecting: t("
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1257
|
+
connecting: t("agentChat.voiceMode.status.connecting", {
|
|
1258
|
+
defaultValue: "Connecting",
|
|
1259
|
+
}),
|
|
1260
|
+
listening: t("agentChat.voiceMode.status.listening", {
|
|
1261
|
+
defaultValue: "Listening",
|
|
1262
|
+
}),
|
|
1263
|
+
speaking: t("agentChat.voiceMode.status.speaking", {
|
|
1264
|
+
defaultValue: "Speaking",
|
|
1265
|
+
}),
|
|
1266
|
+
working: t("agentChat.voiceMode.status.working", {
|
|
1267
|
+
defaultValue: "Working",
|
|
1268
|
+
}),
|
|
1269
|
+
error: t("agentChat.voiceMode.status.error", {
|
|
1270
|
+
defaultValue: "Voice mode needs attention",
|
|
1271
|
+
}),
|
|
1272
|
+
ending: t("agentChat.voiceMode.status.ending", {
|
|
1273
|
+
defaultValue: "Ending voice mode",
|
|
1274
|
+
}),
|
|
1166
1275
|
},
|
|
1167
1276
|
errors: {
|
|
1168
|
-
unsupported: t("
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1277
|
+
unsupported: t("agentChat.voiceMode.errors.unsupported", {
|
|
1278
|
+
defaultValue:
|
|
1279
|
+
"This browser does not support realtime voice conversations.",
|
|
1280
|
+
}),
|
|
1281
|
+
responseFailed: t("agentChat.voiceMode.errors.responseFailed", {
|
|
1282
|
+
defaultValue: "OpenAI could not complete the voice response.",
|
|
1283
|
+
}),
|
|
1284
|
+
sessionFailed: t("agentChat.voiceMode.errors.sessionFailed", {
|
|
1285
|
+
defaultValue: "The realtime voice session encountered an error.",
|
|
1286
|
+
}),
|
|
1287
|
+
channelDisconnected: t("agentChat.voiceMode.errors.channelDisconnected", {
|
|
1288
|
+
defaultValue: "The realtime voice control channel disconnected.",
|
|
1289
|
+
}),
|
|
1290
|
+
connectionTimedOut: t("agentChat.voiceMode.errors.connectionTimedOut", {
|
|
1291
|
+
defaultValue:
|
|
1292
|
+
"The realtime voice connection timed out. Try again after the app finishes loading.",
|
|
1293
|
+
}),
|
|
1294
|
+
connectionFailed: t("agentChat.voiceMode.errors.connectionFailed", {
|
|
1295
|
+
defaultValue: "The realtime voice connection failed.",
|
|
1296
|
+
}),
|
|
1297
|
+
offerFailed: t("agentChat.voiceMode.errors.offerFailed", {
|
|
1298
|
+
defaultValue: "The browser did not create an audio offer.",
|
|
1299
|
+
}),
|
|
1175
1300
|
},
|
|
1176
1301
|
};
|
|
1177
1302
|
}
|
|
@@ -1186,6 +1311,7 @@ function useRealtimeVoiceModeController(
|
|
|
1186
1311
|
copy?: RealtimeVoiceModeCopy,
|
|
1187
1312
|
): RealtimeVoiceModeApi {
|
|
1188
1313
|
const adapters = useComposerRuntimeAdapters();
|
|
1314
|
+
const t = adapters.translate!;
|
|
1189
1315
|
const [state, setState] = useState<"idle" | RealtimeVoiceModeState>("idle");
|
|
1190
1316
|
const [error, setError] = useState<string | null>(null);
|
|
1191
1317
|
const [chatVisible, setChatVisible] = useState(false);
|
|
@@ -1395,7 +1521,12 @@ function useRealtimeVoiceModeController(
|
|
|
1395
1521
|
)
|
|
1396
1522
|
.map((device, index) => ({
|
|
1397
1523
|
deviceId: device.deviceId,
|
|
1398
|
-
label:
|
|
1524
|
+
label:
|
|
1525
|
+
device.label ||
|
|
1526
|
+
t("agentChat.voiceMode.settings.microphoneNumber", {
|
|
1527
|
+
number: index + 1,
|
|
1528
|
+
defaultValue: `Microphone ${index + 1}`,
|
|
1529
|
+
}),
|
|
1399
1530
|
}));
|
|
1400
1531
|
setMicrophones(inputs);
|
|
1401
1532
|
return inputs;
|
|
@@ -1403,7 +1534,7 @@ function useRealtimeVoiceModeController(
|
|
|
1403
1534
|
// Enumeration is progressive enhancement; system default remains usable.
|
|
1404
1535
|
return null;
|
|
1405
1536
|
}
|
|
1406
|
-
}, []);
|
|
1537
|
+
}, [t]);
|
|
1407
1538
|
|
|
1408
1539
|
const startMeterLoop = useCallback(() => {
|
|
1409
1540
|
if (meterFrameRef.current !== null) return;
|
|
@@ -1508,7 +1639,10 @@ function useRealtimeVoiceModeController(
|
|
|
1508
1639
|
} catch {
|
|
1509
1640
|
setMicrophoneError(
|
|
1510
1641
|
copy?.settings.microphoneSwitchFailed ??
|
|
1511
|
-
"
|
|
1642
|
+
t("agentChat.voiceMode.settings.microphoneSwitchFailed", {
|
|
1643
|
+
defaultValue:
|
|
1644
|
+
"Could not switch microphones. Your current microphone is still active.",
|
|
1645
|
+
}),
|
|
1512
1646
|
);
|
|
1513
1647
|
} finally {
|
|
1514
1648
|
setMicrophoneSwitching(false);
|
|
@@ -1520,6 +1654,7 @@ function useRealtimeVoiceModeController(
|
|
|
1520
1654
|
microphoneDeviceId,
|
|
1521
1655
|
microphoneSwitching,
|
|
1522
1656
|
refreshMicrophones,
|
|
1657
|
+
t,
|
|
1523
1658
|
],
|
|
1524
1659
|
);
|
|
1525
1660
|
switchMicrophoneRef.current = setMicrophone;
|
|
@@ -1740,7 +1875,9 @@ function useRealtimeVoiceModeController(
|
|
|
1740
1875
|
fail(
|
|
1741
1876
|
message ||
|
|
1742
1877
|
copy?.errors.sessionFailed ||
|
|
1743
|
-
"
|
|
1878
|
+
t("agentChat.voiceMode.errors.sessionFailed", {
|
|
1879
|
+
defaultValue: "The realtime voice session encountered an error.",
|
|
1880
|
+
}),
|
|
1744
1881
|
);
|
|
1745
1882
|
return;
|
|
1746
1883
|
}
|
|
@@ -1757,6 +1894,7 @@ function useRealtimeVoiceModeController(
|
|
|
1757
1894
|
greetingStarter,
|
|
1758
1895
|
handleFunctionCall,
|
|
1759
1896
|
syncAppState,
|
|
1897
|
+
t,
|
|
1760
1898
|
transcriptSequencer,
|
|
1761
1899
|
transition,
|
|
1762
1900
|
responseCoordinator,
|
|
@@ -1773,7 +1911,10 @@ function useRealtimeVoiceModeController(
|
|
|
1773
1911
|
) {
|
|
1774
1912
|
fail(
|
|
1775
1913
|
copy?.errors.unsupported ??
|
|
1776
|
-
"
|
|
1914
|
+
t("agentChat.voiceMode.errors.unsupported", {
|
|
1915
|
+
defaultValue:
|
|
1916
|
+
"This browser does not support realtime voice conversations.",
|
|
1917
|
+
}),
|
|
1777
1918
|
);
|
|
1778
1919
|
return;
|
|
1779
1920
|
}
|
|
@@ -1804,7 +1945,9 @@ function useRealtimeVoiceModeController(
|
|
|
1804
1945
|
if (!isCurrentAttempt()) return;
|
|
1805
1946
|
fail(
|
|
1806
1947
|
copy?.errors.connectionTimedOut ??
|
|
1807
|
-
"
|
|
1948
|
+
t("agentChat.voiceMode.errors.connectionTimedOut", {
|
|
1949
|
+
defaultValue: "The realtime voice connection timed out.",
|
|
1950
|
+
}),
|
|
1808
1951
|
);
|
|
1809
1952
|
});
|
|
1810
1953
|
connectionGateRef.current = connectionGate;
|
|
@@ -1870,7 +2013,9 @@ function useRealtimeVoiceModeController(
|
|
|
1870
2013
|
if (!isCurrentAttempt()) return;
|
|
1871
2014
|
fail(
|
|
1872
2015
|
copy?.errors.channelDisconnected ??
|
|
1873
|
-
"
|
|
2016
|
+
t("agentChat.voiceMode.errors.channelDisconnected", {
|
|
2017
|
+
defaultValue: "The realtime voice control channel disconnected.",
|
|
2018
|
+
}),
|
|
1874
2019
|
);
|
|
1875
2020
|
};
|
|
1876
2021
|
peer.onconnectionstatechange = () => {
|
|
@@ -1881,7 +2026,9 @@ function useRealtimeVoiceModeController(
|
|
|
1881
2026
|
if (peer.connectionState === "failed") {
|
|
1882
2027
|
fail(
|
|
1883
2028
|
copy?.errors.connectionFailed ??
|
|
1884
|
-
"
|
|
2029
|
+
t("agentChat.voiceMode.errors.connectionFailed", {
|
|
2030
|
+
defaultValue: "The realtime voice connection failed.",
|
|
2031
|
+
}),
|
|
1885
2032
|
);
|
|
1886
2033
|
}
|
|
1887
2034
|
};
|
|
@@ -1893,7 +2040,9 @@ function useRealtimeVoiceModeController(
|
|
|
1893
2040
|
if (!offer.sdp) {
|
|
1894
2041
|
throw new Error(
|
|
1895
2042
|
copy?.errors.offerFailed ??
|
|
1896
|
-
"
|
|
2043
|
+
t("agentChat.voiceMode.errors.offerFailed", {
|
|
2044
|
+
defaultValue: "The browser did not create an audio offer.",
|
|
2045
|
+
}),
|
|
1897
2046
|
);
|
|
1898
2047
|
}
|
|
1899
2048
|
const answer = await createRealtimeVoiceSessionWithCapability(offer.sdp, {
|
|
@@ -1945,6 +2094,7 @@ function useRealtimeVoiceModeController(
|
|
|
1945
2094
|
handleServerEvent,
|
|
1946
2095
|
hydratePreferences,
|
|
1947
2096
|
refreshMicrophones,
|
|
2097
|
+
t,
|
|
1948
2098
|
transcriptSequencer,
|
|
1949
2099
|
transition,
|
|
1950
2100
|
]);
|