@comecaramelos/dsh-chrome-mcp 0.0.1 → 2.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.
Files changed (214) hide show
  1. package/README.md +44 -95
  2. package/assets/dsh-chrome-mcp-social.png +0 -0
  3. package/assets/icon.svg +71 -0
  4. package/assets/prelaunch-windows-chrome.html +277 -0
  5. package/lib/client.js +3005 -568
  6. package/lib/client.js.map +7 -0
  7. package/lib/host/apply.d.ts +40 -0
  8. package/lib/host/apply.d.ts.map +1 -0
  9. package/lib/host/apply.js +57 -0
  10. package/lib/host/apply.js.map +1 -0
  11. package/lib/host/bridge.d.ts +74 -0
  12. package/lib/host/bridge.d.ts.map +1 -0
  13. package/lib/host/bridge.js +168 -0
  14. package/lib/host/bridge.js.map +1 -0
  15. package/lib/host/catalog.d.ts +31 -0
  16. package/lib/host/catalog.d.ts.map +1 -0
  17. package/lib/host/catalog.js +92 -0
  18. package/lib/host/catalog.js.map +1 -0
  19. package/lib/host/commands/chrome-path.d.ts +4 -0
  20. package/lib/host/commands/chrome-path.d.ts.map +1 -0
  21. package/lib/host/commands/chrome-path.js +65 -0
  22. package/lib/host/commands/chrome-path.js.map +1 -0
  23. package/lib/host/commands/chrome-refresh.d.ts +17 -0
  24. package/lib/host/commands/chrome-refresh.d.ts.map +1 -0
  25. package/lib/host/commands/chrome-refresh.js +37 -0
  26. package/lib/host/commands/chrome-refresh.js.map +1 -0
  27. package/lib/host/commands/index.d.ts +26 -0
  28. package/lib/host/commands/index.d.ts.map +1 -0
  29. package/lib/host/commands/index.js +41 -0
  30. package/lib/host/commands/index.js.map +1 -0
  31. package/lib/host/constants.d.ts +306 -0
  32. package/lib/host/constants.d.ts.map +1 -0
  33. package/lib/host/constants.js +319 -0
  34. package/lib/host/constants.js.map +1 -0
  35. package/lib/host/controller/_trace.d.ts +3 -0
  36. package/lib/host/controller/_trace.d.ts.map +1 -0
  37. package/lib/host/controller/_trace.js +14 -0
  38. package/lib/host/controller/_trace.js.map +1 -0
  39. package/lib/host/controller/bridge-run.d.ts +40 -0
  40. package/lib/host/controller/bridge-run.d.ts.map +1 -0
  41. package/lib/host/controller/bridge-run.js +85 -0
  42. package/lib/host/controller/bridge-run.js.map +1 -0
  43. package/lib/host/controller/captures.d.ts +15 -0
  44. package/lib/host/controller/captures.d.ts.map +1 -0
  45. package/lib/host/controller/captures.js +85 -0
  46. package/lib/host/controller/captures.js.map +1 -0
  47. package/lib/host/controller/commits.d.ts +4 -0
  48. package/lib/host/controller/commits.d.ts.map +1 -0
  49. package/lib/host/controller/commits.js +131 -0
  50. package/lib/host/controller/commits.js.map +1 -0
  51. package/lib/host/controller/connections.d.ts +38 -0
  52. package/lib/host/controller/connections.d.ts.map +1 -0
  53. package/lib/host/controller/connections.js +71 -0
  54. package/lib/host/controller/connections.js.map +1 -0
  55. package/lib/host/controller/controller.d.ts +35 -0
  56. package/lib/host/controller/controller.d.ts.map +1 -0
  57. package/lib/host/controller/controller.js +234 -0
  58. package/lib/host/controller/controller.js.map +1 -0
  59. package/lib/host/controller/index.d.ts +30 -0
  60. package/lib/host/controller/index.d.ts.map +1 -0
  61. package/lib/host/controller/index.js +27 -0
  62. package/lib/host/controller/index.js.map +1 -0
  63. package/lib/host/controller/notes.d.ts +26 -0
  64. package/lib/host/controller/notes.d.ts.map +1 -0
  65. package/lib/host/controller/notes.js +44 -0
  66. package/lib/host/controller/notes.js.map +1 -0
  67. package/lib/host/controller/persist.d.ts +16 -0
  68. package/lib/host/controller/persist.d.ts.map +1 -0
  69. package/lib/host/controller/persist.js +98 -0
  70. package/lib/host/controller/persist.js.map +1 -0
  71. package/lib/host/controller/publish.d.ts +4 -0
  72. package/lib/host/controller/publish.d.ts.map +1 -0
  73. package/lib/host/controller/publish.js +56 -0
  74. package/lib/host/controller/publish.js.map +1 -0
  75. package/lib/host/controller/runs.d.ts +80 -0
  76. package/lib/host/controller/runs.d.ts.map +1 -0
  77. package/lib/host/controller/runs.js +394 -0
  78. package/lib/host/controller/runs.js.map +1 -0
  79. package/lib/host/controller/state.d.ts +172 -0
  80. package/lib/host/controller/state.d.ts.map +1 -0
  81. package/lib/host/controller/state.js +2 -0
  82. package/lib/host/controller/state.js.map +1 -0
  83. package/lib/host/controller/windows-run.d.ts +17 -0
  84. package/lib/host/controller/windows-run.d.ts.map +1 -0
  85. package/lib/host/controller/windows-run.js +109 -0
  86. package/lib/host/controller/windows-run.js.map +1 -0
  87. package/lib/host/discovery/candidates.d.ts +47 -0
  88. package/lib/host/discovery/candidates.d.ts.map +1 -0
  89. package/lib/host/discovery/candidates.js +68 -0
  90. package/lib/host/discovery/candidates.js.map +1 -0
  91. package/lib/host/discovery/index.d.ts +19 -0
  92. package/lib/host/discovery/index.d.ts.map +1 -0
  93. package/lib/host/discovery/index.js +19 -0
  94. package/lib/host/discovery/index.js.map +1 -0
  95. package/lib/host/discovery/probe.d.ts +29 -0
  96. package/lib/host/discovery/probe.d.ts.map +1 -0
  97. package/lib/host/discovery/probe.js +155 -0
  98. package/lib/host/discovery/probe.js.map +1 -0
  99. package/lib/host/discovery/scan.d.ts +72 -0
  100. package/lib/host/discovery/scan.d.ts.map +1 -0
  101. package/lib/host/discovery/scan.js +195 -0
  102. package/lib/host/discovery/scan.js.map +1 -0
  103. package/lib/host/flags.d.ts +65 -0
  104. package/lib/host/flags.d.ts.map +1 -0
  105. package/lib/host/flags.js +186 -0
  106. package/lib/host/flags.js.map +1 -0
  107. package/lib/host/kind.d.ts +45 -0
  108. package/lib/host/kind.d.ts.map +1 -0
  109. package/lib/host/kind.js +251 -0
  110. package/lib/host/kind.js.map +1 -0
  111. package/lib/host/manager.d.ts +21 -0
  112. package/lib/host/manager.d.ts.map +1 -0
  113. package/lib/host/manager.js +113 -0
  114. package/lib/host/manager.js.map +1 -0
  115. package/lib/host/migration.d.ts +26 -0
  116. package/lib/host/migration.d.ts.map +1 -0
  117. package/lib/host/migration.js +127 -0
  118. package/lib/host/migration.js.map +1 -0
  119. package/lib/host/platform.d.ts +38 -0
  120. package/lib/host/platform.d.ts.map +1 -0
  121. package/lib/host/platform.js +64 -0
  122. package/lib/host/platform.js.map +1 -0
  123. package/lib/host/plugin-meta.d.ts +21 -0
  124. package/lib/host/plugin-meta.d.ts.map +1 -0
  125. package/lib/host/plugin-meta.js +21 -0
  126. package/lib/host/plugin-meta.js.map +1 -0
  127. package/lib/host/schema.d.ts +361 -0
  128. package/lib/host/schema.d.ts.map +1 -0
  129. package/lib/host/schema.js +196 -0
  130. package/lib/host/schema.js.map +1 -0
  131. package/lib/host/settings.d.ts +28 -0
  132. package/lib/host/settings.d.ts.map +1 -0
  133. package/lib/host/settings.js +91 -0
  134. package/lib/host/settings.js.map +1 -0
  135. package/lib/host/status.d.ts +25 -0
  136. package/lib/host/status.d.ts.map +1 -0
  137. package/lib/host/status.js +45 -0
  138. package/lib/host/status.js.map +1 -0
  139. package/lib/host/tool.d.ts +65 -0
  140. package/lib/host/tool.d.ts.map +1 -0
  141. package/lib/host/tool.js +52 -0
  142. package/lib/host/tool.js.map +1 -0
  143. package/lib/host/types/config.d.ts +101 -0
  144. package/lib/host/types/config.d.ts.map +1 -0
  145. package/lib/host/types/config.js +2 -0
  146. package/lib/host/types/config.js.map +1 -0
  147. package/lib/host/types/executable.d.ts +35 -0
  148. package/lib/host/types/executable.d.ts.map +1 -0
  149. package/lib/host/types/executable.js +9 -0
  150. package/lib/host/types/executable.js.map +1 -0
  151. package/lib/host/types/index.d.ts +24 -0
  152. package/lib/host/types/index.d.ts.map +1 -0
  153. package/lib/host/types/index.js +2 -0
  154. package/lib/host/types/index.js.map +1 -0
  155. package/lib/host/types/plugin.d.ts +38 -0
  156. package/lib/host/types/plugin.d.ts.map +1 -0
  157. package/lib/host/types/plugin.js +10 -0
  158. package/lib/host/types/plugin.js.map +1 -0
  159. package/lib/host/types/services.d.ts +26 -0
  160. package/lib/host/types/services.d.ts.map +1 -0
  161. package/lib/host/types/services.js +9 -0
  162. package/lib/host/types/services.js.map +1 -0
  163. package/lib/host/types/settings.d.ts +180 -0
  164. package/lib/host/types/settings.d.ts.map +1 -0
  165. package/lib/host/types/settings.js +2 -0
  166. package/lib/host/types/settings.js.map +1 -0
  167. package/lib/host/types/stderr.d.ts +51 -0
  168. package/lib/host/types/stderr.d.ts.map +1 -0
  169. package/lib/host/types/stderr.js +9 -0
  170. package/lib/host/types/stderr.js.map +1 -0
  171. package/lib/host/types/windows.d.ts +39 -0
  172. package/lib/host/types/windows.d.ts.map +1 -0
  173. package/lib/host/types/windows.js +9 -0
  174. package/lib/host/types/windows.js.map +1 -0
  175. package/lib/host/values.d.ts +204 -0
  176. package/lib/host/values.d.ts.map +1 -0
  177. package/lib/host/values.js +404 -0
  178. package/lib/host/values.js.map +1 -0
  179. package/lib/host/windows/connect-flags.d.ts +27 -0
  180. package/lib/host/windows/connect-flags.d.ts.map +1 -0
  181. package/lib/host/windows/connect-flags.js +97 -0
  182. package/lib/host/windows/connect-flags.js.map +1 -0
  183. package/lib/host/windows/index.d.ts +31 -0
  184. package/lib/host/windows/index.d.ts.map +1 -0
  185. package/lib/host/windows/index.js +30 -0
  186. package/lib/host/windows/index.js.map +1 -0
  187. package/lib/host/windows/landing.d.ts +47 -0
  188. package/lib/host/windows/landing.d.ts.map +1 -0
  189. package/lib/host/windows/landing.js +174 -0
  190. package/lib/host/windows/landing.js.map +1 -0
  191. package/lib/host/windows/link.d.ts +11 -0
  192. package/lib/host/windows/link.d.ts.map +1 -0
  193. package/lib/host/windows/link.js +52 -0
  194. package/lib/host/windows/link.js.map +1 -0
  195. package/lib/host/windows/paths.d.ts +64 -0
  196. package/lib/host/windows/paths.d.ts.map +1 -0
  197. package/lib/host/windows/paths.js +148 -0
  198. package/lib/host/windows/paths.js.map +1 -0
  199. package/lib/host/windows/run.d.ts +74 -0
  200. package/lib/host/windows/run.d.ts.map +1 -0
  201. package/lib/host/windows/run.js +211 -0
  202. package/lib/host/windows/run.js.map +1 -0
  203. package/lib/host/windows/spawn.d.ts +22 -0
  204. package/lib/host/windows/spawn.d.ts.map +1 -0
  205. package/lib/host/windows/spawn.js +73 -0
  206. package/lib/host/windows/spawn.js.map +1 -0
  207. package/lib/index.d.ts +102 -0
  208. package/lib/index.d.ts.map +1 -0
  209. package/lib/index.js +102 -636
  210. package/lib/index.js.map +1 -0
  211. package/locale/en.json +6 -0
  212. package/locale/zh.json +6 -0
  213. package/package.json +57 -14
  214. package/cordis.patch.yml +0 -5
