local-operator-ui 0.36.7 → 0.36.9

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 (103) hide show
  1. package/out/main/index.js +874 -30
  2. package/out/preload/index.js +42 -1
  3. package/out/renderer/assets/{_basePickBy-Cp4-k2Ra.js → _basePickBy-DMcJ3S7k.js} +1 -1
  4. package/out/renderer/assets/{_baseUniq-SW_cXbV_.js → _baseUniq-D1lM95PP.js} +1 -1
  5. package/out/renderer/assets/{agent-details-page-N3zxLs5x.js → agent-details-page-BtlkMdIG.js} +2 -2
  6. package/out/renderer/assets/{agent-hub-page-BlZWarvE.js → agent-hub-page-CvnosPnb.js} +2 -2
  7. package/out/renderer/assets/{agents-page-C7c-PG8v.js → agents-page-C3p0casO.js} +3 -3
  8. package/out/renderer/assets/{arc-CH4PDipB.js → arc-DsWPhFjP.js} +1 -1
  9. package/out/renderer/assets/{architectureDiagram-IEHRJDOE-YJo8Ggxb.js → architectureDiagram-IEHRJDOE-DNfaqYw7.js} +1 -1
  10. package/out/renderer/assets/{arrow-up-BB9CxXVA.js → arrow-up-Begz0H6P.js} +1 -1
  11. package/out/renderer/assets/{blockDiagram-JOT3LUYC-rOqQnp0E.js → blockDiagram-JOT3LUYC-BJENelAS.js} +1 -1
  12. package/out/renderer/assets/{book-open-DkVxCJ61.js → book-open-5BRzSc1Z.js} +1 -1
  13. package/out/renderer/assets/browser-page-Dwy1BXTz.js +1 -0
  14. package/out/renderer/assets/{browser-webauthn-prompt-Dna4BiM9.js → browser-webauthn-prompt-yiiGM34k.js} +1 -1
  15. package/out/renderer/assets/{building-2-CavXM_-r.js → building-2-T3OwRXqF.js} +1 -1
  16. package/out/renderer/assets/{c4Diagram-VJAJSXHY-DEPLadlT.js → c4Diagram-VJAJSXHY-CEijvBuX.js} +1 -1
  17. package/out/renderer/assets/channel-BuIK1RR0.js +1 -0
  18. package/out/renderer/assets/{chunk-4BMEZGHF-klJwtJH4.js → chunk-4BMEZGHF-CtdEc_y-.js} +1 -1
  19. package/out/renderer/assets/{chunk-A2AXSNBT-DRT8OSGB.js → chunk-A2AXSNBT-DzhnEf46.js} +1 -1
  20. package/out/renderer/assets/{chunk-AEK57VVT-kqK_zO-a.js → chunk-AEK57VVT-BNPXglh5.js} +1 -1
  21. package/out/renderer/assets/{chunk-D6G4REZN-CJZaq5OR.js → chunk-D6G4REZN-xyBvlSTD.js} +1 -1
  22. package/out/renderer/assets/{chunk-RZ5BOZE2-D4tpPnZf.js → chunk-RZ5BOZE2-8dmoMKQZ.js} +1 -1
  23. package/out/renderer/assets/{chunk-XZIHB7SX-D4Kppo6Q.js → chunk-XZIHB7SX-DLfyXOIE.js} +1 -1
  24. package/out/renderer/assets/classDiagram-GIVACNV2-CcLuC1It.js +1 -0
  25. package/out/renderer/assets/classDiagram-v2-COTLJTTW-CcLuC1It.js +1 -0
  26. package/out/renderer/assets/clone-C-B3TV1U.js +1 -0
  27. package/out/renderer/assets/{console-mirror-BOyWikJU.js → console-mirror-Ddv0LHdM.js} +1 -1
  28. package/out/renderer/assets/{consoleCapture-Ctr4ZgwO.js → consoleCapture-Cm0PxzNK.js} +1 -1
  29. package/out/renderer/assets/{conversation-input-sync-DdFRR8Sk.js → conversation-input-sync-i6ilfEnc.js} +271 -271
  30. package/out/renderer/assets/{dagre-OKDRZEBW-ZzTMDgQF.js → dagre-OKDRZEBW-Dob_U_wx.js} +1 -1
  31. package/out/renderer/assets/{diagram-SSKATNLV-D-OEK7di.js → diagram-SSKATNLV-Dh8_PC2b.js} +1 -1
  32. package/out/renderer/assets/{diagram-VNBRO52H-CM6lMapK.js → diagram-VNBRO52H-NZ9qo5oG.js} +1 -1
  33. package/out/renderer/assets/{erDiagram-Q7BY3M3F-2khnybxo.js → erDiagram-Q7BY3M3F-BOUqITNj.js} +1 -1
  34. package/out/renderer/assets/{error-boundary-B2fmvLyi.js → error-boundary-LwhkYl0Z.js} +2 -2
  35. package/out/renderer/assets/{flowDiagram-4HSFHLVR-PmXhc4g8.js → flowDiagram-4HSFHLVR-CZiUXOWe.js} +1 -1
  36. package/out/renderer/assets/{ganttDiagram-APWFNJXF-DXqSjI8D.js → ganttDiagram-APWFNJXF-DqweVTOi.js} +1 -1
  37. package/out/renderer/assets/{gitGraphDiagram-7IBYFJ6S-CE0xYGI6.js → gitGraphDiagram-7IBYFJ6S-KbIhBWBs.js} +1 -1
  38. package/out/renderer/assets/{graph-ydqZFY1l.js → graph-O8aHYPDC.js} +1 -1
  39. package/out/renderer/assets/{hash-Dc3JifaZ.js → hash-WYoE1Khd.js} +1 -1
  40. package/out/renderer/assets/index-6crVx9hx.js +57 -0
  41. package/out/renderer/assets/index-D4j9Ycfd.js +7 -0
  42. package/out/renderer/assets/index-D8fns19J.js +1 -0
  43. package/out/renderer/assets/index-O8V3Lal4.css +1 -0
  44. package/out/renderer/assets/index-Re5k_KqO.js +824 -0
  45. package/out/renderer/assets/{infoDiagram-PH2N3AL5-DOZ3-Qro.js → infoDiagram-PH2N3AL5-DU0dbmt7.js} +1 -1
  46. package/out/renderer/assets/{installer-DWToCmOf.js → installer-C-rmt1Yr.js} +1 -1
  47. package/out/renderer/assets/{journeyDiagram-U35MCT3I-DWis6Sd2.js → journeyDiagram-U35MCT3I-DUybkPWl.js} +1 -1
  48. package/out/renderer/assets/{kanban-definition-NDS4AKOZ-B9e7wrf9.js → kanban-definition-NDS4AKOZ-onKwvi0l.js} +1 -1
  49. package/out/renderer/assets/{layout-CAcs3Q5o.js → layout-RKbPA5d2.js} +1 -1
  50. package/out/renderer/assets/{legacy-agents-page-DrGkO870.js → legacy-agents-page-DmUlGbZ_.js} +5 -5
  51. package/out/renderer/assets/{library-page-J-oYtewu.js → library-page-CsUF7SOa.js} +3 -3
  52. package/out/renderer/assets/{mermaid.core-BKDqOsvz.js → mermaid.core-DdYe4AYl.js} +5 -5
  53. package/out/renderer/assets/mesh-page-CW4tQHZJ.js +1 -0
  54. package/out/renderer/assets/{mindmap-definition-ALO5MXBD-B8ubJP7r.js → mindmap-definition-ALO5MXBD-DrsGVqDZ.js} +1 -1
  55. package/out/renderer/assets/{mini-CSnAsFlT.js → mini-CHtVBEDV.js} +1 -1
  56. package/out/renderer/assets/{notebook-pen-eF7NwmC0.js → notebook-pen-BHmA9Aor.js} +1 -1
  57. package/out/renderer/assets/{org-access-CJDIOfTA.js → org-access-Bqdb8uRz.js} +1 -1
  58. package/out/renderer/assets/{org-surface-gate-BFLSYyz1.js → org-surface-gate-UD-oXlvB.js} +1 -1
  59. package/out/renderer/assets/{page-header-BMDtYfV0.js → page-header-CoSSp6Ks.js} +1 -1
  60. package/out/renderer/assets/{pieDiagram-IB7DONF6-C4Lho4yQ.js → pieDiagram-IB7DONF6-C0gYyuJA.js} +1 -1
  61. package/out/renderer/assets/projects-page-DU4wZXNY.js +45 -0
  62. package/out/renderer/assets/publication-validation-D4Bef-1h.js +1 -0
  63. package/out/renderer/assets/{quadrantDiagram-7GDLP6J5-DauXMmcp.js → quadrantDiagram-7GDLP6J5-DbEt_1Ti.js} +1 -1
  64. package/out/renderer/assets/{radar-MK3ICKWK-_FJ6NjKi.js → radar-MK3ICKWK-BCsS7AKJ.js} +1 -1
  65. package/out/renderer/assets/{radient-auth-buttons-B4LrTMwy.js → radient-auth-buttons-vBcfzZdx.js} +1 -1
  66. package/out/renderer/assets/{requirementDiagram-KVF5MWMF-BBMnxRwM.js → requirementDiagram-KVF5MWMF-C3WoUU_2.js} +1 -1
  67. package/out/renderer/assets/{sankeyDiagram-QLVOVGJD-ZAgJNulV.js → sankeyDiagram-QLVOVGJD-vE9T4I2u.js} +1 -1
  68. package/out/renderer/assets/{schedules-page-BDxSAX-g.js → schedules-page-CGCopxbo.js} +1 -1
  69. package/out/renderer/assets/{sequenceDiagram-X6HHIX6F-ixhBbax-.js → sequenceDiagram-X6HHIX6F-CqwLiiP3.js} +1 -1
  70. package/out/renderer/assets/settings-page-DrZcB4mR.js +287 -0
  71. package/out/renderer/assets/{smartart-CsYIjueA.js → smartart-C6SIDcio.js} +1 -1
  72. package/out/renderer/assets/{stateDiagram-DGXRK772-Cyo1D1sd.js → stateDiagram-DGXRK772-W9r7i0O7.js} +1 -1
  73. package/out/renderer/assets/stateDiagram-v2-YXO3MK2T-cX4iVUJF.js +1 -0
  74. package/out/renderer/assets/{tags-input-chips-DeSDZmGf.js → tags-input-chips-CqW7e8j3.js} +1 -1
  75. package/out/renderer/assets/team-bridge-page-Dt4lEU3y.js +11 -0
  76. package/out/renderer/assets/{timeline-definition-BDJGKUSR-dHKNC0_-.js → timeline-definition-BDJGKUSR-QYcBKPg2.js} +1 -1
  77. package/out/renderer/assets/ui-preferences-store-BWYRkg79.js +1 -0
  78. package/out/renderer/assets/{use-download-agent-mutation-Du8Okjj5.js → use-download-agent-mutation-CNw_prc9.js} +5 -5
  79. package/out/renderer/assets/{use-session-snapshot-hoogT7-L.js → use-session-snapshot-CzQdg29b.js} +1 -1
  80. package/out/renderer/assets/{xychartDiagram-VJFVF3MP-y9FbutsK.js → xychartDiagram-VJFVF3MP-B37S1SAD.js} +1 -1
  81. package/out/renderer/console-capture.html +5 -5
  82. package/out/renderer/index.html +7 -7
  83. package/out/renderer/installer.html +4 -4
  84. package/out/renderer/mini.html +7 -7
  85. package/package.json +3 -3
  86. package/out/renderer/assets/browser-page-Dr5j9bQE.js +0 -1
  87. package/out/renderer/assets/channel-BUsgjri2.js +0 -1
  88. package/out/renderer/assets/classDiagram-GIVACNV2-YCOFP_Sl.js +0 -1
  89. package/out/renderer/assets/classDiagram-v2-COTLJTTW-YCOFP_Sl.js +0 -1
  90. package/out/renderer/assets/clone-DVoefRFN.js +0 -1
  91. package/out/renderer/assets/index-BHi1lUGF.js +0 -1
  92. package/out/renderer/assets/index-BoPP-Dip.js +0 -7
  93. package/out/renderer/assets/index-DY17TKlm.js +0 -824
  94. package/out/renderer/assets/index-Doo51Mbi.js +0 -57
  95. package/out/renderer/assets/index-JreDTE2U.css +0 -1
  96. package/out/renderer/assets/mesh-page-CHZ2qVtJ.js +0 -1
  97. package/out/renderer/assets/projects-page-CNUJUnDM.js +0 -40
  98. package/out/renderer/assets/publication-validation-5oR6aluM.js +0 -1
  99. package/out/renderer/assets/settings-page-DpSl6AZf.js +0 -287
  100. package/out/renderer/assets/stateDiagram-v2-YXO3MK2T-BylkQbLa.js +0 -1
  101. package/out/renderer/assets/team-bridge-page-BnsNoiEd.js +0 -1
  102. package/out/renderer/assets/ui-preferences-store-CFbBU1td.js +0 -1
  103. package/out/renderer/assets/use-memberships-query-C0ico4nv.js +0 -1
package/out/main/index.js CHANGED
@@ -2384,6 +2384,28 @@ const desktopRequestUnion = zod.z.discriminatedUnion("op", [
2384
2384
  conversation_id: id$1,
2385
2385
  action: zod.z.enum(["archive", "unarchive", "join", "leave"])
2386
2386
  }).strict(),
