@nextop-os/browser-node 0.0.15 → 0.0.17
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 +6 -4
- package/dist/chunk-XCKUTY7D.js +852 -0
- package/dist/chunk-XCKUTY7D.js.map +1 -0
- package/dist/electron-main/index.d.ts +14 -40
- package/dist/electron-main/index.js +487 -35
- package/dist/electron-main/index.js.map +1 -1
- package/dist/index.d.ts +110 -3
- package/dist/react/index.d.ts +6 -16
- package/dist/react/index.js +3 -5
- package/dist/workbench/index.d.ts +0 -1
- package/dist/workbench/index.js +1 -1
- package/package.json +7 -4
- package/dist/chunk-TLA56UW3.js +0 -554
- package/dist/chunk-TLA56UW3.js.map +0 -1
- package/dist/types-4cyQPaaT.d.ts +0 -110
|
@@ -0,0 +1,852 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveBrowserSessionPartition
|
|
3
|
+
} from "./chunk-OTK5YBCK.js";
|
|
4
|
+
|
|
5
|
+
// src/react/BrowserNode.tsx
|
|
6
|
+
import {
|
|
7
|
+
ArrowLeftIcon,
|
|
8
|
+
ArrowRightIcon,
|
|
9
|
+
Button,
|
|
10
|
+
LaunchIcon,
|
|
11
|
+
LoadingIcon,
|
|
12
|
+
RefreshIcon,
|
|
13
|
+
cn
|
|
14
|
+
} from "@nextop-os/ui-system";
|
|
15
|
+
|
|
16
|
+
// src/react/useBrowserNodeController.ts
|
|
17
|
+
import { useEffect, useMemo } from "react";
|
|
18
|
+
import { useExternalStoreSnapshot } from "@nextop-os/ui-react-hooks";
|
|
19
|
+
|
|
20
|
+
// src/core/nodeController.ts
|
|
21
|
+
var controllerRegistry = /* @__PURE__ */ new Map();
|
|
22
|
+
function acquireBrowserNodeController(input) {
|
|
23
|
+
const existing = controllerRegistry.get(input.nodeId);
|
|
24
|
+
const entry = existing ?? createBrowserNodeControllerEntry({
|
|
25
|
+
defaultUrl: input.defaultUrl,
|
|
26
|
+
feature: input.feature,
|
|
27
|
+
nodeId: input.nodeId,
|
|
28
|
+
profileId: input.profileId ?? null,
|
|
29
|
+
sessionMode: input.sessionMode ?? "shared"
|
|
30
|
+
});
|
|
31
|
+
entry.context = {
|
|
32
|
+
defaultUrl: input.defaultUrl,
|
|
33
|
+
feature: input.feature,
|
|
34
|
+
nodeId: input.nodeId,
|
|
35
|
+
profileId: input.profileId ?? null,
|
|
36
|
+
sessionMode: input.sessionMode ?? "shared"
|
|
37
|
+
};
|
|
38
|
+
if (!existing) {
|
|
39
|
+
controllerRegistry.set(input.nodeId, entry);
|
|
40
|
+
}
|
|
41
|
+
reconcileBrowserNodeControllerState(entry, {
|
|
42
|
+
allowAutoActivate: false,
|
|
43
|
+
notifyListeners: false
|
|
44
|
+
});
|
|
45
|
+
return entry.controller;
|
|
46
|
+
}
|
|
47
|
+
function createBrowserNodeControllerEntry(context) {
|
|
48
|
+
const runtime = context.feature.runtimeStore.getNodeState(context.nodeId);
|
|
49
|
+
const displayUrl = resolveBrowserNodeDisplayUrl(runtime, context.defaultUrl);
|
|
50
|
+
const entry = {
|
|
51
|
+
connectedRelease: null,
|
|
52
|
+
controller: null,
|
|
53
|
+
context,
|
|
54
|
+
lastColdActivationUrl: null,
|
|
55
|
+
listeners: /* @__PURE__ */ new Set(),
|
|
56
|
+
pendingColdActivationUrl: null,
|
|
57
|
+
refCount: 0,
|
|
58
|
+
runtimeUnsubscribe: null,
|
|
59
|
+
state: {
|
|
60
|
+
displayUrl,
|
|
61
|
+
draftUrl: displayUrl,
|
|
62
|
+
runtime
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
entry.controller = {
|
|
66
|
+
getState() {
|
|
67
|
+
return entry.state;
|
|
68
|
+
},
|
|
69
|
+
goBack() {
|
|
70
|
+
return entry.context.feature.hostApi.goBack({
|
|
71
|
+
nodeId: entry.context.nodeId
|
|
72
|
+
});
|
|
73
|
+
},
|
|
74
|
+
goForward() {
|
|
75
|
+
return entry.context.feature.hostApi.goForward({
|
|
76
|
+
nodeId: entry.context.nodeId
|
|
77
|
+
});
|
|
78
|
+
},
|
|
79
|
+
reload() {
|
|
80
|
+
return entry.context.feature.hostApi.reload({
|
|
81
|
+
nodeId: entry.context.nodeId
|
|
82
|
+
});
|
|
83
|
+
},
|
|
84
|
+
release() {
|
|
85
|
+
entry.refCount = Math.max(0, entry.refCount - 1);
|
|
86
|
+
if (entry.refCount > 0) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
entry.connectedRelease?.();
|
|
90
|
+
entry.connectedRelease = null;
|
|
91
|
+
entry.runtimeUnsubscribe?.();
|
|
92
|
+
entry.runtimeUnsubscribe = null;
|
|
93
|
+
controllerRegistry.delete(entry.context.nodeId);
|
|
94
|
+
},
|
|
95
|
+
retain() {
|
|
96
|
+
entry.refCount += 1;
|
|
97
|
+
if (entry.refCount > 1) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (!controllerRegistry.has(entry.context.nodeId)) {
|
|
101
|
+
controllerRegistry.set(entry.context.nodeId, entry);
|
|
102
|
+
}
|
|
103
|
+
entry.connectedRelease = entry.context.feature.connect();
|
|
104
|
+
entry.runtimeUnsubscribe = entry.context.feature.runtimeStore.subscribe(
|
|
105
|
+
() => {
|
|
106
|
+
reconcileBrowserNodeControllerState(entry, {
|
|
107
|
+
allowAutoActivate: true,
|
|
108
|
+
notifyListeners: true
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
);
|
|
112
|
+
reconcileBrowserNodeControllerState(entry, {
|
|
113
|
+
allowAutoActivate: true,
|
|
114
|
+
notifyListeners: true
|
|
115
|
+
});
|
|
116
|
+
},
|
|
117
|
+
setDraftUrl(nextUrl) {
|
|
118
|
+
if (entry.state.draftUrl === nextUrl) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
entry.state = {
|
|
122
|
+
...entry.state,
|
|
123
|
+
draftUrl: nextUrl
|
|
124
|
+
};
|
|
125
|
+
notifyBrowserNodeControllerListeners(entry);
|
|
126
|
+
},
|
|
127
|
+
subscribe(listener) {
|
|
128
|
+
entry.listeners.add(listener);
|
|
129
|
+
return () => {
|
|
130
|
+
entry.listeners.delete(listener);
|
|
131
|
+
};
|
|
132
|
+
},
|
|
133
|
+
async submitDraftUrl() {
|
|
134
|
+
const resolved = entry.context.feature.resolveAddressInput(
|
|
135
|
+
entry.state.draftUrl
|
|
136
|
+
);
|
|
137
|
+
if (!resolved.url) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (entry.state.draftUrl !== resolved.url) {
|
|
141
|
+
entry.state = {
|
|
142
|
+
...entry.state,
|
|
143
|
+
draftUrl: resolved.url
|
|
144
|
+
};
|
|
145
|
+
notifyBrowserNodeControllerListeners(entry);
|
|
146
|
+
}
|
|
147
|
+
await entry.context.feature.hostApi.navigate({
|
|
148
|
+
nodeId: entry.context.nodeId,
|
|
149
|
+
url: resolved.url
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
return entry;
|
|
154
|
+
}
|
|
155
|
+
function notifyBrowserNodeControllerListeners(entry) {
|
|
156
|
+
for (const listener of entry.listeners) {
|
|
157
|
+
listener();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function resolveBrowserNodeDisplayUrl(runtime, defaultUrl) {
|
|
161
|
+
const resolvedRuntimeUrl = runtime.url?.trim() ?? "";
|
|
162
|
+
return resolvedRuntimeUrl.length > 0 ? resolvedRuntimeUrl : defaultUrl;
|
|
163
|
+
}
|
|
164
|
+
function reconcileBrowserNodeControllerState(entry, options) {
|
|
165
|
+
const runtime = entry.context.feature.runtimeStore.getNodeState(
|
|
166
|
+
entry.context.nodeId
|
|
167
|
+
);
|
|
168
|
+
const displayUrl = resolveBrowserNodeDisplayUrl(
|
|
169
|
+
runtime,
|
|
170
|
+
entry.context.defaultUrl
|
|
171
|
+
);
|
|
172
|
+
const nextDraftUrl = displayUrl !== entry.state.displayUrl ? displayUrl : entry.state.draftUrl;
|
|
173
|
+
const changed = entry.state.runtime !== runtime || entry.state.displayUrl !== displayUrl || entry.state.draftUrl !== nextDraftUrl;
|
|
174
|
+
if (changed) {
|
|
175
|
+
entry.state = {
|
|
176
|
+
displayUrl,
|
|
177
|
+
draftUrl: nextDraftUrl,
|
|
178
|
+
runtime
|
|
179
|
+
};
|
|
180
|
+
if (options.notifyListeners) {
|
|
181
|
+
notifyBrowserNodeControllerListeners(entry);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (options.allowAutoActivate) {
|
|
185
|
+
void maybeActivateColdBrowserNode(entry).catch(() => void 0);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
async function maybeActivateColdBrowserNode(entry) {
|
|
189
|
+
const { defaultUrl, feature, nodeId, profileId, sessionMode } = entry.context;
|
|
190
|
+
const trimmedUrl = defaultUrl.trim();
|
|
191
|
+
if (trimmedUrl.length === 0 || entry.state.runtime.lifecycle !== "cold" || entry.state.runtime.isLoading || entry.state.runtime.error !== null || entry.pendingColdActivationUrl === trimmedUrl || entry.lastColdActivationUrl === trimmedUrl) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
entry.pendingColdActivationUrl = trimmedUrl;
|
|
195
|
+
try {
|
|
196
|
+
await feature.hostApi.activate({
|
|
197
|
+
nodeId,
|
|
198
|
+
profileId,
|
|
199
|
+
sessionMode,
|
|
200
|
+
url: trimmedUrl
|
|
201
|
+
});
|
|
202
|
+
entry.lastColdActivationUrl = trimmedUrl;
|
|
203
|
+
} finally {
|
|
204
|
+
if (entry.pendingColdActivationUrl === trimmedUrl) {
|
|
205
|
+
entry.pendingColdActivationUrl = null;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// src/react/useBrowserNodeController.ts
|
|
211
|
+
function useBrowserNodeController(input) {
|
|
212
|
+
const controller = useMemo(
|
|
213
|
+
() => acquireBrowserNodeController({
|
|
214
|
+
defaultUrl: input.defaultUrl,
|
|
215
|
+
feature: input.feature,
|
|
216
|
+
nodeId: input.nodeId,
|
|
217
|
+
profileId: input.profileId ?? null,
|
|
218
|
+
sessionMode: input.sessionMode ?? "shared"
|
|
219
|
+
}),
|
|
220
|
+
[
|
|
221
|
+
input.defaultUrl,
|
|
222
|
+
input.feature,
|
|
223
|
+
input.nodeId,
|
|
224
|
+
input.profileId,
|
|
225
|
+
input.sessionMode
|
|
226
|
+
]
|
|
227
|
+
);
|
|
228
|
+
useEffect(() => {
|
|
229
|
+
controller.retain();
|
|
230
|
+
return () => {
|
|
231
|
+
controller.release();
|
|
232
|
+
};
|
|
233
|
+
}, [controller]);
|
|
234
|
+
const state = useExternalStoreSnapshot({
|
|
235
|
+
getSnapshot() {
|
|
236
|
+
return controller.getState();
|
|
237
|
+
},
|
|
238
|
+
subscribe(listener) {
|
|
239
|
+
return controller.subscribe(listener);
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
return {
|
|
243
|
+
controller,
|
|
244
|
+
state
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// src/react/useBrowserNodeWebview.ts
|
|
249
|
+
import { useCallback, useEffect as useEffect2, useMemo as useMemo2 } from "react";
|
|
250
|
+
import { useExternalStoreSnapshot as useExternalStoreSnapshot2 } from "@nextop-os/ui-react-hooks";
|
|
251
|
+
|
|
252
|
+
// src/core/webviewController.ts
|
|
253
|
+
var browserGuestUnregisterGraceMs = 250;
|
|
254
|
+
var webviewControllerRegistry = /* @__PURE__ */ new Map();
|
|
255
|
+
var pendingGuestIdsByNodeId = /* @__PURE__ */ new Map();
|
|
256
|
+
var pendingUnregisterTimersByNodeId = /* @__PURE__ */ new Map();
|
|
257
|
+
function acquireBrowserNodeWebviewController(input) {
|
|
258
|
+
const existing = webviewControllerRegistry.get(input.nodeId);
|
|
259
|
+
const entry = existing ?? createBrowserNodeWebviewControllerEntry({
|
|
260
|
+
feature: input.feature,
|
|
261
|
+
initialUrl: input.initialUrl,
|
|
262
|
+
lifecycle: input.lifecycle,
|
|
263
|
+
nodeId: input.nodeId,
|
|
264
|
+
onGuestInteraction: input.onGuestInteraction,
|
|
265
|
+
profileId: input.profileId,
|
|
266
|
+
sessionMode: input.sessionMode
|
|
267
|
+
});
|
|
268
|
+
entry.context = {
|
|
269
|
+
feature: input.feature,
|
|
270
|
+
initialUrl: input.initialUrl,
|
|
271
|
+
lifecycle: input.lifecycle,
|
|
272
|
+
nodeId: input.nodeId,
|
|
273
|
+
onGuestInteraction: input.onGuestInteraction,
|
|
274
|
+
profileId: input.profileId,
|
|
275
|
+
sessionMode: input.sessionMode
|
|
276
|
+
};
|
|
277
|
+
if (!existing) {
|
|
278
|
+
webviewControllerRegistry.set(input.nodeId, entry);
|
|
279
|
+
}
|
|
280
|
+
return entry.controller;
|
|
281
|
+
}
|
|
282
|
+
function createBrowserNodeWebviewControllerEntry(context) {
|
|
283
|
+
const state = resolveBrowserNodeWebviewControllerState(context);
|
|
284
|
+
const entry = {
|
|
285
|
+
attachedListeners: [],
|
|
286
|
+
context,
|
|
287
|
+
controller: null,
|
|
288
|
+
listeners: /* @__PURE__ */ new Set(),
|
|
289
|
+
refCount: 0,
|
|
290
|
+
registeredGuestId: null,
|
|
291
|
+
registeringGuestId: null,
|
|
292
|
+
state,
|
|
293
|
+
webview: null
|
|
294
|
+
};
|
|
295
|
+
entry.controller = {
|
|
296
|
+
getState() {
|
|
297
|
+
return entry.state;
|
|
298
|
+
},
|
|
299
|
+
release() {
|
|
300
|
+
entry.refCount = Math.max(0, entry.refCount - 1);
|
|
301
|
+
if (entry.refCount > 0) {
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
scheduleBrowserNodeGuestUnregister(entry);
|
|
305
|
+
detachBrowserNodeWebview(entry);
|
|
306
|
+
webviewControllerRegistry.delete(entry.context.nodeId);
|
|
307
|
+
},
|
|
308
|
+
retain() {
|
|
309
|
+
entry.refCount += 1;
|
|
310
|
+
if (entry.refCount > 1) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
reconcileBrowserNodeWebviewControllerState(entry, {
|
|
314
|
+
allowHostEffects: true,
|
|
315
|
+
notifyListeners: true,
|
|
316
|
+
rebindWebview: true
|
|
317
|
+
});
|
|
318
|
+
},
|
|
319
|
+
setWebview(element) {
|
|
320
|
+
if (entry.webview === element) {
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
detachBrowserNodeWebview(entry);
|
|
324
|
+
entry.webview = element;
|
|
325
|
+
attachBrowserNodeWebview(entry);
|
|
326
|
+
},
|
|
327
|
+
sync() {
|
|
328
|
+
reconcileBrowserNodeWebviewControllerState(entry, {
|
|
329
|
+
allowHostEffects: true,
|
|
330
|
+
notifyListeners: true,
|
|
331
|
+
rebindWebview: true
|
|
332
|
+
});
|
|
333
|
+
},
|
|
334
|
+
subscribe(listener) {
|
|
335
|
+
entry.listeners.add(listener);
|
|
336
|
+
return () => {
|
|
337
|
+
entry.listeners.delete(listener);
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
return entry;
|
|
342
|
+
}
|
|
343
|
+
function resolveBrowserNodeWebviewControllerState(context) {
|
|
344
|
+
const webviewPartition = resolveBrowserSessionPartition({
|
|
345
|
+
profileId: context.profileId,
|
|
346
|
+
sessionMode: context.sessionMode
|
|
347
|
+
});
|
|
348
|
+
return {
|
|
349
|
+
shouldRenderWebview: context.lifecycle !== "cold",
|
|
350
|
+
webviewKey: `${context.nodeId}:${webviewPartition}`,
|
|
351
|
+
webviewPartition,
|
|
352
|
+
webviewSrc: resolveBrowserWebviewSrc(context.initialUrl)
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
function reconcileBrowserNodeWebviewControllerState(entry, options) {
|
|
356
|
+
const nextState = resolveBrowserNodeWebviewControllerState(entry.context);
|
|
357
|
+
const changed = entry.state.shouldRenderWebview !== nextState.shouldRenderWebview || entry.state.webviewKey !== nextState.webviewKey || entry.state.webviewPartition !== nextState.webviewPartition || entry.state.webviewSrc !== nextState.webviewSrc;
|
|
358
|
+
if (options.allowHostEffects) {
|
|
359
|
+
if (entry.context.lifecycle === "cold") {
|
|
360
|
+
scheduleBrowserNodeGuestUnregister(entry);
|
|
361
|
+
} else {
|
|
362
|
+
clearPendingBrowserNodeGuestUnregister(entry.context.nodeId);
|
|
363
|
+
void entry.context.feature.hostApi.prepareSession({
|
|
364
|
+
nodeId: entry.context.nodeId,
|
|
365
|
+
profileId: entry.context.profileId,
|
|
366
|
+
sessionMode: entry.context.sessionMode
|
|
367
|
+
}).catch(() => void 0);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
if (!changed && !options.rebindWebview) {
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
if (changed) {
|
|
374
|
+
entry.state = nextState;
|
|
375
|
+
}
|
|
376
|
+
detachBrowserNodeWebview(entry);
|
|
377
|
+
attachBrowserNodeWebview(entry);
|
|
378
|
+
if (changed && options.notifyListeners) {
|
|
379
|
+
notifyBrowserNodeWebviewControllerListeners(entry);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
function resolveBrowserWebviewSrc(url) {
|
|
383
|
+
const trimmed = url.trim();
|
|
384
|
+
return trimmed.length > 0 ? trimmed : "about:blank";
|
|
385
|
+
}
|
|
386
|
+
function notifyBrowserNodeWebviewControllerListeners(entry) {
|
|
387
|
+
for (const listener of entry.listeners) {
|
|
388
|
+
listener();
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
function clearPendingBrowserNodeGuestUnregister(nodeId) {
|
|
392
|
+
const timerId = pendingUnregisterTimersByNodeId.get(nodeId);
|
|
393
|
+
if (timerId !== void 0) {
|
|
394
|
+
globalThis.clearTimeout(timerId);
|
|
395
|
+
pendingUnregisterTimersByNodeId.delete(nodeId);
|
|
396
|
+
}
|
|
397
|
+
pendingGuestIdsByNodeId.delete(nodeId);
|
|
398
|
+
}
|
|
399
|
+
function scheduleBrowserNodeGuestUnregister(entry) {
|
|
400
|
+
const guestId = entry.registeredGuestId;
|
|
401
|
+
const nodeId = entry.context.nodeId;
|
|
402
|
+
entry.registeringGuestId = null;
|
|
403
|
+
if (guestId === null) {
|
|
404
|
+
clearPendingBrowserNodeGuestUnregister(nodeId);
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
entry.registeredGuestId = null;
|
|
408
|
+
clearPendingBrowserNodeGuestUnregister(nodeId);
|
|
409
|
+
pendingGuestIdsByNodeId.set(nodeId, guestId);
|
|
410
|
+
const timerId = globalThis.setTimeout(() => {
|
|
411
|
+
pendingUnregisterTimersByNodeId.delete(nodeId);
|
|
412
|
+
const pendingGuestId = pendingGuestIdsByNodeId.get(nodeId);
|
|
413
|
+
pendingGuestIdsByNodeId.delete(nodeId);
|
|
414
|
+
if (typeof pendingGuestId !== "number" || !Number.isFinite(pendingGuestId)) {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
void entry.context.feature.hostApi.unregisterGuest({
|
|
418
|
+
nodeId: entry.context.nodeId,
|
|
419
|
+
webContentsId: pendingGuestId
|
|
420
|
+
}).catch(() => void 0);
|
|
421
|
+
}, browserGuestUnregisterGraceMs);
|
|
422
|
+
pendingUnregisterTimersByNodeId.set(nodeId, timerId);
|
|
423
|
+
}
|
|
424
|
+
function detachBrowserNodeWebview(entry) {
|
|
425
|
+
if (!entry.webview) {
|
|
426
|
+
entry.attachedListeners = [];
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
for (const record of entry.attachedListeners) {
|
|
430
|
+
entry.webview.removeEventListener(record.event, record.listener);
|
|
431
|
+
}
|
|
432
|
+
entry.attachedListeners = [];
|
|
433
|
+
}
|
|
434
|
+
function attachBrowserNodeWebview(entry) {
|
|
435
|
+
const webview = entry.webview;
|
|
436
|
+
if (!webview || !entry.state.shouldRenderWebview) {
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
const registerGuest = async () => {
|
|
440
|
+
const guestId = webview.getWebContentsId?.();
|
|
441
|
+
if (typeof guestId !== "number" || !Number.isFinite(guestId) || guestId <= 0 || entry.registeredGuestId === guestId || entry.registeringGuestId === guestId) {
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
clearPendingBrowserNodeGuestUnregister(entry.context.nodeId);
|
|
445
|
+
entry.registeringGuestId = guestId;
|
|
446
|
+
try {
|
|
447
|
+
await entry.context.feature.hostApi.registerGuest({
|
|
448
|
+
nodeId: entry.context.nodeId,
|
|
449
|
+
profileId: entry.context.profileId,
|
|
450
|
+
sessionMode: entry.context.sessionMode,
|
|
451
|
+
webContentsId: guestId
|
|
452
|
+
});
|
|
453
|
+
entry.registeredGuestId = guestId;
|
|
454
|
+
} finally {
|
|
455
|
+
if (entry.registeringGuestId === guestId) {
|
|
456
|
+
entry.registeringGuestId = null;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
const handleDidAttach = () => {
|
|
461
|
+
void registerGuest().catch(() => void 0);
|
|
462
|
+
};
|
|
463
|
+
const handleDomReady = () => {
|
|
464
|
+
void registerGuest().catch(() => void 0);
|
|
465
|
+
};
|
|
466
|
+
const handleGuestInteraction = () => {
|
|
467
|
+
entry.context.onGuestInteraction?.();
|
|
468
|
+
};
|
|
469
|
+
const records = [
|
|
470
|
+
{ event: "did-attach", listener: handleDidAttach },
|
|
471
|
+
{ event: "dom-ready", listener: handleDomReady },
|
|
472
|
+
{ event: "focus", listener: handleGuestInteraction },
|
|
473
|
+
{ event: "ipc-message", listener: handleGuestInteraction }
|
|
474
|
+
];
|
|
475
|
+
for (const record of records) {
|
|
476
|
+
webview.addEventListener(record.event, record.listener);
|
|
477
|
+
}
|
|
478
|
+
entry.attachedListeners = records;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// src/react/useBrowserNodeWebview.ts
|
|
482
|
+
function useBrowserNodeWebview({
|
|
483
|
+
feature,
|
|
484
|
+
initialUrl,
|
|
485
|
+
lifecycle,
|
|
486
|
+
nodeId,
|
|
487
|
+
onGuestInteraction,
|
|
488
|
+
profileId,
|
|
489
|
+
sessionMode
|
|
490
|
+
}) {
|
|
491
|
+
const controller = useMemo2(
|
|
492
|
+
() => acquireBrowserNodeWebviewController({
|
|
493
|
+
feature,
|
|
494
|
+
initialUrl,
|
|
495
|
+
lifecycle,
|
|
496
|
+
nodeId,
|
|
497
|
+
onGuestInteraction,
|
|
498
|
+
profileId,
|
|
499
|
+
sessionMode
|
|
500
|
+
}),
|
|
501
|
+
[
|
|
502
|
+
feature,
|
|
503
|
+
initialUrl,
|
|
504
|
+
lifecycle,
|
|
505
|
+
nodeId,
|
|
506
|
+
onGuestInteraction,
|
|
507
|
+
profileId,
|
|
508
|
+
sessionMode
|
|
509
|
+
]
|
|
510
|
+
);
|
|
511
|
+
useEffect2(() => {
|
|
512
|
+
controller.retain();
|
|
513
|
+
return () => {
|
|
514
|
+
controller.release();
|
|
515
|
+
};
|
|
516
|
+
}, [controller]);
|
|
517
|
+
useEffect2(() => {
|
|
518
|
+
controller.sync();
|
|
519
|
+
}, [
|
|
520
|
+
controller,
|
|
521
|
+
initialUrl,
|
|
522
|
+
lifecycle,
|
|
523
|
+
nodeId,
|
|
524
|
+
onGuestInteraction,
|
|
525
|
+
profileId,
|
|
526
|
+
sessionMode
|
|
527
|
+
]);
|
|
528
|
+
const state = useExternalStoreSnapshot2({
|
|
529
|
+
getSnapshot() {
|
|
530
|
+
return controller.getState();
|
|
531
|
+
},
|
|
532
|
+
subscribe(listener) {
|
|
533
|
+
return controller.subscribe(listener);
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
const setWebviewRef = useCallback(
|
|
537
|
+
(element) => {
|
|
538
|
+
controller.setWebview(element);
|
|
539
|
+
},
|
|
540
|
+
[controller]
|
|
541
|
+
);
|
|
542
|
+
return {
|
|
543
|
+
shouldRenderWebview: state.shouldRenderWebview,
|
|
544
|
+
setWebviewRef,
|
|
545
|
+
webviewKey: state.webviewKey,
|
|
546
|
+
webviewPartition: state.webviewPartition,
|
|
547
|
+
webviewSrc: state.webviewSrc
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// src/react/BrowserNode.tsx
|
|
552
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
553
|
+
function BrowserNode({
|
|
554
|
+
defaultUrl,
|
|
555
|
+
feature,
|
|
556
|
+
nodeId,
|
|
557
|
+
onFocusRequest,
|
|
558
|
+
profileId = null,
|
|
559
|
+
sessionMode = "shared",
|
|
560
|
+
showHeader = true
|
|
561
|
+
}) {
|
|
562
|
+
const { controller, state } = useBrowserNodeController({
|
|
563
|
+
defaultUrl,
|
|
564
|
+
feature,
|
|
565
|
+
nodeId,
|
|
566
|
+
profileId,
|
|
567
|
+
sessionMode
|
|
568
|
+
});
|
|
569
|
+
const runtime = state.runtime;
|
|
570
|
+
const errorMessage = runtime.error ? formatBrowserNodeErrorMessage(feature, runtime.error) : null;
|
|
571
|
+
const {
|
|
572
|
+
shouldRenderWebview,
|
|
573
|
+
setWebviewRef,
|
|
574
|
+
webviewKey,
|
|
575
|
+
webviewPartition,
|
|
576
|
+
webviewSrc
|
|
577
|
+
} = useBrowserNodeWebview({
|
|
578
|
+
feature,
|
|
579
|
+
initialUrl: state.displayUrl,
|
|
580
|
+
lifecycle: runtime.lifecycle,
|
|
581
|
+
nodeId,
|
|
582
|
+
onGuestInteraction: onFocusRequest,
|
|
583
|
+
profileId,
|
|
584
|
+
sessionMode
|
|
585
|
+
});
|
|
586
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex h-full min-h-0 flex-col overflow-hidden bg-background", children: [
|
|
587
|
+
showHeader ? /* @__PURE__ */ jsx(
|
|
588
|
+
BrowserNodeHeader,
|
|
589
|
+
{
|
|
590
|
+
canGoBack: runtime.canGoBack,
|
|
591
|
+
canGoForward: runtime.canGoForward,
|
|
592
|
+
draftUrl: state.draftUrl,
|
|
593
|
+
feature,
|
|
594
|
+
isCold: runtime.lifecycle === "cold",
|
|
595
|
+
isLoading: runtime.isLoading,
|
|
596
|
+
onDraftUrlChange: (nextUrl) => controller.setDraftUrl(nextUrl),
|
|
597
|
+
onFocusRequest,
|
|
598
|
+
onSubmitUrl: () => {
|
|
599
|
+
void controller.submitDraftUrl().catch(() => void 0);
|
|
600
|
+
},
|
|
601
|
+
onGoBack: () => {
|
|
602
|
+
void controller.goBack().catch(() => void 0);
|
|
603
|
+
},
|
|
604
|
+
onGoForward: () => {
|
|
605
|
+
void controller.goForward().catch(() => void 0);
|
|
606
|
+
},
|
|
607
|
+
onReload: () => {
|
|
608
|
+
void controller.reload().catch(() => void 0);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
) : null,
|
|
612
|
+
/* @__PURE__ */ jsxs("div", { className: "relative min-h-0 flex-1 overflow-hidden bg-background", children: [
|
|
613
|
+
shouldRenderWebview ? /* @__PURE__ */ jsx(
|
|
614
|
+
"webview",
|
|
615
|
+
{
|
|
616
|
+
ref: setWebviewRef,
|
|
617
|
+
className: "absolute inset-0 h-full w-full border-0 bg-background",
|
|
618
|
+
"data-browser-node-webview": "true",
|
|
619
|
+
partition: webviewPartition,
|
|
620
|
+
src: webviewSrc
|
|
621
|
+
},
|
|
622
|
+
webviewKey
|
|
623
|
+
) : null,
|
|
624
|
+
errorMessage ? /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-0 z-10 flex items-center justify-end p-3 text-center", children: /* @__PURE__ */ jsxs(
|
|
625
|
+
"div",
|
|
626
|
+
{
|
|
627
|
+
className: "max-w-[min(320px,100%)] rounded-md border border-border bg-card/95 px-3 py-2 text-sm text-card-foreground shadow-panel",
|
|
628
|
+
role: "status",
|
|
629
|
+
"aria-live": "polite",
|
|
630
|
+
children: [
|
|
631
|
+
/* @__PURE__ */ jsx("div", { className: "font-medium", children: feature.i18n.t("loadFailed") }),
|
|
632
|
+
/* @__PURE__ */ jsx("div", { className: "mt-1 text-xs text-muted-foreground", children: errorMessage })
|
|
633
|
+
]
|
|
634
|
+
}
|
|
635
|
+
) }) : null
|
|
636
|
+
] })
|
|
637
|
+
] });
|
|
638
|
+
}
|
|
639
|
+
function BrowserNodeWorkbenchHeader({
|
|
640
|
+
className,
|
|
641
|
+
defaultActions,
|
|
642
|
+
defaultUrl,
|
|
643
|
+
dragHandleProps,
|
|
644
|
+
feature,
|
|
645
|
+
nodeId,
|
|
646
|
+
onCloseRequest,
|
|
647
|
+
onFocusRequest
|
|
648
|
+
}) {
|
|
649
|
+
const { controller, state } = useBrowserNodeController({
|
|
650
|
+
defaultUrl,
|
|
651
|
+
feature,
|
|
652
|
+
nodeId
|
|
653
|
+
});
|
|
654
|
+
const runtime = state.runtime;
|
|
655
|
+
return /* @__PURE__ */ jsx(
|
|
656
|
+
BrowserNodeHeader,
|
|
657
|
+
{
|
|
658
|
+
canGoBack: runtime.canGoBack,
|
|
659
|
+
canGoForward: runtime.canGoForward,
|
|
660
|
+
className,
|
|
661
|
+
defaultActions,
|
|
662
|
+
draftUrl: state.draftUrl,
|
|
663
|
+
dragHandleProps,
|
|
664
|
+
feature,
|
|
665
|
+
isCold: runtime.lifecycle === "cold",
|
|
666
|
+
isLoading: runtime.isLoading,
|
|
667
|
+
onCloseRequest,
|
|
668
|
+
onDraftUrlChange: (nextUrl) => controller.setDraftUrl(nextUrl),
|
|
669
|
+
onFocusRequest,
|
|
670
|
+
onSubmitUrl: () => {
|
|
671
|
+
void controller.submitDraftUrl().catch(() => void 0);
|
|
672
|
+
},
|
|
673
|
+
onGoBack: () => {
|
|
674
|
+
void controller.goBack().catch(() => void 0);
|
|
675
|
+
},
|
|
676
|
+
onGoForward: () => {
|
|
677
|
+
void controller.goForward().catch(() => void 0);
|
|
678
|
+
},
|
|
679
|
+
onReload: () => {
|
|
680
|
+
void controller.reload().catch(() => void 0);
|
|
681
|
+
},
|
|
682
|
+
withBorder: false
|
|
683
|
+
}
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
function BrowserNodeHeader({
|
|
687
|
+
canGoBack,
|
|
688
|
+
canGoForward,
|
|
689
|
+
className,
|
|
690
|
+
defaultActions,
|
|
691
|
+
draftUrl,
|
|
692
|
+
dragHandleProps,
|
|
693
|
+
feature,
|
|
694
|
+
isCold = false,
|
|
695
|
+
isLoading,
|
|
696
|
+
onCloseRequest,
|
|
697
|
+
onDraftUrlChange,
|
|
698
|
+
onFocusRequest,
|
|
699
|
+
onGoBack,
|
|
700
|
+
onGoForward,
|
|
701
|
+
onReload,
|
|
702
|
+
onSubmitUrl,
|
|
703
|
+
withBorder = true
|
|
704
|
+
}) {
|
|
705
|
+
return /* @__PURE__ */ jsxs(
|
|
706
|
+
"div",
|
|
707
|
+
{
|
|
708
|
+
className: cn(
|
|
709
|
+
"flex h-[var(--workbench-header-height,38px)] min-h-[var(--workbench-header-height,38px)] items-center gap-2 bg-[var(--workbench-window-header-bg)] px-2 pl-3",
|
|
710
|
+
withBorder ? "border-b border-border" : null,
|
|
711
|
+
className
|
|
712
|
+
),
|
|
713
|
+
"data-browser-node-header": "true",
|
|
714
|
+
onDoubleClick: (event) => {
|
|
715
|
+
if (event.target instanceof Element && event.target.closest(".nodrag")) {
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
event.stopPropagation();
|
|
719
|
+
dragHandleProps?.onDoubleClick?.(event);
|
|
720
|
+
},
|
|
721
|
+
children: [
|
|
722
|
+
/* @__PURE__ */ jsxs("div", { className: "inline-flex items-center gap-1", children: [
|
|
723
|
+
/* @__PURE__ */ jsx(
|
|
724
|
+
BrowserNodeHeaderButton,
|
|
725
|
+
{
|
|
726
|
+
disabled: !canGoBack,
|
|
727
|
+
label: feature.i18n.t("actions.back"),
|
|
728
|
+
onClick: onGoBack,
|
|
729
|
+
children: /* @__PURE__ */ jsx(ArrowLeftIcon, { className: "size-4" })
|
|
730
|
+
}
|
|
731
|
+
),
|
|
732
|
+
/* @__PURE__ */ jsx(
|
|
733
|
+
BrowserNodeHeaderButton,
|
|
734
|
+
{
|
|
735
|
+
disabled: !canGoForward,
|
|
736
|
+
label: feature.i18n.t("actions.forward"),
|
|
737
|
+
onClick: onGoForward,
|
|
738
|
+
children: /* @__PURE__ */ jsx(ArrowRightIcon, { className: "size-4" })
|
|
739
|
+
}
|
|
740
|
+
),
|
|
741
|
+
/* @__PURE__ */ jsx(
|
|
742
|
+
BrowserNodeHeaderButton,
|
|
743
|
+
{
|
|
744
|
+
label: feature.i18n.t("actions.reload"),
|
|
745
|
+
onClick: onReload,
|
|
746
|
+
children: /* @__PURE__ */ jsx(RefreshIcon, { className: "size-4" })
|
|
747
|
+
}
|
|
748
|
+
)
|
|
749
|
+
] }),
|
|
750
|
+
/* @__PURE__ */ jsx(
|
|
751
|
+
"div",
|
|
752
|
+
{
|
|
753
|
+
...dragHandleProps,
|
|
754
|
+
className: "h-full w-8 shrink-0 cursor-grab active:cursor-grabbing",
|
|
755
|
+
"data-browser-node-drag-gutter": "true",
|
|
756
|
+
"data-node-drag-handle": "true",
|
|
757
|
+
"aria-hidden": "true"
|
|
758
|
+
}
|
|
759
|
+
),
|
|
760
|
+
/* @__PURE__ */ jsxs(
|
|
761
|
+
"form",
|
|
762
|
+
{
|
|
763
|
+
className: "nodrag flex h-8 min-h-8 min-w-0 flex-1 items-center gap-1.5 rounded-md border border-border bg-[var(--workbench-field-bg)] px-2 focus-within:ring-2 focus-within:ring-ring/60",
|
|
764
|
+
onSubmit: (event) => {
|
|
765
|
+
event.preventDefault();
|
|
766
|
+
event.stopPropagation();
|
|
767
|
+
onSubmitUrl();
|
|
768
|
+
},
|
|
769
|
+
children: [
|
|
770
|
+
/* @__PURE__ */ jsx(LaunchIcon, { className: "size-4 shrink-0 text-muted-foreground" }),
|
|
771
|
+
/* @__PURE__ */ jsx(
|
|
772
|
+
"input",
|
|
773
|
+
{
|
|
774
|
+
"aria-label": feature.i18n.t("addressLabel"),
|
|
775
|
+
className: "h-full min-w-0 flex-1 border-0 bg-transparent text-[13px] leading-none text-foreground outline-none placeholder:text-muted-foreground",
|
|
776
|
+
placeholder: feature.i18n.t("addressPlaceholder"),
|
|
777
|
+
value: draftUrl,
|
|
778
|
+
onChange: (event) => onDraftUrlChange(event.target.value),
|
|
779
|
+
onFocus: onFocusRequest
|
|
780
|
+
}
|
|
781
|
+
),
|
|
782
|
+
isLoading ? /* @__PURE__ */ jsx(LoadingIcon, { className: "size-4 shrink-0 animate-spin text-muted-foreground" }) : null
|
|
783
|
+
]
|
|
784
|
+
}
|
|
785
|
+
),
|
|
786
|
+
defaultActions ? /* @__PURE__ */ jsxs("div", { className: "nodrag flex shrink-0 items-center gap-1.5", children: [
|
|
787
|
+
isCold ? /* @__PURE__ */ jsx(
|
|
788
|
+
"span",
|
|
789
|
+
{
|
|
790
|
+
className: "inline-flex h-[26px] min-w-7 items-center justify-center rounded-md bg-muted/80 px-2 text-[10px] font-semibold lowercase tracking-[0.08em] text-muted-foreground",
|
|
791
|
+
"aria-label": feature.i18n.t("coldStatus"),
|
|
792
|
+
children: feature.i18n.t("coldStatus")
|
|
793
|
+
}
|
|
794
|
+
) : null,
|
|
795
|
+
/* @__PURE__ */ jsx(
|
|
796
|
+
"span",
|
|
797
|
+
{
|
|
798
|
+
className: "contents",
|
|
799
|
+
onClickCapture: (event) => {
|
|
800
|
+
if (!onCloseRequest || !(event.target instanceof Element) || !event.target.closest('[data-workbench-action="close"]')) {
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
onCloseRequest();
|
|
804
|
+
},
|
|
805
|
+
children: defaultActions
|
|
806
|
+
}
|
|
807
|
+
)
|
|
808
|
+
] }) : null
|
|
809
|
+
]
|
|
810
|
+
}
|
|
811
|
+
);
|
|
812
|
+
}
|
|
813
|
+
function formatBrowserNodeErrorMessage(feature, error) {
|
|
814
|
+
switch (error.code) {
|
|
815
|
+
case "invalid-url":
|
|
816
|
+
return feature.i18n.t("errors.invalidUrl", error.params);
|
|
817
|
+
case "navigation-failed":
|
|
818
|
+
return feature.i18n.t("errors.navigationFailed", error.params);
|
|
819
|
+
case "unsupported-protocol":
|
|
820
|
+
return feature.i18n.t("errors.unsupportedProtocol", error.params);
|
|
821
|
+
case "unsupported-url":
|
|
822
|
+
return feature.i18n.t("errors.unsupportedUrl", error.params);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
function BrowserNodeHeaderButton({
|
|
826
|
+
children,
|
|
827
|
+
disabled,
|
|
828
|
+
label,
|
|
829
|
+
onClick
|
|
830
|
+
}) {
|
|
831
|
+
return /* @__PURE__ */ jsx(
|
|
832
|
+
Button,
|
|
833
|
+
{
|
|
834
|
+
"aria-label": label,
|
|
835
|
+
className: "rounded-md",
|
|
836
|
+
disabled,
|
|
837
|
+
size: "icon",
|
|
838
|
+
title: label,
|
|
839
|
+
type: "button",
|
|
840
|
+
variant: "chrome",
|
|
841
|
+
onClick,
|
|
842
|
+
children
|
|
843
|
+
}
|
|
844
|
+
);
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
export {
|
|
848
|
+
BrowserNode,
|
|
849
|
+
BrowserNodeWorkbenchHeader,
|
|
850
|
+
BrowserNodeHeader
|
|
851
|
+
};
|
|
852
|
+
//# sourceMappingURL=chunk-XCKUTY7D.js.map
|