package/lib/client.js CHANGED
@@ -4,582 +4,3019 @@ 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
- var react = require("react");
9
- var reactJsx = require("react/jsx-runtime");
10
- var clientStore = require("@deepseek-ai/dsh-client-store");
11
- // NOTE: no UI primitives required — the extra flags editor is a plain
12
- // textarea writing through the controller (props.saveFlags), not the
13
- // shell Menu primitive the reference used for its profile picker.
36
+ // src/client/index.ts
37
+ var index_exports = {};
38
+ __export(index_exports, {
39
+ LIVE_KEYS: () => LIVE_KEYS,
40
+ RUN_SETTLE_TIMEOUT_MS: () => RUN_SETTLE_TIMEOUT_MS2,
41
+ apply: () => apply,
42
+ card: () => card,
43
+ cardActions: () => cardActions,
44
+ cardState: () => cardState,
45
+ inject: () => inject
46
+ });
47
+ module.exports = __toCommonJS(index_exports);
48
+
49
+ // node_modules/@comecaramelos/dsh-ancesters/lib/client.esm.js
50
+ var react4 = __toESM(require("react"), 1);
51
+ var import_jsx_runtime = require("react/jsx-runtime");
52
+ var react = __toESM(require("react"), 1);
53
+ var import_jsx_runtime2 = require("react/jsx-runtime");
54
+ var react2 = __toESM(require("react"), 1);
55
+ var import_jsx_runtime3 = require("react/jsx-runtime");
56
+ var import_jsx_runtime4 = require("react/jsx-runtime");
57
+ var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
58
+ var import_jsx_runtime5 = require("react/jsx-runtime");
59
+ var import_jsx_runtime6 = require("react/jsx-runtime");
60
+ var import_dsh_client_ui_primitives2 = require("@deepseek-ai/dsh-client-ui-primitives");
61
+ var import_jsx_runtime7 = require("react/jsx-runtime");
62
+ var react3 = __toESM(require("react"), 1);
63
+ var import_jsx_runtime8 = require("react/jsx-runtime");
64
+ var import_dsh_client_ui_primitives3 = require("@deepseek-ai/dsh-client-ui-primitives");
65
+ var import_jsx_runtime9 = require("react/jsx-runtime");
66
+ var import_dsh_client_ui_primitives4 = require("@deepseek-ai/dsh-client-ui-primitives");
67
+ var import_dsh_client_store = require("@deepseek-ai/dsh-client-store");
68
+ function rowConfigKey(pkgName, entryId) {
69
+ if (typeof pkgName !== "string" || pkgName === "") {
70
+ throw new Error("dsh-ancesters: rowConfigKey(pkg, entryId) requires a non-empty package name");
71
+ }
72
+ if (typeof entryId !== "string" || entryId === "") {
73
+ throw new Error("dsh-ancesters: rowConfigKey(pkg, entryId) requires a non-empty entry id");
74
+ }
75
+ return `${pkgName}#${entryId}`;
76
+ }
77
+ var cssText = '.qox3mn_body{flex-direction:column;gap:10px;display:flex}.qox3mn_field{align-items:flex-start;gap:4px;display:flex;flex-direction:column;width:100%}.qox3mn_fieldRow{align-items:center;gap:8px;display:flex;width:100%}.qox3mn_fieldTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px;flex:1;min-width:0}.qox3mn_fieldDesc{color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:400;line-height:18px;width:100%}.qox3mn_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}.qox3mn_selector:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.qox3mn_selector:disabled{cursor:default}.qox3mn_selectorWrap{display:flex}.qox3mn_pillChevron{flex:none}.qox3mn_row{display:flex;gap:10px;align-items:center}.qox3mn_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}.qox3mn_button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.qox3mn_button:disabled{cursor:not-allowed;opacity:.4}.qox3mn_button:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.qox3mn_readOnly{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px}.qox3mn_error{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px;line-height:1.5}.qox3mn_dot{box-sizing:border-box;corner-shape:round;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.qox3mn_dotError{background:var(--dsw-alias-state-error-primary)}.qox3mn_badge{color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-layer-1);border-radius:10px;padding:1px 8px;font-size:11px;line-height:16px;display:inline-flex;align-items:center;flex:none}.qox3mn_empty{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px}.qox3mn_catalog{flex-direction:column;align-items:stretch;gap:10px;display:flex}.qox3mn_catalog+.qox3mn_catalog{border-top:.5px solid var(--dsw-alias-border-l2);padding-top:12px}.qox3mn_catalog>.qox3mn_linkButton{align-self:flex-start}.qox3mn_catalog>.qox3mn_linkButtonRow{align-self:flex-start;display:flex;gap:8px;align-items:center}.qox3mn_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}.qox3mn_linkButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}.qox3mn_linkButton:disabled{opacity:.4;cursor:default}.qox3mn_linkButton:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.qox3mn_rowList{flex-direction:column;align-items:stretch;gap:6px;display:flex}.qox3mn_rowList>button{align-self:flex-start}.qox3mn_catalogEntry{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-module-platform);border-radius:10px;padding:6px}.qox3mn_catalogRow{grid-template-columns:minmax(0,1.4fr) minmax(0,1fr) auto;align-items:center;gap:6px;display:grid}.qox3mn_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%}.qox3mn_catalogInput::placeholder{color:var(--dsw-alias-label-dimmed)}.qox3mn_catalogInput:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-1px}.qox3mn_catalogInput:disabled{opacity:.6;cursor:default}.qox3mn_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}.qox3mn_iconButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.qox3mn_iconButton:disabled{opacity:.4;cursor:default}.qox3mn_iconButton:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.qox3mn_iconButtonDanger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-state-error-primary)}.qox3mn_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}.qox3mn_addButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.qox3mn_addButton:disabled{opacity:.4;cursor:default}.qox3mn_disclosure{flex-direction:column;gap:10px;display:flex}.qox3mn_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}.qox3mn_disclosureSummary::-webkit-details-marker{display:none}.qox3mn_disclosureSummary:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px;border-radius:6px}.qox3mn_disclosureLabel:before{content:"\\25b8";display:inline-block;margin-right:6px;transition:transform .16s}.qox3mn_disclosure[open] .qox3mn_disclosureLabel:before{transform:rotate(90deg)}.qox3mn_disclosureCount{color:var(--dsw-alias-label-tertiary)}.qox3mn_disclosureBody{flex-direction:column;gap:10px;display:flex}.qox3mn_disclosureBody>.qox3mn_linkButton{align-self:flex-start}.qox3mn_disclosureBody>.qox3mn_linkButtonRow{align-self:flex-start;display:flex;gap:8px;align-items:center}.qox3mn_toggleLabel{align-items:center;display:inline-flex;flex:none}.qox3mn_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}.qox3mn_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}.qox3mn_dialogHeader{align-items:center;gap:8px;padding:14px 16px 0;display:flex}.qox3mn_dialogTitle{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:22px;flex:1;min-width:0;margin:0}.qox3mn_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}.qox3mn_dialogClose:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.qox3mn_dialogClose:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.qox3mn_dialogDescription{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;margin:0;padding:0 16px}.qox3mn_dialogBody{flex-direction:column;flex:1;gap:8px;min-height:0;padding:0 16px;display:flex}.qox3mn_candidateToolbar{align-items:center;gap:8px;display:flex}.qox3mn_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}.qox3mn_candidateSearch::placeholder{color:var(--dsw-alias-label-dimmed)}.qox3mn_candidateSearch:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-1px}.qox3mn_candidateList{flex-direction:column;gap:2px;margin:0;padding:0 0 10px;list-style:none;overflow-y:auto;display:flex}.qox3mn_candidate{border-radius:8px;padding:4px 6px}.qox3mn_candidate:hover{background:var(--dsw-alias-interactive-bg-hover)}.qox3mn_candidateLabel{align-items:center;gap:8px;min-width:0;display:flex}.qox3mn_candidateLabel input[type=checkbox]{accent-color:var(--dsw-alias-brand-primary);flex:none;margin:0}.qox3mn_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}.qox3mn_candidateNote{color:var(--dsw-alias-label-tertiary);flex:0 1 auto;min-width:0;overflow-wrap:anywhere;font-size:11px;line-height:16px}.qox3mn_dialogFooter{border-top:.5px solid var(--dsw-alias-border-l2);align-items:center;justify-content:flex-end;gap:8px;padding:12px 16px;display:flex}.qox3mn_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}.qox3mn_dialogButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.qox3mn_dialogButton:disabled{cursor:not-allowed;opacity:.4}.qox3mn_dialogButton:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.qox3mn_dialogButtonPrimary{color:var(--dsw-alias-label-primary);font-weight:600}.qox3mn_selectorLabel{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.qox3mn_result{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.qox3mn_resultValue{color:var(--dsw-alias-label-secondary);margin-left:4px}.qox3mn_resultAge{color:var(--dsw-alias-label-dimmed);margin-left:4px}.qox3mn_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}.qox3mn_noticeTitle{color:var(--dsw-alias-label-primary);font-size:12px;font-weight:600;line-height:18px}.qox3mn_noticeBody{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.qox3mn_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}.qox3mn_docsLinkLabel{text-decoration:none}.qox3mn_docsLink:hover{color:var(--dsw-alias-brand-primary)}.qox3mn_docsLink:hover .qox3mn_docsLinkLabel{text-decoration:underline}.qox3mn_docsLink:hover .qox3mn_docsIcon{color:var(--dsw-alias-brand-primary)}.qox3mn_docsIcon{color:var(--dsw-alias-label-tertiary);flex:none}.qox3mn_docsLink:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px;border-radius:4px}.qox3mn_catalogRowSingle{grid-template-columns:minmax(0,1fr) auto}.qox3mn_catalogInputMono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.qox3mn_footer{border-top:.5px solid var(--dsw-alias-border-l2);align-items:center;justify-content:flex-end;gap:8px;padding:12px 0 0;display:flex}.qox3mn_footerError{color:var(--dsw-alias-label-error);text-align:right;min-width:0;flex:1;margin:0;font-size:12px;line-height:1.5}.qox3mn_footerButton{border:.5px solid var(--dsw-alias-border-l3);border-radius:8px;cursor:pointer;color:var(--dsw-alias-label-secondary);background:transparent;padding:5px 14px;font-size:13px;line-height:1.5}.qox3mn_footerButton:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.qox3mn_footerButton:disabled{cursor:default;opacity:.4}.qox3mn_footerButton:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.qox3mn_footerButtonPrimary{color:var(--dsw-alias-bg-layer-3);font-weight:600;background:var(--dsw-alias-label-primary);border-color:transparent}.qox3mn_footerButtonPrimary:hover:not(:disabled){color:var(--dsw-alias-bg-layer-3);background:var(--dsw-alias-label-secondary);border-color:transparent}.qox3mn_liveLine{align-items:center;gap:8px;display:flex;padding:6px 10px;border:.5px solid var(--dsw-alias-border-l3);border-radius:8px;background:var(--dsw-alias-bg-layer-1)}.qox3mn_liveLine:hover{border-color:var(--dsw-alias-border-l4)}.qox3mn_liveText{flex:1;min-width:0;display:flex;align-items:center;gap:8px}.qox3mn_liveName{color:var(--dsw-alias-label-primary);font-size:14px;line-height:20px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.qox3mn_liveState{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.qox3mn_tools{flex:1 1 auto;min-width:0;display:flex;flex-wrap:wrap;gap:4px;color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}.qox3mn_tools span{padding:0 6px;border:.5px solid var(--dsw-alias-border-l3);border-radius:6px;background:var(--dsw-alias-bg-module-platform)}.qox3mn_control{box-sizing:border-box;width:100%;min-width:0;height:28px;padding:0 10px;border:.5px solid var(--dsw-alias-border-l4);border-radius:8px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;font-size:14px;line-height:22px}.qox3mn_control::placeholder{color:var(--dsw-alias-label-dimmed)}.qox3mn_control:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-1px}.qox3mn_control:disabled{opacity:.6;cursor:default}.qox3mn_textarea{box-sizing:border-box;width:100%;min-width:0;min-height:64px;padding:6px 10px;border:.5px solid var(--dsw-alias-border-l4);border-radius:8px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:18px;resize:vertical}.qox3mn_textarea:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-1px}.qox3mn_textarea:disabled{opacity:.6;cursor:default}.qox3mn_select{box-sizing:border-box;height:28px;min-width:0;padding:0 8px;border:.5px solid var(--dsw-alias-border-l4);border-radius:8px;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;line-height:22px}.qox3mn_select:disabled{opacity:.6;cursor:default}.qox3mn_checkbox{margin:0;accent-color:var(--dsw-alias-brand-primary)}.qox3mn_controlField{align-items:flex-start;gap:4px;display:flex;flex-direction:column;width:100%}.qox3mn_controlTitle{color:var(--dsw-alias-label-primary);font-size:12px;line-height:18px}.qox3mn_controlField .qox3mn_control,.qox3mn_controlField .qox3mn_textarea{margin-top:2px}.qox3mn_grid{display:grid;grid-template-columns:minmax(96px,20%) minmax(0,1fr);gap:6px 10px;align-items:center;width:100%}.qox3mn_grid>label{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.qox3mn_editor{gap:10px;display:flex;flex-direction:column;padding:10px;border:.5px solid var(--dsw-alias-border-l3);border-radius:10px;background:var(--dsw-alias-bg-layer-1)}.qox3mn_editorTitle{color:var(--dsw-alias-label-primary);font-size:14px;line-height:20px}.qox3mn_editorHint{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.qox3mn_editorActions{align-items:center;gap:8px;display:flex;justify-content:flex-end}.qox3mn_transport{color:var(--dsw-alias-label-tertiary);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:18px;word-break:break-all}.qox3mn_summary{color:var(--dsw-alias-label-secondary);font-size:13px;line-height:20px;display:flex;align-items:center;gap:6px}.qox3mn_summaryItem{color:var(--dsw-alias-label-primary);font-size:13px;line-height:20px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.qox3mn_summaryNone{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px}.qox3mn_summaryBroken{color:var(--dsw-alias-state-error-primary);font-size:13px;line-height:20px}.qox3mn_toastRoot{z-index:2147483000;flex-direction:column;gap:8px;display:flex;position:fixed;right:16px;bottom:16px;max-width:min(360px,50vw)}.qox3mn_toast{border:.5px solid var(--dsw-alias-border-l3);border-radius:10px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);padding:8px 10px;font-size:12px;line-height:18px;box-shadow:0 4px 12px #0000001f;overflow-wrap:anywhere}.qox3mn_toast[data-notice-kind=error]{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-label-primary)}.qox3mn_toast[data-notice-kind=stall]{border-color:var(--dsw-alias-border-l4);color:var(--dsw-alias-label-tertiary)}.qox3mn_composerPills{display:inline-flex;gap:4px}.qox3mn_composerPillRoot{display:flex}.qox3mn_composerPill{min-width:0;max-width:220px;height:28px;font:inherit;font-size:13px;font-weight:500;line-height:20px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:transparent;border:none;border-radius:24px;outline:none;align-items:center;gap:4px;padding:0 4px 0 8px;display:inline-flex}.qox3mn_composerPill:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.qox3mn_composerPill:disabled{cursor:default}.qox3mn_composerPillIcon{flex:none;display:inline-flex;color:var(--dsw-alias-label-secondary)}.qox3mn_composerPillLabel{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.qox3mn_composerPillChevron{color:var(--dsw-alias-label-caption);flex:none;transition:transform .12s;display:inline-flex}.qox3mn_composerPillChevronOpen{transform:rotate(180deg)}\n';
78
+ var shared_default = { "body": "qox3mn_body", "field": "qox3mn_field", "fieldRow": "qox3mn_fieldRow", "fieldTitle": "qox3mn_fieldTitle", "fieldDesc": "qox3mn_fieldDesc", "selector": "qox3mn_selector", "selectorWrap": "qox3mn_selectorWrap", "pillChevron": "qox3mn_pillChevron", "row": "qox3mn_row", "button": "qox3mn_button", "readOnly": "qox3mn_readOnly", "error": "qox3mn_error", "dot": "qox3mn_dot", "dotError": "qox3mn_dotError", "badge": "qox3mn_badge", "empty": "qox3mn_empty", "catalog": "qox3mn_catalog", "linkButton": "qox3mn_linkButton", "linkButtonRow": "qox3mn_linkButtonRow", "rowList": "qox3mn_rowList", "catalogEntry": "qox3mn_catalogEntry", "catalogRow": "qox3mn_catalogRow", "catalogInput": "qox3mn_catalogInput", "iconButton": "qox3mn_iconButton", "iconButtonDanger": "qox3mn_iconButtonDanger", "addButton": "qox3mn_addButton", "disclosure": "qox3mn_disclosure", "disclosureSummary": "qox3mn_disclosureSummary", "disclosureLabel": "qox3mn_disclosureLabel", "disclosureCount": "qox3mn_disclosureCount", "disclosureBody": "qox3mn_disclosureBody", "toggleLabel": "qox3mn_toggleLabel", "fetchDialog": "qox3mn_fetchDialog", "dialogContent": "qox3mn_dialogContent", "dialogHeader": "qox3mn_dialogHeader", "dialogTitle": "qox3mn_dialogTitle", "dialogClose": "qox3mn_dialogClose", "dialogDescription": "qox3mn_dialogDescription", "dialogBody": "qox3mn_dialogBody", "candidateToolbar": "qox3mn_candidateToolbar", "candidateSearch": "qox3mn_candidateSearch", "candidateList": "qox3mn_candidateList", "candidate": "qox3mn_candidate", "candidateLabel": "qox3mn_candidateLabel", "candidateId": "qox3mn_candidateId", "candidateNote": "qox3mn_candidateNote", "dialogFooter": "qox3mn_dialogFooter", "dialogButton": "qox3mn_dialogButton", "dialogButtonPrimary": "qox3mn_dialogButtonPrimary", "selectorLabel": "qox3mn_selectorLabel", "result": "qox3mn_result", "resultValue": "qox3mn_resultValue", "resultAge": "qox3mn_resultAge", "notice": "qox3mn_notice", "noticeTitle": "qox3mn_noticeTitle", "noticeBody": "qox3mn_noticeBody", "docsLink": "qox3mn_docsLink", "docsLinkLabel": "qox3mn_docsLinkLabel", "docsIcon": "qox3mn_docsIcon", "catalogRowSingle": "qox3mn_catalogRowSingle", "catalogInputMono": "qox3mn_catalogInputMono", "footer": "qox3mn_footer", "footerError": "qox3mn_footerError", "footerButton": "qox3mn_footerButton", "footerButtonPrimary": "qox3mn_footerButtonPrimary", "liveLine": "qox3mn_liveLine", "liveText": "qox3mn_liveText", "liveName": "qox3mn_liveName", "liveState": "qox3mn_liveState", "tools": "qox3mn_tools", "control": "qox3mn_control", "textarea": "qox3mn_textarea", "select": "qox3mn_select", "checkbox": "qox3mn_checkbox", "controlField": "qox3mn_controlField", "controlTitle": "qox3mn_controlTitle", "grid": "qox3mn_grid", "editor": "qox3mn_editor", "editorTitle": "qox3mn_editorTitle", "editorHint": "qox3mn_editorHint", "editorActions": "qox3mn_editorActions", "transport": "qox3mn_transport", "summary": "qox3mn_summary", "summaryItem": "qox3mn_summaryItem", "summaryNone": "qox3mn_summaryNone", "summaryBroken": "qox3mn_summaryBroken", "toastRoot": "qox3mn_toastRoot", "toast": "qox3mn_toast", "composerPills": "qox3mn_composerPills", "composerPillRoot": "qox3mn_composerPillRoot", "composerPill": "qox3mn_composerPill", "composerPillIcon": "qox3mn_composerPillIcon", "composerPillLabel": "qox3mn_composerPillLabel", "composerPillChevron": "qox3mn_composerPillChevron", "composerPillChevronOpen": "qox3mn_composerPillChevronOpen" };
79
+ 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";
80
+ 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";
81
+ var CLOSE_PATHS = [
82
+ "M14.1168 13.197L13.197 14.1167L1.8833 2.80303L2.80309 1.88324L14.1168 13.197Z",
83
+ "M13.197 1.88326L14.1168 2.80305L2.80309 14.1168L1.8833 13.197L13.197 1.88326Z"
84
+ ];
85
+ var DOCS_PATHS = [
86
+ "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",
87
+ "M15 3h6v6",
88
+ "M10 14 21 3"
89
+ ];
90
+ function RowsBlock(props) {
91
+ const served = Array.isArray(props.values) ? props.values : [];
92
+ const writable = props.writable === true;
93
+ const busy = props.busy === true;
94
+ const labels = Array.isArray(props.labels) ? props.labels : [];
95
+ const placeholders = Array.isArray(props.placeholders) ? props.placeholders : [];
96
+ const columns = Math.max(1, labels.length);
97
+ const blankRow = [];
98
+ for (let blank = 0; blank < columns; blank++) blankRow.push("");
99
+ const servedKey = JSON.stringify(served);
100
+ const draftState = react.useState({ key: "", rows: [] });
101
+ const draft = draftState[0];
102
+ const setDraft = draftState[1];
103
+ const rows = draft.key === servedKey ? draft.rows : served.map((row) => Array.from(row));
104
+ const write = (next) => {
105
+ const saved = props.save === void 0 ? void 0 : props.save(next);
106
+ return Promise.resolve(saved === void 0 ? void 0 : saved).then(
107
+ () => {
108
+ setDraft({ key: "", rows: [] });
109
+ },
110
+ (failure) => {
111
+ throw failure;
112
+ }
113
+ );
114
+ };
115
+ const setCell = (at, column, value) => {
116
+ const next = rows.map((row, index) => index === at ? row.map((cell, cellIndex) => cellIndex === column ? value : cell) : row.slice());
117
+ setDraft({ key: servedKey, rows: next });
118
+ };
119
+ const commit = () => {
120
+ const cleaned = [];
121
+ for (const row of rows) {
122
+ const cells = row.map((cell) => String(cell ?? "").trim());
123
+ if (cells.some((cell) => cell !== "")) cleaned.push(cells);
124
+ }
125
+ return write(cleaned);
126
+ };
127
+ const remove = (at) => {
128
+ return write(rows.filter((_row, index) => index !== at));
129
+ };
130
+ const add = () => {
131
+ setDraft({ key: servedKey, rows: rows.concat([blankRow.slice()]) });
132
+ };
133
+ const rowClass = props.rowClassName === void 0 || props.rowClassName === shared_default.catalogRow ? shared_default.catalogRow : shared_default.catalogRow + " " + props.rowClassName;
134
+ const inputClass = props.inputClassName === void 0 ? shared_default.catalogInput : props.inputClassName;
135
+ const list = (0, import_jsx_runtime2.jsxs)("div", {
136
+ className: shared_default.rowList,
137
+ children: [
138
+ served.length === 0 && props.emptyLabel !== void 0 ? (0, import_jsx_runtime2.jsx)("div", { className: shared_default.empty, children: props.emptyLabel }, "empty") : null,
139
+ rows.map(
140
+ (row, at) => (0, import_jsx_runtime2.jsx)("div", {
141
+ className: shared_default.catalogEntry,
142
+ children: (0, import_jsx_runtime2.jsxs)("div", {
143
+ className: rowClass,
144
+ children: [
145
+ row.map(
146
+ (cell, column) => (0, import_jsx_runtime2.jsx)("input", {
147
+ className: inputClass,
148
+ type: "text",
149
+ value: cell === void 0 ? "" : cell,
150
+ placeholder: placeholders[column] === void 0 ? "" : placeholders[column],
151
+ disabled: !writable || busy,
152
+ onChange: (event) => {
153
+ setCell(at, column, typeof event?.target?.value === "string" ? event.target.value : "");
154
+ },
155
+ onBlur: () => {
156
+ if (writable && !busy) void commit();
157
+ },
158
+ onKeyDown: (event) => {
159
+ if (event.key === "Enter") {
160
+ if (typeof event.preventDefault === "function") event.preventDefault();
161
+ if (writable && !busy) void commit();
162
+ }
163
+ }
164
+ }, column)
165
+ ),
166
+ (0, import_jsx_runtime2.jsx)("button", {
167
+ type: "button",
168
+ className: shared_default.iconButton + " " + shared_default.iconButtonDanger,
169
+ disabled: !writable || busy,
170
+ title: props.removeLabel === void 0 ? "" : props.removeLabel,
171
+ onClick: () => {
172
+ if (writable && !busy) void remove(at);
173
+ },
174
+ children: (0, import_jsx_runtime2.jsx)("svg", {
175
+ width: 14,
176
+ height: 14,
177
+ viewBox: "0 0 16 16",
178
+ fill: "none",
179
+ stroke: "currentColor",
180
+ children: (0, import_jsx_runtime2.jsx)("path", { d: TRASH_PATH })
181
+ })
182
+ }, "remove")
183
+ ]
184
+ })
185
+ }, at)
186
+ ),
187
+ (0, import_jsx_runtime2.jsx)("button", {
188
+ type: "button",
189
+ className: shared_default.linkButton,
190
+ disabled: !writable || busy,
191
+ title: props.addTitle === void 0 ? "" : props.addTitle,
192
+ onClick: () => {
193
+ if (!writable || busy) return;
194
+ add();
195
+ },
196
+ children: props.addLabel === void 0 ? "" : props.addLabel
197
+ }, "add"),
198
+ props.renderAction === void 0 ? null : props.renderAction(rows)
199
+ ]
200
+ });
201
+ if (props.disclosure !== true) return list;
202
+ return (0, import_jsx_runtime2.jsxs)("details", {
203
+ className: shared_default.disclosure,
204
+ children: [
205
+ (0, import_jsx_runtime2.jsxs)("summary", {
206
+ className: shared_default.disclosureSummary,
207
+ children: [
208
+ (0, import_jsx_runtime2.jsx)("span", { className: shared_default.disclosureLabel, children: props.disclosureLabel === void 0 ? "" : props.disclosureLabel }),
209
+ (0, import_jsx_runtime2.jsx)("span", {
210
+ className: shared_default.disclosureCount,
211
+ children: (props.disclosureCountLabel === void 0 ? String(rows.length) : props.disclosureCountLabel).replace("{count}", String(rows.length))
212
+ })
213
+ ]
214
+ }),
215
+ (0, import_jsx_runtime2.jsx)("div", { className: shared_default.disclosureBody, children: list })
216
+ ]
217
+ });
218
+ }
219
+ function DialogBlock(props) {
220
+ var t = props.t;
221
+ var candidates = Array.from(props.candidates);
222
+ var savedIds = Array.from(props.savedIds);
223
+ var savedSet = new Set(savedIds);
224
+ var initialChecked = candidates.filter((candidate) => savedSet.has(candidate.id)).map((candidate) => candidate.id);
225
+ var servedKey = JSON.stringify(candidates) + "|" + JSON.stringify(savedIds);
226
+ var draftState = react2.useState({ key: "", query: "", checked: [] });
227
+ var draft = draftState[0];
228
+ var setDraft = draftState[1];
229
+ var dirty = draft.key !== servedKey;
230
+ var query = dirty ? "" : draft.query;
231
+ var checked = dirty ? initialChecked : draft.checked;
232
+ var checkedSet = new Set(checked);
233
+ var setQuery = (value) => {
234
+ setDraft({ key: servedKey, query: value, checked: Array.from(checkedSet) });
235
+ };
236
+ var setChecked = (make) => {
237
+ setDraft({ key: servedKey, query, checked: make(checked) });
238
+ };
239
+ var needle = query.trim().toLowerCase();
240
+ var shown = needle === "" ? candidates : candidates.filter((candidate) => candidate.id.toLowerCase().indexOf(needle) !== -1);
241
+ var addable = shown.filter((candidate) => !savedSet.has(candidate.id)).map((candidate) => candidate.id);
242
+ var allChecked = addable.length > 0 && addable.every((id) => checkedSet.has(id));
243
+ var toggle = (id, value) => {
244
+ setChecked((current) => value ? current.concat([id]) : current.filter((entry) => entry !== id));
245
+ };
246
+ return (0, import_jsx_runtime3.jsxs)("div", {
247
+ className: shared_default.fetchDialog,
248
+ role: "dialog",
249
+ "aria-modal": "true",
250
+ "aria-label": props.title,
251
+ children: [
252
+ (0, import_jsx_runtime3.jsxs)("div", {
253
+ className: shared_default.dialogContent,
254
+ children: [
255
+ (0, import_jsx_runtime3.jsxs)("div", {
256
+ className: shared_default.dialogHeader,
257
+ children: [
258
+ (0, import_jsx_runtime3.jsx)("h2", { className: shared_default.dialogTitle, children: props.title }),
259
+ (0, import_jsx_runtime3.jsx)("button", {
260
+ type: "button",
261
+ className: shared_default.dialogClose,
262
+ "aria-label": t("close"),
263
+ onClick: () => props.onClose(),
264
+ children: (0, import_jsx_runtime3.jsx)("svg", {
265
+ width: 14,
266
+ height: 14,
267
+ viewBox: "0 0 16 16",
268
+ fill: "none",
269
+ xmlns: "http://www.w3.org/2000/svg",
270
+ "aria-hidden": "true",
271
+ children: CLOSE_PATHS.map((d, at) => (0, import_jsx_runtime3.jsx)("path", { d, fill: "currentColor" }, at))
272
+ })
273
+ })
274
+ ]
275
+ }),
276
+ (0, import_jsx_runtime3.jsx)("p", { className: shared_default.dialogDescription, children: props.description }),
277
+ (0, import_jsx_runtime3.jsxs)("div", {
278
+ className: shared_default.dialogBody,
279
+ children: [
280
+ (0, import_jsx_runtime3.jsxs)("div", {
281
+ className: shared_default.candidateToolbar,
282
+ children: [
283
+ (0, import_jsx_runtime3.jsx)("input", {
284
+ className: shared_default.candidateSearch,
285
+ type: "search",
286
+ placeholder: props.searchPlaceholder,
287
+ "aria-label": props.searchPlaceholder,
288
+ value: query,
289
+ onChange: (event) => {
290
+ setQuery(event && event.target ? String(event.target.value ?? "") : "");
291
+ }
292
+ }),
293
+ (0, import_jsx_runtime3.jsx)("button", {
294
+ type: "button",
295
+ className: shared_default.linkButton,
296
+ onClick: () => {
297
+ if (allChecked) setChecked(() => []);
298
+ else setChecked((current) => Array.from(new Set(current.concat(addable))));
299
+ },
300
+ children: t("selectAll")
301
+ })
302
+ ]
303
+ }),
304
+ shown.length === 0 ? (0, import_jsx_runtime3.jsx)("p", { className: shared_default.empty, children: t("dialogEmpty") }) : (0, import_jsx_runtime3.jsx)("ul", {
305
+ className: shared_default.candidateList,
306
+ children: shown.map((candidate) => {
307
+ var isSaved = savedSet.has(candidate.id);
308
+ var note = candidate.note !== "" ? candidate.note : isSaved ? t("candidateSaved") : "";
309
+ return (0, import_jsx_runtime3.jsx)("li", {
310
+ className: shared_default.candidate,
311
+ children: (0, import_jsx_runtime3.jsxs)("label", {
312
+ className: shared_default.candidateLabel,
313
+ children: [
314
+ (0, import_jsx_runtime3.jsx)("input", {
315
+ type: "checkbox",
316
+ checked: isSaved ? true : checkedSet.has(candidate.id),
317
+ disabled: isSaved,
318
+ onChange: (event) => {
319
+ toggle(candidate.id, !!(event && event.target && event.target.checked));
320
+ }
321
+ }),
322
+ (0, import_jsx_runtime3.jsx)("span", { className: shared_default.candidateId, children: candidate.id }),
323
+ note === "" ? null : (0, import_jsx_runtime3.jsx)("span", { className: shared_default.candidateNote, children: note })
324
+ ]
325
+ })
326
+ }, candidate.id);
327
+ })
328
+ })
329
+ ]
330
+ }),
331
+ (0, import_jsx_runtime3.jsxs)("div", {
332
+ className: shared_default.dialogFooter,
333
+ children: [
334
+ (0, import_jsx_runtime3.jsx)("button", {
335
+ type: "button",
336
+ className: shared_default.dialogButton,
337
+ onClick: () => props.onClose(),
338
+ children: t("cancel")
339
+ }),
340
+ (0, import_jsx_runtime3.jsx)("button", {
341
+ type: "button",
342
+ className: shared_default.dialogButton + " " + shared_default.dialogButtonPrimary,
343
+ onClick: () => props.onAdd(Array.from(checkedSet)),
344
+ children: t("addSelected")
345
+ })
346
+ ]
347
+ })
348
+ ]
349
+ })
350
+ ]
351
+ });
352
+ }
353
+ function PickerBlock(props) {
354
+ const entries = Array.isArray(props.entries) ? props.entries : [];
355
+ const options = Array.isArray(props.options) ? props.options : [];
356
+ const selectedId = props.selectedId === void 0 ? "" : props.selectedId;
357
+ const writable = props.writable === true;
358
+ const busy = props.busy === true;
359
+ const rows = entries.map((entry) => [entry.id, entry.name]);
360
+ const saveEntries = (next) => {
361
+ if (props.save === void 0) return Promise.resolve();
362
+ return Promise.resolve(props.save(next.map((row) => ({ id: row[0] ?? "", name: row[1] ?? "" }))));
363
+ };
364
+ const refreshAction = () => (0, import_jsx_runtime4.jsx)("button", {
365
+ type: "button",
366
+ className: shared_default.linkButton,
367
+ disabled: !writable || busy,
368
+ onClick: () => {
369
+ props.refresh();
370
+ },
371
+ children: busy && props.busyLabel !== void 0 ? props.busyLabel : props.refreshLabel
372
+ });
373
+ const extras = props.actions === void 0 ? [] : props.actions;
374
+ const renderActions = () => (0, import_jsx_runtime4.jsxs)("div", {
375
+ className: shared_default.linkButtonRow,
376
+ children: [
377
+ refreshAction(),
378
+ ...extras.map(
379
+ (action, index) => (0, import_jsx_runtime4.jsx)("button", {
380
+ type: "button",
381
+ className: shared_default.linkButton,
382
+ disabled: !writable || busy || action.disabled === true,
383
+ title: action.title === void 0 ? "" : action.title,
384
+ onClick: () => {
385
+ action.onClick();
386
+ },
387
+ children: action.label
388
+ }, index)
389
+ )
390
+ ]
391
+ });
392
+ return (0, import_jsx_runtime4.jsxs)("div", {
393
+ className: shared_default.catalog,
394
+ children: [
395
+ (0, import_jsx_runtime4.jsxs)("div", {
396
+ className: shared_default.field,
397
+ children: [
398
+ (0, import_jsx_runtime4.jsxs)("div", {
399
+ className: shared_default.fieldRow,
400
+ children: [
401
+ props.selectorLabel === void 0 || props.selectorLabel === "" ? null : (0, import_jsx_runtime4.jsx)("span", { className: shared_default.fieldTitle, children: props.selectorLabel }),
402
+ (0, import_jsx_runtime4.jsx)("span", {
403
+ className: shared_default.selectorWrap,
404
+ children: (0, import_jsx_runtime4.jsx)(import_dsh_client_ui_primitives.Menu, {
405
+ open: props.menuOpen === true,
406
+ onClose: () => {
407
+ props.setMenuOpen(false);
408
+ },
409
+ items: options.map((option) => ({ id: option.id, label: option.label })),
410
+ selectedId: options.some((option) => option.id === selectedId) ? selectedId : "",
411
+ onSelect: (id) => {
412
+ props.setMenuOpen(false);
413
+ props.select(id);
414
+ },
415
+ align: "end",
416
+ portal: true,
417
+ anchor: (0, import_jsx_runtime4.jsxs)("button", {
418
+ type: "button",
419
+ className: shared_default.selector,
420
+ "aria-haspopup": "menu",
421
+ "aria-expanded": props.menuOpen === true,
422
+ disabled: !writable || busy,
423
+ title: props.selectedTitle === void 0 ? "" : props.selectedTitle,
424
+ onClick: () => {
425
+ props.setMenuOpen(!(props.menuOpen === true));
426
+ },
427
+ children: [
428
+ // A bare label; a path needs its own
429
+ // truncating span.
430
+ (0, import_jsx_runtime4.jsx)("span", { className: shared_default.selectorLabel, children: props.selectedLabel }),
431
+ (0, import_jsx_runtime4.jsx)("svg", {
432
+ width: 14,
433
+ height: 14,
434
+ className: shared_default.pillChevron,
435
+ viewBox: "0 0 14 14",
436
+ fill: "none",
437
+ "aria-hidden": "true",
438
+ children: (0, import_jsx_runtime4.jsx)("path", { d: CHEVRON_PATH, fill: "currentColor" })
439
+ })
440
+ ]
441
+ })
442
+ })
443
+ })
444
+ ]
445
+ }),
446
+ props.hint === void 0 ? null : (0, import_jsx_runtime4.jsx)("div", { className: shared_default.fieldDesc, children: props.hint })
447
+ ]
448
+ }),
449
+ RowsBlock({
450
+ values: rows,
451
+ save: saveEntries,
452
+ labels: [props.idLabel === void 0 ? "" : props.idLabel, props.nameLabel === void 0 ? "" : props.nameLabel],
453
+ placeholders: [props.idPlaceholder === void 0 ? "" : props.idPlaceholder, props.namePlaceholder === void 0 ? "" : props.namePlaceholder],
454
+ writable: props.writable,
455
+ busy,
456
+ emptyLabel: props.emptyText,
457
+ addLabel: props.addLabel,
458
+ removeLabel: props.removeLabel,
459
+ disclosureLabel: props.rowsLabel,
460
+ disclosure: true,
461
+ rowClassName: shared_default.catalogRow,
462
+ renderAction: renderActions
463
+ }),
464
+ props.statusLine === void 0 || props.statusLine === null ? null : props.statusLine
465
+ ]
466
+ });
467
+ }
468
+ function FlagsBlock(props) {
469
+ const writable = props.writable === true;
470
+ const busy = props.busy === true;
471
+ const flags = Array.isArray(props.flags) ? props.flags : [];
472
+ const saveValues = (next) => {
473
+ const cleaned = next.map((row) => String(row === void 0 || row === null ? "" : row[0] ?? "").trim()).filter((flag) => flag !== "").filter((flag, index, all) => all.indexOf(flag) === index);
474
+ if (typeof props.save !== "function") return Promise.resolve();
475
+ return Promise.resolve(props.save(cleaned));
476
+ };
477
+ const actions = props.actions === void 0 ? [] : props.actions;
478
+ const renderAction = (visible) => (0, import_jsx_runtime5.jsxs)("div", {
479
+ className: shared_default.linkButtonRow,
480
+ children: actions.map(
481
+ (action, index) => (0, import_jsx_runtime5.jsx)(
482
+ "button",
483
+ {
484
+ type: "button",
485
+ className: shared_default.linkButton,
486
+ disabled: !writable || busy || action.disabled === true,
487
+ title: action.title === void 0 ? action.label : action.title,
488
+ onClick: () => {
489
+ if (!writable || busy) return;
490
+ const current = visible.map((row) => String(row[0] ?? "").trim()).filter((flag) => flag !== "");
491
+ const merged = Array.from(/* @__PURE__ */ new Set([...current, ...action.merged(current)]));
492
+ void saveValues(merged.map((flag) => [flag]));
493
+ },
494
+ children: action.label
495
+ },
496
+ index
497
+ )
498
+ )
499
+ }, "actions");
500
+ return (0, import_jsx_runtime5.jsxs)("div", {
501
+ className: shared_default.catalog,
502
+ children: [
503
+ (0, import_jsx_runtime5.jsxs)("div", {
504
+ className: shared_default.field,
505
+ children: [
506
+ (0, import_jsx_runtime5.jsxs)("div", {
507
+ className: shared_default.fieldRow,
508
+ children: [
509
+ (0, import_jsx_runtime5.jsx)("span", { className: shared_default.fieldTitle, children: props.title }),
510
+ props.link === void 0 || props.link.href === "" ? null : (0, import_jsx_runtime5.jsxs)("a", {
511
+ className: shared_default.docsLink,
512
+ href: props.link.href,
513
+ target: "_blank",
514
+ rel: "noopener noreferrer",
515
+ title: props.link.title === void 0 ? props.link.label : props.link.title,
516
+ children: [
517
+ (0, import_jsx_runtime5.jsx)("svg", {
518
+ className: shared_default.docsIcon,
519
+ width: 14,
520
+ height: 14,
521
+ viewBox: "0 0 24 24",
522
+ fill: "none",
523
+ stroke: "currentColor",
524
+ strokeWidth: 2,
525
+ strokeLinecap: "round",
526
+ strokeLinejoin: "round",
527
+ "aria-hidden": "true",
528
+ children: DOCS_PATHS.map((d, at) => (0, import_jsx_runtime5.jsx)("path", { d }, at))
529
+ }),
530
+ (0, import_jsx_runtime5.jsx)("span", { className: shared_default.docsLinkLabel, children: props.link.label })
531
+ ]
532
+ })
533
+ ]
534
+ }),
535
+ props.hint === void 0 ? null : (0, import_jsx_runtime5.jsx)("div", { className: shared_default.fieldDesc, children: props.hint })
536
+ ]
537
+ }),
538
+ RowsBlock({
539
+ values: flags.map((flag) => [flag]),
540
+ save: saveValues,
541
+ labels: [props.rowLabel === void 0 ? "" : props.rowLabel],
542
+ placeholders: [props.rowPlaceholder === void 0 ? "" : props.rowPlaceholder],
543
+ writable: props.writable,
544
+ busy,
545
+ emptyLabel: props.emptyText,
546
+ addLabel: props.addLabel,
547
+ removeLabel: props.removeLabel,
548
+ disclosureLabel: props.disclosureLabel,
549
+ disclosure: props.disclosure === true,
550
+ rowClassName: shared_default.catalogRowSingle,
551
+ inputClassName: shared_default.catalogInput,
552
+ renderAction
553
+ })
554
+ ]
555
+ });
556
+ }
557
+ function SwitchField(props) {
558
+ const control = (0, import_jsx_runtime6.jsx)(import_dsh_client_ui_primitives2.Switch, {
559
+ checked: props.checked === true,
560
+ label: props.label,
561
+ disabled: props.disabled === true || props.busy === true,
562
+ onChange: () => {
563
+ if (typeof props.onToggle === "function") props.onToggle();
564
+ }
565
+ });
566
+ if (props.variant === "row") {
567
+ return (0, import_jsx_runtime6.jsx)("span", {
568
+ className: shared_default.toggleLabel,
569
+ title: props.title === void 0 ? props.label : props.title,
570
+ "aria-label": props.label,
571
+ children: control
572
+ });
573
+ }
574
+ return (0, import_jsx_runtime6.jsxs)("div", {
575
+ className: shared_default.catalog,
576
+ children: [
577
+ (0, import_jsx_runtime6.jsxs)("div", {
578
+ className: shared_default.field,
579
+ children: [
580
+ (0, import_jsx_runtime6.jsxs)("div", {
581
+ className: shared_default.fieldRow,
582
+ children: [
583
+ (0, import_jsx_runtime6.jsx)("span", { className: shared_default.fieldTitle, children: props.label }),
584
+ (0, import_jsx_runtime6.jsx)("span", {
585
+ className: shared_default.toggleLabel,
586
+ title: props.title === void 0 ? props.label : props.title,
587
+ children: control
588
+ })
589
+ ]
590
+ }),
591
+ props.hint === void 0 ? null : (0, import_jsx_runtime6.jsx)("div", { className: shared_default.fieldDesc, children: props.hint })
592
+ ]
593
+ })
594
+ ]
595
+ });
596
+ }
597
+ function LinkButtonRow(props) {
598
+ return (0, import_jsx_runtime7.jsxs)("div", { className: shared_default.linkButtonRow, children: props.children === void 0 ? [] : [props.children] });
599
+ }
600
+ function ErrorLine(props) {
601
+ const text2 = String(props.text ?? "");
602
+ if (text2 === "") return null;
603
+ return (0, import_jsx_runtime7.jsxs)("p", {
604
+ className: shared_default.error,
605
+ role: "status",
606
+ children: [
607
+ (0, import_jsx_runtime7.jsx)("span", { className: shared_default.dot + " " + shared_default.dotError, "aria-hidden": "true" }),
608
+ (0, import_jsx_runtime7.jsx)("span", { children: text2 })
609
+ ]
610
+ });
611
+ }
612
+ function TextField(props) {
613
+ return field(props, (0, import_jsx_runtime8.jsx)("input", {
614
+ type: "text",
615
+ className: shared_default.control,
616
+ value: text(props.value),
617
+ placeholder: props.placeholder ?? "",
618
+ disabled: props.disabled === true,
619
+ "aria-label": props.label,
620
+ onChange: (event) => props.onChange(event.target.value)
621
+ }));
622
+ }
623
+ function LinesField(props) {
624
+ const box = useTypedBox(linesOf(props.value).join("\n"));
625
+ return field(props, (0, import_jsx_runtime8.jsx)("textarea", {
626
+ className: shared_default.textarea,
627
+ value: box.text,
628
+ placeholder: props.placeholder ?? "",
629
+ disabled: props.disabled === true,
630
+ "aria-label": props.label,
631
+ onChange: (event) => {
632
+ const typed = event.target.value;
633
+ box.stage(typed);
634
+ props.onChange(parseLines(typed));
635
+ }
636
+ }));
637
+ }
638
+ function MapField(props) {
639
+ const box = useTypedBox(serializeMap(props.value));
640
+ return field(props, (0, import_jsx_runtime8.jsx)("textarea", {
641
+ className: shared_default.textarea,
642
+ value: box.text,
643
+ placeholder: props.placeholder ?? "",
644
+ disabled: props.disabled === true,
645
+ "aria-label": props.label,
646
+ onChange: (event) => {
647
+ const typed = event.target.value;
648
+ box.stage(typed);
649
+ props.onChange(parseMap(typed));
650
+ }
651
+ }));
652
+ }
653
+ function SelectField(props) {
654
+ const options = selectOptions(props.value, props.options ?? [], props.placeholder);
655
+ return field(
656
+ props,
657
+ (0, import_jsx_runtime8.jsx)("select", {
658
+ className: shared_default.select,
659
+ value: text(props.value),
660
+ disabled: props.disabled === true,
661
+ "aria-label": props.label,
662
+ onChange: (event) => {
663
+ const picked = options.find((option) => text(option.value) === event.target.value);
664
+ props.onChange(picked === void 0 ? event.target.value : picked.value);
665
+ },
666
+ children: options.map(
667
+ (option) => (0, import_jsx_runtime8.jsx)("option", { value: text(option.value), children: option.label }, text(option.value))
668
+ )
669
+ })
670
+ );
671
+ }
672
+ function DropdownField(props) {
673
+ const openState = react3.useState(false);
674
+ const open = openState[0];
675
+ const setOpen = openState[1];
676
+ const disabled = props.disabled === true;
677
+ const options = selectOptions(props.value, props.options ?? [], props.placeholder);
678
+ const held = text(props.value);
679
+ const current = options.find((option) => text(option.value) === held);
680
+ return field(
681
+ props,
682
+ (0, import_jsx_runtime8.jsx)("span", {
683
+ className: shared_default.selectorWrap,
684
+ children: (0, import_jsx_runtime8.jsx)(import_dsh_client_ui_primitives3.Menu, {
685
+ open: open && !disabled,
686
+ onClose: () => setOpen(false),
687
+ items: options.map((option) => ({ id: text(option.value), label: option.label })),
688
+ selectedId: current === void 0 ? "" : held,
689
+ onSelect: (id) => {
690
+ setOpen(false);
691
+ if (id === held) return;
692
+ const picked = options.find((option) => text(option.value) === id);
693
+ props.onChange(picked === void 0 ? id : picked.value);
694
+ },
695
+ align: "start",
696
+ portal: true,
697
+ anchor: (0, import_jsx_runtime8.jsxs)("button", {
698
+ type: "button",
699
+ className: shared_default.selector,
700
+ "aria-haspopup": "menu",
701
+ "aria-expanded": open && !disabled,
702
+ "aria-label": props.label,
703
+ disabled,
704
+ onClick: () => setOpen(!open),
705
+ children: [
706
+ (0, import_jsx_runtime8.jsx)("span", { className: shared_default.selectorLabel, children: current === void 0 ? props.placeholder ?? "" : current.label }),
707
+ (0, import_jsx_runtime8.jsx)("svg", {
708
+ width: 14,
709
+ height: 14,
710
+ className: shared_default.pillChevron,
711
+ viewBox: "0 0 14 14",
712
+ fill: "none",
713
+ "aria-hidden": "true",
714
+ children: (0, import_jsx_runtime8.jsx)("path", { d: CHEVRON_PATH, fill: "currentColor" })
715
+ })
716
+ ]
717
+ })
718
+ })
719
+ })
720
+ );
721
+ }
722
+ function ExtraField(props) {
723
+ const type = props.type === void 0 ? "string" : props.type;
724
+ if (type === "boolean") {
725
+ return field(
726
+ props,
727
+ (0, import_jsx_runtime8.jsx)("input", {
728
+ type: "checkbox",
729
+ className: shared_default.checkbox,
730
+ checked: props.value === true,
731
+ disabled: props.disabled === true,
732
+ "aria-label": props.label,
733
+ onChange: (event) => props.onChange(event.target.checked)
734
+ })
735
+ );
736
+ }
737
+ if (type === "lines") return (0, import_jsx_runtime8.jsx)(LinesField, props);
738
+ if (type === "select") return SelectField(props);
739
+ if (type === "secret") {
740
+ return field(
741
+ props,
742
+ (0, import_jsx_runtime8.jsx)("input", {
743
+ type: "password",
744
+ className: shared_default.control,
745
+ value: text(props.value),
746
+ placeholder: props.placeholder ?? "",
747
+ disabled: props.disabled === true,
748
+ "aria-label": props.label,
749
+ onChange: (event) => props.onChange(event.target.value)
750
+ })
751
+ );
752
+ }
753
+ if ((props.options ?? []).length > 0) return SelectField(props);
754
+ return TextField(props);
755
+ }
756
+ function selectOptions(value, served, placeholder) {
757
+ const out = [];
758
+ const seen = /* @__PURE__ */ new Set();
759
+ const held = text(value);
760
+ if (held === "" && placeholder !== void 0 && placeholder !== "" && !served.some((option) => text(option.value) === "")) {
761
+ out.push({ value: "", label: placeholder });
762
+ seen.add("");
763
+ }
764
+ for (const option of served) {
765
+ const key = text(option.value);
766
+ if (seen.has(key)) continue;
767
+ seen.add(key);
768
+ out.push(option);
769
+ }
770
+ if (held !== "" && !seen.has(held)) out.push({ value, label: held });
771
+ return out;
772
+ }
773
+ function linesOf(value) {
774
+ if (Array.isArray(value)) return value.map((item) => text(item)).filter((item) => item.trim() !== "");
775
+ if (typeof value === "string") return parseLines(value);
776
+ return [];
777
+ }
778
+ function useTypedBox(served) {
779
+ const typedState = react3.useState({ key: "", text: "" });
780
+ const typed = typedState[0];
781
+ const setTyped = typedState[1];
782
+ return {
783
+ text: typed.key === served ? typed.text : served,
784
+ stage: (text2) => setTyped({ key: served, text: text2 })
785
+ };
786
+ }
787
+ function text(value) {
788
+ if (value === void 0 || value === null) return "";
789
+ return typeof value === "string" ? value : String(value);
790
+ }
791
+ function parseLines(text2) {
792
+ return String(text2).split("\n").filter((line) => line.trim() !== "");
793
+ }
794
+ function parseMap(text2) {
795
+ const out = {};
796
+ for (const line of String(text2).split("\n")) {
797
+ if (line.trim() === "") continue;
798
+ const split = line.indexOf("=");
799
+ if (split <= 0) continue;
800
+ const key = line.slice(0, split);
801
+ if (key.trim() === "") continue;
802
+ out[key] = line.slice(split + 1);
803
+ }
804
+ return out;
805
+ }
806
+ function mapText(value) {
807
+ if (value === void 0 || value === null) return "";
808
+ if (typeof value === "string") return value;
809
+ if (Array.isArray(value)) return value.map((item) => text(item)).join(",");
810
+ if (typeof value === "object") return JSON.stringify(value);
811
+ return String(value);
812
+ }
813
+ function serializeMap(value) {
814
+ if (typeof value === "string") return value;
815
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return "";
816
+ return Object.entries(value).map(([key, item]) => `${key}=${mapText(item)}`).join("\n");
817
+ }
818
+ function field(props, control) {
819
+ return (0, import_jsx_runtime8.jsxs)("div", {
820
+ className: shared_default.controlField,
821
+ children: [
822
+ (0, import_jsx_runtime8.jsx)("div", { className: shared_default.controlTitle, children: props.label }),
823
+ control,
824
+ props.help === void 0 || props.help === "" ? null : (0, import_jsx_runtime8.jsx)("div", { className: shared_default.fieldDesc, children: props.help })
825
+ ]
826
+ });
827
+ }
828
+ function ComposerPill(props) {
829
+ const disabled = props.disabled === true;
830
+ return (0, import_jsx_runtime9.jsx)("span", {
831
+ className: shared_default.composerPillRoot,
832
+ "data-pill": props.key,
833
+ children: (0, import_jsx_runtime9.jsx)(import_dsh_client_ui_primitives4.Menu, {
834
+ open: props.open && !disabled,
835
+ onClose: () => props.setOpen(false),
836
+ items: props.options,
837
+ selectedId: props.selectedId,
838
+ onSelect: (id) => {
839
+ props.setOpen(false);
840
+ if (id === props.selectedId) return;
841
+ Promise.resolve(props.onSelect(id)).catch(() => void 0);
842
+ },
843
+ align: "start",
844
+ side: "top",
845
+ portal: true,
846
+ anchor: (0, import_jsx_runtime9.jsxs)("button", {
847
+ type: "button",
848
+ className: shared_default.composerPill,
849
+ "aria-label": props.ariaLabel ?? (props.title === void 0 ? props.label : `${props.title}: ${props.label}`),
850
+ title: props.title ?? props.label,
851
+ "aria-haspopup": "menu",
852
+ "aria-expanded": props.open && !disabled,
853
+ disabled,
854
+ onClick: () => props.setOpen(!props.open),
855
+ children: [
856
+ props.icon === void 0 || props.icon.length === 0 ? null : (0, import_jsx_runtime9.jsx)("span", {
857
+ className: shared_default.composerPillIcon,
858
+ "aria-hidden": true,
859
+ children: (0, import_jsx_runtime9.jsx)("svg", {
860
+ width: 14,
861
+ height: 14,
862
+ viewBox: "0 0 24 24",
863
+ fill: "none",
864
+ xmlns: "http://www.w3.org/2000/svg",
865
+ children: props.icon.map(
866
+ (d, at) => (0, import_jsx_runtime9.jsx)("path", { d, stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" }, at)
867
+ )
868
+ })
869
+ }),
870
+ (0, import_jsx_runtime9.jsx)("span", { className: shared_default.composerPillLabel, children: props.label }),
871
+ (0, import_jsx_runtime9.jsx)("span", {
872
+ className: shared_default.composerPillChevron + (props.open && !disabled ? " " + shared_default.composerPillChevronOpen : ""),
873
+ "aria-hidden": true,
874
+ children: (0, import_jsx_runtime9.jsx)("svg", {
875
+ width: 14,
876
+ height: 14,
877
+ viewBox: "0 0 14 14",
878
+ fill: "none",
879
+ xmlns: "http://www.w3.org/2000/svg",
880
+ children: (0, import_jsx_runtime9.jsx)("path", { d: CHEVRON_PATH, fill: "currentColor" })
881
+ })
882
+ })
883
+ ]
884
+ })
885
+ })
886
+ });
887
+ }
888
+ var Blocks = {
889
+ RowsBlock,
890
+ DialogBlock,
891
+ PickerBlock,
892
+ FlagsBlock,
893
+ SwitchField,
894
+ LinkButtonRow,
895
+ ErrorLine,
896
+ TextField,
897
+ LinesField,
898
+ MapField,
899
+ SelectField,
900
+ DropdownField,
901
+ ExtraField,
902
+ ComposerPill
903
+ };
904
+ function tag(value) {
905
+ return Object.prototype.toString.call(value).slice(8, -1);
906
+ }
907
+ function isPlainObject(value) {
908
+ const prototype = Object.getPrototypeOf(value);
909
+ if (prototype === null) return true;
910
+ if (prototype === Object.prototype) return true;
911
+ return Object.getPrototypeOf(prototype) === null && tag(value) === "Object";
912
+ }
913
+ function canonicalCollection(value, kind) {
914
+ if (kind === "Set") {
915
+ return [...value].map(canonical).sort((left, right) => stableKey(left) < stableKey(right) ? -1 : 1);
916
+ }
917
+ return [...value].map(([key, item]) => [canonical(key), canonical(item)]).sort((left, right) => stableKey(left[0]) < stableKey(right[0]) ? -1 : 1);
918
+ }
919
+ function answersNothing(value) {
920
+ return value === void 0 || value === null;
921
+ }
922
+ function meaningfulKeys(value) {
923
+ return Object.keys(value).filter((key) => !answersNothing(value[key]));
924
+ }
925
+ function canonical(value) {
926
+ const kind = tag(value);
927
+ if (kind === "Set" || kind === "Map") return canonicalCollection(value, kind);
928
+ return value;
929
+ }
930
+ function stableKey(value) {
931
+ const text2 = JSON.stringify(value, (_key, item) => {
932
+ if (item !== null && typeof item === "object") {
933
+ const kind = tag(item);
934
+ if (kind === "Set" || kind === "Map") return canonicalCollection(item, kind);
935
+ }
936
+ if (item !== null && typeof item === "object" && !Array.isArray(item)) {
937
+ const sorted = {};
938
+ for (const name of Object.keys(item).sort()) {
939
+ const child = item[name];
940
+ if (!answersNothing(child)) sorted[name] = child;
941
+ }
942
+ return sorted;
943
+ }
944
+ return item === void 0 ? null : item;
945
+ });
946
+ return text2 === void 0 ? "null" : text2;
947
+ }
948
+ function sameValue(a, b) {
949
+ if (a === b) return true;
950
+ if (typeof a === "number" && typeof b === "number") return Number.isNaN(a) && Number.isNaN(b);
951
+ const leftMissing = answersNothing(a);
952
+ const rightMissing = answersNothing(b);
953
+ if (leftMissing || rightMissing) return leftMissing && rightMissing;
954
+ if (typeof a !== "object" || typeof b !== "object") return false;
955
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
956
+ if (Array.isArray(a) && Array.isArray(b)) {
957
+ if (a.length !== b.length) return false;
958
+ return a.every((item, index) => sameValue(item, b[index]));
959
+ }
960
+ if (!isPlainObject(a) || !isPlainObject(b)) return sameOpaqueValue(a, b);
961
+ const left = meaningfulKeys(a);
962
+ const right = meaningfulKeys(b);
963
+ if (left.length !== right.length) return false;
964
+ return left.every(
965
+ (key) => Object.prototype.hasOwnProperty.call(b, key) && sameValue(a[key], b[key])
966
+ );
967
+ }
968
+ function sameOpaqueValue(a, b) {
969
+ const kind = tag(a);
970
+ if (kind !== tag(b)) return false;
971
+ if (kind === "Date") return sameValue(a.getTime(), b.getTime());
972
+ if (kind === "RegExp") {
973
+ return sameValue(a.source, b.source) && sameValue(a.flags, b.flags);
974
+ }
975
+ if (kind === "Map" || kind === "Set") {
976
+ return sameValue(canonical(a), canonical(b));
977
+ }
978
+ if (kind === "String" || kind === "Number" || kind === "Boolean") {
979
+ return sameValue(a.valueOf(), b.valueOf());
980
+ }
981
+ return false;
982
+ }
983
+ function nothingCardSnapshot() {
984
+ return {
985
+ available: false,
986
+ writable: false,
987
+ values: {},
988
+ overridden: [],
989
+ lastError: "",
990
+ dirty: false,
991
+ saving: false,
992
+ failed: false,
993
+ actionError: "",
994
+ notice: null
995
+ };
996
+ }
997
+ var RUN_SETTLE_TIMEOUT_MS = 9e4;
998
+ function settleOnRevision(host, settled, write, deadline = RUN_SETTLE_TIMEOUT_MS) {
999
+ if (host.disposed) return Promise.resolve();
1000
+ let satisfied = false;
1001
+ const armed = host.settleOn((snapshot) => {
1002
+ if (settled(snapshot) !== true) return false;
1003
+ satisfied = true;
1004
+ return true;
1005
+ });
1006
+ return new Promise((resolve) => {
1007
+ let done = false;
1008
+ const timer = setTimeout(() => finish(), deadline);
1009
+ host.settleTimer = timer;
1010
+ const finish = (refused = false) => {
1011
+ if (done) return;
1012
+ done = true;
1013
+ clearTimeout(timer);
1014
+ if (host.settleTimer === timer) host.settleTimer = null;
1015
+ armed.cancel();
1016
+ resolve();
1017
+ if (refused || satisfied || host.disposed) return;
1018
+ const state = host.getSnapshot();
1019
+ if (state.lastError !== "") host.noteNotice("error", state.lastError);
1020
+ else host.noteNotice("stall", "");
1021
+ };
1022
+ void armed.settle.then(() => finish());
1023
+ void Promise.resolve(write()).then(
1024
+ (accepted) => {
1025
+ if (accepted === false) {
1026
+ host.noteError("configuration write refused");
1027
+ finish(true);
1028
+ }
1029
+ },
1030
+ (error) => {
1031
+ host.noteError(error);
1032
+ finish(true);
1033
+ }
1034
+ );
1035
+ });
1036
+ }
1037
+ var MAX_WRITE_PASSES = 4;
1038
+ var CardController = class {
1039
+ /** Disposed — every late answer and timer bails on this flag first. */
1040
+ disposed = false;
1041
+ /** The armed stall-deadline timer (owned by ./wait.ts). */
1042
+ settleTimer = null;
1043
+ store;
1044
+ fields;
1045
+ errorField;
1046
+ settleDeadlineMs;
1047
+ settleField;
1048
+ /** The last pass of the write door, when the mount can issue it unfenced. */
1049
+ unfencedMutate;
1050
+ /** The form the entry's row page handed (`view: "page"`), if any. */
1051
+ pageForm;
1052
+ formRef;
1053
+ unsubscribe;
1054
+ /** Staged drafts: only fields that differ from what is served. */
1055
+ draft = {};
1056
+ /** The last served (undrafted) answers, recomputed on every push. */
1057
+ served = {};
1058
+ saving = false;
1059
+ failed = false;
1060
+ actionError = "";
1061
+ noticeQueue = null;
1062
+ noticeSeq = 0;
1063
+ /** Every armed settle; keyed against a published snapshot. */
1064
+ waits = [];
1065
+ constructor(form, spec = {}) {
1066
+ this.fields = spec.fields ?? {};
1067
+ this.errorField = spec.errorField ?? "lastError";
1068
+ this.settleDeadlineMs = spec.settleDeadlineMs;
1069
+ this.settleField = spec.settleField === void 0 || spec.settleField === "" ? void 0 : spec.settleField;
1070
+ this.unfencedMutate = spec.unfencedMutate;
1071
+ this.formRef = form;
1072
+ this.store = (0, import_dsh_client_store.createSnapshotStore)(nothingCardSnapshot());
1073
+ this.unsubscribe = form === void 0 ? void 0 : form.subscribe(() => {
1074
+ this.actionError = "";
1075
+ this.publish();
1076
+ });
1077
+ this.publish();
1078
+ }
1079
+ /** Adopt the form the row page hands with `view: "page"` (identity guarded:
1080
+ * an unchanged adopt is a no-op, keeping its publish out of the render). */
1081
+ adoptForm(form) {
1082
+ if (this.disposed) return;
1083
+ const next = form === void 0 ? void 0 : form;
1084
+ if (this.pageForm === next) return;
1085
+ this.pageForm = next;
1086
+ this.publish();
1087
+ }
1088
+ /** The card's store is what the card body renders. */
1089
+ getSnapshot() {
1090
+ return this.store.getSnapshot();
1091
+ }
1092
+ /** Whether anything serves this entry. */
1093
+ available() {
1094
+ return this.store.getSnapshot().available === true;
1095
+ }
1096
+ /** Read one field off the current snapshot (drafts overlaid). */
1097
+ value(field2) {
1098
+ return this.store.getSnapshot().values[field2];
1099
+ }
1100
+ /** One field as the Host serves it, drafts left out — what a staged edit
1101
+ * that lands back on it is compared against. */
1102
+ servedValue(field2) {
1103
+ return this.documentValue(field2);
1104
+ }
1105
+ /** One field as the served document answers it (what a wait arms against). */
1106
+ documentValue(field2) {
1107
+ const snapshot = this.readSnapshot();
1108
+ if (snapshot === void 0 || snapshot.status !== "ready" || snapshot.value === void 0) return void 0;
1109
+ return snapshot.value[field2];
1110
+ }
1111
+ /**
1112
+ * Staged edit: save one value as a draft. A draft equal to what is served
1113
+ * is not dirty — it stages nothing and dirties nothing.
1114
+ */
1115
+ stage(field2, value) {
1116
+ if (this.disposed) return;
1117
+ const servedValue = this.served[field2];
1118
+ if (valuesEqual(servedValue, value)) {
1119
+ delete this.draft[field2];
1120
+ } else {
1121
+ this.draft[field2] = value;
1122
+ }
1123
+ this.publish();
1124
+ }
1125
+ /** Drop every staged draft: the card answers with what is served. */
1126
+ discard() {
1127
+ if (this.disposed) return;
1128
+ for (const key of Object.keys(this.draft)) delete this.draft[key];
1129
+ this.failed = false;
1130
+ this.publish();
1131
+ }
1132
+ /** Apply exactly the staged fields. Accepted: the drafts clear with the
1133
+ * Host's answers. Refused: the drafts stand, `failed` says so. */
1134
+ async applyStaged() {
1135
+ if (this.disposed) return false;
1136
+ const staged = Object.entries(this.draft);
1137
+ if (staged.length === 0) return true;
1138
+ this.saving = true;
1139
+ this.publish();
1140
+ const ops = staged.map(([field2, value]) => ({ op: "set", path: [field2], value }));
1141
+ const accepted = await this.writeDoor(() => ops);
1142
+ if (accepted) {
1143
+ for (const field2 of Object.keys(this.draft)) delete this.draft[field2];
1144
+ this.failed = false;
1145
+ } else {
1146
+ this.failed = true;
1147
+ this.actionError = "configuration write refused";
1148
+ }
1149
+ this.saving = false;
1150
+ this.publish();
1151
+ return accepted;
1152
+ }
1153
+ /** Immediate write of one field — rows, selections: written on the spot. */
1154
+ async writeField(field2, value) {
1155
+ if (this.disposed) return false;
1156
+ const accepted = await this.writeDoor(() => [{ op: "set", path: [field2], value }]);
1157
+ this.publish();
1158
+ return accepted;
1159
+ }
1160
+ /** Return one field to what the row's own configuration answers: the field
1161
+ * leaves the user layer (an `unset`), and any draft standing over it goes. */
1162
+ async resetField(field2) {
1163
+ if (this.disposed) return false;
1164
+ const accepted = await this.writeDoor(() => [{ op: "unset", path: [field2] }]);
1165
+ if (accepted) delete this.draft[field2];
1166
+ this.publish();
1167
+ return accepted;
1168
+ }
1169
+ /**
1170
+ * Trigger write — a nonce: the value is always a stamp that moved off what
1171
+ * stood before it (`nextStamp`), so a later commit always reads as a diff.
1172
+ * Returns as soon as the write lands; waiting for the Host *run* is the job
1173
+ * of `settle` — armed before this crosses the wire, so nothing can slip
1174
+ * between. A stamp that stands where it stood answers no run at all: two
1175
+ * actions inside one millisecond must not read as nothing happened.
1176
+ */
1177
+ async writeTrigger(field2) {
1178
+ return this.writeDoor((document2) => [{ op: "set", path: [field2], value: nextStamp(document2[field2]) }]);
1179
+ }
1180
+ /** Write a trigger and wait for the mirror to satisfy `settled`. */
1181
+ settleAndTrigger(settled, field2) {
1182
+ return settleOnRevision(this, settled, () => this.writeTrigger(field2), this.settleDeadlineMs);
1183
+ }
1184
+ /** The immediate-write shape of `settleAndTrigger`: write one field on the
1185
+ * spot (a selection, a saved list) and wait for the Host run behind it to
1186
+ * satisfy `settled` — the wait armed before the write crosses the wire. */
1187
+ settleAndWrite(settled, field2, value) {
1188
+ return settleOnRevision(this, settled, () => this.writeField(field2, value), this.settleDeadlineMs);
1189
+ }
1190
+ /**
1191
+ * Entry trigger — the keyed nonce (`entryActions` hole): stamps a moved
1192
+ * value **under `key`** inside the field's map, keeping every other row's
1193
+ * stamp, and writes the whole map through the door. The Host's trigger diff
1194
+ * answers per key (`host/adapter/form.ts`), so exactly the row that stamped
1195
+ * fires its run. The merge is composed inside the door (what the document
1196
+ * stands on now), never from what this half last rendered.
1197
+ */
1198
+ async writeEntryTrigger(field2, key) {
1199
+ const wanted = String(key);
1200
+ return this.writeDoor((document2) => {
1201
+ const current = document2[field2];
1202
+ const stamps = current !== null && typeof current === "object" && !Array.isArray(current) ? { ...current } : {};
1203
+ stamps[wanted] = nextStamp(stamps[wanted]);
1204
+ return [{ op: "set", path: [field2], value: stamps }];
1205
+ });
1206
+ }
1207
+ /** The keyed shape of `settleAndTrigger`: arm the wait, write the entry
1208
+ * nonce, settle when `settled` stands. */
1209
+ settleAndTriggerEntry(settled, field2, key) {
1210
+ return settleOnRevision(this, settled, () => this.writeEntryTrigger(field2, key), this.settleDeadlineMs);
1211
+ }
1212
+ /** Host-run settle on the `settleField` hole: write the trigger for
1213
+ * `triggerField`, settle once what `watchField` answers moves off the value
1214
+ * it stood at when the wait was armed — the diff is the answer, the wall
1215
+ * clock only the backstop. With no `watchField` declared anywhere, the
1216
+ * trigger field itself is what must move.
1217
+ *
1218
+ * Both sides read the **served document**: the store is what the body
1219
+ * renders, and at arm time it may not have answered the field at all (nothing
1220
+ * published yet) — armed against that, every answer reads as a move. */
1221
+ settleTrigger(triggerField, watchField) {
1222
+ const watched = watchField ?? this.settleField ?? triggerField;
1223
+ const armed = this.documentValue(watched);
1224
+ return settleOnRevision(
1225
+ this,
1226
+ () => !sameValue(this.documentValue(watched), armed),
1227
+ () => this.writeTrigger(triggerField),
1228
+ this.settleDeadlineMs
1229
+ );
1230
+ }
1231
+ /** The keyed shape of `settleTrigger`: the wait answers **per entry** — what
1232
+ * `watchField` answers for `key` moves off what it answered when armed. A
1233
+ * mirror shaped as a list of lines answers it by the line's own `id`/`key`,
1234
+ * which is the shape a live mirror really carries. */
1235
+ settleEntryTrigger(triggerField, key, watchField) {
1236
+ const watched = watchField ?? this.settleField ?? triggerField;
1237
+ const wanted = String(key);
1238
+ const armed = keyedAnswer(this.documentValue(watched), wanted);
1239
+ return settleOnRevision(
1240
+ this,
1241
+ () => !sameValue(keyedAnswer(this.documentValue(watched), wanted), armed),
1242
+ () => this.writeEntryTrigger(triggerField, key),
1243
+ this.settleDeadlineMs
1244
+ );
1245
+ }
1246
+ /** Arm one predicate against the next publish (the wait surface). */
1247
+ settleOn(settled) {
1248
+ let resolve = () => void 0;
1249
+ const settle = new Promise((done) => {
1250
+ resolve = done;
1251
+ });
1252
+ const wait = { settled, resolve };
1253
+ this.waits.push(wait);
1254
+ return {
1255
+ settle,
1256
+ cancel: () => {
1257
+ const index = this.waits.indexOf(wait);
1258
+ if (index >= 0) this.waits.splice(index, 1);
1259
+ resolve();
1260
+ }
1261
+ };
1262
+ }
1263
+ /** Stage one failure notice for the body's toast host. */
1264
+ noteNotice(kind, text2) {
1265
+ this.noticeSeq += 1;
1266
+ this.noticeQueue = { seq: this.noticeSeq, kind, text: String(text2 ?? "") };
1267
+ this.publish();
1268
+ }
1269
+ /** Drop one announced notice (by `seq`). */
1270
+ clearNotice(seq) {
1271
+ if (this.noticeQueue !== null && this.noticeQueue.seq === seq) this.noticeQueue = null;
1272
+ this.publish();
1273
+ }
1274
+ /** Surface a refused or failed write as the card's own error line. */
1275
+ noteError(error) {
1276
+ this.actionError = String(error instanceof Error ? error.message : error);
1277
+ this.publish();
1278
+ }
1279
+ /** Release everything: subscriptions, settle, timers. */
1280
+ dispose() {
1281
+ this.disposed = true;
1282
+ this.unsubscribe?.();
1283
+ this.unsubscribe = void 0;
1284
+ this.pageForm = void 0;
1285
+ for (const wait of [...this.waits]) wait.resolve();
1286
+ this.waits.length = 0;
1287
+ if (this.settleTimer !== null && this.settleTimer !== void 0) {
1288
+ clearTimeout(this.settleTimer);
1289
+ this.settleTimer = null;
1290
+ }
1291
+ }
1292
+ /** Everything a registered card body receives — the same object for every
1293
+ * render (identity stable), so a re-render cannot disturb the store. */
1294
+ inject() {
1295
+ return {
1296
+ store: this.store,
1297
+ controller: this,
1298
+ fields: this.fields
1299
+ };
1300
+ }
1301
+ /** Push the served answers, overlaid by drafts, into the store. */
1302
+ publish() {
1303
+ if (this.disposed) return;
1304
+ const previous = this.store.getSnapshot();
1305
+ const snapshot = this.readSnapshot();
1306
+ let values = {};
1307
+ let overridden = [];
1308
+ let available = false;
1309
+ let writable = false;
1310
+ if (snapshot !== void 0 && snapshot.status === "ready" && snapshot.value !== void 0) {
1311
+ available = true;
1312
+ writable = snapshot.writable !== false;
1313
+ values = { ...snapshot.value };
1314
+ const user = snapshot.user;
1315
+ if (user !== null && typeof user === "object") overridden = Object.keys(user);
1316
+ for (const [field2, value] of Object.entries(this.draft)) values[field2] = value;
1317
+ } else {
1318
+ for (const key of Object.keys(this.draft)) delete this.draft[key];
1319
+ this.releaseWaits();
1320
+ }
1321
+ this.served = snapshot !== void 0 && snapshot.status === "ready" && snapshot.value !== void 0 ? { ...snapshot.value } : {};
1322
+ this.store.set({
1323
+ available,
1324
+ writable,
1325
+ values,
1326
+ overridden,
1327
+ lastError: typeof values[this.errorField] === "string" ? String(values[this.errorField]) : "",
1328
+ dirty: Object.keys(this.draft).length > 0,
1329
+ saving: this.saving,
1330
+ failed: this.failed,
1331
+ actionError: this.actionError,
1332
+ notice: this.noticeQueue
1333
+ });
1334
+ const state = this.store.getSnapshot();
1335
+ for (const wait of [...this.waits]) {
1336
+ if (wait.settled(state) === true) {
1337
+ this.waits.splice(this.waits.indexOf(wait), 1);
1338
+ wait.resolve();
1339
+ }
1340
+ }
1341
+ }
1342
+ /** What every read stands on: the shared form's mirror, which subscribes
1343
+ * and so moves with every Host answer. The page's handed `state` is a
1344
+ * snapshot taken when the page rendered — only the fallback when no shared
1345
+ * form stands; the page form is adopted as the write **door**, not as the
1346
+ * source. */
1347
+ readSnapshot() {
1348
+ if (this.formRef !== void 0) {
1349
+ try {
1350
+ return this.formRef.getSnapshot();
1351
+ } catch {
1352
+ }
1353
+ }
1354
+ return this.pageForm?.state;
1355
+ }
1356
+ /**
1357
+ * The write door — a refused pass is re-issued against the re-read document
1358
+ * until the budget runs out, then the refusal is *reported*.
1359
+ *
1360
+ * The door takes what the ops are **made of**, not the ops themselves, for
1361
+ * one reason: anything that has to carry what the document already says (a
1362
+ * keyed trigger's stamp map, a stamp that must move) is composed against
1363
+ * what the document says *now*, on every pass. Composing it once from what
1364
+ * this half last rendered would re-issue a merge made against a document
1365
+ * that already moved — and drop the other row's stamp on the floor.
1366
+ */
1367
+ async writeDoor(compose) {
1368
+ for (let pass = 0; pass < MAX_WRITE_PASSES; pass++) {
1369
+ const snapshot = this.readSnapshot();
1370
+ if (snapshot === void 0 || snapshot.status !== "ready") return false;
1371
+ const document2 = snapshot.value ?? {};
1372
+ const ops = withLiveCleared(compose(document2), this.fields.liveKeys);
1373
+ const pageForm = this.pageForm;
1374
+ const formRef = this.formRef;
1375
+ const door = pageForm !== void 0 ? (next) => pageForm.mutate(next) : formRef !== void 0 ? (next) => formRef.mutate(next) : void 0;
1376
+ if (door === void 0) return false;
1377
+ const last = pass === MAX_WRITE_PASSES - 1 && this.unfencedMutate !== void 0;
1378
+ try {
1379
+ const accepted = last ? await this.unfencedMutate(ops) : await door(ops);
1380
+ if (accepted === true) return true;
1381
+ } catch {
1382
+ }
1383
+ }
1384
+ return false;
1385
+ }
1386
+ /** Nothing serves: no wait can settle. */
1387
+ releaseWaits() {
1388
+ for (const wait of [...this.waits]) {
1389
+ this.waits.splice(this.waits.indexOf(wait), 1);
1390
+ wait.resolve();
1391
+ }
1392
+ }
1393
+ };
1394
+ function withLiveCleared(ops, liveKeys) {
1395
+ if (liveKeys === void 0 || liveKeys.length === 0) return ops;
1396
+ const written = new Set(ops.map((op) => op.path[0]));
1397
+ const clears = liveKeys.filter((key) => !written.has(key)).map((key) => ({ op: "unset", path: [key] }));
1398
+ return clears.length === 0 ? ops : [...ops, ...clears];
1399
+ }
1400
+ function valuesEqual(a, b) {
1401
+ return sameValue(a, b);
1402
+ }
1403
+ function keyedAnswer(value, key) {
1404
+ if (value === null || typeof value !== "object") return value;
1405
+ if (Array.isArray(value)) {
1406
+ const rows = value;
1407
+ const row = rows.find((item) => identityOf(item) === key);
1408
+ if (rows.some((item) => identityOf(item) !== void 0)) return row;
1409
+ return value;
1410
+ }
1411
+ if (Object.prototype.hasOwnProperty.call(value, key)) return value[key];
1412
+ return value;
1413
+ }
1414
+ function identityOf(row) {
1415
+ if (row === null || typeof row !== "object") return void 0;
1416
+ const claimed = row.id ?? row.key;
1417
+ if (typeof claimed === "string") return claimed;
1418
+ if (typeof claimed === "number") return String(claimed);
1419
+ return void 0;
1420
+ }
1421
+ function nextStamp(previous) {
1422
+ const standing = typeof previous === "number" && Number.isFinite(previous) ? previous : 0;
1423
+ return Math.max(Date.now(), standing + 1);
1424
+ }
1425
+ var LOCALE_NAMESPACE = "dshAncesters";
1426
+ var DICTIONARIES = {
1427
+ en: {
1428
+ apply: "Apply",
1429
+ discard: "Discard",
1430
+ saving: "Saving\u2026",
1431
+ unsaved: "Unsaved changes",
1432
+ saveFailed: "Save failed",
1433
+ cancel: "Cancel",
1434
+ close: "Close",
1435
+ search: "Search",
1436
+ searchNoResults: "No matches",
1437
+ searchPlaceholder: "Search\u2026",
1438
+ addSelected: "Add selected",
1439
+ // The page-wide notices (`toasts`): a wait that ended with nothing
1440
+ // answered, and a failure the Host answered without a line of its own.
1441
+ toastStall: "The host did not answer the action \u2014 try it again.",
1442
+ toastError: "The host reported a failure."
1443
+ }
1444
+ };
1445
+ function mergeDictionaries(base = DICTIONARIES, override = {}) {
1446
+ const merged = {};
1447
+ for (const locale of [...Object.keys(base), ...Object.keys(override)]) {
1448
+ merged[locale] = { ...base[locale] === void 0 ? {} : base[locale], ...override[locale] === void 0 ? {} : override[locale] };
1449
+ }
1450
+ return merged;
1451
+ }
1452
+ var TOAST_ROOT_SELECTOR = "div[data-dsh-ancesters-toasts]";
1453
+ var TOAST_DISMISS_MS = 8e3;
1454
+ function noticeText(notice, t) {
1455
+ if (notice.kind === "stall") return t("toastStall");
1456
+ return notice.text !== "" ? notice.text : t("toastError");
1457
+ }
1458
+ function mountToastMirror(source, host, options = {}) {
1459
+ const doc = documentOf(host);
1460
+ if (doc === void 0) return () => void 0;
1461
+ const root = host === void 0 || host === null ? toastRoot(doc) : host;
1462
+ if (root === void 0 || typeof root.appendChild !== "function") return () => void 0;
1463
+ const t = typeof options.t === "function" ? options.t : (key) => key;
1464
+ const dismissMs = typeof options.dismissMs === "number" && options.dismissMs >= 0 ? options.dismissMs : TOAST_DISMISS_MS;
1465
+ let node = void 0;
1466
+ let shownSeq = -1;
1467
+ let timer = void 0;
1468
+ const dismiss = (seq) => {
1469
+ if (typeof source.clearNotice === "function") source.clearNotice(seq);
1470
+ };
1471
+ const stopTimer = () => {
1472
+ if (timer !== void 0) clearTimeout(timer);
1473
+ timer = void 0;
1474
+ };
1475
+ const paint = () => {
1476
+ const snapshot = source.store.getSnapshot();
1477
+ const notice = snapshot.notice ?? null;
1478
+ if (notice === null) {
1479
+ stopTimer();
1480
+ shownSeq = -1;
1481
+ if (node !== void 0) {
1482
+ root.removeChild(node);
1483
+ node = void 0;
1484
+ }
1485
+ return;
1486
+ }
1487
+ if (node === void 0) {
1488
+ node = doc.createElement("div");
1489
+ node.className = shared_default.toast;
1490
+ root.appendChild(node);
1491
+ }
1492
+ node.dataset.dshAncestersToast = String(notice.seq);
1493
+ node.dataset.noticeKind = notice.kind;
1494
+ node.textContent = typeof options.text === "function" ? options.text(notice, snapshot, t) : noticeText(notice, t);
1495
+ const seq = notice.seq;
1496
+ node.onclick = () => dismiss(seq);
1497
+ if (seq !== shownSeq) {
1498
+ shownSeq = seq;
1499
+ stopTimer();
1500
+ if (dismissMs > 0 && typeof setTimeout === "function") timer = setTimeout(() => dismiss(seq), dismissMs);
1501
+ }
1502
+ };
1503
+ const unsubscribe = source.store.subscribe(() => paint());
1504
+ paint();
1505
+ return () => {
1506
+ unsubscribe();
1507
+ stopTimer();
1508
+ if (node !== void 0) {
1509
+ root.removeChild(node);
1510
+ node = void 0;
1511
+ }
1512
+ };
1513
+ }
1514
+ function documentOf(host) {
1515
+ const own = host !== void 0 && host !== null ? host.ownerDocument : void 0;
1516
+ if (own !== void 0 && own !== null) return own;
1517
+ return typeof document === "undefined" ? void 0 : document;
1518
+ }
1519
+ function toastRoot(doc) {
1520
+ const standing = doc.querySelectorAll(TOAST_ROOT_SELECTOR);
1521
+ if (standing.length > 0) return standing[0];
1522
+ const root = doc.createElement("div");
1523
+ root.className = shared_default.toastRoot;
1524
+ root.dataset.dshAncestersToasts = "1";
1525
+ const container = doc.body ?? doc.head;
1526
+ if (container === void 0) return void 0;
1527
+ container.appendChild(root);
1528
+ return root;
1529
+ }
1530
+ var SHEET_ID = "dsh-ancesters-" + hash(cssText);
1531
+ function sheetId(css) {
1532
+ return "dsh-ancesters-" + hash(css);
1533
+ }
1534
+ function injected(id) {
1535
+ return document.querySelectorAll('style[data-dsh-ancesters-css="' + id + '"]').length > 0;
1536
+ }
1537
+ function injectCardStyles(sheets) {
1538
+ if (typeof document === "undefined") return;
1539
+ const wanted = [cssText, ...sheets ?? []].filter((sheet) => typeof sheet === "string" && sheet !== "");
1540
+ for (const sheet of wanted) {
1541
+ const id = sheetId(sheet);
1542
+ if (injected(id)) continue;
1543
+ const styleTag = document.createElement("style");
1544
+ styleTag.dataset.dshAncestersCss = id;
1545
+ styleTag.textContent = sheet;
1546
+ document.head.appendChild(styleTag);
1547
+ }
1548
+ }
1549
+ function hash(text2) {
1550
+ let value = 2166136261 >>> 0;
1551
+ for (let index = 0; index < text2.length; index++) {
1552
+ value ^= text2.charCodeAt(index);
1553
+ value = Math.imul(value, 16777619) >>> 0;
1554
+ }
1555
+ return value.toString(36).padStart(6, "0").slice(0, 6) + "-" + text2.length.toString(36);
1556
+ }
1557
+ function unfencedSettingsWrite(ctx, ns) {
1558
+ const settings = ctx.remote?.settings;
1559
+ const mutate = settings?.mutate;
1560
+ if (settings === void 0 || typeof mutate !== "function") return void 0;
1561
+ return async (ops) => {
1562
+ try {
1563
+ const response = await mutate.call(settings, ns, ops, void 0);
1564
+ return response?.ok === true;
1565
+ } catch {
1566
+ return false;
1567
+ }
1568
+ };
1569
+ }
1570
+ function mountSettingsCard(spec) {
1571
+ if (spec === void 0 || typeof spec !== "object" || Array.isArray(spec)) {
1572
+ throw new TypeError("mountSettingsCard: requires a descriptor object ({ entryId, fields, blocks \u2026 })");
1573
+ }
1574
+ if (typeof spec.entryId !== "string" || spec.entryId === "") {
1575
+ throw new TypeError("mountSettingsCard: the descriptor requires a non-empty `entryId`");
1576
+ }
1577
+ if (spec.pluginId !== void 0 && (typeof spec.pluginId !== "string" || spec.pluginId === "")) {
1578
+ throw new TypeError("mountSettingsCard: the descriptor's `pluginId` must be a non-empty string (the row key needs the package identity)");
1579
+ }
1580
+ if (spec.bundleId !== void 0 && (typeof spec.bundleId !== "string" || spec.bundleId === "")) {
1581
+ throw new TypeError("mountSettingsCard: the descriptor's `bundleId` must be a non-empty string (it is the bundle package the row key is built from)");
1582
+ }
1583
+ if (spec.placement !== void 0 && spec.placement !== "row" && spec.placement !== "bundle") {
1584
+ throw new TypeError('mountSettingsCard: the descriptor\'s `placement` is "row" or "bundle"');
1585
+ }
1586
+ const namespace = spec.localeNamespace === void 0 || spec.localeNamespace === "" ? LOCALE_NAMESPACE : spec.localeNamespace;
1587
+ const bundle = spec.placement === "bundle";
1588
+ const slotName = bundle ? "plugins.bundle.config" : "plugins.row.config";
1589
+ return {
1590
+ inject: ["configForms", "slots", "locale", "remote", "remote.settings"],
1591
+ apply(ctx) {
1592
+ if (ctx === void 0 || typeof ctx !== "object" || ctx.slots === void 0 || typeof ctx.slots.inject !== "function") {
1593
+ throw new TypeError("mountSettingsCard.apply: needs the browser plugin context (the shell's `slots` service)");
1594
+ }
1595
+ injectCardStyles();
1596
+ const dicts = mergeDictionaries(DICTIONARIES, spec.locale ?? {});
1597
+ if (ctx.locale !== void 0 && typeof ctx.locale.register === "function") {
1598
+ ctx.effect(() => ctx.locale.register(namespace, dicts), `${spec.entryId}: dictionaries`);
1599
+ }
1600
+ const form = ctx.configForms === void 0 ? void 0 : ctx.configForms.get(spec.entryId);
1601
+ const controller = new CardController(form, {
1602
+ fields: spec.fields,
1603
+ errorField: spec.errorField,
1604
+ settleDeadlineMs: spec.settleDeadlineMs,
1605
+ settleField: spec.settleField,
1606
+ unfencedMutate: unfencedSettingsWrite(ctx, spec.entryId)
1607
+ });
1608
+ ctx.effect(() => () => controller.dispose(), `${spec.entryId}: card controller`);
1609
+ const translate = ctx.locale !== void 0 && typeof ctx.locale.bind === "function" ? ctx.locale.bind(namespace) : (key) => key;
1610
+ if (spec.toasts === true || spec.toasts !== void 0 && typeof spec.toasts === "object") {
1611
+ const toastHost = typeof spec.toasts === "object" ? spec.toasts.host : void 0;
1612
+ const dismissMs = typeof spec.toasts === "object" ? spec.toasts.dismissMs : void 0;
1613
+ const text2 = typeof spec.toasts === "object" ? spec.toasts.text : void 0;
1614
+ ctx.effect(
1615
+ () => mountToastMirror(controller, toastHost, { t: translate, dismissMs, text: text2 }),
1616
+ `${spec.entryId}: toast host`
1617
+ );
1618
+ }
1619
+ const body = (props) => {
1620
+ const view = String(props?.view) === "page" ? "page" : "summary";
1621
+ controller.adoptForm(props?.form === void 0 ? void 0 : props.form);
1622
+ const snapshot = controller.getSnapshot();
1623
+ if (view === "summary") {
1624
+ if (typeof spec.renderSummary !== "function") return null;
1625
+ return spec.renderSummary(snapshot, translate);
1626
+ }
1627
+ if (!snapshot.available && (spec.blocks ?? []).length === 0) return null;
1628
+ const renders = (spec.blocks ?? []).map(
1629
+ (block, index) => renderBlock(block, { snapshot, controller, view, t: translate })
1630
+ );
1631
+ const composed = renders.filter((rendered) => rendered !== null && rendered !== void 0);
1632
+ if (spec.errorLine === false) return composed;
1633
+ const error = snapshot.actionError !== "" ? snapshot.actionError : snapshot.lastError;
1634
+ const line = ErrorLine({ text: error === "" ? "" : error });
1635
+ return line === null ? composed : [...composed, line];
1636
+ };
1637
+ const pageKey = bundle ? bundleConfigKey(spec.bundleId ?? spec.pluginId) : rowConfigKey(spec.bundleId ?? spec.pluginId ?? "", spec.entryId);
1638
+ ctx.slots.inject(slotName, () => {
1639
+ ctx.slots.register(
1640
+ {
1641
+ name: slotName,
1642
+ key: pageKey,
1643
+ locale: namespace,
1644
+ inject: () => controller.inject()
1645
+ },
1646
+ body
1647
+ );
1648
+ });
1649
+ const composer = spec.composer;
1650
+ if (composer !== void 0) {
1651
+ if (typeof composer.id !== "string" || composer.id === "" || typeof composer.bind !== "function") {
1652
+ throw new TypeError("mountSettingsCard: `composer` needs a non-empty `id` and a `bind` function");
1653
+ }
1654
+ const pills = (props) => {
1655
+ const subscribe = props?.useAncestersComposer;
1656
+ const snapshot = typeof subscribe === "function" ? subscribe((state) => state) : controller.getSnapshot();
1657
+ const openState = react4.useState("");
1658
+ const open = openState[0];
1659
+ const setOpen = openState[1];
1660
+ if (snapshot?.available !== true) return null;
1661
+ const t = typeof props?.t === "function" ? props.t : translate;
1662
+ const declared = composer.bind(snapshot, controller, t) ?? [];
1663
+ if (declared.length === 0) return null;
1664
+ return (0, import_jsx_runtime.jsx)("span", {
1665
+ className: shared_default.composerPills,
1666
+ children: declared.map(
1667
+ (pill) => (0, import_jsx_runtime.jsx)(
1668
+ ComposerPill,
1669
+ { ...pill, open: open === pill.key, setOpen: (next) => setOpen(next ? pill.key : "") },
1670
+ pill.key
1671
+ )
1672
+ )
1673
+ });
1674
+ };
1675
+ ctx.slots.inject("conversation.input.left", () => {
1676
+ ctx.slots.register(
1677
+ {
1678
+ name: "conversation.input.left",
1679
+ // The slot is kind `list`: entries are deduped and
1680
+ // placed by a stable `id`, not a keyed `key`.
1681
+ id: composer.id,
1682
+ locale: namespace,
1683
+ inject: () => ({ hooks: { ancestersComposer: controller.store }, controller })
1684
+ },
1685
+ pills
1686
+ );
1687
+ });
1688
+ }
1689
+ }
1690
+ };
1691
+ }
1692
+ function bundleConfigKey(pkgName) {
1693
+ if (typeof pkgName !== "string" || pkgName === "") {
1694
+ throw new TypeError('mountSettingsCard: `placement: "bundle"` needs `pluginId` or `bundleId` (the bundle page is keyed by its package name)');
1695
+ }
1696
+ return pkgName;
1697
+ }
1698
+ function renderBlock(block, props) {
1699
+ if (block.hideWhenUnavailable === true && props.snapshot.available !== true) return null;
1700
+ const compose = { ...block.props ?? {} };
1701
+ if (block.field !== void 0) {
1702
+ compose.value = props.snapshot.values[block.field];
1703
+ compose.field = block.field;
1704
+ }
1705
+ if (block.liveKeys !== void 0) {
1706
+ for (const liveKey of block.liveKeys) compose[liveKey] = props.snapshot.values[liveKey];
1707
+ }
1708
+ if (typeof block.bind === "function") Object.assign(compose, block.bind(props.snapshot, props.controller, props.t));
1709
+ if (block.action !== void 0) compose.action = composeAction(block.action, props.controller, block.field);
1710
+ const component = typeof block.block === "string" ? BlocksByName[block.block] : block.block;
1711
+ if (typeof component !== "function") return null;
1712
+ return component({ ...compose, t: props.t });
1713
+ }
1714
+ function composeAction(action, controller, field2) {
1715
+ if (typeof action === "function") return action(controller);
1716
+ if (action === "stage") return (value) => controller.stage(String(field2 ?? ""), value);
1717
+ if (action === "apply") return () => controller.applyStaged();
1718
+ if (action === "discard") return () => controller.discard();
1719
+ if (action === "write") return (value) => controller.writeField(String(field2 ?? ""), value);
1720
+ return (value) => value === void 0 ? controller.writeTrigger(String(field2 ?? "")) : controller.writeEntryTrigger(String(field2 ?? ""), value);
1721
+ }
1722
+ var BlocksByName = Blocks;
1723
+ function mountSettingsCard2(spec) {
1724
+ return mountSettingsCard(spec);
1725
+ }
1726
+
1727
+ // src/client/catalog.ts
1728
+ var ID_MAX_LENGTH = 1024;
1729
+ var PATH_PATTERN = /^[^\x00-\x1f\x7f]{1,1024}$/u;
1730
+ var NAME_MAX_LENGTH = 200;
1731
+ var ENTRY_CAP = 50;
1732
+ function isValidEntryId(id) {
1733
+ if (id === "" || id.length > ID_MAX_LENGTH) return false;
1734
+ return PATH_PATTERN.test(id);
1735
+ }
1736
+ function entryLabel(entry) {
1737
+ const name = entry.name.trim();
1738
+ return name === "" ? entry.id : name;
1739
+ }
1740
+ function normalizeEntries(value) {
1741
+ if (!Array.isArray(value)) return [];
1742
+ const out = [];
1743
+ const seen = /* @__PURE__ */ new Set();
1744
+ for (const item of value) {
1745
+ if (out.length === ENTRY_CAP) break;
1746
+ if (item === null || typeof item !== "object" || Array.isArray(item)) continue;
1747
+ const raw = item;
1748
+ const id = typeof raw.id === "string" ? raw.id.trim() : "";
1749
+ if (!isValidEntryId(id) || seen.has(id)) continue;
1750
+ seen.add(id);
1751
+ const name = typeof raw.name === "string" ? raw.name.trim() : "";
1752
+ out.push({ id, name: name.slice(0, NAME_MAX_LENGTH) });
1753
+ }
1754
+ return out;
1755
+ }
1756
+
1757
+ // src/client/card/index.ts
1758
+ var react8 = __toESM(require("react"), 1);
1759
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1760
+
1761
+ // dsh-css:/home/roberto/dev/dsh/dsh-chrome-mcp/src/client/styles/ChromeMcpCard.module.css
1762
+ var cssText2 = '.e5pfpa_body{flex-direction:column;gap:10px;display:flex}.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_badge{color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-layer-1);border-radius:10px;padding:1px 8px;font-size:11px;line-height:16px;display:inline-flex;align-items:center;flex:none}.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:0 1 auto;min-width:0;overflow-wrap:anywhere;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}.e5pfpa_footer{border-top:.5px solid var(--dsw-alias-border-l2);align-items:center;justify-content:flex-end;gap:8px;padding:12px 0 0;display:flex}.e5pfpa_footerError{color:var(--dsw-alias-label-error);text-align:right;min-width:0;flex:1;margin:0;font-size:12px;line-height:1.5}.e5pfpa_footerButton{border:.5px solid var(--dsw-alias-border-l3);border-radius:8px;cursor:pointer;color:var(--dsw-alias-label-secondary);background:transparent;padding:5px 14px;font-size:13px;line-height:1.5}.e5pfpa_footerButton:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.e5pfpa_footerButton:disabled{cursor:default;opacity:.4}.e5pfpa_footerButton:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.e5pfpa_footerButtonPrimary{color:var(--dsw-alias-bg-layer-3);font-weight:600;background:var(--dsw-alias-label-primary);border-color:transparent}.e5pfpa_footerButtonPrimary:hover:not(:disabled){color:var(--dsw-alias-bg-layer-3);background:var(--dsw-alias-label-secondary);border-color:transparent}\n';
1763
+ var ChromeMcpCard_default = { "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", "badge": "e5pfpa_badge", "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", "footer": "e5pfpa_footer", "footerError": "e5pfpa_footerError", "footerButton": "e5pfpa_footerButton", "footerButtonPrimary": "e5pfpa_footerButtonPrimary" };
1764
+
1765
+ // src/client/card/flags.ts
1766
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1767
+
1768
+ // src/client/plugin-meta.ts
1769
+ var ENTRY_ID = "mcp-chrome";
1770
+ var PLUGIN_ID = "@comecaramelos/dsh-chrome-mcp";
1771
+ var BUNDLE_ID = "@comecaramelos/dsh-mcp-manager-bundle";
1772
+ var ROW_CONFIG_KEY = `${BUNDLE_ID}#${ENTRY_ID}`;
1773
+ var LOCALE_NAMESPACE2 = "chromeMcp";
1774
+ var CONFIGURATION_URL = "https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/configuration.md";
1775
+ var RUN_SETTLE_TIMEOUT_MS2 = 9e4;
1776
+ var LIVE_KEYS = [
1777
+ "lastError",
1778
+ "chromeMissing",
1779
+ "executableDiscoveryRevision",
1780
+ "chromeVersion",
1781
+ "executableStatus",
1782
+ "effectiveChromePath",
1783
+ "effectiveSource",
1784
+ "stderrEffective",
1785
+ "defaultExtraFlags",
1786
+ "wslExtraFlags",
1787
+ "wslWindowsExecutable",
1788
+ "windowsChromeStatus",
1789
+ "carriesConnectionMode",
1790
+ "runningFlags",
1791
+ "runningNote"
1792
+ ];
1793
+
1794
+ // src/client/card/icons.ts
1795
+ var CHEVRON_PATH2 = "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";
1796
+ var TRASH_PATH2 = "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";
1797
+ var CLOSE_PATHS2 = [
1798
+ "M14.1168 13.197L13.197 14.1167L1.8833 2.80303L2.80309 1.88324L14.1168 13.197Z",
1799
+ "M13.197 1.88326L14.1168 2.80305L2.80309 14.1168L1.8833 13.197L13.197 1.88326Z"
1800
+ ];
1801
+ var DOCS_PATHS2 = [
1802
+ "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",
1803
+ "M15 3h6v6",
1804
+ "M10 14 21 3"
1805
+ ];
14
1806
 
15
- /**
16
- * Chrome DevTools MCP settings + status card, browser half.
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"];
1807
+ // src/client/card/rows.ts
1808
+ var react5 = __toESM(require("react"), 1);
1809
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1810
+ function RowList(props) {
1811
+ var served = Array.isArray(props.values) ? props.values : [];
1812
+ var writable = props.writable === true;
1813
+ var busy = props.busy === true;
1814
+ var labels = Array.isArray(props.labels) ? props.labels : [];
1815
+ var placeholders = Array.isArray(props.placeholders) ? props.placeholders : [];
1816
+ var columns = Math.max(1, labels.length);
1817
+ var blankRow = [];
1818
+ for (var blank = 0; blank < columns; blank++) blankRow.push("");
1819
+ var draftState = react5.useState(null);
1820
+ var draft = draftState[0];
1821
+ var setDraft = draftState[1];
1822
+ var focusedState = react5.useState(false);
1823
+ var focused = focusedState[0];
1824
+ var setFocused = focusedState[1];
1825
+ var servedKey = JSON.stringify(served);
1826
+ var syncState = react5.useState("");
1827
+ var servedSync = syncState[0];
1828
+ var setServedSync = syncState[1];
1829
+ if (servedKey !== servedSync) {
1830
+ setServedSync(servedKey);
1831
+ if (!focused) setDraft(null);
1832
+ }
1833
+ var rows = draft === null ? served : draft;
1834
+ var save = (next) => {
1835
+ if (typeof props.save !== "function") return Promise.resolve();
1836
+ setDraft(next);
1837
+ return Promise.resolve(props.save(next)).catch(() => {
1838
+ });
1839
+ };
1840
+ var withDraft = (make) => make(draft === null ? served : draft);
1841
+ var patchRow = (index2, column, value) => {
1842
+ setFocused(true);
1843
+ setDraft(
1844
+ withDraft(
1845
+ (current) => current.map(
1846
+ (row, at) => at === index2 ? row.map((cell, field2) => field2 === column ? value : cell) : row
1847
+ )
1848
+ )
1849
+ );
1850
+ };
1851
+ var removeRow = (index2) => {
1852
+ if (!writable || rows[index2] === void 0) return;
1853
+ save(rows.filter((_row, at) => at !== index2));
1854
+ };
1855
+ var addRow = () => {
1856
+ if (!writable) return;
1857
+ setDraft(withDraft((current) => current.concat([blankRow])));
1858
+ };
1859
+ var commit = () => {
1860
+ if (draft === null) return;
1861
+ save(rows);
1862
+ };
1863
+ var fieldNode = (row, index2, column) => (0, import_jsx_runtime10.jsx)("input", {
1864
+ className: props.inputClassName ? ChromeMcpCard_default.catalogInput + " " + props.inputClassName : ChromeMcpCard_default.catalogInput,
1865
+ type: "text",
1866
+ value: row[column] === void 0 ? "" : row[column],
1867
+ placeholder: placeholders[column],
1868
+ "aria-label": labels[column] + " " + (index2 + 1),
1869
+ title: column === 0 ? row[column] : void 0,
1870
+ disabled: !writable,
1871
+ spellCheck: false,
1872
+ autoComplete: "off",
1873
+ autocapitalize: "off",
1874
+ onChange: (event) => {
1875
+ patchRow(index2, column, event && event.target ? String(event.target.value ?? "") : "");
1876
+ },
1877
+ onFocus: () => setFocused(true),
1878
+ onBlur: () => {
1879
+ setFocused(false);
1880
+ commit();
1881
+ },
1882
+ onKeyDown: (event) => {
1883
+ if (event && event.key === "Enter") {
1884
+ setFocused(false);
1885
+ commit();
1886
+ }
1887
+ }
1888
+ }, column);
1889
+ var entryNode = (row, index2) => {
1890
+ var cells = [];
1891
+ var width = row.length === 0 ? columns : row.length;
1892
+ for (var column = 0; column < width; column++) cells.push(fieldNode(row, index2, column));
1893
+ cells.push(
1894
+ (0, import_jsx_runtime10.jsx)("button", {
1895
+ type: "button",
1896
+ className: ChromeMcpCard_default.iconButton + " " + ChromeMcpCard_default.iconButtonDanger,
1897
+ "aria-label": props.removeLabel + " " + (index2 + 1),
1898
+ title: props.removeLabel,
1899
+ disabled: !writable,
1900
+ onClick: () => {
1901
+ removeRow(index2);
1902
+ },
1903
+ children: (0, import_jsx_runtime10.jsx)("svg", {
1904
+ width: 14,
1905
+ height: 14,
1906
+ viewBox: "0 0 16 16",
1907
+ fill: "none",
1908
+ "aria-hidden": "true",
1909
+ children: (0, import_jsx_runtime10.jsx)("path", {
1910
+ d: TRASH_PATH2,
1911
+ stroke: "currentColor",
1912
+ strokeWidth: 1.3,
1913
+ strokeLinecap: "round",
1914
+ strokeLinejoin: "round"
1915
+ })
1916
+ })
1917
+ })
1918
+ );
1919
+ return (0, import_jsx_runtime10.jsx)("div", {
1920
+ className: ChromeMcpCard_default.catalogEntry,
1921
+ children: (0, import_jsx_runtime10.jsxs)("div", {
1922
+ className: props.rowClassName ? ChromeMcpCard_default.catalogRow + " " + props.rowClassName : ChromeMcpCard_default.catalogRow,
1923
+ children: cells
1924
+ })
1925
+ }, index2);
1926
+ };
1927
+ var body = [];
1928
+ if (typeof props.renderAction === "function") body.push(props.renderAction(rows, save));
1929
+ var visible = [];
1930
+ for (var index = 0; index < rows.length; index++) visible.push(entryNode(rows[index], index));
1931
+ body = body.concat(visible);
1932
+ body.push(
1933
+ (0, import_jsx_runtime10.jsx)("button", {
1934
+ type: "button",
1935
+ className: ChromeMcpCard_default.addButton,
1936
+ disabled: !writable || busy,
1937
+ onClick: addRow,
1938
+ children: props.addLabel
1939
+ })
1940
+ );
1941
+ var emptyLine = rows.length === 0 ? (0, import_jsx_runtime10.jsx)("p", { className: ChromeMcpCard_default.empty, children: props.emptyText }) : null;
1942
+ if (props.disclosure !== true) {
1943
+ return [emptyLine].concat(body);
1944
+ }
1945
+ return [
1946
+ emptyLine,
1947
+ (0, import_jsx_runtime10.jsxs)("details", {
1948
+ className: ChromeMcpCard_default.disclosure,
1949
+ children: [
1950
+ (0, import_jsx_runtime10.jsxs)("summary", {
1951
+ className: ChromeMcpCard_default.disclosureSummary,
1952
+ children: [
1953
+ (0, import_jsx_runtime10.jsx)("span", { className: ChromeMcpCard_default.disclosureLabel, children: props.rowsLabel }),
1954
+ (0, import_jsx_runtime10.jsx)("span", { className: ChromeMcpCard_default.disclosureCount, children: String(rows.length) })
1955
+ ]
1956
+ }),
1957
+ (0, import_jsx_runtime10.jsx)("div", { className: ChromeMcpCard_default.disclosureBody, children: body })
1958
+ ]
1959
+ })
1960
+ ];
1961
+ }
43
1962
 
44
- /** Configuration docs link (must match host CONFIGURATION_DOCS_URL). */
45
- var CONFIGURATION_URL =
46
- "https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/configuration.md";
1963
+ // src/client/card/flags.ts
1964
+ function rowsOf(flags) {
1965
+ return flags.map((flag) => [flag]);
1966
+ }
1967
+ function flagsOf(rows) {
1968
+ var flags = [];
1969
+ for (const row of rows) {
1970
+ var flag = typeof row === "undefined" || row === null ? "" : String(row[0] ?? "").trim();
1971
+ if (flag === "") continue;
1972
+ if (flags.indexOf(flag) !== -1) continue;
1973
+ flags.push(flag);
1974
+ }
1975
+ return flags;
1976
+ }
1977
+ function ExtraFlagsEditor(props) {
1978
+ var t = props.t;
1979
+ var writable = props.writable;
1980
+ var flags = Array.isArray(props.flags) ? props.flags : [];
1981
+ var defaults = Array.isArray(props.defaultFlags) ? props.defaultFlags : [];
1982
+ var wsl = Array.isArray(props.wslFlags) ? props.wslFlags : [];
1983
+ var save = (next) => {
1984
+ if (typeof props.saveFlags !== "function") return Promise.resolve();
1985
+ return Promise.resolve(props.saveFlags(flagsOf(next)));
1986
+ };
1987
+ var restoreAction = (visible) => {
1988
+ var current = flagsOf(visible);
1989
+ var merged = current.concat(defaults.filter((flag) => current.indexOf(flag) === -1));
1990
+ return (0, import_jsx_runtime11.jsx)("button", {
1991
+ type: "button",
1992
+ className: ChromeMcpCard_default.linkButton,
1993
+ // The defaults list is host-served — with nothing to recover there
1994
+ // is nothing to do.
1995
+ disabled: !writable || defaults.length === 0,
1996
+ title: t("flagsRestoreTitle"),
1997
+ onClick: () => {
1998
+ if (!writable) return;
1999
+ save(merged.map((flag) => [flag]));
2000
+ },
2001
+ children: t("flagsRestore")
2002
+ });
2003
+ };
2004
+ var wslAction = (visible) => {
2005
+ var current = flagsOf(visible);
2006
+ var merged = current.concat(wsl.filter((flag) => current.indexOf(flag) === -1));
2007
+ return (0, import_jsx_runtime11.jsx)("button", {
2008
+ type: "button",
2009
+ className: ChromeMcpCard_default.linkButton,
2010
+ // The recommended list is host-served — with every one of them
2011
+ // already in the rows there is nothing left to add.
2012
+ disabled: !writable || wsl.length === 0,
2013
+ title: t("flagsWslTitle"),
2014
+ onClick: () => {
2015
+ if (!writable) return;
2016
+ save(merged.map((flag) => [flag]));
2017
+ },
2018
+ children: t("flagsWsl")
2019
+ });
2020
+ };
2021
+ var renderActions = (visible) => (0, import_jsx_runtime11.jsxs)("div", {
2022
+ className: ChromeMcpCard_default.linkButtonRow,
2023
+ children: [restoreAction(visible), wslAction(visible)]
2024
+ });
2025
+ return (0, import_jsx_runtime11.jsxs)("div", {
2026
+ className: ChromeMcpCard_default.catalog,
2027
+ children: [
2028
+ (0, import_jsx_runtime11.jsxs)("div", {
2029
+ className: ChromeMcpCard_default.field,
2030
+ children: [
2031
+ (0, import_jsx_runtime11.jsxs)("div", {
2032
+ className: ChromeMcpCard_default.fieldRow,
2033
+ children: [
2034
+ (0, import_jsx_runtime11.jsx)("span", { className: ChromeMcpCard_default.fieldTitle, children: t("flagsTitle") }),
2035
+ // The guide link rides the same field row the picker's
2036
+ // pill occupies.
2037
+ (0, import_jsx_runtime11.jsxs)("a", {
2038
+ className: ChromeMcpCard_default.docsLink,
2039
+ href: CONFIGURATION_URL,
2040
+ target: "_blank",
2041
+ rel: "noopener noreferrer",
2042
+ title: t("docsMessage"),
2043
+ children: [
2044
+ (0, import_jsx_runtime11.jsx)("svg", {
2045
+ className: ChromeMcpCard_default.docsIcon,
2046
+ width: 14,
2047
+ height: 14,
2048
+ viewBox: "0 0 24 24",
2049
+ fill: "none",
2050
+ stroke: "currentColor",
2051
+ strokeWidth: 2,
2052
+ strokeLinecap: "round",
2053
+ strokeLinejoin: "round",
2054
+ "aria-hidden": "true",
2055
+ children: DOCS_PATHS2.map((d, at) => (0, import_jsx_runtime11.jsx)("path", { d }, at))
2056
+ }),
2057
+ (0, import_jsx_runtime11.jsx)("span", { className: ChromeMcpCard_default.docsLinkLabel, children: t("docsLinkLabel") })
2058
+ ]
2059
+ })
2060
+ ]
2061
+ }),
2062
+ (0, import_jsx_runtime11.jsx)("div", { className: ChromeMcpCard_default.fieldDesc, children: t("flagsHint") })
2063
+ ]
2064
+ }),
2065
+ // One row per saved flag: the single field is the flag itself, and
2066
+ // the two append controls — Restore defaults and Flags WSL — ride
2067
+ // the list's action line.
2068
+ (0, import_jsx_runtime11.jsx)(RowList, {
2069
+ values: rowsOf(flags),
2070
+ labels: [t("flagsValueLabel")],
2071
+ placeholders: [t("flagsValuePlaceholder")],
2072
+ writable,
2073
+ save,
2074
+ renderAction: renderActions,
2075
+ emptyText: t("flagsEmpty"),
2076
+ rowsLabel: t("flagsRows"),
2077
+ addLabel: t("addFlag"),
2078
+ removeLabel: t("removeFlag"),
2079
+ rowClassName: ChromeMcpCard_default.catalogRowSingle,
2080
+ inputClassName: ChromeMcpCard_default.catalogInputMono,
2081
+ disclosure: true
2082
+ })
2083
+ ]
2084
+ });
2085
+ }
47
2086
 
48
- // ── card styles ────────────────────────────────────────────────────
49
- var CSS = {
50
- card: "dshcmc_card",
51
- cardOpen: "dshcmc_cardOpen",
52
- header: "dshcmc_header",
53
- headText: "dshcmc_headText",
54
- nameRow: "dshcmc_nameRow",
55
- name: "dshcmc_name",
56
- desc: "dshcmc_desc",
57
- chevron: "dshcmc_chevron",
58
- chevronOpen: "dshcmc_chevronOpen",
59
- body: "dshcmc_body",
60
- field: "dshcmc_field",
61
- rowText: "dshcmc_rowText",
62
- fieldTitle: "dshcmc_fieldTitle",
63
- fieldDesc: "dshcmc_fieldDesc",
64
- row: "dshcmc_row",
65
- button: "dshcmc_button",
66
- readOnly: "dshcmc_readOnly",
67
- dot: "dshcmc_dot",
68
- dotError: "dshcmc_dotError",
69
- link: "dshcmc_link",
70
- value: "dshcmc_value",
71
- docs: "dshcmc_docs",
72
- editor: "dshcmc_editor",
73
- text: "dshcmc_text"
74
- };
75
- var css =
76
- "." + CSS.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}" +
77
- "." + CSS.card + ":hover{border-color:var(--dsw-alias-label-dimmed)}" +
78
- "." + CSS.cardOpen + "{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}" +
79
- "." + CSS.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}" +
80
- "." + CSS.header + ":focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}" +
81
- "." + CSS.headText + "{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}" +
82
- "." + CSS.nameRow + "{align-items:center;gap:8px;min-width:0;display:flex}" +
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
- }
2087
+ // src/client/card/picker.ts
2088
+ var import_jsx_runtime12 = require("react/jsx-runtime");
2089
+ var import_dsh_client_ui_primitives5 = require("@deepseek-ai/dsh-client-ui-primitives");
2090
+ function CatalogPicker(props) {
2091
+ var entries = props.entries;
2092
+ var options = props.options;
2093
+ var selectedId = props.selectedId;
2094
+ var writable = props.writable;
2095
+ var busy = props.busy === true;
2096
+ var rows = entries.map((entry) => [entry.id, entry.name]);
2097
+ var saveEntries = (next) => {
2098
+ if (typeof props.saveEntries !== "function") return Promise.resolve();
2099
+ return Promise.resolve(props.saveEntries(next.map((row) => ({ id: row[0] ?? "", name: row[1] ?? "" }))));
2100
+ };
2101
+ var fetchAction = () => (0, import_jsx_runtime12.jsx)("button", {
2102
+ type: "button",
2103
+ className: ChromeMcpCard_default.linkButton,
2104
+ disabled: !writable || busy,
2105
+ onClick: () => {
2106
+ props.refresh();
2107
+ },
2108
+ children: busy ? props.busyLabel : props.refreshLabel
2109
+ });
2110
+ var windowsAction = () => (0, import_jsx_runtime12.jsx)("button", {
2111
+ type: "button",
2112
+ className: ChromeMcpCard_default.linkButton,
2113
+ disabled: props.windowsDisabled === true,
2114
+ title: props.windowsTitle,
2115
+ onClick: () => {
2116
+ if (typeof props.windowsOnOpen === "function") props.windowsOnOpen();
2117
+ },
2118
+ children: props.windowsLabel
2119
+ });
2120
+ var renderActions = () => (0, import_jsx_runtime12.jsxs)("div", {
2121
+ className: ChromeMcpCard_default.linkButtonRow,
2122
+ children: [fetchAction(), windowsAction()]
2123
+ });
2124
+ return (0, import_jsx_runtime12.jsxs)("div", {
2125
+ className: ChromeMcpCard_default.catalog,
2126
+ children: [
2127
+ (0, import_jsx_runtime12.jsxs)("div", {
2128
+ className: ChromeMcpCard_default.field,
2129
+ children: [
2130
+ (0, import_jsx_runtime12.jsxs)("div", {
2131
+ className: ChromeMcpCard_default.fieldRow,
2132
+ children: [
2133
+ (0, import_jsx_runtime12.jsx)("span", { className: ChromeMcpCard_default.fieldTitle, children: props.selectorLabel }),
2134
+ (0, import_jsx_runtime12.jsx)("span", {
2135
+ className: ChromeMcpCard_default.selectorWrap,
2136
+ children: (0, import_jsx_runtime12.jsx)(import_dsh_client_ui_primitives5.Menu, {
2137
+ open: props.menuOpen,
2138
+ onClose: () => {
2139
+ props.setMenuOpen(false);
2140
+ },
2141
+ items: options,
2142
+ selectedId: options.some((option) => option.id === selectedId) ? selectedId : "",
2143
+ onSelect: (id) => {
2144
+ props.setMenuOpen(false);
2145
+ props.select(id);
2146
+ },
2147
+ align: "end",
2148
+ portal: true,
2149
+ anchor: (0, import_jsx_runtime12.jsxs)("button", {
2150
+ type: "button",
2151
+ className: ChromeMcpCard_default.selector,
2152
+ "aria-haspopup": "menu",
2153
+ "aria-expanded": props.menuOpen,
2154
+ disabled: !writable || busy,
2155
+ title: props.selectedTitle,
2156
+ onClick: () => {
2157
+ props.setMenuOpen(!props.menuOpen);
2158
+ },
2159
+ children: [
2160
+ // The reference card hands the pill a bare label;
2161
+ // the path needs its own truncating span.
2162
+ (0, import_jsx_runtime12.jsx)("span", { className: ChromeMcpCard_default.selectorLabel, children: props.selectedLabel }),
2163
+ (0, import_jsx_runtime12.jsx)("svg", {
2164
+ width: 14,
2165
+ height: 14,
2166
+ className: ChromeMcpCard_default.pillChevron,
2167
+ viewBox: "0 0 14 14",
2168
+ fill: "none",
2169
+ xmlns: "http://www.w3.org/2000/svg",
2170
+ children: (0, import_jsx_runtime12.jsx)("path", { d: CHEVRON_PATH2, fill: "currentColor" })
2171
+ })
2172
+ ]
2173
+ })
2174
+ })
2175
+ })
2176
+ ]
2177
+ }),
2178
+ (0, import_jsx_runtime12.jsx)("div", { className: ChromeMcpCard_default.fieldDesc, children: props.hint })
2179
+ ]
2180
+ }),
2181
+ // The saved rows: two fields per row, folded behind the disclosure,
2182
+ // with the fetch action sitting inside the body.
2183
+ (0, import_jsx_runtime12.jsx)(RowList, {
2184
+ values: rows,
2185
+ labels: [props.idLabel, props.nameLabel],
2186
+ placeholders: [props.idPlaceholder, props.namePlaceholder],
2187
+ writable,
2188
+ busy,
2189
+ save: saveEntries,
2190
+ renderAction: renderActions,
2191
+ emptyText: props.emptyText,
2192
+ rowsLabel: props.rowsLabel,
2193
+ addLabel: props.addLabel,
2194
+ removeLabel: props.removeLabel,
2195
+ disclosure: true
2196
+ }),
2197
+ props.statusLine === void 0 || props.statusLine === null ? null : props.statusLine
2198
+ ]
2199
+ });
2200
+ }
121
2201
 
