dsh-windows-mcp 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +67 -1
- package/LICENSE +0 -6
- package/README.md +49 -2
- package/README.zh.md +48 -2
- package/lib/client.js +728 -11
- package/lib/dependency.js +150 -23
- package/lib/index.js +267 -27
- package/lib/installs.js +196 -0
- package/lib/mirrors.js +244 -0
- package/lib/pypi.js +171 -0
- package/lib/settings.js +251 -0
- package/lib/updater.js +502 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -31,6 +31,15 @@ window.__ModuleLoader__.load({
|
|
|
31
31
|
var PREPARE_PATH = "/dsh-windows-mcp/prepare";
|
|
32
32
|
var PREPARE_CANCEL_PATH = "/dsh-windows-mcp/prepare/cancel";
|
|
33
33
|
var BRIDGE_PATH = "/dsh-windows-mcp/bridge";
|
|
34
|
+
var UPDATE_PATH = "/dsh-windows-mcp/update";
|
|
35
|
+
var SETTINGS_PATH = "/dsh-windows-mcp/settings";
|
|
36
|
+
var SETTINGS_SLOT = "settings.section";
|
|
37
|
+
var SETTINGS_ENTRY_ID = "windows-mcp-settings";
|
|
38
|
+
var SETTINGS_ORDER = 62;
|
|
39
|
+
var SETTINGS_NAV_ICON_MARKER = "data-dwmc-nav-icon";
|
|
40
|
+
var SETTINGS_NAV_ICON_STYLE_ID = PACKAGE_ID + "-settings-nav-icon-styles";
|
|
41
|
+
var SETTINGS_NAV_ROW_SELECTOR = '[role="dialog"] nav button';
|
|
42
|
+
var REPO_URL = "https://github.com/helloHub1212/dsh-windows-mcp";
|
|
34
43
|
var STYLE_ID = PACKAGE_ID + "-computer-use-styles";
|
|
35
44
|
var LABEL = "Computer Use";
|
|
36
45
|
/** Only `slots` is injected: it is certainly provided, and a declaration for
|
|
@@ -90,6 +99,35 @@ window.__ModuleLoader__.load({
|
|
|
90
99
|
"color:var(--dsw-alias-label-secondary);white-space:pre-wrap;word-break:break-all}",
|
|
91
100
|
".dwmc-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px}",
|
|
92
101
|
"@media (prefers-reduced-motion:reduce){.dwmc-progress>i{animation:none}}",
|
|
102
|
+
/* Settings page: plain cards, one control per row, theme tokens only. */
|
|
103
|
+
".dwmc-set{display:flex;flex-direction:column;gap:18px;max-width:720px;",
|
|
104
|
+
"color:var(--dsw-alias-label-primary);font-size:13px;line-height:1.55}",
|
|
105
|
+
".dwmc-set__head{display:flex;flex-direction:column;gap:4px}",
|
|
106
|
+
".dwmc-set__title{margin:0;font-size:16px;font-weight:600}",
|
|
107
|
+
".dwmc-set__sub{margin:0;font-size:12px;color:var(--dsw-alias-label-secondary)}",
|
|
108
|
+
".dwmc-card{border:1px solid var(--dsw-alias-border-l1);border-radius:12px;",
|
|
109
|
+
"background:var(--dsw-alias-bg-layer-1);padding:14px 16px;display:flex;flex-direction:column;gap:12px}",
|
|
110
|
+
".dwmc-card__title{margin:0;font-size:13px;font-weight:600}",
|
|
111
|
+
".dwmc-field{display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap}",
|
|
112
|
+
".dwmc-field__label{display:flex;flex-direction:column;gap:2px;min-width:180px}",
|
|
113
|
+
".dwmc-field__hint{font-size:12px;color:var(--dsw-alias-label-secondary)}",
|
|
114
|
+
".dwmc-input,.dwmc-select{font:inherit;font-size:12px;color:var(--dsw-alias-label-primary);",
|
|
115
|
+
"background:var(--dsw-alias-bg-base);border:1px solid var(--dsw-alias-border-l1);",
|
|
116
|
+
"border-radius:8px;padding:5px 8px;min-width:120px}",
|
|
117
|
+
".dwmc-input:focus-visible,.dwmc-select:focus-visible{outline:2px solid",
|
|
118
|
+
"color-mix(in srgb,var(--dsw-alias-brand-primary) 45%,transparent);outline-offset:1px}",
|
|
119
|
+
".dwmc-checks{display:flex;flex-wrap:wrap;gap:6px 14px}",
|
|
120
|
+
".dwmc-check{display:inline-flex;align-items:center;gap:6px;font-size:12px;",
|
|
121
|
+
"color:var(--dsw-alias-label-primary);cursor:pointer}",
|
|
122
|
+
".dwmc-check input{accent-color:var(--dsw-alias-brand-primary);margin:0}",
|
|
123
|
+
".dwmc-set__actions{display:flex;align-items:center;gap:10px;flex-wrap:wrap}",
|
|
124
|
+
".dwmc-star{display:inline-flex;align-items:center;gap:7px;height:30px;padding:0 14px;font:inherit;",
|
|
125
|
+
"font-size:12px;cursor:pointer;border-radius:8px;border:1px solid transparent;background:transparent;",
|
|
126
|
+
"color:var(--dsw-alias-brand-primary);",
|
|
127
|
+
"box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--dsw-alias-brand-primary) 42%,transparent)}",
|
|
128
|
+
".dwmc-star:hover{background:color-mix(in srgb,var(--dsw-alias-brand-primary) 12%,transparent)}",
|
|
129
|
+
".dwmc-star svg{width:14px;height:14px;display:block}",
|
|
130
|
+
".dwmc-saved{font-size:12px;color:var(--dsw-alias-state-success-primary)}",
|
|
93
131
|
].join("");
|
|
94
132
|
|
|
95
133
|
/** Inject the stylesheet once. Failures are cosmetic and never fatal. */
|
|
@@ -300,6 +338,53 @@ window.__ModuleLoader__.load({
|
|
|
300
338
|
ready: "依赖已就绪,桌面工具可用(记得把 Computer Use 开关打开)。",
|
|
301
339
|
restart: "已把桥指向新安装的版本,重启 DSH 后生效。",
|
|
302
340
|
hostDown: "连不上宿主",
|
|
341
|
+
version: "运行版本",
|
|
342
|
+
latest: "最新版本",
|
|
343
|
+
previous: "可回退",
|
|
344
|
+
kept: "已保留版本",
|
|
345
|
+
mirror: "镜像",
|
|
346
|
+
updateAvailable: "有新版本",
|
|
347
|
+
upToDate: "已是最新",
|
|
348
|
+
notChecked: "还没检查",
|
|
349
|
+
check: "检查更新",
|
|
350
|
+
checking: "正在测速…",
|
|
351
|
+
updateTo: "更新到",
|
|
352
|
+
rollback: "回退到",
|
|
353
|
+
installVersion: "安装版本",
|
|
354
|
+
versionInstalled: "版本已切换,重启 DSH 生效。",
|
|
355
|
+
noPrevious: "没有可回退的版本",
|
|
356
|
+
updateHint: "更新只下载新版本、旧版本原样保留;出问题一键回退,不需要重装依赖。",
|
|
357
|
+
settingsTitle: "Windows-MCP 参数",
|
|
358
|
+
settingsNav: "桌面控制",
|
|
359
|
+
settingsSub: "调整桥接与更新行为;桥接参数写入配置后需要重启 DSH 才生效。",
|
|
360
|
+
settingsRestart: "桥接参数已写入配置,重启 DSH 后生效。",
|
|
361
|
+
settingsSaved: "已保存",
|
|
362
|
+
settingsSave: "保存",
|
|
363
|
+
settingsSaving: "保存中…",
|
|
364
|
+
settingsRevert: "还原",
|
|
365
|
+
settingsFailed: "保存失败",
|
|
366
|
+
settingsIgnored: "部分取值无效,已回退为默认值:",
|
|
367
|
+
sectionBridge: "桥接(重启 DSH 生效)",
|
|
368
|
+
sectionUpdates: "更新与镜像",
|
|
369
|
+
sectionAbout: "关于",
|
|
370
|
+
timeoutLabel: "工具调用超时",
|
|
371
|
+
timeoutHint: "毫秒,5000–600000。越长越不容易被中断,卡住时也等得越久。",
|
|
372
|
+
scaleLabel: "截图缩放",
|
|
373
|
+
scaleHint: "越小越省视觉 token,细节也越少。",
|
|
374
|
+
backendLabel: "截图后端",
|
|
375
|
+
backendHint: "auto 交给上游挑;dxcam 最快,pillow 最慢但最稳。",
|
|
376
|
+
flashLabel: "截图闪光边框",
|
|
377
|
+
flashHint: "关掉可少一次桌面重绘;默认关闭。",
|
|
378
|
+
excludeLabel: "屏蔽的工具",
|
|
379
|
+
excludeHint: "屏蔽的工具不会出现在模型工具表里,能省上下文。",
|
|
380
|
+
autoCheckLabel: "启动时检查更新",
|
|
381
|
+
autoCheckHint: "每次启动后台查一次最新版,结果缓存 6 小时。",
|
|
382
|
+
mirrorLabel: "首选镜像",
|
|
383
|
+
mirrorHint: "auto 用测速最快的镜像;固定某个可避开波动。",
|
|
384
|
+
starLabel: "点个 star",
|
|
385
|
+
starHint: "觉得有用就给个 star,插件会继续维护。",
|
|
386
|
+
aboutRepo: "仓库",
|
|
387
|
+
aboutVersion: "版本",
|
|
303
388
|
},
|
|
304
389
|
en: {
|
|
305
390
|
title: "Windows desktop automation (Windows-MCP)",
|
|
@@ -327,6 +412,53 @@ window.__ModuleLoader__.load({
|
|
|
327
412
|
ready: "Dependency ready — the desktop tools are available (turn the Computer Use switch on).",
|
|
328
413
|
restart: "The bridge now points at the freshly installed build; restart DSH to apply it.",
|
|
329
414
|
hostDown: "Host unreachable",
|
|
415
|
+
version: "Running version",
|
|
416
|
+
latest: "Latest version",
|
|
417
|
+
previous: "Rollback to",
|
|
418
|
+
kept: "Kept versions",
|
|
419
|
+
mirror: "Mirror",
|
|
420
|
+
updateAvailable: "update available",
|
|
421
|
+
upToDate: "up to date",
|
|
422
|
+
notChecked: "not checked yet",
|
|
423
|
+
check: "Check for updates",
|
|
424
|
+
checking: "Probing mirrors…",
|
|
425
|
+
updateTo: "Update to",
|
|
426
|
+
rollback: "Roll back to",
|
|
427
|
+
installVersion: "Install version",
|
|
428
|
+
versionInstalled: "Version switched — restart DSH to apply it.",
|
|
429
|
+
noPrevious: "no previous version to roll back to",
|
|
430
|
+
updateHint: "An update only downloads the new build; the old one is kept as-is. Roll back with one click — no reinstall.",
|
|
431
|
+
settingsTitle: "Windows-MCP settings",
|
|
432
|
+
settingsNav: "Desktop",
|
|
433
|
+
settingsSub: "Tune the bridge and the update behaviour. Bridge values apply after a DSH restart.",
|
|
434
|
+
settingsRestart: "Bridge values were written to the patch layer — restart DSH to apply them.",
|
|
435
|
+
settingsSaved: "Saved",
|
|
436
|
+
settingsSave: "Save",
|
|
437
|
+
settingsSaving: "Saving…",
|
|
438
|
+
settingsRevert: "Revert",
|
|
439
|
+
settingsFailed: "Could not save",
|
|
440
|
+
settingsIgnored: "Some values were invalid and fell back to the defaults:",
|
|
441
|
+
sectionBridge: "Bridge (applies after a DSH restart)",
|
|
442
|
+
sectionUpdates: "Updates and mirrors",
|
|
443
|
+
sectionAbout: "About",
|
|
444
|
+
timeoutLabel: "Tool call timeout",
|
|
445
|
+
timeoutHint: "Milliseconds, 5000–600000. Longer waits are interrupted less often, and stall longer when something hangs.",
|
|
446
|
+
scaleLabel: "Screenshot scale",
|
|
447
|
+
scaleHint: "Smaller costs fewer vision tokens and shows less detail.",
|
|
448
|
+
backendLabel: "Screenshot backend",
|
|
449
|
+
backendHint: "auto lets the server choose; dxcam is fastest, pillow slowest but most compatible.",
|
|
450
|
+
flashLabel: "Screenshot flash border",
|
|
451
|
+
flashHint: "Turning it off saves one desktop repaint; off by default.",
|
|
452
|
+
excludeLabel: "Hidden tools",
|
|
453
|
+
excludeHint: "Hidden tools stay out of the model's tool table, which saves context.",
|
|
454
|
+
autoCheckLabel: "Check for updates at boot",
|
|
455
|
+
autoCheckHint: "One background check per boot, cached for six hours.",
|
|
456
|
+
mirrorLabel: "Preferred mirror",
|
|
457
|
+
mirrorHint: "auto uses the fastest probe; pinning one avoids jitter.",
|
|
458
|
+
starLabel: "Star on GitHub",
|
|
459
|
+
starHint: "If it helps, a star keeps the plugin maintained.",
|
|
460
|
+
aboutRepo: "Repository",
|
|
461
|
+
aboutVersion: "Version",
|
|
330
462
|
},
|
|
331
463
|
};
|
|
332
464
|
|
|
@@ -381,6 +513,19 @@ window.__ModuleLoader__.load({
|
|
|
381
513
|
});
|
|
382
514
|
}
|
|
383
515
|
|
|
516
|
+
/** POST JSON — used by the version manager (check / install / activate / rollback). */
|
|
517
|
+
function postJson(path, payload) {
|
|
518
|
+
return fetch(apiUrl(path), {
|
|
519
|
+
method: "POST",
|
|
520
|
+
headers: { accept: "application/json", "content-type": "application/json" },
|
|
521
|
+
body: JSON.stringify(payload || {}),
|
|
522
|
+
}).then(function (response) {
|
|
523
|
+
return response.json().then(function (body) {
|
|
524
|
+
return { ok: response.ok, status: response.status, body: body };
|
|
525
|
+
});
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
|
|
384
529
|
function statusDot(tone) {
|
|
385
530
|
return React.createElement("span", { className: "dwmc-dot", "data-tone": tone });
|
|
386
531
|
}
|
|
@@ -419,6 +564,19 @@ window.__ModuleLoader__.load({
|
|
|
419
564
|
var installed = dependency.installed === true;
|
|
420
565
|
var bridged = typeof state.bridged === "number" ? state.bridged : null;
|
|
421
566
|
var log = Array.isArray(prepare.log) ? prepare.log : [];
|
|
567
|
+
var update = state.update || {};
|
|
568
|
+
var versions = update.versions || {};
|
|
569
|
+
var check = update.check || {};
|
|
570
|
+
var install = update.install || {};
|
|
571
|
+
var updateAvailable = versions.updateAvailable === true && typeof check.latest === "string";
|
|
572
|
+
var mirrors = Array.isArray(check.mirrors) ? check.mirrors : [];
|
|
573
|
+
var bestMirror = null;
|
|
574
|
+
for (var mirrorIndex = 0; mirrorIndex < mirrors.length; mirrorIndex += 1) {
|
|
575
|
+
if (mirrors[mirrorIndex] && mirrors[mirrorIndex].id === check.best) {
|
|
576
|
+
bestMirror = mirrors[mirrorIndex];
|
|
577
|
+
break;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
422
580
|
var profile = dependency.profile && typeof dependency.profile === "object" ? dependency.profile : null;
|
|
423
581
|
var profileTarget =
|
|
424
582
|
profile === null
|
|
@@ -441,10 +599,28 @@ window.__ModuleLoader__.load({
|
|
|
441
599
|
});
|
|
442
600
|
};
|
|
443
601
|
|
|
602
|
+
var actUpdate = function (payload) {
|
|
603
|
+
setBusy(true);
|
|
604
|
+
postJson(UPDATE_PATH, payload)
|
|
605
|
+
.catch(function () {
|
|
606
|
+
/* the next poll reports the truth */
|
|
607
|
+
})
|
|
608
|
+
.then(function () {
|
|
609
|
+
setBusy(false);
|
|
610
|
+
reload();
|
|
611
|
+
});
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
var speedText = function (bytesPerSecond) {
|
|
615
|
+
var value = Number(bytesPerSecond);
|
|
616
|
+
if (!isFinite(value) || value <= 0) return "—";
|
|
617
|
+
return value >= 1048576 ? (value / 1048576).toFixed(1) + " MB/s" : Math.max(1, Math.round(value / 1024)) + " KB/s";
|
|
618
|
+
};
|
|
619
|
+
|
|
444
620
|
var head = React.createElement(
|
|
445
621
|
"div",
|
|
446
622
|
{ className: "dwmc-panel__head", key: "head" },
|
|
447
|
-
statusDot(running ? "warn" : installed ? "on" : "warn"),
|
|
623
|
+
statusDot(running ? "warn" : updateAvailable ? "warn" : installed ? "on" : "warn"),
|
|
448
624
|
React.createElement("span", null, t.title),
|
|
449
625
|
);
|
|
450
626
|
|
|
@@ -452,11 +628,23 @@ window.__ModuleLoader__.load({
|
|
|
452
628
|
if (!compact) {
|
|
453
629
|
var rows = [
|
|
454
630
|
[t.dependency, installed ? t.installed + (dependency.version ? " " + dependency.version : "") : dependency.uv === null ? t.uvMissing : t.missing],
|
|
455
|
-
[t.
|
|
456
|
-
[
|
|
457
|
-
|
|
458
|
-
|
|
631
|
+
[t.version, versions.active ? "v" + versions.active : dependency.version ? "v" + dependency.version : "—"],
|
|
632
|
+
[
|
|
633
|
+
t.latest,
|
|
634
|
+
typeof check.latest === "string"
|
|
635
|
+
? "v" + check.latest + " · " + (updateAvailable ? t.updateAvailable : t.upToDate)
|
|
636
|
+
: t.notChecked,
|
|
637
|
+
],
|
|
459
638
|
];
|
|
639
|
+
if (typeof versions.previous === "string") rows.push([t.previous, "v" + versions.previous]);
|
|
640
|
+
if (Array.isArray(versions.available) && versions.available.length > 0) {
|
|
641
|
+
rows.push([t.kept, versions.available.map(function (version) { return "v" + version; }).join(", ")]);
|
|
642
|
+
}
|
|
643
|
+
if (bestMirror !== null) rows.push([t.mirror, bestMirror.label + " · " + speedText(bestMirror.speedBps)]);
|
|
644
|
+
rows.push([t.tool, dependency.executable || "—"]);
|
|
645
|
+
rows.push([t.bridge, bridged === null ? "—" : bridged + " " + t.bridgedTools]);
|
|
646
|
+
rows.push([t.profile, profileTarget]);
|
|
647
|
+
rows.push([t.toggle, state.enabled === true ? t.on : t.off]);
|
|
460
648
|
children.push(
|
|
461
649
|
React.createElement(
|
|
462
650
|
"dl",
|
|
@@ -473,7 +661,13 @@ window.__ModuleLoader__.load({
|
|
|
473
661
|
|
|
474
662
|
if (running) {
|
|
475
663
|
children.push(React.createElement("div", { className: "dwmc-progress", key: "progress" }, React.createElement("i")));
|
|
476
|
-
children.push(
|
|
664
|
+
children.push(
|
|
665
|
+
React.createElement(
|
|
666
|
+
"p",
|
|
667
|
+
{ className: "dwmc-note", key: "running" },
|
|
668
|
+
(install.version ? t.installVersion + " v" + install.version + "… " : "") + t.running,
|
|
669
|
+
),
|
|
670
|
+
);
|
|
477
671
|
var tail = log.slice(-3).join("\n");
|
|
478
672
|
if (tail) children.push(React.createElement("pre", { className: "dwmc-log", key: "log" }, tail));
|
|
479
673
|
} else if (prepare.phase === "failed") {
|
|
@@ -492,17 +686,46 @@ window.__ModuleLoader__.load({
|
|
|
492
686
|
} else if (!installed && !running) {
|
|
493
687
|
children.push(React.createElement("p", { className: "dwmc-note", key: "estimate" }, t.estimate));
|
|
494
688
|
}
|
|
689
|
+
if (!compact && (running || updateAvailable)) {
|
|
690
|
+
children.push(React.createElement("p", { className: "dwmc-note", key: "updateHint" }, t.updateHint));
|
|
691
|
+
}
|
|
495
692
|
if (view.phase === "error") {
|
|
496
693
|
children.push(React.createElement("p", { className: "dwmc-error", key: "offline" }, t.hostDown + ": " + view.error));
|
|
497
694
|
}
|
|
498
695
|
|
|
499
696
|
var actions = [];
|
|
500
697
|
if (running) {
|
|
501
|
-
actions.push(actionButton(t.cancel, function () {
|
|
502
|
-
} else if (!installed) {
|
|
503
|
-
actions.push(actionButton(t.install, function () { act(PREPARE_PATH); }, busy));
|
|
698
|
+
actions.push(actionButton(t.cancel, function () { actUpdate({ action: "cancel" }); }, busy, true));
|
|
504
699
|
} else {
|
|
505
|
-
actions.push(
|
|
700
|
+
actions.push(
|
|
701
|
+
actionButton(
|
|
702
|
+
check.phase === "checking" ? t.checking : t.check,
|
|
703
|
+
function () { actUpdate({ action: "check" }); },
|
|
704
|
+
busy,
|
|
705
|
+
true,
|
|
706
|
+
),
|
|
707
|
+
);
|
|
708
|
+
if (updateAvailable) {
|
|
709
|
+
actions.push(
|
|
710
|
+
actionButton(
|
|
711
|
+
t.updateTo + " v" + check.latest,
|
|
712
|
+
function () { actUpdate({ action: "install", version: check.latest }); },
|
|
713
|
+
busy,
|
|
714
|
+
),
|
|
715
|
+
);
|
|
716
|
+
}
|
|
717
|
+
if (typeof versions.previous === "string") {
|
|
718
|
+
actions.push(
|
|
719
|
+
actionButton(
|
|
720
|
+
t.rollback + " v" + versions.previous,
|
|
721
|
+
function () { actUpdate({ action: "rollback" }); },
|
|
722
|
+
busy,
|
|
723
|
+
true,
|
|
724
|
+
),
|
|
725
|
+
);
|
|
726
|
+
}
|
|
727
|
+
if (!installed) actions.push(actionButton(t.install, function () { act(PREPARE_PATH); }, busy));
|
|
728
|
+
else actions.push(actionButton(t.reinstall, function () { act(PREPARE_PATH); }, busy, true));
|
|
506
729
|
if (prepare.bridge && prepare.bridge.ok !== true) {
|
|
507
730
|
actions.push(actionButton(t.pointBridge, function () { act(BRIDGE_PATH); }, busy, true));
|
|
508
731
|
}
|
|
@@ -517,6 +740,352 @@ window.__ModuleLoader__.load({
|
|
|
517
740
|
return React.createElement(WindowsMcpPanel, { __compact: true });
|
|
518
741
|
}
|
|
519
742
|
|
|
743
|
+
/* ------------------------------------------------------------------ *
|
|
744
|
+
* settings page
|
|
745
|
+
* ------------------------------------------------------------------ */
|
|
746
|
+
|
|
747
|
+
var SCALE_CHOICES = [0.25, 0.5, 0.75, 1];
|
|
748
|
+
var BACKEND_CHOICES = ["auto", "dxcam", "mss", "pillow"];
|
|
749
|
+
var MIRROR_CHOICES = ["auto", "ustc", "aliyun", "tencent", "huawei", "nju", "tuna", "pypi"];
|
|
750
|
+
var EXCLUDE_CHOICES = ["App", "PowerShell", "FileSystem"];
|
|
751
|
+
|
|
752
|
+
/** A mutable copy of the host settings, so a form never edits state in place. */
|
|
753
|
+
function cloneSettings(input) {
|
|
754
|
+
var bridge = input && input.bridge ? input.bridge : {};
|
|
755
|
+
var plugin = input && input.plugin ? input.plugin : {};
|
|
756
|
+
return {
|
|
757
|
+
bridge: {
|
|
758
|
+
toolCallTimeoutMs: Number(bridge.toolCallTimeoutMs),
|
|
759
|
+
excludeTools: Array.isArray(bridge.excludeTools) ? bridge.excludeTools.slice() : [],
|
|
760
|
+
screenshotScale: Number(bridge.screenshotScale),
|
|
761
|
+
screenshotBackend: String(bridge.screenshotBackend || "auto"),
|
|
762
|
+
disableFlash: bridge.disableFlash === true,
|
|
763
|
+
},
|
|
764
|
+
plugin: {
|
|
765
|
+
autoCheckUpdates: plugin.autoCheckUpdates === true,
|
|
766
|
+
preferredMirror: String(plugin.preferredMirror || "auto"),
|
|
767
|
+
},
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
/** GET/POST the host settings; only the settings page mounts this. */
|
|
772
|
+
function useSettings() {
|
|
773
|
+
var pair = React.useState({ phase: "loading", view: null, error: null });
|
|
774
|
+
var view = pair[0];
|
|
775
|
+
var setView = pair[1];
|
|
776
|
+
var savedPair = React.useState({ phase: "idle", body: null, error: null });
|
|
777
|
+
var saved = savedPair[0];
|
|
778
|
+
var setSaved = savedPair[1];
|
|
779
|
+
|
|
780
|
+
var load = React.useCallback(function () {
|
|
781
|
+
return fetch(apiUrl(SETTINGS_PATH), { headers: { accept: "application/json" } })
|
|
782
|
+
.then(function (response) {
|
|
783
|
+
return response.json();
|
|
784
|
+
})
|
|
785
|
+
.then(function (body) {
|
|
786
|
+
setView({ phase: "ready", view: body, error: null });
|
|
787
|
+
return body;
|
|
788
|
+
})
|
|
789
|
+
.catch(function (error) {
|
|
790
|
+
setView({ phase: "error", view: null, error: String(error && error.message ? error.message : error) });
|
|
791
|
+
});
|
|
792
|
+
}, []);
|
|
793
|
+
|
|
794
|
+
React.useEffect(
|
|
795
|
+
function () {
|
|
796
|
+
load();
|
|
797
|
+
},
|
|
798
|
+
[load],
|
|
799
|
+
);
|
|
800
|
+
|
|
801
|
+
var save = React.useCallback(function (next) {
|
|
802
|
+
setSaved({ phase: "saving", body: null, error: null });
|
|
803
|
+
return fetch(apiUrl(SETTINGS_PATH), {
|
|
804
|
+
method: "POST",
|
|
805
|
+
headers: { accept: "application/json", "content-type": "application/json" },
|
|
806
|
+
body: JSON.stringify({ settings: next }),
|
|
807
|
+
})
|
|
808
|
+
.then(function (response) {
|
|
809
|
+
return response.json();
|
|
810
|
+
})
|
|
811
|
+
.then(function (body) {
|
|
812
|
+
setView({ phase: "ready", view: body, error: null });
|
|
813
|
+
setSaved({ phase: body && body.ok === false ? "error" : "saved", body: body, error: null });
|
|
814
|
+
return body;
|
|
815
|
+
})
|
|
816
|
+
.catch(function (error) {
|
|
817
|
+
setSaved({ phase: "error", body: null, error: String(error && error.message ? error.message : error) });
|
|
818
|
+
return null;
|
|
819
|
+
});
|
|
820
|
+
}, []);
|
|
821
|
+
|
|
822
|
+
return { view: view, saved: saved, save: save, reload: load };
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
function starIcon() {
|
|
826
|
+
return React.createElement(
|
|
827
|
+
"svg",
|
|
828
|
+
{ viewBox: "0 0 16 16", fill: "currentColor", "aria-hidden": "true" },
|
|
829
|
+
React.createElement("path", {
|
|
830
|
+
d: "M8 1.2l2.06 4.18 4.61.67-3.34 3.25.79 4.6L8 11.73l-4.12 2.17.79-4.6L1.33 6.05l4.61-.67z",
|
|
831
|
+
}),
|
|
832
|
+
);
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/** Settings → left rail: the plugin's parameter page. */
|
|
836
|
+
function SettingsPage() {
|
|
837
|
+
var t = copy();
|
|
838
|
+
var host = useSettings();
|
|
839
|
+
var view = host.view;
|
|
840
|
+
var draftPair = React.useState(null);
|
|
841
|
+
var draft = draftPair[0];
|
|
842
|
+
var setDraft = draftPair[1];
|
|
843
|
+
|
|
844
|
+
var ready = view.phase === "ready" && view.view && view.view.settings ? view.view : null;
|
|
845
|
+
|
|
846
|
+
React.useEffect(
|
|
847
|
+
function () {
|
|
848
|
+
if (ready !== null) setDraft(cloneSettings(ready.settings));
|
|
849
|
+
},
|
|
850
|
+
[ready === null ? null : JSON.stringify(ready.settings)],
|
|
851
|
+
);
|
|
852
|
+
|
|
853
|
+
if (view.phase === "loading") return React.createElement("p", { className: "dwmc-note" }, t.checking);
|
|
854
|
+
if (view.phase === "error") return React.createElement("p", { className: "dwmc-error" }, t.hostDown + " — " + String(view.error || ""));
|
|
855
|
+
if (draft === null || ready === null) return React.createElement("p", { className: "dwmc-note" }, t.checking);
|
|
856
|
+
|
|
857
|
+
var dirty = JSON.stringify(draft) !== JSON.stringify(cloneSettings(ready.settings));
|
|
858
|
+
var saving = host.saved.phase === "saving";
|
|
859
|
+
|
|
860
|
+
var update = function (section, key, value) {
|
|
861
|
+
var next = cloneSettings(draft);
|
|
862
|
+
next[section][key] = value;
|
|
863
|
+
setDraft(next);
|
|
864
|
+
};
|
|
865
|
+
var toggleExclude = function (name) {
|
|
866
|
+
var next = cloneSettings(draft);
|
|
867
|
+
var at = next.bridge.excludeTools.indexOf(name);
|
|
868
|
+
if (at === -1) next.bridge.excludeTools.push(name);
|
|
869
|
+
else next.bridge.excludeTools.splice(at, 1);
|
|
870
|
+
setDraft(next);
|
|
871
|
+
};
|
|
872
|
+
var save = function () {
|
|
873
|
+
host.save(draft).then(function (body) {
|
|
874
|
+
if (body && body.settings) setDraft(cloneSettings(body.settings));
|
|
875
|
+
});
|
|
876
|
+
};
|
|
877
|
+
|
|
878
|
+
var field = function (label, hint, control) {
|
|
879
|
+
return React.createElement(
|
|
880
|
+
"div",
|
|
881
|
+
{ className: "dwmc-field", key: label },
|
|
882
|
+
React.createElement(
|
|
883
|
+
"div",
|
|
884
|
+
{ className: "dwmc-field__label" },
|
|
885
|
+
React.createElement("span", null, label),
|
|
886
|
+
hint ? React.createElement("span", { className: "dwmc-field__hint" }, hint) : null,
|
|
887
|
+
),
|
|
888
|
+
control,
|
|
889
|
+
);
|
|
890
|
+
};
|
|
891
|
+
var check = function (label, checked, onChange, key) {
|
|
892
|
+
return React.createElement(
|
|
893
|
+
"label",
|
|
894
|
+
{ className: "dwmc-check", key: key || label },
|
|
895
|
+
React.createElement("input", { type: "checkbox", checked: checked === true, onChange: function (event) { onChange(event.target.checked); } }),
|
|
896
|
+
label,
|
|
897
|
+
);
|
|
898
|
+
};
|
|
899
|
+
var card = function (title, children) {
|
|
900
|
+
return React.createElement(
|
|
901
|
+
"section",
|
|
902
|
+
{ className: "dwmc-card" },
|
|
903
|
+
React.createElement("h3", { className: "dwmc-card__title" }, title),
|
|
904
|
+
children,
|
|
905
|
+
);
|
|
906
|
+
};
|
|
907
|
+
|
|
908
|
+
var bridgeCard = card(t.sectionBridge, [
|
|
909
|
+
field(
|
|
910
|
+
t.timeoutLabel,
|
|
911
|
+
t.timeoutHint,
|
|
912
|
+
React.createElement("input", {
|
|
913
|
+
className: "dwmc-input",
|
|
914
|
+
type: "number",
|
|
915
|
+
min: 5000,
|
|
916
|
+
max: 600000,
|
|
917
|
+
step: 1000,
|
|
918
|
+
value: String(draft.bridge.toolCallTimeoutMs),
|
|
919
|
+
onChange: function (event) {
|
|
920
|
+
update("bridge", "toolCallTimeoutMs", Number(event.target.value));
|
|
921
|
+
},
|
|
922
|
+
}),
|
|
923
|
+
),
|
|
924
|
+
field(
|
|
925
|
+
t.scaleLabel,
|
|
926
|
+
t.scaleHint,
|
|
927
|
+
React.createElement(
|
|
928
|
+
"select",
|
|
929
|
+
{
|
|
930
|
+
className: "dwmc-select",
|
|
931
|
+
value: String(draft.bridge.screenshotScale),
|
|
932
|
+
onChange: function (event) {
|
|
933
|
+
update("bridge", "screenshotScale", Number(event.target.value));
|
|
934
|
+
},
|
|
935
|
+
},
|
|
936
|
+
SCALE_CHOICES.map(function (scale) {
|
|
937
|
+
return React.createElement("option", { key: String(scale), value: String(scale) }, String(scale));
|
|
938
|
+
}),
|
|
939
|
+
),
|
|
940
|
+
),
|
|
941
|
+
field(
|
|
942
|
+
t.backendLabel,
|
|
943
|
+
t.backendHint,
|
|
944
|
+
React.createElement(
|
|
945
|
+
"select",
|
|
946
|
+
{
|
|
947
|
+
className: "dwmc-select",
|
|
948
|
+
value: draft.bridge.screenshotBackend,
|
|
949
|
+
onChange: function (event) {
|
|
950
|
+
update("bridge", "screenshotBackend", event.target.value);
|
|
951
|
+
},
|
|
952
|
+
},
|
|
953
|
+
BACKEND_CHOICES.map(function (backend) {
|
|
954
|
+
return React.createElement("option", { key: backend, value: backend }, backend);
|
|
955
|
+
}),
|
|
956
|
+
),
|
|
957
|
+
),
|
|
958
|
+
field(
|
|
959
|
+
t.flashLabel,
|
|
960
|
+
t.flashHint,
|
|
961
|
+
React.createElement("input", {
|
|
962
|
+
type: "checkbox",
|
|
963
|
+
checked: draft.bridge.disableFlash === false ? false : true,
|
|
964
|
+
onChange: function (event) {
|
|
965
|
+
update("bridge", "disableFlash", event.target.checked);
|
|
966
|
+
},
|
|
967
|
+
}),
|
|
968
|
+
),
|
|
969
|
+
field(
|
|
970
|
+
t.excludeLabel,
|
|
971
|
+
t.excludeHint,
|
|
972
|
+
React.createElement(
|
|
973
|
+
"div",
|
|
974
|
+
{ className: "dwmc-checks" },
|
|
975
|
+
EXCLUDE_CHOICES.map(function (name) {
|
|
976
|
+
return check(name, draft.bridge.excludeTools.indexOf(name) !== -1, function () { toggleExclude(name); }, name);
|
|
977
|
+
}),
|
|
978
|
+
),
|
|
979
|
+
),
|
|
980
|
+
]);
|
|
981
|
+
|
|
982
|
+
var updatesCard = card(t.sectionUpdates, [
|
|
983
|
+
field(
|
|
984
|
+
t.autoCheckLabel,
|
|
985
|
+
t.autoCheckHint,
|
|
986
|
+
React.createElement("input", {
|
|
987
|
+
type: "checkbox",
|
|
988
|
+
checked: draft.plugin.autoCheckUpdates === true,
|
|
989
|
+
onChange: function (event) {
|
|
990
|
+
update("plugin", "autoCheckUpdates", event.target.checked);
|
|
991
|
+
},
|
|
992
|
+
}),
|
|
993
|
+
),
|
|
994
|
+
field(
|
|
995
|
+
t.mirrorLabel,
|
|
996
|
+
t.mirrorHint,
|
|
997
|
+
React.createElement(
|
|
998
|
+
"select",
|
|
999
|
+
{
|
|
1000
|
+
className: "dwmc-select",
|
|
1001
|
+
value: draft.plugin.preferredMirror,
|
|
1002
|
+
onChange: function (event) {
|
|
1003
|
+
update("plugin", "preferredMirror", event.target.value);
|
|
1004
|
+
},
|
|
1005
|
+
},
|
|
1006
|
+
MIRROR_CHOICES.map(function (mirror) {
|
|
1007
|
+
return React.createElement("option", { key: mirror, value: mirror }, mirror);
|
|
1008
|
+
}),
|
|
1009
|
+
),
|
|
1010
|
+
),
|
|
1011
|
+
]);
|
|
1012
|
+
|
|
1013
|
+
var notes = [];
|
|
1014
|
+
if (host.saved.phase === "saved") notes.push(React.createElement("span", { key: "saved", className: "dwmc-saved" }, t.settingsSaved));
|
|
1015
|
+
if (host.saved.phase === "error") notes.push(React.createElement("span", { key: "err", className: "dwmc-error" }, t.settingsFailed));
|
|
1016
|
+
if (ready.restartRequired && host.saved.phase !== "saving") {
|
|
1017
|
+
notes.push(React.createElement("span", { key: "restart", className: "dwmc-warn" }, t.settingsRestart));
|
|
1018
|
+
}
|
|
1019
|
+
if (host.saved.body && Array.isArray(host.saved.body.problems) && host.saved.body.problems.length > 0) {
|
|
1020
|
+
notes.push(React.createElement("span", { key: "ignored", className: "dwmc-warn" }, t.settingsIgnored + " " + host.saved.body.problems.join(", ")));
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
var aboutCard = card(t.sectionAbout, [
|
|
1024
|
+
React.createElement(
|
|
1025
|
+
"div",
|
|
1026
|
+
{ className: "dwmc-rows" },
|
|
1027
|
+
React.createElement("dt", null, t.aboutVersion),
|
|
1028
|
+
React.createElement("dd", { className: "dwmc-mono" }, "v" + String(ready.pluginVersion || "?")),
|
|
1029
|
+
React.createElement("dt", null, t.aboutRepo),
|
|
1030
|
+
React.createElement(
|
|
1031
|
+
"dd",
|
|
1032
|
+
null,
|
|
1033
|
+
React.createElement("a", { className: "dwmc-mono", href: REPO_URL, target: "_blank", rel: "noopener noreferrer" }, REPO_URL.replace("https://", "")),
|
|
1034
|
+
),
|
|
1035
|
+
),
|
|
1036
|
+
React.createElement(
|
|
1037
|
+
"div",
|
|
1038
|
+
{ className: "dwmc-set__actions" },
|
|
1039
|
+
React.createElement(
|
|
1040
|
+
"button",
|
|
1041
|
+
{
|
|
1042
|
+
type: "button",
|
|
1043
|
+
className: "dwmc-star",
|
|
1044
|
+
onClick: function () {
|
|
1045
|
+
try {
|
|
1046
|
+
window.open(REPO_URL, "_blank", "noopener,noreferrer");
|
|
1047
|
+
} catch (error) {
|
|
1048
|
+
/* a blocked popup is not worth failing the page over */
|
|
1049
|
+
}
|
|
1050
|
+
},
|
|
1051
|
+
},
|
|
1052
|
+
starIcon(),
|
|
1053
|
+
t.starLabel,
|
|
1054
|
+
),
|
|
1055
|
+
React.createElement("span", { className: "dwmc-note" }, t.starHint),
|
|
1056
|
+
),
|
|
1057
|
+
]);
|
|
1058
|
+
|
|
1059
|
+
return React.createElement(
|
|
1060
|
+
"div",
|
|
1061
|
+
{ className: "dwmc-set" },
|
|
1062
|
+
React.createElement(
|
|
1063
|
+
"div",
|
|
1064
|
+
{ className: "dwmc-set__head" },
|
|
1065
|
+
React.createElement("h2", { className: "dwmc-set__title" }, t.settingsTitle),
|
|
1066
|
+
React.createElement("p", { className: "dwmc-set__sub" }, t.settingsSub),
|
|
1067
|
+
),
|
|
1068
|
+
bridgeCard,
|
|
1069
|
+
updatesCard,
|
|
1070
|
+
aboutCard,
|
|
1071
|
+
React.createElement(
|
|
1072
|
+
"div",
|
|
1073
|
+
{ className: "dwmc-set__actions" },
|
|
1074
|
+
React.createElement(
|
|
1075
|
+
"button",
|
|
1076
|
+
{ type: "button", className: "dwmc-btn", onClick: save, disabled: saving || !dirty },
|
|
1077
|
+
saving ? t.settingsSaving : t.settingsSave,
|
|
1078
|
+
),
|
|
1079
|
+
React.createElement(
|
|
1080
|
+
"button",
|
|
1081
|
+
{ type: "button", className: "dwmc-btn dwmc-btn--quiet", onClick: function () { setDraft(cloneSettings(ready.settings)); }, disabled: saving || !dirty },
|
|
1082
|
+
t.settingsRevert,
|
|
1083
|
+
),
|
|
1084
|
+
notes.length > 0 ? React.createElement("span", { className: "dwmc-set__actions" }, notes) : null,
|
|
1085
|
+
),
|
|
1086
|
+
);
|
|
1087
|
+
}
|
|
1088
|
+
|
|
520
1089
|
/* ------------------------------------------------------------------ *
|
|
521
1090
|
* registration
|
|
522
1091
|
* ------------------------------------------------------------------ */
|
|
@@ -590,6 +1159,135 @@ window.__ModuleLoader__.load({
|
|
|
590
1159
|
}
|
|
591
1160
|
}
|
|
592
1161
|
|
|
1162
|
+
/**
|
|
1163
|
+
* Settings → left rail. `settings.section` is a list slot: one page per
|
|
1164
|
+
* registered entry, ordered by `order`, labelled by `label()`.
|
|
1165
|
+
*/
|
|
1166
|
+
/**
|
|
1167
|
+
* The glyph of the settings entry: a monitor with a cursor cut out of its
|
|
1168
|
+
* screen ("this page drives the desktop"). `settings.section` has no icon
|
|
1169
|
+
* field, so the entry is painted on the shell's nav row: the row is marked
|
|
1170
|
+
* by matching its visible text against our own localized label, the shell's
|
|
1171
|
+
* gear is hidden and a mask-image painted in `currentColor` takes its place.
|
|
1172
|
+
*/
|
|
1173
|
+
function settingsNavMaskSvg() {
|
|
1174
|
+
return [
|
|
1175
|
+
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="#000">',
|
|
1176
|
+
'<path fill-rule="evenodd" d="M2.6 2.6h10.8c.66 0 1.2.54 1.2 1.2v6.4c0 .66-.54 1.2-1.2 1.2H2.6',
|
|
1177
|
+
'c-.66 0-1.2-.54-1.2-1.2V3.8c0-.66.54-1.2 1.2-1.2Zm3.1 2.5 4.5 2.85-2.1.36.98 2.06-1.16.55',
|
|
1178
|
+
'-0.97-2.05-1.3 1.05z"/>',
|
|
1179
|
+
'<rect x="7" y="11.2" width="2" height="1.7" rx=".3"/>',
|
|
1180
|
+
'<rect x="5.2" y="12.9" width="5.6" height="1.4" rx=".7"/>',
|
|
1181
|
+
"</svg>",
|
|
1182
|
+
].join("");
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
/** Encoded as a CSS `mask-image` source; the mask reads alpha only. */
|
|
1186
|
+
function settingsNavMaskUrl(svg) {
|
|
1187
|
+
var source = typeof svg === "string" ? svg : settingsNavMaskSvg();
|
|
1188
|
+
return "data:image/svg+xml," + encodeURIComponent(source).replace(/'/gu, "%27");
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
/**
|
|
1192
|
+
* Whether a nav row is this plugin's own. Pure: the row whose visible text
|
|
1193
|
+
* is the label the shell currently projects, trimmed. An empty label matches
|
|
1194
|
+
* nothing, so a locale that has not resolved yet never marks the whole nav.
|
|
1195
|
+
*/
|
|
1196
|
+
function isOwnSettingsNavRow(rowText, wantedLabel) {
|
|
1197
|
+
var wanted = String(wantedLabel === null || wantedLabel === undefined ? "" : wantedLabel).trim();
|
|
1198
|
+
if (wanted.length === 0) return false;
|
|
1199
|
+
return String(rowText === null || rowText === undefined ? "" : rowText).trim() === wanted;
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
/** Stylesheet for the marked row: hide the shell icon, draw ours. */
|
|
1203
|
+
function settingsNavIconCss(maskUrl) {
|
|
1204
|
+
var marker = "[" + SETTINGS_NAV_ICON_MARKER + "]";
|
|
1205
|
+
return [
|
|
1206
|
+
marker + " > svg{display:none}",
|
|
1207
|
+
marker + "::before{content:'';flex:none;width:16px;height:16px;",
|
|
1208
|
+
"background-color:currentColor;",
|
|
1209
|
+
"-webkit-mask-image:url('" + maskUrl + "');mask-image:url('" + maskUrl + "');",
|
|
1210
|
+
"-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;",
|
|
1211
|
+
"-webkit-mask-position:center;mask-position:center;",
|
|
1212
|
+
"-webkit-mask-size:16px 16px;mask-size:16px 16px}",
|
|
1213
|
+
].join("");
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
/**
|
|
1217
|
+
* Install the nav glyph. Idempotent, and a no-op outside a browser. The
|
|
1218
|
+
* observer follows re-renders and locale switches, so the text and the
|
|
1219
|
+
* glyph can never disagree; the marker and the stylesheet belong to the
|
|
1220
|
+
* fiber when the client context offers `effect`.
|
|
1221
|
+
*/
|
|
1222
|
+
function installSettingsNavIcon(ctx) {
|
|
1223
|
+
if (typeof document === "undefined" || typeof MutationObserver !== "function") return null;
|
|
1224
|
+
var install = function () {
|
|
1225
|
+
var existing = document.getElementById(SETTINGS_NAV_ICON_STYLE_ID);
|
|
1226
|
+
if (existing !== null) return function () {};
|
|
1227
|
+
var style = document.createElement("style");
|
|
1228
|
+
style.id = SETTINGS_NAV_ICON_STYLE_ID;
|
|
1229
|
+
style.textContent = settingsNavIconCss(settingsNavMaskUrl());
|
|
1230
|
+
document.head.appendChild(style);
|
|
1231
|
+
var disposed = false;
|
|
1232
|
+
var scheduled = false;
|
|
1233
|
+
var sync = function () {
|
|
1234
|
+
scheduled = false;
|
|
1235
|
+
if (disposed) return;
|
|
1236
|
+
var wanted = copy().settingsNav;
|
|
1237
|
+
var rows = document.querySelectorAll(SETTINGS_NAV_ROW_SELECTOR);
|
|
1238
|
+
for (var index = 0; index < rows.length; index += 1) {
|
|
1239
|
+
var row = rows[index];
|
|
1240
|
+
if (isOwnSettingsNavRow(row.textContent, wanted)) row.setAttribute(SETTINGS_NAV_ICON_MARKER, "");
|
|
1241
|
+
else row.removeAttribute(SETTINGS_NAV_ICON_MARKER);
|
|
1242
|
+
}
|
|
1243
|
+
};
|
|
1244
|
+
var schedule = function () {
|
|
1245
|
+
if (scheduled || disposed) return;
|
|
1246
|
+
scheduled = true;
|
|
1247
|
+
if (typeof queueMicrotask === "function") queueMicrotask(sync);
|
|
1248
|
+
else setTimeout(sync, 0);
|
|
1249
|
+
};
|
|
1250
|
+
sync();
|
|
1251
|
+
var observer = new MutationObserver(schedule);
|
|
1252
|
+
observer.observe(document.body, { childList: true, subtree: true, characterData: true });
|
|
1253
|
+
return function () {
|
|
1254
|
+
disposed = true;
|
|
1255
|
+
observer.disconnect();
|
|
1256
|
+
var marked = document.querySelectorAll("[" + SETTINGS_NAV_ICON_MARKER + "]");
|
|
1257
|
+
for (var at = 0; at < marked.length; at += 1) marked[at].removeAttribute(SETTINGS_NAV_ICON_MARKER);
|
|
1258
|
+
if (style.parentNode !== null) style.parentNode.removeChild(style);
|
|
1259
|
+
};
|
|
1260
|
+
};
|
|
1261
|
+
if (ctx && typeof ctx.effect === "function") return ctx.effect(install, "dsh-windows-mcp: settings nav icon");
|
|
1262
|
+
return install();
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
function registerSettingsSection(ctx) {
|
|
1266
|
+
try {
|
|
1267
|
+
ctx.slots.inject(SETTINGS_SLOT, function () {
|
|
1268
|
+
return ctx.slots.register(
|
|
1269
|
+
{
|
|
1270
|
+
name: SETTINGS_SLOT,
|
|
1271
|
+
id: SETTINGS_ENTRY_ID,
|
|
1272
|
+
order: SETTINGS_ORDER,
|
|
1273
|
+
label: function () {
|
|
1274
|
+
return copy().settingsNav;
|
|
1275
|
+
},
|
|
1276
|
+
},
|
|
1277
|
+
function SettingsSection() {
|
|
1278
|
+
return React.createElement(SettingsPage, null);
|
|
1279
|
+
},
|
|
1280
|
+
);
|
|
1281
|
+
});
|
|
1282
|
+
} catch (error) {
|
|
1283
|
+
try {
|
|
1284
|
+
console.error("[" + PACKAGE_ID + "] settings section registration failed:", error);
|
|
1285
|
+
} catch (ignored) {
|
|
1286
|
+
/* nothing to do */
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
|
|
593
1291
|
/**
|
|
594
1292
|
* @param ctx - client cordis context.
|
|
595
1293
|
*/
|
|
@@ -597,11 +1295,30 @@ window.__ModuleLoader__.load({
|
|
|
597
1295
|
installStyles();
|
|
598
1296
|
registerComposerPill(ctx);
|
|
599
1297
|
registerBundleSlots(ctx);
|
|
1298
|
+
registerSettingsSection(ctx);
|
|
1299
|
+
installSettingsNavIcon(ctx);
|
|
600
1300
|
}
|
|
601
1301
|
|
|
602
1302
|
exports.apply = apply;
|
|
603
1303
|
exports.inject = inject;
|
|
604
|
-
exports.__internals = {
|
|
1304
|
+
exports.__internals = {
|
|
1305
|
+
CSS: CSS,
|
|
1306
|
+
apiUrl: apiUrl,
|
|
1307
|
+
tooltipFor: tooltipFor,
|
|
1308
|
+
LABEL: LABEL,
|
|
1309
|
+
STATE_PATH: STATE_PATH,
|
|
1310
|
+
UPDATE_PATH: UPDATE_PATH,
|
|
1311
|
+
SETTINGS_PATH: SETTINGS_PATH,
|
|
1312
|
+
REPO_URL: REPO_URL,
|
|
1313
|
+
SETTINGS_SLOT: SETTINGS_SLOT,
|
|
1314
|
+
SETTINGS_NAV_ICON: settingsNavMaskSvg(),
|
|
1315
|
+
SETTINGS_NAV_ICON_MARKER: SETTINGS_NAV_ICON_MARKER,
|
|
1316
|
+
isOwnSettingsNavRow: isOwnSettingsNavRow,
|
|
1317
|
+
settingsNavIconCss: settingsNavIconCss,
|
|
1318
|
+
settingsNavMaskUrl: settingsNavMaskUrl,
|
|
1319
|
+
cloneSettings: cloneSettings,
|
|
1320
|
+
SettingsPage: SettingsPage,
|
|
1321
|
+
};
|
|
605
1322
|
return module.exports;
|
|
606
1323
|
},
|
|
607
1324
|
});
|