@opengeni/react 1.4.2 → 1.4.5-canary.37458084490001
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 +17 -6
- package/dist/artifacts.d.ts +1 -0
- package/dist/artifacts.js +22 -15
- package/dist/artifacts.js.map +1 -1
- package/dist/{browser-viewer-WGJ43YGI.js → browser-viewer-CV23VIHB.js} +8 -6
- package/dist/chunk-3ZC6QPOM.js +174 -0
- package/dist/chunk-3ZC6QPOM.js.map +1 -0
- package/dist/{chunk-YVFRBXST.js → chunk-4BCVCJSU.js} +2 -2
- package/dist/{chunk-JHTIMBOL.js → chunk-5JSUNNQN.js} +26 -4
- package/dist/{chunk-JHTIMBOL.js.map → chunk-5JSUNNQN.js.map} +1 -1
- package/dist/{chunk-MDIXFKA3.js → chunk-6FOTSVWU.js} +303 -109
- package/dist/chunk-6FOTSVWU.js.map +1 -0
- package/dist/{chunk-IUHHPMAX.js → chunk-7RRBX5EM.js} +14 -85
- package/dist/chunk-7RRBX5EM.js.map +1 -0
- package/dist/{chunk-L2J3PU7A.js → chunk-7ZB7I3KP.js} +24 -98
- package/dist/chunk-7ZB7I3KP.js.map +1 -0
- package/dist/{chunk-LVS3MH5T.js → chunk-ATIRUHBZ.js} +128 -10
- package/dist/chunk-ATIRUHBZ.js.map +1 -0
- package/dist/chunk-BOFEQC6K.js +162 -0
- package/dist/chunk-BOFEQC6K.js.map +1 -0
- package/dist/{chunk-VCSERYXH.js → chunk-CESBPQP5.js} +309 -162
- package/dist/chunk-CESBPQP5.js.map +1 -0
- package/dist/{chunk-IYQCWGU5.js → chunk-GHJU7XG7.js} +35 -62
- package/dist/chunk-GHJU7XG7.js.map +1 -0
- package/dist/{chunk-G6YRCIYJ.js → chunk-HNVWMDNN.js} +55 -60
- package/dist/chunk-HNVWMDNN.js.map +1 -0
- package/dist/{chunk-PHBFJCEU.js → chunk-I5EGRXXG.js} +110 -7
- package/dist/chunk-I5EGRXXG.js.map +1 -0
- package/dist/{chunk-PBCZL4JP.js → chunk-INIZSNXT.js} +2 -2
- package/dist/{chunk-XS67YG5R.js → chunk-IX3K4THH.js} +24 -3
- package/dist/chunk-IX3K4THH.js.map +1 -0
- package/dist/chunk-IZV5FTJC.js +55 -0
- package/dist/chunk-IZV5FTJC.js.map +1 -0
- package/dist/chunk-JDJJMBB3.js +1883 -0
- package/dist/chunk-JDJJMBB3.js.map +1 -0
- package/dist/{chunk-C5BF4L26.js → chunk-KHF2VRXF.js} +7 -7
- package/dist/{chunk-QMMCJGFI.js → chunk-L5OKM2VX.js} +2 -2
- package/dist/chunk-LHF6BVM4.js +83 -0
- package/dist/chunk-LHF6BVM4.js.map +1 -0
- package/dist/chunk-N7S72EXK.js +25 -0
- package/dist/chunk-N7S72EXK.js.map +1 -0
- package/dist/{chunk-CVYCLFMH.js → chunk-PEHTX3V7.js} +19 -1
- package/dist/chunk-PEHTX3V7.js.map +1 -0
- package/dist/chunk-R5R5RQQD.js +51 -0
- package/dist/chunk-R5R5RQQD.js.map +1 -0
- package/dist/chunk-RI42H5IU.js +326 -0
- package/dist/chunk-RI42H5IU.js.map +1 -0
- package/dist/{chunk-JH7V627O.js → chunk-SHLZMAJN.js} +2 -11
- package/dist/chunk-SHLZMAJN.js.map +1 -0
- package/dist/{chunk-CREMJMVN.js → chunk-TZQSHCZN.js} +692 -1996
- package/dist/chunk-TZQSHCZN.js.map +1 -0
- package/dist/chunk-ZIB4H5LN.js +101 -0
- package/dist/chunk-ZIB4H5LN.js.map +1 -0
- package/dist/client.d.ts +9 -3
- package/dist/clipboard.js +1 -1
- package/dist/components/browser-viewer.d.ts +6 -0
- package/dist/components/composer.d.ts +3 -69
- package/dist/components/human-input-form.d.ts +3 -32
- package/dist/components/markdown.d.ts +8 -0
- package/dist/components/model-policy-picker.d.ts +4 -37
- package/dist/components/opper-mark.d.ts +7 -0
- package/dist/components/provider-recovery-notice.d.ts +15 -0
- package/dist/components/remark-soft-line-breaks.d.ts +38 -0
- package/dist/components/sandbox-workspace.d.ts +13 -0
- package/dist/components/session-chrome.d.ts +5 -3
- package/dist/components/session-conversation.d.ts +15 -3
- package/dist/composer-messages.d.ts +74 -0
- package/dist/composer-messages.js +9 -0
- package/dist/composer.js +15 -10
- package/dist/{computer-viewer-46UIN7F3.js → computer-viewer-K4CKYRX6.js} +4 -4
- package/dist/connect-setup.d.ts +2 -0
- package/dist/connect.d.ts +1 -1
- package/dist/connect.js +21 -6
- package/dist/connect.js.map +1 -1
- package/dist/{embedded-voice-NIL4RDX3.js → embedded-voice-3ZFWWJ6O.js} +6 -4
- package/dist/{embedded-voice-NIL4RDX3.js.map → embedded-voice-3ZFWWJ6O.js.map} +1 -1
- package/dist/hooks/shared-machines-list.d.ts +4 -0
- package/dist/hooks/use-client-config-flags.d.ts +6 -0
- package/dist/hooks/use-computer-session.d.ts +2 -0
- package/dist/hooks/use-goal.d.ts +2 -1
- package/dist/human-input-model.d.ts +40 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +116 -73
- package/dist/index.js.map +1 -1
- package/dist/interaction.js +5 -5
- package/dist/{latest-question-2POFPYNB.js → latest-question-SWC6QEVN.js} +3 -3
- package/dist/lib/clipboard.d.ts +7 -0
- package/dist/lib/provider-recovery.d.ts +49 -0
- package/dist/machines.js +2 -2
- package/dist/model-mark-paths.d.ts +34 -0
- package/dist/model-mark-paths.js +16 -0
- package/dist/model-picker-model.d.ts +111 -0
- package/dist/model-picker-model.js +25 -0
- package/dist/model-picker-model.js.map +1 -0
- package/dist/{model-policy-picker-menu-WRNSRFQC.js → model-policy-picker-menu-NM4RPTY6.js} +6 -4
- package/dist/model-policy-picker-menu-NM4RPTY6.js.map +1 -0
- package/dist/model-policy.d.ts +10 -0
- package/dist/model-policy.js +5 -1
- package/dist/organization-model.d.ts +32 -0
- package/dist/organization-model.js +65 -0
- package/dist/organization-model.js.map +1 -0
- package/dist/{platform-activity-row-PJJVATTZ.js → platform-activity-row-NJ47YBXW.js} +7 -84
- package/dist/platform-activity-row-NJ47YBXW.js.map +1 -0
- package/dist/queue-presentation-model.d.ts +72 -0
- package/dist/queue-presentation-model.js +38 -0
- package/dist/queue-presentation-model.js.map +1 -0
- package/dist/{queue-surface-implementation-67FS7FUI.js → queue-surface-implementation-YPJFQ3O6.js} +24 -252
- package/dist/queue-surface-implementation-YPJFQ3O6.js.map +1 -0
- package/dist/realtime.js +5 -3
- package/dist/{sandbox-files-34CJJNOY.js → sandbox-files-RCVCMXG7.js} +5 -5
- package/dist/sandbox-files-RCVCMXG7.js.map +1 -0
- package/dist/sandbox-label-model.d.ts +23 -0
- package/dist/sandbox-label-model.js +28 -0
- package/dist/sandbox-label-model.js.map +1 -0
- package/dist/session-attention-model.d.ts +8 -0
- package/dist/session-attention-model.js +46 -0
- package/dist/session-attention-model.js.map +1 -0
- package/dist/session-feedback-model.d.ts +20 -0
- package/dist/session-feedback-model.js +56 -0
- package/dist/session-feedback-model.js.map +1 -0
- package/dist/session-list-model.d.ts +96 -0
- package/dist/session-list-model.js +219 -0
- package/dist/session-list-model.js.map +1 -0
- package/dist/session-status-model.d.ts +19 -0
- package/dist/session-ui.js +26 -17
- package/dist/session-ui.js.map +1 -1
- package/dist/session.js +8 -6
- package/dist/testing/session-scenarios.d.ts +10 -0
- package/dist/testing/session-scenarios.js +303 -0
- package/dist/testing/session-scenarios.js.map +1 -0
- package/dist/timeline/genie-copy.d.ts +3 -0
- package/dist/timeline/genie-loading.d.ts +11 -0
- package/dist/timeline/index.d.ts +1 -1
- package/dist/timeline/notice-presentation.d.ts +7 -0
- package/dist/timeline/patch-apply-command.d.ts +18 -0
- package/dist/timeline/platform-activity-presentation.d.ts +7 -0
- package/dist/timeline/presented-image.d.ts +6 -1
- package/dist/timeline/question-nav-model.d.ts +23 -0
- package/dist/timeline/registry.d.ts +10 -1
- package/dist/timeline/retained-loaders.d.ts +1 -1
- package/dist/timeline/tool-presentation.d.ts +172 -0
- package/dist/timeline/turn-summary-model.d.ts +90 -0
- package/dist/timeline/turn-summary.d.ts +4 -74
- package/dist/timeline/work-presentation.d.ts +42 -0
- package/dist/timeline-model.d.ts +24 -0
- package/dist/timeline-model.js +152 -0
- package/dist/timeline-model.js.map +1 -0
- package/package.json +47 -3
- package/src/artifacts.ts +1 -0
- package/src/client.ts +9 -3
- package/src/components/browser-viewer.tsx +23 -2
- package/src/components/claude-mark.tsx +2 -1
- package/src/components/composer.tsx +4 -144
- package/src/components/computer-viewer.tsx +139 -30
- package/src/components/grok-mark.tsx +2 -1
- package/src/components/human-input-form.tsx +13 -124
- package/src/components/markdown.tsx +19 -1
- package/src/components/message-timeline.tsx +45 -99
- package/src/components/model-mark.tsx +2 -1
- package/src/components/model-policy-picker-menu.tsx +30 -85
- package/src/components/model-policy-picker.tsx +15 -148
- package/src/components/opper-mark.tsx +18 -0
- package/src/components/provider-recovery-notice.tsx +41 -0
- package/src/components/queue-surface-implementation.tsx +20 -349
- package/src/components/remark-soft-line-breaks.ts +166 -0
- package/src/components/sandbox-workspace.tsx +17 -1
- package/src/components/session-chrome.tsx +114 -99
- package/src/components/session-conversation.tsx +55 -6
- package/src/components/session-list.tsx +1 -1
- package/src/components/session-status.tsx +17 -16
- package/src/composer-messages.ts +150 -0
- package/src/connect-setup.tsx +45 -11
- package/src/connect.ts +1 -1
- package/src/hooks/shared-machines-list.ts +34 -1
- package/src/hooks/use-browser-session.ts +445 -168
- package/src/hooks/use-client-config-flags.ts +8 -0
- package/src/hooks/use-computer-session.ts +334 -98
- package/src/hooks/use-goal.ts +66 -10
- package/src/human-input-model.ts +147 -0
- package/src/index.ts +16 -0
- package/src/lib/clipboard.ts +29 -0
- package/src/lib/format.ts +7 -0
- package/src/lib/provider-recovery.ts +202 -0
- package/src/model-mark-paths.ts +53 -0
- package/src/model-picker-model.ts +330 -0
- package/src/model-policy.ts +43 -2
- package/src/organization-model.ts +118 -0
- package/src/queue-presentation-model.ts +464 -0
- package/src/sandbox-label-model.ts +41 -0
- package/src/session-attention-model.ts +59 -0
- package/src/session-feedback-model.ts +95 -0
- package/src/session-list-model.ts +327 -0
- package/src/session-status-model.ts +62 -0
- package/src/testing/session-scenarios.ts +326 -0
- package/src/timeline/activity-rail.tsx +5 -17
- package/src/timeline/genie-copy.ts +19 -0
- package/src/timeline/genie-loading.tsx +13 -18
- package/src/timeline/index.ts +1 -0
- package/src/timeline/notice-presentation.ts +22 -0
- package/src/timeline/patch-apply-command.tsx +135 -0
- package/src/timeline/platform-activity-presentation.ts +122 -0
- package/src/timeline/platform-activity-row.tsx +6 -96
- package/src/timeline/presented-image.ts +16 -2
- package/src/timeline/question-nav-model.ts +28 -0
- package/src/timeline/registry.ts +11 -1
- package/src/timeline/retained-loaders.ts +9 -4
- package/src/timeline/rolling-activity.tsx +7 -19
- package/src/timeline/tool-presentation.ts +1316 -0
- package/src/timeline/tool-renderers.tsx +226 -1025
- package/src/timeline/turn-summary-model.ts +338 -0
- package/src/timeline/turn-summary.tsx +21 -326
- package/src/timeline/work-presentation.ts +139 -0
- package/src/timeline-model.ts +108 -0
- package/styles/compiled.css +8 -0
- package/styles/connect.css +4 -0
- package/dist/chunk-CREMJMVN.js.map +0 -1
- package/dist/chunk-CVYCLFMH.js.map +0 -1
- package/dist/chunk-G6YRCIYJ.js.map +0 -1
- package/dist/chunk-HXYOSSQO.js +0 -117
- package/dist/chunk-HXYOSSQO.js.map +0 -1
- package/dist/chunk-IUHHPMAX.js.map +0 -1
- package/dist/chunk-IYQCWGU5.js.map +0 -1
- package/dist/chunk-JH7V627O.js.map +0 -1
- package/dist/chunk-L2J3PU7A.js.map +0 -1
- package/dist/chunk-LVS3MH5T.js.map +0 -1
- package/dist/chunk-MDIXFKA3.js.map +0 -1
- package/dist/chunk-PHBFJCEU.js.map +0 -1
- package/dist/chunk-VCSERYXH.js.map +0 -1
- package/dist/chunk-XS67YG5R.js.map +0 -1
- package/dist/platform-activity-row-PJJVATTZ.js.map +0 -1
- package/dist/queue-surface-implementation-67FS7FUI.js.map +0 -1
- /package/dist/{browser-viewer-WGJ43YGI.js.map → browser-viewer-CV23VIHB.js.map} +0 -0
- /package/dist/{chunk-YVFRBXST.js.map → chunk-4BCVCJSU.js.map} +0 -0
- /package/dist/{chunk-PBCZL4JP.js.map → chunk-INIZSNXT.js.map} +0 -0
- /package/dist/{chunk-C5BF4L26.js.map → chunk-KHF2VRXF.js.map} +0 -0
- /package/dist/{chunk-QMMCJGFI.js.map → chunk-L5OKM2VX.js.map} +0 -0
- /package/dist/{computer-viewer-46UIN7F3.js.map → composer-messages.js.map} +0 -0
- /package/dist/{model-policy-picker-menu-WRNSRFQC.js.map → computer-viewer-K4CKYRX6.js.map} +0 -0
- /package/dist/{latest-question-2POFPYNB.js.map → latest-question-SWC6QEVN.js.map} +0 -0
- /package/dist/{sandbox-files-34CJJNOY.js.map → model-mark-paths.js.map} +0 -0
|
@@ -18,6 +18,19 @@ import {
|
|
|
18
18
|
} from "../session-context";
|
|
19
19
|
import { usePageLiveActivity } from "./internal";
|
|
20
20
|
|
|
21
|
+
// Keep caller promises intact while fencing their later viewer side effects.
|
|
22
|
+
// These local fences never become part of an operation receipt on the wire.
|
|
23
|
+
const actionResultFences = new WeakMap<object, () => boolean>();
|
|
24
|
+
export function isStaleComputerActionResult(value: unknown): boolean {
|
|
25
|
+
return typeof value === "object" && value !== null && actionResultFences.get(value)?.() === false;
|
|
26
|
+
}
|
|
27
|
+
export function computerActionReceiptError(receipt: ComputerActionReceipt): Error {
|
|
28
|
+
const error = new Error(receipt.error?.message ?? "Desktop input did not complete.");
|
|
29
|
+
const fence = actionResultFences.get(receipt);
|
|
30
|
+
if (fence) actionResultFences.set(error, fence);
|
|
31
|
+
return error;
|
|
32
|
+
}
|
|
33
|
+
|
|
21
34
|
export type UseComputerSessionOptions = EmbeddedComputerInteractionClientOverride & {
|
|
22
35
|
computerSessionId: string | null;
|
|
23
36
|
enabled?: boolean | undefined;
|
|
@@ -54,30 +67,48 @@ export function useComputerSession(options: UseComputerSessionOptions): UseCompu
|
|
|
54
67
|
const { client, workspaceId } = useEmbeddedComputerInteraction(options);
|
|
55
68
|
const computerSessionId = options.computerSessionId;
|
|
56
69
|
const enabled = (options.enabled ?? true) && computerSessionId !== null;
|
|
70
|
+
const sourceRef = useRef<ComputerControlSource>({
|
|
71
|
+
client,
|
|
72
|
+
workspaceId,
|
|
73
|
+
computerSessionId,
|
|
74
|
+
enabled,
|
|
75
|
+
});
|
|
76
|
+
if (
|
|
77
|
+
sourceRef.current.client !== client ||
|
|
78
|
+
sourceRef.current.workspaceId !== workspaceId ||
|
|
79
|
+
sourceRef.current.computerSessionId !== computerSessionId ||
|
|
80
|
+
sourceRef.current.enabled !== enabled
|
|
81
|
+
) {
|
|
82
|
+
sourceRef.current = { client, workspaceId, computerSessionId, enabled };
|
|
83
|
+
}
|
|
84
|
+
const source = sourceRef.current;
|
|
57
85
|
const pageLive = usePageLiveActivity();
|
|
58
86
|
const pollIntervalMs = Math.max(750, options.pollIntervalMs ?? 2_000);
|
|
59
|
-
const [state, setState] = useState<ComputerControlState>(() =>
|
|
60
|
-
|
|
61
|
-
);
|
|
62
|
-
const visible =
|
|
63
|
-
state.computerSessionId === computerSessionId ? state : emptyState(computerSessionId, enabled);
|
|
87
|
+
const [state, setState] = useState<ComputerControlState>(() => emptyState(source, enabled));
|
|
88
|
+
const visible = state.source === source ? state : emptyState(source, enabled);
|
|
64
89
|
const refreshBlocked = isNonRetryableInteractionError(visible.error);
|
|
65
90
|
const selectedTargetIdRef = useRef<string | null>(visible.selectedTargetId);
|
|
66
91
|
selectedTargetIdRef.current = visible.selectedTargetId;
|
|
92
|
+
const selectionRevisionRef = useRef(0);
|
|
93
|
+
const actionResultOrderRef = useRef({ next: 0, settled: 0 });
|
|
67
94
|
const targetsRef = useRef<{
|
|
95
|
+
source: ComputerControlSource;
|
|
68
96
|
computerSessionId: string | null;
|
|
69
97
|
targets: ComputerTarget[];
|
|
70
98
|
}>({
|
|
99
|
+
source,
|
|
71
100
|
computerSessionId,
|
|
72
101
|
targets: visible.targets,
|
|
73
102
|
});
|
|
74
103
|
const observationRef = useRef<{
|
|
104
|
+
source: ComputerControlSource;
|
|
75
105
|
computerSessionId: string | null;
|
|
76
106
|
observation: ComputerObservation | null;
|
|
77
|
-
}>({ computerSessionId, observation: visible.observation });
|
|
78
|
-
if (targetsRef.current.
|
|
79
|
-
targetsRef.current = { computerSessionId, targets: visible.targets };
|
|
107
|
+
}>({ source, computerSessionId, observation: visible.observation });
|
|
108
|
+
if (targetsRef.current.source !== source) {
|
|
109
|
+
targetsRef.current = { source, computerSessionId, targets: visible.targets };
|
|
80
110
|
observationRef.current = {
|
|
111
|
+
source,
|
|
81
112
|
computerSessionId,
|
|
82
113
|
observation: visible.observation,
|
|
83
114
|
};
|
|
@@ -91,12 +122,18 @@ export function useComputerSession(options: UseComputerSessionOptions): UseCompu
|
|
|
91
122
|
});
|
|
92
123
|
const mutationRef = useRef<{
|
|
93
124
|
computerSessionId: string | null;
|
|
125
|
+
source: typeof source;
|
|
94
126
|
count: number;
|
|
95
127
|
}>({
|
|
96
128
|
computerSessionId,
|
|
129
|
+
source,
|
|
97
130
|
count: 0,
|
|
98
131
|
});
|
|
99
132
|
const mountedRef = useRef(true);
|
|
133
|
+
const isCurrentSource = useCallback(
|
|
134
|
+
() => mountedRef.current && sourceRef.current === source,
|
|
135
|
+
[source],
|
|
136
|
+
);
|
|
100
137
|
|
|
101
138
|
const invalidateRefresh = useCallback(() => {
|
|
102
139
|
const id = requestRef.current.id + 1;
|
|
@@ -105,7 +142,7 @@ export function useComputerSession(options: UseComputerSessionOptions): UseCompu
|
|
|
105
142
|
}, []);
|
|
106
143
|
|
|
107
144
|
const refresh = useCallback(async (): Promise<void> => {
|
|
108
|
-
if (!enabled || !computerSessionId) return;
|
|
145
|
+
if (!enabled || !computerSessionId || !isCurrentSource()) return;
|
|
109
146
|
const id = requestRef.current.id + 1;
|
|
110
147
|
requestRef.current.controller?.abort();
|
|
111
148
|
const controller = new AbortController();
|
|
@@ -121,13 +158,14 @@ export function useComputerSession(options: UseComputerSessionOptions): UseCompu
|
|
|
121
158
|
}),
|
|
122
159
|
]);
|
|
123
160
|
discoveryCompleted = true;
|
|
124
|
-
if (!
|
|
161
|
+
if (!isCurrentSource() || requestRef.current.id !== id) return;
|
|
125
162
|
const targets = sortComputerTargets(targetResponse.targets);
|
|
126
163
|
const selected = chooseTarget(targets, selectedTargetIdRef.current);
|
|
127
164
|
// Discovery is useful independently of semantic observation. Publish it
|
|
128
165
|
// now so a slow/unresponsive application cannot block the frame stream
|
|
129
166
|
// or prevent the person from choosing a different window or screen.
|
|
130
|
-
const previous =
|
|
167
|
+
const previous =
|
|
168
|
+
observationRef.current.source === source ? observationRef.current.observation : null;
|
|
131
169
|
const retainedObservation =
|
|
132
170
|
selected &&
|
|
133
171
|
previous?.target.id === selected.id &&
|
|
@@ -136,9 +174,11 @@ export function useComputerSession(options: UseComputerSessionOptions): UseCompu
|
|
|
136
174
|
? previous
|
|
137
175
|
: null;
|
|
138
176
|
selectedTargetIdRef.current = selected?.id ?? null;
|
|
139
|
-
targetsRef.current = { computerSessionId, targets };
|
|
140
|
-
observationRef.current = { computerSessionId, observation: retainedObservation };
|
|
177
|
+
targetsRef.current = { source, computerSessionId, targets };
|
|
178
|
+
observationRef.current = { source, computerSessionId, observation: retainedObservation };
|
|
141
179
|
setState((current) =>
|
|
180
|
+
isCurrentSource() &&
|
|
181
|
+
current.source === source &&
|
|
142
182
|
current.computerSessionId === computerSessionId
|
|
143
183
|
? {
|
|
144
184
|
...current,
|
|
@@ -157,11 +197,13 @@ export function useComputerSession(options: UseComputerSessionOptions): UseCompu
|
|
|
157
197
|
signal: controller.signal,
|
|
158
198
|
})
|
|
159
199
|
: null;
|
|
160
|
-
if (!
|
|
200
|
+
if (!isCurrentSource() || requestRef.current.id !== id) return;
|
|
161
201
|
selectedTargetIdRef.current = selected?.id ?? null;
|
|
162
|
-
targetsRef.current = { computerSessionId, targets };
|
|
163
|
-
observationRef.current = { computerSessionId, observation };
|
|
202
|
+
targetsRef.current = { source, computerSessionId, targets };
|
|
203
|
+
observationRef.current = { source, computerSessionId, observation };
|
|
164
204
|
setState((current) =>
|
|
205
|
+
isCurrentSource() &&
|
|
206
|
+
current.source === source &&
|
|
165
207
|
current.computerSessionId === computerSessionId
|
|
166
208
|
? {
|
|
167
209
|
...current,
|
|
@@ -175,9 +217,11 @@ export function useComputerSession(options: UseComputerSessionOptions): UseCompu
|
|
|
175
217
|
: current,
|
|
176
218
|
);
|
|
177
219
|
} catch (cause) {
|
|
178
|
-
if (controller.signal.aborted || !
|
|
220
|
+
if (controller.signal.aborted || !isCurrentSource() || requestRef.current.id !== id) return;
|
|
179
221
|
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
180
222
|
setState((current) =>
|
|
223
|
+
isCurrentSource() &&
|
|
224
|
+
current.source === source &&
|
|
181
225
|
current.computerSessionId === computerSessionId
|
|
182
226
|
? {
|
|
183
227
|
...current,
|
|
@@ -195,26 +239,30 @@ export function useComputerSession(options: UseComputerSessionOptions): UseCompu
|
|
|
195
239
|
} finally {
|
|
196
240
|
if (requestRef.current.id === id) requestRef.current = { id, controller: null };
|
|
197
241
|
}
|
|
198
|
-
}, [client, computerSessionId, enabled, workspaceId]);
|
|
242
|
+
}, [client, computerSessionId, enabled, isCurrentSource, source, workspaceId]);
|
|
199
243
|
|
|
200
244
|
useEffect(() => {
|
|
201
245
|
mountedRef.current = true;
|
|
202
246
|
if (!enabled) {
|
|
203
247
|
invalidateRefresh();
|
|
204
|
-
setState(emptyState(
|
|
248
|
+
setState(emptyState(source, false));
|
|
205
249
|
return;
|
|
206
250
|
}
|
|
207
251
|
setState((current) =>
|
|
208
|
-
current.
|
|
209
|
-
? {
|
|
210
|
-
|
|
252
|
+
current.source === source
|
|
253
|
+
? {
|
|
254
|
+
...current,
|
|
255
|
+
loading: true,
|
|
256
|
+
mutating: mutationRef.current.source === source && mutationRef.current.count > 0,
|
|
257
|
+
}
|
|
258
|
+
: emptyState(source, true),
|
|
211
259
|
);
|
|
212
260
|
void refresh();
|
|
213
261
|
return () => {
|
|
214
262
|
mountedRef.current = false;
|
|
215
263
|
invalidateRefresh();
|
|
216
264
|
};
|
|
217
|
-
}, [computerSessionId, enabled, invalidateRefresh, refresh]);
|
|
265
|
+
}, [computerSessionId, enabled, invalidateRefresh, refresh, source]);
|
|
218
266
|
|
|
219
267
|
useEffect(() => {
|
|
220
268
|
if (!enabled || !pageLive || visible.mutating || refreshBlocked) return;
|
|
@@ -240,44 +288,38 @@ export function useComputerSession(options: UseComputerSessionOptions): UseCompu
|
|
|
240
288
|
|
|
241
289
|
const runMutation = useCallback(
|
|
242
290
|
async <T>(scopeComputerSessionId: string, operation: () => Promise<T>): Promise<T> => {
|
|
291
|
+
if (!isCurrentSource()) throw new Error("The desktop source is no longer selected.");
|
|
243
292
|
invalidateRefresh();
|
|
244
|
-
if (
|
|
293
|
+
if (
|
|
294
|
+
mutationRef.current.computerSessionId !== scopeComputerSessionId ||
|
|
295
|
+
mutationRef.current.source !== source
|
|
296
|
+
) {
|
|
245
297
|
mutationRef.current = {
|
|
246
298
|
computerSessionId: scopeComputerSessionId,
|
|
299
|
+
source,
|
|
247
300
|
count: 0,
|
|
248
301
|
};
|
|
249
302
|
}
|
|
250
303
|
mutationRef.current.count += 1;
|
|
251
304
|
setState((current) =>
|
|
305
|
+
isCurrentSource() &&
|
|
306
|
+
current.source === source &&
|
|
252
307
|
current.computerSessionId === scopeComputerSessionId
|
|
253
308
|
? { ...current, mutating: true, error: null }
|
|
254
309
|
: current,
|
|
255
310
|
);
|
|
256
311
|
try {
|
|
257
|
-
|
|
258
|
-
setState((current) =>
|
|
259
|
-
current.computerSessionId === scopeComputerSessionId
|
|
260
|
-
? { ...current, controlError: null }
|
|
261
|
-
: current,
|
|
262
|
-
);
|
|
263
|
-
return result;
|
|
264
|
-
} catch (cause) {
|
|
265
|
-
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
266
|
-
setState((current) =>
|
|
267
|
-
current.computerSessionId === scopeComputerSessionId
|
|
268
|
-
? {
|
|
269
|
-
...current,
|
|
270
|
-
error,
|
|
271
|
-
controlError: isInteractionControlUnavailable(error) ? error : current.controlError,
|
|
272
|
-
}
|
|
273
|
-
: current,
|
|
274
|
-
);
|
|
275
|
-
throw error;
|
|
312
|
+
return await operation();
|
|
276
313
|
} finally {
|
|
277
|
-
if (
|
|
314
|
+
if (
|
|
315
|
+
mutationRef.current.computerSessionId === scopeComputerSessionId &&
|
|
316
|
+
mutationRef.current.source === source
|
|
317
|
+
) {
|
|
278
318
|
mutationRef.current.count = Math.max(0, mutationRef.current.count - 1);
|
|
279
319
|
const mutating = mutationRef.current.count > 0;
|
|
280
320
|
setState((current) =>
|
|
321
|
+
isCurrentSource() &&
|
|
322
|
+
current.source === source &&
|
|
281
323
|
current.computerSessionId === scopeComputerSessionId
|
|
282
324
|
? { ...current, mutating }
|
|
283
325
|
: current,
|
|
@@ -285,18 +327,31 @@ export function useComputerSession(options: UseComputerSessionOptions): UseCompu
|
|
|
285
327
|
}
|
|
286
328
|
}
|
|
287
329
|
},
|
|
288
|
-
[invalidateRefresh],
|
|
330
|
+
[invalidateRefresh, isCurrentSource, source],
|
|
289
331
|
);
|
|
290
332
|
|
|
291
333
|
const selectTarget = useCallback(
|
|
292
334
|
async (targetId: string): Promise<ComputerTarget> => {
|
|
293
335
|
if (!computerSessionId) throw new Error("No desktop is selected.");
|
|
294
|
-
|
|
336
|
+
if (!isCurrentSource()) throw new Error("The desktop source is no longer selected.");
|
|
337
|
+
const target =
|
|
338
|
+
targetsRef.current.source === source
|
|
339
|
+
? targetsRef.current.targets.find((candidate) => candidate.id === targetId)
|
|
340
|
+
: null;
|
|
295
341
|
if (!target) throw new Error("The selected desktop target is no longer available.");
|
|
296
342
|
invalidateRefresh();
|
|
343
|
+
const readRequestId = requestRef.current.id;
|
|
344
|
+
const selectionRevision = ++selectionRevisionRef.current;
|
|
345
|
+
const isCurrentSelection = () =>
|
|
346
|
+
isCurrentSource() && selectionRevisionRef.current === selectionRevision;
|
|
347
|
+
// Once admitted, a queued observation survives immediate input.
|
|
348
|
+
const isCurrentSelectionRead = () =>
|
|
349
|
+
isCurrentSelection() && requestRef.current.id === readRequestId;
|
|
297
350
|
selectedTargetIdRef.current = target.id;
|
|
298
|
-
observationRef.current = { computerSessionId, observation: null };
|
|
351
|
+
observationRef.current = { source, computerSessionId, observation: null };
|
|
299
352
|
setState((current) =>
|
|
353
|
+
isCurrentSelection() &&
|
|
354
|
+
current.source === source &&
|
|
300
355
|
current.computerSessionId === computerSessionId
|
|
301
356
|
? {
|
|
302
357
|
...current,
|
|
@@ -313,32 +368,40 @@ export function useComputerSession(options: UseComputerSessionOptions): UseCompu
|
|
|
313
368
|
computerSessionId,
|
|
314
369
|
target.id,
|
|
315
370
|
);
|
|
316
|
-
if (selectedTargetIdRef.current !== target.id) return target;
|
|
317
|
-
observationRef.current = { computerSessionId, observation };
|
|
371
|
+
if (!isCurrentSelectionRead() || selectedTargetIdRef.current !== target.id) return target;
|
|
372
|
+
observationRef.current = { source, computerSessionId, observation };
|
|
318
373
|
setState((current) =>
|
|
319
|
-
|
|
374
|
+
isCurrentSelection() &&
|
|
375
|
+
current.source === source &&
|
|
376
|
+
current.computerSessionId === computerSessionId &&
|
|
377
|
+
current.selectedTargetId === target.id
|
|
320
378
|
? { ...current, observation, loading: false, error: null }
|
|
321
379
|
: current,
|
|
322
380
|
);
|
|
323
381
|
return observation.target;
|
|
324
382
|
} catch (cause) {
|
|
325
383
|
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
326
|
-
|
|
327
|
-
current
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
:
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
384
|
+
if (isCurrentSelectionRead()) {
|
|
385
|
+
setState((current) =>
|
|
386
|
+
isCurrentSelection() &&
|
|
387
|
+
current.source === source &&
|
|
388
|
+
current.computerSessionId === computerSessionId &&
|
|
389
|
+
current.selectedTargetId === target.id
|
|
390
|
+
? {
|
|
391
|
+
...current,
|
|
392
|
+
loading: false,
|
|
393
|
+
error,
|
|
394
|
+
controlError: isInteractionControlUnavailable(error, "observation")
|
|
395
|
+
? error
|
|
396
|
+
: current.controlError,
|
|
397
|
+
}
|
|
398
|
+
: current,
|
|
399
|
+
);
|
|
400
|
+
}
|
|
338
401
|
throw error;
|
|
339
402
|
}
|
|
340
403
|
},
|
|
341
|
-
[client, computerSessionId, invalidateRefresh, workspaceId],
|
|
404
|
+
[client, computerSessionId, invalidateRefresh, isCurrentSource, source, workspaceId],
|
|
342
405
|
);
|
|
343
406
|
|
|
344
407
|
const dispatchAction = useCallback(
|
|
@@ -348,24 +411,30 @@ export function useComputerSession(options: UseComputerSessionOptions): UseCompu
|
|
|
348
411
|
frame: ComputerFrame | null,
|
|
349
412
|
): Promise<ComputerActionReceipt> => {
|
|
350
413
|
if (!computerSessionId) throw new Error("No desktop is selected.");
|
|
414
|
+
if (!isCurrentSource()) throw new Error("The desktop source is no longer selected.");
|
|
351
415
|
if (frame && frame.computerSessionId !== computerSessionId) {
|
|
352
416
|
throw new Error("The displayed desktop frame belongs to another desktop session.");
|
|
353
417
|
}
|
|
354
418
|
const currentObservation =
|
|
419
|
+
observationRef.current.source === source &&
|
|
355
420
|
observationRef.current.computerSessionId === computerSessionId
|
|
356
421
|
? observationRef.current.observation
|
|
357
422
|
: null;
|
|
358
423
|
const focusTarget =
|
|
359
|
-
action.type === "focus"
|
|
424
|
+
action.type === "focus" && targetsRef.current.source === source
|
|
360
425
|
? (targetsRef.current.targets.find((candidate) => candidate.id === action.targetId) ??
|
|
361
426
|
null)
|
|
362
427
|
: null;
|
|
363
|
-
const frameTarget =
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
428
|
+
const frameTarget =
|
|
429
|
+
frame && targetsRef.current.source === source
|
|
430
|
+
? currentObservation?.target.id === frame.targetId
|
|
431
|
+
? currentObservation.target
|
|
432
|
+
: (targetsRef.current.targets.find((candidate) => candidate.id === frame.targetId) ??
|
|
433
|
+
null)
|
|
434
|
+
: null;
|
|
367
435
|
const inputTarget =
|
|
368
436
|
(action.type === "keyboard" || action.type === "clipboard") &&
|
|
437
|
+
targetsRef.current.source === source &&
|
|
369
438
|
targetsRef.current.computerSessionId === computerSessionId
|
|
370
439
|
? targetsRef.current.targets.find(
|
|
371
440
|
(candidate) => candidate.id === selectedTargetIdRef.current,
|
|
@@ -377,6 +446,13 @@ export function useComputerSession(options: UseComputerSessionOptions): UseCompu
|
|
|
377
446
|
if (frame && frame.targetId !== selectedTargetIdRef.current) {
|
|
378
447
|
throw new Error("The displayed desktop frame is no longer selected.");
|
|
379
448
|
}
|
|
449
|
+
if (
|
|
450
|
+
frame &&
|
|
451
|
+
(frame.controllerGeneration !== target.controllerGeneration ||
|
|
452
|
+
frame.targetGeneration !== target.targetGeneration)
|
|
453
|
+
) {
|
|
454
|
+
throw new Error("The displayed desktop frame belongs to an earlier target generation.");
|
|
455
|
+
}
|
|
380
456
|
if (action.type === "pointer") {
|
|
381
457
|
const expectedFrameId = frame?.frameId ?? currentObservation?.frameId ?? null;
|
|
382
458
|
if (!expectedFrameId || action.frameId !== expectedFrameId) {
|
|
@@ -386,38 +462,142 @@ export function useComputerSession(options: UseComputerSessionOptions): UseCompu
|
|
|
386
462
|
if (action.type === "semantic" && !currentObservation) {
|
|
387
463
|
throw new Error("The desktop accessibility tree is not ready for input.");
|
|
388
464
|
}
|
|
465
|
+
const selectedTargetId = selectedTargetIdRef.current;
|
|
466
|
+
const selectedTarget = targetsRef.current.targets.find(
|
|
467
|
+
(candidate) => candidate.id === selectedTargetId,
|
|
468
|
+
);
|
|
469
|
+
const selectedFence = selectedTarget && {
|
|
470
|
+
id: selectedTarget.id,
|
|
471
|
+
computerSessionId: selectedTarget.computerSessionId,
|
|
472
|
+
controllerGeneration: selectedTarget.controllerGeneration,
|
|
473
|
+
targetGeneration: selectedTarget.targetGeneration,
|
|
474
|
+
};
|
|
475
|
+
const selectionRevision = selectionRevisionRef.current;
|
|
476
|
+
const isCurrentView = () =>
|
|
477
|
+
isCurrentSource() &&
|
|
478
|
+
selectionRevisionRef.current === selectionRevision &&
|
|
479
|
+
selectedTargetIdRef.current === selectedTargetId &&
|
|
480
|
+
targetsRef.current.source === source &&
|
|
481
|
+
targetsRef.current.computerSessionId === computerSessionId &&
|
|
482
|
+
sameTargetFence(
|
|
483
|
+
selectedFence,
|
|
484
|
+
targetsRef.current.targets.find((candidate) => candidate.id === selectedTargetId),
|
|
485
|
+
);
|
|
486
|
+
const resultOrder = ++actionResultOrderRef.current.next;
|
|
487
|
+
let admitted = false;
|
|
488
|
+
let resultTargetId = selectedTargetId;
|
|
489
|
+
let resultFence = selectedFence;
|
|
490
|
+
const isCurrentResultView = () =>
|
|
491
|
+
isCurrentSource() &&
|
|
492
|
+
selectionRevisionRef.current === selectionRevision &&
|
|
493
|
+
selectedTargetIdRef.current === resultTargetId &&
|
|
494
|
+
targetsRef.current.source === source &&
|
|
495
|
+
sameTargetFence(
|
|
496
|
+
resultFence,
|
|
497
|
+
targetsRef.current.targets.find((candidate) => candidate.id === resultTargetId),
|
|
498
|
+
);
|
|
499
|
+
const canProjectResult = () => {
|
|
500
|
+
if (!admitted) {
|
|
501
|
+
if (!isCurrentView() || resultOrder < actionResultOrderRef.current.settled) return false;
|
|
502
|
+
actionResultOrderRef.current.settled = resultOrder;
|
|
503
|
+
admitted = true;
|
|
504
|
+
invalidateRefresh();
|
|
505
|
+
}
|
|
506
|
+
return isCurrentResultView() && resultOrder === actionResultOrderRef.current.settled;
|
|
507
|
+
};
|
|
508
|
+
const projectState = (update: (current: ComputerControlState) => ComputerControlState) => {
|
|
509
|
+
setState((current) =>
|
|
510
|
+
admitted &&
|
|
511
|
+
isCurrentResultView() &&
|
|
512
|
+
current.source === source &&
|
|
513
|
+
current.computerSessionId === computerSessionId &&
|
|
514
|
+
current.actionResultOrder <= resultOrder
|
|
515
|
+
? { ...update(current), actionResultOrder: resultOrder }
|
|
516
|
+
: current,
|
|
517
|
+
);
|
|
518
|
+
};
|
|
389
519
|
|
|
390
520
|
return await runMutation(computerSessionId, async () => {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
const
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
521
|
+
try {
|
|
522
|
+
const receipt = await client.actInComputer(workspaceId, computerSessionId, {
|
|
523
|
+
operationId,
|
|
524
|
+
targetId: target.id,
|
|
525
|
+
expectedTargetGeneration: frame?.targetGeneration ?? target.targetGeneration,
|
|
526
|
+
expectedObservationId:
|
|
527
|
+
action.type === "pointer" ? null : (currentObservation?.observationId ?? null),
|
|
528
|
+
expectedFrameId: action.type === "pointer" ? action.frameId : null,
|
|
529
|
+
action,
|
|
530
|
+
});
|
|
531
|
+
const receiptSettled =
|
|
532
|
+
receipt.state === "completed" ||
|
|
533
|
+
receipt.state === "failed" ||
|
|
534
|
+
receipt.state === "outcome_unknown";
|
|
535
|
+
actionResultFences.set(receipt, () =>
|
|
536
|
+
admitted
|
|
537
|
+
? isCurrentResultView() && resultOrder === actionResultOrderRef.current.settled
|
|
538
|
+
: isCurrentView() && resultOrder >= actionResultOrderRef.current.settled,
|
|
539
|
+
);
|
|
540
|
+
// A settled result may project only until a later dispatched action
|
|
541
|
+
// settles for this view. Pending later input does not hide this result.
|
|
542
|
+
if (!receiptSettled || !canProjectResult()) return receipt;
|
|
543
|
+
if (receipt.observation) {
|
|
544
|
+
const observation = receipt.observation;
|
|
545
|
+
observationRef.current = { source, computerSessionId, observation };
|
|
546
|
+
selectedTargetIdRef.current = observation.target.id;
|
|
547
|
+
targetsRef.current.targets = replaceTarget(
|
|
548
|
+
targetsRef.current.targets,
|
|
549
|
+
observation.target,
|
|
550
|
+
);
|
|
551
|
+
resultTargetId = observation.target.id;
|
|
552
|
+
resultFence = {
|
|
553
|
+
id: observation.target.id,
|
|
554
|
+
computerSessionId: observation.target.computerSessionId,
|
|
555
|
+
controllerGeneration: observation.target.controllerGeneration,
|
|
556
|
+
targetGeneration: observation.target.targetGeneration,
|
|
557
|
+
};
|
|
558
|
+
projectState((current) => ({
|
|
559
|
+
...current,
|
|
560
|
+
error: null,
|
|
561
|
+
controlError: null,
|
|
562
|
+
observation,
|
|
563
|
+
selectedTargetId: observation.target.id,
|
|
564
|
+
targets: replaceTarget(current.targets, observation.target),
|
|
565
|
+
}));
|
|
566
|
+
} else {
|
|
567
|
+
projectState((current) => ({ ...current, error: null, controlError: null }));
|
|
568
|
+
void refresh();
|
|
569
|
+
}
|
|
570
|
+
return receipt;
|
|
571
|
+
} catch (cause) {
|
|
572
|
+
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
573
|
+
actionResultFences.set(
|
|
574
|
+
error,
|
|
575
|
+
() =>
|
|
576
|
+
admitted &&
|
|
577
|
+
isCurrentResultView() &&
|
|
578
|
+
resultOrder === actionResultOrderRef.current.settled,
|
|
413
579
|
);
|
|
414
|
-
|
|
415
|
-
|
|
580
|
+
if (canProjectResult()) {
|
|
581
|
+
projectState((current) => ({
|
|
582
|
+
...current,
|
|
583
|
+
error,
|
|
584
|
+
controlError: isInteractionControlUnavailable(error) ? error : current.controlError,
|
|
585
|
+
}));
|
|
586
|
+
}
|
|
587
|
+
throw error;
|
|
416
588
|
}
|
|
417
|
-
return receipt;
|
|
418
589
|
});
|
|
419
590
|
},
|
|
420
|
-
[
|
|
591
|
+
[
|
|
592
|
+
client,
|
|
593
|
+
computerSessionId,
|
|
594
|
+
invalidateRefresh,
|
|
595
|
+
isCurrentSource,
|
|
596
|
+
refresh,
|
|
597
|
+
runMutation,
|
|
598
|
+
source,
|
|
599
|
+
workspaceId,
|
|
600
|
+
],
|
|
421
601
|
);
|
|
422
602
|
|
|
423
603
|
const act = useCallback(
|
|
@@ -439,11 +619,38 @@ export function useComputerSession(options: UseComputerSessionOptions): UseCompu
|
|
|
439
619
|
|
|
440
620
|
const readClipboard = useCallback(async (): Promise<ComputerClipboard> => {
|
|
441
621
|
if (!computerSessionId) throw new Error("No desktop is selected.");
|
|
622
|
+
const selectionRevision = selectionRevisionRef.current;
|
|
623
|
+
const settledOrder = actionResultOrderRef.current.settled;
|
|
624
|
+
const selectedTargetId = selectedTargetIdRef.current;
|
|
625
|
+
const selectedTarget = targetsRef.current.targets.find(
|
|
626
|
+
(candidate) => candidate.id === selectedTargetId,
|
|
627
|
+
);
|
|
628
|
+
const selectedFence = selectedTarget && {
|
|
629
|
+
id: selectedTarget.id,
|
|
630
|
+
computerSessionId: selectedTarget.computerSessionId,
|
|
631
|
+
controllerGeneration: selectedTarget.controllerGeneration,
|
|
632
|
+
targetGeneration: selectedTarget.targetGeneration,
|
|
633
|
+
};
|
|
634
|
+
const isCurrentRead = () =>
|
|
635
|
+
isCurrentSource() &&
|
|
636
|
+
selectionRevisionRef.current === selectionRevision &&
|
|
637
|
+
actionResultOrderRef.current.settled === settledOrder &&
|
|
638
|
+
selectedTargetIdRef.current === selectedTargetId &&
|
|
639
|
+
targetsRef.current.source === source &&
|
|
640
|
+
sameTargetFence(
|
|
641
|
+
selectedFence,
|
|
642
|
+
targetsRef.current.targets.find((candidate) => candidate.id === selectedTargetId),
|
|
643
|
+
);
|
|
442
644
|
try {
|
|
443
|
-
|
|
645
|
+
const clipboard = await client.readComputerClipboard(workspaceId, computerSessionId);
|
|
646
|
+
actionResultFences.set(clipboard, isCurrentRead);
|
|
647
|
+
return clipboard;
|
|
444
648
|
} catch (cause) {
|
|
445
649
|
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
650
|
+
actionResultFences.set(error, isCurrentRead);
|
|
446
651
|
setState((current) =>
|
|
652
|
+
isCurrentRead() &&
|
|
653
|
+
current.source === source &&
|
|
447
654
|
current.computerSessionId === computerSessionId
|
|
448
655
|
? {
|
|
449
656
|
...current,
|
|
@@ -454,7 +661,7 @@ export function useComputerSession(options: UseComputerSessionOptions): UseCompu
|
|
|
454
661
|
);
|
|
455
662
|
throw error;
|
|
456
663
|
}
|
|
457
|
-
}, [client, computerSessionId, workspaceId]);
|
|
664
|
+
}, [client, computerSessionId, isCurrentSource, source, workspaceId]);
|
|
458
665
|
|
|
459
666
|
return {
|
|
460
667
|
session: visible.session,
|
|
@@ -474,7 +681,16 @@ export function useComputerSession(options: UseComputerSessionOptions): UseCompu
|
|
|
474
681
|
};
|
|
475
682
|
}
|
|
476
683
|
|
|
684
|
+
type ComputerControlSource = Pick<
|
|
685
|
+
ReturnType<typeof useEmbeddedComputerInteraction>,
|
|
686
|
+
"client" | "workspaceId"
|
|
687
|
+
> & {
|
|
688
|
+
computerSessionId: string | null;
|
|
689
|
+
enabled: boolean;
|
|
690
|
+
};
|
|
691
|
+
|
|
477
692
|
type ComputerControlState = {
|
|
693
|
+
source: ComputerControlSource;
|
|
478
694
|
computerSessionId: string | null;
|
|
479
695
|
session: ComputerSession | null;
|
|
480
696
|
targets: ComputerTarget[];
|
|
@@ -484,11 +700,13 @@ type ComputerControlState = {
|
|
|
484
700
|
mutating: boolean;
|
|
485
701
|
error: Error | null;
|
|
486
702
|
controlError: Error | null;
|
|
703
|
+
actionResultOrder: number;
|
|
487
704
|
};
|
|
488
705
|
|
|
489
|
-
function emptyState(
|
|
706
|
+
function emptyState(source: ComputerControlSource, loading: boolean): ComputerControlState {
|
|
490
707
|
return {
|
|
491
|
-
|
|
708
|
+
source,
|
|
709
|
+
computerSessionId: source.computerSessionId,
|
|
492
710
|
session: null,
|
|
493
711
|
targets: [],
|
|
494
712
|
selectedTargetId: null,
|
|
@@ -497,6 +715,7 @@ function emptyState(computerSessionId: string | null, loading: boolean): Compute
|
|
|
497
715
|
mutating: false,
|
|
498
716
|
error: null,
|
|
499
717
|
controlError: null,
|
|
718
|
+
actionResultOrder: 0,
|
|
500
719
|
};
|
|
501
720
|
}
|
|
502
721
|
|
|
@@ -538,3 +757,20 @@ function replaceTarget(
|
|
|
538
757
|
target,
|
|
539
758
|
]);
|
|
540
759
|
}
|
|
760
|
+
|
|
761
|
+
type ComputerTargetFence = Pick<
|
|
762
|
+
ComputerTarget,
|
|
763
|
+
"id" | "computerSessionId" | "controllerGeneration" | "targetGeneration"
|
|
764
|
+
>;
|
|
765
|
+
|
|
766
|
+
function sameTargetFence(
|
|
767
|
+
left: ComputerTargetFence | undefined,
|
|
768
|
+
right: ComputerTargetFence | undefined,
|
|
769
|
+
): boolean {
|
|
770
|
+
return (
|
|
771
|
+
left?.id === right?.id &&
|
|
772
|
+
left?.computerSessionId === right?.computerSessionId &&
|
|
773
|
+
left?.controllerGeneration === right?.controllerGeneration &&
|
|
774
|
+
left?.targetGeneration === right?.targetGeneration
|
|
775
|
+
);
|
|
776
|
+
}
|