122
- // ── locale dictionaries ────────────────────────────────────────────
123
- var en = {
124
- title: "Chrome DevTools MCP",
125
- description: "Chrome DevTools MCP server (chrome-devtools-mcp) settings and status",
126
- flagsTitle: "Extra flags",
127
- flagsHint: "One flag per line, appended to `npx -y chrome-devtools-mcp@latest`. Defaults to --no-usage-statistics, --no-performance-crux.",
128
- flagsPlaceholder: "No extra flags",
129
- flagsSave: "Save flags",
130
- versionTitle: "Detected Chrome version",
131
- check: "Re-check Chrome",
132
- checking: "Checking…",
133
- docsMessage: "Find the complete list of server parameters (e.g., --headless, --isolated, --slim) and how to configure WebSocket connections in the Configuration Guide:",
134
- statusError: "Error",
135
- statusOk: "OK",
136
- chromeMissing: "Chrome executable not found",
137
- readOnly: "Settings are read-only in this deployment.",
138
- expand: "Expand",
139
- collapse: "Collapse"
140
- };
141
- // ── card controller ────────────────────────────────────────────────
142
- /** Re-describe cadence (ms) while waiting for the host's check run. */
143
- var CHECK_POLL_TICK_MS = 700;
144
- /** Give up waiting for the check revision to advance after this (ms). The host check subprocess times out at 15s. */
145
- var CHECK_POLL_TIMEOUT_MS = 20000;
146
- /**
147
- * Projects the bound `chrome-mcp` settings scope onto a snapshot store
148
- * the card reads through its `useChromeMcpCard` hook, and owns the two
149
- * write actions (save flags, re-check). `mirror` is the shared
150
- * settings describe mirror (`settingsScope.describe()`) the re-check
151
- * action re-reads while the host's in-memory check status is still en
152
- * route — module plugins have no client→host push channel beyond
153
- * persisted writes.
154
- */
155
- var ChromeMcpCardController = class {
156
- constructor(scope, mirror) {
157
- this.scope = scope;
158
- this.mirror = mirror;
159
- this.disposed = false;
160
- this.pollTimer = null;
161
- this.pollGeneration = 0;
162
- this.checkWaitStarted = false;
163
- this.store = clientStore.createSnapshotStore({
164
- available: false,
165
- writable: false,
166
- extraFlags: [],
167
- lastError: "",
168
- chromeMissing: false,
169
- checkRevision: 0,
170
- chromeVersion: "",
171
- actionError: ""
172
- });
173
- this.unsubscribe = scope.subscribe(() => this.publish());
174
- this.publish();
175
- }
176
- /** Republish the latest scope snapshot into the card store. */
177
- publish() {
178
- if (this.disposed) return;
179
- var previous = this.store.getSnapshot();
180
- var snapshot = this.scope.getSnapshot();
181
- if (snapshot.status !== "ready" || snapshot.value === void 0) {
182
- this.store.set({
183
- available: false,
184
- writable: false,
185
- extraFlags: [],
186
- lastError: "",
187
- chromeMissing: false,
188
- checkRevision: 0,
189
- chromeVersion: "",
190
- actionError: previous.actionError
191
- });
192
- return;
193
- }
194
- var value = snapshot.value;
195
- // The status fields (lastError / chromeMissing / checkRevision /
196
- // chromeVersion) ride the composition **base** layer: describe()
197
- // clones that entry object fresh on every read, while the frozen
198
- // resolved `value` folds the base in only at commits. Base-only
199
- // host changes — the registration-time check, bridge-log and
200
- // tool-call captures — are therefore invisible to `value`, so
201
- // status is read from `base`, falling back to `value` only when
202
- // the read carries no base layer. `extraFlags` keeps reading
203
- // `value`: the persisted user layer wins there, not the
204
- // host-seeded entry field.
205
- var base = snapshot.base ?? {};
206
- var servedRevision =
207
- typeof base.checkRevision === "number"
208
- ? base.checkRevision
209
- : typeof value.checkRevision === "number"
210
- ? value.checkRevision
211
- : 0;
212
- this.store.set({
213
- available: true,
214
- writable: snapshot.writable === true,
215
- extraFlags: Array.isArray(value.extraFlags) ? value.extraFlags : [],
216
- lastError: typeof base.lastError === "string" ? base.lastError : (value.lastError ?? ""),
217
- chromeMissing: base.chromeMissing === undefined ? value.chromeMissing === true : base.chromeMissing === true,
218
- checkRevision: servedRevision,
219
- chromeVersion: typeof base.chromeVersion === "string" ? base.chromeVersion : (typeof value.chromeVersion === "string" ? value.chromeVersion : ""),
220
- actionError: ""
221
- });
222
- // Startup catch-up: the registration-time executable check lands
223
- // in the base layer *after* this first describe read, and a
224
- // module plugin has no push channel — poll the shared describe
225
- // mirror until the served revision advances (the same budget the
226
- // re-check waits with). A card attaching after the check already
227
- // ran sees a nonzero revision and does not poll.
228
- if (!this.checkWaitStarted) {
229
- this.checkWaitStarted = true;
230
- if (servedRevision === 0) this.waitForCheck(0, Date.now() + CHECK_POLL_TIMEOUT_MS);
231
- }
232
- }
233
- /**
234
- * Immediate write of the user's extra flags (persisted; the host's
235
- * validate hook rejects invalid lists here, and the next bridge
236
- * launch starts with them).
237
- */
238
- saveFlags(flags) {
239
- return this.scope.set("extraFlags", flags).catch((error) => this.noteError(error));
240
- }
241
- /**
242
- * Ask the host to re-run the Chrome executable/connection check,
243
- * then wait for the result: write `recheckNonce`, then poll the
244
- * shared describe mirror until the served `checkRevision` advances
245
- * past the click-time value — or the check timeout budget elapses.
246
- */
247
- recheck() {
248
- if (this.disposed) return Promise.resolve();
249
- var baseline = this.store.getSnapshot().checkRevision;
250
- var wait = this.waitForCheck(baseline, Date.now() + CHECK_POLL_TIMEOUT_MS);
251
- return this.scope
252
- .set("recheckNonce", Date.now())
253
- .catch((error) => this.noteError(error))
254
- .then(() => wait);
255
- }
256
- /** Re-describe the settings mirror every tick until the check revision advances or the deadline passes. */
257
- waitForCheck(baseline, deadline) {
258
- var controller = this;
259
- // A new wait supersedes any pending one (the startup catch-up
260
- // poll yielding to a manual re-check), so exactly one poll loop
261
- // runs and the superseded wait settles instead of dangling.
262
- var generation = ++controller.pollGeneration;
263
- return new Promise((resolve) => {
264
- var timer;
265
- var settled = false;
266
- var arm = (delay) => {
267
- timer = setTimeout(tick, delay);
268
- controller.pollTimer = timer;
269
- };
270
- var finish = () => {
271
- if (settled) return;
272
- settled = true;
273
- clearTimeout(timer);
274
- if (controller.pollTimer === timer) controller.pollTimer = null;
275
- resolve();
276
- };
277
- var satisfied = () =>
278
- controller.store.getSnapshot().checkRevision > baseline || Date.now() >= deadline;
279
- var stale = () => controller.disposed || generation !== controller.pollGeneration;
280
- var tick = () => {
281
- if (stale() || satisfied()) {
282
- finish();
283
- return;
284
- }
285
- var loaded;
286
- try {
287
- loaded = controller.mirror.load();
288
- } catch (error) {
289
- loaded = void 0; // mirror read failure: the next tick retries
290
- }
291
- Promise.resolve(loaded)
292
- .catch(() => {})
293
- .then(() => {
294
- if (stale() || satisfied()) {
295
- finish();
296
- return;
297
- }
298
- arm(CHECK_POLL_TICK_MS);
299
- });
300
- };
301
- arm(CHECK_POLL_TICK_MS);
302
- });
303
- }
304
- noteError(error) {
305
- if (this.disposed) return;
306
- var previous = this.store.getSnapshot();
307
- this.store.set({
308
- ...previous,
309
- actionError: error != null && typeof error.message === "string" ? error.message : String(error)
310
- });
311
- }
312
- /** The face the card's slot registration injects. */
313
- inject() {
314
- return {
315
- hooks: { chromeMcpCard: this.store },
316
- saveFlags: (flags) => this.saveFlags(flags),
317
- recheck: () => this.recheck(),
318
- /** One mirror re-read (used when the card opens). */
319
- refresh: () => this.refresh()
320
- };
321
- }
322
- /**
323
- * Re-read the shared describe mirror once so base-layer status
324
- * captured asynchronously by the host (bridge connection errors)
325
- * reaches an opened card without waiting for a write or a re-check.
326
- */
327
- refresh() {
328
- if (this.disposed) return Promise.resolve();
329
- var load;
330
- try {
331
- load = this.mirror.load();
332
- } catch (error) {
333
- load = void 0;
334
- }
335
- return Promise.resolve(load).catch(() => {});
336
- }
337
- dispose() {
338
- this.disposed = true;
339
- if (this.pollTimer !== null) {
340
- clearTimeout(this.pollTimer);
341
- this.pollTimer = null;
342
- }
343
- this.unsubscribe();
344
- }
345
- };
2202
+ // src/client/card/dialog.ts
2203
+ var react6 = __toESM(require("react"), 1);
2204
+ var import_jsx_runtime13 = require("react/jsx-runtime");
2205
+ function CandidateDialog(props) {
2206
+ var t = props.t;
2207
+ var candidates = props.candidates;
2208
+ var savedIds = props.savedIds;
2209
+ var savedSet = new Set(savedIds);
2210
+ var initialChecked = candidates.filter((candidate) => savedSet.has(candidate.id)).map((candidate) => candidate.id);
2211
+ var servedKey = JSON.stringify(candidates) + "|" + JSON.stringify(savedIds);
2212
+ var draftState = react6.useState({ key: "", query: "", checked: [] });
2213
+ var draft = draftState[0];
2214
+ var setDraft = draftState[1];
2215
+ var dirty = draft.key !== servedKey;
2216
+ var query = dirty ? "" : draft.query;
2217
+ var checked = dirty ? initialChecked : draft.checked;
2218
+ var checkedSet = new Set(checked);
2219
+ var setQuery = (value) => {
2220
+ setDraft({ key: servedKey, query: value, checked: Array.from(checkedSet) });
2221
+ };
2222
+ var setChecked = (make) => {
2223
+ setDraft({ key: servedKey, query, checked: make(checked) });
2224
+ };
2225
+ var needle = query.trim().toLowerCase();
2226
+ var shown = needle === "" ? candidates : candidates.filter((candidate) => candidate.id.toLowerCase().indexOf(needle) !== -1);
2227
+ var addable = shown.filter((candidate) => !savedSet.has(candidate.id)).map((candidate) => candidate.id);
2228
+ var allChecked = addable.length > 0 && addable.every((id) => checkedSet.has(id));
2229
+ var toggle = (id, value) => {
2230
+ setChecked((current) => value ? current.concat([id]) : current.filter((entry) => entry !== id));
2231
+ };
2232
+ return (0, import_jsx_runtime13.jsxs)("div", {
2233
+ className: ChromeMcpCard_default.fetchDialog,
2234
+ role: "dialog",
2235
+ "aria-modal": "true",
2236
+ "aria-label": props.title,
2237
+ children: [
2238
+ (0, import_jsx_runtime13.jsxs)("div", {
2239
+ className: ChromeMcpCard_default.dialogContent,
2240
+ children: [
2241
+ (0, import_jsx_runtime13.jsxs)("div", {
2242
+ className: ChromeMcpCard_default.dialogHeader,
2243
+ children: [
2244
+ (0, import_jsx_runtime13.jsx)("h2", { className: ChromeMcpCard_default.dialogTitle, children: props.title }),
2245
+ (0, import_jsx_runtime13.jsx)("button", {
2246
+ type: "button",
2247
+ className: ChromeMcpCard_default.dialogClose,
2248
+ "aria-label": t("close"),
2249
+ onClick: () => props.onClose(),
2250
+ children: (0, import_jsx_runtime13.jsx)("svg", {
2251
+ width: 14,
2252
+ height: 14,
2253
+ viewBox: "0 0 16 16",
2254
+ fill: "none",
2255
+ xmlns: "http://www.w3.org/2000/svg",
2256
+ "aria-hidden": "true",
2257
+ children: CLOSE_PATHS2.map((d, at) => (0, import_jsx_runtime13.jsx)("path", { d, fill: "currentColor" }, at))
2258
+ })
2259
+ })
2260
+ ]
2261
+ }),
2262
+ (0, import_jsx_runtime13.jsx)("p", { className: ChromeMcpCard_default.dialogDescription, children: props.description }),
2263
+ (0, import_jsx_runtime13.jsxs)("div", {
2264
+ className: ChromeMcpCard_default.dialogBody,
2265
+ children: [
2266
+ (0, import_jsx_runtime13.jsxs)("div", {
2267
+ className: ChromeMcpCard_default.candidateToolbar,
2268
+ children: [
2269
+ (0, import_jsx_runtime13.jsx)("input", {
2270
+ className: ChromeMcpCard_default.candidateSearch,
2271
+ type: "search",
2272
+ placeholder: props.searchPlaceholder,
2273
+ "aria-label": props.searchPlaceholder,
2274
+ value: query,
2275
+ onChange: (event) => {
2276
+ setQuery(event && event.target ? String(event.target.value ?? "") : "");
2277
+ }
2278
+ }),
2279
+ (0, import_jsx_runtime13.jsx)("button", {
2280
+ type: "button",
2281
+ className: ChromeMcpCard_default.linkButton,
2282
+ onClick: () => {
2283
+ if (allChecked) setChecked(() => []);
2284
+ else setChecked((current) => Array.from(new Set(current.concat(addable))));
2285
+ },
2286
+ children: t("selectAll")
2287
+ })
2288
+ ]
2289
+ }),
2290
+ shown.length === 0 ? (0, import_jsx_runtime13.jsx)("p", { className: ChromeMcpCard_default.empty, children: t("dialogEmpty") }) : (0, import_jsx_runtime13.jsx)("ul", {
2291
+ className: ChromeMcpCard_default.candidateList,
2292
+ children: shown.map((candidate) => {
2293
+ var isSaved = savedSet.has(candidate.id);
2294
+ var note = candidate.note !== "" ? candidate.note : isSaved ? t("candidateSaved") : "";
2295
+ return (0, import_jsx_runtime13.jsx)("li", {
2296
+ className: ChromeMcpCard_default.candidate,
2297
+ children: (0, import_jsx_runtime13.jsxs)("label", {
2298
+ className: ChromeMcpCard_default.candidateLabel,
2299
+ children: [
2300
+ (0, import_jsx_runtime13.jsx)("input", {
2301
+ type: "checkbox",
2302
+ checked: isSaved ? true : checkedSet.has(candidate.id),
2303
+ disabled: isSaved,
2304
+ onChange: (event) => {
2305
+ toggle(candidate.id, !!(event && event.target && event.target.checked));
2306
+ }
2307
+ }),
2308
+ (0, import_jsx_runtime13.jsx)("span", { className: ChromeMcpCard_default.candidateId, children: candidate.id }),
2309
+ note === "" ? null : (0, import_jsx_runtime13.jsx)("span", { className: ChromeMcpCard_default.candidateNote, children: note })
2310
+ ]
2311
+ })
2312
+ }, candidate.id);
2313
+ })
2314
+ })
2315
+ ]
2316
+ }),
2317
+ (0, import_jsx_runtime13.jsxs)("div", {
2318
+ className: ChromeMcpCard_default.dialogFooter,
2319
+ children: [
2320
+ (0, import_jsx_runtime13.jsx)("button", {
2321
+ type: "button",
2322
+ className: ChromeMcpCard_default.dialogButton,
2323
+ onClick: () => props.onClose(),
2324
+ children: t("cancel")
2325
+ }),
2326
+ (0, import_jsx_runtime13.jsx)("button", {
2327
+ type: "button",
2328
+ className: ChromeMcpCard_default.dialogButton + " " + ChromeMcpCard_default.dialogButtonPrimary,
2329
+ onClick: () => props.onAdd(Array.from(checkedSet)),
2330
+ children: t("addSelected")
2331
+ })
2332
+ ]
2333
+ })
2334
+ ]
2335
+ })
2336
+ ]
2337
+ });
2338
+ }
346
2339
 
