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.
Files changed (99) hide show
  1. package/out/main/index.js +2047 -89
  2. package/out/preload/index.js +46 -1
  3. package/out/renderer/assets/{_basePickBy-DhcDqJuW.js → _basePickBy-CSbKxvDP.js} +1 -1
  4. package/out/renderer/assets/{_baseUniq-DlAVpNmp.js → _baseUniq-BCDEv_Oc.js} +1 -1
  5. package/out/renderer/assets/addressOverlayList-8wdZRD_t.css +1 -0
  6. package/out/renderer/assets/addressOverlayList-BEh9WU4R.js +1 -0
  7. package/out/renderer/assets/{agent-details-page-A51rffCS.js → agent-details-page-ByULov9A.js} +2 -2
  8. package/out/renderer/assets/{agent-hub-page-DQ-tnD26.js → agent-hub-page-B69p4cyN.js} +2 -2
  9. package/out/renderer/assets/{agents-page-Bh6SGDaA.js → agents-page-Cl3KMRZy.js} +3 -3
  10. package/out/renderer/assets/{arc-C-BSLMbh.js → arc-CyVR9eoB.js} +1 -1
  11. package/out/renderer/assets/{architectureDiagram-IEHRJDOE-nDfnCWAM.js → architectureDiagram-IEHRJDOE-D_nbs604.js} +1 -1
  12. package/out/renderer/assets/{arrow-up-JQ86w8h2.js → arrow-up-JG56D3ZM.js} +1 -1
  13. package/out/renderer/assets/{blockDiagram-JOT3LUYC-BJgd2uGT.js → blockDiagram-JOT3LUYC-cGHKlnnj.js} +1 -1
  14. package/out/renderer/assets/{book-open-yjstLkVS.js → book-open-_50fvSpZ.js} +1 -1
  15. package/out/renderer/assets/browser-page-u8Qblazq.js +1 -0
  16. package/out/renderer/assets/{browser-webauthn-prompt-CdHXLdwX.js → browser-webauthn-prompt-BkYnuYH0.js} +1 -1
  17. package/out/renderer/assets/{building-2-Cupv31tS.js → building-2-D-6u9f2b.js} +1 -1
  18. package/out/renderer/assets/{c4Diagram-VJAJSXHY-lIXnBLZ5.js → c4Diagram-VJAJSXHY-DPSkxG1K.js} +1 -1
  19. package/out/renderer/assets/channel-BP0N5Jip.js +1 -0
  20. package/out/renderer/assets/{chunk-4BMEZGHF-DF2ktHZp.js → chunk-4BMEZGHF-k7Xc9N8N.js} +1 -1
  21. package/out/renderer/assets/{chunk-A2AXSNBT-CNRON9Qy.js → chunk-A2AXSNBT-BUQ9LrYj.js} +1 -1
  22. package/out/renderer/assets/{chunk-AEK57VVT-BGXI1SR5.js → chunk-AEK57VVT-T8K0wOxu.js} +1 -1
  23. package/out/renderer/assets/{chunk-D6G4REZN-CW_SyuGg.js → chunk-D6G4REZN-_drafUVH.js} +1 -1
  24. package/out/renderer/assets/{chunk-RZ5BOZE2-gU0oYsA6.js → chunk-RZ5BOZE2-BlfOLAiK.js} +1 -1
  25. package/out/renderer/assets/{chunk-XZIHB7SX-BrVtbfMc.js → chunk-XZIHB7SX-br7k9c-y.js} +1 -1
  26. package/out/renderer/assets/classDiagram-GIVACNV2-DgPhvGMc.js +1 -0
  27. package/out/renderer/assets/classDiagram-v2-COTLJTTW-DgPhvGMc.js +1 -0
  28. package/out/renderer/assets/clone-D_2R859t.js +1 -0
  29. package/out/renderer/assets/{console-mirror-CkCxHgcU.js → console-mirror-BFQaHp1b.js} +1 -1
  30. package/out/renderer/assets/{consoleCapture-EZYleEWx.js → consoleCapture-L2qaS8a8.js} +1 -1
  31. package/out/renderer/assets/{conversation-input-sync-bgD4kyw3.js → conversation-input-sync-CH6BMnVZ.js} +269 -269
  32. package/out/renderer/assets/{dagre-OKDRZEBW-BDR0956-.js → dagre-OKDRZEBW-BnHmlI8H.js} +1 -1
  33. package/out/renderer/assets/{diagram-SSKATNLV-DbCZDtm7.js → diagram-SSKATNLV-q_CpwdD7.js} +1 -1
  34. package/out/renderer/assets/{diagram-VNBRO52H-BpXb_OjM.js → diagram-VNBRO52H-B2mKekLo.js} +1 -1
  35. package/out/renderer/assets/{erDiagram-Q7BY3M3F-CZukR1Y4.js → erDiagram-Q7BY3M3F-CyQWMyA_.js} +1 -1
  36. package/out/renderer/assets/{error-boundary-DJAF_aYL.js → error-boundary-xAR0FhJi.js} +1 -1
  37. package/out/renderer/assets/{flowDiagram-4HSFHLVR-CqKOnXAd.js → flowDiagram-4HSFHLVR-DaECRw9d.js} +1 -1
  38. package/out/renderer/assets/{ganttDiagram-APWFNJXF-B9I0smi4.js → ganttDiagram-APWFNJXF-DcuyjU9V.js} +1 -1
  39. package/out/renderer/assets/{gitGraphDiagram-7IBYFJ6S-Bj6-u-5w.js → gitGraphDiagram-7IBYFJ6S-BY2WvPMI.js} +1 -1
  40. package/out/renderer/assets/{graph-BsgoQZc2.js → graph-or727h2C.js} +1 -1
  41. package/out/renderer/assets/{hash-BLfEUuGg.js → hash-Ch3ejz63.js} +1 -1
  42. package/out/renderer/assets/index-BUzMwM-n.css +1 -0
  43. package/out/renderer/assets/{index-lMi7GI2V.js → index-Bs2qygzx.js} +1 -1
  44. package/out/renderer/assets/index-C1J-T_Mt.js +825 -0
  45. package/out/renderer/assets/{index-D7pJoOu_.js → index-DQE5o_2l.js} +1 -1
  46. package/out/renderer/assets/index-YWHAePtd.js +57 -0
  47. package/out/renderer/assets/{infoDiagram-PH2N3AL5-DQ3aMjor.js → infoDiagram-PH2N3AL5-BXElUsIa.js} +1 -1
  48. package/out/renderer/assets/{installer-Ch3nb3kq.js → installer-DBTiypQJ.js} +1 -1
  49. package/out/renderer/assets/{journeyDiagram-U35MCT3I-B_RccO_x.js → journeyDiagram-U35MCT3I-GAMZIDYa.js} +1 -1
  50. package/out/renderer/assets/{kanban-definition-NDS4AKOZ-BvVJZaiy.js → kanban-definition-NDS4AKOZ-ij90M1gN.js} +1 -1
  51. package/out/renderer/assets/{layout-DCACUDHF.js → layout-DqWzcUI9.js} +1 -1
  52. package/out/renderer/assets/{legacy-agents-page-ccL-99ea.js → legacy-agents-page-D96D0ThJ.js} +4 -4
  53. package/out/renderer/assets/{library-page-C8ymyVUW.js → library-page-BtkHYaRw.js} +3 -3
  54. package/out/renderer/assets/{mermaid.core-BXcegHmP.js → mermaid.core-B9fSy9q8.js} +5 -5
  55. package/out/renderer/assets/{mesh-page-Dn77Ie1F.js → mesh-page-BKS4b5nA.js} +1 -1
  56. package/out/renderer/assets/{mindmap-definition-ALO5MXBD-Bx_gU6ka.js → mindmap-definition-ALO5MXBD-CnOp0ZCp.js} +1 -1
  57. package/out/renderer/assets/{mini-DRt4PdyG.js → mini-BL0qywTp.js} +1 -1
  58. package/out/renderer/assets/{notebook-pen-CKr2WSrK.js → notebook-pen-DQs4gt50.js} +1 -1
  59. package/out/renderer/assets/{org-access-CfGyU5hi.js → org-access-DvoJr_O7.js} +1 -1
  60. package/out/renderer/assets/{org-surface-gate-C86b758Z.js → org-surface-gate-kYD-nQnh.js} +1 -1
  61. package/out/renderer/assets/{page-header-CZcbRwLy.js → page-header-BY6LFXsV.js} +1 -1
  62. package/out/renderer/assets/{pieDiagram-IB7DONF6-jEzs31M9.js → pieDiagram-IB7DONF6-DkbkzE3D.js} +1 -1
  63. package/out/renderer/assets/{projects-page-xuLYnWBj.js → projects-page-BLQzs3uM.js} +1 -1
  64. package/out/renderer/assets/{publication-validation-CCv7pQJH.js → publication-validation-4m4ljeE_.js} +1 -1
  65. package/out/renderer/assets/{quadrantDiagram-7GDLP6J5-C07DLJe_.js → quadrantDiagram-7GDLP6J5-Dfe4cbbn.js} +1 -1
  66. package/out/renderer/assets/{radar-MK3ICKWK-DFqDL3G4.js → radar-MK3ICKWK-BdbftlUx.js} +1 -1
  67. package/out/renderer/assets/{radient-auth-buttons-Huoo_kFK.js → radient-auth-buttons-DngU_dyA.js} +1 -1
  68. package/out/renderer/assets/{requirementDiagram-KVF5MWMF-DZVSGDrD.js → requirementDiagram-KVF5MWMF-PKSI_Jbh.js} +1 -1
  69. package/out/renderer/assets/{sankeyDiagram-QLVOVGJD-Efyu9UfS.js → sankeyDiagram-QLVOVGJD-Bon1uUIU.js} +1 -1
  70. package/out/renderer/assets/{schedules-page-vsfnEcLJ.js → schedules-page-DW0Ae9De.js} +1 -1
  71. package/out/renderer/assets/{sequenceDiagram-X6HHIX6F-C5QQ4cH3.js → sequenceDiagram-X6HHIX6F-Bnoc1VB7.js} +1 -1
  72. package/out/renderer/assets/{settings-page-CnBnFo-w.js → settings-page-Djdtg7c4.js} +1 -1
  73. package/out/renderer/assets/{smartart-D5HiGUV-.js → smartart-CIIex-5K.js} +1 -1
  74. package/out/renderer/assets/{stateDiagram-DGXRK772-DlvSEVs4.js → stateDiagram-DGXRK772-CGmFZKBF.js} +1 -1
  75. package/out/renderer/assets/stateDiagram-v2-YXO3MK2T-C7mh141a.js +1 -0
  76. package/out/renderer/assets/team-bridge-page-B1jfvoLr.js +11 -0
  77. package/out/renderer/assets/{timeline-definition-BDJGKUSR-XgOjVv3O.js → timeline-definition-BDJGKUSR-B3KzRgdo.js} +1 -1
  78. package/out/renderer/assets/ui-preferences-store-YyZ50qSr.js +1 -0
  79. package/out/renderer/assets/{use-download-agent-mutation-BZhcL_Dx.js → use-download-agent-mutation-CGnvP-dC.js} +1 -1
  80. package/out/renderer/assets/{use-session-snapshot-BiiULlsj.js → use-session-snapshot-B0KS_Jw_.js} +1 -1
  81. package/out/renderer/assets/{xychartDiagram-VJFVF3MP-B0CsPCbY.js → xychartDiagram-VJFVF3MP-BEcciMcG.js} +1 -1
  82. package/out/renderer/browser-address-overlay-list.html +35 -0
  83. package/out/renderer/browser-address-overlay.html +43 -0
  84. package/out/renderer/console-capture.html +5 -5
  85. package/out/renderer/index.html +7 -7
  86. package/out/renderer/installer.html +4 -4
  87. package/out/renderer/mini.html +7 -7
  88. package/package.json +8 -3
  89. package/out/renderer/assets/browser-page-Daga5nCP.js +0 -1
  90. package/out/renderer/assets/channel-BqEnrEwi.js +0 -1
  91. package/out/renderer/assets/classDiagram-GIVACNV2-DNUqXdeF.js +0 -1
  92. package/out/renderer/assets/classDiagram-v2-COTLJTTW-DNUqXdeF.js +0 -1
  93. package/out/renderer/assets/clone-_PvxPvQz.js +0 -1
  94. package/out/renderer/assets/index-BZYkm39z.js +0 -825
  95. package/out/renderer/assets/index-CL2MVLxd.css +0 -1
  96. package/out/renderer/assets/index-N0lyyVVo.js +0 -57
  97. package/out/renderer/assets/stateDiagram-v2-YXO3MK2T-CCLt0TcZ.js +0 -1
  98. package/out/renderer/assets/team-bridge-page-JRqkQ8jV.js +0 -11
  99. 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: false,
