local-operator-ui 0.34.55 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/main/index.js +571 -45
- package/out/preload/index.js +50 -1
- package/out/renderer/assets/{_basePickBy-Be_5LfVS.js → _basePickBy-DXcdlCne.js} +1 -1
- package/out/renderer/assets/{_baseUniq-hdy9kmQQ.js → _baseUniq-59s2A3Xi.js} +1 -1
- package/out/renderer/assets/{agent-details-page-CLbcLEwm.js → agent-details-page-DzCMByBi.js} +1 -1
- package/out/renderer/assets/{agent-hub-page-Bf5Aw-Jn.js → agent-hub-page-DJCaMoAj.js} +1 -1
- package/out/renderer/assets/{agents-page-BpaQBOFJ.js → agents-page-jAx30gRr.js} +3 -3
- package/out/renderer/assets/{arc-CgN-UZqL.js → arc-DTGY_Lgi.js} +1 -1
- package/out/renderer/assets/{architectureDiagram-IEHRJDOE-DowMu2Qf.js → architectureDiagram-IEHRJDOE-C1eZGb9i.js} +1 -1
- package/out/renderer/assets/{blockDiagram-JOT3LUYC-DgKTIFqt.js → blockDiagram-JOT3LUYC-DcFFqP3h.js} +1 -1
- package/out/renderer/assets/{book-open-yKrSTgbR.js → book-open-Bi2GxPj-.js} +1 -1
- package/out/renderer/assets/browser-page-Bnzl2Fck.js +1 -0
- package/out/renderer/assets/{browser-webauthn-prompt-CJ6FJJMN.js → browser-webauthn-prompt-DcHbqaLn.js} +1 -1
- package/out/renderer/assets/{c4Diagram-VJAJSXHY-Ctyv3nA_.js → c4Diagram-VJAJSXHY-CzUO0Dxd.js} +1 -1
- package/out/renderer/assets/channel-CBLzxDK-.js +1 -0
- package/out/renderer/assets/{chunk-4BMEZGHF-CW43fWPt.js → chunk-4BMEZGHF-mCAjVMxO.js} +1 -1
- package/out/renderer/assets/{chunk-A2AXSNBT-BfdEIIIg.js → chunk-A2AXSNBT-D6-zo_Fr.js} +1 -1
- package/out/renderer/assets/{chunk-AEK57VVT-Bx4nZ-57.js → chunk-AEK57VVT-B3ngikm8.js} +1 -1
- package/out/renderer/assets/{chunk-D6G4REZN-BA6hwejS.js → chunk-D6G4REZN-ChTXeHBQ.js} +1 -1
- package/out/renderer/assets/{chunk-RZ5BOZE2-Civ6_Z-P.js → chunk-RZ5BOZE2-D2c3_Jya.js} +1 -1
- package/out/renderer/assets/{chunk-XZIHB7SX-BVPRrS8K.js → chunk-XZIHB7SX-BTrxUgxT.js} +1 -1
- package/out/renderer/assets/classDiagram-GIVACNV2-BnE_q2SF.js +1 -0
- package/out/renderer/assets/classDiagram-v2-COTLJTTW-BnE_q2SF.js +1 -0
- package/out/renderer/assets/clone-Ctta4hua.js +1 -0
- package/out/renderer/assets/{console-mirror-D12eLWy7.js → console-mirror-Dc1bE9Vm.js} +1 -1
- package/out/renderer/assets/{consoleCapture-B5s47GGM.js → consoleCapture-Wr6s4sLo.js} +1 -1
- package/out/renderer/assets/{conversation-input-sync-oIWpPfzj.js → conversation-input-sync-C-0MXqyt.js} +140 -140
- package/out/renderer/assets/{dagre-OKDRZEBW-CayNWWG3.js → dagre-OKDRZEBW-B9TiRiqo.js} +1 -1
- package/out/renderer/assets/{diagram-SSKATNLV-CNrEYtm4.js → diagram-SSKATNLV-1SfE1-5B.js} +1 -1
- package/out/renderer/assets/{diagram-VNBRO52H-jzZ7n3uT.js → diagram-VNBRO52H-l4dzkACu.js} +1 -1
- package/out/renderer/assets/{erDiagram-Q7BY3M3F-9BaZ6ZkV.js → erDiagram-Q7BY3M3F-CUgpeoFK.js} +1 -1
- package/out/renderer/assets/{error-boundary-BfL6ia-P.js → error-boundary-C9PRNlmT.js} +2 -2
- package/out/renderer/assets/{flowDiagram-4HSFHLVR-CptBIl13.js → flowDiagram-4HSFHLVR-T5JJ4Xtw.js} +1 -1
- package/out/renderer/assets/{ganttDiagram-APWFNJXF-CgcFEyO7.js → ganttDiagram-APWFNJXF-DQ2mySlQ.js} +1 -1
- package/out/renderer/assets/{gitGraphDiagram-7IBYFJ6S-cmMetjXL.js → gitGraphDiagram-7IBYFJ6S-C9DZfwGd.js} +1 -1
- package/out/renderer/assets/{graph-R1Ok41yA.js → graph-TsL6qeA-.js} +1 -1
- package/out/renderer/assets/index-8yv29dzU.js +57 -0
- package/out/renderer/assets/index-Bk16DqRh.css +1 -0
- package/out/renderer/assets/{index-CNvEPxPr.js → index-D8u5NmIa.js} +177 -162
- package/out/renderer/assets/{index-Dsm4DzII.js → index-P4i-q0KF.js} +1 -1
- package/out/renderer/assets/{index-BFzgup69.js → index-rSXvFxM8.js} +1 -1
- package/out/renderer/assets/{infoDiagram-PH2N3AL5-DeSmG1nL.js → infoDiagram-PH2N3AL5-U47NbuL3.js} +1 -1
- package/out/renderer/assets/{installer-BM-QRi7Y.js → installer-fluCVssE.js} +1 -1
- package/out/renderer/assets/{journeyDiagram-U35MCT3I--ZNfC2qt.js → journeyDiagram-U35MCT3I-Bo7dozG5.js} +1 -1
- package/out/renderer/assets/{kanban-definition-NDS4AKOZ-kUi3-HdL.js → kanban-definition-NDS4AKOZ-Bn0hKA3l.js} +1 -1
- package/out/renderer/assets/{layout-DQq3NfPk.js → layout-CnIEnr3t.js} +1 -1
- package/out/renderer/assets/{legacy-agents-page-D0vembfV.js → legacy-agents-page-pxEj7cjk.js} +1 -1
- package/out/renderer/assets/{mermaid.core-kSHzKq9G.js → mermaid.core-lUsLZpuU.js} +5 -5
- package/out/renderer/assets/{mesh-page-BhfRHXQS.js → mesh-page-vihuNvox.js} +1 -1
- package/out/renderer/assets/{mindmap-definition-ALO5MXBD-BsJiNdkz.js → mindmap-definition-ALO5MXBD-DtB-t5TL.js} +1 -1
- package/out/renderer/assets/{mini-DngBC0gl.js → mini--p2d2BoB.js} +1 -1
- package/out/renderer/assets/{notebook-pen-BbGvgaYT.js → notebook-pen-C-602S82.js} +1 -1
- package/out/renderer/assets/{org-access-DVLt_Z75.js → org-access-DwKpT4sY.js} +1 -1
- package/out/renderer/assets/{org-surface-gate-J1G29FUu.js → org-surface-gate--inYykb1.js} +1 -1
- package/out/renderer/assets/{page-header-BSVtXSul.js → page-header-Cz4WU8y6.js} +1 -1
- package/out/renderer/assets/{pieDiagram-IB7DONF6-D9NPKlbA.js → pieDiagram-IB7DONF6-kHXX0Qj3.js} +1 -1
- package/out/renderer/assets/{projects-page-CV3ap9Ch.js → projects-page-B9Y4di7i.js} +1 -1
- package/out/renderer/assets/{publication-validation-CXXK8RMX.js → publication-validation-LQ7dKLdu.js} +1 -1
- package/out/renderer/assets/{quadrantDiagram-7GDLP6J5-Dp6sSgy_.js → quadrantDiagram-7GDLP6J5-Da2X85Fu.js} +1 -1
- package/out/renderer/assets/{radar-MK3ICKWK-BdYaxH9C.js → radar-MK3ICKWK-C0d75XBo.js} +1 -1
- package/out/renderer/assets/{radient-auth-buttons-2gyRX5Xg.js → radient-auth-buttons-DQVWpG8o.js} +1 -1
- package/out/renderer/assets/{requirementDiagram-KVF5MWMF-CSxZpxDc.js → requirementDiagram-KVF5MWMF-CL-Kku_X.js} +1 -1
- package/out/renderer/assets/{sankeyDiagram-QLVOVGJD-DNfJtnGS.js → sankeyDiagram-QLVOVGJD-DWUg1eAE.js} +1 -1
- package/out/renderer/assets/{schedules-page-BMY6s4UJ.js → schedules-page-B7tSQfbG.js} +1 -1
- package/out/renderer/assets/{sequenceDiagram-X6HHIX6F-wP1VuU8h.js → sequenceDiagram-X6HHIX6F-Bn0FlGHo.js} +1 -1
- package/out/renderer/assets/settings-page-Dty0ZTmK.js +292 -0
- package/out/renderer/assets/{stateDiagram-DGXRK772-BRguDCdq.js → stateDiagram-DGXRK772-CkdY_hLZ.js} +1 -1
- package/out/renderer/assets/stateDiagram-v2-YXO3MK2T-CWy168Js.js +1 -0
- package/out/renderer/assets/{timeline-definition-BDJGKUSR-Sovor235.js → timeline-definition-BDJGKUSR-DodPShzl.js} +1 -1
- package/out/renderer/assets/ui-preferences-store-B_KwjcMZ.js +1 -0
- package/out/renderer/assets/{use-download-agent-mutation-CKVzPGPY.js → use-download-agent-mutation-CIiU8sRW.js} +1 -1
- package/out/renderer/assets/{use-memberships-query-BgAi_tHu.js → use-memberships-query-BMOJPwWD.js} +1 -1
- package/out/renderer/assets/{use-session-snapshot-Ba7FspxL.js → use-session-snapshot-B0aCKABJ.js} +1 -1
- package/out/renderer/assets/{xychartDiagram-VJFVF3MP-BBlbpFBv.js → xychartDiagram-VJFVF3MP-BRifY3vn.js} +1 -1
- package/out/renderer/console-capture.html +5 -5
- package/out/renderer/index.html +7 -7
- package/out/renderer/installer.html +4 -4
- package/out/renderer/mini.html +7 -7
- package/package.json +2 -2
- package/out/renderer/assets/browser-page-CKyYrwF0.js +0 -1
- package/out/renderer/assets/channel-DaruqAQQ.js +0 -1
- package/out/renderer/assets/classDiagram-GIVACNV2-D8vhGZVf.js +0 -1
- package/out/renderer/assets/classDiagram-v2-COTLJTTW-D8vhGZVf.js +0 -1
- package/out/renderer/assets/clone-HcmO38Fx.js +0 -1
- package/out/renderer/assets/index-CsR1USrT.js +0 -57
- package/out/renderer/assets/index-pufheB69.css +0 -1
- package/out/renderer/assets/settings-page-CVUD4U35.js +0 -292
- package/out/renderer/assets/stateDiagram-v2-YXO3MK2T-CqO4MBYu.js +0 -1
- package/out/renderer/assets/ui-preferences-store-g6-xQ9NA.js +0 -1
package/out/main/index.js
CHANGED
|
@@ -185,6 +185,26 @@ const profileName = zod.z.string().min(1).max(128).refine(
|
|
|
185
185
|
)
|
|
186
186
|
);
|
|
187
187
|
const target = zod.z.object({ kind: zod.z.enum(["agent", "team"]), name: profileName }).strict();
|
|
188
|
+
const importSourceName = zod.z.enum(["claude", "codex", "omp"]);
|
|
189
|
+
const importSessionIds = zod.z.record(
|
|
190
|
+
zod.z.string(),
|
|
191
|
+
zod.z.array(zod.z.string().min(1).max(512)).max(5e3)
|
|
192
|
+
);
|
|
193
|
+
const importRequestFields = {
|
|
194
|
+
sources: zod.z.array(importSourceName).max(3).optional(),
|
|
195
|
+
sessionIds: importSessionIds.optional(),
|
|
196
|
+
/*
|
|
197
|
+
* Kept on the wire for the CLI/TUI and future callers, but the Settings
|
|
198
|
+
* surface never sets it: no-default-all is enforced in the UI by requiring
|
|
199
|
+
* an explicit per-session selection before preview and again before import.
|
|
200
|
+
*/
|
|
201
|
+
all: zod.z.boolean().optional(),
|
|
202
|
+
includeSubagents: zod.z.boolean().optional(),
|
|
203
|
+
includeClearedSegments: zod.z.boolean().optional(),
|
|
204
|
+
includeMemory: zod.z.boolean().optional(),
|
|
205
|
+
includeInstructions: zod.z.boolean().optional(),
|
|
206
|
+
maxSessions: zod.z.number().int().min(1).max(5e3).optional()
|
|
207
|
+
};
|
|
188
208
|
const modelSelection = zod.z.object({
|
|
189
209
|
provider: zod.z.string().min(1).max(128),
|
|
190
210
|
model_id: zod.z.string().min(1).max(512),
|
|
@@ -419,6 +439,27 @@ const desktopRequestUnion = zod.z.discriminatedUnion("op", [
|
|
|
419
439
|
zod.z.object({ op: zod.z.literal("runtimes.list") }).strict(),
|
|
420
440
|
zod.z.object({ op: zod.z.literal("profiles.list") }).strict(),
|
|
421
441
|
zod.z.object({ op: zod.z.literal("profiles.get"), name: profileName }).strict(),
|
|
442
|
+
/*
|
|
443
|
+
* The harness import plane. Reads are fixed-shape GETs; preview and start
|
|
444
|
+
* carry the same explicit request fields, and neither is allowed to infer a
|
|
445
|
+
* selection: the UI paints an empty selection and makes the user choose.
|
|
446
|
+
*/
|
|
447
|
+
zod.z.object({ op: zod.z.literal("imports.sources") }).strict(),
|
|
448
|
+
zod.z.object({
|
|
449
|
+
op: zod.z.literal("imports.sessions"),
|
|
450
|
+
source: importSourceName,
|
|
451
|
+
includeSubagents: zod.z.boolean().optional()
|
|
452
|
+
}).strict(),
|
|
453
|
+
zod.z.object({ op: zod.z.literal("imports.preview"), ...importRequestFields }).strict(),
|
|
454
|
+
zod.z.object({ op: zod.z.literal("imports.start"), ...importRequestFields }).strict(),
|
|
455
|
+
zod.z.object({
|
|
456
|
+
op: zod.z.literal("imports.status"),
|
|
457
|
+
jobId: zod.z.string().min(1).max(128)
|
|
458
|
+
}).strict(),
|
|
459
|
+
zod.z.object({
|
|
460
|
+
op: zod.z.literal("imports.cancel"),
|
|
461
|
+
jobId: zod.z.string().min(1).max(128)
|
|
462
|
+
}).strict(),
|
|
422
463
|
/*
|
|
423
464
|
* Definition CAS (proposed additive wire, b-wire-receipt-definition-cas-and-adopt).
|
|
424
465
|
* `expectedDefinitionRevision` is an opaque echo of the `definition_revision` the
|
|
@@ -2780,6 +2821,10 @@ zod.z.object({
|
|
|
2780
2821
|
const DESKTOP_MESSAGE_BYTE_BUDGET = 88e4;
|
|
2781
2822
|
const DESKTOP_CONTROL_BYTE_BUDGET = 262144;
|
|
2782
2823
|
const DESKTOP_SYSTEM_PROMPT_BYTE_BUDGET = 11e5;
|
|
2824
|
+
const IMPORT_OPS = /* @__PURE__ */ new Set([
|
|
2825
|
+
"imports.preview",
|
|
2826
|
+
"imports.start"
|
|
2827
|
+
]);
|
|
2783
2828
|
const MESSAGE_OPS = /* @__PURE__ */ new Set([
|
|
2784
2829
|
"sessions.message",
|
|
2785
2830
|
"sessions.command",
|
|
@@ -2792,7 +2837,7 @@ const MESSAGE_OPS = /* @__PURE__ */ new Set([
|
|
|
2792
2837
|
function desktopRequestByteBudget(op) {
|
|
2793
2838
|
if (op === "legacy.agent.systemPrompt.update")
|
|
2794
2839
|
return DESKTOP_SYSTEM_PROMPT_BYTE_BUDGET;
|
|
2795
|
-
return MESSAGE_OPS.has(op) ? DESKTOP_MESSAGE_BYTE_BUDGET : DESKTOP_CONTROL_BYTE_BUDGET;
|
|
2840
|
+
return MESSAGE_OPS.has(op) || IMPORT_OPS.has(op) ? DESKTOP_MESSAGE_BYTE_BUDGET : DESKTOP_CONTROL_BYTE_BUDGET;
|
|
2796
2841
|
}
|
|
2797
2842
|
const DESKTOP_CONTROL_DEADLINE_MS = 2e4;
|
|
2798
2843
|
const DESKTOP_LONG_READ_DEADLINE_MS = 9e4;
|
|
@@ -3054,6 +3099,18 @@ function assertPairedPublicationTarget(request) {
|
|
|
3054
3099
|
);
|
|
3055
3100
|
}
|
|
3056
3101
|
}
|
|
3102
|
+
function harnessImportBody(request) {
|
|
3103
|
+
return {
|
|
3104
|
+
sources: request.sources ?? null,
|
|
3105
|
+
session_ids: request.sessionIds ?? {},
|
|
3106
|
+
all: request.all ?? false,
|
|
3107
|
+
include_subagents: request.includeSubagents ?? false,
|
|
3108
|
+
include_cleared_segments: request.includeClearedSegments ?? false,
|
|
3109
|
+
include_memory: request.includeMemory ?? true,
|
|
3110
|
+
include_instructions: request.includeInstructions ?? true,
|
|
3111
|
+
...request.maxSessions === void 0 ? {} : { max_sessions: request.maxSessions }
|
|
3112
|
+
};
|
|
3113
|
+
}
|
|
3057
3114
|
function desktopEndpoint(request) {
|
|
3058
3115
|
switch (request.op) {
|
|
3059
3116
|
case "capabilities":
|
|
@@ -3149,6 +3206,38 @@ function desktopEndpoint(request) {
|
|
|
3149
3206
|
method: "POST",
|
|
3150
3207
|
body: { request_id: request.requestId, name: request.name }
|
|
3151
3208
|
};
|
|
3209
|
+
case "imports.sources":
|
|
3210
|
+
return { path: "/v1/desktop/imports/sources", method: "GET" };
|
|
3211
|
+
case "imports.sessions": {
|
|
3212
|
+
const query = new URLSearchParams({ source: request.source });
|
|
3213
|
+
if (request.includeSubagents) query.set("include_subagents", "true");
|
|
3214
|
+
return {
|
|
3215
|
+
path: `/v1/desktop/imports/sessions?${query.toString()}`,
|
|
3216
|
+
method: "GET"
|
|
3217
|
+
};
|
|
3218
|
+
}
|
|
3219
|
+
case "imports.preview":
|
|
3220
|
+
return {
|
|
3221
|
+
path: "/v1/desktop/imports/preview",
|
|
3222
|
+
method: "POST",
|
|
3223
|
+
body: harnessImportBody(request)
|
|
3224
|
+
};
|
|
3225
|
+
case "imports.start":
|
|
3226
|
+
return {
|
|
3227
|
+
path: "/v1/desktop/imports/jobs",
|
|
3228
|
+
method: "POST",
|
|
3229
|
+
body: harnessImportBody(request)
|
|
3230
|
+
};
|
|
3231
|
+
case "imports.status":
|
|
3232
|
+
return {
|
|
3233
|
+
path: `/v1/desktop/imports/jobs/${encodeURIComponent(request.jobId)}`,
|
|
3234
|
+
method: "GET"
|
|
3235
|
+
};
|
|
3236
|
+
case "imports.cancel":
|
|
3237
|
+
return {
|
|
3238
|
+
path: `/v1/desktop/imports/jobs/${encodeURIComponent(request.jobId)}`,
|
|
3239
|
+
method: "DELETE"
|
|
3240
|
+
};
|
|
3152
3241
|
case "profiles.create":
|
|
3153
3242
|
return {
|
|
3154
3243
|
path: "/v1/desktop/profiles",
|
|
@@ -4659,27 +4748,42 @@ function resolveWindowChromeForLaunch(input) {
|
|
|
4659
4748
|
const GLASS_COVERAGES = ["sidebars", "full"];
|
|
4660
4749
|
const GLASS_MAC_MATERIALS = ["window", "menu", "popover"];
|
|
4661
4750
|
const GLASS_WIN_MATERIALS = ["mica", "acrylic", "tabbed"];
|
|
4751
|
+
const DEFAULT_GLASS_INTENSITY = 80;
|
|
4662
4752
|
const DEFAULT_GLASS_SETTINGS = {
|
|
4663
4753
|
enabled: false,
|
|
4664
4754
|
coverage: "full",
|
|
4665
|
-
intensity:
|
|
4755
|
+
intensity: DEFAULT_GLASS_INTENSITY,
|
|
4666
4756
|
material: "window"
|
|
4667
4757
|
};
|
|
4668
4758
|
const WIN_BACKGROUND_MATERIAL_MIN_BUILD = 22621;
|
|
4669
4759
|
const parseVersion = (version) => version.split(".").map((part) => Number.parseInt(part, 10)).map((n) => Number.isFinite(n) ? n : 0);
|
|
4760
|
+
const gpuFeatureUsable = (status2) => typeof status2 === "string" && status2.startsWith("enabled");
|
|
4761
|
+
function glassEnvironmentObjection(environment) {
|
|
4762
|
+
if (!environment) return null;
|
|
4763
|
+
if (environment.accelerationDisabledByLaunch || environment.hardwareAcceleration === false)
|
|
4764
|
+
return "hardware-acceleration-disabled";
|
|
4765
|
+
if (environment.gpu && (!gpuFeatureUsable(environment.gpu.compositing) || !gpuFeatureUsable(environment.gpu.rasterization)))
|
|
4766
|
+
return "gpu-unavailable";
|
|
4767
|
+
if (environment.backdropFilter === false)
|
|
4768
|
+
return "backdrop-filter-unavailable";
|
|
4769
|
+
return null;
|
|
4770
|
+
}
|
|
4670
4771
|
function glassCapabilityFor(input) {
|
|
4671
|
-
const none = (reason) => ({
|
|
4772
|
+
const none = (reason, defaultMaterial = DEFAULT_GLASS_SETTINGS.material) => ({
|
|
4672
4773
|
supported: false,
|
|
4673
4774
|
mechanism: "none",
|
|
4674
4775
|
reason,
|
|
4675
4776
|
materials: [],
|
|
4676
|
-
defaultMaterial
|
|
4777
|
+
defaultMaterial,
|
|
4677
4778
|
liquid: false,
|
|
4779
|
+
defaultEnabled: false,
|
|
4678
4780
|
liveToggle: false
|
|
4679
4781
|
});
|
|
4680
4782
|
const version = parseVersion(input.osVersion);
|
|
4681
4783
|
if (input.platform === "darwin") {
|
|
4682
4784
|
if (input.reducedTransparency) return none("reduced-transparency");
|
|
4785
|
+
const objection = glassEnvironmentObjection(input.environment);
|
|
4786
|
+
if (objection) return none(objection);
|
|
4683
4787
|
return {
|
|
4684
4788
|
supported: true,
|
|
4685
4789
|
mechanism: "vibrancy",
|
|
@@ -4687,26 +4791,26 @@ function glassCapabilityFor(input) {
|
|
|
4687
4791
|
materials: GLASS_MAC_MATERIALS,
|
|
4688
4792
|
defaultMaterial: "window",
|
|
4689
4793
|
liquid: (version[0] ?? 0) >= 26,
|
|
4794
|
+
defaultEnabled: true,
|
|
4690
4795
|
liveToggle: true
|
|
4691
4796
|
};
|
|
4692
4797
|
}
|
|
4693
4798
|
if (input.platform === "win32") {
|
|
4694
4799
|
if ((version[2] ?? 0) < WIN_BACKGROUND_MATERIAL_MIN_BUILD)
|
|
4695
|
-
return none("windows-too-old");
|
|
4696
|
-
|
|
4697
|
-
return {
|
|
4698
|
-
supported: true,
|
|
4699
|
-
mechanism: "background-material",
|
|
4700
|
-
reason: "ok",
|
|
4701
|
-
materials: GLASS_WIN_MATERIALS,
|
|
4702
|
-
defaultMaterial: "mica",
|
|
4703
|
-
liquid: false,
|
|
4704
|
-
liveToggle: false
|
|
4705
|
-
};
|
|
4800
|
+
return none("windows-too-old", "mica");
|
|
4801
|
+
return none("windows-unverified", "mica");
|
|
4706
4802
|
}
|
|
4707
|
-
if (input.platform === "linux") return none("linux-
|
|
4803
|
+
if (input.platform === "linux") return none("linux-compositor-dependent");
|
|
4708
4804
|
return none("unsupported-platform");
|
|
4709
4805
|
}
|
|
4806
|
+
function defaultGlassSettings(capability) {
|
|
4807
|
+
return {
|
|
4808
|
+
enabled: capability.defaultEnabled,
|
|
4809
|
+
coverage: "full",
|
|
4810
|
+
intensity: DEFAULT_GLASS_INTENSITY,
|
|
4811
|
+
material: capability.defaultMaterial
|
|
4812
|
+
};
|
|
4813
|
+
}
|
|
4710
4814
|
const isCoverage = (value) => GLASS_COVERAGES.includes(value);
|
|
4711
4815
|
const migrateCoverage = (value) => value === "chrome" ? "sidebars" : value;
|
|
4712
4816
|
function sanitizeGlassSettings(raw, capability, base = DEFAULT_GLASS_SETTINGS) {
|
|
@@ -4726,6 +4830,40 @@ function sanitizeGlassSettings(raw, capability, base = DEFAULT_GLASS_SETTINGS) {
|
|
|
4726
4830
|
material
|
|
4727
4831
|
};
|
|
4728
4832
|
}
|
|
4833
|
+
const GLASS_FILE_VERSION = 2;
|
|
4834
|
+
const GLASS_SETTINGS_FIELDS = [
|
|
4835
|
+
"enabled",
|
|
4836
|
+
"coverage",
|
|
4837
|
+
"intensity",
|
|
4838
|
+
"material"
|
|
4839
|
+
];
|
|
4840
|
+
function resolveStoredGlass(raw, capability) {
|
|
4841
|
+
if (raw === null || typeof raw !== "object") return null;
|
|
4842
|
+
const record = raw;
|
|
4843
|
+
if (record.settings === null || typeof record.settings !== "object")
|
|
4844
|
+
return null;
|
|
4845
|
+
const defaults = defaultGlassSettings(capability);
|
|
4846
|
+
const stored = sanitizeGlassSettings(
|
|
4847
|
+
record.settings,
|
|
4848
|
+
capability,
|
|
4849
|
+
DEFAULT_GLASS_SETTINGS
|
|
4850
|
+
);
|
|
4851
|
+
const legacy = record.version !== GLASS_FILE_VERSION || !Array.isArray(record.explicit);
|
|
4852
|
+
const explicit = legacy ? [...GLASS_SETTINGS_FIELDS] : GLASS_SETTINGS_FIELDS.filter(
|
|
4853
|
+
(field) => record.explicit.includes(field)
|
|
4854
|
+
);
|
|
4855
|
+
const settings = { ...defaults };
|
|
4856
|
+
for (const field of explicit)
|
|
4857
|
+
settings[field] = stored[field];
|
|
4858
|
+
return {
|
|
4859
|
+
settings,
|
|
4860
|
+
explicit,
|
|
4861
|
+
scheme: record.scheme === "light" ? "light" : "dark"
|
|
4862
|
+
};
|
|
4863
|
+
}
|
|
4864
|
+
function glassGateLine(capability, settings, platform) {
|
|
4865
|
+
return `[window-glass] launch gate platform=${platform} supported=${capability.supported} default=${capability.defaultEnabled} enabled=${settings.enabled} intensity=${settings.intensity} reason=${capability.reason} mechanism=${capability.mechanism}`;
|
|
4866
|
+
}
|
|
4729
4867
|
const glassIsActive = (settings, capability) => settings.enabled && capability.supported;
|
|
4730
4868
|
const GLASS_TRANSPARENT = "#00000000";
|
|
4731
4869
|
function glassWindowOptions(settings, capability, platform) {
|
|
@@ -4763,6 +4901,7 @@ const WINDOW_GLASS_GET_CHANNEL = "window-glass:get";
|
|
|
4763
4901
|
const WINDOW_GLASS_SET_CHANNEL = "window-glass:set";
|
|
4764
4902
|
const WINDOW_GLASS_SCHEME_CHANNEL = "window-glass:scheme";
|
|
4765
4903
|
const WINDOW_GLASS_STATE_CHANNEL = "window-glass:state";
|
|
4904
|
+
const WINDOW_GLASS_RENDERER_CHANNEL = "window-glass:renderer";
|
|
4766
4905
|
const WINDOW_GLASS_FILE = "window-glass.json";
|
|
4767
4906
|
function windowGlassArgument(state2, scheme) {
|
|
4768
4907
|
if (!state2.active) return `${WINDOW_GLASS_ARG}=off`;
|
|
@@ -32318,6 +32457,8 @@ const enUiBrowser = {
|
|
|
32318
32457
|
"ui.browser.browser_tab_strip.close": 'Close "',
|
|
32319
32458
|
"ui.browser.browser_tab_strip.copy_url": "Copy URL",
|
|
32320
32459
|
"ui.browser.browser_tab_strip.close_tab": "Close tab",
|
|
32460
|
+
"ui.browser.browser_tab_strip.mute_tab": "Mute tab",
|
|
32461
|
+
"ui.browser.browser_tab_strip.unmute_tab": "Unmute tab",
|
|
32321
32462
|
"ui.browser.browser_tab_strip.close_tab_shortcut": "Close tab ({shortcut})",
|
|
32322
32463
|
"ui.browser.browser_tab_strip.close_named_tab": "Close {title}",
|
|
32323
32464
|
"ui.browser.browser_tab_strip.all_tabs_unshown": "All tabs — {count, plural, other {{count, number, ::group-off} not shown}}",
|
|
@@ -32330,6 +32471,7 @@ const enUiBrowser = {
|
|
|
32330
32471
|
"ui.browser.browser_url_bar.forward_shortcut": "Forward ({shortcut})",
|
|
32331
32472
|
"ui.browser.browser_url_bar.find_in_page": "Find in page",
|
|
32332
32473
|
"ui.browser.browser_url_bar.find_in_page_shortcut": "Find in page ({shortcut})",
|
|
32474
|
+
"ui.browser.browser_url_bar.print_page": "Print page",
|
|
32333
32475
|
"ui.browser.browser_url_bar.forward": "Forward",
|
|
32334
32476
|
"ui.browser.browser_url_bar.reset_page_zoom_shortcut": "Reset page zoom ({shortcut})",
|
|
32335
32477
|
"ui.browser.browser_url_bar.page_zoom_reset": "Page zoom {zoom, number, ::group-off}%, reset to 100%",
|
|
@@ -34320,18 +34462,18 @@ const enUiSettingsAppearance = {
|
|
|
34320
34462
|
};
|
|
34321
34463
|
const enUiSettingsAppearanceWindowGlass = {
|
|
34322
34464
|
"ui.settings.appearance.window_glass.label": "Translucent window",
|
|
34323
|
-
"ui.settings.appearance.window_glass.description": "Lets your desktop show through the app window, blurred by the system.
|
|
34465
|
+
"ui.settings.appearance.window_glass.description": "Lets your desktop show through the app window, blurred by the system. On by default on macOS where the system supports it, and off elsewhere. Text stays readable on every theme: the slider stops before any theme's text gets hard to read.",
|
|
34324
34466
|
"ui.settings.appearance.window_glass.restart_needed": "Restart Local Operator to apply this change. The window keeps its current look until then.",
|
|
34325
|
-
"ui.settings.appearance.window_glass.unsupported_reduced_transparency": "Translucent window is unavailable because Reduce transparency is on in your system accessibility settings. Turn it off there
|
|
34467
|
+
"ui.settings.appearance.window_glass.unsupported_reduced_transparency": "Translucent window is unavailable because Reduce transparency is on in your system accessibility settings. Turn it off there to enable this control.",
|
|
34326
34468
|
"ui.settings.appearance.window_glass.unsupported_windows_too_old": "Translucent window needs Windows 11 version 22H2 or later.",
|
|
34327
|
-
"ui.settings.appearance.window_glass.unsupported_linux": "Translucent window is not available on Linux: there is no
|
|
34328
|
-
"ui.settings.appearance.window_glass.unsupported_other": "Translucent window is not available on this
|
|
34469
|
+
"ui.settings.appearance.window_glass.unsupported_linux": "Translucent window is not available on Linux: there is no native window blur an app can request. Desktop translucency there depends on the compositor (X11 or Wayland) and can fall back to software blur, which would show the desktop's text through the app's.",
|
|
34470
|
+
"ui.settings.appearance.window_glass.unsupported_other": "Translucent window is not available on this platform: only macOS is verified to composite a blurred window, and the Windows 11 material is not verified on a real machine yet.",
|
|
34329
34471
|
"ui.settings.appearance.window_glass.coverage_label": "Glass applies to",
|
|
34330
34472
|
"ui.settings.appearance.window_glass.coverage_description": "Which parts of the window go translucent. Sidebars only leaves the conversation and pages solid. Full app also covers them and the side panels; web pages, documents and images shown inside stay solid.",
|
|
34331
34473
|
"ui.settings.appearance.window_glass.coverage_sidebars": "Sidebars only",
|
|
34332
34474
|
"ui.settings.appearance.window_glass.coverage_full": "Full app",
|
|
34333
34475
|
"ui.settings.appearance.window_glass.intensity_label": "Frosted to see-through",
|
|
34334
|
-
"ui.settings.appearance.window_glass.intensity_description": "Left is frosted: mostly solid with a hint of the desktop. Right is see-through, as clear as every theme's text allows. Message blocks and cards become tints, inputs
|
|
34476
|
+
"ui.settings.appearance.window_glass.intensity_description": "Left is frosted: mostly solid with a hint of the desktop. Right is see-through, as clear as every theme's text allows. Message blocks and cards become tints, inputs stay distinct from the glass, and code, documents and images stay solid.",
|
|
34335
34477
|
"ui.settings.appearance.window_glass.material_label": "Material",
|
|
34336
34478
|
"ui.settings.appearance.window_glass.material_description": "The system material behind the app. It also sets how far see-through can go.",
|
|
34337
34479
|
"ui.settings.appearance.window_glass.material_window": "Window",
|
|
@@ -34342,7 +34484,11 @@ const enUiSettingsAppearanceWindowGlass = {
|
|
|
34342
34484
|
"ui.settings.appearance.window_glass.material_tabbed": "Tabbed",
|
|
34343
34485
|
"ui.settings.appearance.window_glass.intensity_start": "Frosted",
|
|
34344
34486
|
"ui.settings.appearance.window_glass.intensity_end": "See-through",
|
|
34345
|
-
"ui.settings.appearance.window_glass.intensity_value": "{value}% toward see-through"
|
|
34487
|
+
"ui.settings.appearance.window_glass.intensity_value": "{value}% toward see-through",
|
|
34488
|
+
"ui.settings.appearance.window_glass.unsupported_windows_unverified": "Translucent window is not offered on Windows yet: Mica and Acrylic are implemented but have not been tested on a real Windows machine, so the control stays off until that testing lands.",
|
|
34489
|
+
"ui.settings.appearance.window_glass.unsupported_gpu": "Translucent window is unavailable because the GPU cannot composite it right now: compositing or rasterisation is disabled or running in software. A driver update or leaving a remote session may bring the control back without a restart.",
|
|
34490
|
+
"ui.settings.appearance.window_glass.unsupported_acceleration": "Translucent window is unavailable because hardware acceleration is off for this launch (--disable-gpu or a crash fallback). Relaunch with acceleration on to use it.",
|
|
34491
|
+
"ui.settings.appearance.window_glass.unsupported_backdrop_filter": "Translucent window is unavailable because this renderer cannot draw a backdrop blur."
|
|
34346
34492
|
};
|
|
34347
34493
|
const enUiSettingsConfiguration = {
|
|
34348
34494
|
"ui.settings.configuration.backend_setting_row.read_only": "Read-only",
|
|
@@ -34438,6 +34584,82 @@ const enUiSettingsConfiguration = {
|
|
|
34438
34584
|
"ui.settings.configuration.settings_filter_bar.advanced_shown": "{count, plural, one {{count, number, ::group-off} advanced setting shown — Expand all to see it} other {{count, number, ::group-off} advanced settings shown — Expand all to see them}}",
|
|
34439
34585
|
"ui.settings.configuration.settings_filter_bar.core_and_advanced": "{coreCount, plural, one {{coreCount, number, ::group-off} core setting} other {{coreCount, number, ::group-off} core settings}}, {advancedCount, plural, one {{advancedCount, number, ::group-off} advanced} other {{advancedCount, number, ::group-off} advanced}}"
|
|
34440
34586
|
};
|
|
34587
|
+
const enUiSettingsImports = {
|
|
34588
|
+
"ui.settings.imports.title": "Import conversations",
|
|
34589
|
+
"ui.settings.imports.description": "Copy conversations and curated memory from Claude Code, Codex or omp into Local Operator. The source stores are read-only.",
|
|
34590
|
+
"ui.settings.imports.source_heading": "Sources",
|
|
34591
|
+
"ui.settings.imports.source_not_found": "Not found",
|
|
34592
|
+
"ui.settings.imports.source_sessions": "{count, plural, one {# session} other {# sessions}}",
|
|
34593
|
+
"ui.settings.imports.loading_sources": "Checking for source stores...",
|
|
34594
|
+
"ui.settings.imports.loading_sessions": "Reading {source} sessions...",
|
|
34595
|
+
"ui.settings.imports.loading_preview": "Building the preview...",
|
|
34596
|
+
"ui.settings.imports.empty_sources": "No Claude Code, Codex or omp stores were detected on this machine.",
|
|
34597
|
+
"ui.settings.imports.empty_sessions": "No sessions found for {source}.",
|
|
34598
|
+
"ui.settings.imports.empty_filter": "No sessions match this search.",
|
|
34599
|
+
"ui.settings.imports.error_sources": "Could not read the source stores. {reason}",
|
|
34600
|
+
"ui.settings.imports.error_sessions": "Could not read {source} sessions. {reason}",
|
|
34601
|
+
"ui.settings.imports.error_preview": "Could not build the preview. {reason}",
|
|
34602
|
+
"ui.settings.imports.error_start": "Could not start the import. {reason}",
|
|
34603
|
+
"ui.settings.imports.error_job": "Could not read import progress. {reason}",
|
|
34604
|
+
"ui.settings.imports.error_cancel": "Could not cancel the import. {reason}",
|
|
34605
|
+
"ui.settings.imports.retry": "Retry",
|
|
34606
|
+
"ui.settings.imports.refresh": "Refresh",
|
|
34607
|
+
"ui.settings.imports.search_placeholder": "Search conversations",
|
|
34608
|
+
"ui.settings.imports.select_all": "Select all",
|
|
34609
|
+
"ui.settings.imports.clear_all": "Clear selection",
|
|
34610
|
+
"ui.settings.imports.selected": "{count, plural, one {# selected} other {# selected}}",
|
|
34611
|
+
"ui.settings.imports.selection_limit": "Only {max, number} sessions can be selected at once. Select fewer sessions or import them in batches.",
|
|
34612
|
+
"ui.settings.imports.session_meta": "{messages, plural, one {# message} other {# messages}} · {compactions, plural, one {# compaction} other {# compactions}}",
|
|
34613
|
+
"ui.settings.imports.session_updated": "Updated {date}",
|
|
34614
|
+
"ui.settings.imports.session_index_only": "Index only — no conversation body",
|
|
34615
|
+
"ui.settings.imports.preview_heading": "Preview",
|
|
34616
|
+
"ui.settings.imports.preview_summary": "{sessions, plural, one {# session} other {# sessions}} · {messages, plural, one {# message} other {# messages}} · {compactions, plural, one {# compaction} other {# compactions}}",
|
|
34617
|
+
"ui.settings.imports.preview_memory": "{records, plural, one {# memory record} other {# memory records}} · ~{tokens} tokens",
|
|
34618
|
+
"ui.settings.imports.preview_confirm": "Review the selection above, then confirm the copy.",
|
|
34619
|
+
"ui.settings.imports.preview_required": "Select at least one session and build a preview first.",
|
|
34620
|
+
"ui.settings.imports.confirm_label": "I understand these conversations will be copied into Local Operator.",
|
|
34621
|
+
"ui.settings.imports.import_button": "{count, plural, one {Import # session} other {Import # sessions}}",
|
|
34622
|
+
"ui.settings.imports.importing": "Importing",
|
|
34623
|
+
"ui.settings.imports.progress_heading": "Importing conversations",
|
|
34624
|
+
"ui.settings.imports.progress_job": "Job {job} · {state}",
|
|
34625
|
+
"ui.settings.imports.progress_pending": "{count, plural, one {# session pending} other {# sessions pending}}",
|
|
34626
|
+
"ui.settings.imports.cancel": "Cancel import",
|
|
34627
|
+
"ui.settings.imports.cancelling": "Cancelling...",
|
|
34628
|
+
"ui.settings.imports.result_heading": "Import result",
|
|
34629
|
+
"ui.settings.imports.result_summary": "{state} · {sessions, plural, one {# session} other {# sessions}} · {messages, plural, one {# message} other {# messages}}",
|
|
34630
|
+
"ui.settings.imports.result_summary_cancelled": "{state} · {sessions, number} of {total, plural, one {# session} other {# sessions}} · {messages, plural, one {# message} other {# messages}}",
|
|
34631
|
+
"ui.settings.imports.result_target": "New session {target}",
|
|
34632
|
+
"ui.settings.imports.outcome_imported": "imported",
|
|
34633
|
+
"ui.settings.imports.outcome_resumed": "resumed",
|
|
34634
|
+
"ui.settings.imports.outcome_skipped": "already imported",
|
|
34635
|
+
"ui.settings.imports.outcome_metadata": "index only — nothing to import",
|
|
34636
|
+
"ui.settings.imports.outcome_failed": "failed",
|
|
34637
|
+
"ui.settings.imports.outcome_cancelled": "cancelled",
|
|
34638
|
+
"ui.settings.imports.warnings_heading": "{count, plural, one {# warning} other {# warnings}}",
|
|
34639
|
+
"ui.settings.imports.review_instructions": "Instructions saved for review",
|
|
34640
|
+
"ui.settings.imports.review_memory": "Unattached memory saved for review",
|
|
34641
|
+
"ui.settings.imports.copy_path": "Copy path",
|
|
34642
|
+
"ui.settings.imports.copied": "Copied",
|
|
34643
|
+
"ui.settings.imports.rerun_safe": "Re-running is safe: unchanged sources are skipped, grown sources resume, rewritten sources get a new copy.",
|
|
34644
|
+
"ui.settings.imports.import_another": "Import more conversations",
|
|
34645
|
+
"ui.settings.imports.metadata_only_explain": "The source index has an entry but no conversation body; nothing can be imported. The harness may have removed the rollout.",
|
|
34646
|
+
"ui.settings.imports.state_queued": "queued",
|
|
34647
|
+
"ui.settings.imports.state_running": "running",
|
|
34648
|
+
"ui.settings.imports.state_done": "done",
|
|
34649
|
+
"ui.settings.imports.state_failed": "failed",
|
|
34650
|
+
"ui.settings.imports.state_cancelled": "cancelled",
|
|
34651
|
+
"ui.settings.imports.phase_queued": "queued",
|
|
34652
|
+
"ui.settings.imports.phase_selecting": "selecting",
|
|
34653
|
+
"ui.settings.imports.phase_importing": "importing",
|
|
34654
|
+
"ui.settings.imports.phase_memory": "reading memory",
|
|
34655
|
+
"ui.settings.imports.phase_done": "finishing",
|
|
34656
|
+
"ui.settings.imports.phase_cancelled": "cancelled",
|
|
34657
|
+
"ui.settings.imports.session_pending": "Pending",
|
|
34658
|
+
"ui.settings.imports.session_importing": "Importing",
|
|
34659
|
+
"ui.settings.imports.availability_full": "full",
|
|
34660
|
+
"ui.settings.imports.availability_metadata_only": "index only",
|
|
34661
|
+
"ui.settings.imports.availability_unsupported": "unsupported"
|
|
34662
|
+
};
|
|
34441
34663
|
const enUiSettingsIntegrations = {
|
|
34442
34664
|
"ui.settings.integrations.add_integration_form.add_integration": "Add integration",
|
|
34443
34665
|
"ui.settings.integrations.add_integration_form.name": "Name",
|
|
@@ -36318,6 +36540,7 @@ const bundledEnglish = {
|
|
|
36318
36540
|
...enUiSettingsAppearance,
|
|
36319
36541
|
...enUiSettingsAppearanceWindowGlass,
|
|
36320
36542
|
...enUiSettingsConfiguration,
|
|
36543
|
+
...enUiSettingsImports,
|
|
36321
36544
|
...enUiSettingsIntegrations,
|
|
36322
36545
|
...enUiSettings,
|
|
36323
36546
|
...enUiSettingsModelDefaultEffort,
|
|
@@ -39040,6 +39263,7 @@ const PAGE_ZOOM_STEPS = [
|
|
|
39040
39263
|
4,
|
|
39041
39264
|
5
|
|
39042
39265
|
];
|
|
39266
|
+
const PRINT_CANCELLED = /cancel/i;
|
|
39043
39267
|
class PageControls {
|
|
39044
39268
|
constructor(changed, resultTimeoutMs = 5e3) {
|
|
39045
39269
|
this.changed = changed;
|
|
@@ -39174,6 +39398,36 @@ class PageControls {
|
|
|
39174
39398
|
const factor = command === "reset" ? 1 : command === "in" ? PAGE_ZOOM_STEPS.find((step) => step > current + 1e-3) ?? 5 : [...PAGE_ZOOM_STEPS].reverse().find((step) => step < current - 1e-3) ?? 0.25;
|
|
39175
39399
|
active.view.webContents.setZoomFactor?.(factor);
|
|
39176
39400
|
}
|
|
39401
|
+
/** Print the presented page (LOH-13 slice C).
|
|
39402
|
+
*
|
|
39403
|
+
* HUMAN-ONLY, like everything in this class: it travels the sender-
|
|
39404
|
+
* authorized page-control IPC and the tab's serial lane, never the agent
|
|
39405
|
+
* protocol — an agent that needs a page as a document already has the
|
|
39406
|
+
* PDF/screenshot actions, and silently sending a page to a physical
|
|
39407
|
+
* printer is not a capability a tool call should hold.
|
|
39408
|
+
*
|
|
39409
|
+
* `silent: false` is the load-bearing option: Electron's silent mode
|
|
39410
|
+
* prints to the default printer with default settings and NO dialog,
|
|
39411
|
+
* which for a human press would be a side effect they never confirmed.
|
|
39412
|
+
* The dialog is the consent. A failure the dialog itself reports
|
|
39413
|
+
* (cancelled is the person changing their mind, not a failure) only
|
|
39414
|
+
* surfaces through `notice` when Electron names a real reason. */
|
|
39415
|
+
print(active) {
|
|
39416
|
+
if (!active.presented) return;
|
|
39417
|
+
const wc = active.view.webContents;
|
|
39418
|
+
if (wc.isDestroyed() || !wc.print) {
|
|
39419
|
+
this.notice = "Printing is not available for this page";
|
|
39420
|
+
this.noticeTabId = active.tabId;
|
|
39421
|
+
return;
|
|
39422
|
+
}
|
|
39423
|
+
this.notice = null;
|
|
39424
|
+
wc.print({ silent: false, printBackground: false }, (success, reason) => {
|
|
39425
|
+
if (success || !reason || PRINT_CANCELLED.test(reason)) return;
|
|
39426
|
+
this.notice = `Printing failed: ${reason}`;
|
|
39427
|
+
this.noticeTabId = active.tabId;
|
|
39428
|
+
this.changed();
|
|
39429
|
+
});
|
|
39430
|
+
}
|
|
39177
39431
|
/** Call BEFORE a capability transfer. Isolated per-WebContents zoom means
|
|
39178
39432
|
* resetting this tab cannot alter another tab at the same origin. */
|
|
39179
39433
|
prepareAgent(record) {
|
|
@@ -39183,6 +39437,71 @@ class PageControls {
|
|
|
39183
39437
|
if (changed) this.changed();
|
|
39184
39438
|
}
|
|
39185
39439
|
}
|
|
39440
|
+
class TabAudioTracker {
|
|
39441
|
+
constructor(changed) {
|
|
39442
|
+
this.changed = changed;
|
|
39443
|
+
}
|
|
39444
|
+
states = /* @__PURE__ */ new Map();
|
|
39445
|
+
listeners = /* @__PURE__ */ new Map();
|
|
39446
|
+
/** Start tracking one tab (idempotent) and return its current state. The
|
|
39447
|
+
* muted flag is re-read live on every call: `setAudioMuted` is also
|
|
39448
|
+
* reachable from the page's own media controls in principle, and a stale
|
|
39449
|
+
* cached flag would render a button that claims the opposite of the
|
|
39450
|
+
* page's actual state. */
|
|
39451
|
+
stateFor(tab) {
|
|
39452
|
+
this.track(tab);
|
|
39453
|
+
const state2 = this.states.get(tab.tabId) ?? {
|
|
39454
|
+
audible: false,
|
|
39455
|
+
muted: false
|
|
39456
|
+
};
|
|
39457
|
+
const wc = tab.view.webContents;
|
|
39458
|
+
if (!wc.isDestroyed()) state2.muted = wc.isAudioMuted?.() ?? state2.muted;
|
|
39459
|
+
return { ...state2 };
|
|
39460
|
+
}
|
|
39461
|
+
/** Mute or unmute one tab. Returns the resulting state, or null when the
|
|
39462
|
+
* webContents is already gone — the caller (a human press) then reports
|
|
39463
|
+
* the tab as closed rather than claiming a mute that never landed. */
|
|
39464
|
+
setMuted(tab, muted) {
|
|
39465
|
+
const wc = tab.view.webContents;
|
|
39466
|
+
if (wc.isDestroyed()) return null;
|
|
39467
|
+
this.track(tab);
|
|
39468
|
+
wc.setAudioMuted?.(muted);
|
|
39469
|
+
const state2 = this.stateFor(tab);
|
|
39470
|
+
state2.muted = wc.isAudioMuted?.() ?? muted;
|
|
39471
|
+
this.states.set(tab.tabId, state2);
|
|
39472
|
+
this.changed();
|
|
39473
|
+
return { ...state2 };
|
|
39474
|
+
}
|
|
39475
|
+
/** Drop tracking for every tab not in `liveTabIds`. Called from the
|
|
39476
|
+
* projection, which is the one place main already enumerates the live
|
|
39477
|
+
* set on every change. */
|
|
39478
|
+
prune(liveTabIds) {
|
|
39479
|
+
for (const [tabId, entry] of this.listeners) {
|
|
39480
|
+
if (liveTabIds.has(tabId)) continue;
|
|
39481
|
+
this.listeners.delete(tabId);
|
|
39482
|
+
this.states.delete(tabId);
|
|
39483
|
+
if (!entry.wc.isDestroyed())
|
|
39484
|
+
entry.wc.removeListener("audio-state-changed", entry.listener);
|
|
39485
|
+
}
|
|
39486
|
+
}
|
|
39487
|
+
track(tab) {
|
|
39488
|
+
if (this.listeners.has(tab.tabId)) return;
|
|
39489
|
+
const wc = tab.view.webContents;
|
|
39490
|
+
this.states.set(tab.tabId, {
|
|
39491
|
+
audible: wc.isDestroyed() ? false : wc.isCurrentlyAudible?.() ?? false,
|
|
39492
|
+
muted: wc.isDestroyed() ? false : wc.isAudioMuted?.() ?? false
|
|
39493
|
+
});
|
|
39494
|
+
const listener = (event) => {
|
|
39495
|
+
const current = this.states.get(tab.tabId);
|
|
39496
|
+
if (!current) return;
|
|
39497
|
+
current.audible = event?.audible ?? false;
|
|
39498
|
+
this.states.set(tab.tabId, current);
|
|
39499
|
+
this.changed();
|
|
39500
|
+
};
|
|
39501
|
+
this.listeners.set(tab.tabId, { wc, listener });
|
|
39502
|
+
if (!wc.isDestroyed()) wc.on("audio-state-changed", listener);
|
|
39503
|
+
}
|
|
39504
|
+
}
|
|
39186
39505
|
const DUPLICATABLE_WEB_URL = /^https?:\/\//i;
|
|
39187
39506
|
const DOCUMENT_SCOPED = /* @__PURE__ */ new Set([
|
|
39188
39507
|
"read",
|
|
@@ -39272,6 +39591,10 @@ class BrowserHost {
|
|
|
39272
39591
|
chromeTabOrder = new ChromeTabOrder();
|
|
39273
39592
|
registry;
|
|
39274
39593
|
pageControls;
|
|
39594
|
+
/** Per-tab audio for the strip's mute control (LOH-13 slice C). Main owns
|
|
39595
|
+
* it for the reason `tabLoading` is read here: a background tab's sound
|
|
39596
|
+
* is legible only through this projection. */
|
|
39597
|
+
tabAudio = new TabAudioTracker(() => this.onChanged());
|
|
39275
39598
|
cdp;
|
|
39276
39599
|
approvals;
|
|
39277
39600
|
ownership;
|
|
@@ -39581,6 +39904,9 @@ class BrowserHost {
|
|
|
39581
39904
|
const activeRecord = this.registry.activeTab;
|
|
39582
39905
|
const active = activeRecord && !activeRecord.view.webContents.isDestroyed() ? activeRecord : null;
|
|
39583
39906
|
const navigation = active?.view.webContents.navigationHistory;
|
|
39907
|
+
this.tabAudio.prune(
|
|
39908
|
+
new Set(this.registry.snapshot().map((entry) => entry.tabId))
|
|
39909
|
+
);
|
|
39584
39910
|
return {
|
|
39585
39911
|
pageControls: this.pageControls.state(active),
|
|
39586
39912
|
tabs: this.chromeTabOrder.project(this.registry.snapshot()).map((entry, displayOrder) => ({
|
|
@@ -39616,7 +39942,20 @@ class BrowserHost {
|
|
|
39616
39942
|
// was the whole of what the user saw of an agent's work. Read from the
|
|
39617
39943
|
// record rather than the snapshot: `snapshot()` is the diagnostic
|
|
39618
39944
|
// projection shared with `status`/`tabs`, and this field is chrome.
|
|
39619
|
-
loading: this.tabLoading(entry.tabId)
|
|
39945
|
+
loading: this.tabLoading(entry.tabId),
|
|
39946
|
+
// PER TAB, projected with `loading` and for the same reason: a
|
|
39947
|
+
// background tab making sound has no other surface. `audible`
|
|
39948
|
+
// gates the mute control's visibility; `muted` is its state.
|
|
39949
|
+
// Read from the record (guarded like `tabLoading`) because the
|
|
39950
|
+
// snapshot carries no webContents.
|
|
39951
|
+
audible: (() => {
|
|
39952
|
+
const record = this.registry.get(entry.tabId);
|
|
39953
|
+
return record && !record.view.webContents.isDestroyed() ? this.tabAudio.stateFor(record).audible : false;
|
|
39954
|
+
})(),
|
|
39955
|
+
muted: (() => {
|
|
39956
|
+
const record = this.registry.get(entry.tabId);
|
|
39957
|
+
return record && !record.view.webContents.isDestroyed() ? this.tabAudio.stateFor(record).muted : false;
|
|
39958
|
+
})()
|
|
39620
39959
|
})),
|
|
39621
39960
|
activeTabId: activeRecord?.tabId ?? null,
|
|
39622
39961
|
// The download surface's facts (§16.4): what is in flight, where it went and
|
|
@@ -39718,8 +40057,23 @@ class BrowserHost {
|
|
|
39718
40057
|
error: { code: "tab_closed", message: t("ui.browser.chrome.tab_gone") }
|
|
39719
40058
|
};
|
|
39720
40059
|
let url = "";
|
|
39721
|
-
let destinationId = action === "
|
|
40060
|
+
let destinationId = action === "duplicate" ? null : tabId;
|
|
39722
40061
|
try {
|
|
40062
|
+
if (action === "mute" || action === "unmute") {
|
|
40063
|
+
const result = this.tabAudio.setMuted(record, action === "mute");
|
|
40064
|
+
if (!result)
|
|
40065
|
+
return {
|
|
40066
|
+
status: "failed",
|
|
40067
|
+
action,
|
|
40068
|
+
tabId: null,
|
|
40069
|
+
error: {
|
|
40070
|
+
code: "tab_closed",
|
|
40071
|
+
message: t("ui.browser.chrome.tab_gone")
|
|
40072
|
+
}
|
|
40073
|
+
};
|
|
40074
|
+
this.onChanged();
|
|
40075
|
+
return { status: "completed", action, tabId };
|
|
40076
|
+
}
|
|
39723
40077
|
url = record.view.webContents.getURL();
|
|
39724
40078
|
if (action === "duplicate" && url !== "about:blank" && !DUPLICATABLE_WEB_URL.test(url))
|
|
39725
40079
|
return {
|
|
@@ -39788,6 +40142,7 @@ class BrowserHost {
|
|
|
39788
40142
|
if (!active || active.tabId !== tabId || !active.presented || active.view.webContents.isDestroyed())
|
|
39789
40143
|
throw new Error("The browser tab is no longer presented.");
|
|
39790
40144
|
if (action === "open") this.pageControls.open(active);
|
|
40145
|
+
else if (action === "print") this.pageControls.print(active);
|
|
39791
40146
|
else if (action === "close") this.pageControls.clear();
|
|
39792
40147
|
else if (action === "search")
|
|
39793
40148
|
this.pageControls.search(active, query, forward, next);
|
|
@@ -40527,7 +40882,8 @@ class DownloadArmer {
|
|
|
40527
40882
|
deadlineTimer: null,
|
|
40528
40883
|
capTimer: null,
|
|
40529
40884
|
finish: null,
|
|
40530
|
-
settled: false
|
|
40885
|
+
settled: false,
|
|
40886
|
+
human: false
|
|
40531
40887
|
};
|
|
40532
40888
|
this.captures.set(tabId, capture);
|
|
40533
40889
|
const deadline2 = setTimeout(() => this.settle(capture), timeoutMs);
|
|
@@ -40582,9 +40938,15 @@ class DownloadArmer {
|
|
|
40582
40938
|
*/
|
|
40583
40939
|
decide(item, webContentsId) {
|
|
40584
40940
|
const tabId = this.options.tabForWebContents(webContentsId);
|
|
40585
|
-
|
|
40941
|
+
let capture = tabId === null ? void 0 : this.captures.get(tabId);
|
|
40586
40942
|
if (!capture || capture.settled) {
|
|
40587
|
-
|
|
40943
|
+
capture = (tabId !== null ? this.humanCapture(tabId) : null) ?? void 0;
|
|
40944
|
+
if (!capture) {
|
|
40945
|
+
return {
|
|
40946
|
+
cancel: true,
|
|
40947
|
+
reason: "no download call is armed on this tab"
|
|
40948
|
+
};
|
|
40949
|
+
}
|
|
40588
40950
|
}
|
|
40589
40951
|
const raw = item.getFilename();
|
|
40590
40952
|
const clean = safeName(raw);
|
|
@@ -40706,6 +41068,58 @@ class DownloadArmer {
|
|
|
40706
41068
|
const dirs = this.notes.filter((note) => note.dir !== "").map((n) => n.dir);
|
|
40707
41069
|
return [...this.captures.values()].at(-1)?.dir ?? dirs.at(-1) ?? null;
|
|
40708
41070
|
}
|
|
41071
|
+
/** Build (or reuse) the human capture for one user-owned tab.
|
|
41072
|
+
*
|
|
41073
|
+
* Returns null — leaving the caller to cancel — when this host cannot
|
|
41074
|
+
* honestly call the download human-initiated: the tab is agent-owned or
|
|
41075
|
+
* handed to an agent (`ownerKindOf` reads the registry's CURRENT owner,
|
|
41076
|
+
* which hand-over flips), the armer was built without a human directory,
|
|
41077
|
+
* or that directory cannot be made. A settled human capture is replaced
|
|
41078
|
+
* rather than reused, so its file count bounds one burst (up to the
|
|
41079
|
+
* quiet window after the last write) and never the session.
|
|
41080
|
+
*
|
|
41081
|
+
* The directory is NOT chmodded, unlike `arm`: it is the person's own
|
|
41082
|
+
* downloads folder, which predates this app and whose mode is theirs.
|
|
41083
|
+
* The runtime-cap sampler IS started, for the reason `CAP_SAMPLE_MS`
|
|
41084
|
+
* states: an unknown-size human download has the same disk to fill as
|
|
41085
|
+
* an agent's. */
|
|
41086
|
+
humanCapture(tabId) {
|
|
41087
|
+
if (this.ownerKindOf(tabId) !== "user") return null;
|
|
41088
|
+
const dir = this.options.humanDownloadDir?.() ?? null;
|
|
41089
|
+
if (!dir) return null;
|
|
41090
|
+
try {
|
|
41091
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
41092
|
+
} catch {
|
|
41093
|
+
return null;
|
|
41094
|
+
}
|
|
41095
|
+
const capture = {
|
|
41096
|
+
tabId,
|
|
41097
|
+
dir,
|
|
41098
|
+
timeoutMs: 0,
|
|
41099
|
+
startedAt: (this.options.now ?? Date.now)(),
|
|
41100
|
+
files: [],
|
|
41101
|
+
refusals: [],
|
|
41102
|
+
pending: 0,
|
|
41103
|
+
started: false,
|
|
41104
|
+
live: [],
|
|
41105
|
+
handedOut: /* @__PURE__ */ new Set(),
|
|
41106
|
+
active: null,
|
|
41107
|
+
lastProgressAt: 0,
|
|
41108
|
+
quietTimer: null,
|
|
41109
|
+
deadlineTimer: null,
|
|
41110
|
+
capTimer: null,
|
|
41111
|
+
finish: null,
|
|
41112
|
+
settled: false,
|
|
41113
|
+
human: true
|
|
41114
|
+
};
|
|
41115
|
+
capture.capTimer = setInterval(
|
|
41116
|
+
() => this.sampleCaps(capture),
|
|
41117
|
+
CAP_SAMPLE_MS
|
|
41118
|
+
);
|
|
41119
|
+
capture.capTimer.unref?.();
|
|
41120
|
+
this.captures.set(tabId, capture);
|
|
41121
|
+
return capture;
|
|
41122
|
+
}
|
|
40709
41123
|
/** One upload's own line (review round 1, U3).
|
|
40710
41124
|
*
|
|
40711
41125
|
* WHY AN UPLOAD HAS A NOTE AT ALL, when §16.4 deliberately has no upload
|
|
@@ -40777,7 +41191,7 @@ class DownloadArmer {
|
|
|
40777
41191
|
if (entry.weCancelled) {
|
|
40778
41192
|
if (state2 !== "completed") discardPartial(savePath, this.options.log);
|
|
40779
41193
|
} else if (state2 === "completed") {
|
|
40780
|
-
restrictMode(savePath, this.options.log);
|
|
41194
|
+
if (!capture.human) restrictMode(savePath, this.options.log);
|
|
40781
41195
|
capture.files.push(fileFact(item, savePath));
|
|
40782
41196
|
this.note(capture, name, "saved", 1, "", null);
|
|
40783
41197
|
} else {
|
|
@@ -40933,6 +41347,9 @@ class DownloadArmer {
|
|
|
40933
41347
|
capture.settled = true;
|
|
40934
41348
|
capture.active = null;
|
|
40935
41349
|
this.clearTimers(capture);
|
|
41350
|
+
if (this.captures.get(capture.tabId) === capture) {
|
|
41351
|
+
this.captures.delete(capture.tabId);
|
|
41352
|
+
}
|
|
40936
41353
|
capture.finish?.();
|
|
40937
41354
|
this.options.onActivity?.();
|
|
40938
41355
|
}
|
|
@@ -40963,6 +41380,7 @@ class DownloadArmer {
|
|
|
40963
41380
|
capture.refusals.push(reason);
|
|
40964
41381
|
this.note(capture, clean, "refused", 1, reason, refusal);
|
|
40965
41382
|
this.options.log(`[browser] ${reason}`);
|
|
41383
|
+
this.options.onActivity?.();
|
|
40966
41384
|
return { cancel: true, reason };
|
|
40967
41385
|
}
|
|
40968
41386
|
note(capture, name, outcome, count, reason, refusal) {
|
|
@@ -41215,7 +41633,7 @@ function registerBrowserIpc(options) {
|
|
|
41215
41633
|
"browser-tab-action",
|
|
41216
41634
|
(event, tabId, action) => {
|
|
41217
41635
|
const host = authorize(event);
|
|
41218
|
-
if (action !== "duplicate" && action !== "reload")
|
|
41636
|
+
if (action !== "duplicate" && action !== "reload" && action !== "mute" && action !== "unmute")
|
|
41219
41637
|
throw new Error(t("ui.browser.chrome.invalid_action"));
|
|
41220
41638
|
return host.actOnChromeTab(numberOrThrow(tabId, "tabId"), action);
|
|
41221
41639
|
}
|
|
@@ -41243,7 +41661,9 @@ function registerBrowserIpc(options) {
|
|
|
41243
41661
|
"browser-page-control",
|
|
41244
41662
|
(event, tabId, action, query, forward, next) => {
|
|
41245
41663
|
const host = authorize(event);
|
|
41246
|
-
if (typeof action !== "string" || !["open", "close", "search", "in", "out", "reset"].includes(
|
|
41664
|
+
if (typeof action !== "string" || !["open", "close", "search", "print", "in", "out", "reset"].includes(
|
|
41665
|
+
action
|
|
41666
|
+
))
|
|
41247
41667
|
throw new Error("Invalid page control action.");
|
|
41248
41668
|
if (typeof query !== "string" || query.length > 4096 || typeof forward !== "boolean" || typeof next !== "boolean")
|
|
41249
41669
|
throw new Error("Invalid page search input.");
|
|
@@ -43623,6 +44043,17 @@ async function initializeBrowserHost(options, bindings) {
|
|
|
43623
44043
|
// exists is a marker pointing at nothing. `null` when the lookup cannot answer
|
|
43624
44044
|
// (a tab already gone), which the row renders the old way rather than guessing.
|
|
43625
44045
|
ownerKindFor: (tabId) => registryForDownloads?.get(tabId)?.owner ?? null,
|
|
44046
|
+
// The human download destination (LOH-13 slice C): the person's
|
|
44047
|
+
// own downloads folder, read from the app at decision time. Main
|
|
44048
|
+
// composes it here for the same reason the reveal channel takes
|
|
44049
|
+
// no path: no renderer or page string ever becomes a directory.
|
|
44050
|
+
humanDownloadDir: () => electron.app.getPath("downloads"),
|
|
44051
|
+
// NO `humanDeadlineMs` WIRING (review round 2, MAJOR): the wall-clock
|
|
44052
|
+
// budget this used to pass settled — and cancelled, discarding the
|
|
44053
|
+
// partial — a legitimate in-flight human download at 120 s. A human
|
|
44054
|
+
// capture now ends only on a guard, the quiet window, or the tab.
|
|
44055
|
+
// `COMMAND_TIMEOUTS_S.download` remains the AGENT call's own budget,
|
|
44056
|
+
// passed by its caller and unchanged.
|
|
43626
44057
|
log: log2,
|
|
43627
44058
|
onActivity: () => {
|
|
43628
44059
|
if (options.window.isDestroyed()) return;
|
|
@@ -45640,7 +46071,13 @@ function miniGlassPlan(input) {
|
|
|
45640
46071
|
function miniDocumentState(state2, launchedGlassOn) {
|
|
45641
46072
|
return {
|
|
45642
46073
|
...state2,
|
|
45643
|
-
|
|
46074
|
+
/*
|
|
46075
|
+
* Same rule as the main window's `stateFor`: a restart is only pending for a
|
|
46076
|
+
* SUPPORTED change that cannot be applied live. Without the `supported` term a
|
|
46077
|
+
* capability drop (`mechanism: "none"`, also `liveToggle: false`) reads as a
|
|
46078
|
+
* restart-pending change in the popout as well.
|
|
46079
|
+
*/
|
|
46080
|
+
restartRequired: state2.capability.supported && !state2.capability.liveToggle && state2.active !== launchedGlassOn
|
|
45644
46081
|
};
|
|
45645
46082
|
}
|
|
45646
46083
|
function createMiniView(options) {
|
|
@@ -53901,21 +54338,46 @@ function attachWindowChrome(options) {
|
|
|
53901
54338
|
function windowGlassFilePath(userDataDir) {
|
|
53902
54339
|
return path$1.join(userDataDir, WINDOW_GLASS_FILE);
|
|
53903
54340
|
}
|
|
54341
|
+
let gpuInfoReported = false;
|
|
54342
|
+
electron.app.on("gpu-info-update", () => {
|
|
54343
|
+
gpuInfoReported = true;
|
|
54344
|
+
});
|
|
54345
|
+
function readGlassEnvironmentFacts() {
|
|
54346
|
+
const facts = {};
|
|
54347
|
+
try {
|
|
54348
|
+
facts.accelerationDisabledByLaunch = electron.app.commandLine.hasSwitch("disable-gpu");
|
|
54349
|
+
} catch {
|
|
54350
|
+
facts.accelerationDisabledByLaunch = false;
|
|
54351
|
+
}
|
|
54352
|
+
try {
|
|
54353
|
+
facts.hardwareAcceleration = electron.app.isHardwareAccelerationEnabled();
|
|
54354
|
+
} catch {
|
|
54355
|
+
facts.hardwareAcceleration = null;
|
|
54356
|
+
}
|
|
54357
|
+
try {
|
|
54358
|
+
const status2 = gpuInfoReported ? electron.app.getGPUFeatureStatus() : null;
|
|
54359
|
+
facts.gpu = status2 ? {
|
|
54360
|
+
compositing: status2.gpu_compositing,
|
|
54361
|
+
rasterization: status2.rasterization
|
|
54362
|
+
} : null;
|
|
54363
|
+
} catch {
|
|
54364
|
+
facts.gpu = null;
|
|
54365
|
+
}
|
|
54366
|
+
return facts;
|
|
54367
|
+
}
|
|
53904
54368
|
function readPersistedWindowGlass(userDataDir, capability) {
|
|
53905
54369
|
const raw = readJson(
|
|
53906
54370
|
windowGlassFilePath(userDataDir)
|
|
53907
54371
|
);
|
|
53908
|
-
|
|
53909
|
-
return {
|
|
53910
|
-
settings: sanitizeGlassSettings(raw.settings, capability),
|
|
53911
|
-
scheme: raw.scheme === "light" ? "light" : "dark"
|
|
53912
|
-
};
|
|
54372
|
+
return resolveStoredGlass(raw, capability);
|
|
53913
54373
|
}
|
|
53914
54374
|
function resolveLaunchWindowGlass(input) {
|
|
54375
|
+
const environment = input.environment ?? readGlassEnvironmentFacts();
|
|
53915
54376
|
const capability = glassCapabilityFor({
|
|
53916
54377
|
platform: input.platform ?? process.platform,
|
|
53917
54378
|
osVersion: input.osVersion ?? process.getSystemVersion(),
|
|
53918
|
-
reducedTransparency: input.reducedTransparency ?? electron.nativeTheme.prefersReducedTransparency
|
|
54379
|
+
reducedTransparency: input.reducedTransparency ?? electron.nativeTheme.prefersReducedTransparency,
|
|
54380
|
+
environment
|
|
53919
54381
|
});
|
|
53920
54382
|
let persisted = null;
|
|
53921
54383
|
try {
|
|
@@ -53923,13 +54385,16 @@ function resolveLaunchWindowGlass(input) {
|
|
|
53923
54385
|
} catch {
|
|
53924
54386
|
persisted = null;
|
|
53925
54387
|
}
|
|
53926
|
-
const settings = persisted?.settings ??
|
|
54388
|
+
const settings = persisted?.settings ?? defaultGlassSettings(capability);
|
|
54389
|
+
const explicit = persisted?.explicit ?? [];
|
|
53927
54390
|
const scheme = persisted?.scheme ?? "dark";
|
|
53928
54391
|
return {
|
|
53929
54392
|
capability,
|
|
53930
54393
|
settings,
|
|
54394
|
+
explicit,
|
|
53931
54395
|
scheme,
|
|
53932
|
-
state: stateFor(settings, capability)
|
|
54396
|
+
state: stateFor(settings, capability),
|
|
54397
|
+
environment
|
|
53933
54398
|
};
|
|
53934
54399
|
}
|
|
53935
54400
|
const stateFor = (settings, capability, launchedActive = glassIsActive(settings, capability)) => {
|
|
@@ -53938,7 +54403,16 @@ const stateFor = (settings, capability, launchedActive = glassIsActive(settings,
|
|
|
53938
54403
|
capability,
|
|
53939
54404
|
settings,
|
|
53940
54405
|
active,
|
|
53941
|
-
|
|
54406
|
+
/*
|
|
54407
|
+
* A RESTART IS ONLY PENDING WHEN THE EFFECT IS AVAILABLE AND NOT LIVE.
|
|
54408
|
+
* `liveToggle: false` is also true of `mechanism: "none"`, so a capability
|
|
54409
|
+
* DROP (GPU software fallback, backdrop-filter report, Reduce transparency)
|
|
54410
|
+
* satisfied the old expression and told the user to restart while the window
|
|
54411
|
+
* had already fallen back to opaque. The unsupported state has its own
|
|
54412
|
+
* cause line; this notice is only for a supported change that needs a new
|
|
54413
|
+
* window (QA Q-1 / design D2).
|
|
54414
|
+
*/
|
|
54415
|
+
restartRequired: capability.supported && !capability.liveToggle && active !== launchedActive
|
|
53942
54416
|
};
|
|
53943
54417
|
};
|
|
53944
54418
|
function attachWindowGlass(options) {
|
|
@@ -53946,6 +54420,8 @@ function attachWindowGlass(options) {
|
|
|
53946
54420
|
let capability = options.initial.capability;
|
|
53947
54421
|
let settings = options.initial.settings;
|
|
53948
54422
|
let scheme = options.initial.scheme;
|
|
54423
|
+
const explicit = new Set(options.initial.explicit);
|
|
54424
|
+
let environment = { ...options.initial.environment };
|
|
53949
54425
|
const launchedActive = glassIsActive(settings, capability);
|
|
53950
54426
|
let pinnedTheme = false;
|
|
53951
54427
|
let appliedMechanism = launchedActive ? capability.mechanism : "none";
|
|
@@ -53989,7 +54465,17 @@ function attachWindowGlass(options) {
|
|
|
53989
54465
|
}
|
|
53990
54466
|
};
|
|
53991
54467
|
const persist = () => {
|
|
53992
|
-
if (!writeJsonAtomic(windowGlassFilePath(userDataDir), {
|
|
54468
|
+
if (!writeJsonAtomic(windowGlassFilePath(userDataDir), {
|
|
54469
|
+
/*
|
|
54470
|
+
* Versioned and per-field: the next launch fills only what this list does
|
|
54471
|
+
* not name from its own defaults. Writing the full `settings` object alone
|
|
54472
|
+
* is what made an untouched OFF indistinguishable from a chosen one.
|
|
54473
|
+
*/
|
|
54474
|
+
version: GLASS_FILE_VERSION,
|
|
54475
|
+
settings,
|
|
54476
|
+
explicit: GLASS_SETTINGS_FIELDS.filter((field) => explicit.has(field)),
|
|
54477
|
+
scheme
|
|
54478
|
+
}))
|
|
53993
54479
|
log2(
|
|
53994
54480
|
"[window-glass] the setting could not be saved - it applies to this run and will not survive a restart"
|
|
53995
54481
|
);
|
|
@@ -54010,6 +54496,11 @@ function attachWindowGlass(options) {
|
|
|
54010
54496
|
(event, patch) => {
|
|
54011
54497
|
if (!fromOwnWindow(event)) return null;
|
|
54012
54498
|
settings = sanitizeGlassSettings(patch, capability, settings);
|
|
54499
|
+
if (patch !== null && typeof patch === "object") {
|
|
54500
|
+
for (const field of Object.keys(patch))
|
|
54501
|
+
if (GLASS_SETTINGS_FIELDS.includes(field))
|
|
54502
|
+
explicit.add(field);
|
|
54503
|
+
}
|
|
54013
54504
|
applyToWindow();
|
|
54014
54505
|
persist();
|
|
54015
54506
|
push2();
|
|
@@ -54028,23 +54519,47 @@ function attachWindowGlass(options) {
|
|
|
54028
54519
|
persist();
|
|
54029
54520
|
return true;
|
|
54030
54521
|
});
|
|
54031
|
-
|
|
54522
|
+
electron.ipcMain.handle(WINDOW_GLASS_RENDERER_CHANNEL, (event, report) => {
|
|
54523
|
+
if (!fromOwnWindow(event)) return null;
|
|
54524
|
+
if (report !== null && typeof report === "object") {
|
|
54525
|
+
const value = report.backdropFilter;
|
|
54526
|
+
if (typeof value === "boolean")
|
|
54527
|
+
environment = { ...environment, backdropFilter: value };
|
|
54528
|
+
}
|
|
54529
|
+
reevaluate();
|
|
54530
|
+
return current();
|
|
54531
|
+
});
|
|
54532
|
+
function reevaluate() {
|
|
54533
|
+
environment = {
|
|
54534
|
+
...readGlassEnvironmentFacts(),
|
|
54535
|
+
...environment.backdropFilter === void 0 ? {} : { backdropFilter: environment.backdropFilter }
|
|
54536
|
+
};
|
|
54032
54537
|
const next = glassCapabilityFor({
|
|
54033
54538
|
platform: process.platform,
|
|
54034
54539
|
osVersion: process.getSystemVersion(),
|
|
54035
|
-
reducedTransparency: electron.nativeTheme.prefersReducedTransparency
|
|
54540
|
+
reducedTransparency: electron.nativeTheme.prefersReducedTransparency,
|
|
54541
|
+
environment
|
|
54036
54542
|
});
|
|
54037
|
-
if (next.supported === capability.supported && next.reason === capability.reason)
|
|
54543
|
+
if (next.supported === capability.supported && next.reason === capability.reason && next.mechanism === capability.mechanism)
|
|
54038
54544
|
return;
|
|
54545
|
+
const wasSupported = capability.supported;
|
|
54039
54546
|
capability = next;
|
|
54040
54547
|
settings = sanitizeGlassSettings(settings, capability, settings);
|
|
54548
|
+
if (next.supported && !wasSupported && !explicit.has("enabled"))
|
|
54549
|
+
settings.enabled = defaultGlassSettings(next).enabled;
|
|
54041
54550
|
applyToWindow();
|
|
54042
54551
|
push2();
|
|
54043
54552
|
log2(
|
|
54044
54553
|
`[window-glass] capability changed supported=${next.supported} reason=${next.reason}`
|
|
54045
54554
|
);
|
|
54046
|
-
}
|
|
54555
|
+
}
|
|
54556
|
+
const onNativeThemeUpdated = () => reevaluate();
|
|
54047
54557
|
electron.nativeTheme.on("updated", onNativeThemeUpdated);
|
|
54558
|
+
electron.app.on("gpu-info-update", reevaluate);
|
|
54559
|
+
const onChildProcessGone = (_event, details) => {
|
|
54560
|
+
if (details?.type === "GPU") setTimeout(reevaluate, 0);
|
|
54561
|
+
};
|
|
54562
|
+
electron.app.on("child-process-gone", onChildProcessGone);
|
|
54048
54563
|
if (glassIsActive(settings, capability)) {
|
|
54049
54564
|
electron.nativeTheme.themeSource = scheme;
|
|
54050
54565
|
pinnedTheme = true;
|
|
@@ -54055,7 +54570,10 @@ function attachWindowGlass(options) {
|
|
|
54055
54570
|
electron.ipcMain.removeHandler(WINDOW_GLASS_GET_CHANNEL);
|
|
54056
54571
|
electron.ipcMain.removeHandler(WINDOW_GLASS_SET_CHANNEL);
|
|
54057
54572
|
electron.ipcMain.removeHandler(WINDOW_GLASS_SCHEME_CHANNEL);
|
|
54573
|
+
electron.ipcMain.removeHandler(WINDOW_GLASS_RENDERER_CHANNEL);
|
|
54058
54574
|
electron.nativeTheme.removeListener("updated", onNativeThemeUpdated);
|
|
54575
|
+
electron.app.removeListener("gpu-info-update", reevaluate);
|
|
54576
|
+
electron.app.removeListener("child-process-gone", onChildProcessGone);
|
|
54059
54577
|
if (pinnedTheme) electron.nativeTheme.themeSource = "system";
|
|
54060
54578
|
}
|
|
54061
54579
|
};
|
|
@@ -54808,6 +55326,7 @@ const authPopupWindowOptions = {
|
|
|
54808
55326
|
disableDialogs: true
|
|
54809
55327
|
}
|
|
54810
55328
|
};
|
|
55329
|
+
let glassGateLogged = false;
|
|
54811
55330
|
function createWindow(initialSession = null, openCatalogue = false, request = ownLaunchRequest()) {
|
|
54812
55331
|
const chrome = resolveLaunchWindowChrome({
|
|
54813
55332
|
argv: process.argv,
|
|
@@ -54822,6 +55341,13 @@ function createWindow(initialSession = null, openCatalogue = false, request = ow
|
|
|
54822
55341
|
const glass = resolveLaunchWindowGlass({
|
|
54823
55342
|
userDataDir: electron.app.getPath("userData")
|
|
54824
55343
|
});
|
|
55344
|
+
if (!glassGateLogged) {
|
|
55345
|
+
glassGateLogged = true;
|
|
55346
|
+
logger.info(
|
|
55347
|
+
glassGateLine(glass.capability, glass.settings, process.platform),
|
|
55348
|
+
LogFileType.BACKEND
|
|
55349
|
+
);
|
|
55350
|
+
}
|
|
54825
55351
|
const mainWindow2 = new electron.BrowserWindow({
|
|
54826
55352
|
/*
|
|
54827
55353
|
* The frame, per platform, from a pure function so the matrix is exercised on
|