347
- // ── card component ─────────────────────────────────────────────────
348
- /** Chevron matching the shell's IconChevronDownOutline14 primitive. */
349
- var CHEVRON_PATH =
350
- "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";
2340
+ // src/client/card/run-actions.ts
2341
+ var react7 = __toESM(require("react"), 1);
2342
+ var PROBED_AGE_TICK_MS = 1e4;
2343
+ function useRunActions(deps) {
2344
+ const runningState = react7.useState(false);
2345
+ const isRunning = runningState[0];
2346
+ const setRunning = runningState[1];
2347
+ const windowsRunningState = react7.useState(false);
2348
+ const isWindowsRunning = windowsRunningState[0];
2349
+ const setWindowsRunning = windowsRunningState[1];
2350
+ const dialogRef = react7.useState(null);
2351
+ const dialog = dialogRef[0];
2352
+ const setDialog = dialogRef[1];
2353
+ const probedState = react7.useState(0);
2354
+ const lastProbedAt = probedState[0];
2355
+ const setLastProbedAt = probedState[1];
2356
+ const clockState = react7.useState(() => Date.now());
2357
+ const clock = clockState[0];
2358
+ const setClock = clockState[1];
2359
+ react7.useEffect(() => {
2360
+ const tick = setInterval(() => setClock(Date.now()), PROBED_AGE_TICK_MS);
2361
+ return () => clearInterval(tick);
2362
+ }, []);
2363
+ const runFetch = () => {
2364
+ setRunning(true);
2365
+ let promise;
2366
+ try {
2367
+ promise = deps.refreshExecutables();
2368
+ } catch (caught) {
2369
+ promise = Promise.reject(caught);
2370
+ }
2371
+ Promise.resolve(promise).then(
2372
+ async (statuses) => {
2373
+ setRunning(false);
2374
+ const settled = Date.now();
2375
+ setLastProbedAt(settled);
2376
+ setClock(settled);
2377
+ if (!Array.isArray(statuses) || statuses.length === 0) return;
2378
+ const candidates = statuses.map((status) => ({
2379
+ id: status.path,
2380
+ note: status.version !== "" ? status.version : status.error !== "" ? status.error : status.note,
2381
+ broken: status.version === "" && status.error !== ""
2382
+ }));
2383
+ setDialog({ candidates });
2384
+ },
2385
+ () => {
2386
+ setRunning(false);
2387
+ }
2388
+ );
2389
+ };
2390
+ const runWindowsChrome = () => {
2391
+ setWindowsRunning(true);
2392
+ let promise;
2393
+ try {
2394
+ promise = deps.openWindowsChrome();
2395
+ } catch (caught) {
2396
+ promise = Promise.reject(caught);
2397
+ }
2398
+ Promise.resolve(promise).then(
2399
+ () => {
2400
+ setWindowsRunning(false);
2401
+ },
2402
+ () => {
2403
+ setWindowsRunning(false);
2404
+ }
2405
+ );
2406
+ };
2407
+ const addFromDialog = (ids) => {
2408
+ setDialog(null);
2409
+ if (typeof deps.addExecutables === "function") Promise.resolve(deps.addExecutables(ids)).catch(() => {
2410
+ });
2411
+ };
2412
+ return {
2413
+ running: isRunning,
2414
+ windowsRunning: isWindowsRunning,
2415
+ dialog,
2416
+ runFetch,
2417
+ runWindowsChrome,
2418
+ addFromDialog,
2419
+ closeDialog: () => setDialog(null),
2420
+ lastProbedAt,
2421
+ clock
2422
+ };
2423
+ }
351
2424
 