2387
+ /*
2388
+ * Self-join and the notification preference are their own ops rather than
2389
+ * `lifecycle` actions: the tenant is part of the request (a merge-view row
2390
+ * can name a channel the caller has not joined, and the default tenant
2391
+ * would be the wrong organization), and `notify` is a value, not an
2392
+ * action. The join route is the conversation domain's existing
2393
+ * `POST .../join`; the tenant travels in its body for the same reason the
2394
+ * create/send/read bodies carry one. The notify op rides the READ route's
2395
+ * `notify` field (agent-server #148's shape): it sends no `seq`, so the
2396
+ * cursor cannot move as a side effect of choosing a notification level.
2397
+ */
2398
+ zod.z.object({
2399
+ op: zod.z.literal("team_bridge.conversations.join"),
2400
+ conversation_id: id$1,
2401
+ tenant_id: id$1.optional()
2402
+ }).strict(),
2403
+ zod.z.object({
2404
+ op: zod.z.literal("team_bridge.conversations.notify"),
2405
+ conversation_id: id$1,
2406
+ tenant_id: id$1.optional(),
2407
+ notify: zod.z.enum(["all", "mention", "mute"])
2408
+ }).strict(),
2387
2409
  zod.z.object({
2388
2410
  op: zod.z.literal("team_bridge.conversations.members.add"),
2389
2411
  conversation_id: id$1,
@@ -4861,6 +4883,23 @@ function desktopEndpoint(request) {
4861
4883
  path: `/v1/desktop/team-bridge/conversations/${encodeURIComponent(request.conversation_id)}/${request.action}`,
4862
4884
  method: "POST"
4863
4885
  };
4886
+ case "team_bridge.conversations.join":
4887
+ return {
4888
+ path: `/v1/desktop/team-bridge/conversations/${encodeURIComponent(request.conversation_id)}/join`,
4889
+ method: "POST",
4890
+ ...request.tenant_id ? { body: { tenant_id: request.tenant_id } } : {}
4891
+ };
4892
+ case "team_bridge.conversations.notify":
4893
+ return {
4894
+ /* The read route's notify field (agent-server #148); no `seq`, so
4895
+ this write cannot advance the cursor. */
4896
+ path: `/v1/desktop/team-bridge/conversations/${encodeURIComponent(request.conversation_id)}/read`,
4897
+ method: "POST",
4898
+ body: {
4899
+ ...request.tenant_id ? { tenant_id: request.tenant_id } : {},
4900
+ notify: request.notify
4901
+ }
4902
+ };
4864
4903
  case "team_bridge.conversations.members.add": {
4865
4904
  const query = new URLSearchParams();
4866
4905
  if (request.tenant_id) query.set("tenant_id", request.tenant_id);
@@ -9070,7 +9109,7 @@ const DESKTOP_STREAM_DETAIL = {
9070
9109
  refusedWithoutStatus: "The event stream was refused."
9071
9110
  };
9072
9111
  const SESSION_ID$1 = /^[a-f0-9]{12}$/;
9073
- const SAFE_ID = /^[a-zA-Z0-9_-]{1,128}$/;
9112
+ const SAFE_ID$1 = /^[a-zA-Z0-9_-]{1,128}$/;
9074
9113
  function transportFailureDetail(error) {
9075
9114
  if (error instanceof Error && error.name === "AbortError") {
9076
9115
  return DESKTOP_STREAM_DETAIL.ended;
@@ -9117,7 +9156,7 @@ class DesktopStreamRelay {
9117
9156
  if (!SESSION_ID$1.test(args.sessionId)) {
9118
9157
  throw new Error("Invalid session for streaming.");
9119
9158
  }
9120
- if (args.epoch !== void 0 && !SAFE_ID.test(args.epoch)) {
9159
+ if (args.epoch !== void 0 && !SAFE_ID$1.test(args.epoch)) {
9121
9160
  throw new Error("Invalid stream epoch.");
9122
9161
  }
9123
9162
  if (args.afterSeq !== void 0 && (!Number.isInteger(args.afterSeq) || args.afterSeq < 0)) {
@@ -29452,15 +29491,31 @@ class ApprovalStore {
29452
29491
  );
29453
29492
  }
29454
29493
  }
29494
+ function browserWindowForeground(window) {
29495
+ return !window.isDestroyed() && window.isVisible() && window.isFocused();
29496
+ }
29455
29497
  function browserChromeOwnsChord(facts) {
29456
- return facts.logicalFocus && facts.chromeVisible && !facts.window.isDestroyed() && facts.window.isVisible() && facts.window.isFocused();
29498
+ return facts.logicalFocus && facts.chromeVisible && browserWindowForeground(facts.window);
29499
+ }
29500
+ function browserPageOwnsChord(facts) {
29501
+ const active = facts.activeTab;
29502
+ return Boolean(
29503
+ active?.presented && browserWindowForeground(facts.window) && (facts.focusedContents() === active.webContentsId || facts.focusedViewTabId === active.tabId)
29504
+ );
29457
29505
  }
29458
29506
  function runBrowserMenuAction(intent, target2, owner, route) {
29507
+ if (target2 && target2 !== owner) {
29508
+ if (intent === "reload") target2.webContents?.reload();
29509
+ else if (intent === "hard-reload")
29510
+ target2.webContents?.reloadIgnoringCache();
29511
+ else if (intent === "close-tab") target2.close();
29512
+ return;
29513
+ }
29514
+ if (route(intent)) return;
29459
29515
  if (!target2) return;
29460
- if (target2 === owner && route(intent)) return;
29461
29516
  if (intent === "reload") target2.webContents?.reload();
29462
29517
  else if (intent === "hard-reload") target2.webContents?.reloadIgnoringCache();
29463
- else target2.close();
29518
+ else if (intent === "close-tab") target2.close();
29464
29519
  }
29465
29520
  function createBrowserChordRouter(options) {
29466
29521
  function dispatch(intent, origin) {
@@ -33259,6 +33314,12 @@ const enUiBrowser = {
33259
33314
  "ui.browser.browser_tab_strip.unmute_tab": "Unmute tab",
33260
33315
  "ui.browser.browser_tab_strip.watch": 'Watch "{title}"',
33261
33316
  "ui.browser.browser_url_bar.address": "Address",
33317
+ "ui.browser.browser_url_bar.address_required": "Enter a web address or choose a suggestion.",
33318
+ "ui.browser.browser_url_bar.address_suggestions_available": "{count, plural, one {{count, number, ::group-off} suggestion available} other {{count, number, ::group-off} suggestions available}}",
33319
+ "ui.browser.browser_url_bar.address_suggestions_empty": "No links from these tabs yet.",
33320
+ "ui.browser.browser_url_bar.address_suggestions_label": "From this browser session",
33321
+ "ui.browser.browser_url_bar.address_suggestions_no_match": "No matching links from this browser session.",
33322
+ "ui.browser.browser_url_bar.address_suggestions_unavailable": "Address suggestions are unavailable.",
33262
33323
  "ui.browser.browser_url_bar.approvals_waiting": "Approvals, {count, plural, other {{count, number, ::group-off} waiting}}",
33263
33324
  "ui.browser.browser_url_bar.back": "Back",
33264
33325
  "ui.browser.browser_url_bar.back_shortcut": "Back ({shortcut})",
@@ -33524,7 +33585,26 @@ const enUiChatTags = {
33524
33585
  "ui.chat.tags.move_undone": "Tag move undone",
33525
33586
  "ui.chat.tags.move_title": "Move “{title}”",
33526
33587
  "ui.chat.tags.move_description": "Choose a tag to make it primary, or Untagged to remove all tags.",
33527
- "ui.chat.tags.move_failed": "Tags were not moved. Try again."
33588
+ "ui.chat.tags.move_failed": "Tags were not moved. Try again.",
33589
+ "ui.chat.tags.selection_count": "{count} of 8",
33590
+ "ui.chat.tags.registry_count": "{count} of 32",
33591
+ "ui.chat.tags.create_name": "Create “{name}”",
33592
+ "ui.chat.tags.create_helper": "Saved for all conversations",
33593
+ "ui.chat.tags.selection_limit": "Choose up to 8 tags",
33594
+ "ui.chat.tags.registry_limit": "You can have up to 32 tags",
33595
+ "ui.chat.tags.name_long": "Use 1–24 characters",
33596
+ "ui.chat.tags.name_reserved": "“Untagged” is reserved",
33597
+ "ui.chat.tags.create_failed": "Tag was not created. Try again.",
33598
+ "ui.chat.tags.remove_named": "Remove {name}",
33599
+ "ui.chat.tags.reorder_filtered": "Clear search to reorder tags",
33600
+ "ui.chat.tags.created_name": "Created “{name}”",
33601
+ "ui.chat.tags.selection_status": "{count} selected. Group under {name}.",
33602
+ "ui.chat.tags.tag_usage": "{count, plural, one {# conversation} other {# conversations}}",
33603
+ "ui.chat.tags.create_pending": "Saving tag…",
33604
+ "ui.chat.tags.remote": "Tags cannot be changed on a remote conversation.",
33605
+ "ui.chat.tags.renamed_name": "Renamed to “{name}”",
33606
+ "ui.chat.tags.staged_added": "Added “{name}” to draft",
33607
+ "ui.chat.tags.registry_changed": "Some tags changed elsewhere. Review your selection."
33528
33608
  };
33529
33609
  const enUiChatTranscriptCopy = {
33530
33610
  "ui.chat.transcript_copy.menu.copy_text": "Copy text",
@@ -33534,7 +33614,8 @@ const enUiChatTranscriptCopy = {
33534
33614
  "ui.chat.transcript_copy.toast.copied_selection": "Copied selection",
33535
33615
  "ui.chat.transcript_copy.row.your_message": "Your message",
33536
33616
  "ui.chat.transcript_copy.row.agent_message": "Agent message",
33537
- "ui.chat.transcript_copy.selection.copy": "Copy selection"
33617
+ "ui.chat.transcript_copy.selection.copy": "Copy selection",
33618
+ "ui.chat.transcript_copy.selection.toolbar": "Selection actions"
33538
33619
  };
33539
33620
  const enUiCodeReview = {
33540
33621
  "ui.code_review.activity.close": "closed",
@@ -35031,10 +35112,18 @@ const enUiProjects = {
35031
35112
  "ui.projects.project_todos.no_to_do_snapshots_from_the_linked_sessions_yet_a_session_publish": "No to-do snapshots from the linked sessions yet. A session publishes its list as it works, and it appears here once it has.",
35032
35113
  "ui.projects.project_todos.to_dos": "To-dos",
35033
35114
  "ui.projects.project_updates.attachment": "Attachment",
35115
+ "ui.projects.project_updates.attachments": "Attachments",
35034
35116
  "ui.projects.project_updates.file_actions_for_name": "File actions for {name}",
35035
35117
  "ui.projects.project_updates.loading": "Loading ",
35036
35118
  "ui.projects.project_updates.no_updates_yet_progress_reported_by_you_or_by_the_sessions_workin": "No updates yet. Progress reported by you or by the sessions working on this project is appended here.",
35037
35119
  "ui.projects.project_updates.open_name": "Open {name}",
35120
+ "ui.projects.project_updates.play_name": "Play {name}",
35121
+ "ui.projects.project_updates.preview_failed": "This file could not be opened.",
35122
+ "ui.projects.project_updates.preview_loading": "Loading preview…",
35123
+ "ui.projects.project_updates.preview_moved": "Its stored copy may have moved or was deleted.",
35124
+ "ui.projects.project_updates.preview_unavailable": "Preview is unavailable outside the desktop app.",
35125
+ "ui.projects.project_updates.preview_name": "Preview {name}",
35126
+ "ui.projects.project_updates.show_asset_name": "Show {name}, asset {index} of {count}",
35038
35127
  "ui.projects.project_updates.updates": "Updates",
35039
35128
  "ui.projects.projects_page.checking_the_server_s_projects_support": "Checking the server's Projects support",
35040
35129
  "ui.projects.projects_page.clear_all": "Clear all",
@@ -36212,7 +36301,23 @@ const enUiTeamBridge = {
36212
36301
  "ui.teamBridge.scope.unknown": "Unknown organization",
36213
36302
  "ui.teamBridge.scope.thread_unknown": "This conversation's organization is not available.",
36214
36303
  "ui.teamBridge.scope.create_needs_org": "Choose an organization to start a conversation.",
36215
- "ui.teamBridge.group.more_unread": "{count, plural, one {More destinations, # unread message} other {More destinations, # unread messages}}"
36304
+ "ui.teamBridge.group.more_unread": "{count, plural, one {More destinations, # unread message} other {More destinations, # unread messages}}",
36305
+ "ui.teamBridge.preview.chip": "Preview",
36306
+ "ui.teamBridge.preview.banner": "You’re previewing this channel. Join to see its history and get notifications.",
36307
+ "ui.teamBridge.preview.join": "Join",
36308
+ "ui.teamBridge.preview.join_error": "You could not join this channel. Try again.",
36309
+ "ui.teamBridge.menu.label": "Conversation options",
36310
+ "ui.teamBridge.menu.mute": "Mute conversation",
36311
+ "ui.teamBridge.menu.unmute": "Unmute conversation",
36312
+ "ui.teamBridge.notify.label": "Notification preferences",
36313
+ "ui.teamBridge.notify.all": "All messages",
36314
+ "ui.teamBridge.notify.mention": "Mentions",
36315
+ "ui.teamBridge.notify.mute": "Mute",
36316
+ "ui.teamBridge.notify.muted": "Muted",
36317
+ "ui.teamBridge.notify.error": "Your notification preference was not saved. Try again.",
36318
+ "ui.teamBridge.notify.title": "{conversation} ({organization})",
36319
+ "ui.teamBridge.notify.body": "{sender} sent a message",
36320
+ "ui.teamBridge.notify.body_fallback": "New message"
36216
36321
  };
36217
36322
  const esUiAgentMessages = {
36218
36323
  "ui.agentMessages.dialog.title": "Mensajes de agentes en Radient",
@@ -36627,6 +36732,12 @@ const esUiBrowser = {
36627
36732
  "ui.browser.browser_url_bar.stop_loading": "Detener carga",
36628
36733
  "ui.browser.browser_url_bar.reload": "Recargar",
36629
36734
  "ui.browser.browser_url_bar.address": "Dirección",
36735
+ "ui.browser.browser_url_bar.address_required": "Escribe una dirección web o elige una sugerencia.",
36736
+ "ui.browser.browser_url_bar.address_suggestions_available": "{count, plural, one {{count, number, ::group-off} sugerencia disponible} other {{count, number, ::group-off} sugerencias disponibles}}",
36737
+ "ui.browser.browser_url_bar.address_suggestions_empty": "Aún no hay enlaces de estas pestañas.",
36738
+ "ui.browser.browser_url_bar.address_suggestions_label": "De esta sesión del navegador",
36739
+ "ui.browser.browser_url_bar.address_suggestions_no_match": "No hay enlaces que coincidan en esta sesión del navegador.",
36740
+ "ui.browser.browser_url_bar.address_suggestions_unavailable": "Las sugerencias de direcciones no están disponibles.",
36630
36741
  "ui.browser.browser_url_bar.approvals_waiting": "Aprobaciones, {count, plural, other {{count, number, ::group-off} en espera}}",
36631
36742
  "ui.browser.browser_url_bar.home_destination": "Inicio — ir a la página inicial (en blanco)",
36632
36743
  "ui.browser.browser_url_bar.site_unverified": "No se pudo verificar la conexión con {origin}",
@@ -36892,7 +37003,26 @@ const esUiChatTags = {
36892
37003
  "ui.chat.tags.move_undone": "Movimiento de etiquetas deshecho",
36893
37004
  "ui.chat.tags.move_title": "Mover «{title}»",
36894
37005
  "ui.chat.tags.move_description": "Elige una etiqueta para hacerla principal, o Sin etiqueta para eliminar todas las etiquetas.",
36895
- "ui.chat.tags.move_failed": "No se han movido las etiquetas. Inténtalo de nuevo."
37006
+ "ui.chat.tags.move_failed": "No se han movido las etiquetas. Inténtalo de nuevo.",
37007
+ "ui.chat.tags.selection_count": "{count} de 8",
37008
+ "ui.chat.tags.registry_count": "{count} de 32",
37009
+ "ui.chat.tags.create_name": "Crear «{name}»",
37010
+ "ui.chat.tags.create_helper": "Se guarda para todas las conversaciones",
37011
+ "ui.chat.tags.selection_limit": "Elige hasta 8 etiquetas",
37012
+ "ui.chat.tags.registry_limit": "Puedes tener hasta 32 etiquetas",
37013
+ "ui.chat.tags.name_long": "Usa entre 1 y 24 caracteres",
37014
+ "ui.chat.tags.name_reserved": "«Untagged» es un nombre reservado",
37015
+ "ui.chat.tags.create_failed": "No se ha creado la etiqueta. Inténtalo de nuevo.",
37016
+ "ui.chat.tags.remove_named": "Quitar {name}",
37017
+ "ui.chat.tags.reorder_filtered": "Borra la búsqueda para reordenar las etiquetas",
37018
+ "ui.chat.tags.created_name": "Se ha creado «{name}»",
37019
+ "ui.chat.tags.selection_status": "Selección: {count}. Agrupar en {name}.",
37020
+ "ui.chat.tags.tag_usage": "{count, plural, one {# conversación} many {# de conversaciones} other {# conversaciones}}",
37021
+ "ui.chat.tags.create_pending": "Guardando etiqueta…",
37022
+ "ui.chat.tags.remote": "No se pueden cambiar las etiquetas de una conversación remota.",
37023
+ "ui.chat.tags.renamed_name": "Se ha cambiado el nombre a «{name}»",
37024
+ "ui.chat.tags.staged_added": "Se ha añadido «{name}» al borrador",
37025
+ "ui.chat.tags.registry_changed": "Algunas etiquetas han cambiado en otro lugar. Revisa la selección."
36896
37026
  };
36897
37027
  const esUiCodeReview = {
36898
37028
  "ui.code_review.activity.close": "cerraste",
@@ -38224,10 +38354,18 @@ const esUiProjects = {
38224
38354
  "ui.projects.project_todos.no_to_do_snapshots_from_the_linked_sessions_yet_a_session_publish": "Aún no hay instantáneas de tareas pendientes de las sesiones vinculadas. Una sesión publica su lista a medida que trabaja, y aparecerá aquí cuando lo haga.",
38225
38355
  "ui.projects.project_todos.to_dos": "Tareas pendientes",
38226
38356
  "ui.projects.project_updates.attachment": "Adjunto",
38357
+ "ui.projects.project_updates.attachments": "Adjuntos",
38227
38358
  "ui.projects.project_updates.file_actions_for_name": "Acciones de archivo para {name}",
38228
38359
  "ui.projects.project_updates.loading": "Cargando ",
38229
38360
  "ui.projects.project_updates.no_updates_yet_progress_reported_by_you_or_by_the_sessions_workin": "Aún no hay actualizaciones. El progreso que informes tú o las sesiones que trabajan en este proyecto se añade aquí.",
38230
38361
  "ui.projects.project_updates.open_name": "Abrir {name}",
38362
+ "ui.projects.project_updates.play_name": "Reproducir {name}",
38363
+ "ui.projects.project_updates.preview_failed": "No se pudo abrir este archivo.",
38364
+ "ui.projects.project_updates.preview_loading": "Cargando vista previa…",
38365
+ "ui.projects.project_updates.preview_moved": "Su copia guardada puede haberse movido o eliminado.",
38366
+ "ui.projects.project_updates.preview_name": "Vista previa de {name}",
38367
+ "ui.projects.project_updates.preview_unavailable": "La vista previa no está disponible fuera de la aplicación de escritorio.",
38368
+ "ui.projects.project_updates.show_asset_name": "Mostrar {name}, recurso {index} de {count}",
38231
38369
  "ui.projects.project_updates.updates": "Actualizaciones",
38232
38370
  "ui.projects.projects_page.checking_the_server_s_projects_support": "Comprobando la compatibilidad del servidor con Proyectos",
38233
38371
  "ui.projects.projects_page.clear_all": "Borrar todo",
@@ -39767,6 +39905,12 @@ const frUiBrowser = {
39767
39905
  "ui.browser.browser_url_bar.stop_loading": "Arrêter le chargement",
39768
39906
  "ui.browser.browser_url_bar.reload": "Recharger",
39769
39907
  "ui.browser.browser_url_bar.address": "Adresse",
39908
+ "ui.browser.browser_url_bar.address_required": "Saisissez une adresse web ou choisissez une suggestion.",
39909
+ "ui.browser.browser_url_bar.address_suggestions_available": "{count, plural, one {{count, number, ::group-off} suggestion disponible} many {{count, number, ::group-off} de suggestions disponibles} other {{count, number, ::group-off} suggestions disponibles}}",
39910
+ "ui.browser.browser_url_bar.address_suggestions_empty": "Aucun lien de ces onglets pour l’instant.",
39911
+ "ui.browser.browser_url_bar.address_suggestions_label": "De cette session de navigation",
39912
+ "ui.browser.browser_url_bar.address_suggestions_no_match": "Aucun lien correspondant dans cette session de navigation.",
39913
+ "ui.browser.browser_url_bar.address_suggestions_unavailable": "Les suggestions d’adresses ne sont pas disponibles.",
39770
39914
  "ui.browser.browser_url_bar.approvals_waiting": "Approbations, {count, plural, one {{count, number, ::group-off} en attente} many {{count, number, ::group-off} en attente} other {{count, number, ::group-off} en attente}}",
39771
39915
  "ui.browser.browser_url_bar.home_destination": "Accueil — aller à la page de départ (vide)",
39772
39916
  "ui.browser.browser_url_bar.site_unverified": "La connexion à {origin} n’a pas pu être vérifiée",
@@ -40032,7 +40176,26 @@ const frUiChatTags = {
40032
40176
  "ui.chat.tags.move_undone": "Déplacement des étiquettes annulé",
40033
40177
  "ui.chat.tags.move_title": "Déplacer « {title} »",
40034
40178
  "ui.chat.tags.move_description": "Choisissez une étiquette principale, ou Sans étiquette pour retirer toutes les étiquettes.",
40035
- "ui.chat.tags.move_failed": "Les étiquettes n’ont pas été déplacées. Réessayez."
40179
+ "ui.chat.tags.move_failed": "Les étiquettes n’ont pas été déplacées. Réessayez.",
40180
+ "ui.chat.tags.selection_count": "{count} sur 8",
40181
+ "ui.chat.tags.registry_count": "{count} sur 32",
40182
+ "ui.chat.tags.create_name": "Créer « {name} »",
40183
+ "ui.chat.tags.create_helper": "Enregistré pour toutes les conversations",
40184
+ "ui.chat.tags.selection_limit": "Choisissez jusqu’à 8 étiquettes",
40185
+ "ui.chat.tags.registry_limit": "Vous pouvez avoir jusqu’à 32 étiquettes",
40186
+ "ui.chat.tags.name_long": "Utilisez de 1 à 24 caractères",
40187
+ "ui.chat.tags.name_reserved": "« Untagged » est un nom réservé",
40188
+ "ui.chat.tags.create_failed": "L’étiquette n’a pas été créée. Réessayez.",
40189
+ "ui.chat.tags.remove_named": "Retirer {name}",
40190
+ "ui.chat.tags.reorder_filtered": "Effacez la recherche pour réordonner les étiquettes",
40191
+ "ui.chat.tags.created_name": "« {name} » a été créée",
40192
+ "ui.chat.tags.selection_status": "{count} sélectionnées. Regrouper sous {name}.",
40193
+ "ui.chat.tags.tag_usage": "{count, plural, one {# conversation} many {# de conversations} other {# conversations}}",
40194
+ "ui.chat.tags.create_pending": "Enregistrement de l’étiquette…",
40195
+ "ui.chat.tags.remote": "Les étiquettes d’une conversation distante ne peuvent pas être modifiées.",
40196
+ "ui.chat.tags.renamed_name": "Renommée « {name} »",
40197
+ "ui.chat.tags.staged_added": "« {name} » a été ajoutée au brouillon",
40198
+ "ui.chat.tags.registry_changed": "Des étiquettes ont été modifiées ailleurs. Vérifiez votre sélection."
40036
40199
  };
40037
40200
  const frUiCodeReview = {
40038
40201
  "ui.code_review.activity.close": "avez fermé",
@@ -41364,10 +41527,18 @@ const frUiProjects = {
41364
41527
  "ui.projects.project_todos.no_to_do_snapshots_from_the_linked_sessions_yet_a_session_publish": "Aucun instantané de tâches provenant des sessions liées pour l’instant. Une session publie sa liste au fil du travail, et elle apparaît ici dès qu’elle l’a fait.",
41365
41528
  "ui.projects.project_todos.to_dos": "Tâches",
41366
41529
  "ui.projects.project_updates.attachment": "Pièce jointe",
41530
+ "ui.projects.project_updates.attachments": "Pièces jointes",
41367
41531
  "ui.projects.project_updates.file_actions_for_name": "Actions de fichier pour {name}",
41368
41532
  "ui.projects.project_updates.loading": "Chargement ",
41369
41533
  "ui.projects.project_updates.no_updates_yet_progress_reported_by_you_or_by_the_sessions_workin": "Aucune mise à jour pour l’instant. Les progrès signalés par vous ou par les sessions qui travaillent sur ce projet sont ajoutés ici.",
41370
41534
  "ui.projects.project_updates.open_name": "Ouvrir {name}",
41535
+ "ui.projects.project_updates.play_name": "Lire {name}",
41536
+ "ui.projects.project_updates.preview_failed": "Impossible d’ouvrir ce fichier.",
41537
+ "ui.projects.project_updates.preview_loading": "Chargement de l’aperçu…",
41538
+ "ui.projects.project_updates.preview_moved": "Sa copie enregistrée a peut-être été déplacée ou supprimée.",
41539
+ "ui.projects.project_updates.preview_name": "Aperçu de {name}",
41540
+ "ui.projects.project_updates.preview_unavailable": "L’aperçu n’est pas disponible en dehors de l’application de bureau.",
41541
+ "ui.projects.project_updates.show_asset_name": "Afficher {name}, élément {index} sur {count}",
41371
41542
  "ui.projects.project_updates.updates": "Mises à jour",
41372
41543
  "ui.projects.projects_page.checking_the_server_s_projects_support": "Vérification de la prise en charge des projets par le serveur",
41373
41544
  "ui.projects.projects_page.clear_all": "Tout effacer",
@@ -42994,6 +43165,12 @@ const hiUiBrowser = {
42994
43165
  "ui.browser.browser_tab_strip.unmute_tab": "टैब की आवाज़ चालू करें",
42995
43166
  "ui.browser.browser_tab_strip.watch": '"{title}" देखें',
42996
43167
  "ui.browser.browser_url_bar.address": "पता",
43168
+ "ui.browser.browser_url_bar.address_required": "वेब पता दर्ज करें या कोई सुझाव चुनें।",
43169
+ "ui.browser.browser_url_bar.address_suggestions_available": "{count, plural, one {{count, number, ::group-off} सुझाव उपलब्ध है} other {{count, number, ::group-off} सुझाव उपलब्ध हैं}}",
43170
+ "ui.browser.browser_url_bar.address_suggestions_empty": "इन टैब से अभी कोई लिंक नहीं है।",
43171
+ "ui.browser.browser_url_bar.address_suggestions_label": "इस ब्राउज़र सत्र से",
43172
+ "ui.browser.browser_url_bar.address_suggestions_no_match": "इस ब्राउज़र सत्र में कोई मेल खाता लिंक नहीं है।",
43173
+ "ui.browser.browser_url_bar.address_suggestions_unavailable": "पते के सुझाव उपलब्ध नहीं हैं।",
42997
43174
  "ui.browser.browser_url_bar.approvals_waiting": "स्वीकृतियाँ, {count, plural, other {{count, number, ::group-off} इंतज़ार में}}",
42998
43175
  "ui.browser.browser_url_bar.back": "पीछे",
42999
43176
  "ui.browser.browser_url_bar.back_shortcut": "पीछे ({shortcut})",
@@ -43259,7 +43436,26 @@ const hiUiChatTags = {
43259
43436
  "ui.chat.tags.move_undone": "टैग का ले जाना पूर्ववत किया गया",
43260
43437
  "ui.chat.tags.move_title": "“{title}” ले जाएँ",
43261
43438
  "ui.chat.tags.move_description": "किसी टैग को प्राथमिक बनाने के लिए उसे चुनें, या सभी टैग हटाने के लिए “बिना टैग” चुनें।",
43262
- "ui.chat.tags.move_failed": "टैग नहीं ले जाए गए। फिर कोशिश करें।"
43439
+ "ui.chat.tags.move_failed": "टैग नहीं ले जाए गए। फिर कोशिश करें।",
43440
+ "ui.chat.tags.selection_count": "8 में से {count}",
43441
+ "ui.chat.tags.registry_count": "32 में से {count}",
43442
+ "ui.chat.tags.create_name": "“{name}” बनाएँ",
43443
+ "ui.chat.tags.create_helper": "सभी बातचीतों के लिए सहेजा जाता है",
43444
+ "ui.chat.tags.selection_limit": "अधिकतम 8 टैग चुनें",
43445
+ "ui.chat.tags.registry_limit": "आप अधिकतम 32 टैग रख सकते हैं",
43446
+ "ui.chat.tags.name_long": "1–24 अक्षर इस्तेमाल करें",
43447
+ "ui.chat.tags.name_reserved": "“Untagged” नाम आरक्षित है",
43448
+ "ui.chat.tags.create_failed": "टैग नहीं बनाया गया। फिर कोशिश करें।",
43449
+ "ui.chat.tags.remove_named": "{name} हटाएँ",
43450
+ "ui.chat.tags.reorder_filtered": "टैग का क्रम बदलने के लिए खोज साफ़ करें",
43451
+ "ui.chat.tags.created_name": "“{name}” बनाया गया",
43452
+ "ui.chat.tags.selection_status": "{count, plural, =1 {# चुना गया} other {# चुने गए}}। {name} के तहत समूह बनाएँ।",
43453
+ "ui.chat.tags.tag_usage": "{count} बातचीत",
43454
+ "ui.chat.tags.create_pending": "टैग सहेजा जा रहा है…",
43455
+ "ui.chat.tags.remote": "दूरस्थ बातचीत के टैग नहीं बदले जा सकते।",
43456
+ "ui.chat.tags.renamed_name": "नाम बदलकर “{name}” किया गया",
43457
+ "ui.chat.tags.staged_added": "“{name}” मसौदे में जोड़ा गया",
43458
+ "ui.chat.tags.registry_changed": "कुछ टैग कहीं और बदले गए। अपना चयन जाँचें।"
43263
43459
  };
43264
43460
  const hiUiCodeReview = {
43265
43461
  "ui.code_review.activity.close": "बंद किया",
@@ -44725,10 +44921,18 @@ const hiUiProjects = {
44725
44921
  "ui.projects.project_todos.no_to_do_snapshots_from_the_linked_sessions_yet_a_session_publish": "जुड़े सत्रों से अभी कार्य सूची का कोई स्नैपशॉट नहीं है। काम करते समय सत्र अपनी सूची प्रकाशित करता है, जिसके बाद वह यहाँ दिखती है।",
44726
44922
  "ui.projects.project_todos.to_dos": "कार्य सूची",
44727
44923
  "ui.projects.project_updates.attachment": "अटैचमेंट",
44924
+ "ui.projects.project_updates.attachments": "अनुलग्नक",
44728
44925
  "ui.projects.project_updates.file_actions_for_name": "{name} की फ़ाइल कार्रवाइयाँ",
44729
44926
  "ui.projects.project_updates.loading": "लोड हो रहा है ",
44730
44927
  "ui.projects.project_updates.no_updates_yet_progress_reported_by_you_or_by_the_sessions_workin": "अभी कोई अपडेट नहीं है। आपकी या इस प्रोजेक्ट पर काम कर रहे सत्रों की बताई गई प्रगति यहाँ जोड़ी जाती है।",
44731
44928
  "ui.projects.project_updates.open_name": "{name} खोलें",
44929
+ "ui.projects.project_updates.play_name": "{name} चलाएँ",
44930
+ "ui.projects.project_updates.preview_failed": "यह फ़ाइल खोली नहीं जा सकी।",
44931
+ "ui.projects.project_updates.preview_loading": "पूर्वावलोकन लोड हो रहा है…",
44932
+ "ui.projects.project_updates.preview_moved": "इसकी संग्रहीत प्रतिलिपि स्थानांतरित या हटा दी गई हो सकती है।",
44933
+ "ui.projects.project_updates.preview_name": "{name} का पूर्वावलोकन",
44934
+ "ui.projects.project_updates.preview_unavailable": "डेस्कटॉप ऐप के बाहर पूर्वावलोकन उपलब्ध नहीं है।",
44935
+ "ui.projects.project_updates.show_asset_name": "{name} दिखाएँ, संपत्ति {index} / {count}",
44732
44936
  "ui.projects.project_updates.updates": "अपडेट",
44733
44937
  "ui.projects.projects_page.checking_the_server_s_projects_support": "सर्वर पर प्रोजेक्ट सपोर्ट जाँचा जा रहा है",
44734
44938
  "ui.projects.projects_page.clear_all": "सभी साफ़ करें",
@@ -46269,6 +46473,12 @@ const ruUiBrowser = {
46269
46473
  "ui.browser.browser_tab_strip.unmute_tab": "Включить звук вкладки",
46270
46474
  "ui.browser.browser_tab_strip.watch": "Смотреть «{title}»",
46271
46475
  "ui.browser.browser_url_bar.address": "Адрес",
46476
+ "ui.browser.browser_url_bar.address_required": "Введите веб-адрес или выберите подсказку.",
46477
+ "ui.browser.browser_url_bar.address_suggestions_available": "{count, plural, one {{count, number, ::group-off} подсказка доступна} few {{count, number, ::group-off} подсказки доступны} many {{count, number, ::group-off} подсказок доступно} other {{count, number, ::group-off} подсказки доступны}}",
46478
+ "ui.browser.browser_url_bar.address_suggestions_empty": "В этих вкладках пока нет ссылок.",
46479
+ "ui.browser.browser_url_bar.address_suggestions_label": "Из этой сессии браузера",
46480
+ "ui.browser.browser_url_bar.address_suggestions_no_match": "В этой сессии браузера нет подходящих ссылок.",
46481
+ "ui.browser.browser_url_bar.address_suggestions_unavailable": "Подсказки адресов недоступны.",
46272
46482
  "ui.browser.browser_url_bar.approvals_waiting": "Одобрения, {count, plural, one {ожидает {count, number, ::group-off}} few {ожидают {count, number, ::group-off}} many {ожидают {count, number, ::group-off}} other {ожидают {count, number, ::group-off}}}",
46273
46483
  "ui.browser.browser_url_bar.back": "Назад",
46274
46484
  "ui.browser.browser_url_bar.back_shortcut": "Назад ({shortcut})",
@@ -46534,7 +46744,26 @@ const ruUiChatTags = {
46534
46744
  "ui.chat.tags.move_undone": "Перемещение тега отменено",
46535
46745
  "ui.chat.tags.move_title": "Переместить «{title}»",
46536
46746
  "ui.chat.tags.move_description": "Выберите основной тег или «Без тега», чтобы удалить все теги.",
46537
- "ui.chat.tags.move_failed": "Не удалось переместить теги. Попробуйте ещё раз."
46747
+ "ui.chat.tags.move_failed": "Не удалось переместить теги. Попробуйте ещё раз.",
46748
+ "ui.chat.tags.selection_count": "{count} из 8",
46749
+ "ui.chat.tags.registry_count": "{count} из 32",
46750
+ "ui.chat.tags.create_name": "Создать «{name}»",
46751
+ "ui.chat.tags.create_helper": "Сохраняется для всех разговоров",
46752
+ "ui.chat.tags.selection_limit": "Выберите не более 8 тегов",
46753
+ "ui.chat.tags.registry_limit": "Можно создать не более 32 тегов",
46754
+ "ui.chat.tags.name_long": "Используйте от 1 до 24 символов",
46755
+ "ui.chat.tags.name_reserved": "Имя «Untagged» зарезервировано",
46756
+ "ui.chat.tags.create_failed": "Тег не создан. Попробуйте ещё раз.",
46757
+ "ui.chat.tags.remove_named": "Убрать {name}",
46758
+ "ui.chat.tags.reorder_filtered": "Очистите поиск, чтобы изменить порядок тегов",
46759
+ "ui.chat.tags.created_name": "Создан тег «{name}»",
46760
+ "ui.chat.tags.selection_status": "Выбрано: {count}. Основной тег: {name}.",
46761
+ "ui.chat.tags.tag_usage": "{count, plural, one {# разговор} few {# разговора} many {# разговоров} other {# разговора}}",
46762
+ "ui.chat.tags.create_pending": "Сохранение тега…",
46763
+ "ui.chat.tags.remote": "Нельзя изменить теги удалённого разговора.",
46764
+ "ui.chat.tags.renamed_name": "Новое имя: «{name}»",
46765
+ "ui.chat.tags.staged_added": "Тег «{name}» добавлен в черновик",
46766
+ "ui.chat.tags.registry_changed": "Некоторые теги изменены в другом месте. Проверьте выбор."
46538
46767
  };
46539
46768
  const ruUiCodeReview = {
46540
46769
  "ui.code_review.activity.close": "закрыли",
@@ -47866,10 +48095,18 @@ const ruUiProjects = {
47866
48095
  "ui.projects.project_todos.no_to_do_snapshots_from_the_linked_sessions_yet_a_session_publish": "Снимков списка задач от связанных сессий пока нет. Сессия публикует список по ходу работы, и он появится здесь.",
47867
48096
  "ui.projects.project_todos.to_dos": "Задачи",
47868
48097
  "ui.projects.project_updates.attachment": "Вложение",
48098
+ "ui.projects.project_updates.attachments": "Вложения",
47869
48099
  "ui.projects.project_updates.file_actions_for_name": "Действия с файлом {name}",
47870
48100
  "ui.projects.project_updates.loading": "Загрузка ",
47871
48101
  "ui.projects.project_updates.no_updates_yet_progress_reported_by_you_or_by_the_sessions_workin": "Обновлений пока нет. Прогресс, о котором сообщили вы или сессии, работающие над проектом, будет добавляться здесь.",
47872
48102
  "ui.projects.project_updates.open_name": "Открыть {name}",
48103
+ "ui.projects.project_updates.play_name": "Воспроизвести {name}",
48104
+ "ui.projects.project_updates.preview_failed": "Не удалось открыть этот файл.",
48105
+ "ui.projects.project_updates.preview_loading": "Загрузка предпросмотра…",
48106
+ "ui.projects.project_updates.preview_moved": "Возможно, сохранённая копия была перемещена или удалена.",
48107
+ "ui.projects.project_updates.preview_name": "Предпросмотр {name}",
48108
+ "ui.projects.project_updates.preview_unavailable": "Предпросмотр недоступен вне настольного приложения.",
48109
+ "ui.projects.project_updates.show_asset_name": "Показать {name}, элемент {index} из {count}",
47873
48110
  "ui.projects.project_updates.updates": "Обновления",
47874
48111
  "ui.projects.projects_page.checking_the_server_s_projects_support": "Проверка поддержки проектов на сервере",
47875
48112
  "ui.projects.projects_page.clear_all": "Сбросить всё",
@@ -49496,6 +49733,12 @@ const urUiBrowser = {
49496
49733
  "ui.browser.browser_tab_strip.unmute_tab": "ٹیب کی آواز بحال کریں",
49497
49734
  "ui.browser.browser_tab_strip.watch": "“{title}” پر نظر رکھیں",
49498
49735
  "ui.browser.browser_url_bar.address": "پتہ",
49736
+ "ui.browser.browser_url_bar.address_required": "ویب پتہ درج کریں یا کوئی تجویز منتخب کریں۔",
49737
+ "ui.browser.browser_url_bar.address_suggestions_available": "{count, plural, one {{count, number, ::group-off} تجویز دستیاب ہے} other {{count, number, ::group-off} تجاویز دستیاب ہیں}}",
49738
+ "ui.browser.browser_url_bar.address_suggestions_empty": "ان ٹیب سے ابھی کوئی لنک نہیں ہے۔",
49739
+ "ui.browser.browser_url_bar.address_suggestions_label": "اس براؤزر سیشن سے",
49740
+ "ui.browser.browser_url_bar.address_suggestions_no_match": "اس براؤزر سیشن میں کوئی مماثل لنک نہیں ہے۔",
49741
+ "ui.browser.browser_url_bar.address_suggestions_unavailable": "پتے کی تجاویز دستیاب نہیں ہیں۔",
49499
49742
  "ui.browser.browser_url_bar.approvals_waiting": "منظوریاں، {count, plural, other {{count, number, ::group-off} منتظر}}",
49500
49743
  "ui.browser.browser_url_bar.back": "پیچھے",
49501
49744
  "ui.browser.browser_url_bar.back_shortcut": "پیچھے ({shortcut})",
@@ -49761,7 +50004,26 @@ const urUiChatTags = {
49761
50004
  "ui.chat.tags.move_undone": "ٹیگ کی منتقلی واپس کر دی گئی",
49762
50005
  "ui.chat.tags.move_title": "“{title}” منتقل کریں",
49763
50006
  "ui.chat.tags.move_description": "کسی ٹیگ کو بنیادی بنانے کے لیے اسے منتخب کریں، یا تمام ٹیگز ہٹانے کے لیے “بغیر ٹیگ” منتخب کریں۔",
49764
- "ui.chat.tags.move_failed": "ٹیگز منتقل نہیں ہوئے۔ دوبارہ کوشش کریں۔"
50007
+ "ui.chat.tags.move_failed": "ٹیگز منتقل نہیں ہوئے۔ دوبارہ کوشش کریں۔",
50008
+ "ui.chat.tags.selection_count": "8 میں سے {count}",
50009
+ "ui.chat.tags.registry_count": "32 میں سے {count}",
50010
+ "ui.chat.tags.create_name": "“{name}” بنائیں",
50011
+ "ui.chat.tags.create_helper": "تمام گفتگوؤں کے لیے محفوظ",
50012
+ "ui.chat.tags.selection_limit": "زیادہ سے زیادہ 8 ٹیگز منتخب کریں",
50013
+ "ui.chat.tags.registry_limit": "آپ زیادہ سے زیادہ 32 ٹیگز رکھ سکتے ہیں",
50014
+ "ui.chat.tags.name_long": "1–24 حروف استعمال کریں",
50015
+ "ui.chat.tags.name_reserved": "“Untagged” نام مخصوص ہے",
50016
+ "ui.chat.tags.create_failed": "ٹیگ نہیں بنا۔ دوبارہ کوشش کریں۔",
50017
+ "ui.chat.tags.remove_named": "{name} ہٹائیں",
50018
+ "ui.chat.tags.reorder_filtered": "ٹیگز کی ترتیب بدلنے کے لیے تلاش صاف کریں",
50019
+ "ui.chat.tags.created_name": "“{name}” بنا دیا گیا",
50020
+ "ui.chat.tags.selection_status": "منتخب: {count}۔ {name} کے گروہ میں رکھیں۔",
50021
+ "ui.chat.tags.tag_usage": "{count, plural, one {# گفتگو} other {# گفتگوئیں}}",
50022
+ "ui.chat.tags.create_pending": "ٹیگ محفوظ ہو رہا ہے…",
50023
+ "ui.chat.tags.remote": "ریموٹ گفتگو کے ٹیگز تبدیل نہیں کیے جا سکتے۔",
50024
+ "ui.chat.tags.renamed_name": "نام بدل کر “{name}” کر دیا گیا",
50025
+ "ui.chat.tags.staged_added": "“{name}” مسودے میں شامل کر دیا گیا",
50026
+ "ui.chat.tags.registry_changed": "کچھ ٹیگز کہیں اور تبدیل ہو گئے ہیں۔ اپنے انتخاب کا جائزہ لیں۔"
49765
50027
  };
49766
50028
  const urUiCodeReview = {
49767
50029
  "ui.code_review.activity.close": "بند کیا",
@@ -51247,10 +51509,18 @@ const urUiProjects = {
51247
51509
  "ui.projects.project_todos.no_to_do_snapshots_from_the_linked_sessions_yet_a_session_publish": "ابھی منسلک سیشن سے کاموں کی فہرست کا کوئی عکس نہیں آیا۔ سیشن کام کرتے ہوئے اپنی فہرست شائع کرتا ہے اور تب یہ یہاں نظر آتی ہے۔",
51248
51510
  "ui.projects.project_todos.to_dos": "کاموں کی فہرست",
51249
51511
  "ui.projects.project_updates.attachment": "منسلکہ",
51512
+ "ui.projects.project_updates.attachments": "منسلکات",
51250
51513
  "ui.projects.project_updates.file_actions_for_name": "{name} فائل کے اقدامات",
51251
51514
  "ui.projects.project_updates.loading": "لوڈ ہو رہا ہے ",
51252
51515
  "ui.projects.project_updates.no_updates_yet_progress_reported_by_you_or_by_the_sessions_workin": "ابھی کوئی اپ ڈیٹ نہیں ہے۔ آپ یا اس پروجیکٹ پر کام کرنے والے سیشن کی رپورٹ کردہ پیش رفت یہاں شامل ہوتی ہے۔",
51253
51516
  "ui.projects.project_updates.open_name": "{name} کھولیں",
51517
+ "ui.projects.project_updates.play_name": "{name} چلائیں",
51518
+ "ui.projects.project_updates.preview_failed": "یہ فائل کھولی نہیں جا سکی۔",
51519
+ "ui.projects.project_updates.preview_loading": "پیش منظر لوڈ ہو رہا ہے…",
51520
+ "ui.projects.project_updates.preview_moved": "اس کی محفوظ کاپی منتقل یا حذف ہو گئی ہو سکتی ہے۔",
51521
+ "ui.projects.project_updates.preview_name": "{name} کا پیش منظر",
51522
+ "ui.projects.project_updates.preview_unavailable": "ڈیسک ٹاپ ایپ سے باہر پیش منظر دستیاب نہیں ہے۔",
51523
+ "ui.projects.project_updates.show_asset_name": "{name} دکھائیں، اثاثہ {index} از {count}",
51254
51524
  "ui.projects.project_updates.updates": "اپ ڈیٹس",
51255
51525
  "ui.projects.projects_page.checking_the_server_s_projects_support": "سرور میں پروجیکٹس کی سہولت جانچی جا رہی ہے",
51256
51526
  "ui.projects.projects_page.clear_all": "سب صاف کریں",
@@ -52791,6 +53061,12 @@ const viUiBrowser = {
52791
53061
  "ui.browser.browser_tab_strip.unmute_tab": "Bật tiếng tab",
52792
53062
  "ui.browser.browser_tab_strip.watch": 'Theo dõi "{title}"',
52793
53063
  "ui.browser.browser_url_bar.address": "Địa chỉ",
53064
+ "ui.browser.browser_url_bar.address_required": "Nhập địa chỉ web hoặc chọn một gợi ý.",
53065
+ "ui.browser.browser_url_bar.address_suggestions_available": "{count, plural, other {{count, number, ::group-off} gợi ý có sẵn}}",
53066
+ "ui.browser.browser_url_bar.address_suggestions_empty": "Chưa có liên kết nào từ các tab này.",
53067
+ "ui.browser.browser_url_bar.address_suggestions_label": "Từ phiên trình duyệt này",
53068
+ "ui.browser.browser_url_bar.address_suggestions_no_match": "Không có liên kết phù hợp từ phiên trình duyệt này.",
53069
+ "ui.browser.browser_url_bar.address_suggestions_unavailable": "Gợi ý địa chỉ không khả dụng.",
52794
53070
  "ui.browser.browser_url_bar.approvals_waiting": "Phê duyệt, {count, plural, other {{count, number, ::group-off} đang chờ}}",
52795
53071
  "ui.browser.browser_url_bar.back": "Quay lại",
52796
53072
  "ui.browser.browser_url_bar.back_shortcut": "Quay lại ({shortcut})",
@@ -53056,7 +53332,26 @@ const viUiChatTags = {
53056
53332
  "ui.chat.tags.move_undone": "Đã hoàn tác thao tác chuyển nhãn",
53057
53333
  "ui.chat.tags.move_title": "Chuyển “{title}”",
53058
53334
  "ui.chat.tags.move_description": "Chọn một nhãn làm nhãn chính, hoặc chọn Chưa gắn nhãn để gỡ hết nhãn.",
53059
- "ui.chat.tags.move_failed": "Chưa chuyển được nhãn. Hãy thử lại."
53335
+ "ui.chat.tags.move_failed": "Chưa chuyển được nhãn. Hãy thử lại.",
53336
+ "ui.chat.tags.selection_count": "{count} trên 8",
53337
+ "ui.chat.tags.registry_count": "{count} trên 32",
53338
+ "ui.chat.tags.create_name": "Tạo “{name}”",
53339
+ "ui.chat.tags.create_helper": "Được lưu cho mọi cuộc trò chuyện",
53340
+ "ui.chat.tags.selection_limit": "Chọn tối đa 8 nhãn",
53341
+ "ui.chat.tags.registry_limit": "Bạn có thể có tối đa 32 nhãn",
53342
+ "ui.chat.tags.name_long": "Dùng từ 1 đến 24 ký tự",
53343
+ "ui.chat.tags.name_reserved": "“Untagged” là tên dành riêng",
53344
+ "ui.chat.tags.create_failed": "Chưa tạo được nhãn. Hãy thử lại.",
53345
+ "ui.chat.tags.remove_named": "Bỏ {name}",
53346
+ "ui.chat.tags.reorder_filtered": "Xóa tìm kiếm để sắp xếp lại nhãn",
53347
+ "ui.chat.tags.created_name": "Đã tạo “{name}”",
53348
+ "ui.chat.tags.selection_status": "Đã chọn {count}. Nhóm vào {name}.",
53349
+ "ui.chat.tags.tag_usage": "{count} cuộc trò chuyện",
53350
+ "ui.chat.tags.create_pending": "Đang lưu nhãn…",
53351
+ "ui.chat.tags.remote": "Không thể thay đổi nhãn của cuộc trò chuyện từ xa.",
53352
+ "ui.chat.tags.renamed_name": "Đã đổi tên thành “{name}”",
53353
+ "ui.chat.tags.staged_added": "Đã thêm “{name}” vào bản nháp",
53354
+ "ui.chat.tags.registry_changed": "Một số nhãn đã thay đổi ở nơi khác. Hãy kiểm tra lựa chọn."
53060
53355
  };
53061
53356
  const viUiCodeReview = {
53062
53357
  "ui.code_review.activity.close": "đã đóng",
@@ -54388,10 +54683,18 @@ const viUiProjects = {
54388
54683
  "ui.projects.project_todos.no_to_do_snapshots_from_the_linked_sessions_yet_a_session_publish": "Chưa có bản chụp danh sách việc cần làm từ các phiên liên kết. Phiên sẽ công bố danh sách khi làm việc và danh sách sẽ xuất hiện ở đây.",
54389
54684
  "ui.projects.project_todos.to_dos": "Việc cần làm",
54390
54685
  "ui.projects.project_updates.attachment": "Tệp đính kèm",
54686
+ "ui.projects.project_updates.attachments": "Tệp đính kèm",
54391
54687
  "ui.projects.project_updates.file_actions_for_name": "Thao tác tệp cho {name}",
54392
54688
  "ui.projects.project_updates.loading": "Đang tải ",
54393
54689
  "ui.projects.project_updates.no_updates_yet_progress_reported_by_you_or_by_the_sessions_workin": "Chưa có cập nhật. Tiến độ do bạn hoặc các phiên đang thực hiện dự án báo cáo sẽ được thêm vào đây.",
54394
54690
  "ui.projects.project_updates.open_name": "Mở {name}",
54691
+ "ui.projects.project_updates.play_name": "Phát {name}",
54692
+ "ui.projects.project_updates.preview_failed": "Không thể mở tệp này.",
54693
+ "ui.projects.project_updates.preview_loading": "Đang tải bản xem trước…",
54694
+ "ui.projects.project_updates.preview_moved": "Bản sao đã lưu của tệp có thể đã bị di chuyển hoặc xóa.",
54695
+ "ui.projects.project_updates.preview_name": "Xem trước {name}",
54696
+ "ui.projects.project_updates.preview_unavailable": "Bản xem trước không khả dụng ngoài ứng dụng máy tính.",
54697
+ "ui.projects.project_updates.show_asset_name": "Hiển thị {name}, tài sản {index} trên {count}",
54395
54698
  "ui.projects.project_updates.updates": "Cập nhật",
54396
54699
  "ui.projects.projects_page.checking_the_server_s_projects_support": "Đang kiểm tra khả năng hỗ trợ Dự án của máy chủ",
54397
54700
  "ui.projects.projects_page.clear_all": "Xóa tất cả",
@@ -55741,6 +56044,12 @@ const zhCNUiBrowser = {
55741
56044
  "ui.browser.browser_tab_strip.unmute_tab": "取消标签页静音",
55742
56045
  "ui.browser.browser_tab_strip.watch": "查看“{title}”",
55743
56046
  "ui.browser.browser_url_bar.address": "地址",
56047
+ "ui.browser.browser_url_bar.address_required": "请输入地址或选择一条建议。",
56048
+ "ui.browser.browser_url_bar.address_suggestions_available": "{count, plural, other {{count, number, ::group-off}条建议可用}}",
56049
+ "ui.browser.browser_url_bar.address_suggestions_empty": "这些标签页中还没有链接。",
56050
+ "ui.browser.browser_url_bar.address_suggestions_label": "来自此浏览器会话",
56051
+ "ui.browser.browser_url_bar.address_suggestions_no_match": "此浏览器会话中没有匹配的链接。",
56052
+ "ui.browser.browser_url_bar.address_suggestions_unavailable": "地址建议不可用。",
55744
56053
  "ui.browser.browser_url_bar.approvals_waiting": "审批,{count, plural, other {{count, number, ::group-off}项待处理}}",
55745
56054
  "ui.browser.browser_url_bar.back": "后退",
55746
56055
  "ui.browser.browser_url_bar.back_shortcut": "后退({shortcut})",
@@ -56006,7 +56315,26 @@ const zhCNUiChatTags = {
56006
56315
  "ui.chat.tags.move_undone": "已撤销标签移动",
56007
56316
  "ui.chat.tags.move_title": "移动“{title}”",
56008
56317
  "ui.chat.tags.move_description": "选择一个标签将其设为主标签,或选择“无标签”以移除所有标签。",
56009
- "ui.chat.tags.move_failed": "标签未移动。请重试。"
56318
+ "ui.chat.tags.move_failed": "标签未移动。请重试。",
56319
+ "ui.chat.tags.selection_count": "已选 {count}/8",
56320
+ "ui.chat.tags.registry_count": "{count}/32",
56321
+ "ui.chat.tags.create_name": "创建“{name}”",
56322
+ "ui.chat.tags.create_helper": "保存后可用于所有对话",
56323
+ "ui.chat.tags.selection_limit": "最多选择 8 个标签",
56324
+ "ui.chat.tags.registry_limit": "最多可有 32 个标签",
56325
+ "ui.chat.tags.name_long": "请使用 1–24 个字符",
56326
+ "ui.chat.tags.name_reserved": "“Untagged”是保留名称",
56327
+ "ui.chat.tags.create_failed": "未能创建标签。请重试。",
56328
+ "ui.chat.tags.remove_named": "移除{name}",
56329
+ "ui.chat.tags.reorder_filtered": "清空搜索以调整标签顺序",
56330
+ "ui.chat.tags.created_name": "已创建“{name}”",
56331
+ "ui.chat.tags.selection_status": "已选择 {count} 个,归入{name}。",
56332
+ "ui.chat.tags.tag_usage": "{count} 个对话",
56333
+ "ui.chat.tags.create_pending": "正在保存标签…",
56334
+ "ui.chat.tags.remote": "无法更改远程对话的标签。",
56335
+ "ui.chat.tags.renamed_name": "已重命名为“{name}”",
56336
+ "ui.chat.tags.staged_added": "已将“{name}”加入草稿",
56337
+ "ui.chat.tags.registry_changed": "部分标签已在别处更改。请检查所选标签。"
56010
56338
  };
56011
56339
  const zhCNUiCodeReview = {
56012
56340
  "ui.code_review.activity.close": "已关闭",
@@ -57338,10 +57666,18 @@ const zhCNUiProjects = {
57338
57666
  "ui.projects.project_todos.no_to_do_snapshots_from_the_linked_sessions_yet_a_session_publish": "关联会话尚未提供待办快照。会话在工作过程中发布待办列表后,便会显示在此处。",
57339
57667
  "ui.projects.project_todos.to_dos": "待办事项",
57340
57668
  "ui.projects.project_updates.attachment": "附件",
57669
+ "ui.projects.project_updates.attachments": "附件",
57341
57670
  "ui.projects.project_updates.file_actions_for_name": "{name}的文件操作",
57342
57671
  "ui.projects.project_updates.loading": "正在加载 ",
57343
57672
  "ui.projects.project_updates.no_updates_yet_progress_reported_by_you_or_by_the_sessions_workin": "暂无更新。你或处理此项目的会话汇报的进展会追加到此处。",
57344
57673
  "ui.projects.project_updates.open_name": "打开{name}",
57674
+ "ui.projects.project_updates.play_name": "播放 {name}",
57675
+ "ui.projects.project_updates.preview_failed": "无法打开此文件。",
57676
+ "ui.projects.project_updates.preview_loading": "正在加载预览…",
57677
+ "ui.projects.project_updates.preview_moved": "其已保存的副本可能已被移动或删除。",
57678
+ "ui.projects.project_updates.preview_name": "预览 {name}",
57679
+ "ui.projects.project_updates.preview_unavailable": "预览在桌面应用之外不可用。",
57680
+ "ui.projects.project_updates.show_asset_name": "显示 {name},第 {index} 个,共 {count} 个",
57345
57681
  "ui.projects.project_updates.updates": "更新",
57346
57682
  "ui.projects.projects_page.checking_the_server_s_projects_support": "正在检查服务器是否支持项目功能",
57347
57683
  "ui.projects.projects_page.clear_all": "清除全部",
@@ -61586,6 +61922,121 @@ function factOf(path2) {
61586
61922
  sha256: ""
61587
61923
  };
61588
61924
  }
61925
+ const ADDRESS_HISTORY_MAX_TABS = 20;
61926
+ const ADDRESS_HISTORY_MAX_ENTRIES_PER_TAB = 50;
61927
+ const ADDRESS_HISTORY_MAX_ENTRIES = 256;
61928
+ const ADDRESS_HISTORY_MAX_URL_LENGTH = 2048;
61929
+ const ADDRESS_HISTORY_MAX_TITLE_LENGTH = 256;
61930
+ const ADDRESS_HISTORY_MAX_SESSION_ID_LENGTH = 256;
61931
+ const CONTROL_CHARS$1 = /[\p{Cc}]/u;
61932
+ function windowAround(entries, activeIndexRaw, max) {
61933
+ const activeIndex = Math.min(
61934
+ Math.max(0, Number.isSafeInteger(activeIndexRaw) ? activeIndexRaw : 0),
61935
+ Math.max(0, entries.length - 1)
61936
+ );
61937
+ const start = Math.min(
61938
+ Math.max(0, activeIndex - Math.floor(max / 2)),
61939
+ Math.max(0, entries.length - max)
61940
+ );
61941
+ return {
61942
+ entries: entries.slice(start, start + max),
61943
+ activeIndex,
61944
+ startIndex: start
61945
+ };
61946
+ }
61947
+ function readStack(tab) {
61948
+ const contents = tab.webContents;
61949
+ if (!contents || contents.isDestroyed()) return null;
61950
+ const history = contents.navigationHistory;
61951
+ if (history?.getEntryAtIndex && typeof history.length === "function" && typeof history.getActiveIndex === "function") {
61952
+ try {
61953
+ const total = history.length();
61954
+ if (total > 0) {
61955
+ const activeIndex = Math.max(0, history.getActiveIndex());
61956
+ const max = ADDRESS_HISTORY_MAX_ENTRIES_PER_TAB;
61957
+ const start = Math.min(
61958
+ Math.max(0, activeIndex - Math.floor(max / 2)),
61959
+ Math.max(0, total - max)
61960
+ );
61961
+ const end = Math.min(total, start + max);
61962
+ const entries = [];
61963
+ for (let index = start; index < end; index += 1) {
61964
+ const entry = history.getEntryAtIndex(index);
61965
+ if (entry) entries.push(entry);
61966
+ }
61967
+ return {
61968
+ entries,
61969
+ activeIndex,
61970
+ startIndex: start
61971
+ };
61972
+ }
61973
+ } catch {
61974
+ }
61975
+ }
61976
+ const row = tab.restoreRow;
61977
+ if (!row || row.entries.length === 0) return null;
61978
+ return windowAround(
61979
+ row.entries,
61980
+ row.activeIndex,
61981
+ ADDRESS_HISTORY_MAX_ENTRIES_PER_TAB
61982
+ );
61983
+ }
61984
+ function canonicalHistoryUrl(raw) {
61985
+ if (typeof raw !== "string") return null;
61986
+ if (raw.length === 0 || raw.length > ADDRESS_HISTORY_MAX_URL_LENGTH)
61987
+ return null;
61988
+ if (CONTROL_CHARS$1.test(raw)) return null;
61989
+ let url;
61990
+ try {
61991
+ url = new URL(raw);
61992
+ } catch {
61993
+ return null;
61994
+ }
61995
+ if (url.protocol !== "http:" && url.protocol !== "https:") return null;
61996
+ if (url.username !== "" || url.password !== "") return null;
61997
+ if (url.href === "about:blank") return null;
61998
+ if (url.href.length > ADDRESS_HISTORY_MAX_URL_LENGTH) return null;
61999
+ return url.href;
62000
+ }
62001
+ function historyTitle(raw) {
62002
+ if (typeof raw !== "string") return "";
62003
+ const cleaned = raw.replace(CONTROL_CHARS$1, " ").replace(/\s+/g, " ").trim();
62004
+ return cleaned.length > ADDRESS_HISTORY_MAX_TITLE_LENGTH ? cleaned.slice(0, ADDRESS_HISTORY_MAX_TITLE_LENGTH) : cleaned;
62005
+ }
62006
+ function collectAddressHistory(tabs2, scope, activeTabId) {
62007
+ const inScope = scope === "all" ? [...tabs2] : tabs2.filter(
62008
+ (tab) => tab.sessionId !== null && tab.sessionId === scope.sessionId
62009
+ );
62010
+ const active = inScope.find((tab) => tab.tabId === activeTabId);
62011
+ const ordered = active ? [active, ...inScope.filter((tab) => tab.tabId !== activeTabId)] : inScope;
62012
+ const seen = /* @__PURE__ */ new Set();
62013
+ const entries = [];
62014
+ for (const tab of ordered.slice(0, ADDRESS_HISTORY_MAX_TABS)) {
62015
+ const stack = readStack(tab);
62016
+ if (!stack) continue;
62017
+ stack.entries.forEach((entry, windowIndex) => {
62018
+ const url = canonicalHistoryUrl(entry.url);
62019
+ if (!url) return;
62020
+ if (seen.has(url)) return;
62021
+ if (entries.length >= ADDRESS_HISTORY_MAX_ENTRIES) return;
62022
+ seen.add(url);
62023
+ const sourceIndex = stack.startIndex + windowIndex;
62024
+ entries.push({
62025
+ url,
62026
+ title: historyTitle(entry.title),
62027
+ sourceTabId: tab.tabId,
62028
+ sourceIndex,
62029
+ activeTab: tab.tabId === activeTabId,
62030
+ // The restore fallback's window is computed by the same policy, so this
62031
+ // distance is comparable across live and restored stacks: negative is a
62032
+ // back entry, which the absolute value deliberately discards (the
62033
+ // renderer ranks by closeness, not direction).
62034
+ distanceFromCurrent: Math.abs(sourceIndex - stack.activeIndex)
62035
+ });
62036
+ });
62037
+ }
62038
+ return { activeTabId, entries };
62039
+ }
61589
62040
  class ChromeTabOrder {
61590
62041
  ids = [];
61591
62042
  customized = false;
@@ -62518,6 +62969,15 @@ class BrowserHost {
62518
62969
  }
62519
62970
  /** Main owns this short-lived UI focus fact; it conveys no page capability. */
62520
62971
  chordFocus = false;
62972
+ /**
62973
+ * The tab whose native view last reported focus. Tracked from each view's own
62974
+ * `focus`/`blur` events because `webContents.getFocusedWebContents()` can
62975
+ * answer null while a `WebContentsView` holds focus (measured on Electron
62976
+ * 44.3.0 in a window that is not itself activated). Like `chordFocus`, this
62977
+ * is an input-ownership fact and carries no page capability; `ownsPage`
62978
+ * refuses it unless the tab is the active, presented one.
62979
+ */
62980
+ focusedPageTabId = null;
62521
62981
  onChordFocusChanged;
62522
62982
  /** An explicit tab id fences queued chrome intents: a switch between key
62523
62983
  * delivery and IPC must never search/zoom the newly active tab instead. */
@@ -62540,6 +63000,16 @@ class BrowserHost {
62540
63000
  this.chordFocus = focused;
62541
63001
  this.onChordFocusChanged?.(focused);
62542
63002
  }
63003
+ /** One view gained or lost native focus. Only the id that lost it may clear
63004
+ * the fact: a departing view's blur must not erase a successor's focus. */
63005
+ setPageFocused(tabId, focused) {
63006
+ if (focused) this.focusedPageTabId = tabId;
63007
+ else if (this.focusedPageTabId === tabId) this.focusedPageTabId = null;
63008
+ }
63009
+ /** The window's own renderer gained focus (the user clicked chrome/chat). */
63010
+ clearPageFocus() {
63011
+ this.focusedPageTabId = null;
63012
+ }
62543
63013
  /** Only user URLs survive a close, never a capability, page state or nonce.
62544
63014
  * Conversation attribution is retained only to find the appropriate scope. */
62545
63015
  closedUserTabs = [];
@@ -62558,6 +63028,7 @@ class BrowserHost {
62558
63028
  clearChordState() {
62559
63029
  this.pageControls.clear();
62560
63030
  this.setChordFocus(false);
63031
+ this.clearPageFocus();
62561
63032
  this.closedUserTabs = [];
62562
63033
  }
62563
63034
  /**
@@ -62849,6 +63320,41 @@ class BrowserHost {
62849
63320
  this.onChanged();
62850
63321
  return this.chromeState();
62851
63322
  }
63323
+ /**
63324
+ * Local address suggestions for the field the user is editing.
63325
+ *
63326
+ * THE EXPECTED TAB IS A GUARD, not a hint: a response about tab A rendered
63327
+ * over tab B is how a stale completion navigates a tab the user is no longer
63328
+ * looking at. Main answers the read only when the request still names the
63329
+ * active tab; the renderer separately discards responses whose `activeTabId`
63330
+ * no longer matches, which covers the race between this check and the reply
63331
+ * landing.
63332
+ *
63333
+ * Collection is bounded and derived (`address-history.ts`); this method adds
63334
+ * only the authority the collector must not pretend to own.
63335
+ */
63336
+ addressHistory(request) {
63337
+ const activeTabId = this.registry.activeTab?.tabId ?? null;
63338
+ if (activeTabId !== request.expectedTabId) {
63339
+ throw new BridgeCommandError(
63340
+ "nav_failed",
63341
+ "the active tab changed before suggestions were read"
63342
+ );
63343
+ }
63344
+ return {
63345
+ requestId: request.requestId,
63346
+ ...collectAddressHistory(
63347
+ this.registry.list().map((record) => ({
63348
+ tabId: record.tabId,
63349
+ sessionId: record.sessionId,
63350
+ webContents: record.view.webContents,
63351
+ restoreRow: record.restoreRow ?? null
63352
+ })),
63353
+ request.scope,
63354
+ activeTabId
63355
+ )
63356
+ };
63357
+ }
62852
63358
  /**
62853
63359
  * A URL the USER typed goes through here, and it is deliberately different from
62854
63360
  * an agent navigation in one way and identical in another (design 6.1):
@@ -62861,10 +63367,16 @@ class BrowserHost {
62861
63367
  * `file:` or `javascript:` here would hand a page (or a paste) a way to reach
62862
63368
  * the local filesystem through the app's own URL bar.
62863
63369
  */
62864
- async navigateActive(rawUrl) {
63370
+ async navigateActive(rawUrl, expectedTabId) {
62865
63371
  const record = this.registry.activeTab;
62866
63372
  if (!record)
62867
63373
  throw new BridgeCommandError("tab_closed", "no browser tab is open");
63374
+ if (expectedTabId !== void 0 && record.tabId !== expectedTabId) {
63375
+ throw new BridgeCommandError(
63376
+ "nav_failed",
63377
+ "the active tab changed before the address was submitted"
63378
+ );
63379
+ }
62868
63380
  let url;
62869
63381
  try {
62870
63382
  url = new URL(rawUrl.includes("://") ? rawUrl : `https://${rawUrl}`);
@@ -62948,12 +63460,14 @@ class BrowserHost {
62948
63460
  * area and reports it, and main applies it to the active tab only. */
62949
63461
  setContentRect(rect) {
62950
63462
  this.registry.setContentRect(rect);
63463
+ if (rect === null) this.clearPageFocus();
62951
63464
  return this.chromeState();
62952
63465
  }
62953
63466
  /** Full-window overlays hide the view: a native view paints above all DOM, so
62954
63467
  * an overlay is invisible unless the view goes away (design 11.3). */
62955
63468
  setViewVisible(visible) {
62956
63469
  this.registry.setViewVisible(visible);
63470
+ if (!visible) this.clearPageFocus();
62957
63471
  return this.chromeState();
62958
63472
  }
62959
63473
  /** Hand a user tab to a session, minting the capability. The RENDERER never
@@ -63927,6 +64441,18 @@ async function startWithRetries(options) {
63927
64441
  });
63928
64442
  throw failure;
63929
64443
  }
64444
+ function numberOrThrow(value, name) {
64445
+ if (typeof value !== "number" || !Number.isInteger(value)) {
64446
+ throw new Error(`${name} must be an integer.`);
64447
+ }
64448
+ return value;
64449
+ }
64450
+ function tabIdOrThrow(value, name) {
64451
+ const id2 = numberOrThrow(value, name);
64452
+ if (id2 <= 0) throw new Error(`${name} must be a positive tab id.`);
64453
+ return id2;
64454
+ }
64455
+ const CONTROL_CHARS = /[\p{Cc}]/u;
63930
64456
  const BROWSER_IPC_CHANNELS = [
63931
64457
  "browser-state",
63932
64458
  "browser-set-chord-focus",
@@ -63939,6 +64465,7 @@ const BROWSER_IPC_CHANNELS = [
63939
64465
  "browser-close-tab",
63940
64466
  "browser-close-tabs",
63941
64467
  "browser-activate-tab",
64468
+ "browser-address-history",
63942
64469
  "browser-navigate",
63943
64470
  "browser-home",
63944
64471
  "browser-reload",
@@ -64035,12 +64562,22 @@ function registerBrowserIpc(options) {
64035
64562
  const host = authorize(event);
64036
64563
  return host.activateTab(numberOrThrow(tabId, "tabId"));
64037
64564
  });
64038
- electron.ipcMain.handle("browser-navigate", async (event, url) => {
64039
- const host = authorize(event);
64040
- if (typeof url !== "string" || !url.trim()) {
64041
- throw new Error("A URL is required.");
64565
+ electron.ipcMain.handle(
64566
+ "browser-navigate",
64567
+ async (event, url, expectedTabId) => {
64568
+ const host = authorize(event);
64569
+ if (typeof url !== "string" || !url.trim()) {
64570
+ throw new Error("A URL is required.");
64571
+ }
64572
+ return host.navigateActive(
64573
+ url.trim(),
64574
+ expectedTabId === void 0 || expectedTabId === null ? void 0 : tabIdOrThrow(expectedTabId, "expectedTabId")
64575
+ );
64042
64576
  }
64043
- return host.navigateActive(url.trim());
64577
+ );
64578
+ electron.ipcMain.handle("browser-address-history", (event, request) => {
64579
+ const host = authorize(event);
64580
+ return host.addressHistory(addressHistoryRequestOrThrow(request));
64044
64581
  });
64045
64582
  electron.ipcMain.handle(
64046
64583
  "browser-page-control",
@@ -64198,11 +64735,28 @@ function registerBrowserIpc(options) {
64198
64735
  function unregisterBrowserIpc() {
64199
64736
  for (const channel of BROWSER_IPC_CHANNELS) electron.ipcMain.removeHandler(channel);
64200
64737
  }
64201
- function numberOrThrow(value, name) {
64202
- if (typeof value !== "number" || !Number.isInteger(value)) {
64203
- throw new Error(`${name} must be an integer.`);
64738
+ function addressHistoryRequestOrThrow(value) {
64739
+ if (!value || typeof value !== "object") {
64740
+ throw new Error("An address history request is required.");
64741
+ }
64742
+ const request = value;
64743
+ const requestId2 = numberOrThrow(request.requestId, "requestId");
64744
+ const expectedTabId = tabIdOrThrow(request.expectedTabId, "expectedTabId");
64745
+ let scope;
64746
+ if (request.scope === "all") {
64747
+ scope = "all";
64748
+ } else if (request.scope && typeof request.scope === "object" && typeof request.scope.sessionId === "string") {
64749
+ const sessionId2 = request.scope.sessionId.trim();
64750
+ if (!sessionId2 || sessionId2.length > ADDRESS_HISTORY_MAX_SESSION_ID_LENGTH || CONTROL_CHARS.test(sessionId2)) {
64751
+ throw new Error(
64752
+ "The address history scope needs a valid conversation id."
64753
+ );
64754
+ }
64755
+ scope = { sessionId: sessionId2 };
64756
+ } else {
64757
+ throw new Error("The address history scope is invalid.");
64204
64758
  }
64205
- return value;
64759
+ return { requestId: requestId2, expectedTabId, scope };
64206
64760
  }
64207
64761
  function closeTabsIntentOrThrow(value) {
64208
64762
  if (!value || typeof value !== "object") {
@@ -66217,6 +66771,8 @@ async function initializeBrowserHost(options, bindings) {
66217
66771
  return view;
66218
66772
  }, wireView = function(view, tabId) {
66219
66773
  const contents = view.webContents;
66774
+ contents.on("focus", () => host.setPageFocused(tabId, true));
66775
+ contents.on("blur", () => host.setPageFocused(tabId, false));
66220
66776
  const popupChildren = /* @__PURE__ */ new Set();
66221
66777
  contents.setWindowOpenHandler((details) => {
66222
66778
  const decision = decidePopup(
@@ -66378,6 +66934,7 @@ async function initializeBrowserHost(options, bindings) {
66378
66934
  if (token) approvals.forgetDocument(token);
66379
66935
  registry.forget(tabId);
66380
66936
  }
66937
+ host.setPageFocused(tabId, false);
66381
66938
  host.clearLoadFailure(tabId);
66382
66939
  releaseTab(tabId, contents.id);
66383
66940
  });
@@ -66653,9 +67210,18 @@ async function initializeBrowserHost(options, bindings) {
66653
67210
  ownsChrome,
66654
67211
  ownsPage: () => {
66655
67212
  const record = registry.activeTab;
66656
- return Boolean(
66657
- record?.presented && electron.webContents.getFocusedWebContents()?.id === record.view.webContents.id
66658
- );
67213
+ return browserPageOwnsChord({
67214
+ activeTab: record ? {
67215
+ tabId: record.tabId,
67216
+ webContentsId: record.view.webContents.id,
67217
+ presented: record.presented
67218
+ } : null,
67219
+ // Read lazily after the window/presented floor: a startup or test host
67220
+ // may carry a webContents slice without the getter.
67221
+ focusedContents: () => electron.webContents.getFocusedWebContents()?.id ?? null,
67222
+ focusedViewTabId: host.focusedPageTabId,
67223
+ window: options.window
67224
+ });
66659
67225
  },
66660
67226
  reload: (hard) => {
66661
67227
  host.reloadActive(hard);
@@ -66687,6 +67253,9 @@ async function initializeBrowserHost(options, bindings) {
66687
67253
  const resetChordFocus = () => {
66688
67254
  host.setChordFocus(false);
66689
67255
  };
67256
+ const resetPageFocus = () => {
67257
+ host.clearPageFocus();
67258
+ };
66690
67259
  const resetMenuShortcutSuppression = () => {
66691
67260
  if (!options.window.webContents.isDestroyed())
66692
67261
  options.window.webContents.setIgnoreMenuShortcuts(false);
@@ -66695,6 +67264,7 @@ async function initializeBrowserHost(options, bindings) {
66695
67264
  if (!focused) resetMenuShortcutSuppression();
66696
67265
  };
66697
67266
  options.window.webContents.on("before-input-event", onChromeInput);
67267
+ options.window.webContents.on("focus", resetPageFocus);
66698
67268
  options.window.on("blur", resetMenuShortcutSuppression);
66699
67269
  options.window.on("closed", resetChordFocus);
66700
67270
  const resetOnNavigation = (details) => {
@@ -66708,6 +67278,7 @@ async function initializeBrowserHost(options, bindings) {
66708
67278
  "before-input-event",
66709
67279
  onChromeInput
66710
67280
  );
67281
+ options.window.webContents.removeListener("focus", resetPageFocus);
66711
67282
  options.window.removeListener("blur", resetMenuShortcutSuppression);
66712
67283
  options.window.removeListener("closed", resetChordFocus);
66713
67284
  options.window.webContents.removeListener(
@@ -66999,7 +67570,7 @@ function isAdmittedSender(event, admitted) {
66999
67570
  (candidate) => candidate.window !== null && !candidate.window.isDestroyed() && event.sender === candidate.window.webContents && event.senderFrame === candidate.window.webContents.mainFrame && trustedDesktopFrame(event.senderFrame.url, candidate.url)
67000
67571
  );
67001
67572
  }
67002
- function registerDesktopIPC(window, expectedUrl, request, streams, media, notifier, noteLeftSession, onKeymapSettingEdited, additionalWindows, supplementMounts2) {
67573
+ function registerDesktopIPC(window, expectedUrl, request, streams, media, notifier, noteLeftSession, onKeymapSettingEdited, additionalWindows, supplementMounts2, teamBridgeNotifier) {
67003
67574
  const opened = /* @__PURE__ */ new Map();
67004
67575
  function authorize(event) {
67005
67576
  const admitted = [
@@ -67063,6 +67634,16 @@ function registerDesktopIPC(window, expectedUrl, request, streams, media, notifi
67063
67634
  noteLeftSession?.(args.sessionId);
67064
67635
  });
67065
67636
  }
67637
+ if (teamBridgeNotifier) {
67638
+ electron.ipcMain.handle("team-bridge:notify", (event, input) => {
67639
+ authorize(event);
67640
+ return teamBridgeNotifier.notify(input);
67641
+ });
67642
+ electron.ipcMain.handle("team-bridge:pending-open", (event) => {
67643
+ authorize(event);
67644
+ return teamBridgeNotifier.takePendingOpen();
67645
+ });
67646
+ }
67066
67647
  if (media) {
67067
67648
  electron.ipcMain.handle("desktop-media", (event, input, bytes) => {
67068
67649
  authorize(event);
@@ -69020,6 +69601,227 @@ function installSupplementProtocol(fetchDocument, log2) {
69020
69601
  });
69021
69602
  });
69022
69603
  }
69604
+ const TEAM_BRIDGE_NOTIFY_MAX_TITLE = 320;
69605
+ const TEAM_BRIDGE_NOTIFY_MAX_BODY = 200;
69606
+ const MAX_SEQ = Number.MAX_SAFE_INTEGER;
69607
+ const SAFE_ID = /^[A-Za-z0-9_.:-]{1,128}$/;
69608
+ function hasUnsafeText(value) {
69609
+ for (const character of value) {
69610
+ const code = character.codePointAt(0) ?? 0;
69611
+ if (character === "<" || character === ">" || code < 32 || code === 127) {
69612
+ return true;
69613
+ }
69614
+ }
69615
+ return false;
69616
+ }
69617
+ function cleanId(value) {
69618
+ return typeof value === "string" && SAFE_ID.test(value) ? value : null;
69619
+ }
69620
+ function cleanText(value, max) {
69621
+ if (typeof value !== "string") return null;
69622
+ const trimmed = value.trim();
69623
+ if (trimmed.length === 0 || trimmed.length > max) return null;
69624
+ if (hasUnsafeText(trimmed)) return null;
69625
+ return trimmed;
69626
+ }
69627
+ function parseTeamBridgeNotifyRequest(value) {
69628
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
69629
+ return null;
69630
+ }
69631
+ const input = value;
69632
+ const conversationId = cleanId(input.conversationId);
69633
+ const tenantId = cleanId(input.tenantId);
69634
+ const title = cleanText(input.title, TEAM_BRIDGE_NOTIFY_MAX_TITLE);
69635
+ const body = cleanText(input.body, TEAM_BRIDGE_NOTIFY_MAX_BODY);
69636
+ const seq2 = input.seq;
69637
+ if (conversationId === null || tenantId === null || title === null || body === null || typeof seq2 !== "number" || !Number.isSafeInteger(seq2) || seq2 < 0 || seq2 > MAX_SEQ) {
69638
+ return null;
69639
+ }
69640
+ return { conversationId, tenantId, title, body, seq: seq2 };
69641
+ }
69642
+ function teamBridgeWindowIsQuiet(window) {
69643
+ if (!window) return false;
69644
+ return !window.isDestroyed() && window.isVisible() && !window.isMinimized() && window.isFocused();
69645
+ }
69646
+ class TeamBridgeNotifier {
69647
+ constructor(options) {
69648
+ this.options = options;
69649
+ this.createBanner = options.createBanner ?? ((bannerOptions) => new electron.Notification(bannerOptions));
69650
+ this.canNotify = options.canNotify ?? (() => electron.Notification.isSupported());
69651
+ }
69652
+ /** The live banner per conversation, so a new seq REPLACES it. */
69653
+ banners = /* @__PURE__ */ new Map();
69654
+ /** The banners this process must keep reachable for their clicks to fire. */
69655
+ lifetime = new NotificationLifetime();
69656
+ pendingOpen = null;
69657
+ createBanner;
69658
+ canNotify;
69659
+ /**
69660
+ * Validate, dedupe and deliver one banner.
69661
+ *
69662
+ * REFUSAL IS AN ERROR, not a quiet `false`: a malformed payload is a bug in
69663
+ * the caller, and the IPC handler should reject rather than let the renderer
69664
+ * believe it raised something. A well-formed payload that is suppressed
69665
+ * (quiet, muted by focus, duplicate, unsupported) answers `shown: false`.
69666
+ */
69667
+ notify(input) {
69668
+ const request = parseTeamBridgeNotifyRequest(input);
69669
+ if (request === null) {
69670
+ throw new Error("Invalid Team Bridge notification payload.");
69671
+ }
69672
+ if (this.options.show === "never") return { shown: false };
69673
+ if (!this.canNotify()) return { shown: false };
69674
+ if (teamBridgeWindowIsQuiet(this.options.window())) {
69675
+ return { shown: false };
69676
+ }
69677
+ const previous = this.options.store.notifiedSeq(request.conversationId);
69678
+ if (previous !== void 0 && request.seq <= previous) {
69679
+ return { shown: false };
69680
+ }
69681
+ this.replace(request.conversationId);
69682
+ const notification = this.createBanner({
69683
+ title: request.title,
69684
+ body: request.body,
69685
+ silent: false,
69686
+ ...this.options.icon ? { icon: this.options.icon } : {}
69687
+ });
69688
+ this.lifetime.retain(notification, () => {
69689
+ this.banners.delete(request.conversationId);
69690
+ this.open(request);
69691
+ });
69692
+ notification.once("close", () => {
69693
+ if (this.banners.get(request.conversationId) === notification) {
69694
+ this.banners.delete(request.conversationId);
69695
+ }
69696
+ });
69697
+ notification.show();
69698
+ this.banners.set(request.conversationId, notification);
69699
+ this.options.store.record(request.conversationId, request.seq);
69700
+ return { shown: true };
69701
+ }
69702
+ /**
69703
+ * The target a click parked for the renderer.
69704
+ *
69705
+ * Consumed exactly once: the renderer asks for it on mount and on every
69706
+ * `team-bridge:open` signal, and a later pull gets `null` rather than
69707
+ * replaying a conversation the user has already seen. The value was built
69708
+ * from an already-validated notification payload, so this read does not
69709
+ * re-parse ids.
69710
+ */
69711
+ takePendingOpen() {
69712
+ const pending = this.pendingOpen;
69713
+ this.pendingOpen = null;
69714
+ return pending;
69715
+ }
69716
+ replace(conversationId) {
69717
+ const previous = this.banners.get(conversationId);
69718
+ if (!previous) return;
69719
+ this.banners.delete(conversationId);
69720
+ previous.close?.();
69721
+ }
69722
+ open(request) {
69723
+ const target2 = {
69724
+ conversationId: request.conversationId,
69725
+ tenantId: request.tenantId
69726
+ };
69727
+ this.pendingOpen = target2;
69728
+ const window = this.options.window();
69729
+ if (window && !window.isDestroyed()) {
69730
+ this.options.host.signalOpen();
69731
+ this.options.present?.(window);
69732
+ return;
69733
+ }
69734
+ this.options.host.reopen();
69735
+ }
69736
+ }
69737
+ const TEAM_BRIDGE_NOTIFY_CURSOR_BOUND = 2048;
69738
+ const VERSION = 1;
69739
+ function isCursor(value) {
69740
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
69741
+ }
69742
+ class TeamBridgeNotifyStore {
69743
+ constructor(file, onError = () => void 0) {
69744
+ this.file = file;
69745
+ this.onError = onError;
69746
+ this.load();
69747
+ }
69748
+ cursors = /* @__PURE__ */ new Map();
69749
+ /** The newest seq a banner was raised for, or `undefined`. */
69750
+ notifiedSeq(conversationId) {
69751
+ return this.cursors.get(conversationId);
69752
+ }
69753
+ /**
69754
+ * Remember that this conversation was notified at `seq`.
69755
+ *
69756
+ * Only advances: a late poll whose seq is older than the recorded one must
69757
+ * not move the cursor backwards and re-open the door to a duplicate banner.
69758
+ * Touching an existing key moves it to the newest position so the bound
69759
+ * evicts by usefulness rather than alphabetically.
69760
+ */
69761
+ record(conversationId, seq2) {
69762
+ if (!isCursor(seq2)) return;
69763
+ const previous = this.cursors.get(conversationId);
69764
+ if (previous !== void 0 && seq2 <= previous) return;
69765
+ this.cursors.delete(conversationId);
69766
+ this.cursors.set(conversationId, seq2);
69767
+ this.trim();
69768
+ this.persist();
69769
+ }
69770
+ /**
69771
+ * Enforce the bound on BOTH paths: `record` while a run is live and `load` on
69772
+ * a file written by an older or hand-edited build. A bound enforced only on
69773
+ * the write path is a promise about files this process wrote itself, which is
69774
+ * not the file it has just read.
69775
+ */
69776
+ trim() {
69777
+ while (this.cursors.size > TEAM_BRIDGE_NOTIFY_CURSOR_BOUND) {
69778
+ const oldest = this.cursors.keys().next();
69779
+ if (oldest.done) break;
69780
+ this.cursors.delete(oldest.value);
69781
+ }
69782
+ }
69783
+ load() {
69784
+ let raw;
69785
+ try {
69786
+ raw = fs.readFileSync(this.file, "utf8");
69787
+ } catch {
69788
+ return;
69789
+ }
69790
+ try {
69791
+ const parsed = JSON.parse(raw);
69792
+ if (parsed === null || typeof parsed !== "object" || parsed.version !== VERSION) {
69793
+ return;
69794
+ }
69795
+ const cursors = parsed.cursors;
69796
+ if (cursors === null || typeof cursors !== "object") return;
69797
+ for (const [conversationId, seq2] of Object.entries(
69798
+ cursors
69799
+ )) {
69800
+ if (isCursor(seq2)) this.cursors.set(conversationId, seq2);
69801
+ }
69802
+ this.trim();
69803
+ } catch (error) {
69804
+ this.onError("team-bridge notification cursor file is unreadable", error);
69805
+ }
69806
+ }
69807
+ persist() {
69808
+ const payload = {
69809
+ version: VERSION,
69810
+ cursors: Object.fromEntries(this.cursors)
69811
+ };
69812
+ try {
69813
+ fs.mkdirSync(path$1.dirname(this.file), { recursive: true, mode: 448 });
69814
+ const temporary = `${this.file}.${process.pid}.tmp`;
69815
+ fs.writeFileSync(temporary, JSON.stringify(payload), { mode: 384 });
69816
+ fs.renameSync(temporary, this.file);
69817
+ } catch (error) {
69818
+ this.onError(
69819
+ "team-bridge notification cursor could not be written",
69820
+ error
69821
+ );
69822
+ }
69823
+ }
69824
+ }
69023
69825
  function titlebarOptions(platform, chrome) {
69024
69826
  if (platform === "darwin") {
69025
69827
  return { titleBarStyle: "hidden" };
@@ -77493,6 +78295,25 @@ function createApplicationMenu() {
77493
78295
  {
77494
78296
  label: "View",
77495
78297
  submenu: [
78298
+ {
78299
+ id: "app-new-tab",
78300
+ label: "New Browser Tab",
78301
+ accelerator: "CmdOrCtrl+T",
78302
+ /*
78303
+ * THE NATIVE ACCELERATOR ROUTE FOR CMD+T, which had no menu item at all.
78304
+ * The browser owns the command through the same predicate as Reload
78305
+ * (`ownsPage` for a focused driven page, `ownsChrome` for browser chrome);
78306
+ * outside the browser this is deliberately a no-op, never a window close.
78307
+ * When the chrome's own `before-input-event` already claimed the press it
78308
+ * prevents this accelerator, so rapid/legit presses stay exactly once.
78309
+ */
78310
+ click: (_item, target2) => runBrowserMenuAction(
78311
+ "new-tab",
78312
+ target2,
78313
+ mainWindow,
78314
+ routeBrowserMenu
78315
+ )
78316
+ },
77496
78317
  {
77497
78318
  id: "app-reload",
77498
78319
  label: "Reload",
@@ -78336,6 +79157,28 @@ electron.app.whenReady().then(async () => {
78336
79157
  // line is what tells that trigger apart from the other four.
78337
79158
  reportRaise
78338
79159
  );
79160
+ const teamBridgeNotifier = new TeamBridgeNotifier({
79161
+ window: () => mainWindow,
79162
+ show: windowLaunch.show,
79163
+ icon: image,
79164
+ store: new TeamBridgeNotifyStore(
79165
+ path$1.join(electron.app.getPath("userData"), "team-bridge-notifications.json"),
79166
+ (message, error) => logger.warn(`${message}: ${String(error)}`, LogFileType.BACKEND)
79167
+ ),
79168
+ host: {
79169
+ signalOpen: () => {
79170
+ const window = mainWindow;
79171
+ if (window && !window.isDestroyed()) {
79172
+ window.webContents.send("team-bridge:open");
79173
+ }
79174
+ },
79175
+ reopen: () => electron.app.emit("activate")
79176
+ },
79177
+ present: (window) => raiseWindow(window, windowLaunch.show, {
79178
+ trigger: "banner-click",
79179
+ report: reportRaise
79180
+ })
79181
+ });
78339
79182
  backendService.onBackendReady(() => {
78340
79183
  void desktopNotifier.refreshNotificationContract();
78341
79184
  void backendService.getDesktopFeedRelay().start();
@@ -78608,7 +79451,8 @@ electron.app.whenReady().then(async () => {
78608
79451
  * same instance `createWindow` gave the navigation guard: the arm channel
78609
79452
  * and the guard are two halves of one decision and must share one list.
78610
79453
  */
78611
- supplementMounts
79454
+ supplementMounts,
79455
+ teamBridgeNotifier
78612
79456
  );
78613
79457
  if (devDriverArming.armed && devDriverArming.outDir) {
78614
79458
  registerDevDriverIPC({