@opengeni/react 0.15.0 → 0.20.0
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/README.md +110 -10
- package/dist/{chunk-TOJR776I.js → chunk-6BXBGJ3B.js} +45 -369
- package/dist/chunk-6BXBGJ3B.js.map +1 -0
- package/dist/chunk-6UT5OC3P.js +2133 -0
- package/dist/chunk-6UT5OC3P.js.map +1 -0
- package/dist/chunk-EBTT3MYN.js +92 -0
- package/dist/chunk-EBTT3MYN.js.map +1 -0
- package/dist/chunk-HHFA4AFX.js +425 -0
- package/dist/chunk-HHFA4AFX.js.map +1 -0
- package/dist/chunk-LAZ2A743.js +2393 -0
- package/dist/chunk-LAZ2A743.js.map +1 -0
- package/dist/chunk-MRSECXRP.js +563 -0
- package/dist/chunk-MRSECXRP.js.map +1 -0
- package/dist/chunk-TK7G6XLT.js +18 -0
- package/dist/chunk-TK7G6XLT.js.map +1 -0
- package/dist/composer-DvUaa5ki.d.ts +585 -0
- package/dist/composer.d.ts +6 -0
- package/dist/composer.js +56 -0
- package/dist/composer.js.map +1 -0
- package/dist/index.d.ts +1288 -1785
- package/dist/index.js +7015 -8806
- package/dist/index.js.map +1 -1
- package/dist/machines.d.ts +412 -3
- package/dist/machines.js +3 -1
- package/dist/noto-sans-arabic-loader-IA2T4X2A.js +21 -0
- package/dist/noto-sans-arabic-loader-IA2T4X2A.js.map +1 -0
- package/dist/noto-sans-jp-loader.generated-HFGLYBR3.js +292 -0
- package/dist/noto-sans-jp-loader.generated-HFGLYBR3.js.map +1 -0
- package/dist/queue-surface-implementation-NQMV2ML3.js +890 -0
- package/dist/queue-surface-implementation-NQMV2ML3.js.map +1 -0
- package/dist/session-Gd4iVPYw.d.ts +425 -0
- package/dist/session-context-DIFFlXKc.d.ts +52 -0
- package/dist/session.d.ts +4 -0
- package/dist/session.js +50 -0
- package/dist/session.js.map +1 -0
- package/dist/use-composer-BCfm4mzX.d.ts +112 -0
- package/package.json +20 -4
- package/src/approvals.ts +5 -3
- package/src/client.ts +40 -0
- package/src/components/approval-surface.tsx +198 -0
- package/src/components/chat-composer.tsx +94 -993
- package/src/components/code-editor.tsx +104 -26
- package/src/components/command-palette.tsx +8 -2
- package/src/components/composer-transcription-control.tsx +211 -0
- package/src/components/composer.tsx +1536 -0
- package/src/components/desktop-viewer.tsx +3 -3
- package/src/components/enrollment-consent.tsx +2 -2
- package/src/components/file-browser.tsx +367 -29
- package/src/components/human-input-form.tsx +417 -0
- package/src/components/machine-card.tsx +1 -1
- package/src/components/machines-dashboard.tsx +3 -3
- package/src/components/message-timeline.tsx +15 -7
- package/src/components/model-picker.tsx +12 -3
- package/src/components/pierre-diff.tsx +32 -6
- package/src/components/queue-surface-implementation.tsx +1041 -0
- package/src/components/queue-surface-state.tsx +94 -0
- package/src/components/queue-surface.tsx +41 -559
- package/src/components/sandbox-files.tsx +138 -14
- package/src/components/sandbox-terminal.tsx +8 -2
- package/src/components/sandbox-workspace.tsx +339 -119
- package/src/components/workbench-changes.tsx +84 -33
- package/src/components/workspace-dock.tsx +188 -18
- package/src/composer.ts +60 -0
- package/src/hooks/internal.ts +115 -34
- package/src/hooks/use-composer.ts +335 -104
- package/src/hooks/use-human-input.ts +131 -0
- package/src/hooks/use-machine-chip.ts +2 -2
- package/src/hooks/use-machines.ts +27 -11
- package/src/hooks/use-sandbox-files.ts +310 -62
- package/src/hooks/use-sandbox-git.ts +154 -40
- package/src/hooks/use-sandbox-terminal.ts +28 -6
- package/src/hooks/use-session-capabilities.ts +38 -9
- package/src/hooks/use-session-control.ts +37 -8
- package/src/hooks/use-session-events.ts +522 -48
- package/src/hooks/use-slash-commands.ts +38 -38
- package/src/hooks/use-transcription.ts +757 -0
- package/src/hooks/use-turn-queue.ts +134 -66
- package/src/hooks/use-windowed-sections.ts +2 -2
- package/src/hooks/use-workspace-capture.ts +146 -40
- package/src/hooks/use-workspace-edit.ts +50 -14
- package/src/hooks/use-workspace-sessions.ts +3 -0
- package/src/human-input.ts +72 -0
- package/src/index.ts +76 -6
- package/src/lib/noto-sans-arabic-loader.ts +21 -0
- package/src/lib/noto-sans-jp-loader.generated.ts +295 -0
- package/src/lib/use-portal-token-style.ts +51 -0
- package/src/lib/use-unicode-fonts.ts +57 -0
- package/src/provider.tsx +4 -69
- package/src/session-context.ts +135 -0
- package/src/session.ts +79 -0
- package/src/timeline/parsers.ts +35 -0
- package/src/timeline/projection.ts +12 -4
- package/src/timeline/tool-renderers.tsx +41 -0
- package/src/timeline/turn-summary.tsx +2 -2
- package/src/timeline/types.ts +16 -8
- package/styles/index.css +56 -0
- package/styles/index.d.ts +2 -0
- package/styles/tokens.css +11 -6
- package/styles/tokens.d.ts +2 -0
- package/dist/chunk-TOJR776I.js.map +0 -1
- package/dist/machines-BpdwuQcD.d.ts +0 -449
|
@@ -2,8 +2,8 @@ import { useCallback, useEffect, useRef, useState } from "react";
|
|
|
2
2
|
import { useOpenGeni, type ClientOverride } from "../provider";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* The wake-on-edit state machine
|
|
6
|
-
*
|
|
5
|
+
* The wake-on-edit state machine. This hook owns the logic and the UI renders
|
|
6
|
+
* its result. States are explicit and exhaustive:
|
|
7
7
|
*
|
|
8
8
|
* viewing-cold — cold snapshot open, no local edits (editable, cloud).
|
|
9
9
|
* buffering — local edits buffered; the box is not warm and no warm is
|
|
@@ -80,11 +80,11 @@ function isLive(liveness: string | undefined): boolean {
|
|
|
80
80
|
* Cold-editing without a spinner: the editor opens instantly from the capture,
|
|
81
81
|
* the first keystroke buffers locally AND signals the host to warm the box, and on
|
|
82
82
|
* warm the buffer flushes IF the live file still matches the captured base — else a
|
|
83
|
-
* non-blocking conflict is surfaced and nothing is overwritten
|
|
83
|
+
* non-blocking conflict is surfaced and nothing is overwritten.
|
|
84
84
|
*
|
|
85
85
|
* The base guard compares the live content at flush time against `baseContent`
|
|
86
86
|
* (the exact bytes the editor loaded). This is equivalent to the sha256 compare
|
|
87
|
-
* the
|
|
87
|
+
* the design specifies — M1 leaves `baseHash` null, so the loaded content IS the
|
|
88
88
|
* authoritative base — and avoids async Web Crypto in the render path.
|
|
89
89
|
*/
|
|
90
90
|
export function useWorkspaceEdit(
|
|
@@ -96,31 +96,48 @@ export function useWorkspaceEdit(
|
|
|
96
96
|
const baseContent = options.baseContent ?? null;
|
|
97
97
|
const live = isLive(options.liveness);
|
|
98
98
|
const offline = options.offline === true;
|
|
99
|
+
const identityKey = `${workspaceId}\u0000${sessionId ?? ""}\u0000${path ?? ""}`;
|
|
99
100
|
|
|
100
101
|
const [buffer, setBuffer] = useState<string | null>(null);
|
|
101
102
|
const [wantsWarm, setWantsWarm] = useState(false);
|
|
102
103
|
const [flush, setFlush] = useState<"idle" | "flushing" | "flushed" | "conflict">("idle");
|
|
103
104
|
const [conflict, setConflict] = useState<WorkspaceEditConflict | null>(null);
|
|
104
105
|
const [error, setError] = useState<Error | null>(null);
|
|
106
|
+
const [stateIdentity, setStateIdentity] = useState(identityKey);
|
|
105
107
|
|
|
106
108
|
const onWarmRequested = options.onWarmRequested;
|
|
107
109
|
const bufferRef = useRef<string | null>(null);
|
|
108
110
|
bufferRef.current = buffer;
|
|
109
111
|
// Guards a single in-flight flush against re-entry (liveness can re-render).
|
|
110
112
|
const flushingRef = useRef(false);
|
|
113
|
+
const identityGenerationRef = useRef(0);
|
|
114
|
+
const flushAttemptRef = useRef(0);
|
|
115
|
+
const readAbortRef = useRef<AbortController | null>(null);
|
|
111
116
|
|
|
112
117
|
// Reset the machine when the edited file (or session) changes.
|
|
113
118
|
useEffect(() => {
|
|
119
|
+
identityGenerationRef.current += 1;
|
|
120
|
+
flushAttemptRef.current += 1;
|
|
121
|
+
readAbortRef.current?.abort();
|
|
122
|
+
readAbortRef.current = null;
|
|
123
|
+
setStateIdentity(identityKey);
|
|
114
124
|
setBuffer(null);
|
|
115
125
|
setWantsWarm(false);
|
|
116
126
|
setFlush("idle");
|
|
117
127
|
setConflict(null);
|
|
118
128
|
setError(null);
|
|
119
129
|
flushingRef.current = false;
|
|
120
|
-
|
|
130
|
+
return () => {
|
|
131
|
+
identityGenerationRef.current += 1;
|
|
132
|
+
flushAttemptRef.current += 1;
|
|
133
|
+
readAbortRef.current?.abort();
|
|
134
|
+
readAbortRef.current = null;
|
|
135
|
+
};
|
|
136
|
+
}, [identityKey]);
|
|
121
137
|
|
|
122
138
|
const edit = useCallback(
|
|
123
139
|
(content: string) => {
|
|
140
|
+
if (stateIdentity !== identityKey) return;
|
|
124
141
|
if (offline) return; // read-only — no buffering, no wake.
|
|
125
142
|
setBuffer((prev) => (prev === content ? prev : content));
|
|
126
143
|
// Any new edit invalidates a prior flushed/conflict resolution.
|
|
@@ -134,15 +151,18 @@ export function useWorkspaceEdit(
|
|
|
134
151
|
});
|
|
135
152
|
}
|
|
136
153
|
},
|
|
137
|
-
[offline, live, onWarmRequested],
|
|
154
|
+
[offline, live, onWarmRequested, stateIdentity, identityKey],
|
|
138
155
|
);
|
|
139
156
|
|
|
140
157
|
const doFlush = useCallback(
|
|
141
158
|
async (force: boolean) => {
|
|
159
|
+
if (stateIdentity !== identityKey) return;
|
|
142
160
|
if (!sessionId || !path) return;
|
|
143
161
|
const content = bufferRef.current;
|
|
144
162
|
if (content === null) return;
|
|
145
163
|
if (flushingRef.current) return;
|
|
164
|
+
const identityGeneration = identityGenerationRef.current;
|
|
165
|
+
const flushAttempt = (flushAttemptRef.current += 1);
|
|
146
166
|
flushingRef.current = true;
|
|
147
167
|
setFlush("flushing");
|
|
148
168
|
setError(null);
|
|
@@ -151,7 +171,17 @@ export function useWorkspaceEdit(
|
|
|
151
171
|
// Base guard: re-read the live file and compare to the captured base. A
|
|
152
172
|
// divergence means the box changed under us — surface a conflict, write
|
|
153
173
|
// NOTHING (C2).
|
|
154
|
-
const
|
|
174
|
+
const readAbort = new AbortController();
|
|
175
|
+
readAbortRef.current?.abort();
|
|
176
|
+
readAbortRef.current = readAbort;
|
|
177
|
+
const liveRead = await client.fsRead(
|
|
178
|
+
workspaceId,
|
|
179
|
+
sessionId,
|
|
180
|
+
{ path },
|
|
181
|
+
{ signal: readAbort.signal },
|
|
182
|
+
);
|
|
183
|
+
if (readAbortRef.current === readAbort) readAbortRef.current = null;
|
|
184
|
+
if (identityGenerationRef.current !== identityGeneration) return;
|
|
155
185
|
const liveContent = liveRead.content;
|
|
156
186
|
if (baseContent !== null && liveContent !== baseContent) {
|
|
157
187
|
setConflict({ path, base: baseContent, live: liveContent });
|
|
@@ -160,18 +190,23 @@ export function useWorkspaceEdit(
|
|
|
160
190
|
}
|
|
161
191
|
}
|
|
162
192
|
await client.fsWrite(workspaceId, sessionId, { path, content, overwrite: true });
|
|
193
|
+
if (identityGenerationRef.current !== identityGeneration) return;
|
|
163
194
|
setConflict(null);
|
|
164
195
|
setFlush("flushed");
|
|
165
196
|
setWantsWarm(false);
|
|
166
197
|
} catch (cause) {
|
|
198
|
+
if (identityGenerationRef.current !== identityGeneration) return;
|
|
167
199
|
setError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
168
200
|
// Leave the buffer intact so the user can retry; fall back to buffering.
|
|
169
201
|
setFlush("idle");
|
|
170
202
|
} finally {
|
|
171
|
-
|
|
203
|
+
if (flushAttemptRef.current === flushAttempt) {
|
|
204
|
+
flushingRef.current = false;
|
|
205
|
+
readAbortRef.current = null;
|
|
206
|
+
}
|
|
172
207
|
}
|
|
173
208
|
},
|
|
174
|
-
[client, workspaceId, sessionId, path, baseContent],
|
|
209
|
+
[client, workspaceId, sessionId, path, baseContent, stateIdentity, identityKey],
|
|
175
210
|
);
|
|
176
211
|
|
|
177
212
|
// Auto-flush once the box is warm and a buffer is pending (the wake completed).
|
|
@@ -217,15 +252,16 @@ export function useWorkspaceEdit(
|
|
|
217
252
|
state = "buffering";
|
|
218
253
|
}
|
|
219
254
|
|
|
255
|
+
const identityMatches = stateIdentity === identityKey;
|
|
220
256
|
return {
|
|
221
|
-
state,
|
|
257
|
+
state: identityMatches ? state : offline ? "readonly-offline" : "viewing-cold",
|
|
222
258
|
readOnly: offline,
|
|
223
|
-
buffer,
|
|
224
|
-
wantsWarm,
|
|
259
|
+
buffer: identityMatches ? buffer : null,
|
|
260
|
+
wantsWarm: identityMatches && wantsWarm,
|
|
225
261
|
edit,
|
|
226
|
-
conflict,
|
|
262
|
+
conflict: identityMatches ? conflict : null,
|
|
227
263
|
overwrite,
|
|
228
264
|
discard,
|
|
229
|
-
error,
|
|
265
|
+
error: identityMatches ? error : null,
|
|
230
266
|
};
|
|
231
267
|
}
|
|
@@ -21,6 +21,8 @@ export type UseWorkspaceSessionsResult = {
|
|
|
21
21
|
sessions: Session[];
|
|
22
22
|
/** The complete personal pinned section, also present in `sessions`. */
|
|
23
23
|
pinned: Session[];
|
|
24
|
+
/** True when the server omitted older pins from its bounded pinned section. */
|
|
25
|
+
pinnedTruncated: boolean;
|
|
24
26
|
nextCursor: string | null;
|
|
25
27
|
loading: boolean;
|
|
26
28
|
error: Error | null;
|
|
@@ -65,6 +67,7 @@ export function useWorkspaceSessions(
|
|
|
65
67
|
// itself continues to paginate only `ordinary` rows via nextCursor.
|
|
66
68
|
sessions: [...pinned, ...ordinary],
|
|
67
69
|
pinned,
|
|
70
|
+
pinnedTruncated: page?.pinnedTruncated ?? false,
|
|
68
71
|
nextCursor: page?.nextCursor ?? null,
|
|
69
72
|
loading: state.loading,
|
|
70
73
|
error: state.error,
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { HumanInputQuestion, SessionEvent } from "@opengeni/sdk";
|
|
2
|
+
|
|
3
|
+
/** Minimal actionable request shape available from the durable event log. */
|
|
4
|
+
export type PendingHumanInputRequest = {
|
|
5
|
+
id: string;
|
|
6
|
+
turnId: string | null;
|
|
7
|
+
questions: HumanInputQuestion[];
|
|
8
|
+
allowSkip: boolean;
|
|
9
|
+
expiresAt: string | null;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/** Parse one generic `session.humanInput.requested` event defensively. */
|
|
13
|
+
export function humanInputRequestFromEvent(
|
|
14
|
+
event: Pick<SessionEvent, "type" | "payload" | "turnId">,
|
|
15
|
+
): PendingHumanInputRequest | null {
|
|
16
|
+
if (event.type !== "session.humanInput.requested" || !isRecord(event.payload)) return null;
|
|
17
|
+
const request = event.payload.request;
|
|
18
|
+
if (!isRecord(request) || typeof request.id !== "string" || !Array.isArray(request.questions)) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
id: request.id,
|
|
23
|
+
turnId: event.turnId ?? null,
|
|
24
|
+
questions: request.questions as HumanInputQuestion[],
|
|
25
|
+
allowSkip: request.allowSkip === true,
|
|
26
|
+
expiresAt: typeof request.expiresAt === "string" ? request.expiresAt : null,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Fold a durable event log into the structured requests that are actionable
|
|
32
|
+
* now. Responses remove one request; a terminal owning turn clears any
|
|
33
|
+
* unresolved requests that died with it. Replaying history cannot resurrect a
|
|
34
|
+
* previously answered card.
|
|
35
|
+
*/
|
|
36
|
+
export function projectPendingHumanInputRequests(
|
|
37
|
+
events: SessionEvent[],
|
|
38
|
+
): PendingHumanInputRequest[] {
|
|
39
|
+
const pending = new Map<string, PendingHumanInputRequest>();
|
|
40
|
+
for (const event of events) {
|
|
41
|
+
const requested = humanInputRequestFromEvent(event);
|
|
42
|
+
if (requested) {
|
|
43
|
+
pending.set(requested.id, requested);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (event.type === "user.humanInputResponse" && isRecord(event.payload)) {
|
|
47
|
+
if (typeof event.payload.requestId === "string") pending.delete(event.payload.requestId);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (
|
|
51
|
+
event.type === "turn.completed" ||
|
|
52
|
+
event.type === "turn.failed" ||
|
|
53
|
+
event.type === "turn.cancelled"
|
|
54
|
+
) {
|
|
55
|
+
for (const [id, request] of pending) {
|
|
56
|
+
if (
|
|
57
|
+
request.turnId === null ||
|
|
58
|
+
event.turnId === null ||
|
|
59
|
+
event.turnId === undefined ||
|
|
60
|
+
request.turnId === event.turnId
|
|
61
|
+
) {
|
|
62
|
+
pending.delete(id);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return [...pending.values()];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
71
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
72
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -7,15 +7,30 @@
|
|
|
7
7
|
// @import "@opengeni/react/styles.css";
|
|
8
8
|
// @source "../node_modules/@opengeni/react/src";
|
|
9
9
|
|
|
10
|
-
export type {
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
export type {
|
|
11
|
+
EmbeddedHumanInputSessionClientLike,
|
|
12
|
+
EmbeddedSessionClientLike,
|
|
13
|
+
SessionClientLike,
|
|
14
|
+
} from "./client";
|
|
15
|
+
export { OpenGeniProvider } from "./provider";
|
|
16
|
+
export type { OpenGeniProviderProps } from "./provider";
|
|
17
|
+
export { useOpenGeni, useOpenGeniClient } from "./session-context";
|
|
18
|
+
export type { ClientOverride, OpenGeniContextValue } from "./session-context";
|
|
13
19
|
|
|
14
20
|
// Hooks
|
|
15
21
|
export { useSession, isTitleEvent } from "./hooks/use-session";
|
|
16
22
|
export type { UseSessionOptions, UseSessionResult } from "./hooks/use-session";
|
|
17
|
-
export {
|
|
23
|
+
export {
|
|
24
|
+
SESSION_EVENT_BROWSER_MAX_BYTES,
|
|
25
|
+
SESSION_EVENT_BROWSER_MAX_COUNT,
|
|
26
|
+
SESSION_EVENT_BROWSER_PENDING_MAX_BYTES,
|
|
27
|
+
SESSION_EVENT_BROWSER_PENDING_MAX_COUNT,
|
|
28
|
+
SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES,
|
|
29
|
+
boundBrowserSessionEventWindow,
|
|
30
|
+
useSessionEvents,
|
|
31
|
+
} from "./hooks/use-session-events";
|
|
18
32
|
export type {
|
|
33
|
+
BrowserSessionEventWindow,
|
|
19
34
|
SessionEventsConnectionState,
|
|
20
35
|
UseSessionEventsOptions,
|
|
21
36
|
UseSessionEventsResult,
|
|
@@ -28,6 +43,19 @@ export {
|
|
|
28
43
|
FILE_ONLY_MESSAGE_TEXT,
|
|
29
44
|
} from "./hooks/use-composer";
|
|
30
45
|
export type { ComposerSendExtras, ComposerState, UseComposerOptions } from "./hooks/use-composer";
|
|
46
|
+
export {
|
|
47
|
+
INITIAL_TRANSCRIPTION_CONTROL_STATE,
|
|
48
|
+
appendFinalTranscript,
|
|
49
|
+
transitionTranscriptionControl,
|
|
50
|
+
useTranscription,
|
|
51
|
+
} from "./hooks/use-transcription";
|
|
52
|
+
export type {
|
|
53
|
+
TranscriptionControlAction,
|
|
54
|
+
TranscriptionControlState,
|
|
55
|
+
TranscriptionControlTransition,
|
|
56
|
+
UseTranscriptionOptions,
|
|
57
|
+
UseTranscriptionResult,
|
|
58
|
+
} from "./hooks/use-transcription";
|
|
31
59
|
export { useFileAttachments } from "./hooks/use-file-attachments";
|
|
32
60
|
export type {
|
|
33
61
|
FileAttachment,
|
|
@@ -123,15 +151,25 @@ export type {
|
|
|
123
151
|
UseSandboxTerminalOptions,
|
|
124
152
|
UseSandboxTerminalResult,
|
|
125
153
|
} from "./hooks/use-sandbox-terminal";
|
|
126
|
-
export {
|
|
154
|
+
export {
|
|
155
|
+
CapturedFileUnavailableError,
|
|
156
|
+
FileWriteConflictError,
|
|
157
|
+
useSandboxFiles,
|
|
158
|
+
} from "./hooks/use-sandbox-files";
|
|
127
159
|
export type {
|
|
160
|
+
CapturedFileUnavailableReason,
|
|
128
161
|
FileTreeNode,
|
|
129
162
|
FileTreeStatus,
|
|
163
|
+
SandboxWriteFileOptions,
|
|
130
164
|
UseSandboxFilesOptions,
|
|
131
165
|
UseSandboxFilesResult,
|
|
132
166
|
} from "./hooks/use-sandbox-files";
|
|
133
167
|
export { useSandboxGit } from "./hooks/use-sandbox-git";
|
|
134
|
-
export type {
|
|
168
|
+
export type {
|
|
169
|
+
SandboxGitFileDiff,
|
|
170
|
+
UseSandboxGitOptions,
|
|
171
|
+
UseSandboxGitResult,
|
|
172
|
+
} from "./hooks/use-sandbox-git";
|
|
135
173
|
|
|
136
174
|
// Workbench v2 turn-end capture (the cold-paint data layer)
|
|
137
175
|
export { useWorkspaceCapture } from "./hooks/use-workspace-capture";
|
|
@@ -157,6 +195,27 @@ export type {
|
|
|
157
195
|
// Pending-approvals projection
|
|
158
196
|
export { approvalsFromRequiresAction, projectPendingApprovals } from "./approvals";
|
|
159
197
|
export type { PendingApproval } from "./approvals";
|
|
198
|
+
export { ApprovalSurface, defaultApprovalSurfaceMessages } from "./components/approval-surface";
|
|
199
|
+
export type { ApprovalSurfaceMessages, ApprovalSurfaceProps } from "./components/approval-surface";
|
|
200
|
+
|
|
201
|
+
// Structured human input: event projection, authoritative hook, and styled form.
|
|
202
|
+
export { humanInputRequestFromEvent, projectPendingHumanInputRequests } from "./human-input";
|
|
203
|
+
export type { PendingHumanInputRequest } from "./human-input";
|
|
204
|
+
export { isHumanInputEvent, useHumanInputRequests } from "./hooks/use-human-input";
|
|
205
|
+
export type {
|
|
206
|
+
UseHumanInputRequestsOptions,
|
|
207
|
+
UseHumanInputRequestsResult,
|
|
208
|
+
} from "./hooks/use-human-input";
|
|
209
|
+
export {
|
|
210
|
+
HumanInputForm,
|
|
211
|
+
answersFromDrafts,
|
|
212
|
+
defaultHumanInputFormMessages,
|
|
213
|
+
} from "./components/human-input-form";
|
|
214
|
+
export type {
|
|
215
|
+
HumanInputAnswerDraft,
|
|
216
|
+
HumanInputFormMessages,
|
|
217
|
+
HumanInputFormProps,
|
|
218
|
+
} from "./components/human-input-form";
|
|
160
219
|
|
|
161
220
|
// Timeline projection
|
|
162
221
|
export {
|
|
@@ -275,6 +334,13 @@ export type { CommandPaletteProps } from "./components/command-palette";
|
|
|
275
334
|
// Components
|
|
276
335
|
export { ChatComposer } from "./components/chat-composer";
|
|
277
336
|
export type { ChatComposerProps } from "./components/chat-composer";
|
|
337
|
+
export { ComposerTranscriptionControl } from "./components/composer-transcription-control";
|
|
338
|
+
export type {
|
|
339
|
+
ComposerTranscriptionControlProps,
|
|
340
|
+
ComposerTranscriptionMessages,
|
|
341
|
+
} from "./components/composer-transcription-control";
|
|
342
|
+
export { defaultChatComposerMessages } from "./components/composer";
|
|
343
|
+
export type { ChatComposerMessages } from "./components/composer";
|
|
278
344
|
export { ModelPicker } from "./components/model-picker";
|
|
279
345
|
export type { ModelPickerProps } from "./components/model-picker";
|
|
280
346
|
export { MessageTimeline, TimelineRow } from "./components/message-timeline";
|
|
@@ -318,8 +384,12 @@ export {
|
|
|
318
384
|
initialWorkspaceTab,
|
|
319
385
|
WORKBENCH_TAB_CHANGES,
|
|
320
386
|
WORKBENCH_TAB_FILES,
|
|
387
|
+
WORKBENCH_TAB_TERMINAL,
|
|
388
|
+
WORKBENCH_TAB_DESKTOP,
|
|
389
|
+
WORKBENCH_SURFACES,
|
|
321
390
|
} from "./components/sandbox-workspace";
|
|
322
391
|
export type {
|
|
392
|
+
SandboxWorkspaceSurface,
|
|
323
393
|
SandboxWorkspaceProps,
|
|
324
394
|
UseSandboxWorkspaceTabsOptions,
|
|
325
395
|
UseSandboxWorkspaceTabsResult,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import fontUrl from "@fontsource-variable/noto-sans-arabic/files/noto-sans-arabic-arabic-wght-normal.woff2";
|
|
2
|
+
import unicodeRanges from "@fontsource-variable/noto-sans-arabic/unicode.json";
|
|
3
|
+
|
|
4
|
+
let loading: Promise<void> | undefined;
|
|
5
|
+
|
|
6
|
+
export function loadNotoSansArabic(): Promise<void> {
|
|
7
|
+
loading ??= new FontFace(
|
|
8
|
+
"Noto Sans Arabic Variable",
|
|
9
|
+
`url("${fontUrl}") format("woff2-variations")`,
|
|
10
|
+
{ style: "normal", weight: "100 900", unicodeRange: unicodeRanges.arabic },
|
|
11
|
+
)
|
|
12
|
+
.load()
|
|
13
|
+
.then((font) => {
|
|
14
|
+
document.fonts.add(font);
|
|
15
|
+
})
|
|
16
|
+
.catch((error: unknown) => {
|
|
17
|
+
loading = undefined;
|
|
18
|
+
throw error;
|
|
19
|
+
});
|
|
20
|
+
return loading;
|
|
21
|
+
}
|