352
- /**
353
- * Render the Chrome DevTools MCP settings/status card. Collapsible
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
- }
2425
+ // src/client/card/toggle.ts
2426
+ var import_jsx_runtime14 = require("react/jsx-runtime");
2427
+ var import_dsh_client_ui_primitives6 = require("@deepseek-ai/dsh-client-ui-primitives");
2428
+ function LogOutputToggle(props) {
2429
+ var t = props.t;
2430
+ return (0, import_jsx_runtime14.jsxs)("div", {
2431
+ className: ChromeMcpCard_default.catalog,
2432
+ children: [
2433
+ (0, import_jsx_runtime14.jsxs)("div", {
2434
+ className: ChromeMcpCard_default.field,
2435
+ children: [
2436
+ (0, import_jsx_runtime14.jsxs)("div", {
2437
+ className: ChromeMcpCard_default.fieldRow,
2438
+ children: [
2439
+ (0, import_jsx_runtime14.jsx)("span", { className: ChromeMcpCard_default.fieldTitle, children: t("reduceLabel") }),
2440
+ (0, import_jsx_runtime14.jsx)("span", {
2441
+ className: ChromeMcpCard_default.toggleLabel,
2442
+ title: t("reduceTitle"),
2443
+ children: (0, import_jsx_runtime14.jsx)(import_dsh_client_ui_primitives6.Switch, {
2444
+ checked: props.checked,
2445
+ label: t("reduceLabel"),
2446
+ disabled: props.disabled,
2447
+ onChange: () => {
2448
+ if (typeof props.onToggle === "function") props.onToggle();
2449
+ }
2450
+ })
2451
+ })
2452
+ ]
2453
+ }),
2454
+ (0, import_jsx_runtime14.jsx)("div", { className: ChromeMcpCard_default.fieldDesc, children: t("reduceHint") })
2455
+ ]
2456
+ })
2457
+ ]
2458
+ });
2459
+ }
551
2460
 
552
- // ── plugin body ────────────────────────────────────────────────────
553
- /**
554
- * Mount the settings/status card.
555
- * @param ctx - the browser plugin context.
556
- */
557
- function apply(ctx) {
558
- ctx.effect(() => ctx.locale.register(LOCALE_NS, { en }), "chrome-mcp: dictionaries");
559
- var binder = ctx.settingsScope;
560
- var scope = binder.bind({ namespace: NS });
561
- // The shared describe mirror: the recheck action re-reads it while
562
- // the host's in-memory check status is en route.
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
- }
2461
+ // src/client/card/view.ts
2462
+ function labelOf(entries, id) {
2463
+ const row = entries.find((entry) => entry.id === id);
2464
+ return row === void 0 ? id : entryLabel(row);
2465
+ }
2466
+ function describeCard(state) {
2467
+ const error = state.lastError || state.actionError || "";
2468
+ const servedMode = typeof state.stderrMode === "string" ? state.stderrMode : "";
2469
+ const stderrEffective = servedMode !== "" ? servedMode : state.rowStderr === "console" ? "console" : "log";
2470
+ const executableEntries = normalizeEntries(state.executables);
2471
+ const statusList = Array.isArray(state.executableStatus) ? state.executableStatus : [];
2472
+ const optionIds = executableEntries.map((entry) => entry.id);
2473
+ const effectivePath = typeof state.effectiveChromePath === "string" ? state.effectiveChromePath : "";
2474
+ if (effectivePath !== "" && optionIds.indexOf(effectivePath) === -1) optionIds.push(effectivePath);
2475
+ const executableOptions = optionIds.map((id) => ({ id, label: labelOf(executableEntries, id) }));
2476
+ const brokenIds = statusList.filter((status) => status.version === "" && status.error !== "").map((status) => status.path);
2477
+ return {
2478
+ error,
2479
+ reduceChecked: stderrEffective === "log",
2480
+ executableEntries,
2481
+ executableOptions,
2482
+ brokenIds,
2483
+ effectivePath,
2484
+ version: typeof state.chromeVersion === "string" ? state.chromeVersion : "",
2485
+ missing: state.chromeMissing === true && error === "",
2486
+ dotIsError: error !== "",
2487
+ wslWindowsExecutable: state.wslWindowsExecutable === true,
2488
+ windowsChrome: readWindowsChrome(state.windowsChromeStatus),
2489
+ // Connect mode reads two ways. The mirror's answer is what the Host says
2490
+ // the bridge launches with — authoritative when it has been answered. And
2491
+ // the same question answered off the **saved flags**, which is what the
2492
+ // bridge launches with by the same precedence the flags rows edit: a flag
2493
+ // entry is a real config field a write-back never clears, so it keeps the
2494
+ // answer standing through the reset window every write pass leaves behind
2495
+ // (the same rule the pill's saved-path fallback follows). Either half
2496
+ // answering is enough; neither can contradict what the other states,
2497
+ // because both are asked of the same launch.
2498
+ connectMode: state.carriesConnectionMode === true || flagsCarryConnectionMode(state.extraFlags)
2499
+ };
2500
+ }
2501
+ var CONNECT_FLAG_PATTERN = /^--(?:browserUrl|wsEndpoint)(?:=|(?=\s)|$)/iu;
2502
+ function flagsCarryConnectionMode(flags) {
2503
+ return Array.isArray(flags) && flags.some((flag) => typeof flag === "string" && CONNECT_FLAG_PATTERN.test(flag));
2504
+ }
2505
+ function executableLabel(view, id) {
2506
+ return labelOf(view.executableEntries, id);
2507
+ }
2508
+ function readWindowsChrome(value) {
2509
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return { state: "off", port: 0, error: "" };
2510
+ const record = value;
2511
+ const known = ["off", "launching", "connected", "unreachable", "not-found", "launch-failed", "not-applicable"];
2512
+ const state = typeof record.state === "string" && known.indexOf(record.state) !== -1 ? record.state : "off";
2513
+ const port = typeof record.port === "number" && Number.isFinite(record.port) ? Math.trunc(record.port) : 0;
2514
+ const error = typeof record.error === "string" ? record.error : "";
2515
+ return { state, port, error };
2516
+ }
2517
+ function windowsChromeButton(view) {
2518
+ const status = view.windowsChrome;
2519
+ if (status.state === "launching") return { labelKey: "openWindowsChromeBusy", titleKey: "openWindowsChromeTitle", disabled: true, busy: true };
2520
+ if (status.state === "not-applicable") return { labelKey: "openWindowsChrome", titleKey: "windowsChromeNotWsl", disabled: true, busy: false };
2521
+ if (status.state === "not-found") return { labelKey: "openWindowsChrome", titleKey: "windowsChromeNotFound", disabled: false, busy: false };
2522
+ if (status.state === "unreachable") return { labelKey: "openWindowsChrome", titleKey: "windowsChromeUnreachable", disabled: false, busy: false };
2523
+ return { labelKey: "openWindowsChrome", titleKey: "openWindowsChromeTitle", disabled: false, busy: false };
2524
+ }
2525
+ function isBroken(view, id) {
2526
+ return view.brokenIds.indexOf(id) !== -1;
2527
+ }
2528
+ function probeAge(since, now, t) {
2529
+ const seconds = Math.max(0, Math.floor((now - since) / 1e3));
2530
+ if (seconds < 10) return t("probeJustNow");
2531
+ if (seconds < 60) return String(seconds) + " " + t("probeSecondsAgo");
2532
+ const minutes = Math.floor(seconds / 60);
2533
+ if (minutes < 60) return String(minutes) + " " + t("probeMinutesAgo");
2534
+ return String(Math.floor(minutes / 60)) + " " + t("probeHoursAgo");
2535
+ }
580
2536
 
