local-operator-ui 0.38.1 → 0.39.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/main/index.js +2047 -89
- package/out/preload/index.js +46 -1
- package/out/renderer/assets/{_basePickBy-DhcDqJuW.js → _basePickBy-CSbKxvDP.js} +1 -1
- package/out/renderer/assets/{_baseUniq-DlAVpNmp.js → _baseUniq-BCDEv_Oc.js} +1 -1
- package/out/renderer/assets/addressOverlayList-8wdZRD_t.css +1 -0
- package/out/renderer/assets/addressOverlayList-BEh9WU4R.js +1 -0
- package/out/renderer/assets/{agent-details-page-A51rffCS.js → agent-details-page-ByULov9A.js} +2 -2
- package/out/renderer/assets/{agent-hub-page-DQ-tnD26.js → agent-hub-page-B69p4cyN.js} +2 -2
- package/out/renderer/assets/{agents-page-Bh6SGDaA.js → agents-page-Cl3KMRZy.js} +3 -3
- package/out/renderer/assets/{arc-C-BSLMbh.js → arc-CyVR9eoB.js} +1 -1
- package/out/renderer/assets/{architectureDiagram-IEHRJDOE-nDfnCWAM.js → architectureDiagram-IEHRJDOE-D_nbs604.js} +1 -1
- package/out/renderer/assets/{arrow-up-JQ86w8h2.js → arrow-up-JG56D3ZM.js} +1 -1
- package/out/renderer/assets/{blockDiagram-JOT3LUYC-BJgd2uGT.js → blockDiagram-JOT3LUYC-cGHKlnnj.js} +1 -1
- package/out/renderer/assets/{book-open-yjstLkVS.js → book-open-_50fvSpZ.js} +1 -1
- package/out/renderer/assets/browser-page-u8Qblazq.js +1 -0
- package/out/renderer/assets/{browser-webauthn-prompt-CdHXLdwX.js → browser-webauthn-prompt-BkYnuYH0.js} +1 -1
- package/out/renderer/assets/{building-2-Cupv31tS.js → building-2-D-6u9f2b.js} +1 -1
- package/out/renderer/assets/{c4Diagram-VJAJSXHY-lIXnBLZ5.js → c4Diagram-VJAJSXHY-DPSkxG1K.js} +1 -1
- package/out/renderer/assets/channel-BP0N5Jip.js +1 -0
- package/out/renderer/assets/{chunk-4BMEZGHF-DF2ktHZp.js → chunk-4BMEZGHF-k7Xc9N8N.js} +1 -1
- package/out/renderer/assets/{chunk-A2AXSNBT-CNRON9Qy.js → chunk-A2AXSNBT-BUQ9LrYj.js} +1 -1
- package/out/renderer/assets/{chunk-AEK57VVT-BGXI1SR5.js → chunk-AEK57VVT-T8K0wOxu.js} +1 -1
- package/out/renderer/assets/{chunk-D6G4REZN-CW_SyuGg.js → chunk-D6G4REZN-_drafUVH.js} +1 -1
- package/out/renderer/assets/{chunk-RZ5BOZE2-gU0oYsA6.js → chunk-RZ5BOZE2-BlfOLAiK.js} +1 -1
- package/out/renderer/assets/{chunk-XZIHB7SX-BrVtbfMc.js → chunk-XZIHB7SX-br7k9c-y.js} +1 -1
- package/out/renderer/assets/classDiagram-GIVACNV2-DgPhvGMc.js +1 -0
- package/out/renderer/assets/classDiagram-v2-COTLJTTW-DgPhvGMc.js +1 -0
- package/out/renderer/assets/clone-D_2R859t.js +1 -0
- package/out/renderer/assets/{console-mirror-CkCxHgcU.js → console-mirror-BFQaHp1b.js} +1 -1
- package/out/renderer/assets/{consoleCapture-EZYleEWx.js → consoleCapture-L2qaS8a8.js} +1 -1
- package/out/renderer/assets/{conversation-input-sync-bgD4kyw3.js → conversation-input-sync-CH6BMnVZ.js} +269 -269
- package/out/renderer/assets/{dagre-OKDRZEBW-BDR0956-.js → dagre-OKDRZEBW-BnHmlI8H.js} +1 -1
- package/out/renderer/assets/{diagram-SSKATNLV-DbCZDtm7.js → diagram-SSKATNLV-q_CpwdD7.js} +1 -1
- package/out/renderer/assets/{diagram-VNBRO52H-BpXb_OjM.js → diagram-VNBRO52H-B2mKekLo.js} +1 -1
- package/out/renderer/assets/{erDiagram-Q7BY3M3F-CZukR1Y4.js → erDiagram-Q7BY3M3F-CyQWMyA_.js} +1 -1
- package/out/renderer/assets/{error-boundary-DJAF_aYL.js → error-boundary-xAR0FhJi.js} +1 -1
- package/out/renderer/assets/{flowDiagram-4HSFHLVR-CqKOnXAd.js → flowDiagram-4HSFHLVR-DaECRw9d.js} +1 -1
- package/out/renderer/assets/{ganttDiagram-APWFNJXF-B9I0smi4.js → ganttDiagram-APWFNJXF-DcuyjU9V.js} +1 -1
- package/out/renderer/assets/{gitGraphDiagram-7IBYFJ6S-Bj6-u-5w.js → gitGraphDiagram-7IBYFJ6S-BY2WvPMI.js} +1 -1
- package/out/renderer/assets/{graph-BsgoQZc2.js → graph-or727h2C.js} +1 -1
- package/out/renderer/assets/{hash-BLfEUuGg.js → hash-Ch3ejz63.js} +1 -1
- package/out/renderer/assets/index-BUzMwM-n.css +1 -0
- package/out/renderer/assets/{index-lMi7GI2V.js → index-Bs2qygzx.js} +1 -1
- package/out/renderer/assets/index-C1J-T_Mt.js +825 -0
- package/out/renderer/assets/{index-D7pJoOu_.js → index-DQE5o_2l.js} +1 -1
- package/out/renderer/assets/index-YWHAePtd.js +57 -0
- package/out/renderer/assets/{infoDiagram-PH2N3AL5-DQ3aMjor.js → infoDiagram-PH2N3AL5-BXElUsIa.js} +1 -1
- package/out/renderer/assets/{installer-Ch3nb3kq.js → installer-DBTiypQJ.js} +1 -1
- package/out/renderer/assets/{journeyDiagram-U35MCT3I-B_RccO_x.js → journeyDiagram-U35MCT3I-GAMZIDYa.js} +1 -1
- package/out/renderer/assets/{kanban-definition-NDS4AKOZ-BvVJZaiy.js → kanban-definition-NDS4AKOZ-ij90M1gN.js} +1 -1
- package/out/renderer/assets/{layout-DCACUDHF.js → layout-DqWzcUI9.js} +1 -1
- package/out/renderer/assets/{legacy-agents-page-ccL-99ea.js → legacy-agents-page-D96D0ThJ.js} +4 -4
- package/out/renderer/assets/{library-page-C8ymyVUW.js → library-page-BtkHYaRw.js} +3 -3
- package/out/renderer/assets/{mermaid.core-BXcegHmP.js → mermaid.core-B9fSy9q8.js} +5 -5
- package/out/renderer/assets/{mesh-page-Dn77Ie1F.js → mesh-page-BKS4b5nA.js} +1 -1
- package/out/renderer/assets/{mindmap-definition-ALO5MXBD-Bx_gU6ka.js → mindmap-definition-ALO5MXBD-CnOp0ZCp.js} +1 -1
- package/out/renderer/assets/{mini-DRt4PdyG.js → mini-BL0qywTp.js} +1 -1
- package/out/renderer/assets/{notebook-pen-CKr2WSrK.js → notebook-pen-DQs4gt50.js} +1 -1
- package/out/renderer/assets/{org-access-CfGyU5hi.js → org-access-DvoJr_O7.js} +1 -1
- package/out/renderer/assets/{org-surface-gate-C86b758Z.js → org-surface-gate-kYD-nQnh.js} +1 -1
- package/out/renderer/assets/{page-header-CZcbRwLy.js → page-header-BY6LFXsV.js} +1 -1
- package/out/renderer/assets/{pieDiagram-IB7DONF6-jEzs31M9.js → pieDiagram-IB7DONF6-DkbkzE3D.js} +1 -1
- package/out/renderer/assets/{projects-page-xuLYnWBj.js → projects-page-BLQzs3uM.js} +1 -1
- package/out/renderer/assets/{publication-validation-CCv7pQJH.js → publication-validation-4m4ljeE_.js} +1 -1
- package/out/renderer/assets/{quadrantDiagram-7GDLP6J5-C07DLJe_.js → quadrantDiagram-7GDLP6J5-Dfe4cbbn.js} +1 -1
- package/out/renderer/assets/{radar-MK3ICKWK-DFqDL3G4.js → radar-MK3ICKWK-BdbftlUx.js} +1 -1
- package/out/renderer/assets/{radient-auth-buttons-Huoo_kFK.js → radient-auth-buttons-DngU_dyA.js} +1 -1
- package/out/renderer/assets/{requirementDiagram-KVF5MWMF-DZVSGDrD.js → requirementDiagram-KVF5MWMF-PKSI_Jbh.js} +1 -1
- package/out/renderer/assets/{sankeyDiagram-QLVOVGJD-Efyu9UfS.js → sankeyDiagram-QLVOVGJD-Bon1uUIU.js} +1 -1
- package/out/renderer/assets/{schedules-page-vsfnEcLJ.js → schedules-page-DW0Ae9De.js} +1 -1
- package/out/renderer/assets/{sequenceDiagram-X6HHIX6F-C5QQ4cH3.js → sequenceDiagram-X6HHIX6F-Bnoc1VB7.js} +1 -1
- package/out/renderer/assets/{settings-page-CnBnFo-w.js → settings-page-Djdtg7c4.js} +1 -1
- package/out/renderer/assets/{smartart-D5HiGUV-.js → smartart-CIIex-5K.js} +1 -1
- package/out/renderer/assets/{stateDiagram-DGXRK772-DlvSEVs4.js → stateDiagram-DGXRK772-CGmFZKBF.js} +1 -1
- package/out/renderer/assets/stateDiagram-v2-YXO3MK2T-C7mh141a.js +1 -0
- package/out/renderer/assets/team-bridge-page-B1jfvoLr.js +11 -0
- package/out/renderer/assets/{timeline-definition-BDJGKUSR-XgOjVv3O.js → timeline-definition-BDJGKUSR-B3KzRgdo.js} +1 -1
- package/out/renderer/assets/ui-preferences-store-YyZ50qSr.js +1 -0
- package/out/renderer/assets/{use-download-agent-mutation-BZhcL_Dx.js → use-download-agent-mutation-CGnvP-dC.js} +1 -1
- package/out/renderer/assets/{use-session-snapshot-BiiULlsj.js → use-session-snapshot-B0KS_Jw_.js} +1 -1
- package/out/renderer/assets/{xychartDiagram-VJFVF3MP-B0CsPCbY.js → xychartDiagram-VJFVF3MP-BEcciMcG.js} +1 -1
- package/out/renderer/browser-address-overlay-list.html +35 -0
- package/out/renderer/browser-address-overlay.html +43 -0
- package/out/renderer/console-capture.html +5 -5
- package/out/renderer/index.html +7 -7
- package/out/renderer/installer.html +4 -4
- package/out/renderer/mini.html +7 -7
- package/package.json +8 -3
- package/out/renderer/assets/browser-page-Daga5nCP.js +0 -1
- package/out/renderer/assets/channel-BqEnrEwi.js +0 -1
- package/out/renderer/assets/classDiagram-GIVACNV2-DNUqXdeF.js +0 -1
- package/out/renderer/assets/classDiagram-v2-COTLJTTW-DNUqXdeF.js +0 -1
- package/out/renderer/assets/clone-_PvxPvQz.js +0 -1
- package/out/renderer/assets/index-BZYkm39z.js +0 -825
- package/out/renderer/assets/index-CL2MVLxd.css +0 -1
- package/out/renderer/assets/index-N0lyyVVo.js +0 -57
- package/out/renderer/assets/stateDiagram-v2-YXO3MK2T-CCLt0TcZ.js +0 -1
- package/out/renderer/assets/team-bridge-page-JRqkQ8jV.js +0 -11
- package/out/renderer/assets/ui-preferences-store-C91RrpgH.js +0 -1
package/out/main/index.js
CHANGED
|
@@ -190,6 +190,17 @@ zod.z.object({
|
|
|
190
190
|
}).passthrough()
|
|
191
191
|
}).passthrough()
|
|
192
192
|
}).passthrough();
|
|
193
|
+
const SIDEBAR_DESTINATION_IDS = [
|
|
194
|
+
"aida",
|
|
195
|
+
"library",
|
|
196
|
+
"projects",
|
|
197
|
+
"agents",
|
|
198
|
+
"schedules",
|
|
199
|
+
"browser",
|
|
200
|
+
"team-bridge",
|
|
201
|
+
"agent-hub",
|
|
202
|
+
"mesh"
|
|
203
|
+
];
|
|
193
204
|
const JOURNAL_ORDER_QUERY_BYTES = 6 * 1024;
|
|
194
205
|
const JOURNAL_ORDER_ID_LIMIT = 512;
|
|
195
206
|
function appendJournalOrderIds(query, path2, ids) {
|
|
@@ -488,6 +499,11 @@ const PROJECTS_SEARCH_MAX_CHARS = 256;
|
|
|
488
499
|
const PROJECTS_SEARCH_DEFAULT_LIMIT = 100;
|
|
489
500
|
const PROJECTS_SEARCH_MAX_LIMIT = 200;
|
|
490
501
|
const hubItemKind = zod.z.enum(["agent", "team", "skill"]);
|
|
502
|
+
const sidebarDestinationId = zod.z.enum(SIDEBAR_DESTINATION_IDS);
|
|
503
|
+
const sidebarLayoutSchema = zod.z.object({
|
|
504
|
+
visible: zod.z.array(sidebarDestinationId).max(SIDEBAR_DESTINATION_IDS.length),
|
|
505
|
+
more: zod.z.array(sidebarDestinationId).max(SIDEBAR_DESTINATION_IDS.length)
|
|
506
|
+
}).strict();
|
|
491
507
|
const desktopRequestUnion = zod.z.discriminatedUnion("op", [
|
|
492
508
|
zod.z.object({ op: zod.z.literal("capabilities") }).strict(),
|
|
493
509
|
/*
|
|
@@ -2989,6 +3005,23 @@ const desktopRequestUnion = zod.z.discriminatedUnion("op", [
|
|
|
2989
3005
|
op: zod.z.literal("library.librarian"),
|
|
2990
3006
|
/** `status` is the read's own op; the other four are the writes. */
|
|
2991
3007
|
action: zod.z.enum(["status", "open", "pause", "resume", "run"]).optional()
|
|
3008
|
+
}).strict(),
|
|
3009
|
+
/*
|
|
3010
|
+
* THE SIDEBAR LAYOUT RESOURCE (design contract § 2, § 4). One read and one
|
|
3011
|
+
* write, mirroring the two HTTP verbs: the resource is a single document, and
|
|
3012
|
+
* a patch vocabulary over a nine-entry ordered list would have to invent
|
|
3013
|
+
* operations for moves the client already knows how to compute.
|
|
3014
|
+
*
|
|
3015
|
+
* The write carries the WHOLE layout rather than a delta because the route
|
|
3016
|
+
* normalises and persists a complete document (last writer wins, atomic file
|
|
3017
|
+
* replace). Sending the full list is also what makes an optimistic write
|
|
3018
|
+
* self-describing on the retry path: the body a retry replays is the layout
|
|
3019
|
+
* the user saw, not a move that could be applied twice.
|
|
3020
|
+
*/
|
|
3021
|
+
zod.z.object({ op: zod.z.literal("sidebar_layout.get") }).strict(),
|
|
3022
|
+
zod.z.object({
|
|
3023
|
+
op: zod.z.literal("sidebar_layout.set"),
|
|
3024
|
+
layout: sidebarLayoutSchema
|
|
2992
3025
|
}).strict()
|
|
2993
3026
|
]);
|
|
2994
3027
|
const desktopRequestSchema = desktopRequestUnion.superRefine(
|
|
@@ -5387,6 +5420,22 @@ function desktopEndpoint(request) {
|
|
|
5387
5420
|
ttl_s: request.ttlS
|
|
5388
5421
|
}
|
|
5389
5422
|
};
|
|
5423
|
+
/*
|
|
5424
|
+
* The sidebar customisation resource (design contract § 4). A GET/PUT pair
|
|
5425
|
+
* on one route, with the layout in an envelope rather than as the bare body:
|
|
5426
|
+
* the route answers `{layout}` and accepts `{layout}`, so a future field
|
|
5427
|
+
* beside it is additive instead of a breaking shape change. The PUT sends
|
|
5428
|
+
* the stored ids verbatim - the client owns the order, the route owns
|
|
5429
|
+
* validation and the default fill - so nothing is re-sorted in transit.
|
|
5430
|
+
*/
|
|
5431
|
+
case "sidebar_layout.get":
|
|
5432
|
+
return { path: "/v1/desktop/sidebar-layout", method: "GET" };
|
|
5433
|
+
case "sidebar_layout.set":
|
|
5434
|
+
return {
|
|
5435
|
+
path: "/v1/desktop/sidebar-layout",
|
|
5436
|
+
method: "PUT",
|
|
5437
|
+
body: { layout: request.layout }
|
|
5438
|
+
};
|
|
5390
5439
|
}
|
|
5391
5440
|
}
|
|
5392
5441
|
const MAX_PROBE_PATHS = 64;
|
|
@@ -14955,10 +15004,24 @@ function resolveLauncherWatchPlan(input) {
|
|
|
14955
15004
|
};
|
|
14956
15005
|
}
|
|
14957
15006
|
if (input.mode !== "headless") {
|
|
15007
|
+
if (input.mode !== "inactive" || input.boundedVisibleProbe !== true) {
|
|
15008
|
+
return {
|
|
15009
|
+
watch: false,
|
|
15010
|
+
launcherPid: null,
|
|
15011
|
+
reason: `window mode ${input.mode} is not launcher-bound: a person can close it`
|
|
15012
|
+
};
|
|
15013
|
+
}
|
|
15014
|
+
if (!Number.isInteger(input.launcherPid) || input.launcherPid <= 1) {
|
|
15015
|
+
return {
|
|
15016
|
+
watch: false,
|
|
15017
|
+
launcherPid: null,
|
|
15018
|
+
reason: "visible probe already detached (no launcher to outlive)"
|
|
15019
|
+
};
|
|
15020
|
+
}
|
|
14958
15021
|
return {
|
|
14959
|
-
watch:
|
|
14960
|
-
launcherPid:
|
|
14961
|
-
reason: `
|
|
15022
|
+
watch: true,
|
|
15023
|
+
launcherPid: input.launcherPid,
|
|
15024
|
+
reason: `visible probe launched by pid ${input.launcherPid}; it is capped and quits when that process goes`
|
|
14962
15025
|
};
|
|
14963
15026
|
}
|
|
14964
15027
|
if (!Number.isInteger(input.launcherPid) || input.launcherPid <= 1) {
|
|
@@ -26003,11 +26066,11 @@ class ConsoleHost {
|
|
|
26003
26066
|
{ rendered: null }
|
|
26004
26067
|
);
|
|
26005
26068
|
}
|
|
26006
|
-
const
|
|
26069
|
+
const rect2 = cropToWindow(
|
|
26007
26070
|
report.contentRect,
|
|
26008
26071
|
window.getContentBounds()
|
|
26009
26072
|
);
|
|
26010
|
-
const png = await this.captureWithRetry(window,
|
|
26073
|
+
const png = await this.captureWithRetry(window, rect2);
|
|
26011
26074
|
const grid = runtime.emulator.grid;
|
|
26012
26075
|
return {
|
|
26013
26076
|
image_base64: png.toString("base64"),
|
|
@@ -26043,10 +26106,10 @@ class ConsoleHost {
|
|
|
26043
26106
|
* renderer paints antialiased text and a cursor — and the empty-terminal case is the
|
|
26044
26107
|
* offscreen path's problem too, so this is not a new ceiling.
|
|
26045
26108
|
*/
|
|
26046
|
-
async captureWithRetry(window,
|
|
26109
|
+
async captureWithRetry(window, rect2) {
|
|
26047
26110
|
let bytes = 0;
|
|
26048
26111
|
for (let attempt = 1; attempt <= 2; attempt++) {
|
|
26049
|
-
const image2 = await window.webContents.capturePage(
|
|
26112
|
+
const image2 = await window.webContents.capturePage(rect2);
|
|
26050
26113
|
const png = framePng(image2);
|
|
26051
26114
|
bytes = png.length;
|
|
26052
26115
|
if (hasTerminalContent(image2.toBitmap())) {
|
|
@@ -27011,14 +27074,14 @@ function wrapBracketedPaste(bytes) {
|
|
|
27011
27074
|
out.set(end, start.length + bytes.length);
|
|
27012
27075
|
return out;
|
|
27013
27076
|
}
|
|
27014
|
-
function cropToWindow(
|
|
27015
|
-
const x = Math.max(Math.floor(
|
|
27016
|
-
const y = Math.max(Math.floor(
|
|
27077
|
+
function cropToWindow(rect2, bounds) {
|
|
27078
|
+
const x = Math.max(Math.floor(rect2.x), 0);
|
|
27079
|
+
const y = Math.max(Math.floor(rect2.y), 0);
|
|
27017
27080
|
return {
|
|
27018
27081
|
x,
|
|
27019
27082
|
y,
|
|
27020
|
-
width: Math.max(Math.min(Math.floor(
|
|
27021
|
-
height: Math.max(Math.min(Math.floor(
|
|
27083
|
+
width: Math.max(Math.min(Math.floor(rect2.width), bounds.width - x), 1),
|
|
27084
|
+
height: Math.max(Math.min(Math.floor(rect2.height), bounds.height - y), 1)
|
|
27022
27085
|
};
|
|
27023
27086
|
}
|
|
27024
27087
|
function framePng(image2) {
|
|
@@ -27787,12 +27850,12 @@ function contentReportOrThrow(value) {
|
|
|
27787
27850
|
throw new Error("A console content report is required.");
|
|
27788
27851
|
}
|
|
27789
27852
|
const report = value;
|
|
27790
|
-
const
|
|
27791
|
-
if (!
|
|
27853
|
+
const rect2 = report.contentRect;
|
|
27854
|
+
if (!rect2 || typeof rect2 !== "object") {
|
|
27792
27855
|
throw new Error("A console content report needs a contentRect.");
|
|
27793
27856
|
}
|
|
27794
27857
|
const numbers = ["x", "y", "width", "height"].map((key) => {
|
|
27795
|
-
const raw =
|
|
27858
|
+
const raw = rect2[key];
|
|
27796
27859
|
if (typeof raw !== "number" || !Number.isFinite(raw)) {
|
|
27797
27860
|
throw new Error(`contentRect.${key} must be a number.`);
|
|
27798
27861
|
}
|
|
@@ -28292,6 +28355,60 @@ async function startConsoleHost(options) {
|
|
|
28292
28355
|
};
|
|
28293
28356
|
}
|
|
28294
28357
|
}
|
|
28358
|
+
const DEV_DRIVER_ENV = "LOCAL_OPERATOR_UI_DEV_DRIVER";
|
|
28359
|
+
const DEV_DRIVER_OUT_ENV = "LOCAL_OPERATOR_UI_DEV_DRIVER_OUT";
|
|
28360
|
+
const DEV_DRIVER_ARG = "--lo-dev-driver";
|
|
28361
|
+
const DEV_DRIVER_CAPTURE = "dev-driver-capture";
|
|
28362
|
+
const DEV_DRIVER_FACTS = "dev-driver-facts";
|
|
28363
|
+
const DEV_DRIVER_ADDRESS_OVERLAY = "dev-driver-address-overlay";
|
|
28364
|
+
const DEV_DRIVER_ADDRESS_OVERLAY_PROJECT = "dev-driver-address-overlay-project";
|
|
28365
|
+
const DEV_DRIVER_ADDRESS_OVERLAY_EVENT = "dev-driver-address-overlay-event";
|
|
28366
|
+
const TRUTHY = /* @__PURE__ */ new Set(["1", "true"]);
|
|
28367
|
+
function readOptIn(env) {
|
|
28368
|
+
const raw = env[DEV_DRIVER_ENV];
|
|
28369
|
+
if (raw === void 0 || raw.trim() === "")
|
|
28370
|
+
return { asked: false, value: null };
|
|
28371
|
+
return { asked: true, value: raw.trim() };
|
|
28372
|
+
}
|
|
28373
|
+
function resolveDevDriverArming(input) {
|
|
28374
|
+
const off = (problems = []) => ({
|
|
28375
|
+
armed: false,
|
|
28376
|
+
outDir: null,
|
|
28377
|
+
problems
|
|
28378
|
+
});
|
|
28379
|
+
const optIn = readOptIn(input.env);
|
|
28380
|
+
if (!optIn.asked) return off();
|
|
28381
|
+
if (!TRUTHY.has(optIn.value.toLowerCase())) {
|
|
28382
|
+
return off([
|
|
28383
|
+
`${DEV_DRIVER_ENV}=${optIn.value} is not an opt-in (accepted: 1, true); the renderer dev driver stayed off`
|
|
28384
|
+
]);
|
|
28385
|
+
}
|
|
28386
|
+
if (input.windowMode !== "headless" && input.windowMode !== "inactive") {
|
|
28387
|
+
return off([
|
|
28388
|
+
`the renderer dev driver refused to arm in a "${input.windowMode}" window; it never raises one, so use --window-mode=headless (or inactive)`
|
|
28389
|
+
]);
|
|
28390
|
+
}
|
|
28391
|
+
const outDir = input.env[DEV_DRIVER_OUT_ENV]?.trim() ?? "";
|
|
28392
|
+
if (outDir === "" || !outDir.startsWith("/")) {
|
|
28393
|
+
return off([
|
|
28394
|
+
`${DEV_DRIVER_ENV} was set but ${DEV_DRIVER_OUT_ENV} is ${outDir === "" ? "missing" : `"${outDir}"`}; an absolute frames directory is required, so the driver did not arm`
|
|
28395
|
+
]);
|
|
28396
|
+
}
|
|
28397
|
+
return { armed: true, outDir, problems: [] };
|
|
28398
|
+
}
|
|
28399
|
+
function headlessExerciserAllowed(input) {
|
|
28400
|
+
return input.arming.armed && input.windowMode === "headless";
|
|
28401
|
+
}
|
|
28402
|
+
function devDriverArgument(outDir) {
|
|
28403
|
+
return `${DEV_DRIVER_ARG}=${outDir}`;
|
|
28404
|
+
}
|
|
28405
|
+
function describeDevDriverArming(arming) {
|
|
28406
|
+
if (arming.armed) {
|
|
28407
|
+
return `[dev-driver] ARMED; frames are written to ${arming.outDir}`;
|
|
28408
|
+
}
|
|
28409
|
+
if (arming.problems.length === 0) return null;
|
|
28410
|
+
return `[dev-driver] not armed: ${arming.problems.join("; ")}`;
|
|
28411
|
+
}
|
|
28295
28412
|
const WEBAUTHN_GROUP_SUFFIX = ".webauthn";
|
|
28296
28413
|
const WEBAUTHN_PROMPT_REASON = "use a passkey for $1";
|
|
28297
28414
|
const WEBAUTHN_CHOOSER_TIMEOUT_MS = 6e4;
|
|
@@ -28714,6 +28831,1572 @@ function labelOf(account) {
|
|
|
28714
28831
|
if (!account) return "an unnamed passkey";
|
|
28715
28832
|
return account.displayName || account.name || "an unnamed passkey";
|
|
28716
28833
|
}
|
|
28834
|
+
const ADDRESS_LIST_ENV = "LOCAL_OPERATOR_UI_BROWSER_ADDRESS_OVERLAY_S2";
|
|
28835
|
+
const ADDRESS_LIST_DOCUMENT = "browser-address-overlay-list.html";
|
|
28836
|
+
const ADDRESS_LIST_HEADER_HEIGHT = 28;
|
|
28837
|
+
const ADDRESS_LIST_ROW_HEIGHT = 35.5;
|
|
28838
|
+
const ADDRESS_LIST_ROWS_MAX = 5;
|
|
28839
|
+
const ADDRESS_LIST_MAX_HEIGHT_DIP = 212;
|
|
28840
|
+
const ADDRESS_LIST_NOTICE_HEIGHT_DIP = 104;
|
|
28841
|
+
const ADDRESS_LIST_ANCHOR_GAP_MAX = 24;
|
|
28842
|
+
const ADDRESS_LIST_MAX_PAYLOAD_BYTES = 16 * 1024;
|
|
28843
|
+
const ADDRESS_LIST_MAX_TEXT = 512;
|
|
28844
|
+
const ADDRESS_LIST_MAX_TITLE = 256;
|
|
28845
|
+
const ADDRESS_LIST_MAX_LABEL = 256;
|
|
28846
|
+
const ADDRESS_LIST_THEME_FALLBACK = "#1b1f24";
|
|
28847
|
+
const DIP_INT32_MAX$1 = 2 ** 31 - 1;
|
|
28848
|
+
const NOTICE_KINDS = [
|
|
28849
|
+
"error",
|
|
28850
|
+
"hint",
|
|
28851
|
+
"empty",
|
|
28852
|
+
"no_match"
|
|
28853
|
+
];
|
|
28854
|
+
const HOSTILE_STRING = /[\p{Cc}\u2028\u2029\u202A-\u202E\u2066-\u2069]/gu;
|
|
28855
|
+
const COLOR = /^(#[0-9a-f]{6}|rgb\(\d{1,3}, ?\d{1,3}, ?\d{1,3}\))$/i;
|
|
28856
|
+
const PROJECTION_KEYS = /* @__PURE__ */ new Set([
|
|
28857
|
+
"v",
|
|
28858
|
+
"seq",
|
|
28859
|
+
"tabId",
|
|
28860
|
+
"visible",
|
|
28861
|
+
"pageRect",
|
|
28862
|
+
"field",
|
|
28863
|
+
"listTop",
|
|
28864
|
+
"active",
|
|
28865
|
+
"view",
|
|
28866
|
+
"label",
|
|
28867
|
+
"dir",
|
|
28868
|
+
"theme"
|
|
28869
|
+
]);
|
|
28870
|
+
const RECT_KEYS = ["x", "y", "width", "height"];
|
|
28871
|
+
const THEME_KEYS = [
|
|
28872
|
+
"elevated",
|
|
28873
|
+
"ink",
|
|
28874
|
+
"inkMuted",
|
|
28875
|
+
"accent",
|
|
28876
|
+
"accentWash",
|
|
28877
|
+
"rowHover",
|
|
28878
|
+
"hairline",
|
|
28879
|
+
"control"
|
|
28880
|
+
];
|
|
28881
|
+
function isRecord$1(value) {
|
|
28882
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
28883
|
+
}
|
|
28884
|
+
function hasOnlyKeys(value, allowed) {
|
|
28885
|
+
const set2 = allowed instanceof Set ? allowed : new Set(allowed);
|
|
28886
|
+
for (const key of Object.keys(value)) if (!set2.has(key)) return false;
|
|
28887
|
+
return true;
|
|
28888
|
+
}
|
|
28889
|
+
function finite(value) {
|
|
28890
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
28891
|
+
}
|
|
28892
|
+
function rect(value) {
|
|
28893
|
+
if (!isRecord$1(value)) return null;
|
|
28894
|
+
if (!hasOnlyKeys(value, RECT_KEYS)) return null;
|
|
28895
|
+
const x = finite(value.x);
|
|
28896
|
+
const y = finite(value.y);
|
|
28897
|
+
const width = finite(value.width);
|
|
28898
|
+
const height = finite(value.height);
|
|
28899
|
+
if (x === null || y === null || width === null || height === null)
|
|
28900
|
+
return null;
|
|
28901
|
+
return { x, y, width, height };
|
|
28902
|
+
}
|
|
28903
|
+
function sanitizeOverlayText(value, cap) {
|
|
28904
|
+
if (typeof value !== "string") return null;
|
|
28905
|
+
const stripped = value.replace(HOSTILE_STRING, "");
|
|
28906
|
+
const points = [...stripped];
|
|
28907
|
+
return points.length > cap ? points.slice(0, cap).join("") : stripped;
|
|
28908
|
+
}
|
|
28909
|
+
function sanitizeRows(value) {
|
|
28910
|
+
if (!Array.isArray(value)) return null;
|
|
28911
|
+
if (value.length === 0 || value.length > ADDRESS_LIST_ROWS_MAX) return null;
|
|
28912
|
+
const rows = [];
|
|
28913
|
+
for (const entry of value) {
|
|
28914
|
+
if (!isRecord$1(entry) || !hasOnlyKeys(entry, ["text", "title"])) return null;
|
|
28915
|
+
const text = sanitizeOverlayText(entry.text, ADDRESS_LIST_MAX_TEXT);
|
|
28916
|
+
const title = sanitizeOverlayText(entry.title, ADDRESS_LIST_MAX_TITLE);
|
|
28917
|
+
if (text === null || title === null) return null;
|
|
28918
|
+
if (text.length === 0) return null;
|
|
28919
|
+
rows.push({ text, title });
|
|
28920
|
+
}
|
|
28921
|
+
return rows;
|
|
28922
|
+
}
|
|
28923
|
+
function sanitizeTheme(value) {
|
|
28924
|
+
if (value === void 0) return { theme: null, fact: "absent" };
|
|
28925
|
+
if (!isRecord$1(value) || !hasOnlyKeys(value, THEME_KEYS)) {
|
|
28926
|
+
return { theme: null, fact: "fallback" };
|
|
28927
|
+
}
|
|
28928
|
+
const theme = {};
|
|
28929
|
+
for (const key of THEME_KEYS) {
|
|
28930
|
+
const raw = value[key];
|
|
28931
|
+
if (typeof raw !== "string" || !COLOR.test(raw)) {
|
|
28932
|
+
return { theme: null, fact: "fallback" };
|
|
28933
|
+
}
|
|
28934
|
+
theme[key] = raw;
|
|
28935
|
+
}
|
|
28936
|
+
return { theme, fact: "resolved" };
|
|
28937
|
+
}
|
|
28938
|
+
function jsonBytes(value) {
|
|
28939
|
+
try {
|
|
28940
|
+
return new TextEncoder().encode(JSON.stringify(value)).length;
|
|
28941
|
+
} catch {
|
|
28942
|
+
return Number.POSITIVE_INFINITY;
|
|
28943
|
+
}
|
|
28944
|
+
}
|
|
28945
|
+
function sanitizeProjection(raw) {
|
|
28946
|
+
if (jsonBytes(raw) > ADDRESS_LIST_MAX_PAYLOAD_BYTES) {
|
|
28947
|
+
return { ok: false, reason: "payload_too_large" };
|
|
28948
|
+
}
|
|
28949
|
+
if (!isRecord$1(raw)) return { ok: false, reason: "not_an_object" };
|
|
28950
|
+
if (!hasOnlyKeys(raw, PROJECTION_KEYS)) {
|
|
28951
|
+
return { ok: false, reason: "unknown_key" };
|
|
28952
|
+
}
|
|
28953
|
+
if (raw.v !== 1) return { ok: false, reason: "bad_version" };
|
|
28954
|
+
if (!Number.isInteger(raw.seq) || raw.seq < 1) {
|
|
28955
|
+
return { ok: false, reason: "bad_seq" };
|
|
28956
|
+
}
|
|
28957
|
+
if (!Number.isInteger(raw.tabId)) return { ok: false, reason: "bad_tab" };
|
|
28958
|
+
if (typeof raw.visible !== "boolean") {
|
|
28959
|
+
return { ok: false, reason: "bad_visible" };
|
|
28960
|
+
}
|
|
28961
|
+
if (!raw.visible) {
|
|
28962
|
+
return {
|
|
28963
|
+
ok: true,
|
|
28964
|
+
projection: {
|
|
28965
|
+
v: 1,
|
|
28966
|
+
seq: raw.seq,
|
|
28967
|
+
tabId: raw.tabId,
|
|
28968
|
+
visible: false,
|
|
28969
|
+
pageRect: { x: 0, y: 0, width: 0, height: 0 },
|
|
28970
|
+
field: { x: 0, y: 0, width: 0, height: 0 },
|
|
28971
|
+
listTop: 0,
|
|
28972
|
+
active: -1,
|
|
28973
|
+
view: { kind: "notice", notice: "empty", text: "" },
|
|
28974
|
+
label: "",
|
|
28975
|
+
dir: "ltr",
|
|
28976
|
+
theme: null,
|
|
28977
|
+
themeFact: "absent"
|
|
28978
|
+
}
|
|
28979
|
+
};
|
|
28980
|
+
}
|
|
28981
|
+
const pageRect = rect(raw.pageRect);
|
|
28982
|
+
const field = rect(raw.field);
|
|
28983
|
+
if (pageRect === null || field === null) {
|
|
28984
|
+
return { ok: false, reason: "bad_rect" };
|
|
28985
|
+
}
|
|
28986
|
+
const listTop = finite(raw.listTop);
|
|
28987
|
+
if (listTop === null) return { ok: false, reason: "bad_list_top" };
|
|
28988
|
+
if (field.width <= 0 || field.height <= 0) {
|
|
28989
|
+
return { ok: false, reason: "bad_field" };
|
|
28990
|
+
}
|
|
28991
|
+
if (!Number.isInteger(raw.active)) return { ok: false, reason: "bad_active" };
|
|
28992
|
+
const active = raw.active;
|
|
28993
|
+
const viewRaw = raw.view;
|
|
28994
|
+
if (!isRecord$1(viewRaw)) return { ok: false, reason: "bad_view" };
|
|
28995
|
+
let view;
|
|
28996
|
+
if (viewRaw.kind === "rows") {
|
|
28997
|
+
if (!hasOnlyKeys(viewRaw, ["kind", "rows"])) {
|
|
28998
|
+
return { ok: false, reason: "bad_view" };
|
|
28999
|
+
}
|
|
29000
|
+
const rows = sanitizeRows(viewRaw.rows);
|
|
29001
|
+
if (rows === null) return { ok: false, reason: "bad_rows" };
|
|
29002
|
+
view = { kind: "rows", rows };
|
|
29003
|
+
} else if (viewRaw.kind === "notice") {
|
|
29004
|
+
if (!hasOnlyKeys(viewRaw, ["kind", "notice", "text"])) {
|
|
29005
|
+
return { ok: false, reason: "bad_view" };
|
|
29006
|
+
}
|
|
29007
|
+
if (!NOTICE_KINDS.includes(viewRaw.notice)) {
|
|
29008
|
+
return { ok: false, reason: "bad_notice" };
|
|
29009
|
+
}
|
|
29010
|
+
const text = sanitizeOverlayText(viewRaw.text, ADDRESS_LIST_MAX_LABEL);
|
|
29011
|
+
if (text === null) return { ok: false, reason: "bad_notice_text" };
|
|
29012
|
+
view = {
|
|
29013
|
+
kind: "notice",
|
|
29014
|
+
notice: viewRaw.notice,
|
|
29015
|
+
text
|
|
29016
|
+
};
|
|
29017
|
+
} else {
|
|
29018
|
+
return { ok: false, reason: "bad_view" };
|
|
29019
|
+
}
|
|
29020
|
+
if (view.kind === "rows" ? active < -1 || active >= view.rows.length : active !== -1) {
|
|
29021
|
+
return { ok: false, reason: "bad_active" };
|
|
29022
|
+
}
|
|
29023
|
+
const label = sanitizeOverlayText(raw.label, ADDRESS_LIST_MAX_LABEL);
|
|
29024
|
+
if (label === null) return { ok: false, reason: "bad_label" };
|
|
29025
|
+
if (raw.dir !== "ltr" && raw.dir !== "rtl") {
|
|
29026
|
+
return { ok: false, reason: "bad_dir" };
|
|
29027
|
+
}
|
|
29028
|
+
const { theme, fact } = sanitizeTheme(raw.theme);
|
|
29029
|
+
return {
|
|
29030
|
+
ok: true,
|
|
29031
|
+
projection: {
|
|
29032
|
+
v: 1,
|
|
29033
|
+
seq: raw.seq,
|
|
29034
|
+
tabId: raw.tabId,
|
|
29035
|
+
visible: true,
|
|
29036
|
+
pageRect,
|
|
29037
|
+
field,
|
|
29038
|
+
listTop,
|
|
29039
|
+
active,
|
|
29040
|
+
view,
|
|
29041
|
+
label,
|
|
29042
|
+
dir: raw.dir,
|
|
29043
|
+
theme,
|
|
29044
|
+
themeFact: fact
|
|
29045
|
+
}
|
|
29046
|
+
};
|
|
29047
|
+
}
|
|
29048
|
+
function int32$1(value) {
|
|
29049
|
+
if (!Number.isFinite(value)) return null;
|
|
29050
|
+
const rounded = Math.round(value);
|
|
29051
|
+
return Math.abs(rounded) > DIP_INT32_MAX$1 ? null : rounded;
|
|
29052
|
+
}
|
|
29053
|
+
function addressListHeightFor(view) {
|
|
29054
|
+
if (view.kind === "notice") return ADDRESS_LIST_NOTICE_HEIGHT_DIP;
|
|
29055
|
+
return Math.ceil(
|
|
29056
|
+
ADDRESS_LIST_HEADER_HEIGHT + 2 + view.rows.length * ADDRESS_LIST_ROW_HEIGHT + 2 + 1
|
|
29057
|
+
);
|
|
29058
|
+
}
|
|
29059
|
+
function addressListBoundsFor(field, listTop, view, owner) {
|
|
29060
|
+
const fx = int32$1(field.x);
|
|
29061
|
+
const fy = int32$1(field.y);
|
|
29062
|
+
const fw = int32$1(field.width);
|
|
29063
|
+
const fh = int32$1(field.height);
|
|
29064
|
+
const top = int32$1(listTop);
|
|
29065
|
+
const ownerWidth = int32$1(owner.width);
|
|
29066
|
+
const ownerHeight = int32$1(owner.height);
|
|
29067
|
+
if (fx === null || fy === null || fw === null || fh === null || top === null || ownerWidth === null || ownerHeight === null) {
|
|
29068
|
+
return null;
|
|
29069
|
+
}
|
|
29070
|
+
if (fw <= 0 || fh <= 0 || ownerWidth <= 0 || ownerHeight <= 0) return null;
|
|
29071
|
+
const gap = top - (fy + fh);
|
|
29072
|
+
if (gap < 0 || gap > ADDRESS_LIST_ANCHOR_GAP_MAX) return null;
|
|
29073
|
+
const height = addressListHeightFor(view);
|
|
29074
|
+
if (height > ADDRESS_LIST_MAX_HEIGHT_DIP) return null;
|
|
29075
|
+
const left = Math.max(0, fx);
|
|
29076
|
+
const right = Math.min(ownerWidth, fx + fw);
|
|
29077
|
+
const bottom = Math.min(ownerHeight, top + height);
|
|
29078
|
+
if (right <= left) return null;
|
|
29079
|
+
if (bottom - top !== height) return null;
|
|
29080
|
+
return { x: left, y: top, width: right - left, height };
|
|
29081
|
+
}
|
|
29082
|
+
function addressListRowIndexAt(view, offsetY) {
|
|
29083
|
+
if (view.kind !== "rows") return null;
|
|
29084
|
+
if (!Number.isFinite(offsetY)) return null;
|
|
29085
|
+
const withinRows = offsetY - ADDRESS_LIST_HEADER_HEIGHT - 2;
|
|
29086
|
+
if (withinRows < 0) return null;
|
|
29087
|
+
const index = Math.floor(withinRows / ADDRESS_LIST_ROW_HEIGHT);
|
|
29088
|
+
return index >= 0 && index < view.rows.length ? index : null;
|
|
29089
|
+
}
|
|
29090
|
+
function measurementValue(value) {
|
|
29091
|
+
if (!isRecord$1(value)) return null;
|
|
29092
|
+
if (!Number.isInteger(value.appliedSeq)) return null;
|
|
29093
|
+
if (!Number.isInteger(value.rowCount)) return null;
|
|
29094
|
+
const headerHeight = finite(value.headerHeight);
|
|
29095
|
+
const viewportHeight = finite(value.viewportHeight);
|
|
29096
|
+
const scrollHeight = finite(value.scrollHeight);
|
|
29097
|
+
if (headerHeight === null || viewportHeight === null || scrollHeight === null)
|
|
29098
|
+
return null;
|
|
29099
|
+
if (!Array.isArray(value.rowRects)) return null;
|
|
29100
|
+
const rowRects = [];
|
|
29101
|
+
for (const entry of value.rowRects) {
|
|
29102
|
+
if (!isRecord$1(entry)) return null;
|
|
29103
|
+
const top = finite(entry.top);
|
|
29104
|
+
const height = finite(entry.height);
|
|
29105
|
+
if (top === null || height === null) return null;
|
|
29106
|
+
rowRects.push({ top, height });
|
|
29107
|
+
}
|
|
29108
|
+
const innerHeight = finite(value.innerHeight);
|
|
29109
|
+
const visualHeight = finite(value.visualHeight);
|
|
29110
|
+
let activeRowRect = null;
|
|
29111
|
+
if (value.activeRowRect !== null && value.activeRowRect !== void 0) {
|
|
29112
|
+
if (!isRecord$1(value.activeRowRect)) return null;
|
|
29113
|
+
const top = finite(value.activeRowRect.top);
|
|
29114
|
+
const height = finite(value.activeRowRect.height);
|
|
29115
|
+
if (top === null || height === null) return null;
|
|
29116
|
+
activeRowRect = { top, height };
|
|
29117
|
+
}
|
|
29118
|
+
const appliedSeq = value.appliedSeq;
|
|
29119
|
+
const rowCount = value.rowCount;
|
|
29120
|
+
if (typeof appliedSeq !== "number" || typeof rowCount !== "number")
|
|
29121
|
+
return null;
|
|
29122
|
+
return {
|
|
29123
|
+
appliedSeq,
|
|
29124
|
+
rowCount,
|
|
29125
|
+
headerHeight,
|
|
29126
|
+
rowRects,
|
|
29127
|
+
viewportHeight,
|
|
29128
|
+
scrollHeight,
|
|
29129
|
+
...innerHeight === null ? {} : { innerHeight },
|
|
29130
|
+
...visualHeight === null ? {} : { visualHeight },
|
|
29131
|
+
activeRowRect
|
|
29132
|
+
};
|
|
29133
|
+
}
|
|
29134
|
+
function addressListMeasurementProblem(raw, projection, requestedHeight) {
|
|
29135
|
+
const measurement = measurementValue(raw);
|
|
29136
|
+
if (measurement === null) return "measure_invalid";
|
|
29137
|
+
if (measurement.appliedSeq !== projection.seq) return "measure_stale";
|
|
29138
|
+
const reported = measurement.visualHeight ?? measurement.innerHeight;
|
|
29139
|
+
if (reported !== void 0 && Math.abs(reported - requestedHeight) > 1) {
|
|
29140
|
+
return "measure_viewport";
|
|
29141
|
+
}
|
|
29142
|
+
if (projection.view.kind !== "rows") {
|
|
29143
|
+
return measurement.rowCount === 0 ? null : "measure_extra_rows";
|
|
29144
|
+
}
|
|
29145
|
+
const rows = projection.view.rows;
|
|
29146
|
+
if (measurement.rowCount !== rows.length) return "measure_row_count";
|
|
29147
|
+
if (measurement.rowRects.length !== rows.length) return "measure_row_rects";
|
|
29148
|
+
if (Math.abs(measurement.headerHeight - ADDRESS_LIST_HEADER_HEIGHT) > 0.5) {
|
|
29149
|
+
return "measure_header";
|
|
29150
|
+
}
|
|
29151
|
+
for (const row of measurement.rowRects) {
|
|
29152
|
+
if (Math.abs(row.height - ADDRESS_LIST_ROW_HEIGHT) > 0.5) {
|
|
29153
|
+
return "measure_row_height";
|
|
29154
|
+
}
|
|
29155
|
+
}
|
|
29156
|
+
if (measurement.scrollHeight > measurement.viewportHeight + 1) {
|
|
29157
|
+
return "measure_scroll";
|
|
29158
|
+
}
|
|
29159
|
+
if (projection.active >= 0) {
|
|
29160
|
+
if (measurement.activeRowRect === null) return "measure_active_missing";
|
|
29161
|
+
const paintBox = measurement.visualHeight ?? measurement.innerHeight ?? requestedHeight;
|
|
29162
|
+
const { top, height } = measurement.activeRowRect;
|
|
29163
|
+
const documentBottom = measurement.headerHeight + top + height;
|
|
29164
|
+
if (top < -0.5 || documentBottom > paintBox + 0.5) {
|
|
29165
|
+
return "active_not_visible";
|
|
29166
|
+
}
|
|
29167
|
+
}
|
|
29168
|
+
return null;
|
|
29169
|
+
}
|
|
29170
|
+
function addressListViewModel(projection) {
|
|
29171
|
+
const fallback = {
|
|
29172
|
+
elevated: ADDRESS_LIST_THEME_FALLBACK,
|
|
29173
|
+
ink: "#e6e8ea",
|
|
29174
|
+
inkMuted: "#9aa0a6",
|
|
29175
|
+
accent: "#5b9dd9",
|
|
29176
|
+
accentWash: "#23303d",
|
|
29177
|
+
rowHover: "#24282e",
|
|
29178
|
+
hairline: "#2f343b",
|
|
29179
|
+
control: "#3a4048"
|
|
29180
|
+
};
|
|
29181
|
+
const theme = projection.theme ?? fallback;
|
|
29182
|
+
return {
|
|
29183
|
+
v: 1,
|
|
29184
|
+
seq: projection.seq,
|
|
29185
|
+
dir: projection.dir,
|
|
29186
|
+
label: projection.label,
|
|
29187
|
+
active: projection.active,
|
|
29188
|
+
view: projection.view,
|
|
29189
|
+
height: addressListHeightFor(projection.view),
|
|
29190
|
+
theme
|
|
29191
|
+
};
|
|
29192
|
+
}
|
|
29193
|
+
const ADDRESS_OVERLAY_S1_ENV = "LOCAL_OPERATOR_UI_BROWSER_ADDRESS_OVERLAY_S1";
|
|
29194
|
+
const ADDRESS_OVERLAY_VISIBLE_ENV = "LOCAL_OPERATOR_UI_BROWSER_ADDRESS_OVERLAY_VISIBLE";
|
|
29195
|
+
const ADDRESS_OVERLAY_VISIBLE_MAX_MS = 12e4;
|
|
29196
|
+
const ADDRESS_OVERLAY_DOCUMENT = "browser-address-overlay.html";
|
|
29197
|
+
const ADDRESS_OVERLAY_MAX_HEIGHT_DIP = 160;
|
|
29198
|
+
const ADDRESS_OVERLAY_READY_TIMEOUT_MS = 5e3;
|
|
29199
|
+
const DIP_INT32_MAX = 2 ** 31 - 1;
|
|
29200
|
+
const INDEX_DOCUMENT_SUFFIX$2 = /\/?index\.html(\?.*)?$/;
|
|
29201
|
+
const TRAILING_SLASH$1 = /\/$/;
|
|
29202
|
+
function addressOverlayDocumentUrlFor(rendererUrl) {
|
|
29203
|
+
return documentUrlFor(rendererUrl, ADDRESS_OVERLAY_DOCUMENT);
|
|
29204
|
+
}
|
|
29205
|
+
function addressListDocumentUrlFor(rendererUrl) {
|
|
29206
|
+
return documentUrlFor(rendererUrl, ADDRESS_LIST_DOCUMENT);
|
|
29207
|
+
}
|
|
29208
|
+
function documentUrlFor(rendererUrl, document) {
|
|
29209
|
+
return INDEX_DOCUMENT_SUFFIX$2.test(rendererUrl) ? rendererUrl.replace(INDEX_DOCUMENT_SUFFIX$2, `/${document}`) : `${rendererUrl.replace(TRAILING_SLASH$1, "")}/${document}`;
|
|
29210
|
+
}
|
|
29211
|
+
function resolveAddressOverlayGate(input) {
|
|
29212
|
+
const raw = input.env[ADDRESS_OVERLAY_S1_ENV];
|
|
29213
|
+
const listRaw = input.env[ADDRESS_LIST_ENV];
|
|
29214
|
+
const visibleRaw = input.env[ADDRESS_OVERLAY_VISIBLE_ENV];
|
|
29215
|
+
const listAsked = listRaw !== void 0 && listRaw.trim() !== "";
|
|
29216
|
+
const visibleAsked = visibleRaw !== void 0 && visibleRaw.trim() !== "";
|
|
29217
|
+
if (raw === void 0 || raw.trim() === "") {
|
|
29218
|
+
if (visibleAsked) {
|
|
29219
|
+
return {
|
|
29220
|
+
asked: true,
|
|
29221
|
+
enabled: false,
|
|
29222
|
+
list: false,
|
|
29223
|
+
visible: false,
|
|
29224
|
+
reason: `${ADDRESS_OVERLAY_VISIBLE_ENV} needs ${ADDRESS_OVERLAY_S1_ENV}=1 and ${ADDRESS_LIST_ENV}=1: the visible probe is an increment on S1's child and S2's mirror, not a third arm`
|
|
29225
|
+
};
|
|
29226
|
+
}
|
|
29227
|
+
return listAsked ? {
|
|
29228
|
+
asked: true,
|
|
29229
|
+
enabled: false,
|
|
29230
|
+
list: false,
|
|
29231
|
+
visible: false,
|
|
29232
|
+
reason: `${ADDRESS_LIST_ENV} needs ${ADDRESS_OVERLAY_S1_ENV}=1: S2's list is a mirror inside S1's child, not a second child`
|
|
29233
|
+
} : {
|
|
29234
|
+
asked: false,
|
|
29235
|
+
enabled: false,
|
|
29236
|
+
list: false,
|
|
29237
|
+
visible: false,
|
|
29238
|
+
reason: null
|
|
29239
|
+
};
|
|
29240
|
+
}
|
|
29241
|
+
const asked = true;
|
|
29242
|
+
if (raw.trim() !== "1") {
|
|
29243
|
+
return {
|
|
29244
|
+
asked,
|
|
29245
|
+
enabled: false,
|
|
29246
|
+
list: false,
|
|
29247
|
+
visible: false,
|
|
29248
|
+
reason: `${ADDRESS_OVERLAY_S1_ENV}=${raw.trim()} is not an opt-in (accepted: 1); the address overlay stayed off`
|
|
29249
|
+
};
|
|
29250
|
+
}
|
|
29251
|
+
if (listAsked && listRaw.trim() !== "1") {
|
|
29252
|
+
return {
|
|
29253
|
+
asked,
|
|
29254
|
+
enabled: false,
|
|
29255
|
+
list: false,
|
|
29256
|
+
visible: false,
|
|
29257
|
+
reason: `${ADDRESS_LIST_ENV}=${listRaw.trim()} is not an opt-in (accepted: 1); the address overlay stayed off`
|
|
29258
|
+
};
|
|
29259
|
+
}
|
|
29260
|
+
if (visibleAsked) {
|
|
29261
|
+
if (visibleRaw.trim() !== "1") {
|
|
29262
|
+
return {
|
|
29263
|
+
asked,
|
|
29264
|
+
enabled: false,
|
|
29265
|
+
list: false,
|
|
29266
|
+
visible: false,
|
|
29267
|
+
reason: `${ADDRESS_OVERLAY_VISIBLE_ENV}=${visibleRaw.trim()} is not an opt-in (accepted: 1); the address overlay stayed off`
|
|
29268
|
+
};
|
|
29269
|
+
}
|
|
29270
|
+
if (!listAsked) {
|
|
29271
|
+
return {
|
|
29272
|
+
asked,
|
|
29273
|
+
enabled: false,
|
|
29274
|
+
list: false,
|
|
29275
|
+
visible: false,
|
|
29276
|
+
reason: `${ADDRESS_OVERLAY_VISIBLE_ENV} needs ${ADDRESS_LIST_ENV}=1: S3's visible probe shows S2's mirror, never a bare S1 child`
|
|
29277
|
+
};
|
|
29278
|
+
}
|
|
29279
|
+
if (input.windowMode === "headless") {
|
|
29280
|
+
return {
|
|
29281
|
+
asked,
|
|
29282
|
+
enabled: false,
|
|
29283
|
+
list: false,
|
|
29284
|
+
visible: false,
|
|
29285
|
+
reason: `${ADDRESS_OVERLAY_VISIBLE_ENV} refused in a "headless" window: a headless run labelled visible would misattribute its evidence; use --window-mode=inactive`
|
|
29286
|
+
};
|
|
29287
|
+
}
|
|
29288
|
+
if (input.windowMode !== "inactive") {
|
|
29289
|
+
return {
|
|
29290
|
+
asked,
|
|
29291
|
+
enabled: false,
|
|
29292
|
+
list: false,
|
|
29293
|
+
visible: false,
|
|
29294
|
+
reason: `${ADDRESS_OVERLAY_VISIBLE_ENV} refused in a "${input.windowMode}" window: a visible probe never runs in the product's own mode; use --window-mode=inactive`
|
|
29295
|
+
};
|
|
29296
|
+
}
|
|
29297
|
+
if (!input.devDriverArmed) {
|
|
29298
|
+
return {
|
|
29299
|
+
asked,
|
|
29300
|
+
enabled: false,
|
|
29301
|
+
list: false,
|
|
29302
|
+
visible: false,
|
|
29303
|
+
reason: "the visible address overlay needs the armed renderer dev driver; it has no product path"
|
|
29304
|
+
};
|
|
29305
|
+
}
|
|
29306
|
+
if (input.packaged === true) {
|
|
29307
|
+
return {
|
|
29308
|
+
asked,
|
|
29309
|
+
enabled: false,
|
|
29310
|
+
list: false,
|
|
29311
|
+
visible: false,
|
|
29312
|
+
reason: `${ADDRESS_OVERLAY_VISIBLE_ENV} refused in a packaged build: S3 is an unpackaged probe, never product surface`
|
|
29313
|
+
};
|
|
29314
|
+
}
|
|
29315
|
+
return {
|
|
29316
|
+
asked,
|
|
29317
|
+
enabled: true,
|
|
29318
|
+
list: true,
|
|
29319
|
+
visible: true,
|
|
29320
|
+
reason: null
|
|
29321
|
+
};
|
|
29322
|
+
}
|
|
29323
|
+
if (!input.devDriverArmed) {
|
|
29324
|
+
return {
|
|
29325
|
+
asked,
|
|
29326
|
+
enabled: false,
|
|
29327
|
+
list: false,
|
|
29328
|
+
visible: false,
|
|
29329
|
+
reason: "the address overlay needs the armed renderer dev driver; it has no user-facing trigger in S1"
|
|
29330
|
+
};
|
|
29331
|
+
}
|
|
29332
|
+
if (input.windowMode !== "headless") {
|
|
29333
|
+
return {
|
|
29334
|
+
asked,
|
|
29335
|
+
enabled: false,
|
|
29336
|
+
list: false,
|
|
29337
|
+
visible: false,
|
|
29338
|
+
reason: `the address overlay refused to arm in a "${input.windowMode}" window; S1 has no user-facing path, so use --window-mode=headless`
|
|
29339
|
+
};
|
|
29340
|
+
}
|
|
29341
|
+
return {
|
|
29342
|
+
asked,
|
|
29343
|
+
enabled: true,
|
|
29344
|
+
list: listAsked,
|
|
29345
|
+
visible: false,
|
|
29346
|
+
reason: null
|
|
29347
|
+
};
|
|
29348
|
+
}
|
|
29349
|
+
function int32(value) {
|
|
29350
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return null;
|
|
29351
|
+
const rounded = Math.round(value);
|
|
29352
|
+
return Math.abs(rounded) > DIP_INT32_MAX ? null : rounded;
|
|
29353
|
+
}
|
|
29354
|
+
function overlayBoundsFor(contentRect, owner) {
|
|
29355
|
+
if (contentRect === null) return null;
|
|
29356
|
+
const x = int32(contentRect.x);
|
|
29357
|
+
const y = int32(contentRect.y);
|
|
29358
|
+
const width = int32(contentRect.width);
|
|
29359
|
+
const height = int32(contentRect.height);
|
|
29360
|
+
const ownerWidth = int32(owner.width);
|
|
29361
|
+
const ownerHeight = int32(owner.height);
|
|
29362
|
+
if (x === null || y === null || width === null || height === null || ownerWidth === null || ownerHeight === null)
|
|
29363
|
+
return null;
|
|
29364
|
+
if (width <= 0 || height <= 0 || ownerWidth <= 0 || ownerHeight <= 0) {
|
|
29365
|
+
return null;
|
|
29366
|
+
}
|
|
29367
|
+
const left = Math.max(0, x);
|
|
29368
|
+
const top = Math.max(0, y);
|
|
29369
|
+
const right = Math.min(ownerWidth, x + width);
|
|
29370
|
+
const bottom = Math.min(
|
|
29371
|
+
ownerHeight,
|
|
29372
|
+
y + Math.min(height, ADDRESS_OVERLAY_MAX_HEIGHT_DIP)
|
|
29373
|
+
);
|
|
29374
|
+
if (right <= left || bottom <= top) return null;
|
|
29375
|
+
return { x: left, y: top, width: right - left, height: bottom - top };
|
|
29376
|
+
}
|
|
29377
|
+
function copyLayout(snapshot2) {
|
|
29378
|
+
return {
|
|
29379
|
+
activeTabId: snapshot2.activeTabId,
|
|
29380
|
+
presented: snapshot2.presented,
|
|
29381
|
+
contentRect: snapshot2.contentRect ? {
|
|
29382
|
+
x: snapshot2.contentRect.x,
|
|
29383
|
+
y: snapshot2.contentRect.y,
|
|
29384
|
+
width: snapshot2.contentRect.width,
|
|
29385
|
+
height: snapshot2.contentRect.height
|
|
29386
|
+
} : null,
|
|
29387
|
+
revision: snapshot2.revision
|
|
29388
|
+
};
|
|
29389
|
+
}
|
|
29390
|
+
function layoutKeyOf(snapshot2) {
|
|
29391
|
+
const rect2 = snapshot2.contentRect;
|
|
29392
|
+
return `${snapshot2.activeTabId}:${snapshot2.presented ? 1 : 0}:${rect2 ? `${rect2.x},${rect2.y},${rect2.width},${rect2.height}` : "null"}`;
|
|
29393
|
+
}
|
|
29394
|
+
function sameRect(a, b) {
|
|
29395
|
+
if (b === null) return false;
|
|
29396
|
+
return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
|
|
29397
|
+
}
|
|
29398
|
+
const OVERLAY_BACKGROUND = "#1b1f24";
|
|
29399
|
+
function createHardenedView(options) {
|
|
29400
|
+
const partition = `address-overlay-${node_crypto.randomUUID()}`;
|
|
29401
|
+
const childSession = electron.session.fromPartition(partition);
|
|
29402
|
+
childSession.setPermissionRequestHandler(
|
|
29403
|
+
(_contents, _permission, callback) => {
|
|
29404
|
+
callback(false);
|
|
29405
|
+
}
|
|
29406
|
+
);
|
|
29407
|
+
childSession.setPermissionCheckHandler(() => false);
|
|
29408
|
+
childSession.on("will-download", (event) => {
|
|
29409
|
+
event.preventDefault();
|
|
29410
|
+
});
|
|
29411
|
+
const view = new electron.WebContentsView({
|
|
29412
|
+
webPreferences: {
|
|
29413
|
+
partition,
|
|
29414
|
+
sandbox: true,
|
|
29415
|
+
contextIsolation: true,
|
|
29416
|
+
nodeIntegration: false,
|
|
29417
|
+
webSecurity: true,
|
|
29418
|
+
allowRunningInsecureContent: false,
|
|
29419
|
+
// No `preload`, and no app imports in either document. The flag is the
|
|
29420
|
+
// S1/S2 split: false for the inert document, true for the mirror list.
|
|
29421
|
+
javascript: options.javascript,
|
|
29422
|
+
// The mirror is a tiny document that must stay frame-accurate while the
|
|
29423
|
+
// owner window is hidden, unfocused or occluded (headless probe, a
|
|
29424
|
+
// background app window). Chromium otherwise throttles its frame loop,
|
|
29425
|
+
// and a native resize can sit unapplied for ~1 s — the exact interval
|
|
29426
|
+
// `measure_viewport` is there to catch. S1 keeps the default.
|
|
29427
|
+
...options.javascript ? { backgroundThrottling: false } : {}
|
|
29428
|
+
}
|
|
29429
|
+
});
|
|
29430
|
+
view.webContents.setWindowOpenHandler(() => ({ action: "deny" }));
|
|
29431
|
+
view.setBackgroundColor(OVERLAY_BACKGROUND);
|
|
29432
|
+
return view;
|
|
29433
|
+
}
|
|
29434
|
+
class AddressOverlayController {
|
|
29435
|
+
window;
|
|
29436
|
+
documentUrl;
|
|
29437
|
+
createView;
|
|
29438
|
+
javascript;
|
|
29439
|
+
onEvent;
|
|
29440
|
+
readinessTimeoutMs;
|
|
29441
|
+
log;
|
|
29442
|
+
now;
|
|
29443
|
+
appMetrics;
|
|
29444
|
+
view;
|
|
29445
|
+
childWebContentsId;
|
|
29446
|
+
state;
|
|
29447
|
+
failedReason;
|
|
29448
|
+
hiddenReason;
|
|
29449
|
+
generation;
|
|
29450
|
+
childCreatedCount;
|
|
29451
|
+
shownCount;
|
|
29452
|
+
lastShow;
|
|
29453
|
+
layout;
|
|
29454
|
+
layoutKey;
|
|
29455
|
+
boundTabId;
|
|
29456
|
+
domReady;
|
|
29457
|
+
finished;
|
|
29458
|
+
loadStartedAt;
|
|
29459
|
+
loadMs;
|
|
29460
|
+
readyWaiter;
|
|
29461
|
+
readinessTimer;
|
|
29462
|
+
showInFlight;
|
|
29463
|
+
disposingChild;
|
|
29464
|
+
disposed;
|
|
29465
|
+
ownerListeners;
|
|
29466
|
+
childListeners;
|
|
29467
|
+
// ---- S2 mirror state ----------------------------------------------------
|
|
29468
|
+
lastSeq;
|
|
29469
|
+
shownSeq;
|
|
29470
|
+
lastProjection;
|
|
29471
|
+
/** The rectangle last applied for a projection. `onLayout`'s unchanged-key
|
|
29472
|
+
* re-assert uses THIS, never the S1 top-strip rule: an S2 child sits at the
|
|
29473
|
+
* dock, and re-raising it with the S1 rectangle would move the list. */
|
|
29474
|
+
lastAppliedBounds;
|
|
29475
|
+
lastMeasurement;
|
|
29476
|
+
themeFact;
|
|
29477
|
+
applyCount;
|
|
29478
|
+
commitsEmitted;
|
|
29479
|
+
commitsDropped;
|
|
29480
|
+
hideCounts;
|
|
29481
|
+
keysBlocked;
|
|
29482
|
+
mouseDowns;
|
|
29483
|
+
mouseUps;
|
|
29484
|
+
mouseMoves;
|
|
29485
|
+
mouseDownRow;
|
|
29486
|
+
contextMenuBlocked;
|
|
29487
|
+
// ---- S3 focus facts -----------------------------------------------------
|
|
29488
|
+
/** Counted only for the scripted (S2/S3) child, so S1's listener set stays
|
|
29489
|
+
* exactly what it was; see `wireChild`'s S2 policy comment. */
|
|
29490
|
+
childFocusEvents;
|
|
29491
|
+
childBlurEvents;
|
|
29492
|
+
/** The launch gate's `visible` fact, copied in so `facts()` can report it
|
|
29493
|
+
* without re-deriving the gate (which it must never do: one pure decision). */
|
|
29494
|
+
visibleMode;
|
|
29495
|
+
lastRejected;
|
|
29496
|
+
projectQueue;
|
|
29497
|
+
constructor(deps) {
|
|
29498
|
+
this.window = deps.window;
|
|
29499
|
+
this.documentUrl = deps.documentUrl;
|
|
29500
|
+
this.createView = deps.createView ?? (() => createHardenedView({ javascript: deps.javascript === true }));
|
|
29501
|
+
this.javascript = deps.javascript === true;
|
|
29502
|
+
this.onEvent = deps.onEvent ?? null;
|
|
29503
|
+
this.readinessTimeoutMs = deps.readinessTimeoutMs ?? ADDRESS_OVERLAY_READY_TIMEOUT_MS;
|
|
29504
|
+
this.log = deps.log ?? (() => {
|
|
29505
|
+
});
|
|
29506
|
+
this.now = deps.now ?? (() => Date.now());
|
|
29507
|
+
this.appMetrics = deps.appMetrics ?? null;
|
|
29508
|
+
this.view = null;
|
|
29509
|
+
this.childWebContentsId = null;
|
|
29510
|
+
this.state = "absent";
|
|
29511
|
+
this.failedReason = null;
|
|
29512
|
+
this.hiddenReason = null;
|
|
29513
|
+
this.generation = 0;
|
|
29514
|
+
this.childCreatedCount = 0;
|
|
29515
|
+
this.shownCount = 0;
|
|
29516
|
+
this.lastShow = null;
|
|
29517
|
+
this.layout = {
|
|
29518
|
+
activeTabId: null,
|
|
29519
|
+
presented: false,
|
|
29520
|
+
contentRect: null,
|
|
29521
|
+
revision: 0
|
|
29522
|
+
};
|
|
29523
|
+
this.layoutKey = "none";
|
|
29524
|
+
this.boundTabId = null;
|
|
29525
|
+
this.domReady = false;
|
|
29526
|
+
this.finished = false;
|
|
29527
|
+
this.loadStartedAt = null;
|
|
29528
|
+
this.loadMs = null;
|
|
29529
|
+
this.readyWaiter = null;
|
|
29530
|
+
this.readinessTimer = null;
|
|
29531
|
+
this.showInFlight = null;
|
|
29532
|
+
this.disposingChild = false;
|
|
29533
|
+
this.disposed = false;
|
|
29534
|
+
this.ownerListeners = [];
|
|
29535
|
+
this.childListeners = [];
|
|
29536
|
+
this.lastSeq = 0;
|
|
29537
|
+
this.shownSeq = -1;
|
|
29538
|
+
this.lastProjection = null;
|
|
29539
|
+
this.lastAppliedBounds = null;
|
|
29540
|
+
this.lastMeasurement = null;
|
|
29541
|
+
this.themeFact = "absent";
|
|
29542
|
+
this.applyCount = 0;
|
|
29543
|
+
this.commitsEmitted = 0;
|
|
29544
|
+
this.commitsDropped = /* @__PURE__ */ new Map();
|
|
29545
|
+
this.hideCounts = /* @__PURE__ */ new Map();
|
|
29546
|
+
this.keysBlocked = 0;
|
|
29547
|
+
this.mouseDowns = 0;
|
|
29548
|
+
this.mouseUps = 0;
|
|
29549
|
+
this.mouseMoves = 0;
|
|
29550
|
+
this.mouseDownRow = null;
|
|
29551
|
+
this.contextMenuBlocked = 0;
|
|
29552
|
+
this.childFocusEvents = 0;
|
|
29553
|
+
this.childBlurEvents = 0;
|
|
29554
|
+
this.visibleMode = deps.visible === true;
|
|
29555
|
+
this.lastRejected = null;
|
|
29556
|
+
this.projectQueue = Promise.resolve();
|
|
29557
|
+
this.attachOwnerListeners();
|
|
29558
|
+
}
|
|
29559
|
+
// ---- observer hooks ------------------------------------------------------
|
|
29560
|
+
/**
|
|
29561
|
+
* The registry's post-layout hook. Called after every page layout pass,
|
|
29562
|
+
* including null and suppressed ones.
|
|
29563
|
+
*
|
|
29564
|
+
* A changed key (tab, presentation or rectangle) hides and invalidates first:
|
|
29565
|
+
* a moved page must never keep a stale rectangle floating over it, and a
|
|
29566
|
+
* resized window invalidates old geometry until a fresh renderer rect
|
|
29567
|
+
* arrives. An unchanged key is the background-page-creation case: the layout
|
|
29568
|
+
* pass may have added a page above the child, so the child is re-added last
|
|
29569
|
+
* and given the same rectangle. It never re-opens by itself; showing again is
|
|
29570
|
+
* always an explicit probe call.
|
|
29571
|
+
*/
|
|
29572
|
+
onLayout(snapshot2) {
|
|
29573
|
+
if (this.disposed) return;
|
|
29574
|
+
const next = copyLayout(snapshot2);
|
|
29575
|
+
const nextKey = layoutKeyOf(next);
|
|
29576
|
+
const previousKey = this.layoutKey;
|
|
29577
|
+
this.layout = next;
|
|
29578
|
+
this.layoutKey = nextKey;
|
|
29579
|
+
if (previousKey !== nextKey) {
|
|
29580
|
+
this.hide("layout");
|
|
29581
|
+
return;
|
|
29582
|
+
}
|
|
29583
|
+
if (this.state !== "shown") return;
|
|
29584
|
+
const view = this.view;
|
|
29585
|
+
if (!view || this.viewUsable(view) === false) return;
|
|
29586
|
+
const bounds = this.lastProjection !== null ? this.lastAppliedBounds : overlayBoundsFor(next.contentRect, this.ownerSize());
|
|
29587
|
+
if (bounds === null || !next.presented) {
|
|
29588
|
+
this.hide("layout_invalid");
|
|
29589
|
+
return;
|
|
29590
|
+
}
|
|
29591
|
+
try {
|
|
29592
|
+
view.setBounds(bounds);
|
|
29593
|
+
this.raiseChild(view);
|
|
29594
|
+
} catch (error) {
|
|
29595
|
+
this.releaseChild("layout_error");
|
|
29596
|
+
this.log(`[browser] address overlay layout failed: ${String(error)}`);
|
|
29597
|
+
}
|
|
29598
|
+
}
|
|
29599
|
+
/** The bound page is going away. Dispose the child before the page release. */
|
|
29600
|
+
onTabReleased(tabId) {
|
|
29601
|
+
if (this.disposed) return;
|
|
29602
|
+
if (this.boundTabId !== tabId) return;
|
|
29603
|
+
this.releaseChild("bound-tab-released");
|
|
29604
|
+
}
|
|
29605
|
+
/** The bound page's renderer died but its webContents survives (a crashed tab).
|
|
29606
|
+
* The child is bound to that page's rectangle, so it goes with the renderer; a
|
|
29607
|
+
* later explicit show may create a fresh one. */
|
|
29608
|
+
onPageGone(tabId) {
|
|
29609
|
+
if (this.disposed) return;
|
|
29610
|
+
if (this.boundTabId !== tabId) return;
|
|
29611
|
+
this.releaseChild("page-crash");
|
|
29612
|
+
}
|
|
29613
|
+
// ---- probe surface ------------------------------------------------------
|
|
29614
|
+
async showOverlay() {
|
|
29615
|
+
if (this.showInFlight) return this.showInFlight;
|
|
29616
|
+
const pending = this.showOnce();
|
|
29617
|
+
this.showInFlight = pending;
|
|
29618
|
+
void pending.then(
|
|
29619
|
+
() => {
|
|
29620
|
+
if (this.showInFlight === pending) this.showInFlight = null;
|
|
29621
|
+
},
|
|
29622
|
+
() => {
|
|
29623
|
+
if (this.showInFlight === pending) this.showInFlight = null;
|
|
29624
|
+
}
|
|
29625
|
+
);
|
|
29626
|
+
return pending;
|
|
29627
|
+
}
|
|
29628
|
+
hide(reason = "hide") {
|
|
29629
|
+
if (this.disposed) return;
|
|
29630
|
+
this.generation += 1;
|
|
29631
|
+
this.settleReady(false);
|
|
29632
|
+
this.hiddenReason = reason;
|
|
29633
|
+
this.hideCounts.set(reason, (this.hideCounts.get(reason) ?? 0) + 1);
|
|
29634
|
+
this.shownSeq = -1;
|
|
29635
|
+
this.mouseDownRow = null;
|
|
29636
|
+
this.lastMeasurement = null;
|
|
29637
|
+
const view = this.view;
|
|
29638
|
+
if (view && this.viewUsable(view)) {
|
|
29639
|
+
try {
|
|
29640
|
+
view.setVisible(false);
|
|
29641
|
+
} catch {
|
|
29642
|
+
}
|
|
29643
|
+
}
|
|
29644
|
+
if (this.state === "shown") {
|
|
29645
|
+
this.state = this.failedReason ? "failed" : this.domReady && this.finished ? "ready" : "loading";
|
|
29646
|
+
}
|
|
29647
|
+
this.log(`[browser] address overlay hidden: ${reason}`);
|
|
29648
|
+
}
|
|
29649
|
+
/** `WebContents.isFocused()` behind a guard: a disposed view throws. */
|
|
29650
|
+
childIsFocused(view) {
|
|
29651
|
+
try {
|
|
29652
|
+
return view.webContents.isFocused();
|
|
29653
|
+
} catch {
|
|
29654
|
+
return false;
|
|
29655
|
+
}
|
|
29656
|
+
}
|
|
29657
|
+
/** Facts only: identifiers, geometry and counters. No page content. */
|
|
29658
|
+
facts() {
|
|
29659
|
+
const view = this.view;
|
|
29660
|
+
let child = null;
|
|
29661
|
+
if (view) {
|
|
29662
|
+
let visible = false;
|
|
29663
|
+
let bounds = null;
|
|
29664
|
+
let order = -1;
|
|
29665
|
+
let childCount = 0;
|
|
29666
|
+
try {
|
|
29667
|
+
visible = view.getVisible();
|
|
29668
|
+
bounds = view.getBounds();
|
|
29669
|
+
const children = this.window.contentView.children;
|
|
29670
|
+
childCount = children.length;
|
|
29671
|
+
order = children.indexOf(view);
|
|
29672
|
+
} catch {
|
|
29673
|
+
}
|
|
29674
|
+
child = {
|
|
29675
|
+
webContentsId: this.childWebContentsId ?? view.webContents.id,
|
|
29676
|
+
visible,
|
|
29677
|
+
bounds,
|
|
29678
|
+
order,
|
|
29679
|
+
childCount,
|
|
29680
|
+
focusEvents: this.childFocusEvents,
|
|
29681
|
+
blurEvents: this.childBlurEvents,
|
|
29682
|
+
isFocused: this.childIsFocused(view)
|
|
29683
|
+
};
|
|
29684
|
+
}
|
|
29685
|
+
let ownerDestroyed = true;
|
|
29686
|
+
let contentBounds = null;
|
|
29687
|
+
try {
|
|
29688
|
+
ownerDestroyed = this.window.isDestroyed();
|
|
29689
|
+
if (!ownerDestroyed) contentBounds = this.ownerSize();
|
|
29690
|
+
} catch {
|
|
29691
|
+
ownerDestroyed = true;
|
|
29692
|
+
}
|
|
29693
|
+
let ownerWindowFocused = false;
|
|
29694
|
+
try {
|
|
29695
|
+
ownerWindowFocused = !ownerDestroyed && this.window.isFocused();
|
|
29696
|
+
} catch {
|
|
29697
|
+
ownerWindowFocused = false;
|
|
29698
|
+
}
|
|
29699
|
+
let ownerFocusedContentsId = null;
|
|
29700
|
+
try {
|
|
29701
|
+
ownerFocusedContentsId = electron.webContents.getFocusedWebContents()?.id ?? null;
|
|
29702
|
+
} catch {
|
|
29703
|
+
ownerFocusedContentsId = null;
|
|
29704
|
+
}
|
|
29705
|
+
return {
|
|
29706
|
+
documentUrl: this.documentUrl,
|
|
29707
|
+
state: this.state,
|
|
29708
|
+
failedReason: this.failedReason,
|
|
29709
|
+
hiddenReason: this.hiddenReason,
|
|
29710
|
+
generation: this.generation,
|
|
29711
|
+
childCreatedCount: this.childCreatedCount,
|
|
29712
|
+
shownCount: this.shownCount,
|
|
29713
|
+
lastShow: this.lastShow,
|
|
29714
|
+
readiness: {
|
|
29715
|
+
domReady: this.domReady,
|
|
29716
|
+
finished: this.finished,
|
|
29717
|
+
loadMs: this.loadMs
|
|
29718
|
+
},
|
|
29719
|
+
child,
|
|
29720
|
+
owner: {
|
|
29721
|
+
windowId: typeof this.window.id === "number" ? this.window.id : null,
|
|
29722
|
+
destroyed: ownerDestroyed,
|
|
29723
|
+
contentBounds
|
|
29724
|
+
},
|
|
29725
|
+
layout: copyLayout(this.layout),
|
|
29726
|
+
listeners: {
|
|
29727
|
+
owner: this.ownerListeners.length,
|
|
29728
|
+
child: this.childListeners.length
|
|
29729
|
+
},
|
|
29730
|
+
pendingShow: this.showInFlight !== null,
|
|
29731
|
+
readinessTimer: this.readinessTimer !== null,
|
|
29732
|
+
visibleMode: this.visibleMode,
|
|
29733
|
+
ownerFocusedContentsId,
|
|
29734
|
+
ownerWindowFocused,
|
|
29735
|
+
s2: {
|
|
29736
|
+
list: this.javascript,
|
|
29737
|
+
javascript: this.javascript,
|
|
29738
|
+
lastSeq: this.lastSeq,
|
|
29739
|
+
shownSeq: this.shownSeq,
|
|
29740
|
+
lastRejected: this.lastRejected,
|
|
29741
|
+
theme: this.themeFact,
|
|
29742
|
+
applyCount: this.applyCount,
|
|
29743
|
+
view: this.lastProjection === null ? { kind: "none", rowCount: 0, active: -1 } : {
|
|
29744
|
+
kind: this.lastProjection.view.kind,
|
|
29745
|
+
rowCount: this.lastProjection.view.kind === "rows" ? this.lastProjection.view.rows.length : 0,
|
|
29746
|
+
active: this.lastProjection.active
|
|
29747
|
+
},
|
|
29748
|
+
measured: this.lastMeasurement,
|
|
29749
|
+
commitsEmitted: this.commitsEmitted,
|
|
29750
|
+
commitsDropped: Object.fromEntries(this.commitsDropped),
|
|
29751
|
+
hideCounts: Object.fromEntries(this.hideCounts),
|
|
29752
|
+
keysBlocked: this.keysBlocked,
|
|
29753
|
+
mouseDowns: this.mouseDowns,
|
|
29754
|
+
mouseUps: this.mouseUps,
|
|
29755
|
+
mouseMoves: this.mouseMoves,
|
|
29756
|
+
contextMenuBlocked: this.contextMenuBlocked
|
|
29757
|
+
},
|
|
29758
|
+
metrics: this.appMetrics ? this.appMetrics() : null
|
|
29759
|
+
};
|
|
29760
|
+
}
|
|
29761
|
+
async probe(action, argument) {
|
|
29762
|
+
if (action === "show") {
|
|
29763
|
+
await this.showOverlay();
|
|
29764
|
+
return this.facts();
|
|
29765
|
+
}
|
|
29766
|
+
if (action === "hide") {
|
|
29767
|
+
this.hide("probe");
|
|
29768
|
+
return this.facts();
|
|
29769
|
+
}
|
|
29770
|
+
if (action === "click") {
|
|
29771
|
+
this.probeClick(argument);
|
|
29772
|
+
await new Promise((done) => setTimeout(done, 40));
|
|
29773
|
+
return this.facts();
|
|
29774
|
+
}
|
|
29775
|
+
if (action === "measure") {
|
|
29776
|
+
await this.probeMeasure();
|
|
29777
|
+
return this.facts();
|
|
29778
|
+
}
|
|
29779
|
+
if (action === "key") {
|
|
29780
|
+
this.probeKey(argument);
|
|
29781
|
+
await new Promise((done) => setTimeout(done, 20));
|
|
29782
|
+
return this.facts();
|
|
29783
|
+
}
|
|
29784
|
+
return this.facts();
|
|
29785
|
+
}
|
|
29786
|
+
/**
|
|
29787
|
+
* Apply one renderer projection (S2). Everything arriving here was or will be
|
|
29788
|
+
* revalidated: JSON size, shape, every string, tab/layout freshness, zoom,
|
|
29789
|
+
* anchor geometry and — after the child paints — the child's own measurements.
|
|
29790
|
+
*
|
|
29791
|
+
* Serialized: a per-frame renderer can outpace `executeJavaScript`, and two
|
|
29792
|
+
* overlapping applies would let an older model win. The queue is per
|
|
29793
|
+
* controller, so a burst between frames costs one apply each in arrival order.
|
|
29794
|
+
*/
|
|
29795
|
+
project(raw) {
|
|
29796
|
+
const run = () => this.projectOnce(raw);
|
|
29797
|
+
const next = this.projectQueue.then(run, run);
|
|
29798
|
+
this.projectQueue = next.then(
|
|
29799
|
+
() => void 0,
|
|
29800
|
+
() => void 0
|
|
29801
|
+
);
|
|
29802
|
+
return next;
|
|
29803
|
+
}
|
|
29804
|
+
async projectOnce(raw) {
|
|
29805
|
+
const parsed = sanitizeProjection(raw);
|
|
29806
|
+
if (!parsed.ok) {
|
|
29807
|
+
this.drop(parsed.reason);
|
|
29808
|
+
return {
|
|
29809
|
+
accepted: false,
|
|
29810
|
+
shown: false,
|
|
29811
|
+
reason: parsed.reason,
|
|
29812
|
+
bounds: null
|
|
29813
|
+
};
|
|
29814
|
+
}
|
|
29815
|
+
const projection = parsed.projection;
|
|
29816
|
+
this.themeFact = projection.themeFact;
|
|
29817
|
+
if (!projection.visible) {
|
|
29818
|
+
if (projection.seq > this.lastSeq) this.lastSeq = projection.seq;
|
|
29819
|
+
this.hide("projection_hidden");
|
|
29820
|
+
return { accepted: true, shown: false, reason: null, bounds: null };
|
|
29821
|
+
}
|
|
29822
|
+
if (this.disposed) {
|
|
29823
|
+
return this.refuseProjection("disposed");
|
|
29824
|
+
}
|
|
29825
|
+
if (projection.seq < this.lastSeq) {
|
|
29826
|
+
this.drop("stale_seq");
|
|
29827
|
+
return {
|
|
29828
|
+
accepted: false,
|
|
29829
|
+
shown: this.state === "shown",
|
|
29830
|
+
reason: "stale_seq",
|
|
29831
|
+
bounds: null
|
|
29832
|
+
};
|
|
29833
|
+
}
|
|
29834
|
+
if (projection.seq === this.lastSeq && projection.seq === this.shownSeq) {
|
|
29835
|
+
return {
|
|
29836
|
+
accepted: true,
|
|
29837
|
+
shown: this.state === "shown",
|
|
29838
|
+
reason: null,
|
|
29839
|
+
bounds: this.view && this.viewUsable(this.view) ? this.view.getBounds() : null
|
|
29840
|
+
};
|
|
29841
|
+
}
|
|
29842
|
+
if (this.failedReason) return this.refuseProjection("overlay_failed");
|
|
29843
|
+
if (this.ownerUnavailable()) {
|
|
29844
|
+
this.releaseChild("owner_unavailable");
|
|
29845
|
+
return this.refuseProjection("owner_unavailable");
|
|
29846
|
+
}
|
|
29847
|
+
const zoom = this.ownerZoom();
|
|
29848
|
+
if (zoom === null || Math.abs(zoom - 1) > 1e-3) {
|
|
29849
|
+
return this.refuseProjection("zoomed");
|
|
29850
|
+
}
|
|
29851
|
+
const layout = this.layout;
|
|
29852
|
+
if (projection.tabId !== layout.activeTabId) {
|
|
29853
|
+
return this.refuseProjection("stale_tab");
|
|
29854
|
+
}
|
|
29855
|
+
if (!layout.presented || !sameRect(projection.pageRect, layout.contentRect)) {
|
|
29856
|
+
return this.refuseProjection("stale_layout");
|
|
29857
|
+
}
|
|
29858
|
+
const bounds = addressListBoundsFor(
|
|
29859
|
+
projection.field,
|
|
29860
|
+
projection.listTop,
|
|
29861
|
+
projection.view,
|
|
29862
|
+
this.ownerSize()
|
|
29863
|
+
);
|
|
29864
|
+
if (bounds === null) return this.refuseProjection("anchor_inconsistent");
|
|
29865
|
+
this.generation += 1;
|
|
29866
|
+
const generation = this.generation;
|
|
29867
|
+
this.boundTabId = projection.tabId;
|
|
29868
|
+
if (!this.view) {
|
|
29869
|
+
const created = this.createChild(bounds);
|
|
29870
|
+
if (!created) {
|
|
29871
|
+
return this.refuseProjection(
|
|
29872
|
+
this.failedReason ?? "child_create_failed"
|
|
29873
|
+
);
|
|
29874
|
+
}
|
|
29875
|
+
}
|
|
29876
|
+
if (!(this.domReady && this.finished)) {
|
|
29877
|
+
this.state = "loading";
|
|
29878
|
+
const ready = await this.waitForReady();
|
|
29879
|
+
if (!ready)
|
|
29880
|
+
return this.refuseProjection(this.failedReason ?? "invalidated");
|
|
29881
|
+
}
|
|
29882
|
+
if (this.disposed || generation !== this.generation) {
|
|
29883
|
+
return this.refuseProjection("invalidated");
|
|
29884
|
+
}
|
|
29885
|
+
const view = this.view;
|
|
29886
|
+
if (!view || !this.viewUsable(view)) {
|
|
29887
|
+
return this.refuseProjection("child_unavailable");
|
|
29888
|
+
}
|
|
29889
|
+
view.setBounds(bounds);
|
|
29890
|
+
this.raiseChild(view);
|
|
29891
|
+
view.setVisible(true);
|
|
29892
|
+
const model = addressListViewModel(projection);
|
|
29893
|
+
let measured = null;
|
|
29894
|
+
let problem = null;
|
|
29895
|
+
try {
|
|
29896
|
+
for (let attempt = 0; attempt < 8; attempt += 1) {
|
|
29897
|
+
if (attempt % 2 === 1) view.setBounds(bounds);
|
|
29898
|
+
measured = await view.webContents.executeJavaScript(
|
|
29899
|
+
`window.__loAddressOverlay && window.__loAddressOverlay.apply(${JSON.stringify(model)})`,
|
|
29900
|
+
false
|
|
29901
|
+
);
|
|
29902
|
+
problem = addressListMeasurementProblem(
|
|
29903
|
+
measured,
|
|
29904
|
+
projection,
|
|
29905
|
+
bounds.height
|
|
29906
|
+
);
|
|
29907
|
+
if (problem === null) break;
|
|
29908
|
+
if (problem !== "measure_scroll" && problem !== "measure_viewport")
|
|
29909
|
+
break;
|
|
29910
|
+
await new Promise((done) => setTimeout(done, 150));
|
|
29911
|
+
}
|
|
29912
|
+
} catch {
|
|
29913
|
+
return this.refuseProjection("apply_error");
|
|
29914
|
+
}
|
|
29915
|
+
if (problem !== null) return this.refuseProjection(problem, bounds);
|
|
29916
|
+
this.applyCount += 1;
|
|
29917
|
+
this.lastSeq = projection.seq;
|
|
29918
|
+
this.shownSeq = projection.seq;
|
|
29919
|
+
this.lastProjection = projection;
|
|
29920
|
+
this.lastAppliedBounds = bounds;
|
|
29921
|
+
this.lastMeasurement = measured;
|
|
29922
|
+
this.lastRejected = null;
|
|
29923
|
+
this.state = "shown";
|
|
29924
|
+
this.hiddenReason = null;
|
|
29925
|
+
return { accepted: true, shown: true, reason: null, bounds };
|
|
29926
|
+
}
|
|
29927
|
+
refuseProjection(reason, bounds = null) {
|
|
29928
|
+
this.lastRejected = reason;
|
|
29929
|
+
this.drop(reason);
|
|
29930
|
+
this.hide(reason);
|
|
29931
|
+
return { accepted: false, shown: false, reason, bounds };
|
|
29932
|
+
}
|
|
29933
|
+
drop(reason) {
|
|
29934
|
+
this.commitsDropped.set(reason, (this.commitsDropped.get(reason) ?? 0) + 1);
|
|
29935
|
+
}
|
|
29936
|
+
ownerZoom() {
|
|
29937
|
+
try {
|
|
29938
|
+
const zoom = this.window.webContents.getZoomFactor();
|
|
29939
|
+
return typeof zoom === "number" && Number.isFinite(zoom) ? zoom : null;
|
|
29940
|
+
} catch {
|
|
29941
|
+
return null;
|
|
29942
|
+
}
|
|
29943
|
+
}
|
|
29944
|
+
rowAt(y) {
|
|
29945
|
+
if (this.state !== "shown") return null;
|
|
29946
|
+
if (this.lastProjection === null) return null;
|
|
29947
|
+
if (this.shownSeq !== this.lastSeq || this.shownSeq < 1) return null;
|
|
29948
|
+
const projection = this.lastProjection;
|
|
29949
|
+
if (projection.view.kind !== "rows") return null;
|
|
29950
|
+
const index = addressListRowIndexAt(projection.view, y);
|
|
29951
|
+
if (index === null || index >= projection.view.rows.length) return null;
|
|
29952
|
+
return index;
|
|
29953
|
+
}
|
|
29954
|
+
emitCommit(index) {
|
|
29955
|
+
const projection = this.lastProjection;
|
|
29956
|
+
const sink = this.onEvent;
|
|
29957
|
+
if (projection === null || sink === null) {
|
|
29958
|
+
this.drop("no_sink");
|
|
29959
|
+
return;
|
|
29960
|
+
}
|
|
29961
|
+
if (projection.view.kind !== "rows" || index >= projection.view.rows.length) {
|
|
29962
|
+
this.drop("commit_index");
|
|
29963
|
+
return;
|
|
29964
|
+
}
|
|
29965
|
+
this.commitsEmitted += 1;
|
|
29966
|
+
sink({ type: "commit", seq: projection.seq, index });
|
|
29967
|
+
this.log(
|
|
29968
|
+
`[browser] address overlay commit emitted: seq=${projection.seq} index=${index}`
|
|
29969
|
+
);
|
|
29970
|
+
}
|
|
29971
|
+
/**
|
|
29972
|
+
* Probe-only mouse path: `sendInputEvent` into the child's own input pipeline.
|
|
29973
|
+
* This exercises the child and the before-mouse-event observer; it does NOT
|
|
29974
|
+
* exercise macOS NSView hit-testing between sibling views (matrix V3).
|
|
29975
|
+
*/
|
|
29976
|
+
probeClick(argument) {
|
|
29977
|
+
const view = this.view;
|
|
29978
|
+
const measurement = this.lastMeasurement;
|
|
29979
|
+
if (!view || !this.viewUsable(view) || !measurement) {
|
|
29980
|
+
throw new Error("address_overlay_click: no applied list to click");
|
|
29981
|
+
}
|
|
29982
|
+
const arg = argument ?? {};
|
|
29983
|
+
const row = arg.row;
|
|
29984
|
+
const upRow = arg.upRow === void 0 ? row : arg.upRow;
|
|
29985
|
+
const rows = measurement.rowRects;
|
|
29986
|
+
if (typeof row !== "number" || !Number.isInteger(row) || !rows[row]) {
|
|
29987
|
+
throw new Error(
|
|
29988
|
+
"address_overlay_click: row must be a rendered row index"
|
|
29989
|
+
);
|
|
29990
|
+
}
|
|
29991
|
+
if (typeof upRow !== "number" || !Number.isInteger(upRow)) {
|
|
29992
|
+
throw new Error("address_overlay_click: upRow must be an integer");
|
|
29993
|
+
}
|
|
29994
|
+
const point = (index) => {
|
|
29995
|
+
const rect2 = rows[index];
|
|
29996
|
+
const fallbackY = ADDRESS_LIST_HEADER_HEIGHT + 2 + (index + 0.5) * ADDRESS_LIST_ROW_HEIGHT;
|
|
29997
|
+
return rect2 ? {
|
|
29998
|
+
x: 40,
|
|
29999
|
+
y: ADDRESS_LIST_HEADER_HEIGHT + 2 + rect2.top + rect2.height / 2
|
|
30000
|
+
} : { x: 40, y: fallbackY };
|
|
30001
|
+
};
|
|
30002
|
+
const receive = (type2, index) => {
|
|
30003
|
+
const { x, y } = point(index);
|
|
30004
|
+
view.webContents.sendInputEvent({
|
|
30005
|
+
type: type2,
|
|
30006
|
+
x: Math.round(x),
|
|
30007
|
+
y: Math.round(y),
|
|
30008
|
+
button: "left",
|
|
30009
|
+
clickCount: 1
|
|
30010
|
+
});
|
|
30011
|
+
};
|
|
30012
|
+
receive("mouseMove", row);
|
|
30013
|
+
receive("mouseDown", row);
|
|
30014
|
+
receive("mouseUp", upRow);
|
|
30015
|
+
}
|
|
30016
|
+
/**
|
|
30017
|
+
* Probe-only key into the child's own input pipeline. It is how the gated
|
|
30018
|
+
* harness proves `before-input-event` swallows keys; CDP-injected keys go
|
|
30019
|
+
* through a different path and were measured NOT to fire it.
|
|
30020
|
+
*/
|
|
30021
|
+
probeKey(argument) {
|
|
30022
|
+
const view = this.view;
|
|
30023
|
+
if (!view || !this.viewUsable(view)) {
|
|
30024
|
+
throw new Error("address_overlay_key: no child");
|
|
30025
|
+
}
|
|
30026
|
+
const arg = argument ?? {};
|
|
30027
|
+
const keyCode = typeof arg.keyCode === "string" && arg.keyCode.length > 0 ? arg.keyCode.slice(0, 32) : "A";
|
|
30028
|
+
view.webContents.sendInputEvent({ type: "keyDown", keyCode });
|
|
30029
|
+
view.webContents.sendInputEvent({ type: "keyUp", keyCode });
|
|
30030
|
+
}
|
|
30031
|
+
async probeMeasure() {
|
|
30032
|
+
const view = this.view;
|
|
30033
|
+
if (!view || !this.viewUsable(view)) {
|
|
30034
|
+
throw new Error("address_overlay_measure: no child");
|
|
30035
|
+
}
|
|
30036
|
+
const measured = await view.webContents.executeJavaScript(
|
|
30037
|
+
"window.__loAddressOverlay && window.__loAddressOverlay.measure()",
|
|
30038
|
+
false
|
|
30039
|
+
);
|
|
30040
|
+
this.lastMeasurement = measured;
|
|
30041
|
+
}
|
|
30042
|
+
/**
|
|
30043
|
+
* Idempotent teardown for the host's stop/rollback/window-close paths.
|
|
30044
|
+
* Closing the webContents before detaching is required: removing a
|
|
30045
|
+
* `WebContentsView` does not release its renderer.
|
|
30046
|
+
*/
|
|
30047
|
+
dispose(reason) {
|
|
30048
|
+
if (this.disposed) return;
|
|
30049
|
+
this.disposed = true;
|
|
30050
|
+
this.generation += 1;
|
|
30051
|
+
this.settleReady(false);
|
|
30052
|
+
this.detachOwnerListeners();
|
|
30053
|
+
this.destroyChild();
|
|
30054
|
+
this.state = "disposed";
|
|
30055
|
+
this.hiddenReason = reason;
|
|
30056
|
+
this.log(`[browser] address overlay disposed: ${reason}`);
|
|
30057
|
+
}
|
|
30058
|
+
get isDisposed() {
|
|
30059
|
+
return this.disposed;
|
|
30060
|
+
}
|
|
30061
|
+
// ---- show path ----------------------------------------------------------
|
|
30062
|
+
async showOnce() {
|
|
30063
|
+
const refuse = (reason) => {
|
|
30064
|
+
this.lastShow = { shown: false, reason, bounds: null, at: this.now() };
|
|
30065
|
+
return { shown: false, reason, bounds: null };
|
|
30066
|
+
};
|
|
30067
|
+
try {
|
|
30068
|
+
if (this.disposed) return refuse("disposed");
|
|
30069
|
+
if (this.failedReason) return refuse(this.failedReason);
|
|
30070
|
+
if (this.ownerUnavailable()) {
|
|
30071
|
+
this.releaseChild("owner_unavailable");
|
|
30072
|
+
return refuse("owner_unavailable");
|
|
30073
|
+
}
|
|
30074
|
+
const snapshot2 = this.layout;
|
|
30075
|
+
const bounds = overlayBoundsFor(snapshot2.contentRect, this.ownerSize());
|
|
30076
|
+
if (snapshot2.activeTabId === null || !snapshot2.presented || bounds === null) {
|
|
30077
|
+
return refuse("no_layout");
|
|
30078
|
+
}
|
|
30079
|
+
this.generation += 1;
|
|
30080
|
+
const generation = this.generation;
|
|
30081
|
+
this.boundTabId = snapshot2.activeTabId;
|
|
30082
|
+
if (!this.view) {
|
|
30083
|
+
const created = this.createChild(bounds);
|
|
30084
|
+
if (!created) return refuse(this.failedReason ?? "child_create_failed");
|
|
30085
|
+
}
|
|
30086
|
+
if (!(this.domReady && this.finished)) {
|
|
30087
|
+
this.state = "loading";
|
|
30088
|
+
const ready = await this.waitForReady();
|
|
30089
|
+
if (!ready) return refuse(this.failedReason ?? "invalidated");
|
|
30090
|
+
}
|
|
30091
|
+
if (this.disposed || generation !== this.generation) {
|
|
30092
|
+
return refuse("invalidated");
|
|
30093
|
+
}
|
|
30094
|
+
if (this.ownerUnavailable()) {
|
|
30095
|
+
this.releaseChild("owner_unavailable");
|
|
30096
|
+
return refuse("owner_unavailable");
|
|
30097
|
+
}
|
|
30098
|
+
const view = this.view;
|
|
30099
|
+
if (!view) return refuse("child_missing");
|
|
30100
|
+
const finalBounds = overlayBoundsFor(
|
|
30101
|
+
this.layout.contentRect,
|
|
30102
|
+
this.ownerSize()
|
|
30103
|
+
);
|
|
30104
|
+
if (finalBounds === null || this.layout.activeTabId === null || !this.layout.presented || this.layout.activeTabId !== this.boundTabId) {
|
|
30105
|
+
return refuse("invalidated");
|
|
30106
|
+
}
|
|
30107
|
+
view.setBounds(finalBounds);
|
|
30108
|
+
this.raiseChild(view);
|
|
30109
|
+
view.setVisible(true);
|
|
30110
|
+
this.state = "shown";
|
|
30111
|
+
this.hiddenReason = null;
|
|
30112
|
+
this.shownCount += 1;
|
|
30113
|
+
this.lastShow = {
|
|
30114
|
+
shown: true,
|
|
30115
|
+
reason: null,
|
|
30116
|
+
bounds: finalBounds,
|
|
30117
|
+
at: this.now()
|
|
30118
|
+
};
|
|
30119
|
+
this.log(
|
|
30120
|
+
`[browser] address overlay shown tab=${this.boundTabId} bounds=${finalBounds.x},${finalBounds.y},${finalBounds.width},${finalBounds.height} generation=${generation}`
|
|
30121
|
+
);
|
|
30122
|
+
return { shown: true, reason: null, bounds: finalBounds };
|
|
30123
|
+
} catch (error) {
|
|
30124
|
+
this.failChild(`show_error:${String(error)}`);
|
|
30125
|
+
return refuse("child_error");
|
|
30126
|
+
}
|
|
30127
|
+
}
|
|
30128
|
+
createChild(bounds) {
|
|
30129
|
+
let view;
|
|
30130
|
+
try {
|
|
30131
|
+
view = this.createView();
|
|
30132
|
+
} catch (error) {
|
|
30133
|
+
this.failChild(`child_create_failed:${String(error)}`);
|
|
30134
|
+
return false;
|
|
30135
|
+
}
|
|
30136
|
+
this.view = view;
|
|
30137
|
+
this.childWebContentsId = view.webContents.id;
|
|
30138
|
+
this.childCreatedCount += 1;
|
|
30139
|
+
this.domReady = false;
|
|
30140
|
+
this.finished = false;
|
|
30141
|
+
this.loadMs = null;
|
|
30142
|
+
this.loadStartedAt = null;
|
|
30143
|
+
try {
|
|
30144
|
+
this.window.contentView.addChildView(view);
|
|
30145
|
+
view.setBounds(bounds);
|
|
30146
|
+
view.setVisible(false);
|
|
30147
|
+
} catch (error) {
|
|
30148
|
+
this.failChild(`child_attach_failed:${String(error)}`);
|
|
30149
|
+
return false;
|
|
30150
|
+
}
|
|
30151
|
+
this.wireChild(view);
|
|
30152
|
+
this.loadStartedAt = this.now();
|
|
30153
|
+
try {
|
|
30154
|
+
const loading = view.webContents.loadURL(this.documentUrl);
|
|
30155
|
+
void Promise.resolve(loading).catch((error) => {
|
|
30156
|
+
if (this.disposed || this.view !== view) return;
|
|
30157
|
+
this.failChild(`load_failed:${String(error)}`);
|
|
30158
|
+
});
|
|
30159
|
+
} catch (error) {
|
|
30160
|
+
this.failChild(`load_failed:${String(error)}`);
|
|
30161
|
+
return false;
|
|
30162
|
+
}
|
|
30163
|
+
this.log(
|
|
30164
|
+
`[browser] address overlay child created wc=${view.webContents.id}`
|
|
30165
|
+
);
|
|
30166
|
+
return true;
|
|
30167
|
+
}
|
|
30168
|
+
waitForReady() {
|
|
30169
|
+
if (this.domReady && this.finished) return Promise.resolve(true);
|
|
30170
|
+
if (this.readyWaiter) {
|
|
30171
|
+
return Promise.resolve(false);
|
|
30172
|
+
}
|
|
30173
|
+
return new Promise((resolve) => {
|
|
30174
|
+
this.readyWaiter = resolve;
|
|
30175
|
+
this.readinessTimer = setTimeout(() => {
|
|
30176
|
+
this.readinessTimer = null;
|
|
30177
|
+
if (this.readyWaiter) this.failChild("load_timeout");
|
|
30178
|
+
}, this.readinessTimeoutMs);
|
|
30179
|
+
});
|
|
30180
|
+
}
|
|
30181
|
+
settleReady(ok) {
|
|
30182
|
+
if (this.readinessTimer !== null) {
|
|
30183
|
+
clearTimeout(this.readinessTimer);
|
|
30184
|
+
this.readinessTimer = null;
|
|
30185
|
+
}
|
|
30186
|
+
const waiter = this.readyWaiter;
|
|
30187
|
+
this.readyWaiter = null;
|
|
30188
|
+
if (waiter) waiter(ok);
|
|
30189
|
+
}
|
|
30190
|
+
maybeReady() {
|
|
30191
|
+
if (!(this.domReady && this.finished)) return;
|
|
30192
|
+
if (this.loadStartedAt !== null && this.loadMs === null) {
|
|
30193
|
+
this.loadMs = this.now() - this.loadStartedAt;
|
|
30194
|
+
}
|
|
30195
|
+
if (this.state === "loading" || this.state === "absent") {
|
|
30196
|
+
this.state = this.readyWaiter ? this.state : "ready";
|
|
30197
|
+
}
|
|
30198
|
+
this.settleReady(true);
|
|
30199
|
+
}
|
|
30200
|
+
// ---- child wiring -------------------------------------------------------
|
|
30201
|
+
wireChild(view) {
|
|
30202
|
+
const contents = view.webContents;
|
|
30203
|
+
const on = (target2, event, listener) => {
|
|
30204
|
+
const emitter = target2;
|
|
30205
|
+
emitter.on(event, listener);
|
|
30206
|
+
this.childListeners.push(() => {
|
|
30207
|
+
try {
|
|
30208
|
+
emitter.removeListener(event, listener);
|
|
30209
|
+
} catch {
|
|
30210
|
+
}
|
|
30211
|
+
});
|
|
30212
|
+
};
|
|
30213
|
+
on(contents, "dom-ready", () => {
|
|
30214
|
+
if (this.view !== view) return;
|
|
30215
|
+
this.domReady = true;
|
|
30216
|
+
this.maybeReady();
|
|
30217
|
+
});
|
|
30218
|
+
on(contents, "did-finish-load", () => {
|
|
30219
|
+
if (this.view !== view) return;
|
|
30220
|
+
this.finished = true;
|
|
30221
|
+
this.maybeReady();
|
|
30222
|
+
});
|
|
30223
|
+
on(contents, "did-fail-load", (...args) => {
|
|
30224
|
+
if (this.view !== view) return;
|
|
30225
|
+
const code = typeof args[1] === "number" ? args[1] : 0;
|
|
30226
|
+
const isMainFrame = args[4] !== false;
|
|
30227
|
+
if (!isMainFrame || code === -3) return;
|
|
30228
|
+
this.failChild(`load_failed:${code}`);
|
|
30229
|
+
});
|
|
30230
|
+
on(contents, "render-process-gone", () => {
|
|
30231
|
+
if (this.view !== view) return;
|
|
30232
|
+
this.failChild("child_crash");
|
|
30233
|
+
});
|
|
30234
|
+
on(contents, "destroyed", () => {
|
|
30235
|
+
if (this.view !== view) return;
|
|
30236
|
+
if (this.disposingChild) return;
|
|
30237
|
+
this.failChild("child_destroyed");
|
|
30238
|
+
});
|
|
30239
|
+
on(contents, "will-navigate", (...args) => {
|
|
30240
|
+
const url = typeof args[1] === "string" ? args[1] : "";
|
|
30241
|
+
const event = args[0];
|
|
30242
|
+
if (url === this.documentUrl) return;
|
|
30243
|
+
event?.preventDefault?.();
|
|
30244
|
+
this.log(`[browser] address overlay refused navigation to ${url}`);
|
|
30245
|
+
});
|
|
30246
|
+
on(contents, "will-redirect", (...args) => {
|
|
30247
|
+
const url = typeof args[1] === "string" ? args[1] : "";
|
|
30248
|
+
const event = args[0];
|
|
30249
|
+
if (url === this.documentUrl) return;
|
|
30250
|
+
event?.preventDefault?.();
|
|
30251
|
+
this.log(`[browser] address overlay refused redirect to ${url}`);
|
|
30252
|
+
});
|
|
30253
|
+
if (!this.javascript) return;
|
|
30254
|
+
on(contents, "before-input-event", (...args) => {
|
|
30255
|
+
const event = args[0];
|
|
30256
|
+
event?.preventDefault?.();
|
|
30257
|
+
this.keysBlocked += 1;
|
|
30258
|
+
});
|
|
30259
|
+
on(contents, "context-menu", (...args) => {
|
|
30260
|
+
const event = args[0];
|
|
30261
|
+
event?.preventDefault?.();
|
|
30262
|
+
this.contextMenuBlocked += 1;
|
|
30263
|
+
});
|
|
30264
|
+
on(contents, "focus", () => {
|
|
30265
|
+
this.childFocusEvents += 1;
|
|
30266
|
+
});
|
|
30267
|
+
on(contents, "blur", () => {
|
|
30268
|
+
this.childBlurEvents += 1;
|
|
30269
|
+
});
|
|
30270
|
+
on(contents, "before-mouse-event", (...args) => {
|
|
30271
|
+
const event = args[0];
|
|
30272
|
+
const mouse = args[1];
|
|
30273
|
+
if (!mouse || typeof mouse.x !== "number" || typeof mouse.y !== "number") {
|
|
30274
|
+
return;
|
|
30275
|
+
}
|
|
30276
|
+
if (mouse.type === "mouseMove") {
|
|
30277
|
+
this.mouseMoves += 1;
|
|
30278
|
+
return;
|
|
30279
|
+
}
|
|
30280
|
+
event?.preventDefault?.();
|
|
30281
|
+
if (mouse.type === "mouseDown") {
|
|
30282
|
+
this.mouseDowns += 1;
|
|
30283
|
+
this.mouseDownRow = this.rowAt(mouse.y);
|
|
30284
|
+
return;
|
|
30285
|
+
}
|
|
30286
|
+
if (mouse.type !== "mouseUp") return;
|
|
30287
|
+
this.mouseUps += 1;
|
|
30288
|
+
const row = this.rowAt(mouse.y);
|
|
30289
|
+
const downRow = this.mouseDownRow;
|
|
30290
|
+
this.mouseDownRow = null;
|
|
30291
|
+
if (downRow === null || row === null) {
|
|
30292
|
+
this.drop("mouse_no_row");
|
|
30293
|
+
return;
|
|
30294
|
+
}
|
|
30295
|
+
if (downRow !== row) {
|
|
30296
|
+
this.drop("mouse_mismatch");
|
|
30297
|
+
return;
|
|
30298
|
+
}
|
|
30299
|
+
this.emitCommit(row);
|
|
30300
|
+
});
|
|
30301
|
+
}
|
|
30302
|
+
/** Terminal for this host lifetime: a broken document must not be retried. */
|
|
30303
|
+
failChild(reason) {
|
|
30304
|
+
if (this.failedReason) return;
|
|
30305
|
+
this.failedReason = reason;
|
|
30306
|
+
this.state = "failed";
|
|
30307
|
+
this.settleReady(false);
|
|
30308
|
+
this.destroyChild();
|
|
30309
|
+
this.log(`[browser] address overlay failed: ${reason}`);
|
|
30310
|
+
}
|
|
30311
|
+
/** Recoverable: the child goes, a later explicit show may lazily recreate one. */
|
|
30312
|
+
releaseChild(reason) {
|
|
30313
|
+
this.generation += 1;
|
|
30314
|
+
this.settleReady(false);
|
|
30315
|
+
this.destroyChild();
|
|
30316
|
+
if (!this.failedReason) this.state = "absent";
|
|
30317
|
+
this.boundTabId = null;
|
|
30318
|
+
this.domReady = false;
|
|
30319
|
+
this.finished = false;
|
|
30320
|
+
this.loadStartedAt = null;
|
|
30321
|
+
this.log(`[browser] address overlay child released: ${reason}`);
|
|
30322
|
+
}
|
|
30323
|
+
destroyChild() {
|
|
30324
|
+
const view = this.view;
|
|
30325
|
+
if (!view) return;
|
|
30326
|
+
this.view = null;
|
|
30327
|
+
this.childWebContentsId = null;
|
|
30328
|
+
this.disposingChild = true;
|
|
30329
|
+
for (const remove of this.childListeners.splice(0)) remove();
|
|
30330
|
+
try {
|
|
30331
|
+
if (!view.webContents.isDestroyed()) view.webContents.close();
|
|
30332
|
+
} catch {
|
|
30333
|
+
}
|
|
30334
|
+
try {
|
|
30335
|
+
this.window.contentView.removeChildView(view);
|
|
30336
|
+
} catch {
|
|
30337
|
+
}
|
|
30338
|
+
this.disposingChild = false;
|
|
30339
|
+
}
|
|
30340
|
+
raiseChild(view) {
|
|
30341
|
+
this.window.contentView.addChildView(view);
|
|
30342
|
+
}
|
|
30343
|
+
viewUsable(view) {
|
|
30344
|
+
try {
|
|
30345
|
+
return !view.webContents.isDestroyed();
|
|
30346
|
+
} catch {
|
|
30347
|
+
return false;
|
|
30348
|
+
}
|
|
30349
|
+
}
|
|
30350
|
+
// ---- owner wiring -------------------------------------------------------
|
|
30351
|
+
attachOwnerListeners() {
|
|
30352
|
+
const on = (target2, event, listener) => {
|
|
30353
|
+
const emitter = target2;
|
|
30354
|
+
emitter.on(event, listener);
|
|
30355
|
+
this.ownerListeners.push(() => {
|
|
30356
|
+
try {
|
|
30357
|
+
emitter.removeListener(event, listener);
|
|
30358
|
+
} catch {
|
|
30359
|
+
}
|
|
30360
|
+
});
|
|
30361
|
+
};
|
|
30362
|
+
on(this.window, "blur", () => {
|
|
30363
|
+
this.hide("window_blur");
|
|
30364
|
+
});
|
|
30365
|
+
on(this.window, "minimize", () => {
|
|
30366
|
+
this.hide("window_minimize");
|
|
30367
|
+
});
|
|
30368
|
+
on(this.window, "closed", () => {
|
|
30369
|
+
this.dispose("window_closed");
|
|
30370
|
+
});
|
|
30371
|
+
const contents = this.window.webContents;
|
|
30372
|
+
on(contents, "render-process-gone", () => {
|
|
30373
|
+
this.releaseChild("owner_crash");
|
|
30374
|
+
});
|
|
30375
|
+
on(contents, "destroyed", () => {
|
|
30376
|
+
this.dispose("owner_destroyed");
|
|
30377
|
+
});
|
|
30378
|
+
on(contents, "did-start-navigation", (...args) => {
|
|
30379
|
+
const details = args[0];
|
|
30380
|
+
if (details?.isMainFrame && details.isSameDocument !== true) {
|
|
30381
|
+
this.releaseChild("owner_navigation");
|
|
30382
|
+
}
|
|
30383
|
+
});
|
|
30384
|
+
}
|
|
30385
|
+
detachOwnerListeners() {
|
|
30386
|
+
for (const remove of this.ownerListeners.splice(0)) remove();
|
|
30387
|
+
}
|
|
30388
|
+
ownerSize() {
|
|
30389
|
+
const bounds = this.window.getContentBounds();
|
|
30390
|
+
return { width: bounds.width, height: bounds.height };
|
|
30391
|
+
}
|
|
30392
|
+
ownerUnavailable() {
|
|
30393
|
+
try {
|
|
30394
|
+
return this.window.isDestroyed() || this.window.webContents.isDestroyed();
|
|
30395
|
+
} catch {
|
|
30396
|
+
return true;
|
|
30397
|
+
}
|
|
30398
|
+
}
|
|
30399
|
+
}
|
|
28717
30400
|
const PRIVATE_FILE_MODE = 384;
|
|
28718
30401
|
const PRIVATE_DIR_MODE = 448;
|
|
28719
30402
|
function writeJsonAtomic(path2, value) {
|
|
@@ -34003,7 +35686,7 @@ const enUiCommandPalette = {
|
|
|
34003
35686
|
"ui.command_palette.palette_search.unread": "Unread",
|
|
34004
35687
|
"ui.command_palette.palette_search.untitled_chat": "Untitled chat",
|
|
34005
35688
|
"ui.command_palette.use_palette_sources.agent_chat": "Agent chat",
|
|
34006
|
-
"ui.command_palette.use_palette_sources.agent_hub": "Agent
|
|
35689
|
+
"ui.command_palette.use_palette_sources.agent_hub": "Agent Hub",
|
|
34007
35690
|
"ui.command_palette.use_palette_sources.agent_settings": "Agent settings",
|
|
34008
35691
|
"ui.command_palette.use_palette_sources.analytics": "Analytics",
|
|
34009
35692
|
"ui.command_palette.use_palette_sources.app_backend_and_environment": "App, backend and environment",
|
|
@@ -36509,7 +38192,7 @@ const enUiSettingsUpdates = {
|
|
|
36509
38192
|
const enUiShell = {
|
|
36510
38193
|
"ui.shell.collapsible_app_logo.local_operator": "Local Operator",
|
|
36511
38194
|
"ui.shell.panel_rail.panels": "Panels",
|
|
36512
|
-
"ui.shell.sidebar_navigation.agent_hub": "Agent
|
|
38195
|
+
"ui.shell.sidebar_navigation.agent_hub": "Agent Hub",
|
|
36513
38196
|
"ui.shell.sidebar_navigation.agents": "Agents",
|
|
36514
38197
|
"ui.shell.sidebar_navigation.aida_missed": "{name}, {count, plural, one {{count, number, ::group-off} missed message} other {{count, number, ::group-off} missed messages}}",
|
|
36515
38198
|
"ui.shell.sidebar_navigation.aida_working": "{name}, working",
|
|
@@ -36517,17 +38200,31 @@ const enUiShell = {
|
|
|
36517
38200
|
"ui.shell.sidebar_navigation.browser_waiting": "Browser, {count, plural, one {{countText} waiting} other {{countText} waiting}}",
|
|
36518
38201
|
"ui.shell.sidebar_navigation.close_sidebar": "Close sidebar",
|
|
36519
38202
|
"ui.shell.sidebar_navigation.collapse_sidebar": "Collapse sidebar",
|
|
38203
|
+
"ui.shell.sidebar_navigation.drag_armed": "Moving {name}. Press Escape to cancel.",
|
|
36520
38204
|
"ui.shell.sidebar_navigation.expand_sidebar": "Expand sidebar",
|
|
36521
38205
|
"ui.shell.sidebar_navigation.mesh": "Mesh",
|
|
36522
38206
|
"ui.shell.sidebar_navigation.mesh_waiting": "Mesh, {count, plural, one {{countText} waiting} other {{countText} waiting}}",
|
|
38207
|
+
"ui.shell.sidebar_navigation.more_waiting": "More, {count, plural, one {{countText} waiting} other {{countText} waiting}}",
|
|
38208
|
+
"ui.shell.sidebar_navigation.more_working": "More, working",
|
|
38209
|
+
"ui.shell.sidebar_navigation.move_down": "Move down",
|
|
38210
|
+
"ui.shell.sidebar_navigation.move_note": "Moved {name} to position {position} of {total}.",
|
|
38211
|
+
"ui.shell.sidebar_navigation.move_to_more": "Move to More",
|
|
38212
|
+
"ui.shell.sidebar_navigation.move_to_more_note": "{name} moved to More.",
|
|
38213
|
+
"ui.shell.sidebar_navigation.move_up": "Move up",
|
|
36523
38214
|
"ui.shell.sidebar_navigation.new_chat": "New chat",
|
|
36524
38215
|
"ui.shell.sidebar_navigation.new_chat_shortcut": "New chat ({shortcut})",
|
|
36525
38216
|
"ui.shell.sidebar_navigation.projects": "Projects",
|
|
38217
|
+
"ui.shell.sidebar_navigation.reset_layout": "Reset sidebar layout",
|
|
38218
|
+
"ui.shell.sidebar_navigation.reset_note": "Sidebar layout reset.",
|
|
38219
|
+
"ui.shell.sidebar_navigation.row_actions": "Actions for {name}",
|
|
38220
|
+
"ui.shell.sidebar_navigation.save_failed": "Could not save the sidebar layout.",
|
|
36526
38221
|
"ui.shell.sidebar_navigation.schedules": "Schedules",
|
|
36527
38222
|
"ui.shell.sidebar_navigation.search": "Search",
|
|
36528
38223
|
"ui.shell.sidebar_navigation.search_accessible": "Search ({everything}) — chats ({chats})",
|
|
36529
38224
|
"ui.shell.sidebar_navigation.search_tooltip": "Search everything ({everything}) · your chats ({chats})",
|
|
36530
38225
|
"ui.shell.sidebar_navigation.settings": "Settings",
|
|
38226
|
+
"ui.shell.sidebar_navigation.show_in_sidebar": "Show in sidebar",
|
|
38227
|
+
"ui.shell.sidebar_navigation.show_in_sidebar_note": "{name} shown in the sidebar.",
|
|
36531
38228
|
"ui.shell.user_profile_sidebar.account_settings": "Account settings",
|
|
36532
38229
|
"ui.shell.user_profile_sidebar.account_unavailable": "Account unavailable",
|
|
36533
38230
|
"ui.shell.user_profile_sidebar.checking_account": "Checking account…",
|
|
@@ -40081,17 +41778,31 @@ const esUiShell = {
|
|
|
40081
41778
|
"ui.shell.sidebar_navigation.browser_waiting": "Navegador, {count, plural, one {{countText} en espera} many {{countText} en espera} other {{countText} en espera}}",
|
|
40082
41779
|
"ui.shell.sidebar_navigation.close_sidebar": "Cerrar barra lateral",
|
|
40083
41780
|
"ui.shell.sidebar_navigation.collapse_sidebar": "Contraer barra lateral",
|
|
41781
|
+
"ui.shell.sidebar_navigation.drag_armed": "Moviendo {name}. Pulsa Esc para cancelar.",
|
|
40084
41782
|
"ui.shell.sidebar_navigation.expand_sidebar": "Expandir barra lateral",
|
|
40085
41783
|
"ui.shell.sidebar_navigation.mesh": "Mesh",
|
|
40086
41784
|
"ui.shell.sidebar_navigation.mesh_waiting": "Mesh, {count, plural, one {{countText} en espera} many {{countText} en espera} other {{countText} en espera}}",
|
|
41785
|
+
"ui.shell.sidebar_navigation.more_waiting": "Más, {count, plural, one {{countText} en espera} many {{countText} en espera} other {{countText} en espera}}",
|
|
41786
|
+
"ui.shell.sidebar_navigation.more_working": "Más, trabajando",
|
|
41787
|
+
"ui.shell.sidebar_navigation.move_down": "Bajar",
|
|
41788
|
+
"ui.shell.sidebar_navigation.move_note": "Se movió {name} a la posición {position} de {total}.",
|
|
41789
|
+
"ui.shell.sidebar_navigation.move_to_more": "Mover a Más",
|
|
41790
|
+
"ui.shell.sidebar_navigation.move_to_more_note": "{name} se movió a Más.",
|
|
41791
|
+
"ui.shell.sidebar_navigation.move_up": "Subir",
|
|
40087
41792
|
"ui.shell.sidebar_navigation.new_chat": "Nuevo chat",
|
|
40088
41793
|
"ui.shell.sidebar_navigation.new_chat_shortcut": "Nuevo chat ({shortcut})",
|
|
40089
41794
|
"ui.shell.sidebar_navigation.projects": "Proyectos",
|
|
41795
|
+
"ui.shell.sidebar_navigation.reset_layout": "Restablecer la disposición",
|
|
41796
|
+
"ui.shell.sidebar_navigation.reset_note": "Disposición de la barra lateral restablecida.",
|
|
41797
|
+
"ui.shell.sidebar_navigation.row_actions": "Acciones para {name}",
|
|
41798
|
+
"ui.shell.sidebar_navigation.save_failed": "No se pudo guardar la disposición de la barra lateral.",
|
|
40090
41799
|
"ui.shell.sidebar_navigation.schedules": "Programaciones",
|
|
40091
41800
|
"ui.shell.sidebar_navigation.search": "Buscar",
|
|
40092
41801
|
"ui.shell.sidebar_navigation.search_accessible": "Buscar en todo ({everything}) — chats ({chats})",
|
|
40093
41802
|
"ui.shell.sidebar_navigation.search_tooltip": "Buscar en todo ({everything}) · tus chats ({chats})",
|
|
40094
41803
|
"ui.shell.sidebar_navigation.settings": "Ajustes",
|
|
41804
|
+
"ui.shell.sidebar_navigation.show_in_sidebar": "Mostrar en la barra lateral",
|
|
41805
|
+
"ui.shell.sidebar_navigation.show_in_sidebar_note": "{name} ahora aparece en la barra lateral.",
|
|
40095
41806
|
"ui.shell.user_profile_sidebar.account_settings": "Ajustes de cuenta",
|
|
40096
41807
|
"ui.shell.user_profile_sidebar.account_unavailable": "Cuenta no disponible",
|
|
40097
41808
|
"ui.shell.user_profile_sidebar.checking_account": "Comprobando la cuenta…",
|
|
@@ -43645,17 +45356,31 @@ const frUiShell = {
|
|
|
43645
45356
|
"ui.shell.sidebar_navigation.browser_waiting": "Navigateur, {count, plural, one {{countText} approbation en attente} many {{countText} d’approbations en attente} other {{countText} approbations en attente}}",
|
|
43646
45357
|
"ui.shell.sidebar_navigation.close_sidebar": "Fermer la barre latérale",
|
|
43647
45358
|
"ui.shell.sidebar_navigation.collapse_sidebar": "Réduire la barre latérale",
|
|
45359
|
+
"ui.shell.sidebar_navigation.drag_armed": "Déplacement de {name}. Appuyez sur Esc pour annuler.",
|
|
43648
45360
|
"ui.shell.sidebar_navigation.expand_sidebar": "Développer la barre latérale",
|
|
43649
45361
|
"ui.shell.sidebar_navigation.mesh": "Maillage",
|
|
43650
45362
|
"ui.shell.sidebar_navigation.mesh_waiting": "Mesh, {count, plural, one {{countText} demande en attente} many {{countText} de demandes en attente} other {{countText} demandes en attente}}",
|
|
45363
|
+
"ui.shell.sidebar_navigation.more_waiting": "Plus, {count, plural, one {{countText} en attente} many {{countText} en attente} other {{countText} en attente}}",
|
|
45364
|
+
"ui.shell.sidebar_navigation.more_working": "Plus, en cours",
|
|
45365
|
+
"ui.shell.sidebar_navigation.move_down": "Déplacer vers le bas",
|
|
45366
|
+
"ui.shell.sidebar_navigation.move_note": "{name} est maintenant en position {position} sur {total}.",
|
|
45367
|
+
"ui.shell.sidebar_navigation.move_to_more": "Déplacer vers Plus",
|
|
45368
|
+
"ui.shell.sidebar_navigation.move_to_more_note": "{name} fait maintenant partie de Plus.",
|
|
45369
|
+
"ui.shell.sidebar_navigation.move_up": "Déplacer vers le haut",
|
|
43651
45370
|
"ui.shell.sidebar_navigation.new_chat": "Nouvelle conversation",
|
|
43652
45371
|
"ui.shell.sidebar_navigation.new_chat_shortcut": "Nouvelle conversation ({shortcut})",
|
|
43653
45372
|
"ui.shell.sidebar_navigation.projects": "Projets",
|
|
45373
|
+
"ui.shell.sidebar_navigation.reset_layout": "Réinitialiser la disposition",
|
|
45374
|
+
"ui.shell.sidebar_navigation.reset_note": "Disposition de la barre latérale réinitialisée.",
|
|
45375
|
+
"ui.shell.sidebar_navigation.row_actions": "Actions pour {name}",
|
|
45376
|
+
"ui.shell.sidebar_navigation.save_failed": "Impossible d’enregistrer la disposition de la barre latérale.",
|
|
43654
45377
|
"ui.shell.sidebar_navigation.schedules": "Planifications",
|
|
43655
45378
|
"ui.shell.sidebar_navigation.search": "Rechercher",
|
|
43656
45379
|
"ui.shell.sidebar_navigation.search_accessible": "Rechercher ({everything}) — conversations ({chats})",
|
|
43657
45380
|
"ui.shell.sidebar_navigation.search_tooltip": "Tout rechercher ({everything}) · vos conversations ({chats})",
|
|
43658
45381
|
"ui.shell.sidebar_navigation.settings": "Paramètres",
|
|
45382
|
+
"ui.shell.sidebar_navigation.show_in_sidebar": "Afficher dans la barre latérale",
|
|
45383
|
+
"ui.shell.sidebar_navigation.show_in_sidebar_note": "{name} apparaît maintenant dans la barre latérale.",
|
|
43659
45384
|
"ui.shell.user_profile_sidebar.account_settings": "Paramètres du compte",
|
|
43660
45385
|
"ui.shell.user_profile_sidebar.account_unavailable": "Compte indisponible",
|
|
43661
45386
|
"ui.shell.user_profile_sidebar.checking_account": "Vérification du compte…",
|
|
@@ -47167,17 +48892,31 @@ const hiUiShell = {
|
|
|
47167
48892
|
"ui.shell.sidebar_navigation.browser_waiting": "ब्राउज़र, {count, plural, one {{countText} इंतज़ार में} other {{countText} इंतज़ार में}}",
|
|
47168
48893
|
"ui.shell.sidebar_navigation.close_sidebar": "साइडबार बंद करें",
|
|
47169
48894
|
"ui.shell.sidebar_navigation.collapse_sidebar": "साइडबार समेटें",
|
|
48895
|
+
"ui.shell.sidebar_navigation.drag_armed": "{name} को ले जाया जा रहा है। रद्द करने के लिए Esc दबाएँ।",
|
|
47170
48896
|
"ui.shell.sidebar_navigation.expand_sidebar": "साइडबार फैलाएँ",
|
|
47171
48897
|
"ui.shell.sidebar_navigation.mesh": "मेश",
|
|
47172
48898
|
"ui.shell.sidebar_navigation.mesh_waiting": "मेश, {count, plural, one {{countText} इंतज़ार में} other {{countText} इंतज़ार में}}",
|
|
48899
|
+
"ui.shell.sidebar_navigation.more_waiting": "और, {count, plural, one {{countText} इंतज़ार में} other {{countText} इंतज़ार में}}",
|
|
48900
|
+
"ui.shell.sidebar_navigation.more_working": "और, काम जारी है",
|
|
48901
|
+
"ui.shell.sidebar_navigation.move_down": "नीचे ले जाएँ",
|
|
48902
|
+
"ui.shell.sidebar_navigation.move_note": "{name} को कुल {total} में से स्थान {position} पर ले जाया गया।",
|
|
48903
|
+
"ui.shell.sidebar_navigation.move_to_more": "और सेक्शन में ले जाएँ",
|
|
48904
|
+
"ui.shell.sidebar_navigation.move_to_more_note": "{name} को और सेक्शन में ले जाया गया।",
|
|
48905
|
+
"ui.shell.sidebar_navigation.move_up": "ऊपर ले जाएँ",
|
|
47173
48906
|
"ui.shell.sidebar_navigation.new_chat": "नई चैट",
|
|
47174
48907
|
"ui.shell.sidebar_navigation.new_chat_shortcut": "नई चैट ({shortcut})",
|
|
47175
48908
|
"ui.shell.sidebar_navigation.projects": "प्रोजेक्ट",
|
|
48909
|
+
"ui.shell.sidebar_navigation.reset_layout": "साइडबार लेआउट रीसेट करें",
|
|
48910
|
+
"ui.shell.sidebar_navigation.reset_note": "साइडबार लेआउट रीसेट हो गया।",
|
|
48911
|
+
"ui.shell.sidebar_navigation.row_actions": "{name} की कार्रवाइयाँ",
|
|
48912
|
+
"ui.shell.sidebar_navigation.save_failed": "साइडबार लेआउट सहेजा नहीं जा सका।",
|
|
47176
48913
|
"ui.shell.sidebar_navigation.schedules": "शेड्यूल",
|
|
47177
48914
|
"ui.shell.sidebar_navigation.search": "खोजें",
|
|
47178
48915
|
"ui.shell.sidebar_navigation.search_accessible": "खोजें ({everything}) — चैट ({chats})",
|
|
47179
48916
|
"ui.shell.sidebar_navigation.search_tooltip": "सब कुछ खोजें ({everything}) · आपकी चैट ({chats})",
|
|
47180
48917
|
"ui.shell.sidebar_navigation.settings": "सेटिंग्स",
|
|
48918
|
+
"ui.shell.sidebar_navigation.show_in_sidebar": "साइडबार में दिखाएँ",
|
|
48919
|
+
"ui.shell.sidebar_navigation.show_in_sidebar_note": "{name} अब साइडबार में है।",
|
|
47181
48920
|
"ui.shell.user_profile_sidebar.account_settings": "खाते की सेटिंग्स",
|
|
47182
48921
|
"ui.shell.user_profile_sidebar.account_unavailable": "खाता उपलब्ध नहीं",
|
|
47183
48922
|
"ui.shell.user_profile_sidebar.checking_account": "खाता जाँचा जा रहा है…",
|
|
@@ -50528,17 +52267,31 @@ const ruUiShell = {
|
|
|
50528
52267
|
"ui.shell.sidebar_navigation.browser_waiting": "Браузер, {count, plural, one {ожидает {countText}} few {ожидают {countText}} many {ожидают {countText}} other {ожидают {countText}}}",
|
|
50529
52268
|
"ui.shell.sidebar_navigation.close_sidebar": "Закрыть боковую панель",
|
|
50530
52269
|
"ui.shell.sidebar_navigation.collapse_sidebar": "Свернуть боковую панель",
|
|
52270
|
+
"ui.shell.sidebar_navigation.drag_armed": "Перемещение: {name}. Нажмите Esc, чтобы отменить.",
|
|
50531
52271
|
"ui.shell.sidebar_navigation.expand_sidebar": "Развернуть боковую панель",
|
|
50532
52272
|
"ui.shell.sidebar_navigation.mesh": "Меш-сеть",
|
|
50533
52273
|
"ui.shell.sidebar_navigation.mesh_waiting": "Меш-сеть, {count, plural, one {ожидает {countText}} few {ожидают {countText}} many {ожидают {countText}} other {ожидают {countText}}}",
|
|
52274
|
+
"ui.shell.sidebar_navigation.more_waiting": "Ещё, {count, plural, one {ожидает {countText}} few {ожидают {countText}} many {ожидают {countText}} other {ожидают {countText}}}",
|
|
52275
|
+
"ui.shell.sidebar_navigation.more_working": "Ещё, идёт работа",
|
|
52276
|
+
"ui.shell.sidebar_navigation.move_down": "Переместить ниже",
|
|
52277
|
+
"ui.shell.sidebar_navigation.move_note": "{name} теперь на позиции {position} из {total}.",
|
|
52278
|
+
"ui.shell.sidebar_navigation.move_to_more": "Переместить в «Ещё»",
|
|
52279
|
+
"ui.shell.sidebar_navigation.move_to_more_note": "{name} теперь в «Ещё».",
|
|
52280
|
+
"ui.shell.sidebar_navigation.move_up": "Переместить выше",
|
|
50534
52281
|
"ui.shell.sidebar_navigation.new_chat": "Новый чат",
|
|
50535
52282
|
"ui.shell.sidebar_navigation.new_chat_shortcut": "Новый чат ({shortcut})",
|
|
50536
52283
|
"ui.shell.sidebar_navigation.projects": "Проекты",
|
|
52284
|
+
"ui.shell.sidebar_navigation.reset_layout": "Сбросить расположение боковой панели",
|
|
52285
|
+
"ui.shell.sidebar_navigation.reset_note": "Расположение боковой панели сброшено.",
|
|
52286
|
+
"ui.shell.sidebar_navigation.row_actions": "Действия для {name}",
|
|
52287
|
+
"ui.shell.sidebar_navigation.save_failed": "Не удалось сохранить расположение боковой панели.",
|
|
50537
52288
|
"ui.shell.sidebar_navigation.schedules": "Расписания",
|
|
50538
52289
|
"ui.shell.sidebar_navigation.search": "Поиск",
|
|
50539
52290
|
"ui.shell.sidebar_navigation.search_accessible": "Поиск ({everything}) — чаты ({chats})",
|
|
50540
52291
|
"ui.shell.sidebar_navigation.search_tooltip": "Поиск по всему ({everything}) · ваши чаты ({chats})",
|
|
50541
52292
|
"ui.shell.sidebar_navigation.settings": "Настройки",
|
|
52293
|
+
"ui.shell.sidebar_navigation.show_in_sidebar": "Показать на боковой панели",
|
|
52294
|
+
"ui.shell.sidebar_navigation.show_in_sidebar_note": "{name} теперь отображается на боковой панели.",
|
|
50542
52295
|
"ui.shell.user_profile_sidebar.account_settings": "Настройки учётной записи",
|
|
50543
52296
|
"ui.shell.user_profile_sidebar.account_unavailable": "Учётная запись недоступна",
|
|
50544
52297
|
"ui.shell.user_profile_sidebar.checking_account": "Проверка учётной записи…",
|
|
@@ -53975,17 +55728,31 @@ const urUiShell = {
|
|
|
53975
55728
|
"ui.shell.sidebar_navigation.browser_waiting": "براؤزر، {count, plural, one {{countText} منتظر} other {{countText} منتظر}}",
|
|
53976
55729
|
"ui.shell.sidebar_navigation.close_sidebar": "سائیڈ بار بند کریں",
|
|
53977
55730
|
"ui.shell.sidebar_navigation.collapse_sidebar": "سائیڈ بار سمیٹیں",
|
|
55731
|
+
"ui.shell.sidebar_navigation.drag_armed": "{name} منتقل کیا جا رہا ہے۔ منسوخ کرنے کے لیے Esc دبائیں۔",
|
|
53978
55732
|
"ui.shell.sidebar_navigation.expand_sidebar": "سائیڈ بار پھیلائیں",
|
|
53979
55733
|
"ui.shell.sidebar_navigation.mesh": "Mesh",
|
|
53980
55734
|
"ui.shell.sidebar_navigation.mesh_waiting": "Mesh، {count, plural, one {{countText} منتظر} other {{countText} منتظر}}",
|
|
55735
|
+
"ui.shell.sidebar_navigation.more_waiting": "مزید، {count, plural, one {{countText} منتظر} other {{countText} منتظر}}",
|
|
55736
|
+
"ui.shell.sidebar_navigation.more_working": "مزید، کام جاری ہے",
|
|
55737
|
+
"ui.shell.sidebar_navigation.move_down": "نیچے کریں",
|
|
55738
|
+
"ui.shell.sidebar_navigation.move_note": "{name} کو {total} میں سے {position} مقام پر منتقل کر دیا گیا۔",
|
|
55739
|
+
"ui.shell.sidebar_navigation.move_to_more": "مزید حصے میں منتقل کریں",
|
|
55740
|
+
"ui.shell.sidebar_navigation.move_to_more_note": "{name} کو مزید حصے میں منتقل کر دیا گیا۔",
|
|
55741
|
+
"ui.shell.sidebar_navigation.move_up": "اوپر کریں",
|
|
53981
55742
|
"ui.shell.sidebar_navigation.new_chat": "نئی چیٹ",
|
|
53982
55743
|
"ui.shell.sidebar_navigation.new_chat_shortcut": "نئی چیٹ — {shortcut}",
|
|
53983
55744
|
"ui.shell.sidebar_navigation.projects": "پروجیکٹس",
|
|
55745
|
+
"ui.shell.sidebar_navigation.reset_layout": "سائیڈ بار کا لے آؤٹ ری سیٹ کریں",
|
|
55746
|
+
"ui.shell.sidebar_navigation.reset_note": "سائیڈ بار کا لے آؤٹ ری سیٹ ہو گیا۔",
|
|
55747
|
+
"ui.shell.sidebar_navigation.row_actions": "{name} کے لیے اقدامات",
|
|
55748
|
+
"ui.shell.sidebar_navigation.save_failed": "سائیڈ بار کا لے آؤٹ محفوظ نہیں ہو سکا۔",
|
|
53984
55749
|
"ui.shell.sidebar_navigation.schedules": "شیڈول",
|
|
53985
55750
|
"ui.shell.sidebar_navigation.search": "تلاش کریں",
|
|
53986
55751
|
"ui.shell.sidebar_navigation.search_accessible": "تلاش — {everything} — چیٹس — {chats}",
|
|
53987
55752
|
"ui.shell.sidebar_navigation.search_tooltip": "سب کچھ تلاش کریں — {everything} · آپ کی چیٹس — {chats}",
|
|
53988
55753
|
"ui.shell.sidebar_navigation.settings": "ترتیبات",
|
|
55754
|
+
"ui.shell.sidebar_navigation.show_in_sidebar": "سائیڈ بار میں دکھائیں",
|
|
55755
|
+
"ui.shell.sidebar_navigation.show_in_sidebar_note": "{name} اب سائیڈ بار میں ہے۔",
|
|
53989
55756
|
"ui.shell.user_profile_sidebar.account_settings": "اکاؤنٹ کی ترتیبات",
|
|
53990
55757
|
"ui.shell.user_profile_sidebar.account_unavailable": "اکاؤنٹ دستیاب نہیں",
|
|
53991
55758
|
"ui.shell.user_profile_sidebar.checking_account": "اکاؤنٹ کی جانچ ہو رہی ہے…",
|
|
@@ -57145,17 +58912,31 @@ const viUiShell = {
|
|
|
57145
58912
|
"ui.shell.sidebar_navigation.browser_waiting": "Trình duyệt, {count, plural, other {{countText} đang chờ}}",
|
|
57146
58913
|
"ui.shell.sidebar_navigation.close_sidebar": "Đóng thanh bên",
|
|
57147
58914
|
"ui.shell.sidebar_navigation.collapse_sidebar": "Thu gọn thanh bên",
|
|
58915
|
+
"ui.shell.sidebar_navigation.drag_armed": "Đang chuyển {name}. Nhấn Esc để hủy.",
|
|
57148
58916
|
"ui.shell.sidebar_navigation.expand_sidebar": "Mở rộng thanh bên",
|
|
57149
58917
|
"ui.shell.sidebar_navigation.mesh": "Mạng lưới",
|
|
57150
58918
|
"ui.shell.sidebar_navigation.mesh_waiting": "Mạng lưới, {count, plural, other {{countText} đang chờ}}",
|
|
58919
|
+
"ui.shell.sidebar_navigation.more_waiting": "Khác, {count, plural, other {{countText} đang chờ}}",
|
|
58920
|
+
"ui.shell.sidebar_navigation.more_working": "Khác, đang làm việc",
|
|
58921
|
+
"ui.shell.sidebar_navigation.move_down": "Chuyển xuống",
|
|
58922
|
+
"ui.shell.sidebar_navigation.move_note": "Đã chuyển {name} đến vị trí {position} trên {total}.",
|
|
58923
|
+
"ui.shell.sidebar_navigation.move_to_more": "Chuyển vào mục Khác",
|
|
58924
|
+
"ui.shell.sidebar_navigation.move_to_more_note": "Đã chuyển {name} vào mục Khác.",
|
|
58925
|
+
"ui.shell.sidebar_navigation.move_up": "Chuyển lên",
|
|
57151
58926
|
"ui.shell.sidebar_navigation.new_chat": "Cuộc trò chuyện mới",
|
|
57152
58927
|
"ui.shell.sidebar_navigation.new_chat_shortcut": "Cuộc trò chuyện mới ({shortcut})",
|
|
57153
58928
|
"ui.shell.sidebar_navigation.projects": "Dự án",
|
|
58929
|
+
"ui.shell.sidebar_navigation.reset_layout": "Đặt lại bố cục thanh bên",
|
|
58930
|
+
"ui.shell.sidebar_navigation.reset_note": "Đã đặt lại bố cục thanh bên.",
|
|
58931
|
+
"ui.shell.sidebar_navigation.row_actions": "Thao tác cho {name}",
|
|
58932
|
+
"ui.shell.sidebar_navigation.save_failed": "Không lưu được bố cục thanh bên.",
|
|
57154
58933
|
"ui.shell.sidebar_navigation.schedules": "Lịch chạy",
|
|
57155
58934
|
"ui.shell.sidebar_navigation.search": "Tìm kiếm",
|
|
57156
58935
|
"ui.shell.sidebar_navigation.search_accessible": "Tìm kiếm ({everything}) — cuộc trò chuyện ({chats})",
|
|
57157
58936
|
"ui.shell.sidebar_navigation.search_tooltip": "Tìm mọi thứ ({everything}) · cuộc trò chuyện của bạn ({chats})",
|
|
57158
58937
|
"ui.shell.sidebar_navigation.settings": "Cài đặt",
|
|
58938
|
+
"ui.shell.sidebar_navigation.show_in_sidebar": "Hiện trong thanh bên",
|
|
58939
|
+
"ui.shell.sidebar_navigation.show_in_sidebar_note": "{name} đã được hiển thị trong thanh bên.",
|
|
57159
58940
|
"ui.shell.user_profile_sidebar.account_settings": "Cài đặt tài khoản",
|
|
57160
58941
|
"ui.shell.user_profile_sidebar.account_unavailable": "Không truy cập được tài khoản",
|
|
57161
58942
|
"ui.shell.user_profile_sidebar.checking_account": "Đang kiểm tra tài khoản…",
|
|
@@ -60614,17 +62395,31 @@ const zhCNUiShell = {
|
|
|
60614
62395
|
"ui.shell.sidebar_navigation.browser_waiting": "浏览器,{count, plural, other {{countText}项待处理}}",
|
|
60615
62396
|
"ui.shell.sidebar_navigation.close_sidebar": "关闭侧边栏",
|
|
60616
62397
|
"ui.shell.sidebar_navigation.collapse_sidebar": "收起侧边栏",
|
|
62398
|
+
"ui.shell.sidebar_navigation.drag_armed": "正在移动{name}。按Esc取消。",
|
|
60617
62399
|
"ui.shell.sidebar_navigation.expand_sidebar": "展开侧边栏",
|
|
60618
62400
|
"ui.shell.sidebar_navigation.mesh": "网格",
|
|
60619
62401
|
"ui.shell.sidebar_navigation.mesh_waiting": "网格,{count, plural, other {{countText}项待处理}}",
|
|
62402
|
+
"ui.shell.sidebar_navigation.more_waiting": "更多,{count, plural, other {{countText}项待处理}}",
|
|
62403
|
+
"ui.shell.sidebar_navigation.more_working": "更多,工作中",
|
|
62404
|
+
"ui.shell.sidebar_navigation.move_down": "下移",
|
|
62405
|
+
"ui.shell.sidebar_navigation.move_note": "已将{name}移至第{position}位,共{total}个。",
|
|
62406
|
+
"ui.shell.sidebar_navigation.move_to_more": "移至更多",
|
|
62407
|
+
"ui.shell.sidebar_navigation.move_to_more_note": "{name}已移至更多。",
|
|
62408
|
+
"ui.shell.sidebar_navigation.move_up": "上移",
|
|
60620
62409
|
"ui.shell.sidebar_navigation.new_chat": "新建对话",
|
|
60621
62410
|
"ui.shell.sidebar_navigation.new_chat_shortcut": "新建对话({shortcut})",
|
|
60622
62411
|
"ui.shell.sidebar_navigation.projects": "项目",
|
|
62412
|
+
"ui.shell.sidebar_navigation.reset_layout": "重置侧边栏布局",
|
|
62413
|
+
"ui.shell.sidebar_navigation.reset_note": "侧边栏布局已重置。",
|
|
62414
|
+
"ui.shell.sidebar_navigation.row_actions": "{name}的操作",
|
|
62415
|
+
"ui.shell.sidebar_navigation.save_failed": "无法保存侧边栏布局。",
|
|
60623
62416
|
"ui.shell.sidebar_navigation.schedules": "定时任务",
|
|
60624
62417
|
"ui.shell.sidebar_navigation.search": "搜索",
|
|
60625
62418
|
"ui.shell.sidebar_navigation.search_accessible": "搜索({everything})—对话({chats})",
|
|
60626
62419
|
"ui.shell.sidebar_navigation.search_tooltip": "搜索全部内容({everything})·搜索对话({chats})",
|
|
60627
62420
|
"ui.shell.sidebar_navigation.settings": "设置",
|
|
62421
|
+
"ui.shell.sidebar_navigation.show_in_sidebar": "在侧边栏中显示",
|
|
62422
|
+
"ui.shell.sidebar_navigation.show_in_sidebar_note": "{name}已显示在侧边栏中。",
|
|
60628
62423
|
"ui.shell.user_profile_sidebar.account_settings": "账户设置",
|
|
60629
62424
|
"ui.shell.user_profile_sidebar.account_unavailable": "账户不可用",
|
|
60630
62425
|
"ui.shell.user_profile_sidebar.checking_account": "正在检查账户……",
|
|
@@ -62955,10 +64750,29 @@ function mintNonce() {
|
|
|
62955
64750
|
return node_crypto.randomBytes(16).toString("hex");
|
|
62956
64751
|
}
|
|
62957
64752
|
class TabRegistry {
|
|
62958
|
-
|
|
64753
|
+
/**
|
|
64754
|
+
* The fourth callback is a post-layout observer, optional and defaulted so
|
|
64755
|
+
* every existing three-argument construction is unchanged.
|
|
64756
|
+
*
|
|
64757
|
+
* WHY IT EXISTS: a native child positioned over a page has to know that the
|
|
64758
|
+
* presented tab, its presentation bit or the content rectangle moved. Polling
|
|
64759
|
+
* the registry would read a rectangle that is already stale, and `onChanged`
|
|
64760
|
+
* is deliberately NOT that signal (`setContentRect` does not call it). The
|
|
64761
|
+
* snapshot is copied and delivered after every pass — including null and
|
|
64762
|
+
* suppressed layouts — because "nothing is presented now" is exactly when a
|
|
64763
|
+
* stale overlay must hide.
|
|
64764
|
+
*
|
|
64765
|
+
* WHY IT CANNOT FAIL THE LAYOUT: page geometry is the product's behaviour and
|
|
64766
|
+
* an observer is infrastructure around it. A throwing observer is swallowed
|
|
64767
|
+
* here (the controller logs its own failure) so one experimental child can
|
|
64768
|
+
* never leave pages unlaid-out.
|
|
64769
|
+
*/
|
|
64770
|
+
constructor(viewFactory, onRemove, onChanged, onLayout = () => {
|
|
64771
|
+
}) {
|
|
62959
64772
|
this.viewFactory = viewFactory;
|
|
62960
64773
|
this.onRemove = onRemove;
|
|
62961
64774
|
this.onChanged = onChanged;
|
|
64775
|
+
this.onLayout = onLayout;
|
|
62962
64776
|
}
|
|
62963
64777
|
tabs = /* @__PURE__ */ new Map();
|
|
62964
64778
|
nextTabId = 1;
|
|
@@ -62968,6 +64782,9 @@ class TabRegistry {
|
|
|
62968
64782
|
/** Tabs with a command in flight, keyed by tabId. See `lane`. */
|
|
62969
64783
|
busy = /* @__PURE__ */ new Set();
|
|
62970
64784
|
epochListeners = [];
|
|
64785
|
+
/** Monotone count of layout passes, for an observer that needs identity, not
|
|
64786
|
+
* just values (S1's address overlay). */
|
|
64787
|
+
layoutRevision = 0;
|
|
62971
64788
|
// ---- creation, addressing, listing ---------------------------------------
|
|
62972
64789
|
/**
|
|
62973
64790
|
* Create a tab. A user tab becomes active (the user asked for it); an AGENT
|
|
@@ -63244,8 +65061,8 @@ class TabRegistry {
|
|
|
63244
65061
|
* hijacked window. Visiting the route again reports a fresh rect and restores
|
|
63245
65062
|
* it. A null rect is also how the caller hides the view at teardown, so it must
|
|
63246
65063
|
* never be swallowed on the way here (see `use-browser-chrome`). */
|
|
63247
|
-
setContentRect(
|
|
63248
|
-
this.contentRect =
|
|
65064
|
+
setContentRect(rect2) {
|
|
65065
|
+
this.contentRect = rect2;
|
|
63249
65066
|
this.applyLayout();
|
|
63250
65067
|
}
|
|
63251
65068
|
/** Full-window overlays hide the view (design 11.3): a native view paints
|
|
@@ -63270,6 +65087,22 @@ class TabRegistry {
|
|
|
63270
65087
|
);
|
|
63271
65088
|
record.view.setVisible(record.presented);
|
|
63272
65089
|
}
|
|
65090
|
+
this.layoutRevision += 1;
|
|
65091
|
+
const rect2 = this.contentRect;
|
|
65092
|
+
try {
|
|
65093
|
+
this.onLayout({
|
|
65094
|
+
activeTabId: this.activeTabId,
|
|
65095
|
+
presented: active?.presented === true,
|
|
65096
|
+
contentRect: rect2 ? {
|
|
65097
|
+
x: rect2.x,
|
|
65098
|
+
y: rect2.y,
|
|
65099
|
+
width: rect2.width,
|
|
65100
|
+
height: rect2.height
|
|
65101
|
+
} : null,
|
|
65102
|
+
revision: this.layoutRevision
|
|
65103
|
+
});
|
|
65104
|
+
} catch {
|
|
65105
|
+
}
|
|
63273
65106
|
}
|
|
63274
65107
|
// ---- removal -------------------------------------------------------------
|
|
63275
65108
|
/**
|
|
@@ -65433,9 +67266,9 @@ class BrowserHost {
|
|
|
65433
67266
|
}
|
|
65434
67267
|
/** Layout authority is the renderer's (design 11.2): it measures the content
|
|
65435
67268
|
* area and reports it, and main applies it to the active tab only. */
|
|
65436
|
-
setContentRect(
|
|
65437
|
-
this.registry.setContentRect(
|
|
65438
|
-
if (
|
|
67269
|
+
setContentRect(rect2) {
|
|
67270
|
+
this.registry.setContentRect(rect2);
|
|
67271
|
+
if (rect2 === null) this.clearPageFocus();
|
|
65439
67272
|
return this.chromeState();
|
|
65440
67273
|
}
|
|
65441
67274
|
/** Full-window overlays hide the view: a native view paints above all DOM, so
|
|
@@ -66460,7 +68293,15 @@ const BROWSER_IPC_CHANNELS = [
|
|
|
66460
68293
|
"browser-reveal-downloads",
|
|
66461
68294
|
/** The user's own recovery click on the `unavailable` state (round 1, D1a).
|
|
66462
68295
|
* Named here so the namespace stays the one place that lists its channels. */
|
|
66463
|
-
"browser-retry"
|
|
68296
|
+
"browser-retry",
|
|
68297
|
+
/**
|
|
68298
|
+
* S1's probe-only channel. Listed here for unregistration only: the handler
|
|
68299
|
+
* below is registered only when the launch gate passed, and this entry makes
|
|
68300
|
+
* `unregisterBrowserIpc` remove it on a stop/retry like every other channel
|
|
68301
|
+
* (removing an unregistered handler is a no-op).
|
|
68302
|
+
*/
|
|
68303
|
+
DEV_DRIVER_ADDRESS_OVERLAY,
|
|
68304
|
+
DEV_DRIVER_ADDRESS_OVERLAY_PROJECT
|
|
66464
68305
|
];
|
|
66465
68306
|
function registerBrowserIpc(options) {
|
|
66466
68307
|
function authorizeSender(event) {
|
|
@@ -66605,9 +68446,9 @@ function registerBrowserIpc(options) {
|
|
|
66605
68446
|
}
|
|
66606
68447
|
return host.historyActive(direction);
|
|
66607
68448
|
});
|
|
66608
|
-
electron.ipcMain.handle("browser-set-content-rect", (event,
|
|
68449
|
+
electron.ipcMain.handle("browser-set-content-rect", (event, rect2) => {
|
|
66609
68450
|
const host = authorize(event);
|
|
66610
|
-
return host.setContentRect(contentRectOrNull(
|
|
68451
|
+
return host.setContentRect(contentRectOrNull(rect2));
|
|
66611
68452
|
});
|
|
66612
68453
|
electron.ipcMain.handle("browser-set-visible", (event, visible) => {
|
|
66613
68454
|
const host = authorize(event);
|
|
@@ -66706,6 +68547,42 @@ function registerBrowserIpc(options) {
|
|
|
66706
68547
|
await options.retry();
|
|
66707
68548
|
return { retried: true };
|
|
66708
68549
|
});
|
|
68550
|
+
if (options.addressOverlayProbe) {
|
|
68551
|
+
electron.ipcMain.handle(
|
|
68552
|
+
DEV_DRIVER_ADDRESS_OVERLAY,
|
|
68553
|
+
(event, action, argument) => {
|
|
68554
|
+
authorizeSender(event);
|
|
68555
|
+
if (action !== "show" && action !== "hide" && action !== "facts" && action !== "click" && action !== "measure" && action !== "key") {
|
|
68556
|
+
throw new Error(
|
|
68557
|
+
"address_overlay_invalid_action: expected 'show', 'hide', 'facts', 'click', 'measure' or 'key'."
|
|
68558
|
+
);
|
|
68559
|
+
}
|
|
68560
|
+
const probe = options.addressOverlayProbe;
|
|
68561
|
+
if (!probe) {
|
|
68562
|
+
throw new Error(
|
|
68563
|
+
"address_overlay_unavailable: the overlay probe is not running."
|
|
68564
|
+
);
|
|
68565
|
+
}
|
|
68566
|
+
const readsArgument = action === "click" || action === "measure" || action === "key";
|
|
68567
|
+
return probe(action, readsArgument ? argument : void 0);
|
|
68568
|
+
}
|
|
68569
|
+
);
|
|
68570
|
+
}
|
|
68571
|
+
if (options.addressOverlayProject) {
|
|
68572
|
+
electron.ipcMain.handle(
|
|
68573
|
+
DEV_DRIVER_ADDRESS_OVERLAY_PROJECT,
|
|
68574
|
+
(event, payload) => {
|
|
68575
|
+
authorizeSender(event);
|
|
68576
|
+
const project = options.addressOverlayProject;
|
|
68577
|
+
if (!project) {
|
|
68578
|
+
throw new Error(
|
|
68579
|
+
"address_overlay_unavailable: the overlay projection is not running."
|
|
68580
|
+
);
|
|
68581
|
+
}
|
|
68582
|
+
return project(payload);
|
|
68583
|
+
}
|
|
68584
|
+
);
|
|
68585
|
+
}
|
|
66709
68586
|
}
|
|
66710
68587
|
function unregisterBrowserIpc() {
|
|
66711
68588
|
for (const channel of BROWSER_IPC_CHANNELS) electron.ipcMain.removeHandler(channel);
|
|
@@ -66769,17 +68646,17 @@ function contentRectOrNull(value) {
|
|
|
66769
68646
|
if (value === null || value === void 0) return null;
|
|
66770
68647
|
if (typeof value !== "object")
|
|
66771
68648
|
throw new Error("Content rect must be an object.");
|
|
66772
|
-
const
|
|
68649
|
+
const rect2 = value;
|
|
66773
68650
|
for (const key of ["x", "y", "width", "height"]) {
|
|
66774
|
-
if (typeof
|
|
68651
|
+
if (typeof rect2[key] !== "number" || !Number.isFinite(rect2[key])) {
|
|
66775
68652
|
throw new Error("Content rect needs finite x, y, width and height.");
|
|
66776
68653
|
}
|
|
66777
68654
|
}
|
|
66778
68655
|
return {
|
|
66779
|
-
x:
|
|
66780
|
-
y:
|
|
66781
|
-
width:
|
|
66782
|
-
height:
|
|
68656
|
+
x: rect2.x,
|
|
68657
|
+
y: rect2.y,
|
|
68658
|
+
width: rect2.width,
|
|
68659
|
+
height: rect2.height
|
|
66783
68660
|
};
|
|
66784
68661
|
}
|
|
66785
68662
|
function paneNavigationDirection(input) {
|
|
@@ -68683,6 +70560,37 @@ function startBrowserHost(options) {
|
|
|
68683
70560
|
await startBrowserHost(options).catch(() => {
|
|
68684
70561
|
});
|
|
68685
70562
|
},
|
|
70563
|
+
/*
|
|
70564
|
+
* S1's probe channel is registered ONLY when this launch's gate passed, and
|
|
70565
|
+
* then it reads the current attempt's callback through `bindings` (a retry
|
|
70566
|
+
* replaces it) rather than capturing a controller here.
|
|
70567
|
+
*/
|
|
70568
|
+
...options.addressOverlay?.enabled ? {
|
|
70569
|
+
addressOverlayProbe: (action, argument) => {
|
|
70570
|
+
const probe = bindings.addressOverlayProbe;
|
|
70571
|
+
if (!probe) {
|
|
70572
|
+
throw new Error(
|
|
70573
|
+
"address_overlay_unavailable: the overlay controller is not running."
|
|
70574
|
+
);
|
|
70575
|
+
}
|
|
70576
|
+
return probe(action, argument);
|
|
70577
|
+
},
|
|
70578
|
+
/* S2's projection. Registered only with the S1 probe, because the
|
|
70579
|
+
* projection can only drive a child the probe's gate also armed. The
|
|
70580
|
+
* current attempt's handler is read through `bindings` per call, like
|
|
70581
|
+
* the probe above, so a retry cannot leave a stale controller. */
|
|
70582
|
+
...options.addressOverlay.list === true ? {
|
|
70583
|
+
addressOverlayProject: (payload) => {
|
|
70584
|
+
const project = bindings.addressOverlayProject;
|
|
70585
|
+
if (!project) {
|
|
70586
|
+
throw new Error(
|
|
70587
|
+
"address_overlay_unavailable: the overlay controller is not running."
|
|
70588
|
+
);
|
|
70589
|
+
}
|
|
70590
|
+
return project(payload);
|
|
70591
|
+
}
|
|
70592
|
+
} : {}
|
|
70593
|
+
} : {},
|
|
68686
70594
|
log: options.log
|
|
68687
70595
|
});
|
|
68688
70596
|
starting = startWithRetries({
|
|
@@ -68719,6 +70627,7 @@ async function initializeBrowserHost(options, bindings) {
|
|
|
68719
70627
|
};
|
|
68720
70628
|
try {
|
|
68721
70629
|
let releaseTab = function(tabId, webContentsId) {
|
|
70630
|
+
addressOverlay?.onTabReleased(tabId);
|
|
68722
70631
|
const view = views.get(tabId);
|
|
68723
70632
|
views.delete(tabId);
|
|
68724
70633
|
downloads.forget(tabId);
|
|
@@ -68870,6 +70779,9 @@ async function initializeBrowserHost(options, bindings) {
|
|
|
68870
70779
|
});
|
|
68871
70780
|
contents.on("did-stop-loading", notifyChrome);
|
|
68872
70781
|
contents.on("page-title-updated", notifyChrome);
|
|
70782
|
+
contents.on("render-process-gone", () => {
|
|
70783
|
+
addressOverlay?.onPageGone(tabId);
|
|
70784
|
+
});
|
|
68873
70785
|
contents.on(
|
|
68874
70786
|
"did-fail-load",
|
|
68875
70787
|
(_event, code, description, url, isMainFrame) => {
|
|
@@ -69064,6 +70976,27 @@ async function initializeBrowserHost(options, bindings) {
|
|
|
69064
70976
|
const views = /* @__PURE__ */ new Map();
|
|
69065
70977
|
const releases = /* @__PURE__ */ new Set();
|
|
69066
70978
|
let stateWriter = null;
|
|
70979
|
+
const addressOverlay = options.addressOverlay?.enabled === true ? new AddressOverlayController({
|
|
70980
|
+
window: options.window,
|
|
70981
|
+
documentUrl: options.addressOverlay.documentUrl,
|
|
70982
|
+
javascript: options.addressOverlay.list === true,
|
|
70983
|
+
visible: options.addressOverlay.visible === true,
|
|
70984
|
+
// The only consumer of a validated row click: the owner window's own
|
|
70985
|
+
// renderer. Guarded because a stop can race a queued mouse event.
|
|
70986
|
+
onEvent: (event) => {
|
|
70987
|
+
if (!options.window.isDestroyed()) {
|
|
70988
|
+
options.window.webContents.send(
|
|
70989
|
+
DEV_DRIVER_ADDRESS_OVERLAY_EVENT,
|
|
70990
|
+
event
|
|
70991
|
+
);
|
|
70992
|
+
}
|
|
70993
|
+
},
|
|
70994
|
+
log: log2,
|
|
70995
|
+
appMetrics: () => electron.app.getAppMetrics().map((metric) => ({
|
|
70996
|
+
pid: metric.pid,
|
|
70997
|
+
type: metric.type
|
|
70998
|
+
}))
|
|
70999
|
+
}) : null;
|
|
69067
71000
|
const sessionStore = new BrowserSessionStore({
|
|
69068
71001
|
dir: path$1.join(options.userDataDir, "browser"),
|
|
69069
71002
|
log: log2
|
|
@@ -69104,7 +71037,9 @@ async function initializeBrowserHost(options, bindings) {
|
|
|
69104
71037
|
};
|
|
69105
71038
|
const notifyChanged = () => {
|
|
69106
71039
|
stateWriter?.publishNow();
|
|
69107
|
-
options.window.webContents.
|
|
71040
|
+
if (!options.window.isDestroyed() && !options.window.webContents.isDestroyed()) {
|
|
71041
|
+
options.window.webContents.send("browser-state-changed");
|
|
71042
|
+
}
|
|
69108
71043
|
captureSession();
|
|
69109
71044
|
};
|
|
69110
71045
|
const registry = new TabRegistry(
|
|
@@ -69115,7 +71050,8 @@ async function initializeBrowserHost(options, bindings) {
|
|
|
69115
71050
|
return view;
|
|
69116
71051
|
},
|
|
69117
71052
|
releaseTab,
|
|
69118
|
-
notifyChanged
|
|
71053
|
+
notifyChanged,
|
|
71054
|
+
(snapshot2) => addressOverlay?.onLayout(snapshot2)
|
|
69119
71055
|
);
|
|
69120
71056
|
registryForDownloads = registry;
|
|
69121
71057
|
rollback.add(async () => {
|
|
@@ -69124,6 +71060,7 @@ async function initializeBrowserHost(options, bindings) {
|
|
|
69124
71060
|
registry.destroyAll();
|
|
69125
71061
|
await Promise.allSettled([...releases]);
|
|
69126
71062
|
});
|
|
71063
|
+
rollback.add(() => addressOverlay?.dispose("startup-rollback"));
|
|
69127
71064
|
const ownership = new OwnershipLedger({
|
|
69128
71065
|
mayAdopt: (token, requester) => {
|
|
69129
71066
|
const record = registry.requireSurface(token);
|
|
@@ -69150,7 +71087,9 @@ async function initializeBrowserHost(options, bindings) {
|
|
|
69150
71087
|
const approvals = new ApprovalStore({
|
|
69151
71088
|
dir: path$1.join(options.userDataDir, "browser"),
|
|
69152
71089
|
onChanged: () => {
|
|
69153
|
-
options.window.webContents.
|
|
71090
|
+
if (!options.window.isDestroyed() && !options.window.webContents.isDestroyed()) {
|
|
71091
|
+
options.window.webContents.send("browser-consent-changed");
|
|
71092
|
+
}
|
|
69154
71093
|
consentNotifier.announce(approvals.pendingEntries());
|
|
69155
71094
|
},
|
|
69156
71095
|
log: log2
|
|
@@ -69232,6 +71171,7 @@ async function initializeBrowserHost(options, bindings) {
|
|
|
69232
71171
|
host.clearPageFocus();
|
|
69233
71172
|
};
|
|
69234
71173
|
const resetMenuShortcutSuppression = () => {
|
|
71174
|
+
if (options.window.isDestroyed()) return;
|
|
69235
71175
|
if (!options.window.webContents.isDestroyed())
|
|
69236
71176
|
options.window.webContents.setIgnoreMenuShortcuts(false);
|
|
69237
71177
|
};
|
|
@@ -69249,6 +71189,7 @@ async function initializeBrowserHost(options, bindings) {
|
|
|
69249
71189
|
options.window.webContents.on("render-process-gone", resetChordFocus);
|
|
69250
71190
|
const detachChords = () => {
|
|
69251
71191
|
host.clearChordState();
|
|
71192
|
+
if (options.window.isDestroyed()) return;
|
|
69252
71193
|
options.window.webContents.removeListener(
|
|
69253
71194
|
"before-input-event",
|
|
69254
71195
|
onChromeInput
|
|
@@ -69358,6 +71299,36 @@ async function initializeBrowserHost(options, bindings) {
|
|
|
69358
71299
|
);
|
|
69359
71300
|
rollback.add(() => stateWriter?.clear());
|
|
69360
71301
|
stateWriter.start(server.port, sessionKey);
|
|
71302
|
+
const probeAddressOverlay = async (action, argument) => {
|
|
71303
|
+
if (!addressOverlay) {
|
|
71304
|
+
throw new Error(
|
|
71305
|
+
"address_overlay_unavailable: this host owns no controller."
|
|
71306
|
+
);
|
|
71307
|
+
}
|
|
71308
|
+
const facts2 = await addressOverlay.probe(action, argument);
|
|
71309
|
+
const record = registry.activeTab;
|
|
71310
|
+
const page = record ? {
|
|
71311
|
+
tabId: record.tabId,
|
|
71312
|
+
presented: record.presented,
|
|
71313
|
+
bounds: record.view.getBounds?.() ?? null,
|
|
71314
|
+
visible: record.view.getVisible?.() ?? false
|
|
71315
|
+
} : null;
|
|
71316
|
+
return { ...facts2, page };
|
|
71317
|
+
};
|
|
71318
|
+
const projectAddressOverlay = (payload) => {
|
|
71319
|
+
if (!addressOverlay) {
|
|
71320
|
+
throw new Error(
|
|
71321
|
+
"address_overlay_unavailable: this host owns no controller."
|
|
71322
|
+
);
|
|
71323
|
+
}
|
|
71324
|
+
return addressOverlay.project(payload);
|
|
71325
|
+
};
|
|
71326
|
+
if (addressOverlay) {
|
|
71327
|
+
bindings.addressOverlayProbe = probeAddressOverlay;
|
|
71328
|
+
if (options.addressOverlay?.list === true) {
|
|
71329
|
+
bindings.addressOverlayProject = projectAddressOverlay;
|
|
71330
|
+
}
|
|
71331
|
+
}
|
|
69361
71332
|
Object.assign(bindings, {
|
|
69362
71333
|
window: () => options.window,
|
|
69363
71334
|
expectedUrl: options.expectedUrl,
|
|
@@ -69387,6 +71358,7 @@ async function initializeBrowserHost(options, bindings) {
|
|
|
69387
71358
|
profileDir,
|
|
69388
71359
|
agentTabs: () => registry.agentTabCount(),
|
|
69389
71360
|
sessionCookies: () => restoreReport,
|
|
71361
|
+
...addressOverlay ? { addressOverlay: probeAddressOverlay } : {},
|
|
69390
71362
|
stop: async () => {
|
|
69391
71363
|
detachChords();
|
|
69392
71364
|
const atStop = captureTabs(
|
|
@@ -69401,6 +71373,7 @@ async function initializeBrowserHost(options, bindings) {
|
|
|
69401
71373
|
);
|
|
69402
71374
|
}
|
|
69403
71375
|
sessionStore.flush();
|
|
71376
|
+
addressOverlay?.dispose("host-stop");
|
|
69404
71377
|
registry.destroyAll();
|
|
69405
71378
|
await Promise.allSettled([...releases]);
|
|
69406
71379
|
await cdp.close();
|
|
@@ -70419,57 +72392,6 @@ class DesktopNotifier {
|
|
|
70419
72392
|
notification.show();
|
|
70420
72393
|
}
|
|
70421
72394
|
}
|
|
70422
|
-
const DEV_DRIVER_ENV = "LOCAL_OPERATOR_UI_DEV_DRIVER";
|
|
70423
|
-
const DEV_DRIVER_OUT_ENV = "LOCAL_OPERATOR_UI_DEV_DRIVER_OUT";
|
|
70424
|
-
const DEV_DRIVER_ARG = "--lo-dev-driver";
|
|
70425
|
-
const DEV_DRIVER_CAPTURE = "dev-driver-capture";
|
|
70426
|
-
const DEV_DRIVER_FACTS = "dev-driver-facts";
|
|
70427
|
-
const TRUTHY = /* @__PURE__ */ new Set(["1", "true"]);
|
|
70428
|
-
function readOptIn(env) {
|
|
70429
|
-
const raw = env[DEV_DRIVER_ENV];
|
|
70430
|
-
if (raw === void 0 || raw.trim() === "")
|
|
70431
|
-
return { asked: false, value: null };
|
|
70432
|
-
return { asked: true, value: raw.trim() };
|
|
70433
|
-
}
|
|
70434
|
-
function resolveDevDriverArming(input) {
|
|
70435
|
-
const off = (problems = []) => ({
|
|
70436
|
-
armed: false,
|
|
70437
|
-
outDir: null,
|
|
70438
|
-
problems
|
|
70439
|
-
});
|
|
70440
|
-
const optIn = readOptIn(input.env);
|
|
70441
|
-
if (!optIn.asked) return off();
|
|
70442
|
-
if (!TRUTHY.has(optIn.value.toLowerCase())) {
|
|
70443
|
-
return off([
|
|
70444
|
-
`${DEV_DRIVER_ENV}=${optIn.value} is not an opt-in (accepted: 1, true); the renderer dev driver stayed off`
|
|
70445
|
-
]);
|
|
70446
|
-
}
|
|
70447
|
-
if (input.windowMode !== "headless" && input.windowMode !== "inactive") {
|
|
70448
|
-
return off([
|
|
70449
|
-
`the renderer dev driver refused to arm in a "${input.windowMode}" window; it never raises one, so use --window-mode=headless (or inactive)`
|
|
70450
|
-
]);
|
|
70451
|
-
}
|
|
70452
|
-
const outDir = input.env[DEV_DRIVER_OUT_ENV]?.trim() ?? "";
|
|
70453
|
-
if (outDir === "" || !outDir.startsWith("/")) {
|
|
70454
|
-
return off([
|
|
70455
|
-
`${DEV_DRIVER_ENV} was set but ${DEV_DRIVER_OUT_ENV} is ${outDir === "" ? "missing" : `"${outDir}"`}; an absolute frames directory is required, so the driver did not arm`
|
|
70456
|
-
]);
|
|
70457
|
-
}
|
|
70458
|
-
return { armed: true, outDir, problems: [] };
|
|
70459
|
-
}
|
|
70460
|
-
function headlessExerciserAllowed(input) {
|
|
70461
|
-
return input.arming.armed && input.windowMode === "headless";
|
|
70462
|
-
}
|
|
70463
|
-
function devDriverArgument(outDir) {
|
|
70464
|
-
return `${DEV_DRIVER_ARG}=${outDir}`;
|
|
70465
|
-
}
|
|
70466
|
-
function describeDevDriverArming(arming) {
|
|
70467
|
-
if (arming.armed) {
|
|
70468
|
-
return `[dev-driver] ARMED; frames are written to ${arming.outDir}`;
|
|
70469
|
-
}
|
|
70470
|
-
if (arming.problems.length === 0) return null;
|
|
70471
|
-
return `[dev-driver] not armed: ${arming.problems.join("; ")}`;
|
|
70472
|
-
}
|
|
70473
72395
|
const FRAME_LABEL = /^[a-z0-9][a-z0-9_-]{0,63}$/;
|
|
70474
72396
|
function registerDevDriverIPC(options) {
|
|
70475
72397
|
const { window, expectedUrl, outDir, identity: identity2 } = options;
|
|
@@ -80764,6 +82686,17 @@ const devDriverArming = resolveDevDriverArming({
|
|
|
80764
82686
|
});
|
|
80765
82687
|
const devDriverLine = describeDevDriverArming(devDriverArming);
|
|
80766
82688
|
if (devDriverLine) console.log(devDriverLine);
|
|
82689
|
+
const addressOverlayGate = resolveAddressOverlayGate({
|
|
82690
|
+
env: launchEnv,
|
|
82691
|
+
devDriverArmed: devDriverArming.armed,
|
|
82692
|
+
windowMode: windowLaunch.mode,
|
|
82693
|
+
packaged: electron.app.isPackaged
|
|
82694
|
+
});
|
|
82695
|
+
if (addressOverlayGate.asked) {
|
|
82696
|
+
console.log(
|
|
82697
|
+
addressOverlayGate.enabled ? addressOverlayGate.visible ? "[address-overlay] ARMED (S3 VISIBLE probe, inactive window, 120 s cap, no product path)" : `[address-overlay] ARMED (S${addressOverlayGate.list ? "2 probe: mirror list" : "1 probe: native child"}, no product path)` : `[address-overlay] not armed: ${addressOverlayGate.reason}`
|
|
82698
|
+
);
|
|
82699
|
+
}
|
|
80767
82700
|
const devDriverWebPreferences = devDriverArming.armed && devDriverArming.outDir ? { additionalArguments: [devDriverArgument(devDriverArming.outDir)] } : {};
|
|
80768
82701
|
const telemetryWebPreferences = {
|
|
80769
82702
|
additionalArguments: [telemetryArgument(telemetryLaunch.enabled)]
|
|
@@ -81035,7 +82968,10 @@ electron.app.whenReady().then(async () => {
|
|
|
81035
82968
|
const launcher = resolveLauncherWatchPlan({
|
|
81036
82969
|
mode: windowLaunch.mode,
|
|
81037
82970
|
launcherPid: process.ppid,
|
|
81038
|
-
env: launchEnv
|
|
82971
|
+
env: launchEnv,
|
|
82972
|
+
// S3 only: the one visible run nobody is watching still must not
|
|
82973
|
+
// outlive its launcher. False for every product launch.
|
|
82974
|
+
boundedVisibleProbe: addressOverlayGate.visible
|
|
81039
82975
|
});
|
|
81040
82976
|
logger.info(`[window-mode] ${launcher.reason}`, LogFileType.BACKEND);
|
|
81041
82977
|
if (windowLaunch.mode !== "normal") {
|
|
@@ -81050,12 +82986,23 @@ electron.app.whenReady().then(async () => {
|
|
|
81050
82986
|
onLauncherGone: ({ message, detail }) => endHeadlessRun(message, detail)
|
|
81051
82987
|
});
|
|
81052
82988
|
}
|
|
81053
|
-
if (
|
|
82989
|
+
if (addressOverlayGate.visible) {
|
|
82990
|
+
const cap = setTimeout(() => {
|
|
82991
|
+
endHeadlessRun(
|
|
82992
|
+
`the visible address-overlay probe reached its ${ADDRESS_OVERLAY_VISIBLE_MAX_MS / 1e3} s cap`
|
|
82993
|
+
);
|
|
82994
|
+
}, ADDRESS_OVERLAY_VISIBLE_MAX_MS);
|
|
82995
|
+
cap.unref();
|
|
82996
|
+
console.log(
|
|
82997
|
+
`[address-overlay] visible probe cap: ${ADDRESS_OVERLAY_VISIBLE_MAX_MS / 1e3} s from launch`
|
|
82998
|
+
);
|
|
82999
|
+
}
|
|
83000
|
+
if (windowLaunch.mode === "headless" || addressOverlayGate.visible) {
|
|
81054
83001
|
for (const signal of ["SIGTERM", "SIGINT"]) {
|
|
81055
83002
|
process.on(signal, () => endHeadlessRun(`received ${signal}`));
|
|
81056
83003
|
}
|
|
81057
83004
|
}
|
|
81058
|
-
if (windowLaunch.mode !== "headless") {
|
|
83005
|
+
if (windowLaunch.mode !== "headless" && !addressOverlayGate.visible) {
|
|
81059
83006
|
const onSigtermDuringQuit = () => {
|
|
81060
83007
|
if (!quitState.isQuitting()) {
|
|
81061
83008
|
process.removeListener("SIGTERM", onSigtermDuringQuit);
|
|
@@ -81167,6 +83114,7 @@ electron.app.whenReady().then(async () => {
|
|
|
81167
83114
|
});
|
|
81168
83115
|
const rendererUrl = rendererDocumentUrl();
|
|
81169
83116
|
const consoleCaptureUrl = consoleCaptureUrlFor(rendererUrl);
|
|
83117
|
+
const addressOverlayDocumentUrl = addressOverlayGate.list ? addressListDocumentUrlFor(rendererUrl) : addressOverlayDocumentUrlFor(rendererUrl);
|
|
81170
83118
|
backendService.observeDesktopFeed(
|
|
81171
83119
|
(frame) => {
|
|
81172
83120
|
if (frame.type === "notification") {
|
|
@@ -81975,6 +83923,16 @@ electron.app.whenReady().then(async () => {
|
|
|
81975
83923
|
// document, and the preload every renderer in this app gets, which is what
|
|
81976
83924
|
// lets main feed the reconstruction to it.
|
|
81977
83925
|
consoleCaptureUrl,
|
|
83926
|
+
// S1: only a gated headless probe arm gets this option at all. The
|
|
83927
|
+
// URL is this app's own packaged document, not a caller's string.
|
|
83928
|
+
...addressOverlayGate.enabled ? {
|
|
83929
|
+
addressOverlay: {
|
|
83930
|
+
enabled: true,
|
|
83931
|
+
list: addressOverlayGate.list,
|
|
83932
|
+
visible: addressOverlayGate.visible,
|
|
83933
|
+
documentUrl: addressOverlayDocumentUrl
|
|
83934
|
+
}
|
|
83935
|
+
} : {},
|
|
81978
83936
|
preloadPath: path$1.join(__dirname, "../preload/index.js"),
|
|
81979
83937
|
/*
|
|
81980
83938
|
* The console's surfaces are handed the user's own PATH, from the same
|