14960
- launcherPid: null,
14961
- reason: `window mode ${input.mode} is not launcher-bound: a person can close it`
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 rect = cropToWindow(
26069
+ const rect2 = cropToWindow(
26007
26070
  report.contentRect,
26008
26071
  window.getContentBounds()
26009
26072
  );
26010
- const png = await this.captureWithRetry(window, rect);
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, rect) {
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(rect);
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(rect, bounds) {
27015
- const x = Math.max(Math.floor(rect.x), 0);
27016
- const y = Math.max(Math.floor(rect.y), 0);
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(rect.width), bounds.width - x), 1),
27021
- height: Math.max(Math.min(Math.floor(rect.height), bounds.height - y), 1)
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 rect = report.contentRect;
27791
- if (!rect || typeof rect !== "object") {
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 = rect[key];
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 hub",
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 hub",
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
- constructor(viewFactory, onRemove, onChanged) {
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(rect) {
63248
- this.contentRect = rect;
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(rect) {
65437
- this.registry.setContentRect(rect);
65438
- if (rect === null) this.clearPageFocus();
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, rect) => {
68449
+ electron.ipcMain.handle("browser-set-content-rect", (event, rect2) => {
66609
68450
  const host = authorize(event);
66610
- return host.setContentRect(contentRectOrNull(rect));
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 rect = value;
68649
+ const rect2 = value;
66773
68650
  for (const key of ["x", "y", "width", "height"]) {
66774
- if (typeof rect[key] !== "number" || !Number.isFinite(rect[key])) {
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: rect.x,
66780
- y: rect.y,
66781
- width: rect.width,
66782
- height: rect.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.send("browser-state-changed");
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.send("browser-consent-changed");
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 (windowLaunch.mode === "headless") {
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