581
- exports.apply = apply;
582
- exports.inject = inject;
583
- return module.exports;
2537
+ // src/client/card/index.ts
2538
+ function optionLabel(view, id, t) {
2539
+ const label = executableLabel(view, id);
2540
+ return isBroken(view, id) ? label + " (" + t("unavailableSuffix") + ")" : label;
2541
+ }
2542
+ function ChromeMcpCard(props) {
2543
+ const t = props.t;
2544
+ const state = props.useChromeMcpCard((snapshot) => snapshot);
2545
+ const menuState = react8.useState(false);
2546
+ const menuOpen = menuState[0];
2547
+ const setMenuOpen = menuState[1];
2548
+ const runs = useRunActions({
2549
+ refreshExecutables: props.refreshExecutables,
2550
+ openWindowsChrome: props.openWindowsChrome,
2551
+ addExecutables: props.addExecutables
2552
+ });
2553
+ if (props.view !== "page") return null;
2554
+ if (!state.available) return null;
2555
+ const view = describeCard(state);
2556
+ const rows = view.executableEntries;
2557
+ const savedIds = rows.map((entry) => entry.id);
2558
+ const options = view.executableOptions.map((option) => ({ id: option.id, label: optionLabel(view, option.id, t) }));
2559
+ const dotLabel = view.error !== "" ? view.error : t("chromeMissing");
2560
+ const dotAria = view.dotIsError || view.missing ? t("statusError") : t("statusOk");
2561
+ const statusDot = (0, import_jsx_runtime15.jsx)("span", {
2562
+ className: ChromeMcpCard_default.dot + " " + ChromeMcpCard_default.dotError,
2563
+ role: "img",
2564
+ "aria-label": dotAria,
2565
+ title: dotLabel
2566
+ });
2567
+ const windowsButton = windowsChromeButton(view);
2568
+ const windowsDisabled = windowsButton.disabled || runs.windowsRunning || !state.writable;
2569
+ const windowsBusy = windowsButton.busy || runs.windowsRunning;
2570
+ const connectLabel = view.windowsChrome.port > 0 ? t("connectModeLabel") + " (http://127.0.0.1:" + String(view.windowsChrome.port) + ")" : t("connectModeLabel");
2571
+ const selectedLabel = view.connectMode ? connectLabel : view.effectivePath === "" ? t("executableNone") : executableLabel(view, view.effectivePath);
2572
+ const statusLine = runs.running ? (0, import_jsx_runtime15.jsx)("p", { className: ChromeMcpCard_default.result, role: "status", children: t("probing") }) : view.version === "" ? null : (0, import_jsx_runtime15.jsxs)("p", {
2573
+ className: ChromeMcpCard_default.result,
2574
+ role: "status",
2575
+ children: [
2576
+ t("versionLabel"),
2577
+ (0, import_jsx_runtime15.jsx)("span", { className: ChromeMcpCard_default.resultValue, children: view.version }),
2578
+ runs.lastProbedAt === 0 ? null : (0, import_jsx_runtime15.jsx)("span", { className: ChromeMcpCard_default.resultAge, children: probeAge(runs.lastProbedAt, runs.clock, t) })
2579
+ ]
2580
+ });
2581
+ const errorLine = view.error !== "" ? (0, import_jsx_runtime15.jsxs)("p", { className: ChromeMcpCard_default.error, role: "status", children: [statusDot, view.error] }) : view.missing ? (0, import_jsx_runtime15.jsxs)("p", { className: ChromeMcpCard_default.error, role: "status", children: [statusDot, t("chromeMissing")] }) : null;
2582
+ return (0, import_jsx_runtime15.jsxs)("div", {
2583
+ className: ChromeMcpCard_default.body,
2584
+ children: [
2585
+ // Nothing the page can write from here is writable: the row's
2586
+ // configuration is owned elsewhere (a home patch or the page's own
2587
+ // read-only state).
2588
+ !state.writable ? (0, import_jsx_runtime15.jsx)("p", { className: ChromeMcpCard_default.readOnly, role: "status", children: t("readOnly") }) : null,
2589
+ (0, import_jsx_runtime15.jsx)(CatalogPicker, {
2590
+ t,
2591
+ entries: rows,
2592
+ options,
2593
+ idPlaceholder: t("executableIdPlaceholder"),
2594
+ namePlaceholder: t("namePlaceholder"),
2595
+ selectedId: view.effectivePath,
2596
+ selectedLabel,
2597
+ selectedTitle: view.connectMode ? connectLabel : view.effectivePath !== "" ? view.effectivePath : selectedLabel,
2598
+ writable: state.writable,
2599
+ busy: runs.running,
2600
+ menuOpen,
2601
+ setMenuOpen,
2602
+ connectMode: view.connectMode,
2603
+ selectorLabel: t("executableLabel"),
2604
+ hint: view.connectMode ? t("connectModeHint") : t("executableHint"),
2605
+ windowsLabel: windowsBusy ? t("openWindowsChromeBusy") : t(windowsButton.labelKey),
2606
+ windowsTitle: t(windowsButton.titleKey),
2607
+ windowsDisabled,
2608
+ windowsOnOpen: runs.runWindowsChrome,
2609
+ rowsLabel: t("executableRows"),
2610
+ emptyText: t("executableEmpty"),
2611
+ idLabel: t("executableIdLabel"),
2612
+ nameLabel: t("executableNameLabel"),
2613
+ removeLabel: t("removeExecutable"),
2614
+ addLabel: t("addExecutable"),
2615
+ refreshLabel: t("fetchExecutables"),
2616
+ busyLabel: t("fetching"),
2617
+ select: (id) => {
2618
+ if (typeof props.selectExecutable === "function") void props.selectExecutable(id);
2619
+ },
2620
+ saveEntries: (entries) => props.saveExecutables(entries),
2621
+ refresh: runs.runFetch,
2622
+ statusLine
2623
+ }),
2624
+ // The one state no control of this form can express: the host runs
2625
+ // under WSL and what is selected is a Windows executable — which the
2626
+ // bridge cannot launch, however healthy its `--version` answer looks.
2627
+ // Nothing has failed yet, so this is a notice (static copy, shown only
2628
+ // while the host reports the state), not a third error surface: the
2629
+ // required configuration differs.
2630
+ view.wslWindowsExecutable ? (0, import_jsx_runtime15.jsxs)("div", {
2631
+ className: ChromeMcpCard_default.notice,
2632
+ role: "note",
2633
+ children: [
2634
+ (0, import_jsx_runtime15.jsx)("div", { className: ChromeMcpCard_default.noticeTitle, children: t("wslWindowsTitle") }),
2635
+ (0, import_jsx_runtime15.jsx)("div", { className: ChromeMcpCard_default.noticeBody, children: t("wslWindowsNote") })
2636
+ ]
2637
+ }) : null,
2638
+ (0, import_jsx_runtime15.jsx)(ExtraFlagsEditor, {
2639
+ t,
2640
+ flags: Array.isArray(state.extraFlags) ? state.extraFlags : [],
2641
+ defaultFlags: Array.isArray(state.defaultExtraFlags) ? state.defaultExtraFlags : [],
2642
+ wslFlags: Array.isArray(state.wslExtraFlags) ? state.wslExtraFlags : [],
2643
+ writable: state.writable,
2644
+ saveFlags: props.saveFlags
2645
+ }),
2646
+ (0, import_jsx_runtime15.jsx)(LogOutputToggle, {
2647
+ t,
2648
+ checked: view.reduceChecked,
2649
+ disabled: !state.writable,
2650
+ onToggle: props.toggleStderr
2651
+ }),
2652
+ errorLine,
2653
+ runs.dialog !== null ? (0, import_jsx_runtime15.jsx)(CandidateDialog, {
2654
+ t,
2655
+ candidates: runs.dialog.candidates,
2656
+ savedIds,
2657
+ title: t("dialogExecutablesTitle"),
2658
+ description: t("dialogExecutablesDescription"),
2659
+ searchPlaceholder: t("searchExecutables"),
2660
+ onAdd: runs.addFromDialog,
2661
+ onClose: runs.closeDialog
2662
+ }) : null,
2663
+ // The form writes nothing as it is edited: every staged edit is
2664
+ // persisted only when **Apply** lands them, and **Discard** drops the
2665
+ // whole draft — reverting every staged value to what the host already
2666
+ // serves.
2667
+ (0, import_jsx_runtime15.jsxs)("div", {
2668
+ className: ChromeMcpCard_default.footer,
2669
+ children: [
2670
+ // A staged edit announces itself on the save line, since the
2671
+ // identity line the page draws says nothing about it.
2672
+ state.dirty ? (0, import_jsx_runtime15.jsx)("span", { className: ChromeMcpCard_default.badge, children: t("unsaved") }) : null,
2673
+ state.failed ? (0, import_jsx_runtime15.jsx)("p", { className: ChromeMcpCard_default.footerError, role: "status", children: t("saveFailed") }) : null,
2674
+ (0, import_jsx_runtime15.jsx)("button", {
2675
+ type: "button",
2676
+ className: ChromeMcpCard_default.footerButton,
2677
+ onClick: () => {
2678
+ if (typeof props.discard === "function") props.discard();
2679
+ },
2680
+ children: t("discard")
2681
+ }),
2682
+ (0, import_jsx_runtime15.jsx)("button", {
2683
+ type: "button",
2684
+ className: ChromeMcpCard_default.footerButton + " " + ChromeMcpCard_default.footerButtonPrimary,
2685
+ disabled: !state.dirty || state.saving || !state.writable,
2686
+ onClick: () => {
2687
+ if (typeof props.apply === "function") void props.apply();
2688
+ },
2689
+ children: state.saving ? t("saving") : t("apply")
2690
+ })
2691
+ ]
2692
+ })
2693
+ ]
2694
+ });
2695
+ }
2696
+
2697
+ // src/client/read.ts
2698
+ function sameEntries(a, b) {
2699
+ if (a === b) return true;
2700
+ if (a.length !== b.length) return false;
2701
+ for (let i = 0; i < a.length; i++) if (a[i].id !== b[i].id || a[i].name !== b[i].name) return false;
2702
+ return true;
2703
+ }
2704
+ function readFlags(value) {
2705
+ if (!Array.isArray(value)) return [];
2706
+ return value.filter((flag) => typeof flag === "string" && flag !== "");
2707
+ }
2708
+ function readNumber(value, fallback) {
2709
+ return typeof value === "number" ? value : fallback;
2710
+ }
2711
+ function readString(value, fallback) {
2712
+ return typeof value === "string" ? value : fallback;
2713
+ }
2714
+ function readWindowsChromeStatus(value) {
2715
+ const fallback = { state: "off", port: 0, error: "" };
2716
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return fallback;
2717
+ const status = value.state;
2718
+ const states = ["off", "launching", "connected", "unreachable", "not-found", "launch-failed", "not-applicable"];
2719
+ const port = value.port;
2720
+ const error = value.error;
2721
+ return {
2722
+ state: typeof status === "string" && states.indexOf(status) !== -1 ? status : "off",
2723
+ port: typeof port === "number" && Number.isFinite(port) ? Math.trunc(port) : 0,
2724
+ error: typeof error === "string" ? error : ""
2725
+ };
2726
+ }
2727
+ function readStatus(value) {
2728
+ if (!Array.isArray(value)) return [];
2729
+ const statuses = [];
2730
+ for (const item of value) {
2731
+ if (item === null || typeof item !== "object" || Array.isArray(item)) continue;
2732
+ const path = readString(item.path, "");
2733
+ if (path === "") continue;
2734
+ statuses.push({
2735
+ path,
2736
+ version: readString(item.version, ""),
2737
+ error: readString(item.error, ""),
2738
+ note: readString(item.note, "")
2739
+ });
2740
+ }
2741
+ return statuses;
2742
+ }
2743
+
2744
+ // src/client/project.ts
2745
+ function nothingProjected() {
2746
+ return {
2747
+ available: false,
2748
+ writable: false,
2749
+ extraFlags: [],
2750
+ lastError: "",
2751
+ chromeMissing: false,
2752
+ executableDiscoveryRevision: 0,
2753
+ chromeVersion: "",
2754
+ executables: [],
2755
+ executableStatus: [],
2756
+ chromePath: "",
2757
+ effectiveChromePath: "",
2758
+ effectiveSource: "",
2759
+ stderrMode: "",
2760
+ rowStderr: "log",
2761
+ defaultExtraFlags: [],
2762
+ wslExtraFlags: [],
2763
+ wslWindowsExecutable: false,
2764
+ windowsChromeStatus: { state: "off", port: 0, error: "" },
2765
+ carriesConnectionMode: false,
2766
+ actionError: "",
2767
+ dirty: false,
2768
+ saving: false,
2769
+ failed: false
2770
+ };
2771
+ }
2772
+ function projectCard(snapshot, heldWslNotice2) {
2773
+ if (snapshot.available !== true) return nothingProjected();
2774
+ const value = snapshot.values ?? {};
2775
+ const chromePath = readString(value.chromePath, "");
2776
+ const answeredEffective = readString(value.effectiveChromePath, "");
2777
+ const wslNotice = value.wslWindowsExecutable === true ? true : answeredEffective === "" && chromePath !== "" ? heldWslNotice2 : false;
2778
+ const effectiveChromePath = answeredEffective !== "" ? answeredEffective : value.wslWindowsExecutable === true ? "" : chromePath;
2779
+ const effectiveSource = value.effectiveSource === "selected" || value.effectiveSource === "row-config" ? value.effectiveSource : "";
2780
+ const stderrMode = value.stderrMode === "log" || value.stderrMode === "console" ? value.stderrMode : "";
2781
+ return {
2782
+ available: true,
2783
+ writable: snapshot.writable === true,
2784
+ extraFlags: readFlags(value.extraFlags),
2785
+ lastError: readString(value.lastError, ""),
2786
+ chromeMissing: value.chromeMissing === true,
2787
+ executableDiscoveryRevision: readNumber(value.executableDiscoveryRevision, 0),
2788
+ chromeVersion: readString(value.chromeVersion, ""),
2789
+ executables: normalizeEntries(value.executables),
2790
+ executableStatus: readStatus(value.executableStatus),
2791
+ chromePath,
2792
+ effectiveChromePath,
2793
+ effectiveSource,
2794
+ stderrMode,
2795
+ // The mode the bridge actually routes console output through, as the
2796
+ // Host resolves it — what the field states wins, the live answer fills
2797
+ // the gap.
2798
+ rowStderr: value.stderrEffective === "console" ? "console" : "log",
2799
+ defaultExtraFlags: readFlags(value.defaultExtraFlags),
2800
+ wslExtraFlags: readFlags(value.wslExtraFlags),
2801
+ wslWindowsExecutable: wslNotice,
2802
+ windowsChromeStatus: readWindowsChromeStatus(value.windowsChromeStatus),
2803
+ carriesConnectionMode: value.carriesConnectionMode === true,
2804
+ actionError: snapshot.actionError ?? "",
2805
+ dirty: snapshot.dirty === true,
2806
+ saving: snapshot.saving === true,
2807
+ failed: snapshot.failed === true
2808
+ };
2809
+ }
2810
+
2811
+ // src/client/block.ts
2812
+ var RUN_COUNTER = "executableDiscoveryRevision";
2813
+ var heldWslNotice = /* @__PURE__ */ new WeakMap();
2814
+ function runCounter(snapshot) {
2815
+ const value = snapshot.values?.[RUN_COUNTER];
2816
+ return typeof value === "number" ? value : 0;
2817
+ }
2818
+ function cardState(controller) {
2819
+ const state = projectCard(controller.getSnapshot(), heldWslNotice.get(controller) === true);
2820
+ heldWslNotice.set(controller, state.wslWindowsExecutable);
2821
+ return state;
2822
+ }
2823
+ function cardActions(controller) {
2824
+ const current = () => cardState(controller);
2825
+ const saveExecutables = (entries) => {
2826
+ const next = normalizeEntries(entries);
2827
+ if (sameEntries(next, current().executables)) return Promise.resolve();
2828
+ const baseline = runCounter(controller.getSnapshot());
2829
+ return controller.settleAndWrite((snapshot) => runCounter(snapshot) > baseline, "executables", next);
2830
+ };
2831
+ return {
2832
+ /** Stage the extra-flags list; a list equal to what is served is not
2833
+ * dirty (the controller drops that draft). */
2834
+ saveFlags: (flags) => {
2835
+ controller.stage("extraFlags", Array.isArray(flags) ? flags : []);
2836
+ return Promise.resolve();
2837
+ },
2838
+ /** Stage the flipped "Reduce log output" mode. A flip that lands back on
2839
+ * what is already effective drops the draft instead of staging a no-op:
2840
+ * it stages the served value itself, which the controller reads as clean. */
2841
+ toggleStderr: () => {
2842
+ const state = current();
2843
+ const held = state.stderrMode;
2844
+ const effective = held === "" ? state.rowStderr : held;
2845
+ const next = effective === "log" ? "console" : "log";
2846
+ const servedRaw = controller.servedValue("stderrMode");
2847
+ const served = servedRaw === "log" || servedRaw === "console" ? servedRaw : "";
2848
+ const servedEffective = served === "" ? state.rowStderr : served;
2849
+ controller.stage("stderrMode", next === servedEffective ? served : next);
2850
+ return Promise.resolve();
2851
+ },
2852
+ /** Select the executable the bridge launches with: written on the spot,
2853
+ * settled by the Host's own re-run. Picking what already runs writes
2854
+ * nothing. */
2855
+ selectExecutable: (id) => {
2856
+ const target = typeof id === "string" ? id.trim() : "";
2857
+ if (target === "" || target === current().effectiveChromePath) return Promise.resolve();
2858
+ const baseline = runCounter(controller.getSnapshot());
2859
+ return controller.settleAndWrite((snapshot) => runCounter(snapshot) > baseline, "chromePath", target);
2860
+ },
2861
+ saveExecutables,
2862
+ /** Merge the ids picked in the dialog into the saved rows. */
2863
+ addExecutables: (ids) => saveExecutables(current().executables.concat(
2864
+ (Array.isArray(ids) ? ids : []).filter((id) => typeof id === "string" && id.trim() !== "").map((id) => ({ id: id.trim(), name: "" }))
2865
+ )),
2866
+ /** Ask the Host to re-probe the saved rows; answers every path the run
2867
+ * answered for, read off the served mirror once it settles. */
2868
+ refreshExecutables: () => controller.settleTrigger("refreshExecutablesNonce", RUN_COUNTER).then(() => readStatus(controller.value("executableStatus"))),
2869
+ /** Ask the Host to drive a Windows-side Chrome in connect mode; what the
2870
+ * button reports is served through `windowsChromeStatus`. */
2871
+ openWindowsChrome: () => controller.settleTrigger("openWindowsChromeNonce", RUN_COUNTER),
2872
+ /** Persist every staged edit in one pass. */
2873
+ apply: async () => {
2874
+ await controller.applyStaged();
2875
+ },
2876
+ /** Drop every staged edit. */
2877
+ discard: () => controller.discard()
2878
+ };
2879
+ }
2880
+ function ChromeCardBlock(props) {
2881
+ const state = cardState(props.controller);
2882
+ return ChromeMcpCard({
2883
+ t: props.t,
2884
+ view: "page",
2885
+ useChromeMcpCard: (selector) => selector(state),
2886
+ ...cardActions(props.controller)
2887
+ });
2888
+ }
2889
+
2890
+ // src/client/locales/en-US.ts
2891
+ var en = {
2892
+ executableLabel: "Chrome executable",
2893
+ executableHint: "The executable every connection launched from this row launches with, unless the connection picked its own. Changing it answers the live connections again.",
2894
+ executableNone: "No Chrome executable detected",
2895
+ executableRows: "Saved executables",
2896
+ executableIdLabel: "Executable path",
2897
+ executableIdPlaceholder: "Path to a Chrome executable",
2898
+ executableNameLabel: "Display name",
2899
+ namePlaceholder: "Display name",
2900
+ addExecutable: "Add executable",
2901
+ removeExecutable: "Delete executable",
2902
+ fetchExecutables: "Fetch executables",
2903
+ fetching: "Fetching\u2026",
2904
+ executableEmpty: "No executables saved. Use \u201CFetch executables\u201D, or add the Chrome path by hand.",
2905
+ dialogExecutablesTitle: "Choose executables to add",
2906
+ dialogExecutablesDescription: "These are the Chrome executables this host's last run answered for. Choose the ones to add.",
2907
+ dialogEmpty: "Nothing matches your search.",
2908
+ searchExecutables: "Search executables",
2909
+ selectAll: "Select all",
2910
+ addSelected: "Add selected",
2911
+ candidateSaved: "Saved",
2912
+ close: "Close",
2913
+ cancel: "Cancel",
2914
+ unavailableSuffix: "not runnable",
2915
+ wslWindowsTitle: "Windows executable under WSL",
2916
+ wslWindowsNote: "This host runs inside Windows Subsystem for Linux and the selected executable is the Windows Chrome, which no connection here can launch: the debug pipes it opens do not survive the WSL interop boundary, and its debugging port stays bound to the Windows loopback. It answers --version correctly, so the pill looks healthy until the first tool call fails. Drive it instead with the \u201CWindows Chrome (WSL debug port)\u201D connection, or with its run \u2014 Prelaunch Windows Chrome, the action the executable block carries next to Fetch executables, or the prelaunch_windows_chrome tool \u2014 which starts the Windows browser on a free debugging port. The run only reads whether Windows loopback reaches this WSL host (networkingMode=mirrored in %USERPROFILE%\\.wslconfig, then wsl --shutdown \u2014 a manual Windows-side setting it never edits). Chrome installed inside the Linux distro needs none of this.",
2917
+ probing: "Checking the executables\u2026",
2918
+ versionLabel: "Probed version:",
2919
+ probeJustNow: "just now",
2920
+ probeSecondsAgo: "seconds ago",
2921
+ probeMinutesAgo: "minutes ago",
2922
+ probeHoursAgo: "hours ago",
2923
+ flagsTitle: "Extra flags",
2924
+ flagsHint: "The extra flags every connection launched from this row launches with, unless the connection carries its own. One flag per row \u2014 applying them answers the live connections again.",
2925
+ flagsEmpty: "No extra flags saved. Add one by hand, or use \u201CRestore defaults\u201D.",
2926
+ flagsRows: "Saved flags",
2927
+ flagsValueLabel: "Extra flag",
2928
+ flagsValuePlaceholder: "e.g. --headless",
2929
+ addFlag: "Add flag",
2930
+ removeFlag: "Delete flag",
2931
+ flagsRestore: "Restore defaults",
2932
+ flagsRestoreTitle: "Keep every flag the rows carry and append the defaults that are still missing",
2933
+ flagsWsl: "Flags WSL",
2934
+ flagsWslTitle: "Append the recommended WSL<->Windows flags",
2935
+ openWindowsChrome: "Prelaunch Windows Chrome",
2936
+ openWindowsChromeTitle: "Start the Windows-side Chrome with --remote-debugging-port and refresh whatever Windows Chrome connections stand live",
2937
+ openWindowsChromeBusy: "Starting Windows Chrome\u2026",
2938
+ windowsChromeNotWsl: "Windows Chrome does not apply outside WSL.",
2939
+ windowsChromeNotFound: "No Windows Chrome was found (Program Files / Program Files (x86)) \u2014 run it again once one is installed.",
2940
+ windowsChromeUnreachable: "Needs networkingMode=mirrored in %USERPROFILE%\\.wslconfig, with wsl --shutdown: right now this WSL host cannot reach the Windows loopback.",
2941
+ connectModeLabel: "Connect mode",
2942
+ connectModeHint: "Connect mode: the connection launches no local Chrome (it connects with --browserUrl / --wsEndpoint); the local selection stays put.",
2943
+ reduceLabel: "Reduce log output",
2944
+ reduceTitle: "Capture the connection's stderr into the plugin's log file",
2945
+ reduceHint: "Captures stderr into the plugin's log file. Turn it off to debug the raw output; switching answers the live connections again.",
2946
+ // The staged form's own copy: nothing is written until **Apply** lands every
2947
+ // staged edit, so the card announces what a save would store and what it
2948
+ // could not land.
2949
+ apply: "Apply",
2950
+ discard: "Discard",
2951
+ saving: "Applying\u2026",
2952
+ unsaved: "Unsaved",
2953
+ saveFailed: "The last save was not accepted.",
2954
+ docsMessage: "Find the complete list of server parameters (e.g., --headless, --isolated, --slim) and how to configure WebSocket connections in the Configuration Guide.",
2955
+ docsLinkLabel: "Configuration Guide",
2956
+ statusError: "Error",
2957
+ statusOk: "OK",
2958
+ chromeMissing: "Chrome executable not found",
2959
+ // The body-mounted toast host's copy: it announces what the card's own error
2960
+ // line can only show while the row page is open. An error notice carries the
2961
+ // Host's captured line, so `toastError` only stands behind an empty one.
2962
+ toastStall: "The Chrome host did not answer the action \u2014 try it again.",
2963
+ toastError: "The Chrome host reported a failure.",
2964
+ readOnly: "Settings are read-only in this deployment."
2965
+ };
2966
+
2967
+ // src/client/locales/index.ts
2968
+ var DICTIONARIES2 = { en };
2969
+
2970
+ // src/client/styles/index.ts
2971
+ var CARD_CSS_ID = PLUGIN_ID + "/ChromeMcpCard.module.css";
2972
+ function injectCardStyles2() {
2973
+ if (typeof document === "undefined") return;
2974
+ if (document.querySelector('style[data-plugin-css="' + CARD_CSS_ID + '"]') !== null) return;
2975
+ const styleTag = document.createElement("style");
2976
+ styleTag.dataset.plugin = PLUGIN_ID;
2977
+ styleTag.dataset.pluginCss = CARD_CSS_ID;
2978
+ styleTag.textContent = cssText2;
2979
+ document.head.appendChild(styleTag);
2980
+ }
2981
+
2982
+ // src/client/index.ts
2983
+ var card = mountSettingsCard2({
2984
+ entryId: ENTRY_ID,
2985
+ pluginId: PLUGIN_ID,
2986
+ bundleId: BUNDLE_ID,
2987
+ localeNamespace: LOCALE_NAMESPACE2,
2988
+ locale: DICTIONARIES2,
2989
+ fields: {
2990
+ // The flags and the "Reduce log output" mode are edited as a draft:
2991
+ // nothing reaches the Host until Apply.
2992
+ staged: ["extraFlags", "stderrMode"],
2993
+ // The saved rows and the selection drive a Host run, so they land on
2994
+ // the click that asked for them.
2995
+ immediate: ["executables", "chromePath"],
2996
+ triggers: ["refreshExecutablesNonce", "openWindowsChromeNonce"],
2997
+ liveKeys: [...LIVE_KEYS]
2998
+ },
2999
+ blocks: [{ block: ChromeCardBlock, bind: (_snapshot, controller) => ({ controller }) }],
3000
+ // The body states the error itself (the dot and the line under the
3001
+ // toggle), so the shared second line stays off.
3002
+ errorLine: false,
3003
+ // A failure that lands while the person is elsewhere in the app is still
3004
+ // announced: the row page is closed most of the time a run is happening.
3005
+ // An error carries the Host's own line; a stall has no text of its own and
3006
+ // reads from the mirror — the known missing-executable answer when it
3007
+ // stands, the "the host did not answer" line otherwise.
3008
+ toasts: {
3009
+ text: (notice, snapshot, t) => notice.kind === "error" ? notice.text !== "" ? notice.text : t("toastError") : snapshot.values?.chromeMissing === true ? t("chromeMissing") : t("toastStall")
3010
+ },
3011
+ settleField: "executableDiscoveryRevision",
3012
+ settleDeadlineMs: RUN_SETTLE_TIMEOUT_MS2
3013
+ });
3014
+ var inject = card.inject;
3015
+ function apply(ctx) {
3016
+ injectCardStyles2();
3017
+ card.apply(ctx);
3018
+ }
3019
+ return module.exports;
584
3020
  }
585
3021
  });
3022
+ //# sourceMappingURL=client.js.map