@opengeni/react 0.28.2 → 0.32.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/dist/approvals.d.ts +14 -0
- package/dist/{chunk-M7X4JZOD.js → chunk-4QOQ2DSR.js} +8 -1
- package/dist/chunk-4QOQ2DSR.js.map +1 -0
- package/dist/{chunk-HQFPO7VX.js → chunk-7CJOAW3V.js} +24 -3
- package/dist/{chunk-HQFPO7VX.js.map → chunk-7CJOAW3V.js.map} +1 -1
- package/dist/{chunk-PMSGE233.js → chunk-A2WITZTI.js} +377 -113
- package/dist/chunk-A2WITZTI.js.map +1 -0
- package/dist/{chunk-BC6RKIHS.js → chunk-GZ4KR67U.js} +11 -11
- package/dist/{chunk-4F2YLLHP.js → chunk-KZRUYCL4.js} +14 -17
- package/dist/chunk-KZRUYCL4.js.map +1 -0
- package/dist/{chunk-OCHBZ2X7.js → chunk-TCCE5ZU5.js} +192 -28
- package/dist/chunk-TCCE5ZU5.js.map +1 -0
- package/dist/chunk-U255M5EZ.js +178 -0
- package/dist/chunk-U255M5EZ.js.map +1 -0
- package/dist/{chunk-XVVQP7A4.js → chunk-WY2QN2CX.js} +2539 -2174
- package/dist/chunk-WY2QN2CX.js.map +1 -0
- package/dist/chunk-ZNNE2VES.js +6413 -0
- package/dist/chunk-ZNNE2VES.js.map +1 -0
- package/dist/client.d.ts +33 -0
- package/dist/commands/index.d.ts +3 -0
- package/dist/commands/registry.d.ts +41 -0
- package/dist/commands/types.d.ts +85 -0
- package/dist/components/approval-surface.d.ts +28 -0
- package/dist/components/chat-composer.d.ts +51 -0
- package/dist/components/code-editor.d.ts +43 -0
- package/dist/components/command-palette.d.ts +25 -0
- package/dist/components/composer-transcription-control.d.ts +36 -0
- package/dist/components/composer.d.ts +277 -0
- package/dist/components/copy-button.d.ts +26 -0
- package/dist/components/desktop-viewer.d.ts +85 -0
- package/dist/components/diff-view.d.ts +38 -0
- package/dist/components/enrollment-consent.d.ts +33 -0
- package/dist/components/enrollment-device-flow.d.ts +27 -0
- package/dist/components/file-browser.d.ts +47 -0
- package/dist/components/fleet-tile.d.ts +18 -0
- package/dist/components/human-input-form.d.ts +47 -0
- package/dist/components/machine-card.d.ts +21 -0
- package/dist/components/machine-dock-bar.d.ts +31 -0
- package/dist/components/machine-health-pill.d.ts +9 -0
- package/dist/components/machine-metrics.d.ts +15 -0
- package/dist/components/machine-status-pill.d.ts +54 -0
- package/dist/components/machines/health.d.ts +25 -0
- package/dist/components/machines/machine-detail.d.ts +14 -0
- package/dist/components/machines/metric-history-chart.d.ts +24 -0
- package/dist/components/machines/metric-sparkline.d.ts +9 -0
- package/dist/components/machines/series.d.ts +24 -0
- package/dist/components/machines-dashboard.d.ts +30 -0
- package/dist/components/markdown.d.ts +31 -0
- package/dist/components/message-timeline.d.ts +98 -0
- package/dist/components/model-picker.d.ts +19 -0
- package/dist/components/pierre-diff.d.ts +34 -0
- package/dist/components/pierre-file.d.ts +27 -0
- package/dist/components/queue-draft-policy.d.ts +7 -0
- package/dist/components/queue-surface-implementation.d.ts +20 -0
- package/dist/components/queue-surface-state.d.ts +12 -0
- package/dist/components/queue-surface.d.ts +12 -0
- package/dist/components/sandbox-files.d.ts +49 -0
- package/dist/components/sandbox-terminal.d.ts +123 -0
- package/dist/components/sandbox-workspace.d.ts +128 -0
- package/dist/components/session-chrome.d.ts +70 -0
- package/dist/components/session-status.d.ts +25 -0
- package/dist/components/soften-streaming-markdown.d.ts +13 -0
- package/dist/components/stream-reveal.d.ts +37 -0
- package/dist/components/tip-follow.d.ts +139 -0
- package/dist/components/tooltip.d.ts +16 -0
- package/dist/components/workbench-changes.d.ts +34 -0
- package/dist/components/workspace-dock.d.ts +35 -0
- package/dist/composer.d.ts +11 -7
- package/dist/composer.js +9 -6
- package/dist/hooks/internal.d.ts +46 -0
- package/dist/hooks/use-available-models.d.ts +37 -0
- package/dist/hooks/use-billing-usage.d.ts +26 -0
- package/dist/hooks/use-codex-accounts.d.ts +69 -0
- package/dist/{use-composer-ClHy1rkL.d.ts → hooks/use-composer.d.ts} +16 -24
- package/dist/hooks/use-desktop-stream.d.ts +43 -0
- package/dist/hooks/use-environments.d.ts +35 -0
- package/dist/{use-file-attachments-BmT8WtqO.d.ts → hooks/use-file-attachments.d.ts} +6 -9
- package/dist/hooks/use-goal.d.ts +46 -0
- package/dist/hooks/use-human-input.d.ts +26 -0
- package/dist/hooks/use-last-started-turn-policy.d.ts +27 -0
- package/dist/hooks/use-machine-chip.d.ts +54 -0
- package/dist/hooks/use-machines.d.ts +81 -0
- package/dist/hooks/use-packs.d.ts +27 -0
- package/dist/hooks/use-relay-frame-stream.d.ts +53 -0
- package/dist/hooks/use-rigs.d.ts +87 -0
- package/dist/hooks/use-sandbox-files.d.ts +108 -0
- package/dist/hooks/use-sandbox-git.d.ts +58 -0
- package/dist/hooks/use-sandbox-terminal.d.ts +62 -0
- package/dist/hooks/use-scheduled-tasks.d.ts +15 -0
- package/dist/hooks/use-session-capabilities.d.ts +83 -0
- package/dist/hooks/use-session-control.d.ts +22 -0
- package/dist/hooks/use-session-events.d.ts +92 -0
- package/dist/hooks/use-session-lineage.d.ts +17 -0
- package/dist/hooks/use-session-mcp-approval-policy.d.ts +23 -0
- package/dist/hooks/use-session.d.ts +23 -0
- package/dist/hooks/use-slash-commands.d.ts +66 -0
- package/dist/hooks/use-terminal-stream.d.ts +46 -0
- package/dist/hooks/use-transcription.d.ts +73 -0
- package/dist/hooks/use-turn-queue.d.ts +44 -0
- package/dist/hooks/use-voice-input.d.ts +25 -0
- package/dist/hooks/use-windowed-sections.d.ts +42 -0
- package/dist/hooks/use-workspace-capture.d.ts +49 -0
- package/dist/hooks/use-workspace-edit.d.ts +73 -0
- package/dist/hooks/use-workspace-sessions.d.ts +30 -0
- package/dist/hooks/use-workspaces.d.ts +22 -0
- package/dist/human-input.d.ts +18 -0
- package/dist/index.d.ts +144 -2119
- package/dist/index.js +421 -324
- package/dist/index.js.map +1 -1
- package/dist/lib/clipboard.d.ts +7 -0
- package/dist/lib/cn.d.ts +3 -0
- package/dist/lib/format.d.ts +48 -0
- package/dist/lib/git-patch.d.ts +8 -0
- package/dist/lib/motion-inspect.d.ts +5 -0
- package/dist/lib/motion.d.ts +14 -0
- package/dist/lib/noto-sans-arabic-loader.d.ts +1 -0
- package/dist/lib/noto-sans-jp-loader.generated.d.ts +1 -0
- package/dist/lib/relay-wire.d.ts +30 -0
- package/dist/lib/use-portal-token-style.d.ts +9 -0
- package/dist/lib/use-theme-type.d.ts +5 -0
- package/dist/lib/use-unicode-fonts.d.ts +12 -0
- package/dist/lib/xterm-renderer.d.ts +19 -0
- package/dist/lib/xterm-theme.d.ts +75 -0
- package/dist/machines.d.ts +30 -413
- package/dist/machines.js +3 -3
- package/dist/model-policy.d.ts +32 -0
- package/dist/model-policy.js +33 -0
- package/dist/model-policy.js.map +1 -0
- package/dist/provider.d.ts +16 -0
- package/dist/{queue-surface-implementation-UQ535OKQ.js → queue-surface-implementation-THPAKGMD.js} +2 -2
- package/dist/{queue-surface-implementation-UQ535OKQ.js.map → queue-surface-implementation-THPAKGMD.js.map} +1 -1
- package/dist/session-context.d.ts +82 -0
- package/dist/session-ui.d.ts +10 -7
- package/dist/session-ui.js +9 -5
- package/dist/session.d.ts +28 -6
- package/dist/session.js +11 -11
- package/dist/timeline/activity-rail.d.ts +19 -0
- package/dist/timeline/disclosure-context.d.ts +14 -0
- package/dist/timeline/entrance.d.ts +14 -0
- package/dist/timeline/fleet-decision-projection.d.ts +9 -0
- package/dist/timeline/fleet-decision-row.d.ts +11 -0
- package/dist/timeline/fold-memory.d.ts +15 -0
- package/dist/timeline/index.d.ts +18 -0
- package/dist/timeline/parsers.d.ts +96 -0
- package/dist/timeline/projection.d.ts +38 -0
- package/dist/timeline/registry.d.ts +40 -0
- package/dist/timeline/screenshot-lightbox.d.ts +26 -0
- package/dist/timeline/seen-activity-ids.d.ts +2 -0
- package/dist/timeline/shared.d.ts +125 -0
- package/dist/timeline/tool-diff.d.ts +8 -0
- package/dist/timeline/tool-display-name.d.ts +10 -0
- package/dist/timeline/tool-renderers.d.ts +5 -0
- package/dist/timeline/turn-summary.d.ts +116 -0
- package/dist/{use-turn-queue-Drm_ypue.d.ts → timeline/types.d.ts} +57 -67
- package/dist/types/machines.d.ts +11 -0
- package/package.json +8 -4
- package/src/client.ts +1 -0
- package/src/components/composer-transcription-control.tsx +278 -130
- package/src/components/composer.tsx +20 -15
- package/src/components/copy-button.tsx +127 -0
- package/src/components/markdown.tsx +231 -32
- package/src/components/message-timeline.tsx +1382 -378
- package/src/components/model-picker.tsx +83 -43
- package/src/components/queue-surface-implementation.tsx +1 -1
- package/src/components/session-chrome.tsx +942 -0
- package/src/components/soften-streaming-markdown.ts +205 -0
- package/src/components/stream-reveal.ts +195 -0
- package/src/components/tip-follow.ts +459 -0
- package/src/components/tooltip.tsx +103 -0
- package/src/composer.ts +6 -0
- package/src/hooks/internal.ts +30 -4
- package/src/hooks/use-available-models.ts +51 -4
- package/src/hooks/use-composer.ts +24 -18
- package/src/hooks/use-last-started-turn-policy.ts +95 -0
- package/src/hooks/use-session-events.ts +359 -5
- package/src/hooks/use-voice-input.ts +232 -0
- package/src/index.ts +49 -1
- package/src/lib/clipboard.ts +58 -0
- package/src/lib/format.ts +18 -0
- package/src/lib/motion-inspect.ts +5 -0
- package/src/lib/motion.ts +80 -0
- package/src/model-policy.ts +210 -0
- package/src/session-ui.ts +7 -0
- package/src/session.ts +2 -0
- package/src/timeline/activity-rail.tsx +46 -7
- package/src/timeline/entrance.tsx +10 -0
- package/src/timeline/fold-memory.ts +52 -0
- package/src/timeline/index.ts +11 -1
- package/src/timeline/projection.ts +252 -47
- package/src/timeline/seen-activity-ids.ts +19 -0
- package/src/timeline/shared.tsx +1 -1
- package/src/timeline/tool-renderers.tsx +75 -6
- package/src/timeline/turn-summary.tsx +387 -97
- package/src/timeline/types.ts +47 -0
- package/styles/index.css +99 -7
- package/styles/tokens.css +44 -3
- package/dist/chunk-4F2YLLHP.js.map +0 -1
- package/dist/chunk-M7X4JZOD.js.map +0 -1
- package/dist/chunk-OCHBZ2X7.js.map +0 -1
- package/dist/chunk-PMSGE233.js.map +0 -1
- package/dist/chunk-WAA247AB.js +0 -3858
- package/dist/chunk-WAA247AB.js.map +0 -1
- package/dist/chunk-XVVQP7A4.js.map +0 -1
- package/dist/composer-BMzimGTO.d.ts +0 -528
- package/dist/session-C3tIXLim.d.ts +0 -264
- package/dist/session-context-CAjlaeZb.d.ts +0 -84
- package/dist/session-ui-B_7cfN5u.d.ts +0 -259
- /package/dist/{chunk-BC6RKIHS.js.map → chunk-GZ4KR67U.js.map} +0 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import type { ClientVoiceInputConfig, OpenGeniClient } from "@opengeni/sdk";
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
|
+
import { appendFinalTranscript } from "./use-transcription";
|
|
4
|
+
|
|
5
|
+
export type VoiceInputStatus =
|
|
6
|
+
| "idle"
|
|
7
|
+
| "requesting-permission"
|
|
8
|
+
| "recording"
|
|
9
|
+
| "transcribing"
|
|
10
|
+
| "error";
|
|
11
|
+
|
|
12
|
+
export type UseVoiceInputOptions = {
|
|
13
|
+
client: Pick<OpenGeniClient, "transcribeAudio"> | null;
|
|
14
|
+
workspaceId: string;
|
|
15
|
+
capability: ClientVoiceInputConfig | null;
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
value: string;
|
|
18
|
+
setValue: (value: string) => void;
|
|
19
|
+
focusInput: () => void;
|
|
20
|
+
disabled?: boolean | undefined;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type UseVoiceInputResult = {
|
|
24
|
+
status: VoiceInputStatus;
|
|
25
|
+
error: string | null;
|
|
26
|
+
available: boolean;
|
|
27
|
+
/** Live mic stream while recording; null once stopped/cancelled. */
|
|
28
|
+
stream: MediaStream | null;
|
|
29
|
+
start: () => Promise<boolean>;
|
|
30
|
+
/** Stop capture and immediately upload/transcribe into the draft (no Send). */
|
|
31
|
+
stop: () => void;
|
|
32
|
+
/** Discard the in-flight recording or transcription; Escape also cancels. */
|
|
33
|
+
cancel: () => void;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const MIME_PREFERENCES = ["audio/webm;codecs=opus", "audio/mp4", "audio/ogg;codecs=opus"];
|
|
37
|
+
|
|
38
|
+
export function useVoiceInput({
|
|
39
|
+
client,
|
|
40
|
+
workspaceId,
|
|
41
|
+
capability,
|
|
42
|
+
enabled,
|
|
43
|
+
value,
|
|
44
|
+
setValue,
|
|
45
|
+
focusInput,
|
|
46
|
+
disabled = false,
|
|
47
|
+
}: UseVoiceInputOptions): UseVoiceInputResult {
|
|
48
|
+
const [status, setStatus] = useState<VoiceInputStatus>("idle");
|
|
49
|
+
const [error, setError] = useState<string | null>(null);
|
|
50
|
+
const [stream, setStream] = useState<MediaStream | null>(null);
|
|
51
|
+
const generationRef = useRef(0);
|
|
52
|
+
const recorderRef = useRef<MediaRecorder | null>(null);
|
|
53
|
+
const streamRef = useRef<MediaStream | null>(null);
|
|
54
|
+
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
55
|
+
const controllerRef = useRef<AbortController | null>(null);
|
|
56
|
+
const valueRef = useRef(value);
|
|
57
|
+
valueRef.current = value;
|
|
58
|
+
|
|
59
|
+
const clearRuntime = useCallback(() => {
|
|
60
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
61
|
+
timerRef.current = null;
|
|
62
|
+
streamRef.current?.getTracks().forEach((track) => track.stop());
|
|
63
|
+
streamRef.current = null;
|
|
64
|
+
recorderRef.current = null;
|
|
65
|
+
setStream(null);
|
|
66
|
+
}, []);
|
|
67
|
+
|
|
68
|
+
const cancel = useCallback(() => {
|
|
69
|
+
generationRef.current += 1;
|
|
70
|
+
controllerRef.current?.abort();
|
|
71
|
+
controllerRef.current = null;
|
|
72
|
+
const recorder = recorderRef.current;
|
|
73
|
+
clearRuntime();
|
|
74
|
+
if (recorder && recorder.state !== "inactive") recorder.stop();
|
|
75
|
+
setStatus("idle");
|
|
76
|
+
setError(null);
|
|
77
|
+
focusInput();
|
|
78
|
+
}, [clearRuntime, focusInput]);
|
|
79
|
+
|
|
80
|
+
const stop = () => {
|
|
81
|
+
const recorder = recorderRef.current;
|
|
82
|
+
if (!recorder || recorder.state === "inactive") return;
|
|
83
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
84
|
+
timerRef.current = null;
|
|
85
|
+
setStatus("transcribing");
|
|
86
|
+
recorder.stop();
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const start = async (): Promise<boolean> => {
|
|
90
|
+
if (
|
|
91
|
+
disabled ||
|
|
92
|
+
!client ||
|
|
93
|
+
!enabled ||
|
|
94
|
+
!capability?.available ||
|
|
95
|
+
status === "requesting-permission" ||
|
|
96
|
+
status === "recording" ||
|
|
97
|
+
status === "transcribing" ||
|
|
98
|
+
!navigator.mediaDevices?.getUserMedia ||
|
|
99
|
+
typeof MediaRecorder === "undefined"
|
|
100
|
+
) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
const voiceClient = client;
|
|
104
|
+
const generation = ++generationRef.current;
|
|
105
|
+
setStatus("requesting-permission");
|
|
106
|
+
setError(null);
|
|
107
|
+
try {
|
|
108
|
+
const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
109
|
+
if (generation !== generationRef.current) {
|
|
110
|
+
mediaStream.getTracks().forEach((track) => track.stop());
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
const mimeType = chooseMimeType(capability.acceptedMimeTypes);
|
|
114
|
+
const recorder = new MediaRecorder(mediaStream, mimeType ? { mimeType } : undefined);
|
|
115
|
+
const chunks: BlobPart[] = [];
|
|
116
|
+
const startedAt = Date.now();
|
|
117
|
+
streamRef.current = mediaStream;
|
|
118
|
+
recorderRef.current = recorder;
|
|
119
|
+
setStream(mediaStream);
|
|
120
|
+
recorder.ondataavailable = (event) => {
|
|
121
|
+
if (event.data.size > 0) chunks.push(event.data);
|
|
122
|
+
};
|
|
123
|
+
recorder.onstop = () => {
|
|
124
|
+
clearRuntime();
|
|
125
|
+
if (generation !== generationRef.current) return;
|
|
126
|
+
const audio = new Blob(chunks, { type: recorder.mimeType || mimeType || "audio/webm" });
|
|
127
|
+
if (audio.size === 0) {
|
|
128
|
+
setStatus("error");
|
|
129
|
+
setError("invalid_audio");
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const controller = new AbortController();
|
|
133
|
+
controllerRef.current = controller;
|
|
134
|
+
void voiceClient
|
|
135
|
+
.transcribeAudio(workspaceId, {
|
|
136
|
+
audio,
|
|
137
|
+
mimeType: audio.type,
|
|
138
|
+
durationSeconds: Math.max(0, (Date.now() - startedAt) / 1000),
|
|
139
|
+
signal: controller.signal,
|
|
140
|
+
})
|
|
141
|
+
.then((response) => {
|
|
142
|
+
if (generation !== generationRef.current || controller.signal.aborted) return;
|
|
143
|
+
const next = appendFinalTranscript(valueRef.current, response.text);
|
|
144
|
+
if (next !== valueRef.current) {
|
|
145
|
+
valueRef.current = next;
|
|
146
|
+
setValue(next);
|
|
147
|
+
}
|
|
148
|
+
setStatus("idle");
|
|
149
|
+
focusInput();
|
|
150
|
+
})
|
|
151
|
+
.catch((reason: unknown) => {
|
|
152
|
+
if (generation !== generationRef.current || controller.signal.aborted) return;
|
|
153
|
+
setStatus("error");
|
|
154
|
+
setError(errorCode(reason));
|
|
155
|
+
})
|
|
156
|
+
.finally(() => {
|
|
157
|
+
if (controllerRef.current === controller) controllerRef.current = null;
|
|
158
|
+
});
|
|
159
|
+
};
|
|
160
|
+
recorder.start();
|
|
161
|
+
setStatus("recording");
|
|
162
|
+
timerRef.current = setTimeout(
|
|
163
|
+
() => stop(),
|
|
164
|
+
Math.min(capability.maxDurationSeconds, 60) * 1000,
|
|
165
|
+
);
|
|
166
|
+
return true;
|
|
167
|
+
} catch (reason) {
|
|
168
|
+
if (generation !== generationRef.current) return false;
|
|
169
|
+
clearRuntime();
|
|
170
|
+
setStatus("error");
|
|
171
|
+
setError(errorCode(reason));
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
useEffect(() => {
|
|
177
|
+
const onKeyDown = (event: KeyboardEvent) => {
|
|
178
|
+
if (event.key === "Escape" && status !== "idle") {
|
|
179
|
+
event.preventDefault();
|
|
180
|
+
cancel();
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
document.addEventListener("keydown", onKeyDown);
|
|
184
|
+
return () => document.removeEventListener("keydown", onKeyDown);
|
|
185
|
+
}, [cancel, status]);
|
|
186
|
+
|
|
187
|
+
useEffect(
|
|
188
|
+
() => () => {
|
|
189
|
+
generationRef.current += 1;
|
|
190
|
+
controllerRef.current?.abort();
|
|
191
|
+
const recorder = recorderRef.current;
|
|
192
|
+
clearRuntime();
|
|
193
|
+
if (recorder && recorder.state !== "inactive") recorder.stop();
|
|
194
|
+
},
|
|
195
|
+
[clearRuntime],
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
status,
|
|
200
|
+
error,
|
|
201
|
+
available: Boolean(capability?.available && enabled && !disabled),
|
|
202
|
+
stream,
|
|
203
|
+
start,
|
|
204
|
+
stop,
|
|
205
|
+
cancel,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function chooseMimeType(accepted: string[]): string | undefined {
|
|
210
|
+
const normalized = accepted.map((value) => value.trim().toLowerCase());
|
|
211
|
+
return MIME_PREFERENCES.find((mimeType) => {
|
|
212
|
+
const bare = mimeType.split(";")[0] ?? mimeType;
|
|
213
|
+
const allowed =
|
|
214
|
+
normalized.includes(mimeType) ||
|
|
215
|
+
normalized.includes(bare) ||
|
|
216
|
+
normalized.some((candidate) => candidate.split(";")[0] === bare);
|
|
217
|
+
return (
|
|
218
|
+
allowed &&
|
|
219
|
+
(typeof MediaRecorder.isTypeSupported !== "function" ||
|
|
220
|
+
MediaRecorder.isTypeSupported(mimeType))
|
|
221
|
+
);
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function errorCode(error: unknown): string {
|
|
226
|
+
if (error && typeof error === "object" && "code" in error && typeof error.code === "string") {
|
|
227
|
+
return error.code;
|
|
228
|
+
}
|
|
229
|
+
if (error instanceof DOMException && error.name === "NotAllowedError") return "permission_denied";
|
|
230
|
+
if (error instanceof DOMException && error.name === "NotSupportedError") return "not_supported";
|
|
231
|
+
return "unknown";
|
|
232
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -49,6 +49,13 @@ export {
|
|
|
49
49
|
FILE_ONLY_MESSAGE_TEXT,
|
|
50
50
|
} from "./hooks/use-composer";
|
|
51
51
|
export type { ComposerSendExtras, ComposerState, UseComposerOptions } from "./hooks/use-composer";
|
|
52
|
+
export { useVoiceInput } from "./hooks/use-voice-input";
|
|
53
|
+
export type {
|
|
54
|
+
UseVoiceInputOptions,
|
|
55
|
+
UseVoiceInputResult,
|
|
56
|
+
VoiceInputStatus,
|
|
57
|
+
} from "./hooks/use-voice-input";
|
|
58
|
+
export { COMPOSER_PAYMENT_REQUIRED_MESSAGE, composerSubmissionErrorMessage } from "./lib/format";
|
|
52
59
|
export {
|
|
53
60
|
INITIAL_TRANSCRIPTION_CONTROL_STATE,
|
|
54
61
|
appendFinalTranscript,
|
|
@@ -74,8 +81,25 @@ export type {
|
|
|
74
81
|
UseTurnQueueOptions,
|
|
75
82
|
UseTurnQueueResult,
|
|
76
83
|
} from "./hooks/use-turn-queue";
|
|
84
|
+
export {
|
|
85
|
+
useLastStartedTurnPolicy,
|
|
86
|
+
isLastStartedTurnPolicyEvent,
|
|
87
|
+
} from "./hooks/use-last-started-turn-policy";
|
|
88
|
+
export type {
|
|
89
|
+
LastStartedTurnPolicy,
|
|
90
|
+
UseLastStartedTurnPolicyOptions,
|
|
91
|
+
UseLastStartedTurnPolicyResult,
|
|
92
|
+
} from "./hooks/use-last-started-turn-policy";
|
|
77
93
|
export { QueueSurface } from "./components/queue-surface";
|
|
78
94
|
export type { QueueSurfaceProps } from "./components/queue-surface";
|
|
95
|
+
export { SessionChrome, sessionChromeGoalPillState } from "./components/session-chrome";
|
|
96
|
+
export type {
|
|
97
|
+
SessionChromeAgentsSignal,
|
|
98
|
+
SessionChromeProps,
|
|
99
|
+
SessionChromeSignalId,
|
|
100
|
+
SessionChromeSignalTone,
|
|
101
|
+
} from "./components/session-chrome";
|
|
102
|
+
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./components/tooltip";
|
|
79
103
|
export { OPEN_WORKSTREAM_CONTROL_EVENT } from "./components/chat-composer";
|
|
80
104
|
export { useGoal, isGoalEvent } from "./hooks/use-goal";
|
|
81
105
|
export type { UseGoalOptions, UseGoalResult } from "./hooks/use-goal";
|
|
@@ -131,10 +155,12 @@ export { useWorkspaces } from "./hooks/use-workspaces";
|
|
|
131
155
|
export type { UseWorkspacesOptions, UseWorkspacesResult } from "./hooks/use-workspaces";
|
|
132
156
|
export { useBillingUsage } from "./hooks/use-billing-usage";
|
|
133
157
|
export type { UseBillingUsageOptions, UseBillingUsageResult } from "./hooks/use-billing-usage";
|
|
134
|
-
export { useAvailableModels } from "./hooks/use-available-models";
|
|
158
|
+
export { useAvailableModels, useWorkspaceModelCatalog } from "./hooks/use-available-models";
|
|
135
159
|
export type {
|
|
136
160
|
UseAvailableModelsOptions,
|
|
137
161
|
UseAvailableModelsResult,
|
|
162
|
+
UseWorkspaceModelCatalogOptions,
|
|
163
|
+
UseWorkspaceModelCatalogResult,
|
|
138
164
|
} from "./hooks/use-available-models";
|
|
139
165
|
|
|
140
166
|
// Sandbox surfacing (Phase 5): capability negotiation + terminal/files/diff/desktop
|
|
@@ -244,7 +270,10 @@ export type {
|
|
|
244
270
|
ActivityItem,
|
|
245
271
|
AgentMessageItem,
|
|
246
272
|
AuthNeededItem,
|
|
273
|
+
ContextCompactionItem,
|
|
247
274
|
GoalItem,
|
|
275
|
+
MachineInputBatchItem,
|
|
276
|
+
MachineInputMember,
|
|
248
277
|
NoticeItem,
|
|
249
278
|
ReasoningItem,
|
|
250
279
|
SandboxItem,
|
|
@@ -364,9 +393,28 @@ export { defaultChatComposerMessages } from "./components/composer";
|
|
|
364
393
|
export type { ChatComposerMessages } from "./components/composer";
|
|
365
394
|
export { ModelPicker } from "./components/model-picker";
|
|
366
395
|
export type { ModelPickerProps } from "./components/model-picker";
|
|
396
|
+
export {
|
|
397
|
+
advancedSourceSummary,
|
|
398
|
+
availabilityReasonLabel,
|
|
399
|
+
billingClassForModel,
|
|
400
|
+
billingClassLabel,
|
|
401
|
+
coerceReasoningEffortForModel,
|
|
402
|
+
defaultEffortForModel,
|
|
403
|
+
effortOptionsForModel,
|
|
404
|
+
findPickerRow,
|
|
405
|
+
groupPickerRowsByBillingClass,
|
|
406
|
+
labelLatencyMode,
|
|
407
|
+
payerSummaryForModel,
|
|
408
|
+
projectPickerRows,
|
|
409
|
+
runnableLatencyModesForModel,
|
|
410
|
+
sortPickerRows,
|
|
411
|
+
} from "./model-policy";
|
|
412
|
+
export type { LatencyModeId, PickerBillingClass, PickerModelRow } from "./model-policy";
|
|
367
413
|
export { MessageTimeline, TimelineRow } from "./components/message-timeline";
|
|
368
414
|
export type { MessageTimelineProps } from "./components/message-timeline";
|
|
369
415
|
export { Markdown } from "./components/markdown";
|
|
416
|
+
export { CopyButton, CopyHoverFrame } from "./components/copy-button";
|
|
417
|
+
export { copyTextToClipboard, tableElementToTsv } from "./lib/clipboard";
|
|
370
418
|
export type { MarkdownProps } from "./components/markdown";
|
|
371
419
|
export { SessionStatus, StatusDot, SESSION_STATUS_META } from "./components/session-status";
|
|
372
420
|
export type {
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copy plain text to the clipboard. Prefers the async Clipboard API; falls
|
|
3
|
+
* back to a short-lived textarea for older embeds / denied permissions.
|
|
4
|
+
*/
|
|
5
|
+
export async function copyTextToClipboard(text: string): Promise<boolean> {
|
|
6
|
+
const value = text.replace(/\u00a0/g, " ");
|
|
7
|
+
if (value.length === 0) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
|
|
12
|
+
await navigator.clipboard.writeText(value);
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
} catch {
|
|
16
|
+
// Fall through to execCommand path.
|
|
17
|
+
}
|
|
18
|
+
if (typeof document === "undefined") {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const area = document.createElement("textarea");
|
|
23
|
+
area.value = value;
|
|
24
|
+
area.setAttribute("readonly", "");
|
|
25
|
+
area.style.position = "fixed";
|
|
26
|
+
area.style.left = "-9999px";
|
|
27
|
+
area.style.top = "0";
|
|
28
|
+
document.body.appendChild(area);
|
|
29
|
+
area.select();
|
|
30
|
+
const ok = document.execCommand("copy");
|
|
31
|
+
document.body.removeChild(area);
|
|
32
|
+
return ok;
|
|
33
|
+
} catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Serialize an HTML table to tab-separated values (spreadsheet-friendly). */
|
|
39
|
+
export function tableElementToTsv(table: HTMLTableElement | null | undefined): string {
|
|
40
|
+
if (!table) {
|
|
41
|
+
return "";
|
|
42
|
+
}
|
|
43
|
+
const rows = Array.from(table.querySelectorAll("tr"));
|
|
44
|
+
return rows
|
|
45
|
+
.map((row) =>
|
|
46
|
+
Array.from(row.querySelectorAll("th,td"))
|
|
47
|
+
.map((cell) => {
|
|
48
|
+
const raw = (cell.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
49
|
+
if (/[\t\n"]/.test(raw)) {
|
|
50
|
+
return `"${raw.replace(/"/g, '""')}"`;
|
|
51
|
+
}
|
|
52
|
+
return raw;
|
|
53
|
+
})
|
|
54
|
+
.join("\t"),
|
|
55
|
+
)
|
|
56
|
+
.filter((line) => line.length > 0)
|
|
57
|
+
.join("\n");
|
|
58
|
+
}
|
package/src/lib/format.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { OpenGeniApiError } from "@opengeni/sdk";
|
|
2
|
+
|
|
1
3
|
/** Compact relative time: "now", "42s", "7m", "3h", "2d", then a date. */
|
|
2
4
|
export function formatRelativeTime(iso: string, now: Date = new Date()): string {
|
|
3
5
|
const then = new Date(iso).getTime();
|
|
@@ -93,6 +95,22 @@ export function tryParseJson(text: string): unknown {
|
|
|
93
95
|
export const CREDIT_EXHAUSTION_MESSAGE =
|
|
94
96
|
"Out of OpenGeni credits — this workspace's balance is empty. Add credits to continue; the conversation is preserved.";
|
|
95
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Actionable composer copy for an edge rejection before a turn is accepted.
|
|
100
|
+
* Unlike an in-flight credit exhaustion, this path has not consumed the
|
|
101
|
+
* actor-private draft or any finalized attachment.
|
|
102
|
+
*/
|
|
103
|
+
export const COMPOSER_PAYMENT_REQUIRED_MESSAGE =
|
|
104
|
+
"This turn requires OpenGeni managed credits, but the account balance is empty. Add credits or choose a connected Codex subscription model, then retry. Your draft and attachments are preserved.";
|
|
105
|
+
|
|
106
|
+
export function composerSubmissionErrorMessage(error: Error): string {
|
|
107
|
+
return error instanceof OpenGeniApiError &&
|
|
108
|
+
error.status === 402 &&
|
|
109
|
+
error.code === "payment_required"
|
|
110
|
+
? COMPOSER_PAYMENT_REQUIRED_MESSAGE
|
|
111
|
+
: error.message;
|
|
112
|
+
}
|
|
113
|
+
|
|
96
114
|
/**
|
|
97
115
|
* Does this failure/completion payload (or raw error string) mean the
|
|
98
116
|
* workspace ran out of OpenGeni credits? Matches the engine's
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { flushSync } from "react-dom";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Live `prefers-reduced-motion` check for JS-driven motion (scroll glides,
|
|
5
|
+
* streaming word reveals). CSS animations are already neutralized globally in
|
|
6
|
+
* styles/index.css; this lets the imperative paths skip their work entirely.
|
|
7
|
+
*/
|
|
8
|
+
let query: MediaQueryList | null | undefined;
|
|
9
|
+
|
|
10
|
+
export function prefersReducedMotion(): boolean {
|
|
11
|
+
if (query === undefined) {
|
|
12
|
+
query =
|
|
13
|
+
typeof window !== "undefined" && typeof window.matchMedia === "function"
|
|
14
|
+
? window.matchMedia("(prefers-reduced-motion: reduce)")
|
|
15
|
+
: null;
|
|
16
|
+
}
|
|
17
|
+
return query?.matches ?? false;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Test seam: forget the cached MediaQueryList so a stubbed matchMedia applies. */
|
|
21
|
+
export function resetReducedMotionCache(): void {
|
|
22
|
+
query = undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type ViewTransitionResult = { finished: Promise<void> };
|
|
26
|
+
type ViewTransitionCallback = () => void;
|
|
27
|
+
type ViewTransitionOptions = {
|
|
28
|
+
update: ViewTransitionCallback;
|
|
29
|
+
types?: string[];
|
|
30
|
+
};
|
|
31
|
+
type ViewTransitionStarter = {
|
|
32
|
+
(callback: ViewTransitionCallback): ViewTransitionResult;
|
|
33
|
+
(options: ViewTransitionOptions): ViewTransitionResult;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/** Active VT type for markdown crystallize — scoped CSS, not a full-page blink. */
|
|
37
|
+
export const MARKDOWN_CRYSTALLIZE_VT_TYPE = "og-md-crystallize";
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Run a DOM update inside the browser View Transition API when available.
|
|
41
|
+
* Snapshots the before/after paint and cross-fades (or custom CSS morphs)
|
|
42
|
+
* between them — the platform "morph" primitive React's experimental
|
|
43
|
+
* `<ViewTransition>` wraps. Falls back to a synchronous update.
|
|
44
|
+
*/
|
|
45
|
+
export function runViewTransition(update: () => void, options?: { types?: string[] }): void {
|
|
46
|
+
if (prefersReducedMotion()) {
|
|
47
|
+
update();
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const doc = typeof document !== "undefined" ? document : null;
|
|
51
|
+
const start = doc
|
|
52
|
+
? ((doc as Document & { startViewTransition?: ViewTransitionStarter }).startViewTransition ??
|
|
53
|
+
null)
|
|
54
|
+
: null;
|
|
55
|
+
if (typeof start !== "function") {
|
|
56
|
+
update();
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const types = options?.types;
|
|
60
|
+
if (types && types.length > 0) {
|
|
61
|
+
try {
|
|
62
|
+
Reflect.apply(start, doc, [
|
|
63
|
+
{
|
|
64
|
+
update: () => {
|
|
65
|
+
flushSync(update);
|
|
66
|
+
},
|
|
67
|
+
types,
|
|
68
|
+
},
|
|
69
|
+
]);
|
|
70
|
+
return;
|
|
71
|
+
} catch {
|
|
72
|
+
// Older engines only accept the callback form.
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
Reflect.apply(start, doc, [
|
|
76
|
+
() => {
|
|
77
|
+
flushSync(update);
|
|
78
|
+
},
|
|
79
|
+
]);
|
|
80
|
+
}
|