@comecaramelos/dsh-chrome-mcp 0.0.1 → 1.0.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/README.md +22 -78
- package/assets/dsh-chrome-mcp-social.png +0 -0
- package/assets/icon.svg +71 -0
- package/assets/prelaunch-windows-chrome.html +277 -0
- package/lib/client.js +1433 -568
- package/lib/client.js.map +7 -0
- package/lib/host/apply.d.ts +3 -0
- package/lib/host/apply.d.ts.map +1 -0
- package/lib/host/apply.js +31 -0
- package/lib/host/apply.js.map +1 -0
- package/lib/host/bridge.d.ts +65 -0
- package/lib/host/bridge.d.ts.map +1 -0
- package/lib/host/bridge.js +176 -0
- package/lib/host/bridge.js.map +1 -0
- package/lib/host/catalog.d.ts +31 -0
- package/lib/host/catalog.d.ts.map +1 -0
- package/lib/host/catalog.js +92 -0
- package/lib/host/catalog.js.map +1 -0
- package/lib/host/constants.d.ts +229 -0
- package/lib/host/constants.d.ts.map +1 -0
- package/lib/host/constants.js +242 -0
- package/lib/host/constants.js.map +1 -0
- package/lib/host/controller/bridge-run.d.ts +38 -0
- package/lib/host/controller/bridge-run.d.ts.map +1 -0
- package/lib/host/controller/bridge-run.js +69 -0
- package/lib/host/controller/bridge-run.js.map +1 -0
- package/lib/host/controller/captures.d.ts +22 -0
- package/lib/host/controller/captures.d.ts.map +1 -0
- package/lib/host/controller/captures.js +60 -0
- package/lib/host/controller/captures.js.map +1 -0
- package/lib/host/controller/commits.d.ts +4 -0
- package/lib/host/controller/commits.d.ts.map +1 -0
- package/lib/host/controller/commits.js +75 -0
- package/lib/host/controller/commits.js.map +1 -0
- package/lib/host/controller/controller.d.ts +40 -0
- package/lib/host/controller/controller.d.ts.map +1 -0
- package/lib/host/controller/controller.js +167 -0
- package/lib/host/controller/controller.js.map +1 -0
- package/lib/host/controller/index.d.ts +26 -0
- package/lib/host/controller/index.d.ts.map +1 -0
- package/lib/host/controller/index.js +25 -0
- package/lib/host/controller/index.js.map +1 -0
- package/lib/host/controller/notes.d.ts +26 -0
- package/lib/host/controller/notes.d.ts.map +1 -0
- package/lib/host/controller/notes.js +49 -0
- package/lib/host/controller/notes.js.map +1 -0
- package/lib/host/controller/persist.d.ts +9 -0
- package/lib/host/controller/persist.d.ts.map +1 -0
- package/lib/host/controller/persist.js +51 -0
- package/lib/host/controller/persist.js.map +1 -0
- package/lib/host/controller/runs.d.ts +56 -0
- package/lib/host/controller/runs.d.ts.map +1 -0
- package/lib/host/controller/runs.js +211 -0
- package/lib/host/controller/runs.js.map +1 -0
- package/lib/host/controller/state.d.ts +70 -0
- package/lib/host/controller/state.d.ts.map +1 -0
- package/lib/host/controller/state.js +2 -0
- package/lib/host/controller/state.js.map +1 -0
- package/lib/host/controller/windows-run.d.ts +9 -0
- package/lib/host/controller/windows-run.d.ts.map +1 -0
- package/lib/host/controller/windows-run.js +78 -0
- package/lib/host/controller/windows-run.js.map +1 -0
- package/lib/host/discovery/candidates.d.ts +37 -0
- package/lib/host/discovery/candidates.d.ts.map +1 -0
- package/lib/host/discovery/candidates.js +56 -0
- package/lib/host/discovery/candidates.js.map +1 -0
- package/lib/host/discovery/index.d.ts +18 -0
- package/lib/host/discovery/index.d.ts.map +1 -0
- package/lib/host/discovery/index.js +18 -0
- package/lib/host/discovery/index.js.map +1 -0
- package/lib/host/discovery/probe.d.ts +18 -0
- package/lib/host/discovery/probe.d.ts.map +1 -0
- package/lib/host/discovery/probe.js +97 -0
- package/lib/host/discovery/probe.js.map +1 -0
- package/lib/host/discovery/scan.d.ts +67 -0
- package/lib/host/discovery/scan.d.ts.map +1 -0
- package/lib/host/discovery/scan.js +169 -0
- package/lib/host/discovery/scan.js.map +1 -0
- package/lib/host/flags.d.ts +42 -0
- package/lib/host/flags.d.ts.map +1 -0
- package/lib/host/flags.js +118 -0
- package/lib/host/flags.js.map +1 -0
- package/lib/host/platform.d.ts +38 -0
- package/lib/host/platform.d.ts.map +1 -0
- package/lib/host/platform.js +64 -0
- package/lib/host/platform.js.map +1 -0
- package/lib/host/plugin-meta.d.ts +12 -0
- package/lib/host/plugin-meta.d.ts.map +1 -0
- package/lib/host/plugin-meta.js +12 -0
- package/lib/host/plugin-meta.js.map +1 -0
- package/lib/host/schema.d.ts +307 -0
- package/lib/host/schema.d.ts.map +1 -0
- package/lib/host/schema.js +171 -0
- package/lib/host/schema.js.map +1 -0
- package/lib/host/settings.d.ts +9 -0
- package/lib/host/settings.d.ts.map +1 -0
- package/lib/host/settings.js +70 -0
- package/lib/host/settings.js.map +1 -0
- package/lib/host/status.d.ts +28 -0
- package/lib/host/status.d.ts.map +1 -0
- package/lib/host/status.js +60 -0
- package/lib/host/status.js.map +1 -0
- package/lib/host/tool.d.ts +59 -0
- package/lib/host/tool.d.ts.map +1 -0
- package/lib/host/tool.js +51 -0
- package/lib/host/tool.js.map +1 -0
- package/lib/host/types/config.d.ts +47 -0
- package/lib/host/types/config.d.ts.map +1 -0
- package/lib/host/types/config.js +2 -0
- package/lib/host/types/config.js.map +1 -0
- package/lib/host/types/executable.d.ts +30 -0
- package/lib/host/types/executable.d.ts.map +1 -0
- package/lib/host/types/executable.js +9 -0
- package/lib/host/types/executable.js.map +1 -0
- package/lib/host/types/index.d.ts +23 -0
- package/lib/host/types/index.d.ts.map +1 -0
- package/lib/host/types/index.js +2 -0
- package/lib/host/types/index.js.map +1 -0
- package/lib/host/types/plugin.d.ts +38 -0
- package/lib/host/types/plugin.d.ts.map +1 -0
- package/lib/host/types/plugin.js +10 -0
- package/lib/host/types/plugin.js.map +1 -0
- package/lib/host/types/settings.d.ts +127 -0
- package/lib/host/types/settings.d.ts.map +1 -0
- package/lib/host/types/settings.js +2 -0
- package/lib/host/types/settings.js.map +1 -0
- package/lib/host/types/stderr.d.ts +53 -0
- package/lib/host/types/stderr.d.ts.map +1 -0
- package/lib/host/types/stderr.js +9 -0
- package/lib/host/types/stderr.js.map +1 -0
- package/lib/host/types/windows.d.ts +39 -0
- package/lib/host/types/windows.d.ts.map +1 -0
- package/lib/host/types/windows.js +9 -0
- package/lib/host/types/windows.js.map +1 -0
- package/lib/host/values.d.ts +89 -0
- package/lib/host/values.d.ts.map +1 -0
- package/lib/host/values.js +212 -0
- package/lib/host/values.js.map +1 -0
- package/lib/host/windows/connect-flags.d.ts +16 -0
- package/lib/host/windows/connect-flags.d.ts.map +1 -0
- package/lib/host/windows/connect-flags.js +67 -0
- package/lib/host/windows/connect-flags.js.map +1 -0
- package/lib/host/windows/index.d.ts +31 -0
- package/lib/host/windows/index.d.ts.map +1 -0
- package/lib/host/windows/index.js +30 -0
- package/lib/host/windows/index.js.map +1 -0
- package/lib/host/windows/landing.d.ts +47 -0
- package/lib/host/windows/landing.d.ts.map +1 -0
- package/lib/host/windows/landing.js +174 -0
- package/lib/host/windows/landing.js.map +1 -0
- package/lib/host/windows/link.d.ts +11 -0
- package/lib/host/windows/link.d.ts.map +1 -0
- package/lib/host/windows/link.js +52 -0
- package/lib/host/windows/link.js.map +1 -0
- package/lib/host/windows/paths.d.ts +43 -0
- package/lib/host/windows/paths.d.ts.map +1 -0
- package/lib/host/windows/paths.js +121 -0
- package/lib/host/windows/paths.js.map +1 -0
- package/lib/host/windows/run.d.ts +74 -0
- package/lib/host/windows/run.d.ts.map +1 -0
- package/lib/host/windows/run.js +210 -0
- package/lib/host/windows/run.js.map +1 -0
- package/lib/host/windows/spawn.d.ts +22 -0
- package/lib/host/windows/spawn.d.ts.map +1 -0
- package/lib/host/windows/spawn.js +73 -0
- package/lib/host/windows/spawn.js.map +1 -0
- package/lib/index.d.ts +86 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +72 -625
- package/lib/index.js.map +1 -0
- package/package.json +26 -4
package/lib/client.js
CHANGED
|
@@ -4,582 +4,1447 @@ window.__ModuleLoader__.load({
|
|
|
4
4
|
var module = { exports: {} };
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
"use strict";
|
|
8
|
+
var __create = Object.create;
|
|
9
|
+
var __defProp = Object.defineProperty;
|
|
10
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
11
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
12
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
27
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
28
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
29
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
30
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
31
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
32
|
+
mod
|
|
33
|
+
));
|
|
34
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
7
35
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
36
|
+
// src/client/index.ts
|
|
37
|
+
var index_exports = {};
|
|
38
|
+
__export(index_exports, {
|
|
39
|
+
CHECK_POLL_TICK_MS: () => CHECK_POLL_TICK_MS,
|
|
40
|
+
CHECK_POLL_TIMEOUT_MS: () => CHECK_POLL_TIMEOUT_MS,
|
|
41
|
+
apply: () => apply,
|
|
42
|
+
inject: () => inject
|
|
43
|
+
});
|
|
44
|
+
module.exports = __toCommonJS(index_exports);
|
|
45
|
+
|
|
46
|
+
// src/client/card/index.ts
|
|
47
|
+
var react4 = __toESM(require("react"), 1);
|
|
48
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
49
|
+
|
|
50
|
+
// dsh-css:/home/roberto/dev/dsh/dsh-chrome-mcp/src/client/styles/ChromeMcpCard.module.css
|
|
51
|
+
var cssText = '.e5pfpa_card{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-3);border-radius:16px;list-style:none;transition:border-color .16s,background .16s}.e5pfpa_card:hover{border-color:var(--dsw-alias-label-dimmed)}.e5pfpa_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.e5pfpa_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.e5pfpa_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.e5pfpa_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.e5pfpa_nameRow{align-items:center;gap:8px;min-width:0;display:flex}.e5pfpa_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.e5pfpa_desc{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.e5pfpa_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.e5pfpa_chevronOpen{transform:rotate(180deg)}.e5pfpa_body{border-top:.5px solid var(--dsw-alias-border-l2);display:flex;flex-direction:column;gap:10px;margin:0 16px;padding:12px 0}.e5pfpa_field{align-items:flex-start;gap:4px;display:flex;flex-direction:column;width:100%}.e5pfpa_fieldRow{align-items:center;gap:8px;display:flex;width:100%}.e5pfpa_fieldTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px;flex:1;min-width:0}.e5pfpa_fieldDesc{color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:400;line-height:18px;width:100%}.e5pfpa_selector{background:var(--dsw-alias-bg-module-platform);height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:18px;align-items:center;gap:12px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.e5pfpa_selector:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.e5pfpa_selector:disabled{cursor:default}.e5pfpa_selectorWrap{display:flex}.e5pfpa_pillChevron{flex:none}.e5pfpa_row{display:flex;gap:10px;align-items:center}.e5pfpa_button{display:inline-flex;align-items:center;justify-content:center;gap:4px;border:.5px solid var(--dsw-alias-border-l3);border-radius:14px;cursor:pointer;font-size:12px;line-height:18px;color:var(--dsw-alias-label-primary);background:transparent;padding:0 10px;height:28px}.e5pfpa_button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.e5pfpa_button:disabled{cursor:not-allowed;opacity:.4}.e5pfpa_button:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.e5pfpa_readOnly{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px}.e5pfpa_error{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px;line-height:1.5}.e5pfpa_dot{box-sizing:border-box;corner-shape:round;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.e5pfpa_dotError{background:var(--dsw-alias-state-error-primary)}.e5pfpa_empty{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px}.e5pfpa_catalog{flex-direction:column;align-items:stretch;gap:10px;display:flex}.e5pfpa_catalog+.e5pfpa_catalog{border-top:.5px solid var(--dsw-alias-border-l2);padding-top:12px}.e5pfpa_catalog>.e5pfpa_linkButton{align-self:flex-start}.e5pfpa_catalog>.e5pfpa_linkButtonRow{align-self:flex-start;display:flex;gap:8px;align-items:center}.e5pfpa_linkButton{box-sizing:border-box;border:none;background:0 0;color:var(--dsw-alias-label-tertiary);cursor:pointer;border-radius:14px;align-items:center;height:28px;padding:0 10px;font:inherit;font-size:12px;line-height:18px;display:inline-flex}.e5pfpa_linkButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}.e5pfpa_linkButton:disabled{opacity:.4;cursor:default}.e5pfpa_linkButton:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.e5pfpa_catalogEntry{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-module-platform);border-radius:10px;padding:6px}.e5pfpa_catalogRow{grid-template-columns:minmax(0,1.4fr) minmax(0,1fr) auto;align-items:center;gap:6px;display:grid}.e5pfpa_catalogInput{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);border-radius:8px;height:28px;padding:0 10px;font:inherit;font-size:14px;line-height:22px;min-width:0;width:100%}.e5pfpa_catalogInput::placeholder{color:var(--dsw-alias-label-dimmed)}.e5pfpa_catalogInput:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-1px}.e5pfpa_catalogInput:disabled{opacity:.6;cursor:default}.e5pfpa_iconButton{box-sizing:border-box;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;display:inline-flex}.e5pfpa_iconButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.e5pfpa_iconButton:disabled{opacity:.4;cursor:default}.e5pfpa_iconButton:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.e5pfpa_iconButtonDanger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-state-error-primary)}.e5pfpa_addButton{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:14px;align-self:flex-start;align-items:center;gap:4px;height:28px;padding:0 10px;font:inherit;font-size:12px;line-height:18px;display:inline-flex}.e5pfpa_addButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.e5pfpa_addButton:disabled{opacity:.4;cursor:default}.e5pfpa_disclosure{flex-direction:column;gap:10px;display:flex}.e5pfpa_disclosureSummary{color:var(--dsw-alias-label-secondary);cursor:pointer;list-style:none;align-items:center;gap:6px;font-size:12px;line-height:18px;user-select:none;display:flex}.e5pfpa_disclosureSummary::-webkit-details-marker{display:none}.e5pfpa_disclosureSummary:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px;border-radius:6px}.e5pfpa_disclosureLabel:before{content:"\\25b8";display:inline-block;margin-right:6px;transition:transform .16s}.e5pfpa_disclosure[open] .e5pfpa_disclosureLabel:before{transform:rotate(90deg)}.e5pfpa_disclosureCount{color:var(--dsw-alias-label-tertiary)}.e5pfpa_disclosureBody{flex-direction:column;gap:10px;display:flex}.e5pfpa_disclosureBody>.e5pfpa_linkButton{align-self:flex-start}.e5pfpa_disclosureBody>.e5pfpa_linkButtonRow{align-self:flex-start;display:flex;gap:8px;align-items:center}.e5pfpa_toggleLabel{align-items:center;display:inline-flex;flex:none}.e5pfpa_fetchDialog{background:color-mix(in srgb,var(--dsw-alias-label-primary) 28%,transparent);align-items:center;justify-content:center;display:flex;position:fixed;inset:0;z-index:90}.e5pfpa_dialogContent{background:var(--dsw-alias-bg-layer-3);border:.5px solid var(--dsw-alias-border-l3);border-radius:16px;flex-direction:column;gap:10px;width:min(520px,calc(100vw - 32px));max-height:min(620px,calc(100vh - 48px));display:flex;overflow:hidden}.e5pfpa_dialogHeader{align-items:center;gap:8px;padding:14px 16px 0;display:flex}.e5pfpa_dialogTitle{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:22px;flex:1;min-width:0;margin:0}.e5pfpa_dialogClose{box-sizing:border-box;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;flex:none;width:28px;height:28px;display:inline-flex}.e5pfpa_dialogClose:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.e5pfpa_dialogClose:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.e5pfpa_dialogDescription{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;margin:0;padding:0 16px}.e5pfpa_dialogBody{flex-direction:column;flex:1;gap:8px;min-height:0;padding:0 16px;display:flex}.e5pfpa_candidateToolbar{align-items:center;gap:8px;display:flex}.e5pfpa_candidateSearch{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);border-radius:8px;height:32px;padding:0 10px;font:inherit;font-size:12px;line-height:20px;flex:1;min-width:0}.e5pfpa_candidateSearch::placeholder{color:var(--dsw-alias-label-dimmed)}.e5pfpa_candidateSearch:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-1px}.e5pfpa_candidateList{flex-direction:column;gap:2px;margin:0;padding:0 0 10px;list-style:none;overflow-y:auto;display:flex}.e5pfpa_candidate{border-radius:8px;padding:4px 6px}.e5pfpa_candidate:hover{background:var(--dsw-alias-interactive-bg-hover)}.e5pfpa_candidateLabel{align-items:center;gap:8px;min-width:0;display:flex}.e5pfpa_candidateLabel input[type=checkbox]{accent-color:var(--dsw-alias-brand-primary);flex:none;margin:0}.e5pfpa_candidateId{color:var(--dsw-alias-label-primary);font-size:12px;line-height:18px;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.e5pfpa_candidateNote{color:var(--dsw-alias-label-tertiary);flex:none;font-size:11px;line-height:16px}.e5pfpa_dialogFooter{border-top:.5px solid var(--dsw-alias-border-l2);align-items:center;justify-content:flex-end;gap:8px;padding:12px 16px;display:flex}.e5pfpa_dialogButton{display:inline-flex;align-items:center;justify-content:center;gap:4px;border:.5px solid var(--dsw-alias-border-l3);border-radius:16px;cursor:pointer;font-size:13px;line-height:20px;color:var(--dsw-alias-label-primary);background:transparent;padding:0 12px;height:32px}.e5pfpa_dialogButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.e5pfpa_dialogButton:disabled{cursor:not-allowed;opacity:.4}.e5pfpa_dialogButton:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.e5pfpa_dialogButtonPrimary{color:var(--dsw-alias-label-primary);font-weight:600}.e5pfpa_selectorLabel{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.e5pfpa_result{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.e5pfpa_resultValue{color:var(--dsw-alias-label-secondary);margin-left:4px}.e5pfpa_resultAge{color:var(--dsw-alias-label-dimmed);margin-left:4px}.e5pfpa_notice{flex-direction:column;gap:4px;display:flex;border:.5px solid var(--dsw-alias-border-l3);border-radius:10px;background:var(--dsw-alias-bg-layer-1);padding:8px 10px}.e5pfpa_noticeTitle{color:var(--dsw-alias-label-primary);font-size:12px;font-weight:600;line-height:18px}.e5pfpa_noticeBody{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.e5pfpa_docsLink{color:var(--dsw-alias-label-tertiary);align-items:center;gap:4px;display:inline-flex;text-decoration:none;font-size:12px;line-height:1.5}.e5pfpa_docsLinkLabel{text-decoration:none}.e5pfpa_docsLink:hover{color:var(--dsw-alias-brand-primary)}.e5pfpa_docsLink:hover .e5pfpa_docsLinkLabel{text-decoration:underline}.e5pfpa_docsLink:hover .e5pfpa_docsIcon{color:var(--dsw-alias-brand-primary)}.e5pfpa_docsIcon{color:var(--dsw-alias-label-tertiary);flex:none}.e5pfpa_docsLink:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px;border-radius:4px}.e5pfpa_catalogRowSingle{grid-template-columns:minmax(0,1fr) auto}.e5pfpa_catalogInputMono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}\n';
|
|
52
|
+
var ChromeMcpCard_default = { "card": "e5pfpa_card", "cardOpen": "e5pfpa_cardOpen", "header": "e5pfpa_header", "headText": "e5pfpa_headText", "nameRow": "e5pfpa_nameRow", "name": "e5pfpa_name", "desc": "e5pfpa_desc", "chevron": "e5pfpa_chevron", "chevronOpen": "e5pfpa_chevronOpen", "body": "e5pfpa_body", "field": "e5pfpa_field", "fieldRow": "e5pfpa_fieldRow", "fieldTitle": "e5pfpa_fieldTitle", "fieldDesc": "e5pfpa_fieldDesc", "selector": "e5pfpa_selector", "selectorWrap": "e5pfpa_selectorWrap", "pillChevron": "e5pfpa_pillChevron", "row": "e5pfpa_row", "button": "e5pfpa_button", "readOnly": "e5pfpa_readOnly", "error": "e5pfpa_error", "dot": "e5pfpa_dot", "dotError": "e5pfpa_dotError", "empty": "e5pfpa_empty", "catalog": "e5pfpa_catalog", "linkButton": "e5pfpa_linkButton", "linkButtonRow": "e5pfpa_linkButtonRow", "catalogEntry": "e5pfpa_catalogEntry", "catalogRow": "e5pfpa_catalogRow", "catalogInput": "e5pfpa_catalogInput", "iconButton": "e5pfpa_iconButton", "iconButtonDanger": "e5pfpa_iconButtonDanger", "addButton": "e5pfpa_addButton", "disclosure": "e5pfpa_disclosure", "disclosureSummary": "e5pfpa_disclosureSummary", "disclosureLabel": "e5pfpa_disclosureLabel", "disclosureCount": "e5pfpa_disclosureCount", "disclosureBody": "e5pfpa_disclosureBody", "toggleLabel": "e5pfpa_toggleLabel", "fetchDialog": "e5pfpa_fetchDialog", "dialogContent": "e5pfpa_dialogContent", "dialogHeader": "e5pfpa_dialogHeader", "dialogTitle": "e5pfpa_dialogTitle", "dialogClose": "e5pfpa_dialogClose", "dialogDescription": "e5pfpa_dialogDescription", "dialogBody": "e5pfpa_dialogBody", "candidateToolbar": "e5pfpa_candidateToolbar", "candidateSearch": "e5pfpa_candidateSearch", "candidateList": "e5pfpa_candidateList", "candidate": "e5pfpa_candidate", "candidateLabel": "e5pfpa_candidateLabel", "candidateId": "e5pfpa_candidateId", "candidateNote": "e5pfpa_candidateNote", "dialogFooter": "e5pfpa_dialogFooter", "dialogButton": "e5pfpa_dialogButton", "dialogButtonPrimary": "e5pfpa_dialogButtonPrimary", "selectorLabel": "e5pfpa_selectorLabel", "result": "e5pfpa_result", "resultValue": "e5pfpa_resultValue", "resultAge": "e5pfpa_resultAge", "notice": "e5pfpa_notice", "noticeTitle": "e5pfpa_noticeTitle", "noticeBody": "e5pfpa_noticeBody", "docsLink": "e5pfpa_docsLink", "docsLinkLabel": "e5pfpa_docsLinkLabel", "docsIcon": "e5pfpa_docsIcon", "catalogRowSingle": "e5pfpa_catalogRowSingle", "catalogInputMono": "e5pfpa_catalogInputMono" };
|
|
53
|
+
|
|
54
|
+
// src/client/card/flags.ts
|
|
55
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
56
|
+
|
|
57
|
+
// src/client/plugin-meta.ts
|
|
58
|
+
var SETTINGS_NAMESPACE = "chrome-mcp";
|
|
59
|
+
var LOCALE_NAMESPACE = "chromeMcp";
|
|
60
|
+
var PLUGIN_ID = "@comecaramelos/dsh-chrome-mcp";
|
|
61
|
+
var CONFIGURATION_URL = "https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/configuration.md";
|
|
62
|
+
var inject = ["slots", "locale", "settingsScope"];
|
|
63
|
+
|
|
64
|
+
// src/client/card/icons.ts
|
|
65
|
+
var CHEVRON_PATH = "M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z";
|
|
66
|
+
var TRASH_PATH = "M2.5 4h11M6.5 4V2.5h3V4M4 4l.7 9a1 1 0 001 .9h4.6a1 1 0 001-.9L12 4M6.5 6.8v4.4M9.5 6.8v4.4";
|
|
67
|
+
var CLOSE_PATHS = [
|
|
68
|
+
"M14.1168 13.197L13.197 14.1167L1.8833 2.80303L2.80309 1.88324L14.1168 13.197Z",
|
|
69
|
+
"M13.197 1.88326L14.1168 2.80305L2.80309 14.1168L1.8833 13.197L13.197 1.88326Z"
|
|
70
|
+
];
|
|
71
|
+
var DOCS_PATHS = [
|
|
72
|
+
"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",
|
|
73
|
+
"M15 3h6v6",
|
|
74
|
+
"M10 14 21 3"
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
// src/client/card/rows.ts
|
|
78
|
+
var react = __toESM(require("react"), 1);
|
|
79
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
80
|
+
function RowList(props) {
|
|
81
|
+
var served = Array.isArray(props.values) ? props.values : [];
|
|
82
|
+
var writable = props.writable === true;
|
|
83
|
+
var busy = props.busy === true;
|
|
84
|
+
var labels = Array.isArray(props.labels) ? props.labels : [];
|
|
85
|
+
var placeholders = Array.isArray(props.placeholders) ? props.placeholders : [];
|
|
86
|
+
var columns = Math.max(1, labels.length);
|
|
87
|
+
var blankRow = [];
|
|
88
|
+
for (var blank = 0; blank < columns; blank++) blankRow.push("");
|
|
89
|
+
var draftState = react.useState(null);
|
|
90
|
+
var draft = draftState[0];
|
|
91
|
+
var setDraft = draftState[1];
|
|
92
|
+
var focusedState = react.useState(false);
|
|
93
|
+
var focused = focusedState[0];
|
|
94
|
+
var setFocused = focusedState[1];
|
|
95
|
+
var servedKey = JSON.stringify(served);
|
|
96
|
+
var syncState = react.useState("");
|
|
97
|
+
var servedSync = syncState[0];
|
|
98
|
+
var setServedSync = syncState[1];
|
|
99
|
+
if (servedKey !== servedSync) {
|
|
100
|
+
setServedSync(servedKey);
|
|
101
|
+
if (!focused) setDraft(null);
|
|
102
|
+
}
|
|
103
|
+
var rows = draft === null ? served : draft;
|
|
104
|
+
var save = (next) => {
|
|
105
|
+
if (typeof props.save !== "function") return Promise.resolve();
|
|
106
|
+
setDraft(next);
|
|
107
|
+
return Promise.resolve(props.save(next)).catch(() => {
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
var withDraft = (make) => make(draft === null ? served : draft);
|
|
111
|
+
var patchRow = (index2, column, value) => {
|
|
112
|
+
setFocused(true);
|
|
113
|
+
setDraft(
|
|
114
|
+
withDraft(
|
|
115
|
+
(current) => current.map(
|
|
116
|
+
(row, at) => at === index2 ? row.map((cell, field) => field === column ? value : cell) : row
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
);
|
|
120
|
+
};
|
|
121
|
+
var removeRow = (index2) => {
|
|
122
|
+
if (!writable || rows[index2] === void 0) return;
|
|
123
|
+
save(rows.filter((_row, at) => at !== index2));
|
|
124
|
+
};
|
|
125
|
+
var addRow = () => {
|
|
126
|
+
if (!writable) return;
|
|
127
|
+
setDraft(withDraft((current) => current.concat([blankRow])));
|
|
128
|
+
};
|
|
129
|
+
var commit = () => {
|
|
130
|
+
if (draft === null) return;
|
|
131
|
+
save(rows);
|
|
132
|
+
};
|
|
133
|
+
var fieldNode = (row, index2, column) => (0, import_jsx_runtime.jsx)("input", {
|
|
134
|
+
className: props.inputClassName ? ChromeMcpCard_default.catalogInput + " " + props.inputClassName : ChromeMcpCard_default.catalogInput,
|
|
135
|
+
type: "text",
|
|
136
|
+
value: row[column] === void 0 ? "" : row[column],
|
|
137
|
+
placeholder: placeholders[column],
|
|
138
|
+
"aria-label": labels[column] + " " + (index2 + 1),
|
|
139
|
+
title: column === 0 ? row[column] : void 0,
|
|
140
|
+
disabled: !writable,
|
|
141
|
+
spellCheck: false,
|
|
142
|
+
autoComplete: "off",
|
|
143
|
+
autocapitalize: "off",
|
|
144
|
+
onChange: (event) => {
|
|
145
|
+
patchRow(index2, column, event && event.target ? String(event.target.value ?? "") : "");
|
|
146
|
+
},
|
|
147
|
+
onFocus: () => setFocused(true),
|
|
148
|
+
onBlur: () => {
|
|
149
|
+
setFocused(false);
|
|
150
|
+
commit();
|
|
151
|
+
},
|
|
152
|
+
onKeyDown: (event) => {
|
|
153
|
+
if (event && event.key === "Enter") {
|
|
154
|
+
setFocused(false);
|
|
155
|
+
commit();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}, column);
|
|
159
|
+
var entryNode = (row, index2) => {
|
|
160
|
+
var cells = [];
|
|
161
|
+
var width = row.length === 0 ? columns : row.length;
|
|
162
|
+
for (var column = 0; column < width; column++) cells.push(fieldNode(row, index2, column));
|
|
163
|
+
cells.push(
|
|
164
|
+
(0, import_jsx_runtime.jsx)("button", {
|
|
165
|
+
type: "button",
|
|
166
|
+
className: ChromeMcpCard_default.iconButton + " " + ChromeMcpCard_default.iconButtonDanger,
|
|
167
|
+
"aria-label": props.removeLabel + " " + (index2 + 1),
|
|
168
|
+
title: props.removeLabel,
|
|
169
|
+
disabled: !writable,
|
|
170
|
+
onClick: () => {
|
|
171
|
+
removeRow(index2);
|
|
172
|
+
},
|
|
173
|
+
children: (0, import_jsx_runtime.jsx)("svg", {
|
|
174
|
+
width: 14,
|
|
175
|
+
height: 14,
|
|
176
|
+
viewBox: "0 0 16 16",
|
|
177
|
+
fill: "none",
|
|
178
|
+
"aria-hidden": "true",
|
|
179
|
+
children: (0, import_jsx_runtime.jsx)("path", {
|
|
180
|
+
d: TRASH_PATH,
|
|
181
|
+
stroke: "currentColor",
|
|
182
|
+
strokeWidth: 1.3,
|
|
183
|
+
strokeLinecap: "round",
|
|
184
|
+
strokeLinejoin: "round"
|
|
185
|
+
})
|
|
186
|
+
})
|
|
187
|
+
})
|
|
188
|
+
);
|
|
189
|
+
return (0, import_jsx_runtime.jsx)("div", {
|
|
190
|
+
className: ChromeMcpCard_default.catalogEntry,
|
|
191
|
+
children: (0, import_jsx_runtime.jsxs)("div", {
|
|
192
|
+
className: props.rowClassName ? ChromeMcpCard_default.catalogRow + " " + props.rowClassName : ChromeMcpCard_default.catalogRow,
|
|
193
|
+
children: cells
|
|
194
|
+
})
|
|
195
|
+
}, index2);
|
|
196
|
+
};
|
|
197
|
+
var body = [];
|
|
198
|
+
if (typeof props.renderAction === "function") body.push(props.renderAction(rows, save));
|
|
199
|
+
var visible = [];
|
|
200
|
+
for (var index = 0; index < rows.length; index++) visible.push(entryNode(rows[index], index));
|
|
201
|
+
body = body.concat(visible);
|
|
202
|
+
body.push(
|
|
203
|
+
(0, import_jsx_runtime.jsx)("button", {
|
|
204
|
+
type: "button",
|
|
205
|
+
className: ChromeMcpCard_default.addButton,
|
|
206
|
+
disabled: !writable || busy,
|
|
207
|
+
onClick: addRow,
|
|
208
|
+
children: props.addLabel
|
|
209
|
+
})
|
|
210
|
+
);
|
|
211
|
+
var emptyLine = rows.length === 0 ? (0, import_jsx_runtime.jsx)("p", { className: ChromeMcpCard_default.empty, children: props.emptyText }) : null;
|
|
212
|
+
if (props.disclosure !== true) {
|
|
213
|
+
return [emptyLine].concat(body);
|
|
214
|
+
}
|
|
215
|
+
return [
|
|
216
|
+
emptyLine,
|
|
217
|
+
(0, import_jsx_runtime.jsxs)("details", {
|
|
218
|
+
className: ChromeMcpCard_default.disclosure,
|
|
219
|
+
children: [
|
|
220
|
+
(0, import_jsx_runtime.jsxs)("summary", {
|
|
221
|
+
className: ChromeMcpCard_default.disclosureSummary,
|
|
222
|
+
children: [
|
|
223
|
+
(0, import_jsx_runtime.jsx)("span", { className: ChromeMcpCard_default.disclosureLabel, children: props.rowsLabel }),
|
|
224
|
+
(0, import_jsx_runtime.jsx)("span", { className: ChromeMcpCard_default.disclosureCount, children: String(rows.length) })
|
|
225
|
+
]
|
|
226
|
+
}),
|
|
227
|
+
(0, import_jsx_runtime.jsx)("div", { className: ChromeMcpCard_default.disclosureBody, children: body })
|
|
228
|
+
]
|
|
229
|
+
})
|
|
230
|
+
];
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// src/client/card/flags.ts
|
|
234
|
+
function rowsOf(flags) {
|
|
235
|
+
return flags.map((flag) => [flag]);
|
|
236
|
+
}
|
|
237
|
+
function flagsOf(rows) {
|
|
238
|
+
var flags = [];
|
|
239
|
+
for (const row of rows) {
|
|
240
|
+
var flag = typeof row === "undefined" || row === null ? "" : String(row[0] ?? "").trim();
|
|
241
|
+
if (flag === "") continue;
|
|
242
|
+
if (flags.indexOf(flag) !== -1) continue;
|
|
243
|
+
flags.push(flag);
|
|
244
|
+
}
|
|
245
|
+
return flags;
|
|
246
|
+
}
|
|
247
|
+
function ExtraFlagsEditor(props) {
|
|
248
|
+
var t = props.t;
|
|
249
|
+
var writable = props.writable;
|
|
250
|
+
var flags = Array.isArray(props.flags) ? props.flags : [];
|
|
251
|
+
var defaults = Array.isArray(props.defaultFlags) ? props.defaultFlags : [];
|
|
252
|
+
var wsl = Array.isArray(props.wslFlags) ? props.wslFlags : [];
|
|
253
|
+
var save = (next) => {
|
|
254
|
+
if (typeof props.saveFlags !== "function") return Promise.resolve();
|
|
255
|
+
return Promise.resolve(props.saveFlags(flagsOf(next)));
|
|
256
|
+
};
|
|
257
|
+
var restoreAction = (visible) => {
|
|
258
|
+
var current = flagsOf(visible);
|
|
259
|
+
var merged = current.concat(defaults.filter((flag) => current.indexOf(flag) === -1));
|
|
260
|
+
return (0, import_jsx_runtime2.jsx)("button", {
|
|
261
|
+
type: "button",
|
|
262
|
+
className: ChromeMcpCard_default.linkButton,
|
|
263
|
+
// The defaults list is host-served — with nothing to recover there
|
|
264
|
+
// is nothing to do.
|
|
265
|
+
disabled: !writable || defaults.length === 0,
|
|
266
|
+
title: t("flagsRestoreTitle"),
|
|
267
|
+
onClick: () => {
|
|
268
|
+
if (!writable) return;
|
|
269
|
+
save(merged.map((flag) => [flag]));
|
|
270
|
+
},
|
|
271
|
+
children: t("flagsRestore")
|
|
272
|
+
});
|
|
273
|
+
};
|
|
274
|
+
var wslAction = (visible) => {
|
|
275
|
+
var current = flagsOf(visible);
|
|
276
|
+
var merged = current.concat(wsl.filter((flag) => current.indexOf(flag) === -1));
|
|
277
|
+
return (0, import_jsx_runtime2.jsx)("button", {
|
|
278
|
+
type: "button",
|
|
279
|
+
className: ChromeMcpCard_default.linkButton,
|
|
280
|
+
// The recommended list is host-served — with every one of them
|
|
281
|
+
// already in the rows there is nothing left to add.
|
|
282
|
+
disabled: !writable || wsl.length === 0,
|
|
283
|
+
title: t("flagsWslTitle"),
|
|
284
|
+
onClick: () => {
|
|
285
|
+
if (!writable) return;
|
|
286
|
+
save(merged.map((flag) => [flag]));
|
|
287
|
+
},
|
|
288
|
+
children: t("flagsWsl")
|
|
289
|
+
});
|
|
290
|
+
};
|
|
291
|
+
var renderActions = (visible) => (0, import_jsx_runtime2.jsxs)("div", {
|
|
292
|
+
className: ChromeMcpCard_default.linkButtonRow,
|
|
293
|
+
children: [restoreAction(visible), wslAction(visible)]
|
|
294
|
+
});
|
|
295
|
+
return (0, import_jsx_runtime2.jsxs)("div", {
|
|
296
|
+
className: ChromeMcpCard_default.catalog,
|
|
297
|
+
children: [
|
|
298
|
+
(0, import_jsx_runtime2.jsxs)("div", {
|
|
299
|
+
className: ChromeMcpCard_default.field,
|
|
300
|
+
children: [
|
|
301
|
+
(0, import_jsx_runtime2.jsxs)("div", {
|
|
302
|
+
className: ChromeMcpCard_default.fieldRow,
|
|
303
|
+
children: [
|
|
304
|
+
(0, import_jsx_runtime2.jsx)("span", { className: ChromeMcpCard_default.fieldTitle, children: t("flagsTitle") }),
|
|
305
|
+
// The guide link rides the same field row the picker's
|
|
306
|
+
// pill occupies.
|
|
307
|
+
(0, import_jsx_runtime2.jsxs)("a", {
|
|
308
|
+
className: ChromeMcpCard_default.docsLink,
|
|
309
|
+
href: CONFIGURATION_URL,
|
|
310
|
+
target: "_blank",
|
|
311
|
+
rel: "noopener noreferrer",
|
|
312
|
+
title: t("docsMessage"),
|
|
313
|
+
children: [
|
|
314
|
+
(0, import_jsx_runtime2.jsx)("svg", {
|
|
315
|
+
className: ChromeMcpCard_default.docsIcon,
|
|
316
|
+
width: 14,
|
|
317
|
+
height: 14,
|
|
318
|
+
viewBox: "0 0 24 24",
|
|
319
|
+
fill: "none",
|
|
320
|
+
stroke: "currentColor",
|
|
321
|
+
strokeWidth: 2,
|
|
322
|
+
strokeLinecap: "round",
|
|
323
|
+
strokeLinejoin: "round",
|
|
324
|
+
"aria-hidden": "true",
|
|
325
|
+
children: DOCS_PATHS.map((d, at) => (0, import_jsx_runtime2.jsx)("path", { d }, at))
|
|
326
|
+
}),
|
|
327
|
+
(0, import_jsx_runtime2.jsx)("span", { className: ChromeMcpCard_default.docsLinkLabel, children: t("docsLinkLabel") })
|
|
328
|
+
]
|
|
329
|
+
})
|
|
330
|
+
]
|
|
331
|
+
}),
|
|
332
|
+
(0, import_jsx_runtime2.jsx)("div", { className: ChromeMcpCard_default.fieldDesc, children: t("flagsHint") })
|
|
333
|
+
]
|
|
334
|
+
}),
|
|
335
|
+
// One row per saved flag: the single field is the flag itself, and
|
|
336
|
+
// the two append controls — Restore defaults and Flags WSL — ride
|
|
337
|
+
// the list's action line.
|
|
338
|
+
(0, import_jsx_runtime2.jsx)(RowList, {
|
|
339
|
+
values: rowsOf(flags),
|
|
340
|
+
labels: [t("flagsValueLabel")],
|
|
341
|
+
placeholders: [t("flagsValuePlaceholder")],
|
|
342
|
+
writable,
|
|
343
|
+
save,
|
|
344
|
+
renderAction: renderActions,
|
|
345
|
+
emptyText: t("flagsEmpty"),
|
|
346
|
+
rowsLabel: t("flagsRows"),
|
|
347
|
+
addLabel: t("addFlag"),
|
|
348
|
+
removeLabel: t("removeFlag"),
|
|
349
|
+
rowClassName: ChromeMcpCard_default.catalogRowSingle,
|
|
350
|
+
inputClassName: ChromeMcpCard_default.catalogInputMono,
|
|
351
|
+
disclosure: true
|
|
352
|
+
})
|
|
353
|
+
]
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// src/client/card/picker.ts
|
|
358
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
359
|
+
var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
360
|
+
function CatalogPicker(props) {
|
|
361
|
+
var entries = props.entries;
|
|
362
|
+
var options = props.options;
|
|
363
|
+
var selectedId = props.selectedId;
|
|
364
|
+
var writable = props.writable;
|
|
365
|
+
var busy = props.busy === true;
|
|
366
|
+
var rows = entries.map((entry) => [entry.id, entry.name]);
|
|
367
|
+
var saveEntries = (next) => {
|
|
368
|
+
if (typeof props.saveEntries !== "function") return Promise.resolve();
|
|
369
|
+
return Promise.resolve(props.saveEntries(next.map((row) => ({ id: row[0] ?? "", name: row[1] ?? "" }))));
|
|
370
|
+
};
|
|
371
|
+
var fetchAction = () => (0, import_jsx_runtime3.jsx)("button", {
|
|
372
|
+
type: "button",
|
|
373
|
+
className: ChromeMcpCard_default.linkButton,
|
|
374
|
+
disabled: !writable || busy,
|
|
375
|
+
onClick: () => {
|
|
376
|
+
props.refresh();
|
|
377
|
+
},
|
|
378
|
+
children: busy ? props.busyLabel : props.refreshLabel
|
|
379
|
+
});
|
|
380
|
+
var windowsAction = () => (0, import_jsx_runtime3.jsx)("button", {
|
|
381
|
+
type: "button",
|
|
382
|
+
className: ChromeMcpCard_default.linkButton,
|
|
383
|
+
disabled: props.windowsDisabled === true,
|
|
384
|
+
title: props.windowsTitle,
|
|
385
|
+
onClick: () => {
|
|
386
|
+
if (typeof props.windowsOnOpen === "function") props.windowsOnOpen();
|
|
387
|
+
},
|
|
388
|
+
children: props.windowsLabel
|
|
389
|
+
});
|
|
390
|
+
var renderActions = () => (0, import_jsx_runtime3.jsxs)("div", {
|
|
391
|
+
className: ChromeMcpCard_default.linkButtonRow,
|
|
392
|
+
children: [fetchAction(), windowsAction()]
|
|
393
|
+
});
|
|
394
|
+
return (0, import_jsx_runtime3.jsxs)("div", {
|
|
395
|
+
className: ChromeMcpCard_default.catalog,
|
|
396
|
+
children: [
|
|
397
|
+
(0, import_jsx_runtime3.jsxs)("div", {
|
|
398
|
+
className: ChromeMcpCard_default.field,
|
|
399
|
+
children: [
|
|
400
|
+
(0, import_jsx_runtime3.jsxs)("div", {
|
|
401
|
+
className: ChromeMcpCard_default.fieldRow,
|
|
402
|
+
children: [
|
|
403
|
+
(0, import_jsx_runtime3.jsx)("span", { className: ChromeMcpCard_default.fieldTitle, children: props.selectorLabel }),
|
|
404
|
+
(0, import_jsx_runtime3.jsx)("span", {
|
|
405
|
+
className: ChromeMcpCard_default.selectorWrap,
|
|
406
|
+
children: (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives.Menu, {
|
|
407
|
+
open: props.menuOpen,
|
|
408
|
+
onClose: () => {
|
|
409
|
+
props.setMenuOpen(false);
|
|
410
|
+
},
|
|
411
|
+
items: options,
|
|
412
|
+
selectedId: options.some((option) => option.id === selectedId) ? selectedId : "",
|
|
413
|
+
onSelect: (id) => {
|
|
414
|
+
props.setMenuOpen(false);
|
|
415
|
+
props.select(id);
|
|
416
|
+
},
|
|
417
|
+
align: "end",
|
|
418
|
+
portal: true,
|
|
419
|
+
anchor: (0, import_jsx_runtime3.jsxs)("button", {
|
|
420
|
+
type: "button",
|
|
421
|
+
className: ChromeMcpCard_default.selector,
|
|
422
|
+
"aria-haspopup": "menu",
|
|
423
|
+
"aria-expanded": props.menuOpen,
|
|
424
|
+
disabled: !writable || busy,
|
|
425
|
+
title: props.selectedTitle,
|
|
426
|
+
onClick: () => {
|
|
427
|
+
props.setMenuOpen(!props.menuOpen);
|
|
428
|
+
},
|
|
429
|
+
children: [
|
|
430
|
+
// The reference card hands the pill a bare label;
|
|
431
|
+
// the path needs its own truncating span.
|
|
432
|
+
(0, import_jsx_runtime3.jsx)("span", { className: ChromeMcpCard_default.selectorLabel, children: props.selectedLabel }),
|
|
433
|
+
(0, import_jsx_runtime3.jsx)("svg", {
|
|
434
|
+
width: 14,
|
|
435
|
+
height: 14,
|
|
436
|
+
className: ChromeMcpCard_default.pillChevron,
|
|
437
|
+
viewBox: "0 0 14 14",
|
|
438
|
+
fill: "none",
|
|
439
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
440
|
+
children: (0, import_jsx_runtime3.jsx)("path", { d: CHEVRON_PATH, fill: "currentColor" })
|
|
441
|
+
})
|
|
442
|
+
]
|
|
443
|
+
})
|
|
444
|
+
})
|
|
445
|
+
})
|
|
446
|
+
]
|
|
447
|
+
}),
|
|
448
|
+
(0, import_jsx_runtime3.jsx)("div", { className: ChromeMcpCard_default.fieldDesc, children: props.hint })
|
|
449
|
+
]
|
|
450
|
+
}),
|
|
451
|
+
// The saved rows: two fields per row, folded behind the disclosure,
|
|
452
|
+
// with the fetch action sitting inside the body.
|
|
453
|
+
(0, import_jsx_runtime3.jsx)(RowList, {
|
|
454
|
+
values: rows,
|
|
455
|
+
labels: [props.idLabel, props.nameLabel],
|
|
456
|
+
placeholders: [props.idPlaceholder, props.namePlaceholder],
|
|
457
|
+
writable,
|
|
458
|
+
busy,
|
|
459
|
+
save: saveEntries,
|
|
460
|
+
renderAction: renderActions,
|
|
461
|
+
emptyText: props.emptyText,
|
|
462
|
+
rowsLabel: props.rowsLabel,
|
|
463
|
+
addLabel: props.addLabel,
|
|
464
|
+
removeLabel: props.removeLabel,
|
|
465
|
+
disclosure: true
|
|
466
|
+
}),
|
|
467
|
+
props.statusLine === void 0 || props.statusLine === null ? null : props.statusLine
|
|
468
|
+
]
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// src/client/card/dialog.ts
|
|
473
|
+
var react2 = __toESM(require("react"), 1);
|
|
474
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
475
|
+
function CandidateDialog(props) {
|
|
476
|
+
var t = props.t;
|
|
477
|
+
var candidates = props.candidates;
|
|
478
|
+
var savedIds = props.savedIds;
|
|
479
|
+
var savedSet = new Set(savedIds);
|
|
480
|
+
var initialChecked = candidates.filter((candidate) => savedSet.has(candidate.id)).map((candidate) => candidate.id);
|
|
481
|
+
var servedKey = JSON.stringify(candidates) + "|" + JSON.stringify(savedIds);
|
|
482
|
+
var draftState = react2.useState({ key: "", query: "", checked: [] });
|
|
483
|
+
var draft = draftState[0];
|
|
484
|
+
var setDraft = draftState[1];
|
|
485
|
+
var dirty = draft.key !== servedKey;
|
|
486
|
+
var query = dirty ? "" : draft.query;
|
|
487
|
+
var checked = dirty ? initialChecked : draft.checked;
|
|
488
|
+
var checkedSet = new Set(checked);
|
|
489
|
+
var setQuery = (value) => {
|
|
490
|
+
setDraft({ key: servedKey, query: value, checked: Array.from(checkedSet) });
|
|
491
|
+
};
|
|
492
|
+
var setChecked = (make) => {
|
|
493
|
+
setDraft({ key: servedKey, query, checked: make(checked) });
|
|
494
|
+
};
|
|
495
|
+
var needle = query.trim().toLowerCase();
|
|
496
|
+
var shown = needle === "" ? candidates : candidates.filter((candidate) => candidate.id.toLowerCase().indexOf(needle) !== -1);
|
|
497
|
+
var addable = shown.filter((candidate) => !savedSet.has(candidate.id)).map((candidate) => candidate.id);
|
|
498
|
+
var allChecked = addable.length > 0 && addable.every((id) => checkedSet.has(id));
|
|
499
|
+
var toggle = (id, value) => {
|
|
500
|
+
setChecked((current) => value ? current.concat([id]) : current.filter((entry) => entry !== id));
|
|
501
|
+
};
|
|
502
|
+
return (0, import_jsx_runtime4.jsxs)("div", {
|
|
503
|
+
className: ChromeMcpCard_default.fetchDialog,
|
|
504
|
+
role: "dialog",
|
|
505
|
+
"aria-modal": "true",
|
|
506
|
+
"aria-label": props.title,
|
|
507
|
+
children: [
|
|
508
|
+
(0, import_jsx_runtime4.jsxs)("div", {
|
|
509
|
+
className: ChromeMcpCard_default.dialogContent,
|
|
510
|
+
children: [
|
|
511
|
+
(0, import_jsx_runtime4.jsxs)("div", {
|
|
512
|
+
className: ChromeMcpCard_default.dialogHeader,
|
|
513
|
+
children: [
|
|
514
|
+
(0, import_jsx_runtime4.jsx)("h2", { className: ChromeMcpCard_default.dialogTitle, children: props.title }),
|
|
515
|
+
(0, import_jsx_runtime4.jsx)("button", {
|
|
516
|
+
type: "button",
|
|
517
|
+
className: ChromeMcpCard_default.dialogClose,
|
|
518
|
+
"aria-label": t("close"),
|
|
519
|
+
onClick: () => props.onClose(),
|
|
520
|
+
children: (0, import_jsx_runtime4.jsx)("svg", {
|
|
521
|
+
width: 14,
|
|
522
|
+
height: 14,
|
|
523
|
+
viewBox: "0 0 16 16",
|
|
524
|
+
fill: "none",
|
|
525
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
526
|
+
"aria-hidden": "true",
|
|
527
|
+
children: CLOSE_PATHS.map((d, at) => (0, import_jsx_runtime4.jsx)("path", { d, fill: "currentColor" }, at))
|
|
528
|
+
})
|
|
529
|
+
})
|
|
530
|
+
]
|
|
531
|
+
}),
|
|
532
|
+
(0, import_jsx_runtime4.jsx)("p", { className: ChromeMcpCard_default.dialogDescription, children: props.description }),
|
|
533
|
+
(0, import_jsx_runtime4.jsxs)("div", {
|
|
534
|
+
className: ChromeMcpCard_default.dialogBody,
|
|
535
|
+
children: [
|
|
536
|
+
(0, import_jsx_runtime4.jsxs)("div", {
|
|
537
|
+
className: ChromeMcpCard_default.candidateToolbar,
|
|
538
|
+
children: [
|
|
539
|
+
(0, import_jsx_runtime4.jsx)("input", {
|
|
540
|
+
className: ChromeMcpCard_default.candidateSearch,
|
|
541
|
+
type: "search",
|
|
542
|
+
placeholder: props.searchPlaceholder,
|
|
543
|
+
"aria-label": props.searchPlaceholder,
|
|
544
|
+
value: query,
|
|
545
|
+
onChange: (event) => {
|
|
546
|
+
setQuery(event && event.target ? String(event.target.value ?? "") : "");
|
|
547
|
+
}
|
|
548
|
+
}),
|
|
549
|
+
(0, import_jsx_runtime4.jsx)("button", {
|
|
550
|
+
type: "button",
|
|
551
|
+
className: ChromeMcpCard_default.linkButton,
|
|
552
|
+
onClick: () => {
|
|
553
|
+
if (allChecked) setChecked(() => []);
|
|
554
|
+
else setChecked((current) => Array.from(new Set(current.concat(addable))));
|
|
555
|
+
},
|
|
556
|
+
children: t("selectAll")
|
|
557
|
+
})
|
|
558
|
+
]
|
|
559
|
+
}),
|
|
560
|
+
shown.length === 0 ? (0, import_jsx_runtime4.jsx)("p", { className: ChromeMcpCard_default.empty, children: t("dialogEmpty") }) : (0, import_jsx_runtime4.jsx)("ul", {
|
|
561
|
+
className: ChromeMcpCard_default.candidateList,
|
|
562
|
+
children: shown.map((candidate) => {
|
|
563
|
+
var isSaved = savedSet.has(candidate.id);
|
|
564
|
+
var note = candidate.note !== "" ? candidate.note : isSaved ? t("candidateSaved") : "";
|
|
565
|
+
return (0, import_jsx_runtime4.jsx)("li", {
|
|
566
|
+
className: ChromeMcpCard_default.candidate,
|
|
567
|
+
children: (0, import_jsx_runtime4.jsxs)("label", {
|
|
568
|
+
className: ChromeMcpCard_default.candidateLabel,
|
|
569
|
+
children: [
|
|
570
|
+
(0, import_jsx_runtime4.jsx)("input", {
|
|
571
|
+
type: "checkbox",
|
|
572
|
+
checked: isSaved ? true : checkedSet.has(candidate.id),
|
|
573
|
+
disabled: isSaved,
|
|
574
|
+
onChange: (event) => {
|
|
575
|
+
toggle(candidate.id, !!(event && event.target && event.target.checked));
|
|
576
|
+
}
|
|
577
|
+
}),
|
|
578
|
+
(0, import_jsx_runtime4.jsx)("span", { className: ChromeMcpCard_default.candidateId, children: candidate.id }),
|
|
579
|
+
note === "" ? null : (0, import_jsx_runtime4.jsx)("span", { className: ChromeMcpCard_default.candidateNote, children: note })
|
|
580
|
+
]
|
|
581
|
+
})
|
|
582
|
+
}, candidate.id);
|
|
583
|
+
})
|
|
584
|
+
})
|
|
585
|
+
]
|
|
586
|
+
}),
|
|
587
|
+
(0, import_jsx_runtime4.jsxs)("div", {
|
|
588
|
+
className: ChromeMcpCard_default.dialogFooter,
|
|
589
|
+
children: [
|
|
590
|
+
(0, import_jsx_runtime4.jsx)("button", {
|
|
591
|
+
type: "button",
|
|
592
|
+
className: ChromeMcpCard_default.dialogButton,
|
|
593
|
+
onClick: () => props.onClose(),
|
|
594
|
+
children: t("cancel")
|
|
595
|
+
}),
|
|
596
|
+
(0, import_jsx_runtime4.jsx)("button", {
|
|
597
|
+
type: "button",
|
|
598
|
+
className: ChromeMcpCard_default.dialogButton + " " + ChromeMcpCard_default.dialogButtonPrimary,
|
|
599
|
+
onClick: () => props.onAdd(Array.from(checkedSet)),
|
|
600
|
+
children: t("addSelected")
|
|
601
|
+
})
|
|
602
|
+
]
|
|
603
|
+
})
|
|
604
|
+
]
|
|
605
|
+
})
|
|
606
|
+
]
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// src/client/card/run-actions.ts
|
|
611
|
+
var react3 = __toESM(require("react"), 1);
|
|
612
|
+
var PROBED_AGE_TICK_MS = 1e4;
|
|
613
|
+
function useRunActions(deps) {
|
|
614
|
+
const runningState = react3.useState(false);
|
|
615
|
+
const isRunning = runningState[0];
|
|
616
|
+
const setRunning = runningState[1];
|
|
617
|
+
const windowsRunningState = react3.useState(false);
|
|
618
|
+
const isWindowsRunning = windowsRunningState[0];
|
|
619
|
+
const setWindowsRunning = windowsRunningState[1];
|
|
620
|
+
const dialogRef = react3.useState(null);
|
|
621
|
+
const dialog = dialogRef[0];
|
|
622
|
+
const setDialog = dialogRef[1];
|
|
623
|
+
const probedState = react3.useState(0);
|
|
624
|
+
const lastProbedAt = probedState[0];
|
|
625
|
+
const setLastProbedAt = probedState[1];
|
|
626
|
+
const clockState = react3.useState(() => Date.now());
|
|
627
|
+
const clock = clockState[0];
|
|
628
|
+
const setClock = clockState[1];
|
|
629
|
+
react3.useEffect(() => {
|
|
630
|
+
const tick = setInterval(() => setClock(Date.now()), PROBED_AGE_TICK_MS);
|
|
631
|
+
return () => clearInterval(tick);
|
|
632
|
+
}, []);
|
|
633
|
+
const runFetch = () => {
|
|
634
|
+
setRunning(true);
|
|
635
|
+
let promise;
|
|
636
|
+
try {
|
|
637
|
+
promise = deps.refreshExecutables();
|
|
638
|
+
} catch (caught) {
|
|
639
|
+
promise = Promise.reject(caught);
|
|
640
|
+
}
|
|
641
|
+
Promise.resolve(promise).then(
|
|
642
|
+
async (statuses) => {
|
|
643
|
+
setRunning(false);
|
|
644
|
+
const settled = Date.now();
|
|
645
|
+
setLastProbedAt(settled);
|
|
646
|
+
setClock(settled);
|
|
647
|
+
if (!Array.isArray(statuses) || statuses.length === 0) return;
|
|
648
|
+
const candidates = statuses.map((status) => ({
|
|
649
|
+
id: status.path,
|
|
650
|
+
note: status.version !== "" ? status.version : status.error,
|
|
651
|
+
broken: status.version === "" && status.error !== ""
|
|
652
|
+
}));
|
|
653
|
+
setDialog({ candidates });
|
|
654
|
+
},
|
|
655
|
+
() => {
|
|
656
|
+
setRunning(false);
|
|
657
|
+
}
|
|
658
|
+
);
|
|
659
|
+
};
|
|
660
|
+
const runWindowsChrome = () => {
|
|
661
|
+
setWindowsRunning(true);
|
|
662
|
+
let promise;
|
|
663
|
+
try {
|
|
664
|
+
promise = deps.openWindowsChrome();
|
|
665
|
+
} catch (caught) {
|
|
666
|
+
promise = Promise.reject(caught);
|
|
667
|
+
}
|
|
668
|
+
Promise.resolve(promise).then(
|
|
669
|
+
() => {
|
|
670
|
+
setWindowsRunning(false);
|
|
671
|
+
},
|
|
672
|
+
() => {
|
|
673
|
+
setWindowsRunning(false);
|
|
674
|
+
}
|
|
675
|
+
);
|
|
676
|
+
};
|
|
677
|
+
const addFromDialog = (ids) => {
|
|
678
|
+
setDialog(null);
|
|
679
|
+
if (typeof deps.addExecutables === "function") Promise.resolve(deps.addExecutables(ids)).catch(() => {
|
|
680
|
+
});
|
|
681
|
+
};
|
|
682
|
+
return {
|
|
683
|
+
running: isRunning,
|
|
684
|
+
windowsRunning: isWindowsRunning,
|
|
685
|
+
dialog,
|
|
686
|
+
runFetch,
|
|
687
|
+
runWindowsChrome,
|
|
688
|
+
addFromDialog,
|
|
689
|
+
closeDialog: () => setDialog(null),
|
|
690
|
+
lastProbedAt,
|
|
691
|
+
clock
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
// src/client/card/toggle.ts
|
|
696
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
697
|
+
var import_dsh_client_ui_primitives2 = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
698
|
+
function LogOutputToggle(props) {
|
|
699
|
+
var t = props.t;
|
|
700
|
+
return (0, import_jsx_runtime5.jsxs)("div", {
|
|
701
|
+
className: ChromeMcpCard_default.catalog,
|
|
702
|
+
children: [
|
|
703
|
+
(0, import_jsx_runtime5.jsxs)("div", {
|
|
704
|
+
className: ChromeMcpCard_default.field,
|
|
705
|
+
children: [
|
|
706
|
+
(0, import_jsx_runtime5.jsxs)("div", {
|
|
707
|
+
className: ChromeMcpCard_default.fieldRow,
|
|
708
|
+
children: [
|
|
709
|
+
(0, import_jsx_runtime5.jsx)("span", { className: ChromeMcpCard_default.fieldTitle, children: t("reduceLabel") }),
|
|
710
|
+
(0, import_jsx_runtime5.jsx)("span", {
|
|
711
|
+
className: ChromeMcpCard_default.toggleLabel,
|
|
712
|
+
title: t("reduceTitle"),
|
|
713
|
+
children: (0, import_jsx_runtime5.jsx)(import_dsh_client_ui_primitives2.Switch, {
|
|
714
|
+
checked: props.checked,
|
|
715
|
+
label: t("reduceLabel"),
|
|
716
|
+
disabled: props.disabled,
|
|
717
|
+
onChange: () => {
|
|
718
|
+
if (typeof props.onToggle === "function") props.onToggle();
|
|
719
|
+
}
|
|
720
|
+
})
|
|
721
|
+
})
|
|
722
|
+
]
|
|
723
|
+
}),
|
|
724
|
+
(0, import_jsx_runtime5.jsx)("div", { className: ChromeMcpCard_default.fieldDesc, children: t("reduceHint") })
|
|
725
|
+
]
|
|
726
|
+
})
|
|
727
|
+
]
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// src/client/catalog.ts
|
|
732
|
+
var ID_MAX_LENGTH = 1024;
|
|
733
|
+
var PATH_PATTERN = /^[^\x00-\x1f\x7f]{1,1024}$/u;
|
|
734
|
+
var NAME_MAX_LENGTH = 200;
|
|
735
|
+
var ENTRY_CAP = 50;
|
|
736
|
+
function isValidEntryId(id) {
|
|
737
|
+
if (id === "" || id.length > ID_MAX_LENGTH) return false;
|
|
738
|
+
return PATH_PATTERN.test(id);
|
|
739
|
+
}
|
|
740
|
+
function entryLabel(entry) {
|
|
741
|
+
const name = entry.name.trim();
|
|
742
|
+
return name === "" ? entry.id : name;
|
|
743
|
+
}
|
|
744
|
+
function normalizeEntries(value) {
|
|
745
|
+
if (!Array.isArray(value)) return [];
|
|
746
|
+
const out = [];
|
|
747
|
+
const seen = /* @__PURE__ */ new Set();
|
|
748
|
+
for (const item of value) {
|
|
749
|
+
if (out.length === ENTRY_CAP) break;
|
|
750
|
+
if (item === null || typeof item !== "object" || Array.isArray(item)) continue;
|
|
751
|
+
const raw = item;
|
|
752
|
+
const id = typeof raw.id === "string" ? raw.id.trim() : "";
|
|
753
|
+
if (!isValidEntryId(id) || seen.has(id)) continue;
|
|
754
|
+
seen.add(id);
|
|
755
|
+
const name = typeof raw.name === "string" ? raw.name.trim() : "";
|
|
756
|
+
out.push({ id, name: name.slice(0, NAME_MAX_LENGTH) });
|
|
757
|
+
}
|
|
758
|
+
return out;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// src/client/card/view.ts
|
|
762
|
+
function labelOf(entries, id) {
|
|
763
|
+
const row = entries.find((entry) => entry.id === id);
|
|
764
|
+
return row === void 0 ? id : entryLabel(row);
|
|
765
|
+
}
|
|
766
|
+
function describeCard(state) {
|
|
767
|
+
const error = state.lastError || state.actionError || "";
|
|
768
|
+
const servedMode = typeof state.stderrMode === "string" ? state.stderrMode : "";
|
|
769
|
+
const stderrEffective = servedMode !== "" ? servedMode : state.rowStderr === "console" ? "console" : "log";
|
|
770
|
+
const executableEntries = normalizeEntries(state.executables);
|
|
771
|
+
const statusList = Array.isArray(state.executableStatus) ? state.executableStatus : [];
|
|
772
|
+
const optionIds = executableEntries.map((entry) => entry.id);
|
|
773
|
+
const effectivePath = typeof state.effectiveChromePath === "string" ? state.effectiveChromePath : "";
|
|
774
|
+
if (effectivePath !== "" && optionIds.indexOf(effectivePath) === -1) optionIds.push(effectivePath);
|
|
775
|
+
const executableOptions = optionIds.map((id) => ({ id, label: labelOf(executableEntries, id) }));
|
|
776
|
+
const brokenIds = statusList.filter((status) => status.version === "" && status.error !== "").map((status) => status.path);
|
|
777
|
+
return {
|
|
778
|
+
error,
|
|
779
|
+
reduceChecked: stderrEffective === "log",
|
|
780
|
+
executableEntries,
|
|
781
|
+
executableOptions,
|
|
782
|
+
brokenIds,
|
|
783
|
+
effectivePath,
|
|
784
|
+
version: typeof state.chromeVersion === "string" ? state.chromeVersion : "",
|
|
785
|
+
missing: state.chromeMissing === true && error === "",
|
|
786
|
+
dotIsError: error !== "",
|
|
787
|
+
wslWindowsExecutable: state.wslWindowsExecutable === true,
|
|
788
|
+
windowsChrome: readWindowsChrome(state.windowsChromeStatus),
|
|
789
|
+
connectMode: state.carriesConnectionMode === true
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
function executableLabel(view, id) {
|
|
793
|
+
return labelOf(view.executableEntries, id);
|
|
794
|
+
}
|
|
795
|
+
function readWindowsChrome(value) {
|
|
796
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return { state: "off", port: 0, error: "" };
|
|
797
|
+
const record = value;
|
|
798
|
+
const known = ["off", "launching", "connected", "unreachable", "not-found", "launch-failed", "not-applicable"];
|
|
799
|
+
const state = typeof record.state === "string" && known.indexOf(record.state) !== -1 ? record.state : "off";
|
|
800
|
+
const port = typeof record.port === "number" && Number.isFinite(record.port) ? Math.trunc(record.port) : 0;
|
|
801
|
+
const error = typeof record.error === "string" ? record.error : "";
|
|
802
|
+
return { state, port, error };
|
|
803
|
+
}
|
|
804
|
+
function windowsChromeButton(view) {
|
|
805
|
+
const status = view.windowsChrome;
|
|
806
|
+
if (status.state === "launching") return { labelKey: "openWindowsChromeBusy", titleKey: "openWindowsChromeTitle", disabled: true, busy: true };
|
|
807
|
+
if (status.state === "not-applicable") return { labelKey: "openWindowsChrome", titleKey: "windowsChromeNotWsl", disabled: true, busy: false };
|
|
808
|
+
if (status.state === "not-found") return { labelKey: "openWindowsChrome", titleKey: "windowsChromeNotFound", disabled: true, busy: false };
|
|
809
|
+
if (status.state === "unreachable") return { labelKey: "openWindowsChrome", titleKey: "windowsChromeUnreachable", disabled: true, busy: false };
|
|
810
|
+
return { labelKey: "openWindowsChrome", titleKey: "openWindowsChromeTitle", disabled: false, busy: false };
|
|
811
|
+
}
|
|
812
|
+
function isBroken(view, id) {
|
|
813
|
+
return view.brokenIds.indexOf(id) !== -1;
|
|
814
|
+
}
|
|
815
|
+
function probeAge(since, now, t) {
|
|
816
|
+
const seconds = Math.max(0, Math.floor((now - since) / 1e3));
|
|
817
|
+
if (seconds < 10) return t("probeJustNow");
|
|
818
|
+
if (seconds < 60) return String(seconds) + " " + t("probeSecondsAgo");
|
|
819
|
+
const minutes = Math.floor(seconds / 60);
|
|
820
|
+
if (minutes < 60) return String(minutes) + " " + t("probeMinutesAgo");
|
|
821
|
+
return String(Math.floor(minutes / 60)) + " " + t("probeHoursAgo");
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
// src/client/card/index.ts
|
|
825
|
+
function optionLabel(view, id, t) {
|
|
826
|
+
const label = executableLabel(view, id);
|
|
827
|
+
return isBroken(view, id) ? label + " (" + t("unavailableSuffix") + ")" : label;
|
|
828
|
+
}
|
|
829
|
+
function ChromeMcpCard(props) {
|
|
830
|
+
const t = props.t;
|
|
831
|
+
const state = props.useChromeMcpCard((snapshot) => snapshot);
|
|
832
|
+
const openState = react4.useState(false);
|
|
833
|
+
const open = openState[0];
|
|
834
|
+
const setOpen = openState[1];
|
|
835
|
+
const menuState = react4.useState(false);
|
|
836
|
+
const menuOpen = menuState[0];
|
|
837
|
+
const setMenuOpen = menuState[1];
|
|
838
|
+
const bodyId = react4.useId();
|
|
839
|
+
const runs = useRunActions({
|
|
840
|
+
refreshExecutables: props.refreshExecutables,
|
|
841
|
+
openWindowsChrome: props.openWindowsChrome,
|
|
842
|
+
addExecutables: props.addExecutables
|
|
843
|
+
});
|
|
844
|
+
if (!state.available) return null;
|
|
845
|
+
const view = describeCard(state);
|
|
846
|
+
const rows = view.executableEntries;
|
|
847
|
+
const savedIds = rows.map((entry) => entry.id);
|
|
848
|
+
const options = view.executableOptions.map((option) => ({ id: option.id, label: optionLabel(view, option.id, t) }));
|
|
849
|
+
const dotLabel = view.error !== "" ? view.error : t("chromeMissing");
|
|
850
|
+
const dotAria = view.dotIsError || view.missing ? t("statusError") : t("statusOk");
|
|
851
|
+
const windowsButton = windowsChromeButton(view);
|
|
852
|
+
const windowsDisabled = windowsButton.disabled || runs.windowsRunning || !state.writable;
|
|
853
|
+
const windowsBusy = windowsButton.busy || runs.windowsRunning;
|
|
854
|
+
const connectLabel = view.windowsChrome.port > 0 ? t("connectModeLabel") + " (http://127.0.0.1:" + String(view.windowsChrome.port) + ")" : t("connectModeLabel");
|
|
855
|
+
const selectedLabel = view.connectMode ? connectLabel : view.effectivePath === "" ? t("executableNone") : executableLabel(view, view.effectivePath);
|
|
856
|
+
const statusLine = runs.running ? (0, import_jsx_runtime6.jsx)("p", { className: ChromeMcpCard_default.result, role: "status", children: t("probing") }) : view.version === "" ? null : (0, import_jsx_runtime6.jsxs)("p", {
|
|
857
|
+
className: ChromeMcpCard_default.result,
|
|
858
|
+
role: "status",
|
|
859
|
+
children: [
|
|
860
|
+
t("versionLabel"),
|
|
861
|
+
(0, import_jsx_runtime6.jsx)("span", { className: ChromeMcpCard_default.resultValue, children: view.version }),
|
|
862
|
+
runs.lastProbedAt === 0 ? null : (0, import_jsx_runtime6.jsx)("span", { className: ChromeMcpCard_default.resultAge, children: probeAge(runs.lastProbedAt, runs.clock, t) })
|
|
863
|
+
]
|
|
864
|
+
});
|
|
865
|
+
return (0, import_jsx_runtime6.jsx)("li", {
|
|
866
|
+
className: ChromeMcpCard_default.card + (open ? " " + ChromeMcpCard_default.cardOpen : ""),
|
|
867
|
+
children: [
|
|
868
|
+
(0, import_jsx_runtime6.jsxs)("button", {
|
|
869
|
+
type: "button",
|
|
870
|
+
className: ChromeMcpCard_default.header,
|
|
871
|
+
"aria-expanded": open,
|
|
872
|
+
"aria-label": t(open ? "collapse" : "expand") + ": " + t("title"),
|
|
873
|
+
"aria-controls": bodyId,
|
|
874
|
+
onClick: () => {
|
|
875
|
+
setOpen(!open);
|
|
876
|
+
if (!open && typeof props.refresh === "function") void props.refresh();
|
|
877
|
+
},
|
|
878
|
+
children: [
|
|
879
|
+
(0, import_jsx_runtime6.jsxs)("span", {
|
|
880
|
+
className: ChromeMcpCard_default.headText,
|
|
881
|
+
children: [
|
|
882
|
+
(0, import_jsx_runtime6.jsxs)("div", {
|
|
883
|
+
className: ChromeMcpCard_default.nameRow,
|
|
884
|
+
children: [
|
|
885
|
+
(0, import_jsx_runtime6.jsx)("span", { className: ChromeMcpCard_default.name, children: t("title") }),
|
|
886
|
+
view.dotIsError || view.missing ? (0, import_jsx_runtime6.jsx)("span", {
|
|
887
|
+
className: ChromeMcpCard_default.dot + " " + ChromeMcpCard_default.dotError,
|
|
888
|
+
role: "img",
|
|
889
|
+
"aria-label": dotAria,
|
|
890
|
+
title: dotLabel
|
|
891
|
+
}) : null
|
|
892
|
+
]
|
|
893
|
+
}),
|
|
894
|
+
(0, import_jsx_runtime6.jsx)("span", { className: ChromeMcpCard_default.desc, children: t("description") })
|
|
895
|
+
]
|
|
896
|
+
}),
|
|
897
|
+
(0, import_jsx_runtime6.jsx)("svg", {
|
|
898
|
+
width: 14,
|
|
899
|
+
height: 14,
|
|
900
|
+
className: ChromeMcpCard_default.chevron + (open ? " " + ChromeMcpCard_default.chevronOpen : ""),
|
|
901
|
+
viewBox: "0 0 14 14",
|
|
902
|
+
fill: "none",
|
|
903
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
904
|
+
children: (0, import_jsx_runtime6.jsx)("path", { d: CHEVRON_PATH, fill: "currentColor" })
|
|
905
|
+
})
|
|
906
|
+
]
|
|
907
|
+
}),
|
|
908
|
+
open ? (0, import_jsx_runtime6.jsxs)("div", {
|
|
909
|
+
id: bodyId,
|
|
910
|
+
className: ChromeMcpCard_default.body,
|
|
911
|
+
children: [
|
|
912
|
+
!state.writable ? (0, import_jsx_runtime6.jsx)("p", { className: ChromeMcpCard_default.readOnly, role: "status", children: t("readOnly") }) : null,
|
|
913
|
+
(0, import_jsx_runtime6.jsx)(CatalogPicker, {
|
|
914
|
+
t,
|
|
915
|
+
entries: rows,
|
|
916
|
+
options,
|
|
917
|
+
idPlaceholder: t("executableIdPlaceholder"),
|
|
918
|
+
namePlaceholder: t("namePlaceholder"),
|
|
919
|
+
selectedId: view.effectivePath,
|
|
920
|
+
selectedLabel,
|
|
921
|
+
selectedTitle: view.connectMode ? connectLabel : view.effectivePath !== "" ? view.effectivePath : selectedLabel,
|
|
922
|
+
writable: state.writable,
|
|
923
|
+
busy: runs.running,
|
|
924
|
+
menuOpen,
|
|
925
|
+
setMenuOpen,
|
|
926
|
+
connectMode: view.connectMode,
|
|
927
|
+
selectorLabel: t("executableLabel"),
|
|
928
|
+
hint: view.connectMode ? t("connectModeHint") : t("executableHint"),
|
|
929
|
+
windowsLabel: windowsBusy ? t("openWindowsChromeBusy") : t(windowsButton.labelKey),
|
|
930
|
+
windowsTitle: t(windowsButton.titleKey),
|
|
931
|
+
windowsDisabled,
|
|
932
|
+
windowsOnOpen: runs.runWindowsChrome,
|
|
933
|
+
rowsLabel: t("executableRows"),
|
|
934
|
+
emptyText: t("executableEmpty"),
|
|
935
|
+
idLabel: t("executableIdLabel"),
|
|
936
|
+
nameLabel: t("executableNameLabel"),
|
|
937
|
+
removeLabel: t("removeExecutable"),
|
|
938
|
+
addLabel: t("addExecutable"),
|
|
939
|
+
refreshLabel: t("fetchExecutables"),
|
|
940
|
+
busyLabel: t("fetching"),
|
|
941
|
+
select: (id) => {
|
|
942
|
+
if (typeof props.selectExecutable === "function") void props.selectExecutable(id);
|
|
943
|
+
},
|
|
944
|
+
saveEntries: (entries) => props.saveExecutables(entries),
|
|
945
|
+
refresh: runs.runFetch,
|
|
946
|
+
statusLine
|
|
947
|
+
}),
|
|
948
|
+
// The one state no control of this card can express: the
|
|
949
|
+
// host runs under WSL and what is selected is a Windows
|
|
950
|
+
// executable — which the bridge cannot launch, however
|
|
951
|
+
// healthy its `--version` answer looks. Nothing has failed
|
|
952
|
+
// yet, so this is a notice (static copy, shown only while
|
|
953
|
+
// the host reports the state), not a third error surface:
|
|
954
|
+
// the required configuration differs.
|
|
955
|
+
view.wslWindowsExecutable ? (0, import_jsx_runtime6.jsxs)("div", {
|
|
956
|
+
className: ChromeMcpCard_default.notice,
|
|
957
|
+
role: "note",
|
|
958
|
+
children: [
|
|
959
|
+
(0, import_jsx_runtime6.jsx)("div", { className: ChromeMcpCard_default.noticeTitle, children: t("wslWindowsTitle") }),
|
|
960
|
+
(0, import_jsx_runtime6.jsx)("div", { className: ChromeMcpCard_default.noticeBody, children: t("wslWindowsNote") })
|
|
961
|
+
]
|
|
962
|
+
}) : null,
|
|
963
|
+
(0, import_jsx_runtime6.jsx)(ExtraFlagsEditor, {
|
|
964
|
+
t,
|
|
965
|
+
flags: Array.isArray(state.extraFlags) ? state.extraFlags : [],
|
|
966
|
+
defaultFlags: Array.isArray(state.defaultExtraFlags) ? state.defaultExtraFlags : [],
|
|
967
|
+
wslFlags: Array.isArray(state.wslExtraFlags) ? state.wslExtraFlags : [],
|
|
968
|
+
writable: state.writable,
|
|
969
|
+
saveFlags: props.saveFlags
|
|
970
|
+
}),
|
|
971
|
+
(0, import_jsx_runtime6.jsx)(LogOutputToggle, {
|
|
972
|
+
t,
|
|
973
|
+
checked: view.reduceChecked,
|
|
974
|
+
disabled: !state.writable,
|
|
975
|
+
onToggle: props.toggleStderr
|
|
976
|
+
}),
|
|
977
|
+
state.lastError ? (0, import_jsx_runtime6.jsx)("p", { className: ChromeMcpCard_default.error, role: "status", children: state.lastError }) : null,
|
|
978
|
+
state.actionError ? (0, import_jsx_runtime6.jsx)("p", { className: ChromeMcpCard_default.error, role: "status", children: state.actionError }) : null,
|
|
979
|
+
runs.dialog !== null ? (0, import_jsx_runtime6.jsx)(CandidateDialog, {
|
|
980
|
+
t,
|
|
981
|
+
candidates: runs.dialog.candidates,
|
|
982
|
+
savedIds,
|
|
983
|
+
title: t("dialogExecutablesTitle"),
|
|
984
|
+
description: t("dialogExecutablesDescription"),
|
|
985
|
+
searchPlaceholder: t("searchExecutables"),
|
|
986
|
+
onAdd: runs.addFromDialog,
|
|
987
|
+
onClose: runs.closeDialog
|
|
988
|
+
}) : null
|
|
989
|
+
]
|
|
990
|
+
}) : null
|
|
991
|
+
]
|
|
992
|
+
});
|
|
993
|
+
}
|
|
14
994
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
* Registers one card into the shared `settings.plugin.item` slot
|
|
19
|
-
* (Settings → Plugins → Plugin configuration), keyed by the
|
|
20
|
-
* `chrome-mcp` settings namespace the host half serves. The card
|
|
21
|
-
* offers, per the product spec:
|
|
22
|
-
* - error reporting: chrome executable / connection errors surfaced
|
|
23
|
-
* from the host's `lastError`, reported by a single UI element: the
|
|
24
|
-
* red status dot in the header, whose `title` carries the full
|
|
25
|
-
* message. A served `chromeMissing` flag keeps that dot red even
|
|
26
|
-
* when the captured error was cleared (its title then falls back to
|
|
27
|
-
* the not-found label). No body paragraph, no extra badge.
|
|
28
|
-
* - an extra flags editor (host field `extraFlags`, one flag per
|
|
29
|
-
* line, default `--no-usage-statistics`, `--no-performance-crux`), writing the persisted
|
|
30
|
-
* field via scope.set (host fiber.update relaunches the bridge),
|
|
31
|
-
* - the Chrome executable version discovered by the last check, and
|
|
32
|
-
* - the full configuration-guide message with a link to
|
|
33
|
-
* https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/configuration.md
|
|
34
|
-
* The card is collapsible like the stock plugin cards: closed by
|
|
35
|
-
* default, with the header as its disclosure button.
|
|
36
|
-
*/
|
|
37
|
-
/** Settings namespace served by the host half; also the slot key. */
|
|
38
|
-
var NS = "chrome-mcp";
|
|
39
|
-
/** Locale dictionary namespace. */
|
|
40
|
-
var LOCALE_NS = "chromeMcp";
|
|
41
|
-
/** Required services (cordis fiber inject). */
|
|
42
|
-
var inject = ["slots", "locale", "settingsScope"];
|
|
995
|
+
// src/client/controller/budget.ts
|
|
996
|
+
var CHECK_POLL_TICK_MS = 700;
|
|
997
|
+
var CHECK_POLL_TIMEOUT_MS = 2e4;
|
|
43
998
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/configuration.md";
|
|
999
|
+
// src/client/controller/controller.ts
|
|
1000
|
+
var import_dsh_client_store = require("@deepseek-ai/dsh-client-store");
|
|
47
1001
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
"." + CSS.name + "{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}" +
|
|
84
|
-
"." + CSS.desc + "{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}" +
|
|
85
|
-
"." + CSS.chevron + "{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}" +
|
|
86
|
-
"." + CSS.chevronOpen + "{transform:rotate(180deg)}" +
|
|
87
|
-
"." + CSS.body + "{border-top:.5px solid var(--dsw-alias-border-l2);display:flex;flex-direction:column;gap:10px;margin:0 16px;padding:12px 0}" +
|
|
88
|
-
"." + CSS.field + "{align-items:flex-start;gap:8px;display:flex}" +
|
|
89
|
-
"." + CSS.rowText + "{flex-direction:column;flex:1;gap:4px;min-width:0;padding-right:48px;display:flex}" +
|
|
90
|
-
"." + CSS.fieldTitle + "{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}" +
|
|
91
|
-
"." + CSS.fieldDesc + "{color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:400;line-height:18px}" +
|
|
92
|
-
"." + CSS.row + "{display:flex;gap:10px;align-items:center}" +
|
|
93
|
-
"." + CSS.button +
|
|
94
|
-
"{display:inline-flex;align-items:center;justify-content:center;gap:4px;border:.5px solid var(--dsw-alias-border-l3);" +
|
|
95
|
-
"border-radius:14px;cursor:pointer;font-size:12px;line-height:18px;color:var(--dsw-alias-label-primary);" +
|
|
96
|
-
"background:transparent;padding:0 10px;height:28px}" +
|
|
97
|
-
"." + CSS.button + ":hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}" +
|
|
98
|
-
"." + CSS.button + ":disabled{cursor:not-allowed;opacity:.4}" +
|
|
99
|
-
"." + CSS.button + ":focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}" +
|
|
100
|
-
"." + CSS.readOnly + "{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px}" +
|
|
101
|
-
"." + CSS.dot + "{box-sizing:border-box;corner-shape:round;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}" +
|
|
102
|
-
"." + CSS.dotError + "{background:var(--dsw-alias-state-error-primary)}" +
|
|
103
|
-
"." + CSS.link + "{color:var(--dsw-alias-brand-primary);text-decoration:none;font-size:12px;line-height:1.5}" +
|
|
104
|
-
"." + CSS.link + ":hover{text-decoration:underline}" +
|
|
105
|
-
"." + CSS.value + "{margin:0;color:var(--dsw-alias-label-secondary);font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;line-height:1.5;white-space:pre-wrap}" +
|
|
106
|
-
"." + CSS.docs + "{margin:0;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.5;overflow-wrap:anywhere}" +
|
|
107
|
-
"." + CSS.editor + "{flex-direction:column;gap:8px;display:flex}" +
|
|
108
|
-
"." + CSS.text +
|
|
109
|
-
"{box-sizing:border-box;width:100%;border:.5px solid var(--dsw-alias-border-l3);border-radius:10px;background:var(--dsw-alias-bg-layer-1);" +
|
|
110
|
-
"color:var(--dsw-alias-label-primary);font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;line-height:1.5;" +
|
|
111
|
-
"padding:6px 10px;min-height:64px;resize:vertical}" +
|
|
112
|
-
"." + CSS.text + ":focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-1px}";
|
|
113
|
-
var TAG_ID = "@comecaramelos/dsh-chrome-mcp/ChromeMcpCard.module.css";
|
|
114
|
-
if (typeof document !== "undefined" && document.querySelector('style[data-plugin-css="' + TAG_ID + '"]') === null) {
|
|
115
|
-
var tag = document.createElement("style");
|
|
116
|
-
tag.dataset.plugin = "@comecaramelos/dsh-chrome-mcp";
|
|
117
|
-
tag.dataset.pluginCss = TAG_ID;
|
|
118
|
-
tag.textContent = css;
|
|
119
|
-
document.head.appendChild(tag);
|
|
120
|
-
}
|
|
1002
|
+
// src/client/controller/read.ts
|
|
1003
|
+
function readNumber(value, fallback) {
|
|
1004
|
+
return typeof value === "number" ? value : fallback;
|
|
1005
|
+
}
|
|
1006
|
+
function readString(value, fallback) {
|
|
1007
|
+
return typeof value === "string" ? value : fallback;
|
|
1008
|
+
}
|
|
1009
|
+
function readWindowsChromeStatus(value) {
|
|
1010
|
+
const fallback = { state: "off", port: 0, error: "" };
|
|
1011
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return fallback;
|
|
1012
|
+
const status = value.state;
|
|
1013
|
+
const states = ["off", "launching", "connected", "unreachable", "not-found", "launch-failed", "not-applicable"];
|
|
1014
|
+
const port = value.port;
|
|
1015
|
+
const error = value.error;
|
|
1016
|
+
return {
|
|
1017
|
+
state: typeof status === "string" && states.indexOf(status) !== -1 ? status : "off",
|
|
1018
|
+
port: typeof port === "number" && Number.isFinite(port) ? Math.trunc(port) : 0,
|
|
1019
|
+
error: typeof error === "string" ? error : ""
|
|
1020
|
+
};
|
|
1021
|
+
}
|
|
1022
|
+
function readStatus(value) {
|
|
1023
|
+
if (!Array.isArray(value)) return [];
|
|
1024
|
+
const statuses = [];
|
|
1025
|
+
for (const item of value) {
|
|
1026
|
+
if (item === null || typeof item !== "object" || Array.isArray(item)) continue;
|
|
1027
|
+
const path = readString(item.path, "");
|
|
1028
|
+
if (path === "") continue;
|
|
1029
|
+
statuses.push({
|
|
1030
|
+
path,
|
|
1031
|
+
version: readString(item.version, ""),
|
|
1032
|
+
error: readString(item.error, "")
|
|
1033
|
+
});
|
|
1034
|
+
}
|
|
1035
|
+
return statuses;
|
|
1036
|
+
}
|
|
121
1037
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
1038
|
+
// src/client/controller/controller.ts
|
|
1039
|
+
var ChromeMcpCardController = class {
|
|
1040
|
+
scope;
|
|
1041
|
+
mirror;
|
|
1042
|
+
disposed = false;
|
|
1043
|
+
pollTimer = null;
|
|
1044
|
+
pollGeneration = 0;
|
|
1045
|
+
waitStarted = false;
|
|
1046
|
+
store;
|
|
1047
|
+
unsubscribe;
|
|
1048
|
+
constructor(scope, mirror) {
|
|
1049
|
+
this.scope = scope;
|
|
1050
|
+
this.mirror = mirror;
|
|
1051
|
+
this.store = (0, import_dsh_client_store.createSnapshotStore)({
|
|
1052
|
+
available: false,
|
|
1053
|
+
writable: false,
|
|
1054
|
+
extraFlags: [],
|
|
1055
|
+
lastError: "",
|
|
1056
|
+
chromeMissing: false,
|
|
1057
|
+
executableDiscoveryRevision: 0,
|
|
1058
|
+
chromeVersion: "",
|
|
1059
|
+
executables: [],
|
|
1060
|
+
executableStatus: [],
|
|
1061
|
+
chromePath: "",
|
|
1062
|
+
effectiveChromePath: "",
|
|
1063
|
+
effectiveSource: "",
|
|
1064
|
+
stderrMode: "",
|
|
1065
|
+
rowStderr: "log",
|
|
1066
|
+
defaultExtraFlags: [],
|
|
1067
|
+
wslExtraFlags: [],
|
|
1068
|
+
wslWindowsExecutable: false,
|
|
1069
|
+
windowsChromeStatus: { state: "off", port: 0, error: "" },
|
|
1070
|
+
carriesConnectionMode: false,
|
|
1071
|
+
actionError: ""
|
|
1072
|
+
});
|
|
1073
|
+
this.unsubscribe = scope.subscribe(() => this.publish());
|
|
1074
|
+
this.publish();
|
|
1075
|
+
}
|
|
1076
|
+
/** Republish the latest scope snapshot into the card store. */
|
|
1077
|
+
publish() {
|
|
1078
|
+
if (this.disposed) return;
|
|
1079
|
+
const previous = this.store.getSnapshot();
|
|
1080
|
+
const snapshot = this.scope.getSnapshot();
|
|
1081
|
+
if (snapshot.status !== "ready" || snapshot.value === void 0) {
|
|
1082
|
+
this.store.set({
|
|
1083
|
+
available: false,
|
|
1084
|
+
writable: false,
|
|
1085
|
+
extraFlags: [],
|
|
1086
|
+
lastError: "",
|
|
1087
|
+
chromeMissing: false,
|
|
1088
|
+
executableDiscoveryRevision: 0,
|
|
1089
|
+
chromeVersion: "",
|
|
1090
|
+
executables: [],
|
|
1091
|
+
executableStatus: [],
|
|
1092
|
+
chromePath: "",
|
|
1093
|
+
effectiveChromePath: "",
|
|
1094
|
+
effectiveSource: "",
|
|
1095
|
+
stderrMode: "",
|
|
1096
|
+
rowStderr: "log",
|
|
1097
|
+
defaultExtraFlags: [],
|
|
1098
|
+
wslExtraFlags: [],
|
|
1099
|
+
wslWindowsExecutable: false,
|
|
1100
|
+
windowsChromeStatus: { state: "off", port: 0, error: "" },
|
|
1101
|
+
carriesConnectionMode: false,
|
|
1102
|
+
actionError: previous.actionError
|
|
1103
|
+
});
|
|
1104
|
+
return;
|
|
1105
|
+
}
|
|
1106
|
+
const value = snapshot.value;
|
|
1107
|
+
const base = snapshot.base ?? {};
|
|
1108
|
+
const servedRevision = readNumber(base.executableDiscoveryRevision, readNumber(value.executableDiscoveryRevision, 0));
|
|
1109
|
+
const servedVersion = readString(base.chromeVersion, readString(value.chromeVersion, ""));
|
|
1110
|
+
const servedExecutables = normalizeEntries(value.executables);
|
|
1111
|
+
const servedStatus = readStatus(base.executableStatus);
|
|
1112
|
+
const servedEffectivePath = readString(
|
|
1113
|
+
base.effectiveChromePath,
|
|
1114
|
+
readString(value.effectiveChromePath, readString(value.chromePath, ""))
|
|
1115
|
+
);
|
|
1116
|
+
const servedSource = base.effectiveSource === "selected" || base.effectiveSource === "row-config" ? base.effectiveSource : value.effectiveSource === "selected" || value.effectiveSource === "row-config" ? value.effectiveSource : "";
|
|
1117
|
+
const servedStderrMode = typeof value.stderrMode === "string" ? value.stderrMode : "";
|
|
1118
|
+
const servedRowStderr = typeof base.rowStderr === "string" ? base.rowStderr : typeof value.rowStderr === "string" ? value.rowStderr : "log";
|
|
1119
|
+
const servedDefaults = Array.isArray(base.defaultExtraFlags) ? base.defaultExtraFlags.filter((flag) => typeof flag === "string" && flag !== "") : Array.isArray(value.defaultExtraFlags) ? value.defaultExtraFlags.filter((flag) => typeof flag === "string" && flag !== "") : [];
|
|
1120
|
+
const servedWslFlags = Array.isArray(base.wslExtraFlags) ? base.wslExtraFlags.filter((flag) => typeof flag === "string" && flag !== "") : Array.isArray(value.wslExtraFlags) ? value.wslExtraFlags.filter((flag) => typeof flag === "string" && flag !== "") : [];
|
|
1121
|
+
const servedWslWindows = base.wslWindowsExecutable === void 0 ? value.wslWindowsExecutable === true : base.wslWindowsExecutable === true;
|
|
1122
|
+
const servedWindowsChrome = readWindowsChromeStatus(base.windowsChromeStatus !== void 0 ? base.windowsChromeStatus : value.windowsChromeStatus);
|
|
1123
|
+
const servedConnectMode = base.carriesConnectionMode === void 0 ? value.carriesConnectionMode === true : base.carriesConnectionMode === true;
|
|
1124
|
+
this.store.set({
|
|
1125
|
+
available: true,
|
|
1126
|
+
writable: snapshot.writable === true,
|
|
1127
|
+
extraFlags: Array.isArray(value.extraFlags) ? value.extraFlags : [],
|
|
1128
|
+
lastError: typeof base.lastError === "string" ? base.lastError : value.lastError ?? "",
|
|
1129
|
+
chromeMissing: base.chromeMissing === void 0 ? value.chromeMissing === true : base.chromeMissing === true,
|
|
1130
|
+
executableDiscoveryRevision: servedRevision,
|
|
1131
|
+
chromeVersion: servedVersion,
|
|
1132
|
+
executables: servedExecutables,
|
|
1133
|
+
executableStatus: servedStatus,
|
|
1134
|
+
chromePath: readString(value.chromePath, ""),
|
|
1135
|
+
effectiveChromePath: servedEffectivePath,
|
|
1136
|
+
effectiveSource: servedSource,
|
|
1137
|
+
stderrMode: servedStderrMode === "log" || servedStderrMode === "console" ? servedStderrMode : "",
|
|
1138
|
+
rowStderr: servedRowStderr === "console" ? "console" : "log",
|
|
1139
|
+
defaultExtraFlags: servedDefaults,
|
|
1140
|
+
wslExtraFlags: servedWslFlags,
|
|
1141
|
+
wslWindowsExecutable: servedWslWindows,
|
|
1142
|
+
windowsChromeStatus: servedWindowsChrome,
|
|
1143
|
+
carriesConnectionMode: servedConnectMode,
|
|
1144
|
+
actionError: ""
|
|
1145
|
+
});
|
|
1146
|
+
if (!this.waitStarted) {
|
|
1147
|
+
this.waitStarted = true;
|
|
1148
|
+
if (servedRevision === 0) void this.waitFor("executableDiscoveryRevision", 0, Date.now() + CHECK_POLL_TIMEOUT_MS);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
/**
|
|
1152
|
+
* Immediate write of the user's extra flags (persisted; the host's
|
|
1153
|
+
* validate hook rejects invalid lists here, and the next bridge
|
|
1154
|
+
* launch starts with them).
|
|
1155
|
+
*/
|
|
1156
|
+
saveFlags(flags) {
|
|
1157
|
+
return this.scope.set("extraFlags", flags).catch((error) => this.noteError(error));
|
|
1158
|
+
}
|
|
1159
|
+
/**
|
|
1160
|
+
* Pick one saved executable. Writes the persisted `chromePath` (the host
|
|
1161
|
+
* restarts the bridge connection carrying it and re-runs the discovery), then
|
|
1162
|
+
* waits until the served revision advances past the click-time value.
|
|
1163
|
+
*/
|
|
1164
|
+
selectExecutable(id) {
|
|
1165
|
+
if (this.disposed) return Promise.resolve();
|
|
1166
|
+
const target = typeof id === "string" ? id.trim() : "";
|
|
1167
|
+
if (target === "") return Promise.resolve();
|
|
1168
|
+
const baseline = this.store.getSnapshot().executableDiscoveryRevision;
|
|
1169
|
+
const wait = this.waitFor("executableDiscoveryRevision", baseline, Date.now() + CHECK_POLL_TIMEOUT_MS);
|
|
1170
|
+
return this.scope.set("chromePath", target).catch((error) => this.noteError(error)).then(() => wait);
|
|
1171
|
+
}
|
|
1172
|
+
/**
|
|
1173
|
+
* Save the whole rows list: the card owns the draft, the host re-normalizes
|
|
1174
|
+
* on the way in and re-runs the scan the new ids imply.
|
|
1175
|
+
*/
|
|
1176
|
+
saveExecutables(entries) {
|
|
1177
|
+
if (this.disposed) return Promise.resolve();
|
|
1178
|
+
const rows = normalizeEntries(entries);
|
|
1179
|
+
const snapshot = this.store.getSnapshot();
|
|
1180
|
+
const unchanged = JSON.stringify(rows) === JSON.stringify(snapshot.executables);
|
|
1181
|
+
if (unchanged) return Promise.resolve();
|
|
1182
|
+
const baseline = snapshot.executableDiscoveryRevision;
|
|
1183
|
+
const wait = this.waitFor("executableDiscoveryRevision", baseline, Date.now() + CHECK_POLL_TIMEOUT_MS);
|
|
1184
|
+
return this.scope.set("executables", rows).catch((error) => this.noteError(error)).then(() => wait);
|
|
1185
|
+
}
|
|
1186
|
+
/**
|
|
1187
|
+
* Ask the host to re-probe the saved rows and fold what it finds into them,
|
|
1188
|
+
* then wait for the served revision to advance: write the trigger, then poll
|
|
1189
|
+
* the shared describe mirror until it moves — or the budget elapses. Resolves
|
|
1190
|
+
* with every candidate the run answered for and what it said about each, so
|
|
1191
|
+
* the dialog opens over the run's own answer list.
|
|
1192
|
+
*/
|
|
1193
|
+
refreshExecutables() {
|
|
1194
|
+
if (this.disposed) return Promise.resolve([]);
|
|
1195
|
+
const baseline = this.store.getSnapshot().executableDiscoveryRevision;
|
|
1196
|
+
const wait = this.waitFor("executableDiscoveryRevision", baseline, Date.now() + CHECK_POLL_TIMEOUT_MS);
|
|
1197
|
+
return this.scope.set("refreshExecutablesNonce", Date.now()).catch((error) => this.noteError(error)).then(() => wait).then(() => {
|
|
1198
|
+
const served = this.store.getSnapshot();
|
|
1199
|
+
return Array.isArray(served.executableStatus) ? served.executableStatus : [];
|
|
1200
|
+
});
|
|
1201
|
+
}
|
|
1202
|
+
/**
|
|
1203
|
+
* Merge the ids picked in the dialog into the saved rows: every id already
|
|
1204
|
+
* there keeps its place (and its name), the missing ones append nameless.
|
|
1205
|
+
* The host re-normalizes on the way in and re-runs the scan the new ids
|
|
1206
|
+
* imply, so the same revision counter carries the wait.
|
|
1207
|
+
*/
|
|
1208
|
+
addExecutables(ids) {
|
|
1209
|
+
if (this.disposed) return Promise.resolve();
|
|
1210
|
+
const served = this.store.getSnapshot();
|
|
1211
|
+
const rows = normalizeEntries(served.executables);
|
|
1212
|
+
const merged = rows.concat(
|
|
1213
|
+
(Array.isArray(ids) ? ids : []).filter((id) => typeof id === "string" && id.trim() !== "").map((id) => ({ id: id.trim(), name: "" }))
|
|
1214
|
+
);
|
|
1215
|
+
const next = normalizeEntries(merged);
|
|
1216
|
+
if (JSON.stringify(next) === JSON.stringify(rows)) return Promise.resolve();
|
|
1217
|
+
const baseline = served.executableDiscoveryRevision;
|
|
1218
|
+
const wait = this.waitFor("executableDiscoveryRevision", baseline, Date.now() + CHECK_POLL_TIMEOUT_MS);
|
|
1219
|
+
return this.scope.set("executables", next).catch((error) => this.noteError(error)).then(() => wait);
|
|
1220
|
+
}
|
|
1221
|
+
/**
|
|
1222
|
+
* Ask the host to drive a Windows-side Chrome in connect mode: write the
|
|
1223
|
+
* trigger, then wait for the run to settle (same revision counter every
|
|
1224
|
+
* executable run advances). What the button ends up reporting — launched,
|
|
1225
|
+
* already connected, prereq missing — is served through
|
|
1226
|
+
* `windowsChromeStatus`, not through the promise.
|
|
1227
|
+
*/
|
|
1228
|
+
openWindowsChrome() {
|
|
1229
|
+
if (this.disposed) return Promise.resolve();
|
|
1230
|
+
const baseline = this.store.getSnapshot().executableDiscoveryRevision;
|
|
1231
|
+
const wait = this.waitFor("executableDiscoveryRevision", baseline, Date.now() + CHECK_POLL_TIMEOUT_MS);
|
|
1232
|
+
return this.scope.set("openWindowsChromeNonce", Date.now()).catch((error) => this.noteError(error)).then(() => wait);
|
|
1233
|
+
}
|
|
1234
|
+
/**
|
|
1235
|
+
* Flip the "Reduce log output" toggle: persists `stderrMode` as "log"
|
|
1236
|
+
* (capture stderr into the bridge log file) or "console" (echo it for
|
|
1237
|
+
* debugging), starting from what the host currently resolves — the served
|
|
1238
|
+
* `stderrMode` when set, else the row-config `rowStderr` fallback. The host
|
|
1239
|
+
* restarts the bridge connection on the change.
|
|
1240
|
+
*/
|
|
1241
|
+
toggleStderr() {
|
|
1242
|
+
if (this.disposed) return Promise.resolve();
|
|
1243
|
+
const snapshot = this.store.getSnapshot();
|
|
1244
|
+
const effective = snapshot.stderrMode === "log" || snapshot.stderrMode === "console" ? snapshot.stderrMode : snapshot.rowStderr === "console" ? "console" : "log";
|
|
1245
|
+
const next = effective === "log" ? "console" : "log";
|
|
1246
|
+
return this.scope.set("stderrMode", next).catch((error) => this.noteError(error));
|
|
1247
|
+
}
|
|
1248
|
+
/** The face the card's slot registration injects. */
|
|
1249
|
+
inject() {
|
|
1250
|
+
return {
|
|
1251
|
+
hooks: { chromeMcpCard: this.store },
|
|
1252
|
+
saveFlags: (flags) => this.saveFlags(flags),
|
|
1253
|
+
toggleStderr: () => this.toggleStderr(),
|
|
1254
|
+
selectExecutable: (id) => this.selectExecutable(id),
|
|
1255
|
+
saveExecutables: (entries) => this.saveExecutables(entries),
|
|
1256
|
+
refreshExecutables: () => this.refreshExecutables(),
|
|
1257
|
+
addExecutables: (ids) => this.addExecutables(ids),
|
|
1258
|
+
openWindowsChrome: () => this.openWindowsChrome(),
|
|
1259
|
+
/** One mirror re-read (used when the card opens). */
|
|
1260
|
+
refresh: () => this.refresh()
|
|
1261
|
+
};
|
|
1262
|
+
}
|
|
1263
|
+
/**
|
|
1264
|
+
* Re-read the shared describe mirror once so base-layer status
|
|
1265
|
+
* captured asynchronously by the host (bridge connection errors)
|
|
1266
|
+
* reaches an opened card without waiting for a write or a run.
|
|
1267
|
+
*/
|
|
1268
|
+
refresh() {
|
|
1269
|
+
if (this.disposed) return Promise.resolve();
|
|
1270
|
+
let load;
|
|
1271
|
+
try {
|
|
1272
|
+
load = this.mirror.load();
|
|
1273
|
+
} catch {
|
|
1274
|
+
load = void 0;
|
|
1275
|
+
}
|
|
1276
|
+
return Promise.resolve(load).catch(() => {
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
dispose() {
|
|
1280
|
+
this.disposed = true;
|
|
1281
|
+
if (this.pollTimer !== null) {
|
|
1282
|
+
clearTimeout(this.pollTimer);
|
|
1283
|
+
this.pollTimer = null;
|
|
1284
|
+
}
|
|
1285
|
+
this.unsubscribe();
|
|
1286
|
+
}
|
|
1287
|
+
/**
|
|
1288
|
+
* Re-describe the settings mirror every tick until the given revision field
|
|
1289
|
+
* advances past `baseline`, or the deadline passes.
|
|
1290
|
+
*/
|
|
1291
|
+
waitFor(field, baseline, deadline) {
|
|
1292
|
+
const generation = ++this.pollGeneration;
|
|
1293
|
+
const controller = this;
|
|
1294
|
+
return new Promise((resolve) => {
|
|
1295
|
+
let timer;
|
|
1296
|
+
let settled = false;
|
|
1297
|
+
const arm = (delay) => {
|
|
1298
|
+
timer = setTimeout(tick, delay);
|
|
1299
|
+
controller.pollTimer = timer ?? null;
|
|
1300
|
+
};
|
|
1301
|
+
const finish = () => {
|
|
1302
|
+
if (settled) return;
|
|
1303
|
+
settled = true;
|
|
1304
|
+
clearTimeout(timer);
|
|
1305
|
+
if (controller.pollTimer === (timer ?? null)) controller.pollTimer = null;
|
|
1306
|
+
resolve();
|
|
1307
|
+
};
|
|
1308
|
+
const satisfied = () => readNumber(controller.store.getSnapshot()[field], baseline) > baseline || Date.now() >= deadline;
|
|
1309
|
+
const stale = () => controller.disposed || generation !== controller.pollGeneration;
|
|
1310
|
+
const tick = () => {
|
|
1311
|
+
if (stale() || satisfied()) {
|
|
1312
|
+
finish();
|
|
1313
|
+
return;
|
|
1314
|
+
}
|
|
1315
|
+
let loaded;
|
|
1316
|
+
try {
|
|
1317
|
+
loaded = controller.mirror.load();
|
|
1318
|
+
} catch {
|
|
1319
|
+
loaded = void 0;
|
|
1320
|
+
}
|
|
1321
|
+
Promise.resolve(loaded).catch(() => ({})).then(() => {
|
|
1322
|
+
if (stale() || satisfied()) {
|
|
1323
|
+
finish();
|
|
1324
|
+
return;
|
|
1325
|
+
}
|
|
1326
|
+
arm(CHECK_POLL_TICK_MS);
|
|
1327
|
+
});
|
|
1328
|
+
};
|
|
1329
|
+
arm(CHECK_POLL_TICK_MS);
|
|
1330
|
+
});
|
|
1331
|
+
}
|
|
1332
|
+
/** Surface a rejected action on the card (store-only; never host state). */
|
|
1333
|
+
noteError(error) {
|
|
1334
|
+
if (this.disposed) return;
|
|
1335
|
+
const previous = this.store.getSnapshot();
|
|
1336
|
+
this.store.set({
|
|
1337
|
+
...previous,
|
|
1338
|
+
actionError: error != null && typeof error.message === "string" ? error.message : String(error)
|
|
1339
|
+
});
|
|
1340
|
+
}
|
|
1341
|
+
};
|
|
346
1342
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
1343
|
+
// src/client/locales/en-US.ts
|
|
1344
|
+
var en = {
|
|
1345
|
+
title: "DSH Chrome MCP",
|
|
1346
|
+
description: "DSH Chrome MCP server (chrome-devtools-mcp) settings and status",
|
|
1347
|
+
executableLabel: "Chrome executable",
|
|
1348
|
+
executableHint: "The saved executable the bridge launches with. Selecting one restarts the connection.",
|
|
1349
|
+
executableNone: "No Chrome executable detected",
|
|
1350
|
+
executableRows: "Saved executables",
|
|
1351
|
+
executableIdLabel: "Executable path",
|
|
1352
|
+
executableIdPlaceholder: "Path to a Chrome executable",
|
|
1353
|
+
executableNameLabel: "Display name",
|
|
1354
|
+
namePlaceholder: "Display name",
|
|
1355
|
+
addExecutable: "Add executable",
|
|
1356
|
+
removeExecutable: "Delete executable",
|
|
1357
|
+
fetchExecutables: "Fetch executables",
|
|
1358
|
+
fetching: "Fetching\u2026",
|
|
1359
|
+
executableEmpty: "No executables saved. Use \u201CFetch executables\u201D, or add the Chrome path by hand.",
|
|
1360
|
+
dialogExecutablesTitle: "Choose executables to add",
|
|
1361
|
+
dialogExecutablesDescription: "These are the Chrome executables this host's last run answered for. Choose the ones to add.",
|
|
1362
|
+
dialogEmpty: "Nothing matches your search.",
|
|
1363
|
+
searchExecutables: "Search executables",
|
|
1364
|
+
selectAll: "Select all",
|
|
1365
|
+
addSelected: "Add selected",
|
|
1366
|
+
candidateSaved: "Saved",
|
|
1367
|
+
close: "Close",
|
|
1368
|
+
cancel: "Cancel",
|
|
1369
|
+
unavailableSuffix: "not runnable",
|
|
1370
|
+
wslWindowsTitle: "Windows executable under WSL",
|
|
1371
|
+
wslWindowsNote: "This host runs inside Windows Subsystem for Linux and the selected executable is the Windows Chrome, which the bridge cannot launch: the debug pipes it opens do not survive the WSL interop boundary, and its debugging port stays bound to the Windows loopback, unreachable over the WSL2 NAT bridge. It answers --version correctly, so the pill looks healthy until the first tool call fails. To drive the Windows browser, start Chrome yourself with --remote-debugging-port=9222 --user-data-dir=C:\\chrome-debug, add --browserUrl=http://127.0.0.1:9222 to the extra flags, and make Windows loopback reachable from WSL by setting networkingMode=mirrored in %USERPROFILE%\\.wslconfig followed by wsl --shutdown. Chrome installed inside the Linux distro needs none of this.",
|
|
1372
|
+
probing: "Checking the executables\u2026",
|
|
1373
|
+
versionLabel: "Probed version:",
|
|
1374
|
+
probeJustNow: "just now",
|
|
1375
|
+
probeSecondsAgo: "seconds ago",
|
|
1376
|
+
probeMinutesAgo: "minutes ago",
|
|
1377
|
+
probeHoursAgo: "hours ago",
|
|
1378
|
+
flagsTitle: "Extra flags",
|
|
1379
|
+
flagsHint: "The extra flags the bridge launches with. One flag per row \u2014 saving restarts the connection.",
|
|
1380
|
+
flagsEmpty: "No extra flags saved. Add one by hand, or use \u201CRestore defaults\u201D.",
|
|
1381
|
+
flagsRows: "Saved flags",
|
|
1382
|
+
flagsValueLabel: "Extra flag",
|
|
1383
|
+
flagsValuePlaceholder: "e.g. --headless",
|
|
1384
|
+
addFlag: "Add flag",
|
|
1385
|
+
removeFlag: "Delete flag",
|
|
1386
|
+
flagsRestore: "Restore defaults",
|
|
1387
|
+
flagsRestoreTitle: "Keep every flag the rows carry and append the defaults that are still missing",
|
|
1388
|
+
flagsWsl: "Flags WSL",
|
|
1389
|
+
flagsWslTitle: "A\xF1adir flags recomendados para WSL<->Windows",
|
|
1390
|
+
openWindowsChrome: "Prelaunch Windows Chrome",
|
|
1391
|
+
openWindowsChromeTitle: "Lanza el Chrome del lado Windows con --remote-debugging-port y deja el bridge en modo conexi\xF3n",
|
|
1392
|
+
openWindowsChromeBusy: "Iniciando Chrome de Windows\u2026",
|
|
1393
|
+
windowsChromeNotWsl: "Windows Chrome no aplica fuera de WSL.",
|
|
1394
|
+
windowsChromeNotFound: "No se encontr\xF3 Chrome en el lado Windows (Program Files / Program Files (x86)).",
|
|
1395
|
+
windowsChromeUnreachable: "Requiere networkingMode=mirrored en %USERPROFILE%\\.wslconfig, con wsl --shutdown: hoy el loopback de Windows no es alcanzable desde este host WSL.",
|
|
1396
|
+
connectModeLabel: "Connect mode",
|
|
1397
|
+
connectModeHint: "Modo conexi\xF3n: el bridge no lanza un Chrome local (conecta con --browserUrl / --wsEndpoint); la selecci\xF3n local queda a la espera.",
|
|
1398
|
+
reduceLabel: "Reduce log output",
|
|
1399
|
+
reduceTitle: "Capture the bridge's stderr into the plugin's log file",
|
|
1400
|
+
reduceHint: "Captures stderr into the plugin's log file. Turn it off to debug the raw bridge output; switching restarts the bridge connection.",
|
|
1401
|
+
docsMessage: "Find the complete list of server parameters (e.g., --headless, --isolated, --slim) and how to configure WebSocket connections in the Configuration Guide.",
|
|
1402
|
+
docsLinkLabel: "Configuration Guide",
|
|
1403
|
+
statusError: "Error",
|
|
1404
|
+
statusOk: "OK",
|
|
1405
|
+
chromeMissing: "Chrome executable not found",
|
|
1406
|
+
readOnly: "Settings are read-only in this deployment.",
|
|
1407
|
+
expand: "Expand",
|
|
1408
|
+
collapse: "Collapse"
|
|
1409
|
+
};
|
|
351
1410
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
* like the stock plugin cards: the header is a disclosure button
|
|
355
|
-
* (closed by default) and the controls live in the body.
|
|
356
|
-
* @param props - locale copy, the card snapshot hook, and its actions.
|
|
357
|
-
* @returns the card, or nothing until the namespace is served.
|
|
358
|
-
*/
|
|
359
|
-
function ChromeMcpCard(props) {
|
|
360
|
-
var t = props.t;
|
|
361
|
-
var state = props.useChromeMcpCard((snapshot) => snapshot);
|
|
362
|
-
var openState = react.useState(false);
|
|
363
|
-
var open = openState[0];
|
|
364
|
-
var setOpen = openState[1];
|
|
365
|
-
var checkingState = react.useState(false);
|
|
366
|
-
var isChecking = checkingState[0];
|
|
367
|
-
var setChecking = checkingState[1];
|
|
368
|
-
var bodyId = react.useId();
|
|
369
|
-
var flagsRef = react.useRef(null);
|
|
370
|
-
if (!state.available) return null;
|
|
371
|
-
// Header status dot: the single error surface. Red while a
|
|
372
|
-
// check/connection error, a rejected action, or the known
|
|
373
|
-
// executable-missing flag (which survives bridge-update-success
|
|
374
|
-
// clears of lastError) is present; hidden while healthy.
|
|
375
|
-
var error = state.lastError || state.actionError || "";
|
|
376
|
-
var tip = error !== "" ? error : state.chromeMissing === true ? t("chromeMissing") : "";
|
|
377
|
-
var dotAria = tip !== "" ? t("statusError") : t("statusOk");
|
|
378
|
-
return (0, reactJsx.jsx)("li", {
|
|
379
|
-
className: CSS.card + (open ? " " + CSS.cardOpen : ""),
|
|
380
|
-
children: [
|
|
381
|
-
(0, reactJsx.jsxs)("button", {
|
|
382
|
-
type: "button",
|
|
383
|
-
className: CSS.header,
|
|
384
|
-
"aria-expanded": open,
|
|
385
|
-
"aria-label": t(open ? "collapse" : "expand") + ": " + t("title"),
|
|
386
|
-
"aria-controls": bodyId,
|
|
387
|
-
onClick: () => {
|
|
388
|
-
setOpen(!open);
|
|
389
|
-
if (!open && typeof props.refresh === "function") props.refresh();
|
|
390
|
-
},
|
|
391
|
-
children: [
|
|
392
|
-
(0, reactJsx.jsxs)("span", {
|
|
393
|
-
className: CSS.headText,
|
|
394
|
-
children: [
|
|
395
|
-
(0, reactJsx.jsxs)("div", {
|
|
396
|
-
className: CSS.nameRow,
|
|
397
|
-
children: [
|
|
398
|
-
(0, reactJsx.jsx)("span", {
|
|
399
|
-
className: CSS.name,
|
|
400
|
-
children: t("title")
|
|
401
|
-
}),
|
|
402
|
-
tip !== ""
|
|
403
|
-
? (0, reactJsx.jsx)("span", {
|
|
404
|
-
className: CSS.dot + " " + CSS.dotError,
|
|
405
|
-
role: "img",
|
|
406
|
-
"aria-label": dotAria,
|
|
407
|
-
title: tip
|
|
408
|
-
})
|
|
409
|
-
: null
|
|
410
|
-
]
|
|
411
|
-
}),
|
|
412
|
-
(0, reactJsx.jsx)("span", { className: CSS.desc, children: t("description") })
|
|
413
|
-
]
|
|
414
|
-
}),
|
|
415
|
-
(0, reactJsx.jsx)("svg", {
|
|
416
|
-
width: 14,
|
|
417
|
-
height: 14,
|
|
418
|
-
className: CSS.chevron + (open ? " " + CSS.chevronOpen : ""),
|
|
419
|
-
viewBox: "0 0 14 14",
|
|
420
|
-
fill: "none",
|
|
421
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
422
|
-
children: (0, reactJsx.jsx)("path", { d: CHEVRON_PATH, fill: "currentColor" })
|
|
423
|
-
})
|
|
424
|
-
]
|
|
425
|
-
}),
|
|
426
|
-
open
|
|
427
|
-
? (0, reactJsx.jsxs)("div", {
|
|
428
|
-
id: bodyId,
|
|
429
|
-
className: CSS.body,
|
|
430
|
-
children: [
|
|
431
|
-
!state.writable
|
|
432
|
-
? (0, reactJsx.jsx)("p", { className: CSS.readOnly, role: "status", children: t("readOnly") })
|
|
433
|
-
: null,
|
|
434
|
-
(0, reactJsx.jsxs)("div", {
|
|
435
|
-
className: CSS.field,
|
|
436
|
-
children: [
|
|
437
|
-
(0, reactJsx.jsxs)("div", {
|
|
438
|
-
className: CSS.rowText,
|
|
439
|
-
children: [
|
|
440
|
-
(0, reactJsx.jsx)("div", { className: CSS.fieldTitle, children: t("flagsTitle") }),
|
|
441
|
-
(0, reactJsx.jsx)("div", { className: CSS.fieldDesc, children: t("flagsHint") })
|
|
442
|
-
]
|
|
443
|
-
})
|
|
444
|
-
]
|
|
445
|
-
}),
|
|
446
|
-
// Extra flags editor: one flag per line; the write goes
|
|
447
|
-
// through props.saveFlags (host validate + fiber.update).
|
|
448
|
-
// `key` re-mounts the textarea when the store value
|
|
449
|
-
// changes underneath the editor (e.g. a save from another
|
|
450
|
-
// window), so the shown value never drifts.
|
|
451
|
-
state.writable
|
|
452
|
-
? (0, reactJsx.jsxs)("div", {
|
|
453
|
-
className: CSS.editor,
|
|
454
|
-
children: [
|
|
455
|
-
(0, reactJsx.jsx)("textarea", {
|
|
456
|
-
className: CSS.text,
|
|
457
|
-
rows: Math.max(3, state.extraFlags.length),
|
|
458
|
-
placeholder: t("flagsPlaceholder"),
|
|
459
|
-
defaultValue: state.extraFlags.join("\n"),
|
|
460
|
-
key: state.extraFlags.join("\n"),
|
|
461
|
-
ref: flagsRef
|
|
462
|
-
}),
|
|
463
|
-
(0, reactJsx.jsx)("div", {
|
|
464
|
-
className: CSS.row,
|
|
465
|
-
children: (0, reactJsx.jsx)("button", {
|
|
466
|
-
type: "button",
|
|
467
|
-
className: CSS.button,
|
|
468
|
-
disabled: !state.writable,
|
|
469
|
-
onClick: () => {
|
|
470
|
-
var raw = flagsRef.current && typeof flagsRef.current.value === "string" ? flagsRef.current.value : "";
|
|
471
|
-
props.saveFlags(
|
|
472
|
-
raw
|
|
473
|
-
.split(/\r?\n/u)
|
|
474
|
-
.map((line) => line.trim())
|
|
475
|
-
.filter((line) => line !== "")
|
|
476
|
-
);
|
|
477
|
-
},
|
|
478
|
-
children: t("flagsSave")
|
|
479
|
-
})
|
|
480
|
-
})
|
|
481
|
-
]
|
|
482
|
-
})
|
|
483
|
-
: (0, reactJsx.jsx)("p", {
|
|
484
|
-
className: CSS.value,
|
|
485
|
-
children:
|
|
486
|
-
state.extraFlags.length > 0 ? state.extraFlags.join("\n") : t("flagsPlaceholder")
|
|
487
|
-
}),
|
|
488
|
-
(0, reactJsx.jsxs)("div", {
|
|
489
|
-
className: CSS.row,
|
|
490
|
-
children: [
|
|
491
|
-
(0, reactJsx.jsx)("button", {
|
|
492
|
-
type: "button",
|
|
493
|
-
className: CSS.button,
|
|
494
|
-
disabled: !state.writable || isChecking,
|
|
495
|
-
onClick: () => {
|
|
496
|
-
setChecking(true);
|
|
497
|
-
var checkPromise;
|
|
498
|
-
try {
|
|
499
|
-
checkPromise = props.recheck();
|
|
500
|
-
} catch (error) {
|
|
501
|
-
checkPromise = Promise.reject(error);
|
|
502
|
-
}
|
|
503
|
-
if (checkPromise == null || typeof checkPromise.finally !== "function") {
|
|
504
|
-
checkPromise = Promise.resolve(checkPromise);
|
|
505
|
-
}
|
|
506
|
-
checkPromise.finally(() => setChecking(false));
|
|
507
|
-
},
|
|
508
|
-
children: isChecking ? t("checking") : t("check")
|
|
509
|
-
})
|
|
510
|
-
]
|
|
511
|
-
}),
|
|
512
|
-
// Executable version discovered by the last check.
|
|
513
|
-
state.chromeVersion !== ""
|
|
514
|
-
? (0, reactJsx.jsxs)("div", {
|
|
515
|
-
className: CSS.field,
|
|
516
|
-
children: [
|
|
517
|
-
(0, reactJsx.jsxs)("div", {
|
|
518
|
-
className: CSS.rowText,
|
|
519
|
-
children: [
|
|
520
|
-
(0, reactJsx.jsx)("div", { className: CSS.fieldTitle, children: t("versionTitle") }),
|
|
521
|
-
(0, reactJsx.jsx)("p", {
|
|
522
|
-
className: CSS.value,
|
|
523
|
-
children: state.chromeVersion
|
|
524
|
-
})
|
|
525
|
-
]
|
|
526
|
-
})
|
|
527
|
-
]
|
|
528
|
-
})
|
|
529
|
-
: null,
|
|
530
|
-
// Product spec: the full configuration-guide message, with
|
|
531
|
-
// the upstream URL rendered as the external link.
|
|
532
|
-
(0, reactJsx.jsxs)("p", {
|
|
533
|
-
className: CSS.docs,
|
|
534
|
-
children: [
|
|
535
|
-
t("docsMessage") + " ",
|
|
536
|
-
(0, reactJsx.jsx)("a", {
|
|
537
|
-
className: CSS.link,
|
|
538
|
-
href: CONFIGURATION_URL,
|
|
539
|
-
target: "_blank",
|
|
540
|
-
rel: "noopener noreferrer",
|
|
541
|
-
children: CONFIGURATION_URL
|
|
542
|
-
})
|
|
543
|
-
]
|
|
544
|
-
})
|
|
545
|
-
]
|
|
546
|
-
})
|
|
547
|
-
: null
|
|
548
|
-
]
|
|
549
|
-
});
|
|
550
|
-
}
|
|
1411
|
+
// src/client/locales/index.ts
|
|
1412
|
+
var DICTIONARIES = { en };
|
|
551
1413
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
var controller = new ChromeMcpCardController(scope, binder.describe());
|
|
564
|
-
ctx.effect(
|
|
565
|
-
() => () => controller.dispose(),
|
|
566
|
-
"chrome-mcp: card controller"
|
|
567
|
-
);
|
|
568
|
-
ctx.slots.inject("settings.plugin.item", () =>
|
|
569
|
-
ctx.slots.register(
|
|
570
|
-
{
|
|
571
|
-
name: "settings.plugin.item",
|
|
572
|
-
key: NS,
|
|
573
|
-
locale: LOCALE_NS,
|
|
574
|
-
inject: () => controller.inject()
|
|
575
|
-
},
|
|
576
|
-
ChromeMcpCard
|
|
577
|
-
)
|
|
578
|
-
);
|
|
579
|
-
}
|
|
1414
|
+
// src/client/styles/index.ts
|
|
1415
|
+
var CARD_CSS_ID = PLUGIN_ID + "/ChromeMcpCard.module.css";
|
|
1416
|
+
function injectCardStyles() {
|
|
1417
|
+
if (typeof document === "undefined") return;
|
|
1418
|
+
if (document.querySelector('style[data-plugin-css="' + CARD_CSS_ID + '"]') !== null) return;
|
|
1419
|
+
const styleTag = document.createElement("style");
|
|
1420
|
+
styleTag.dataset.plugin = PLUGIN_ID;
|
|
1421
|
+
styleTag.dataset.pluginCss = CARD_CSS_ID;
|
|
1422
|
+
styleTag.textContent = cssText;
|
|
1423
|
+
document.head.appendChild(styleTag);
|
|
1424
|
+
}
|
|
580
1425
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
1426
|
+
// src/client/apply.ts
|
|
1427
|
+
function apply(ctx) {
|
|
1428
|
+
injectCardStyles();
|
|
1429
|
+
ctx.effect(() => ctx.locale.register(LOCALE_NAMESPACE, DICTIONARIES), `${SETTINGS_NAMESPACE}: dictionaries`);
|
|
1430
|
+
const binder = ctx.settingsScope;
|
|
1431
|
+
const scope = binder.bind({ namespace: SETTINGS_NAMESPACE });
|
|
1432
|
+
const controller = new ChromeMcpCardController(scope, binder.describe());
|
|
1433
|
+
ctx.effect(
|
|
1434
|
+
() => () => controller.dispose(),
|
|
1435
|
+
`${SETTINGS_NAMESPACE}: card controller`
|
|
1436
|
+
);
|
|
1437
|
+
ctx.slots.inject("settings.plugin.item", () => ctx.slots.register(
|
|
1438
|
+
{
|
|
1439
|
+
name: "settings.plugin.item",
|
|
1440
|
+
key: SETTINGS_NAMESPACE,
|
|
1441
|
+
locale: LOCALE_NAMESPACE,
|
|
1442
|
+
inject: () => controller.inject()
|
|
1443
|
+
},
|
|
1444
|
+
ChromeMcpCard
|
|
1445
|
+
));
|
|
1446
|
+
}
|
|
1447
|
+
return module.exports;
|
|
584
1448
|
}
|
|
585
1449
|
});
|
|
1450
|
+
//# sourceMappingURL=client.js.map
|