@opengeni/react 0.13.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 +129 -23
- package/dist/chunk-6BXBGJ3B.js +1956 -0
- 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 +642 -1080
- package/dist/index.js +7672 -8005
- package/dist/index.js.map +1 -1
- package/dist/machines.d.ts +412 -3
- package/dist/machines.js +25 -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 +23 -4
- package/src/approvals.ts +5 -3
- package/src/client.ts +50 -1
- package/src/components/approval-surface.tsx +198 -0
- package/src/components/chat-composer.tsx +104 -751
- 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 +82 -16
- package/src/components/machine-health-pill.tsx +68 -0
- package/src/components/machine-metrics.tsx +10 -24
- package/src/components/machines/health.ts +146 -0
- package/src/components/machines/machine-detail.tsx +220 -0
- package/src/components/machines/metric-history-chart.tsx +298 -0
- package/src/components/machines/metric-sparkline.tsx +76 -0
- package/src/components/machines/series.ts +113 -0
- package/src/components/machines-dashboard.tsx +16 -4
- 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 +60 -0
- package/src/components/sandbox-files.tsx +230 -21
- package/src/components/sandbox-terminal.tsx +8 -2
- package/src/components/sandbox-workspace.tsx +489 -135
- package/src/components/session-status.tsx +0 -6
- package/src/components/workbench-changes.tsx +145 -50
- package/src/components/workspace-dock.tsx +329 -68
- package/src/composer.ts +60 -0
- package/src/hooks/internal.ts +115 -34
- package/src/hooks/use-composer.ts +635 -74
- 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 +43 -15
- package/src/hooks/use-session-events.ts +523 -48
- package/src/hooks/use-session-lineage.ts +15 -6
- package/src/hooks/use-session.ts +10 -2
- package/src/hooks/use-slash-commands.ts +38 -38
- package/src/hooks/use-transcription.ts +757 -0
- package/src/hooks/use-turn-queue.ts +273 -77
- 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 +88 -12
- 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/machines.ts +16 -0
- package/src/provider.tsx +163 -41
- package/src/session-context.ts +135 -0
- package/src/session.ts +79 -0
- package/src/timeline/parsers.ts +78 -6
- package/src/timeline/projection.ts +36 -6
- 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 +78 -0
- package/styles/index.d.ts +2 -0
- package/styles/tokens.css +11 -6
- package/styles/tokens.d.ts +2 -0
- package/dist/chunk-NFYVQWIB.js +0 -1377
- package/dist/chunk-NFYVQWIB.js.map +0 -1
- package/dist/machines-CnlMb7E-.d.ts +0 -329
package/src/hooks/internal.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { SessionEvent } from "@opengeni/sdk";
|
|
2
|
-
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
|
-
import type {
|
|
2
|
+
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
3
|
+
import type { EmbeddedSessionClientLike } from "../client";
|
|
4
4
|
|
|
5
5
|
export type AsyncListState<T> = {
|
|
6
6
|
data: T | null;
|
|
@@ -14,7 +14,7 @@ export type AsyncListState<T> = {
|
|
|
14
14
|
* responses (superseded by a newer load or an unmount) are dropped.
|
|
15
15
|
*/
|
|
16
16
|
export function usePolledValue<T>(
|
|
17
|
-
load: () => Promise<T>,
|
|
17
|
+
load: (signal?: AbortSignal) => Promise<T>,
|
|
18
18
|
options: { pollIntervalMs?: number | undefined; enabled?: boolean | undefined } = {},
|
|
19
19
|
): AsyncListState<T> {
|
|
20
20
|
const enabled = options.enabled ?? true;
|
|
@@ -23,32 +23,55 @@ export function usePolledValue<T>(
|
|
|
23
23
|
const [loading, setLoading] = useState(enabled);
|
|
24
24
|
const [error, setError] = useState<Error | null>(null);
|
|
25
25
|
const generation = useRef(0);
|
|
26
|
-
const
|
|
26
|
+
const activeLoadRef = useRef(load);
|
|
27
|
+
useLayoutEffect(() => {
|
|
28
|
+
activeLoadRef.current = load;
|
|
29
|
+
}, [load]);
|
|
30
|
+
const requestAbortRef = useRef<AbortController | null>(null);
|
|
31
|
+
const [stateIdentity, setStateIdentity] = useState<{ load: typeof load }>(() => ({ load }));
|
|
27
32
|
|
|
28
33
|
// A new loader identity means a new query (different session/workspace/...):
|
|
29
34
|
// drop the previous result instead of showing it as the new query's data.
|
|
30
35
|
useEffect(() => {
|
|
31
|
-
if (
|
|
32
|
-
|
|
36
|
+
if (stateIdentity.load !== load) {
|
|
37
|
+
setStateIdentity({ load });
|
|
33
38
|
setData(null);
|
|
34
39
|
setError(null);
|
|
35
40
|
}
|
|
36
|
-
}, [load]);
|
|
41
|
+
}, [load, stateIdentity.load]);
|
|
37
42
|
|
|
38
43
|
const run = useCallback(async () => {
|
|
44
|
+
// A callback retained by a completed mutation from the previous query must
|
|
45
|
+
// not supersede or settle the current query's request.
|
|
46
|
+
if (activeLoadRef.current !== load) return;
|
|
39
47
|
const ticket = ++generation.current;
|
|
48
|
+
requestAbortRef.current?.abort();
|
|
49
|
+
const requestAbort = new AbortController();
|
|
50
|
+
requestAbortRef.current = requestAbort;
|
|
40
51
|
try {
|
|
41
|
-
const result = await load();
|
|
42
|
-
if (
|
|
52
|
+
const result = await load(requestAbort.signal);
|
|
53
|
+
if (
|
|
54
|
+
ticket === generation.current &&
|
|
55
|
+
activeLoadRef.current === load &&
|
|
56
|
+
!requestAbort.signal.aborted
|
|
57
|
+
) {
|
|
43
58
|
setData(result);
|
|
44
59
|
setError(null);
|
|
45
60
|
setLoading(false);
|
|
46
61
|
}
|
|
47
62
|
} catch (cause) {
|
|
48
|
-
if (
|
|
63
|
+
if (
|
|
64
|
+
ticket === generation.current &&
|
|
65
|
+
activeLoadRef.current === load &&
|
|
66
|
+
!requestAbort.signal.aborted
|
|
67
|
+
) {
|
|
49
68
|
setError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
50
69
|
setLoading(false);
|
|
51
70
|
}
|
|
71
|
+
} finally {
|
|
72
|
+
if (requestAbortRef.current === requestAbort) {
|
|
73
|
+
requestAbortRef.current = null;
|
|
74
|
+
}
|
|
52
75
|
}
|
|
53
76
|
}, [load]);
|
|
54
77
|
|
|
@@ -62,16 +85,24 @@ export function usePolledValue<T>(
|
|
|
62
85
|
if (pollIntervalMs === undefined || pollIntervalMs <= 0) {
|
|
63
86
|
return () => {
|
|
64
87
|
generation.current += 1;
|
|
88
|
+
requestAbortRef.current?.abort();
|
|
65
89
|
};
|
|
66
90
|
}
|
|
67
91
|
const timer = setInterval(() => void run(), pollIntervalMs);
|
|
68
92
|
return () => {
|
|
69
93
|
clearInterval(timer);
|
|
70
94
|
generation.current += 1;
|
|
95
|
+
requestAbortRef.current?.abort();
|
|
71
96
|
};
|
|
72
97
|
}, [run, enabled, pollIntervalMs]);
|
|
73
98
|
|
|
74
|
-
|
|
99
|
+
const identityMatches = stateIdentity.load === load;
|
|
100
|
+
return {
|
|
101
|
+
data: identityMatches ? data : null,
|
|
102
|
+
loading: identityMatches ? loading : enabled,
|
|
103
|
+
error: identityMatches ? error : null,
|
|
104
|
+
refresh: run,
|
|
105
|
+
};
|
|
75
106
|
}
|
|
76
107
|
|
|
77
108
|
export type MutationState = {
|
|
@@ -85,12 +116,21 @@ export type MutationState = {
|
|
|
85
116
|
* operation's value, or `null` after capturing the error in `mutationError`
|
|
86
117
|
* (callers then roll back optimistic state).
|
|
87
118
|
*/
|
|
88
|
-
export function useMutationRunner(): MutationState & {
|
|
119
|
+
export function useMutationRunner(identity: unknown = undefined): MutationState & {
|
|
89
120
|
run: <T>(operation: () => Promise<T>) => Promise<T | null>;
|
|
90
121
|
} {
|
|
91
122
|
const [mutating, setMutating] = useState(false);
|
|
92
123
|
const [mutationError, setMutationError] = useState<Error | null>(null);
|
|
124
|
+
const [stateIdentity, setStateIdentity] = useState<unknown>(() => identity);
|
|
93
125
|
const inFlight = useRef(0);
|
|
126
|
+
const generation = useRef(0);
|
|
127
|
+
const identityRef = useRef(identity);
|
|
128
|
+
useLayoutEffect(() => {
|
|
129
|
+
if (Object.is(identityRef.current, identity)) return;
|
|
130
|
+
identityRef.current = identity;
|
|
131
|
+
generation.current += 1;
|
|
132
|
+
inFlight.current = 0;
|
|
133
|
+
}, [identity]);
|
|
94
134
|
const mounted = useRef(true);
|
|
95
135
|
useEffect(() => {
|
|
96
136
|
mounted.current = true;
|
|
@@ -98,30 +138,63 @@ export function useMutationRunner(): MutationState & {
|
|
|
98
138
|
mounted.current = false;
|
|
99
139
|
};
|
|
100
140
|
}, []);
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
setMutating(
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
if (!Object.is(stateIdentity, identity)) {
|
|
143
|
+
setStateIdentity(() => identity);
|
|
144
|
+
setMutating(false);
|
|
105
145
|
setMutationError(null);
|
|
106
146
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
147
|
+
}, [identity, stateIdentity]);
|
|
148
|
+
const run = useCallback(
|
|
149
|
+
async <T>(operation: () => Promise<T>): Promise<T | null> => {
|
|
150
|
+
const ownedIdentity = identity;
|
|
151
|
+
const ownedGeneration = generation.current;
|
|
152
|
+
if (!Object.is(identityRef.current, ownedIdentity)) return null;
|
|
153
|
+
inFlight.current += 1;
|
|
110
154
|
if (mounted.current) {
|
|
111
|
-
|
|
155
|
+
setMutating(true);
|
|
156
|
+
setMutationError(null);
|
|
112
157
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
158
|
+
try {
|
|
159
|
+
const result = await operation();
|
|
160
|
+
if (
|
|
161
|
+
!mounted.current ||
|
|
162
|
+
generation.current !== ownedGeneration ||
|
|
163
|
+
!Object.is(identityRef.current, ownedIdentity)
|
|
164
|
+
) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
return result;
|
|
168
|
+
} catch (cause) {
|
|
169
|
+
if (
|
|
170
|
+
mounted.current &&
|
|
171
|
+
generation.current === ownedGeneration &&
|
|
172
|
+
Object.is(identityRef.current, ownedIdentity)
|
|
173
|
+
) {
|
|
174
|
+
setMutationError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
175
|
+
}
|
|
176
|
+
return null;
|
|
177
|
+
} finally {
|
|
178
|
+
if (
|
|
179
|
+
generation.current === ownedGeneration &&
|
|
180
|
+
Object.is(identityRef.current, ownedIdentity)
|
|
181
|
+
) {
|
|
182
|
+
inFlight.current -= 1;
|
|
183
|
+
if (mounted.current && inFlight.current === 0) {
|
|
184
|
+
setMutating(false);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
118
187
|
}
|
|
119
|
-
}
|
|
120
|
-
|
|
188
|
+
},
|
|
189
|
+
[identity],
|
|
190
|
+
);
|
|
191
|
+
const identityMatches = Object.is(stateIdentity, identity);
|
|
121
192
|
return {
|
|
122
|
-
mutating,
|
|
123
|
-
mutationError,
|
|
124
|
-
clearMutationError: useCallback(() =>
|
|
193
|
+
mutating: identityMatches && mutating,
|
|
194
|
+
mutationError: identityMatches ? mutationError : null,
|
|
195
|
+
clearMutationError: useCallback(() => {
|
|
196
|
+
if (Object.is(identityRef.current, identity)) setMutationError(null);
|
|
197
|
+
}, [identity]),
|
|
125
198
|
run,
|
|
126
199
|
};
|
|
127
200
|
}
|
|
@@ -142,20 +215,25 @@ export type SessionEventFeedOptions = {
|
|
|
142
215
|
* `events` log or tails the stream directly (reconnect handled by the SDK).
|
|
143
216
|
*/
|
|
144
217
|
export function useSessionEventTrigger(
|
|
145
|
-
client:
|
|
218
|
+
client: EmbeddedSessionClientLike,
|
|
146
219
|
workspaceId: string,
|
|
147
220
|
sessionId: string | null | undefined,
|
|
148
221
|
match: (event: SessionEvent) => boolean,
|
|
149
222
|
onEvent: (event: SessionEvent) => void,
|
|
150
223
|
options: SessionEventFeedOptions = {},
|
|
224
|
+
reconcileBeforeLive?: (() => void | Promise<void>) | undefined,
|
|
151
225
|
): void {
|
|
152
226
|
const enabled = options.enabled ?? true;
|
|
153
227
|
const events = options.events;
|
|
154
228
|
const sharedFeed = events !== undefined;
|
|
155
229
|
const matchRef = useRef(match);
|
|
156
|
-
matchRef.current = match;
|
|
157
230
|
const onEventRef = useRef(onEvent);
|
|
158
|
-
|
|
231
|
+
const reconcileBeforeLiveRef = useRef(reconcileBeforeLive);
|
|
232
|
+
useLayoutEffect(() => {
|
|
233
|
+
matchRef.current = match;
|
|
234
|
+
onEventRef.current = onEvent;
|
|
235
|
+
reconcileBeforeLiveRef.current = reconcileBeforeLive;
|
|
236
|
+
}, [match, onEvent, reconcileBeforeLive]);
|
|
159
237
|
const consumedRef = useRef(0);
|
|
160
238
|
const feedKeyRef = useRef<string | null>(null);
|
|
161
239
|
|
|
@@ -198,6 +276,7 @@ export function useSessionEventTrigger(
|
|
|
198
276
|
const stream = client.streamEvents(workspaceId, sessionId, {
|
|
199
277
|
after: session.lastSequence,
|
|
200
278
|
signal: controller.signal,
|
|
279
|
+
beforeLive: async () => await reconcileBeforeLiveRef.current?.(),
|
|
201
280
|
});
|
|
202
281
|
for await (const event of stream) {
|
|
203
282
|
if (matchRef.current(event)) {
|
|
@@ -218,7 +297,9 @@ export function useSessionEventTrigger(
|
|
|
218
297
|
/** Debounce rapid event bursts into one trailing call (default 150ms). */
|
|
219
298
|
export function useDebouncedCallback(callback: () => void, delayMs = 150): () => void {
|
|
220
299
|
const callbackRef = useRef(callback);
|
|
221
|
-
|
|
300
|
+
useLayoutEffect(() => {
|
|
301
|
+
callbackRef.current = callback;
|
|
302
|
+
}, [callback]);
|
|
222
303
|
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
223
304
|
useEffect(() => {
|
|
224
305
|
return () => {
|