@prettier-ai/dsh-client-ui-settings-plugins 0.1.2-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js ADDED
@@ -0,0 +1,1837 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@prettier-ai/dsh-client-ui-settings-plugins",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let _prettier_ai_dsh_client_ui_slots = require("@prettier-ai/dsh-client-ui-slots");
8
+ let react_jsx_runtime = require("react/jsx-runtime");
9
+ let react = require("react");
10
+ let _prettier_ai_dsh_client_ui_primitives = require("@prettier-ai/dsh-client-ui-primitives");
11
+ let _prettier_ai_dsh_client_store = require("@prettier-ai/dsh-client-store");
12
+ //#region \0dsh-css:/home/runner/work/dsh-publisher/dsh-publisher/upstream/packages/client/ui-settings-plugins/src/client/fields.module.css.mjs
13
+ const css$3 = ".ObQdYa_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.ObQdYa_field+.ObQdYa_field{border-top:1px solid var(--dsw-alias-border-l2)}.ObQdYa_head{align-items:center;gap:8px;display:flex}.ObQdYa_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.ObQdYa_badges{align-items:center;gap:8px;display:inline-flex}.ObQdYa_badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.ObQdYa_badgeMuted{white-space:nowrap;color:var(--dsw-alias-label-tertiary);border-radius:999px;padding:1px 8px;font-size:11px;line-height:17px}.ObQdYa_reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.ObQdYa_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.ObQdYa_reset:disabled{cursor:default}.ObQdYa_input{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.ObQdYa_input:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.ObQdYa_input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.ObQdYa_inputInvalid{border-color:var(--dsw-alias-label-error);}.ObQdYa_invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.ObQdYa_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}";
14
+ const tagId$3 = "@prettier-ai/dsh-client-ui-settings-plugins/fields.module.css";
15
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
16
+ const tag = document.createElement("style");
17
+ tag.dataset.plugin = "@prettier-ai/dsh-client-ui-settings-plugins";
18
+ tag.dataset.pluginCss = tagId$3;
19
+ tag.textContent = css$3;
20
+ document.head.appendChild(tag);
21
+ }
22
+ var fields_module_css_default = {
23
+ "badge": "ObQdYa_badge",
24
+ "badgeMuted": "ObQdYa_badgeMuted",
25
+ "badges": "ObQdYa_badges",
26
+ "field": "ObQdYa_field",
27
+ "head": "ObQdYa_head",
28
+ "hint": "ObQdYa_hint",
29
+ "input": "ObQdYa_input",
30
+ "inputInvalid": "ObQdYa_inputInvalid",
31
+ "invalid": "ObQdYa_invalid",
32
+ "label": "ObQdYa_label",
33
+ "reset": "ObQdYa_reset"
34
+ };
35
+ //#endregion
36
+ //#region lib/types/client/fields.js
37
+ /**
38
+ * Hand-written controls for the plugin configuration forms. Each renders one
39
+ * field's label, its staged text, whether saving would leave an override, and
40
+ * — when one stands — the reset that stages a clear back to the composition
41
+ * layer. Nothing here writes: a control reports what the user typed, and the
42
+ * card's save is the single point where a draft becomes a document mutation.
43
+ */
44
+ /**
45
+ * A staged value field. `numeric` only hints the keypad: which drafts a field
46
+ * accepts is decided by its spec, so the control never silently rewrites what
47
+ * the user typed.
48
+ * @param props - the field's copy, its staged text, and the edit actions.
49
+ * @returns the labelled control.
50
+ */
51
+ function ValueField(props) {
52
+ return (0, react_jsx_runtime.jsxs)("div", {
53
+ className: fields_module_css_default.field,
54
+ children: [
55
+ (0, react_jsx_runtime.jsxs)("div", {
56
+ className: fields_module_css_default.head,
57
+ children: [(0, react_jsx_runtime.jsx)("label", {
58
+ className: fields_module_css_default.label,
59
+ htmlFor: props.id,
60
+ children: props.label
61
+ }), props.overridden ? (0, react_jsx_runtime.jsxs)("span", {
62
+ className: fields_module_css_default.badges,
63
+ children: [(0, react_jsx_runtime.jsx)("span", {
64
+ className: fields_module_css_default.badge,
65
+ children: props.overriddenLabel
66
+ }), (0, react_jsx_runtime.jsx)("button", {
67
+ type: "button",
68
+ className: fields_module_css_default.reset,
69
+ disabled: props.disabled,
70
+ onClick: props.onReset,
71
+ children: props.resetLabel
72
+ })]
73
+ }) : null]
74
+ }),
75
+ (0, react_jsx_runtime.jsx)("input", {
76
+ id: props.id,
77
+ className: props.invalid ? fields_module_css_default.inputInvalid : fields_module_css_default.input,
78
+ type: "text",
79
+ ...props.numeric === true ? { inputMode: "numeric" } : {},
80
+ ...props.invalid ? { "aria-invalid": true } : {},
81
+ value: props.text,
82
+ placeholder: props.placeholder ?? "",
83
+ disabled: props.disabled,
84
+ onChange: (event) => {
85
+ props.onEdit(event.target.value);
86
+ }
87
+ }),
88
+ (0, react_jsx_runtime.jsx)("p", {
89
+ className: props.invalid ? fields_module_css_default.invalid : fields_module_css_default.hint,
90
+ children: props.invalid ? props.invalidLabel : props.hint
91
+ })
92
+ ]
93
+ });
94
+ }
95
+ /**
96
+ * A write-only credential control. The value never rides a response, so the
97
+ * control reports only whether one is configured and starts blank; a blank
98
+ * draft writes nothing, which keeps the stored key rather than clearing it.
99
+ * @param props - the field's copy, its staged text, and the configured state.
100
+ * @returns the labelled control.
101
+ */
102
+ function SecretField(props) {
103
+ return (0, react_jsx_runtime.jsxs)("div", {
104
+ className: fields_module_css_default.field,
105
+ children: [
106
+ (0, react_jsx_runtime.jsxs)("div", {
107
+ className: fields_module_css_default.head,
108
+ children: [(0, react_jsx_runtime.jsx)("label", {
109
+ className: fields_module_css_default.label,
110
+ htmlFor: props.id,
111
+ children: props.label
112
+ }), (0, react_jsx_runtime.jsx)("span", {
113
+ className: fields_module_css_default.badges,
114
+ children: (0, react_jsx_runtime.jsx)("span", {
115
+ className: props.configured ? fields_module_css_default.badge : fields_module_css_default.badgeMuted,
116
+ children: props.stateLabel
117
+ })
118
+ })]
119
+ }),
120
+ (0, react_jsx_runtime.jsx)("input", {
121
+ id: props.id,
122
+ className: fields_module_css_default.input,
123
+ type: "password",
124
+ autoComplete: "off",
125
+ value: props.text,
126
+ disabled: props.disabled,
127
+ onChange: (event) => {
128
+ props.onEdit(event.target.value);
129
+ }
130
+ }),
131
+ (0, react_jsx_runtime.jsx)("p", {
132
+ className: fields_module_css_default.hint,
133
+ children: props.hint
134
+ })
135
+ ]
136
+ });
137
+ }
138
+ //#endregion
139
+ //#region ../../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
140
+ function r(e) {
141
+ var t, f, n = "";
142
+ if ("string" == typeof e || "number" == typeof e) n += e;
143
+ else if ("object" == typeof e) if (Array.isArray(e)) {
144
+ var o = e.length;
145
+ for (t = 0; t < o; t++) e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
146
+ } else for (f in e) e[f] && (n && (n += " "), n += f);
147
+ return n;
148
+ }
149
+ function clsx() {
150
+ for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
151
+ return n;
152
+ }
153
+ //#endregion
154
+ //#region \0dsh-css:/home/runner/work/dsh-publisher/dsh-publisher/upstream/packages/client/ui-settings-plugins/src/client/PluginCard.module.css.mjs
155
+ const css$2 = "._5eVFka_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}._5eVFka_card:hover{border-color:var(--dsw-alias-label-dimmed)}._5eVFka_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}._5eVFka_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}._5eVFka_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}._5eVFka_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}._5eVFka_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}._5eVFka_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}._5eVFka_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}._5eVFka_chevronOpen{transform:rotate(180deg)}._5eVFka_body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}._5eVFka_readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}._5eVFka_pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}._5eVFka_footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}._5eVFka_failed{min-width:0;color:var(--dsw-alias-label-error);flex:1;margin:0;font-size:12px;line-height:1.5}._5eVFka_discard,._5eVFka_save{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}._5eVFka_discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}._5eVFka_discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}._5eVFka_save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}._5eVFka_discard:disabled,._5eVFka_save:disabled{opacity:.4;cursor:default}._5eVFka_discard:focus-visible,._5eVFka_save:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}";
156
+ const tagId$2 = "@prettier-ai/dsh-client-ui-settings-plugins/PluginCard.module.css";
157
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
158
+ const tag = document.createElement("style");
159
+ tag.dataset.plugin = "@prettier-ai/dsh-client-ui-settings-plugins";
160
+ tag.dataset.pluginCss = tagId$2;
161
+ tag.textContent = css$2;
162
+ document.head.appendChild(tag);
163
+ }
164
+ var PluginCard_module_css_default = {
165
+ "body": "_5eVFka_body",
166
+ "card": "_5eVFka_card",
167
+ "cardOpen": "_5eVFka_cardOpen",
168
+ "chevron": "_5eVFka_chevron",
169
+ "chevronOpen": "_5eVFka_chevronOpen",
170
+ "description": "_5eVFka_description",
171
+ "discard": "_5eVFka_discard",
172
+ "failed": "_5eVFka_failed",
173
+ "footer": "_5eVFka_footer",
174
+ "headText": "_5eVFka_headText",
175
+ "header": "_5eVFka_header",
176
+ "name": "_5eVFka_name",
177
+ "pending": "_5eVFka_pending",
178
+ "readOnly": "_5eVFka_readOnly",
179
+ "save": "_5eVFka_save"
180
+ };
181
+ //#endregion
182
+ //#region lib/types/client/PluginCard.js
183
+ /**
184
+ * One plugin's card: a header naming the plugin and what its settings govern,
185
+ * disclosing that plugin's controls in place, with the save that writes them.
186
+ *
187
+ * The header is its own button rather than a shared disclosure row because a
188
+ * card stacks its name over its description, while that row lays the two side
189
+ * by side — the layout, not the behavior, is what differs. Disclosure is
190
+ * card-local state: which card a user has open is a reading gesture, not
191
+ * something the Host or the section has any stake in. Staged edits outlive
192
+ * collapsing, so the header marks a card holding unsaved edits.
193
+ *
194
+ * A card renders nothing while its namespace is unavailable: a deployment that
195
+ * does not compose the owning plugin should show no trace of it, rather than a
196
+ * disabled card the user cannot act on.
197
+ */
198
+ /**
199
+ * Render one plugin card.
200
+ * @param props - the plugin's copy keys, its form state, and its controls.
201
+ * @returns the card, or nothing when the namespace is unavailable.
202
+ */
203
+ function PluginCard(props) {
204
+ const [open, setOpen] = (0, react.useState)(false);
205
+ const saveStarted = (0, react.useRef)(false);
206
+ const { state } = props;
207
+ (0, react.useEffect)(() => {
208
+ if (state.saving) {
209
+ saveStarted.current = true;
210
+ return;
211
+ }
212
+ if (!saveStarted.current) return;
213
+ saveStarted.current = false;
214
+ if (!state.dirty && !state.failed) setOpen(false);
215
+ }, [
216
+ state.dirty,
217
+ state.failed,
218
+ state.saving
219
+ ]);
220
+ if (!state.available) return null;
221
+ const title = props.t(props.titleKey);
222
+ const blocked = !state.dirty || state.invalid || state.saving;
223
+ return (0, react_jsx_runtime.jsxs)("li", {
224
+ className: clsx(PluginCard_module_css_default.card, open && PluginCard_module_css_default.cardOpen),
225
+ children: [(0, react_jsx_runtime.jsxs)("button", {
226
+ type: "button",
227
+ className: PluginCard_module_css_default.header,
228
+ "aria-expanded": open,
229
+ "aria-label": `${props.t(open ? "collapse" : "expand")}: ${title}`,
230
+ onClick: () => {
231
+ setOpen(!open);
232
+ },
233
+ children: [
234
+ (0, react_jsx_runtime.jsxs)("span", {
235
+ className: PluginCard_module_css_default.headText,
236
+ children: [(0, react_jsx_runtime.jsx)("span", {
237
+ className: PluginCard_module_css_default.name,
238
+ children: title
239
+ }), (0, react_jsx_runtime.jsx)("span", {
240
+ className: PluginCard_module_css_default.description,
241
+ children: props.t(props.descriptionKey)
242
+ })]
243
+ }),
244
+ state.dirty ? (0, react_jsx_runtime.jsx)("span", {
245
+ className: PluginCard_module_css_default.pending,
246
+ children: props.t("unsaved")
247
+ }) : null,
248
+ (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: clsx(PluginCard_module_css_default.chevron, open && PluginCard_module_css_default.chevronOpen) })
249
+ ]
250
+ }), open ? (0, react_jsx_runtime.jsxs)("div", {
251
+ className: PluginCard_module_css_default.body,
252
+ children: [
253
+ !state.writable ? (0, react_jsx_runtime.jsx)("p", {
254
+ className: PluginCard_module_css_default.readOnly,
255
+ role: "status",
256
+ children: props.t("readOnly")
257
+ }) : null,
258
+ props.children,
259
+ (0, react_jsx_runtime.jsxs)("div", {
260
+ className: PluginCard_module_css_default.footer,
261
+ children: [
262
+ state.failed ? (0, react_jsx_runtime.jsx)("p", {
263
+ className: PluginCard_module_css_default.failed,
264
+ role: "status",
265
+ children: props.t("saveFailed")
266
+ }) : null,
267
+ (0, react_jsx_runtime.jsx)("button", {
268
+ type: "button",
269
+ className: PluginCard_module_css_default.discard,
270
+ disabled: !state.dirty || state.saving,
271
+ onClick: props.onDiscard,
272
+ children: props.t("discard")
273
+ }),
274
+ (0, react_jsx_runtime.jsx)("button", {
275
+ type: "button",
276
+ className: PluginCard_module_css_default.save,
277
+ disabled: blocked,
278
+ onClick: props.onSave,
279
+ children: props.t(state.saving ? "saving" : "save")
280
+ })
281
+ ]
282
+ })
283
+ ]
284
+ }) : null]
285
+ });
286
+ }
287
+ //#endregion
288
+ //#region lib/types/client/AgentLoopCard.js
289
+ /**
290
+ * Render the agent-loop card.
291
+ * @param props - locale copy, the card snapshot, and its form actions.
292
+ * @returns the card.
293
+ */
294
+ function AgentLoopCard(props) {
295
+ const { t } = props;
296
+ const state = props.useAgentLoopCard((snapshot) => snapshot);
297
+ return (0, react_jsx_runtime.jsx)(PluginCard, {
298
+ t,
299
+ titleKey: "agentLoopTitle",
300
+ descriptionKey: "agentLoopDescription",
301
+ state,
302
+ onSave: props.save,
303
+ onDiscard: props.discard,
304
+ children: (0, react_jsx_runtime.jsx)(ValueField, {
305
+ id: "plugin-config-agent-loop-parallel",
306
+ label: t("agentLoopMaxParallel"),
307
+ hint: t("agentLoopMaxParallelHint"),
308
+ overriddenLabel: t("overridden"),
309
+ resetLabel: t("reset"),
310
+ invalidLabel: t("invalidNumber"),
311
+ numeric: true,
312
+ disabled: !state.writable,
313
+ ...state.maxParallelToolCalls,
314
+ onEdit: (text) => {
315
+ props.edit("maxParallelToolCalls", text);
316
+ },
317
+ onReset: () => {
318
+ props.resetField("maxParallelToolCalls");
319
+ }
320
+ })
321
+ });
322
+ }
323
+ //#endregion
324
+ //#region lib/types/client/BashCard.js
325
+ /**
326
+ * Render the shell card.
327
+ * @param props - locale copy, the card snapshot, and its form actions.
328
+ * @returns the card.
329
+ */
330
+ function BashCard(props) {
331
+ const { t } = props;
332
+ const state = props.useBashCard((snapshot) => snapshot);
333
+ const disabled = !state.writable;
334
+ return (0, react_jsx_runtime.jsxs)(PluginCard, {
335
+ t,
336
+ titleKey: "bashTitle",
337
+ descriptionKey: "bashDescription",
338
+ state,
339
+ onSave: props.save,
340
+ onDiscard: props.discard,
341
+ children: [(0, react_jsx_runtime.jsx)(ValueField, {
342
+ id: "plugin-config-bash-timeout",
343
+ label: t("bashTimeoutMs"),
344
+ hint: t("bashTimeoutMsHint"),
345
+ overriddenLabel: t("overridden"),
346
+ resetLabel: t("reset"),
347
+ invalidLabel: t("invalidNumber"),
348
+ numeric: true,
349
+ disabled,
350
+ ...state.timeoutMs,
351
+ onEdit: (text) => {
352
+ props.edit("timeoutMs", text);
353
+ },
354
+ onReset: () => {
355
+ props.resetField("timeoutMs");
356
+ }
357
+ }), (0, react_jsx_runtime.jsx)(ValueField, {
358
+ id: "plugin-config-bash-output",
359
+ label: t("bashMaxOutputBytes"),
360
+ hint: t("bashMaxOutputBytesHint"),
361
+ overriddenLabel: t("overridden"),
362
+ resetLabel: t("reset"),
363
+ invalidLabel: t("invalidNumber"),
364
+ numeric: true,
365
+ disabled,
366
+ ...state.maxOutputBytes,
367
+ onEdit: (text) => {
368
+ props.edit("maxOutputBytes", text);
369
+ },
370
+ onReset: () => {
371
+ props.resetField("maxOutputBytes");
372
+ }
373
+ })]
374
+ });
375
+ }
376
+ //#endregion
377
+ //#region \0dsh-css:/home/runner/work/dsh-publisher/dsh-publisher/upstream/packages/client/ui-settings-plugins/src/client/PluginsSettingsSection.module.css.mjs
378
+ const css$1 = ".SmWI6q_section{max-width:760px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:12px;display:flex}.SmWI6q_heading{margin:0;font-size:18px;font-weight:600}.SmWI6q_intro{color:var(--dsw-alias-label-tertiary);margin:0;font-size:13px}.SmWI6q_tabs{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:flex-end;gap:22px;margin-top:2px;display:flex}.SmWI6q_tab{color:var(--dsw-alias-label-tertiary);font:inherit;cursor:pointer;background:0 0;border:0;padding:7px 1px 9px;font-size:13px;line-height:20px;position:relative}.SmWI6q_tab:hover,.SmWI6q_tab[data-active=true]{color:var(--dsw-alias-label-primary)}.SmWI6q_tab[data-active=true]:after,.SmWI6q_tab:focus-visible:after{background:var(--dsw-alias-label-primary);content:\"\";border-radius:2px 2px 0 0;height:2px;position:absolute;bottom:-1px;left:0;right:0}.SmWI6q_tab:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px;color:var(--dsw-alias-label-primary);border-radius:2px}.SmWI6q_panel{min-width:0;padding-top:2px}.SmWI6q_cards{flex-direction:column;gap:10px;margin:0;padding:0;list-style:none;display:flex}.SmWI6q_empty{color:var(--dsw-alias-label-tertiary);margin:0;font-size:13px}";
379
+ const tagId$1 = "@prettier-ai/dsh-client-ui-settings-plugins/PluginsSettingsSection.module.css";
380
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
381
+ const tag = document.createElement("style");
382
+ tag.dataset.plugin = "@prettier-ai/dsh-client-ui-settings-plugins";
383
+ tag.dataset.pluginCss = tagId$1;
384
+ tag.textContent = css$1;
385
+ document.head.appendChild(tag);
386
+ }
387
+ var PluginsSettingsSection_module_css_default = {
388
+ "cards": "SmWI6q_cards",
389
+ "empty": "SmWI6q_empty",
390
+ "heading": "SmWI6q_heading",
391
+ "intro": "SmWI6q_intro",
392
+ "panel": "SmWI6q_panel",
393
+ "section": "SmWI6q_section",
394
+ "tab": "SmWI6q_tab",
395
+ "tabs": "SmWI6q_tabs"
396
+ };
397
+ //#endregion
398
+ //#region lib/types/client/ConfigurablePluginsTab.js
399
+ /**
400
+ * Configurable Host plugins contributed to the shared Plugins section.
401
+ *
402
+ * The tab enumerates settings namespaces but never interprets one — a card
403
+ * arrives through `settings.plugin.item` keyed by the namespace it edits, so a
404
+ * plugin that ships a browser half owns its own card and this tab only decides
405
+ * which keys to dispatch.
406
+ */
407
+ /**
408
+ * Render cards registered by plugins that expose editable settings.
409
+ * @param props - locale copy, slot rendering, and the namespaces to dispatch.
410
+ * @returns the card list, or the empty line once the Host has answered.
411
+ */
412
+ function ConfigurablePluginsTab(props) {
413
+ const { t, renderSlot } = props;
414
+ const { loaded, namespaces } = props.useConfigurablePlugins((snapshot) => snapshot);
415
+ if (namespaces.length > 0) return (0, react_jsx_runtime.jsx)("ul", {
416
+ className: PluginsSettingsSection_module_css_default.cards,
417
+ children: namespaces.map((ns) => (0, react_jsx_runtime.jsx)(react.Fragment, { children: renderSlot("settings.plugin.item", {}, { entryKey: ns }) }, ns))
418
+ });
419
+ return loaded ? (0, react_jsx_runtime.jsx)("p", {
420
+ className: PluginsSettingsSection_module_css_default.empty,
421
+ children: t("empty")
422
+ }) : null;
423
+ }
424
+ //#endregion
425
+ //#region lib/types/client/PluginsSettingsSection.js
426
+ /** Plugins settings section: localized tabs around feature-owned pages. */
427
+ /** Render one Plugins page whose contents arrive from feature-owned tabs. */
428
+ function PluginsSettingsSection({ t, renderSlot, useTabs }) {
429
+ const tabsId = (0, react.useId)();
430
+ const tabRefs = (0, react.useRef)([]);
431
+ const rows = useTabs((value) => value);
432
+ const [activeId, setActiveId] = (0, react.useState)();
433
+ const [visitedIds, setVisitedIds] = (0, react.useState)(() => /* @__PURE__ */ new Set());
434
+ const active = rows.find((row) => row.id === activeId)?.id ?? rows[0]?.id;
435
+ (0, react.useEffect)(() => {
436
+ if (active === void 0) return;
437
+ setVisitedIds((previous) => {
438
+ if (previous.has(active)) return previous;
439
+ return new Set([...previous, active]);
440
+ });
441
+ }, [active]);
442
+ return (0, react_jsx_runtime.jsxs)("div", {
443
+ className: PluginsSettingsSection_module_css_default.section,
444
+ children: [
445
+ (0, react_jsx_runtime.jsx)("h2", {
446
+ className: PluginsSettingsSection_module_css_default.heading,
447
+ children: t("title")
448
+ }),
449
+ (0, react_jsx_runtime.jsx)("p", {
450
+ className: PluginsSettingsSection_module_css_default.intro,
451
+ children: t("intro")
452
+ }),
453
+ rows.length === 0 ? (0, react_jsx_runtime.jsx)("p", {
454
+ className: PluginsSettingsSection_module_css_default.empty,
455
+ children: t("empty")
456
+ }) : (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("div", {
457
+ className: PluginsSettingsSection_module_css_default.tabs,
458
+ role: "tablist",
459
+ "aria-label": t("tabs"),
460
+ children: rows.map((row, index) => {
461
+ const selected = row.id === active;
462
+ return (0, react_jsx_runtime.jsx)("button", {
463
+ ref: (element) => {
464
+ tabRefs.current[index] = element;
465
+ },
466
+ id: `${tabsId}-tab-${row.id}`,
467
+ type: "button",
468
+ role: "tab",
469
+ className: PluginsSettingsSection_module_css_default.tab,
470
+ "aria-selected": selected,
471
+ "aria-controls": `${tabsId}-panel-${row.id}`,
472
+ "data-active": selected ? "true" : void 0,
473
+ tabIndex: selected ? 0 : -1,
474
+ onClick: () => {
475
+ setActiveId(row.id);
476
+ },
477
+ onKeyDown: (event) => {
478
+ let nextIndex;
479
+ switch (event.key) {
480
+ case "ArrowRight":
481
+ nextIndex = (index + 1) % rows.length;
482
+ break;
483
+ case "ArrowLeft":
484
+ nextIndex = (index - 1 + rows.length) % rows.length;
485
+ break;
486
+ case "Home":
487
+ nextIndex = 0;
488
+ break;
489
+ case "End":
490
+ nextIndex = rows.length - 1;
491
+ break;
492
+ default: return;
493
+ }
494
+ event.preventDefault();
495
+ const nextRow = rows[nextIndex];
496
+ const nextTab = tabRefs.current[nextIndex];
497
+ setActiveId(nextRow.id);
498
+ nextTab.focus();
499
+ },
500
+ children: row.label
501
+ }, row.id);
502
+ })
503
+ }), rows.filter((row) => row.id === active || visitedIds.has(row.id)).map((row) => {
504
+ const selected = row.id === active;
505
+ return (0, react_jsx_runtime.jsx)("div", {
506
+ id: `${tabsId}-panel-${row.id}`,
507
+ className: PluginsSettingsSection_module_css_default.panel,
508
+ role: "tabpanel",
509
+ "aria-labelledby": `${tabsId}-tab-${row.id}`,
510
+ hidden: !selected,
511
+ children: renderSlot("settings.plugins.tab", {}, { only: row.id })
512
+ }, row.id);
513
+ })] })
514
+ ]
515
+ });
516
+ }
517
+ //#endregion
518
+ //#region \0dsh-css:/home/runner/work/dsh-publisher/dsh-publisher/upstream/packages/client/ui-settings-plugins/src/client/SubagentModelSelectionCard.module.css.mjs
519
+ const css = ".zWjHyq_permission{gap:6px;padding:12px 0;display:grid}.zWjHyq_toggleRow{color:var(--dsw-alias-label-secondary);justify-content:space-between;align-items:flex-start;gap:16px;font-size:13px;line-height:1.5;display:flex}.zWjHyq_toggleLabel{flex:1;min-width:0}.zWjHyq_switch{box-sizing:border-box;background:var(--dsw-alias-border-l3);cursor:pointer;border:0;border-radius:10px;flex:none;width:36px;height:20px;padding:2px;position:relative}.zWjHyq_switchOn{background:var(--dsw-alias-brand-primary)}.zWjHyq_switch:disabled{cursor:default;opacity:.5}.zWjHyq_switch:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.zWjHyq_thumb{background:var(--dsw-alias-label-primary-foreground);border-radius:50%;width:16px;height:16px;transition:transform .12s;display:block}.zWjHyq_switchOn .zWjHyq_thumb{transform:translate(16px)}.zWjHyq_selection{gap:10px;display:grid}.zWjHyq_hint,.zWjHyq_notice,.zWjHyq_invalid,.zWjHyq_conflict{margin:0;font-size:12px;line-height:1.5}.zWjHyq_hint,.zWjHyq_notice{color:var(--dsw-alias-label-tertiary)}.zWjHyq_invalid,.zWjHyq_conflict{color:var(--dsw-alias-label-error)}.zWjHyq_catalogError{color:var(--dsw-alias-label-error);justify-content:space-between;align-items:center;gap:12px;font-size:12px;display:flex}.zWjHyq_catalogError button{color:var(--dsw-alias-brand-primary);cursor:pointer;background:0 0;border:0;padding:0}.zWjHyq_models{border:1px solid var(--dsw-alias-border-l2);border-radius:8px;gap:6px;min-width:0;max-height:280px;margin:0;padding:10px;display:grid;overflow:auto}.zWjHyq_models legend{color:var(--dsw-alias-label-secondary);padding:0 4px;font-size:12px}.zWjHyq_modelGroup{gap:6px;display:grid}.zWjHyq_modelGroup+.zWjHyq_modelGroup{border-top:1px solid var(--dsw-alias-border-l3);margin-top:4px;padding-top:10px}.zWjHyq_providerName{color:var(--dsw-alias-label-tertiary);padding:0 6px;font-size:11px;font-weight:500}.zWjHyq_model{cursor:pointer;border-radius:6px;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:8px;min-width:0;padding:6px;display:grid}.zWjHyq_model:hover{background:var(--dsw-alias-bg-layer-4)}.zWjHyq_modelName,.zWjHyq_route{text-overflow:ellipsis;white-space:nowrap;display:block;overflow:hidden}.zWjHyq_modelName{color:var(--dsw-alias-label-primary);font-size:13px}.zWjHyq_route{color:var(--dsw-alias-label-tertiary);margin-top:2px;font-size:11px}.zWjHyq_unavailable{color:var(--dsw-alias-label-tertiary);font-size:11px}";
520
+ const tagId = "@prettier-ai/dsh-client-ui-settings-plugins/SubagentModelSelectionCard.module.css";
521
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
522
+ const tag = document.createElement("style");
523
+ tag.dataset.plugin = "@prettier-ai/dsh-client-ui-settings-plugins";
524
+ tag.dataset.pluginCss = tagId;
525
+ tag.textContent = css;
526
+ document.head.appendChild(tag);
527
+ }
528
+ var SubagentModelSelectionCard_module_css_default = {
529
+ "catalogError": "zWjHyq_catalogError",
530
+ "conflict": "zWjHyq_conflict",
531
+ "hint": "zWjHyq_hint",
532
+ "invalid": "zWjHyq_invalid",
533
+ "model": "zWjHyq_model",
534
+ "modelGroup": "zWjHyq_modelGroup",
535
+ "modelName": "zWjHyq_modelName",
536
+ "models": "zWjHyq_models",
537
+ "notice": "zWjHyq_notice",
538
+ "permission": "zWjHyq_permission",
539
+ "providerName": "zWjHyq_providerName",
540
+ "route": "zWjHyq_route",
541
+ "selection": "zWjHyq_selection",
542
+ "switch": "zWjHyq_switch",
543
+ "switchOn": "zWjHyq_switchOn",
544
+ "thumb": "zWjHyq_thumb",
545
+ "toggleLabel": "zWjHyq_toggleLabel",
546
+ "toggleRow": "zWjHyq_toggleRow",
547
+ "unavailable": "zWjHyq_unavailable"
548
+ };
549
+ //#endregion
550
+ //#region lib/types/client/SubagentModelSelectionCard.js
551
+ /** User control for model-selectable subagent delegation in new sessions. */
552
+ /**
553
+ * Render the default-off preference and its exact adapter-route choices.
554
+ * @param props - locale copy, the card snapshot, and its toggle action.
555
+ * @returns the preference card, or nothing when the namespace is unavailable.
556
+ */
557
+ function SubagentModelSelectionCard(props) {
558
+ const { t } = props;
559
+ const state = props.useSubagentModelSelectionCard((snapshot) => snapshot);
560
+ const availableGroups = /* @__PURE__ */ new Map();
561
+ const unavailable = [];
562
+ for (const candidate of state.candidates) {
563
+ if (!candidate.available) {
564
+ unavailable.push(candidate);
565
+ continue;
566
+ }
567
+ const group = availableGroups.get(candidate.provider);
568
+ if (group === void 0) availableGroups.set(candidate.provider, {
569
+ providerName: candidate.providerName,
570
+ candidates: [candidate]
571
+ });
572
+ else group.candidates.push(candidate);
573
+ }
574
+ const renderCandidate = (candidate) => (0, react_jsx_runtime.jsxs)("label", {
575
+ className: SubagentModelSelectionCard_module_css_default.model,
576
+ children: [
577
+ (0, react_jsx_runtime.jsx)("input", {
578
+ type: "checkbox",
579
+ checked: candidate.selected,
580
+ disabled: !state.writable || state.saving,
581
+ onChange: () => {
582
+ props.toggleModel(candidate.key);
583
+ }
584
+ }),
585
+ (0, react_jsx_runtime.jsxs)("span", { children: [(0, react_jsx_runtime.jsx)("span", {
586
+ className: SubagentModelSelectionCard_module_css_default.modelName,
587
+ children: candidate.modelName
588
+ }), (0, react_jsx_runtime.jsx)("span", {
589
+ className: SubagentModelSelectionCard_module_css_default.route,
590
+ children: `${candidate.providerName} · ${candidate.provider}/${candidate.model}`
591
+ })] }),
592
+ !candidate.available ? (0, react_jsx_runtime.jsx)("span", {
593
+ className: SubagentModelSelectionCard_module_css_default.unavailable,
594
+ children: t("subagentModelSelectionUnavailable")
595
+ }) : null
596
+ ]
597
+ }, candidate.key);
598
+ return (0, react_jsx_runtime.jsxs)(PluginCard, {
599
+ t,
600
+ titleKey: "subagentModelSelectionTitle",
601
+ descriptionKey: "subagentModelSelectionDescription",
602
+ state,
603
+ onSave: props.save,
604
+ onDiscard: props.discard,
605
+ children: [
606
+ (0, react_jsx_runtime.jsxs)("div", {
607
+ className: SubagentModelSelectionCard_module_css_default.permission,
608
+ children: [(0, react_jsx_runtime.jsxs)("div", {
609
+ className: SubagentModelSelectionCard_module_css_default.toggleRow,
610
+ children: [(0, react_jsx_runtime.jsx)("span", {
611
+ className: SubagentModelSelectionCard_module_css_default.toggleLabel,
612
+ children: t("subagentModelSelectionToggle")
613
+ }), (0, react_jsx_runtime.jsx)("button", {
614
+ type: "button",
615
+ role: "switch",
616
+ "aria-checked": state.enabled,
617
+ "aria-label": t("subagentModelSelectionToggle"),
618
+ className: clsx(SubagentModelSelectionCard_module_css_default.switch, state.enabled && SubagentModelSelectionCard_module_css_default.switchOn),
619
+ disabled: !state.writable || state.saving,
620
+ onClick: props.toggleEnabled,
621
+ children: (0, react_jsx_runtime.jsx)("span", { className: SubagentModelSelectionCard_module_css_default.thumb })
622
+ })]
623
+ }), (0, react_jsx_runtime.jsx)("p", {
624
+ className: SubagentModelSelectionCard_module_css_default.hint,
625
+ children: t(state.enabled ? "subagentModelSelectionChoose" : "subagentModelSelectionOff")
626
+ })]
627
+ }),
628
+ state.enabled ? (0, react_jsx_runtime.jsxs)("div", {
629
+ className: SubagentModelSelectionCard_module_css_default.selection,
630
+ children: [
631
+ state.catalogStatus === "loading" ? (0, react_jsx_runtime.jsx)("p", {
632
+ className: SubagentModelSelectionCard_module_css_default.notice,
633
+ role: "status",
634
+ children: t("subagentModelSelectionLoading")
635
+ }) : null,
636
+ state.catalogStatus === "error" ? (0, react_jsx_runtime.jsxs)("div", {
637
+ className: SubagentModelSelectionCard_module_css_default.catalogError,
638
+ role: "alert",
639
+ children: [(0, react_jsx_runtime.jsx)("span", { children: t("subagentModelSelectionLoadFailed") }), (0, react_jsx_runtime.jsx)("button", {
640
+ type: "button",
641
+ disabled: state.saving,
642
+ onClick: props.retryCatalog,
643
+ children: t("subagentModelSelectionRetry")
644
+ })]
645
+ }) : null,
646
+ state.catalogPartial ? (0, react_jsx_runtime.jsx)("p", {
647
+ className: SubagentModelSelectionCard_module_css_default.notice,
648
+ children: t("subagentModelSelectionPartial")
649
+ }) : null,
650
+ state.candidates.length > 0 ? (0, react_jsx_runtime.jsxs)("fieldset", {
651
+ className: SubagentModelSelectionCard_module_css_default.models,
652
+ children: [
653
+ (0, react_jsx_runtime.jsx)("legend", { children: t("subagentModelSelectionAllowed") }),
654
+ [...availableGroups].map(([provider, group]) => (0, react_jsx_runtime.jsxs)("div", {
655
+ className: SubagentModelSelectionCard_module_css_default.modelGroup,
656
+ children: [(0, react_jsx_runtime.jsx)("div", {
657
+ className: SubagentModelSelectionCard_module_css_default.providerName,
658
+ children: group.providerName
659
+ }), group.candidates.map(renderCandidate)]
660
+ }, provider)),
661
+ unavailable.length > 0 ? (0, react_jsx_runtime.jsxs)("div", {
662
+ className: SubagentModelSelectionCard_module_css_default.modelGroup,
663
+ children: [(0, react_jsx_runtime.jsx)("div", {
664
+ className: SubagentModelSelectionCard_module_css_default.providerName,
665
+ children: t("subagentModelSelectionUnavailableGroup")
666
+ }), unavailable.map(renderCandidate)]
667
+ }) : null
668
+ ]
669
+ }) : state.catalogStatus === "ready" ? (0, react_jsx_runtime.jsx)("p", {
670
+ className: SubagentModelSelectionCard_module_css_default.notice,
671
+ children: t("subagentModelSelectionEmpty")
672
+ }) : null,
673
+ state.invalid ? (0, react_jsx_runtime.jsx)("p", {
674
+ className: SubagentModelSelectionCard_module_css_default.invalid,
675
+ children: t("subagentModelSelectionRequired")
676
+ }) : null
677
+ ]
678
+ }) : null,
679
+ state.conflicted ? (0, react_jsx_runtime.jsx)("p", {
680
+ className: SubagentModelSelectionCard_module_css_default.conflict,
681
+ role: "status",
682
+ children: t("subagentModelSelectionConflict")
683
+ }) : null
684
+ ]
685
+ });
686
+ }
687
+ //#endregion
688
+ //#region lib/types/client/WebSearchCard.js
689
+ /**
690
+ * Render the web-search card.
691
+ * @param props - locale copy, the card snapshot, and its form actions.
692
+ * @returns the card.
693
+ */
694
+ function WebSearchCard(props) {
695
+ const { t } = props;
696
+ const state = props.useWebSearchCard((snapshot) => snapshot);
697
+ const disabled = !state.writable;
698
+ return (0, react_jsx_runtime.jsxs)(PluginCard, {
699
+ t,
700
+ titleKey: "webSearchTitle",
701
+ descriptionKey: "webSearchDescription",
702
+ state,
703
+ onSave: props.save,
704
+ onDiscard: props.discard,
705
+ children: [
706
+ (0, react_jsx_runtime.jsx)(SecretField, {
707
+ id: "plugin-config-web-search-key",
708
+ label: t("webSearchApiKey"),
709
+ hint: t("webSearchApiKeyHint"),
710
+ disabled: !state.apiKeyWritable,
711
+ text: state.apiKey.text,
712
+ configured: state.apiKeyConfigured,
713
+ stateLabel: state.apiKeyConfigured ? t("webSearchApiKeySet") : t("webSearchApiKeyUnset"),
714
+ onEdit: (text) => {
715
+ props.edit("apiKey", text);
716
+ }
717
+ }),
718
+ (0, react_jsx_runtime.jsx)(ValueField, {
719
+ id: "plugin-config-web-search-endpoint",
720
+ label: t("webSearchBaseUrl"),
721
+ hint: t("webSearchBaseUrlHint"),
722
+ overriddenLabel: t("overridden"),
723
+ resetLabel: t("reset"),
724
+ invalidLabel: t("invalidNumber"),
725
+ disabled,
726
+ ...state.baseURL,
727
+ onEdit: (text) => {
728
+ props.edit("baseURL", text);
729
+ },
730
+ onReset: () => {
731
+ props.resetField("baseURL");
732
+ }
733
+ }),
734
+ (0, react_jsx_runtime.jsx)(ValueField, {
735
+ id: "plugin-config-web-search-max-uses",
736
+ label: t("webSearchMaxUses"),
737
+ hint: t("webSearchMaxUsesHint"),
738
+ overriddenLabel: t("overridden"),
739
+ resetLabel: t("reset"),
740
+ invalidLabel: t("invalidNumber"),
741
+ numeric: true,
742
+ disabled,
743
+ ...state.maxUses,
744
+ onEdit: (text) => {
745
+ props.edit("maxUses", text);
746
+ },
747
+ onReset: () => {
748
+ props.resetField("maxUses");
749
+ }
750
+ })
751
+ ]
752
+ });
753
+ }
754
+ //#endregion
755
+ //#region lib/types/client/card-form.js
756
+ /**
757
+ * Shared form model behind every plugin card.
758
+ *
759
+ * A card stages what the user types and writes it only when they save. Each
760
+ * settings write is a durable, revision-fenced document mutation, so a control
761
+ * that committed as it settled turned one edit into a write the user never
762
+ * asked for and could not preview; staged text makes what is on screen exactly
763
+ * what a save would store.
764
+ *
765
+ * A field shows its effective value — the user layer over the composition
766
+ * layer over the schema default — and whether the user layer carries it. That
767
+ * presence, not a value comparison, is what marks a field overridden: an
768
+ * override equal to the composition default is still an override.
769
+ */
770
+ /**
771
+ * A whole-number field. An empty draft clears the field; any other draft that
772
+ * is not a finite number blocks the save.
773
+ * @param field - field name inside the namespace section.
774
+ * @returns the field's conversion spec.
775
+ */
776
+ function numberField(field) {
777
+ return {
778
+ field,
779
+ format: (value) => typeof value === "number" ? String(value) : "",
780
+ parse: (text) => {
781
+ const trimmed = text.trim();
782
+ if (trimmed === "") return { kind: "clear" };
783
+ const parsed = Number(trimmed);
784
+ return Number.isFinite(parsed) ? {
785
+ kind: "set",
786
+ value: parsed
787
+ } : void 0;
788
+ }
789
+ };
790
+ }
791
+ /**
792
+ * A free-text field. An empty draft clears the field, so emptying the control
793
+ * and saving is the same gesture as resetting it.
794
+ * @param field - field name inside the namespace section.
795
+ * @returns the field's conversion spec.
796
+ */
797
+ function textField(field) {
798
+ return {
799
+ field,
800
+ format: (value) => typeof value === "string" ? value : "",
801
+ parse: (text) => {
802
+ const trimmed = text.trim();
803
+ return trimmed === "" ? { kind: "clear" } : {
804
+ kind: "set",
805
+ value: trimmed
806
+ };
807
+ }
808
+ };
809
+ }
810
+ /**
811
+ * Stages one card's edits over one settings namespace and writes them on save.
812
+ *
813
+ * The form publishes through a snapshot store because slot components read
814
+ * through a snapshot selector, while both the scope and the local drafts
815
+ * change underneath; every projection is rebuilt from the two together.
816
+ */
817
+ var CardForm = class {
818
+ scope;
819
+ specs;
820
+ secretSpecs;
821
+ staged = /* @__PURE__ */ new Map();
822
+ listeners = /* @__PURE__ */ new Set();
823
+ saving = false;
824
+ failed = false;
825
+ /**
826
+ * @param scope - the bound settings scope for this card's namespace.
827
+ * @param specs - the section fields this card edits.
828
+ * @param secrets - the card's write-only controls, written outside the section.
829
+ */
830
+ constructor(scope, specs, secrets = []) {
831
+ this.scope = scope;
832
+ this.specs = new Map(specs.map((spec) => [spec.field, spec]));
833
+ this.secretSpecs = new Map(secrets.map((spec) => [spec.field, spec]));
834
+ scope.subscribe(() => {
835
+ this.publish();
836
+ });
837
+ }
838
+ /**
839
+ * Publish a projection of this form, rebuilt whenever the scope or a draft changes.
840
+ * @param project - build the card's state from the form's current reads.
841
+ * @returns the store the card's component reads through its bound selector.
842
+ */
843
+ bind(project) {
844
+ const store = (0, _prettier_ai_dsh_client_store.createSnapshotStore)(project());
845
+ this.listeners.add(() => {
846
+ store.set(project());
847
+ });
848
+ return store;
849
+ }
850
+ /**
851
+ * Read the card-level state: what the Host serves, and what a save would do.
852
+ * @returns the form state every card shares.
853
+ */
854
+ shell() {
855
+ const snapshot = this.scope.getSnapshot();
856
+ const plan = this.plan();
857
+ return {
858
+ available: snapshot.status === "ready",
859
+ writable: snapshot.writable,
860
+ dirty: plan.length > 0,
861
+ invalid: plan.some((item) => item.run === void 0),
862
+ saving: this.saving,
863
+ failed: this.failed
864
+ };
865
+ }
866
+ /**
867
+ * Read one control's state.
868
+ * @param field - field name of a section field or of a write-only control.
869
+ * @returns the draft text, whether a save would leave an override, and whether it is invalid.
870
+ */
871
+ field(field) {
872
+ const staged = this.staged.get(field);
873
+ if (this.secretSpecs.has(field)) return {
874
+ text: staged?.text ?? "",
875
+ overridden: false,
876
+ invalid: false
877
+ };
878
+ const spec = this.spec(field);
879
+ if (staged === void 0) return {
880
+ text: spec.format(this.sectionValue(field)),
881
+ overridden: this.stored(field),
882
+ invalid: false
883
+ };
884
+ const write = staged.clear ? { kind: "clear" } : spec.parse(staged.text);
885
+ return {
886
+ text: staged.text,
887
+ overridden: write?.kind === "set",
888
+ invalid: write === void 0
889
+ };
890
+ }
891
+ /**
892
+ * Build the edit, reset, save, and discard actions bound to this form.
893
+ * @returns the actions a card's slot entry injects.
894
+ */
895
+ actions() {
896
+ return {
897
+ edit: (field, text) => {
898
+ this.stage(field, {
899
+ text,
900
+ clear: false
901
+ });
902
+ },
903
+ resetField: (field) => {
904
+ this.stage(field, {
905
+ text: this.spec(field).format(this.baseValue(field)),
906
+ clear: true
907
+ });
908
+ },
909
+ save: () => {
910
+ this.save();
911
+ },
912
+ discard: () => {
913
+ if (this.staged.size === 0 && !this.failed) return;
914
+ this.staged.clear();
915
+ this.failed = false;
916
+ this.publish();
917
+ }
918
+ };
919
+ }
920
+ /**
921
+ * Write every staged edit, then re-seed from what the Host accepted.
922
+ *
923
+ * The Host is the only authority on whether a value was accepted — its
924
+ * validators own the constraints no schema can express — so the outcome is
925
+ * read back from the section rather than predicted here. A save that did not
926
+ * land keeps its drafts, so the user can correct them instead of retyping.
927
+ * @returns settlement after every write and the read-back.
928
+ */
929
+ async save() {
930
+ const plan = this.plan();
931
+ const writes = plan.flatMap((item) => item.run === void 0 ? [] : [item.run]);
932
+ if (plan.length === 0 || this.saving || writes.length !== plan.length) return;
933
+ this.saving = true;
934
+ this.failed = false;
935
+ this.publish();
936
+ let landed = true;
937
+ for (const write of writes) landed = await write() && landed;
938
+ if (landed) this.staged.clear();
939
+ this.saving = false;
940
+ this.failed = !landed;
941
+ this.publish();
942
+ }
943
+ /**
944
+ * Every staged edit a save would write. An entry whose draft is not a value
945
+ * its field accepts carries no write: the form is still dirty, and the save
946
+ * refuses rather than dropping the edit.
947
+ * @returns the planned writes, in the order the fields were staged.
948
+ */
949
+ plan() {
950
+ const plan = [];
951
+ for (const [field, staged] of this.staged) {
952
+ const secret = this.secretSpecs.get(field);
953
+ if (secret !== void 0) {
954
+ const value = staged.text.trim();
955
+ if (value !== "") plan.push({
956
+ field,
957
+ run: () => secret.write(value)
958
+ });
959
+ continue;
960
+ }
961
+ const spec = this.spec(field);
962
+ if (staged.clear) {
963
+ if (this.stored(field)) plan.push({
964
+ field,
965
+ run: () => this.clear(field)
966
+ });
967
+ continue;
968
+ }
969
+ if (staged.text === spec.format(this.sectionValue(field))) continue;
970
+ const write = spec.parse(staged.text);
971
+ if (write === void 0) plan.push({
972
+ field,
973
+ run: void 0
974
+ });
975
+ else if (write.kind === "clear") plan.push({
976
+ field,
977
+ run: () => this.clear(field)
978
+ });
979
+ else plan.push({
980
+ field,
981
+ run: () => this.store(field, write.value)
982
+ });
983
+ }
984
+ return plan;
985
+ }
986
+ async clear(field) {
987
+ await this.scope.unset(field);
988
+ return !this.stored(field);
989
+ }
990
+ async store(field, value) {
991
+ await this.scope.set(field, value);
992
+ return this.userLayer()?.[field] === value;
993
+ }
994
+ stage(field, edit) {
995
+ this.staged.set(field, edit);
996
+ this.failed = false;
997
+ this.publish();
998
+ }
999
+ spec(field) {
1000
+ const spec = this.specs.get(field);
1001
+ if (spec === void 0) throw new Error(`plugin card has no field ${field}`);
1002
+ return spec;
1003
+ }
1004
+ snapshotOf() {
1005
+ return this.scope.getSnapshot();
1006
+ }
1007
+ sectionValue(field) {
1008
+ return this.snapshotOf().value?.[field];
1009
+ }
1010
+ baseValue(field) {
1011
+ return this.snapshotOf().base?.[field];
1012
+ }
1013
+ userLayer() {
1014
+ return this.snapshotOf().user;
1015
+ }
1016
+ stored(field) {
1017
+ const user = this.userLayer();
1018
+ return user !== void 0 && Object.hasOwn(user, field);
1019
+ }
1020
+ publish() {
1021
+ for (const listener of this.listeners) listener();
1022
+ }
1023
+ };
1024
+ //#endregion
1025
+ //#region lib/types/client/agent-loop-card-controller.js
1026
+ /** The agent-loop card's staged form over the `agent-loop` settings namespace. */
1027
+ /**
1028
+ * Namespace of the agent loop's user-owned settings. Spelled here rather than
1029
+ * imported: a client package must not depend on a Host package.
1030
+ */
1031
+ const AGENT_LOOP_NS = "agent-loop";
1032
+ /** Bridges the `agent-loop` scope onto the card's staged form. */
1033
+ var AgentLoopCardController = class {
1034
+ form;
1035
+ store;
1036
+ /** @param scope - the bound settings scope for the `agent-loop` namespace. */
1037
+ constructor(scope) {
1038
+ this.form = new CardForm(scope, [numberField("maxParallelToolCalls")]);
1039
+ this.store = this.form.bind(() => this.projection());
1040
+ }
1041
+ projection() {
1042
+ return {
1043
+ ...this.form.shell(),
1044
+ maxParallelToolCalls: this.form.field("maxParallelToolCalls")
1045
+ };
1046
+ }
1047
+ /**
1048
+ * Build the face the card's slot registration injects.
1049
+ * @returns the card's snapshot and its form actions.
1050
+ */
1051
+ inject() {
1052
+ return {
1053
+ hooks: { agentLoopCard: this.store },
1054
+ ...this.form.actions()
1055
+ };
1056
+ }
1057
+ };
1058
+ //#endregion
1059
+ //#region lib/types/client/bash-card-controller.js
1060
+ /** The shell card's staged form over the `bash` settings namespace. */
1061
+ /**
1062
+ * Namespace of the shell capability. Spelled here rather than imported: a
1063
+ * client package must not depend on a Host package, and the executor families
1064
+ * that own it spell the same value.
1065
+ */
1066
+ const SHELL_NS = "shell";
1067
+ /** Bridges the `bash` scope onto the shell card's staged form. */
1068
+ var BashCardController = class {
1069
+ form;
1070
+ store;
1071
+ /** @param scope - the bound settings scope for the `bash` namespace. */
1072
+ constructor(scope) {
1073
+ this.form = new CardForm(scope, [numberField("timeoutMs"), numberField("maxOutputBytes")]);
1074
+ this.store = this.form.bind(() => this.projection());
1075
+ }
1076
+ projection() {
1077
+ return {
1078
+ ...this.form.shell(),
1079
+ timeoutMs: this.form.field("timeoutMs"),
1080
+ maxOutputBytes: this.form.field("maxOutputBytes")
1081
+ };
1082
+ }
1083
+ /**
1084
+ * Build the face the card's slot registration injects.
1085
+ * @returns the card's snapshot and its form actions.
1086
+ */
1087
+ inject() {
1088
+ return {
1089
+ hooks: { bashCard: this.store },
1090
+ ...this.form.actions()
1091
+ };
1092
+ }
1093
+ };
1094
+ //#endregion
1095
+ //#region lib/types/client/tab-store.js
1096
+ /**
1097
+ * The configurable-plugins tab's card list.
1098
+ *
1099
+ * The tab dispatches its slot by settings namespace, so what it renders is
1100
+ * the intersection of two ledgers: the namespaces the Host serves and the
1101
+ * cards registered into `settings.plugin.item`. A served namespace no card
1102
+ * claims renders nothing — another surface owns it, or this deployment ships
1103
+ * no browser half for it — and a card whose namespace the Host does not serve
1104
+ * is never dispatched, so a plugin this deployment did not compose leaves no
1105
+ * trace and does not count toward the empty line.
1106
+ */
1107
+ /** Derives the served namespaces from the shared describe mirror and pairs them with the cards that claim them. */
1108
+ var ConfigurablePluginsTabController = class {
1109
+ describeFace;
1110
+ entries;
1111
+ store = (0, _prettier_ai_dsh_client_store.createSnapshotStore)({
1112
+ loaded: false,
1113
+ namespaces: []
1114
+ });
1115
+ disposed = false;
1116
+ unsubscribe;
1117
+ /**
1118
+ * @param describeFace - the shared mirror's describe face; its refreshes
1119
+ * (document commits, reconnects) are what keep the served set current.
1120
+ * @param entries - reads the cards currently registered into the section's slot.
1121
+ */
1122
+ constructor(describeFace, entries) {
1123
+ this.describeFace = describeFace;
1124
+ this.entries = entries;
1125
+ this.unsubscribe = describeFace.subscribe(() => {
1126
+ this.publish();
1127
+ });
1128
+ describeFace.ensure();
1129
+ this.publish();
1130
+ }
1131
+ /** Republish after the slot ledger changed; a card registered late joins here. */
1132
+ refresh() {
1133
+ if (this.disposed) return;
1134
+ this.publish();
1135
+ }
1136
+ /** Stop publishing and stop following the mirror. */
1137
+ dispose() {
1138
+ this.disposed = true;
1139
+ this.unsubscribe();
1140
+ }
1141
+ /**
1142
+ * Build the face the tab's slot registration injects.
1143
+ * @returns the tab's snapshot source.
1144
+ */
1145
+ inject() {
1146
+ return { hooks: { configurablePlugins: this.store } };
1147
+ }
1148
+ publish() {
1149
+ if (this.disposed) return;
1150
+ const mirrored = this.describeFace.getSnapshot();
1151
+ const loaded = mirrored.view !== void 0;
1152
+ const served = new Set(mirrored.view?.namespaces.map((view) => view.ns) ?? []);
1153
+ const namespaces = this.entries().flatMap((entry) => entry.options.key !== void 0 && served.has(entry.options.key) ? [entry.options.key] : []);
1154
+ const previous = this.store.getSnapshot();
1155
+ if (previous.loaded === loaded && previous.namespaces.length === namespaces.length && previous.namespaces.every((ns, index) => ns === namespaces[index])) return;
1156
+ this.store.set({
1157
+ loaded,
1158
+ namespaces
1159
+ });
1160
+ }
1161
+ };
1162
+ //#endregion
1163
+ //#region lib/types/client/subagent-model-selection-card-controller.js
1164
+ /** Staged editor for the Host-owned subagent model allowlist. */
1165
+ /** Namespace of the Host-owned subagent model-selection preference. */
1166
+ const SUBAGENT_MODEL_SELECTION_NS = "subagent-model-selection";
1167
+ /**
1168
+ * Stable identity for one exact route; callers resolve it by lookup and never parse it.
1169
+ * @param route - Provider/model route to identify.
1170
+ * @returns Opaque key for lookup within the card.
1171
+ */
1172
+ function subagentModelKey(route) {
1173
+ return `${route.provider}\0${route.model}`;
1174
+ }
1175
+ /**
1176
+ * Join live adapter metadata with stored routes that remain removable after disappearance.
1177
+ * @param groups - Current model directory grouped by provider.
1178
+ * @param stored - Routes in the effective settings value.
1179
+ * @param selected - Opaque route keys selected in the current draft.
1180
+ * @returns Candidate rows for the card.
1181
+ */
1182
+ function subagentModelCandidates(groups, stored, selected) {
1183
+ const storedByKey = new Map(stored.map((route) => [subagentModelKey(route), route]));
1184
+ const candidates = groups.flatMap((group) => group.models.map((model) => {
1185
+ const route = {
1186
+ provider: group.id,
1187
+ model: model.id
1188
+ };
1189
+ const key = subagentModelKey(route);
1190
+ storedByKey.delete(key);
1191
+ return {
1192
+ ...route,
1193
+ key,
1194
+ providerName: group.name,
1195
+ modelName: model.name,
1196
+ available: true,
1197
+ selected: selected.has(key)
1198
+ };
1199
+ }));
1200
+ for (const route of storedByKey.values()) {
1201
+ const key = subagentModelKey(route);
1202
+ candidates.push({
1203
+ ...route,
1204
+ key,
1205
+ providerName: route.provider,
1206
+ modelName: route.model,
1207
+ available: false,
1208
+ selected: selected.has(key)
1209
+ });
1210
+ }
1211
+ return candidates;
1212
+ }
1213
+ function sameRoutes(left, right) {
1214
+ if (left.length !== right.length) return false;
1215
+ const rightKeys = new Set(right.map(subagentModelKey));
1216
+ return left.every((route) => rightKeys.has(subagentModelKey(route)));
1217
+ }
1218
+ /** Bridges one settings scope and the live adapter directory onto a staged card. */
1219
+ var SubagentModelSelectionCardController = class {
1220
+ scope;
1221
+ session;
1222
+ catalogGroups = [];
1223
+ catalogPartial = false;
1224
+ catalogStatus = "idle";
1225
+ draftEnabled;
1226
+ draftRoutes;
1227
+ draftRevision;
1228
+ saving = false;
1229
+ failed = false;
1230
+ conflicted = false;
1231
+ disposed = false;
1232
+ saveGeneration = 0;
1233
+ catalogGeneration = 0;
1234
+ store;
1235
+ unsubscribe;
1236
+ /**
1237
+ * @param scope - bound `subagent-model-selection` settings scope.
1238
+ * @param session - Host Session model-catalog face.
1239
+ */
1240
+ constructor(scope, session) {
1241
+ this.scope = scope;
1242
+ this.session = session;
1243
+ this.store = (0, _prettier_ai_dsh_client_store.createSnapshotStore)(this.projection());
1244
+ this.unsubscribe = scope.subscribe(() => {
1245
+ if (!this.saving && this.draftRoutes !== void 0 && this.scope.getSnapshot().revision !== this.draftRevision) if (this.currentEnabled() === this.enabled() && sameRoutes(this.currentRoutes(), this.desiredRoutes())) this.clearDraft();
1246
+ else this.conflicted = true;
1247
+ if (this.enabled() && this.catalogStatus === "idle") this.loadCatalog();
1248
+ this.publish();
1249
+ });
1250
+ if (this.enabled() && this.catalogStatus === "idle") this.loadCatalog();
1251
+ }
1252
+ /** Stop observing settings and suppress late directory/write settlements. */
1253
+ dispose() {
1254
+ this.disposed = true;
1255
+ this.saveGeneration += 1;
1256
+ this.catalogGeneration += 1;
1257
+ this.unsubscribe();
1258
+ }
1259
+ /**
1260
+ * Build the renderer face for this card.
1261
+ * @returns The snapshot and staged card actions injected into the renderer.
1262
+ */
1263
+ inject() {
1264
+ return {
1265
+ hooks: { subagentModelSelectionCard: this.store },
1266
+ toggleEnabled: () => {
1267
+ this.toggleEnabled();
1268
+ },
1269
+ toggleModel: (key) => {
1270
+ this.toggleModel(key);
1271
+ },
1272
+ retryCatalog: () => {
1273
+ this.loadCatalog();
1274
+ },
1275
+ save: () => {
1276
+ this.save();
1277
+ },
1278
+ discard: () => {
1279
+ this.discard();
1280
+ }
1281
+ };
1282
+ }
1283
+ currentRoutes() {
1284
+ return this.scope.getSnapshot().value?.allowedModels.map((route) => ({ ...route })) ?? [];
1285
+ }
1286
+ currentEnabled() {
1287
+ return this.scope.getSnapshot().value?.enabled ?? false;
1288
+ }
1289
+ selected() {
1290
+ return new Set(this.draftRoutes?.keys() ?? this.currentRoutes().map(subagentModelKey));
1291
+ }
1292
+ enabled() {
1293
+ return this.draftEnabled ?? this.currentEnabled();
1294
+ }
1295
+ beginDraft() {
1296
+ if (this.draftRoutes === void 0) {
1297
+ const snapshot = this.scope.getSnapshot();
1298
+ this.draftEnabled = snapshot.value?.enabled ?? false;
1299
+ this.draftRoutes = new Map(snapshot.value?.allowedModels.map((route) => [subagentModelKey(route), { ...route }]) ?? []);
1300
+ this.draftRevision = snapshot.revision;
1301
+ }
1302
+ return this.draftRoutes;
1303
+ }
1304
+ toggleEnabled() {
1305
+ const snapshot = this.scope.getSnapshot();
1306
+ if (this.disposed || snapshot.status !== "ready" || !snapshot.writable || this.saving) return;
1307
+ this.beginDraft();
1308
+ this.draftEnabled = !this.draftEnabled;
1309
+ this.failed = false;
1310
+ if (this.draftEnabled && this.catalogStatus === "idle") this.loadCatalog();
1311
+ this.publish();
1312
+ }
1313
+ toggleModel(key) {
1314
+ if (!this.enabled() || this.saving || !this.scope.getSnapshot().writable) return;
1315
+ const candidate = this.candidates().find((candidate) => candidate.key === key);
1316
+ if (candidate === void 0) return;
1317
+ const routes = this.beginDraft();
1318
+ if (routes.has(key)) routes.delete(key);
1319
+ else routes.set(key, {
1320
+ provider: candidate.provider,
1321
+ model: candidate.model
1322
+ });
1323
+ this.failed = false;
1324
+ this.publish();
1325
+ }
1326
+ clearDraft() {
1327
+ this.draftEnabled = void 0;
1328
+ this.draftRoutes = void 0;
1329
+ this.draftRevision = void 0;
1330
+ this.failed = false;
1331
+ this.conflicted = false;
1332
+ }
1333
+ discard() {
1334
+ if (this.saving) return;
1335
+ this.clearDraft();
1336
+ this.publish();
1337
+ }
1338
+ candidates() {
1339
+ const retained = new Map(this.currentRoutes().map((route) => [subagentModelKey(route), route]));
1340
+ for (const [key, route] of this.draftRoutes ?? []) retained.set(key, route);
1341
+ return subagentModelCandidates(this.catalogGroups, [...retained.values()], this.selected());
1342
+ }
1343
+ desiredRoutes() {
1344
+ return [...this.draftRoutes?.values() ?? this.currentRoutes()].map((route) => ({ ...route }));
1345
+ }
1346
+ async save() {
1347
+ const snapshot = this.scope.getSnapshot();
1348
+ const desiredEnabled = this.enabled();
1349
+ const desired = this.desiredRoutes();
1350
+ if (this.disposed || snapshot.status !== "ready" || !snapshot.writable || this.saving || this.currentEnabled() === desiredEnabled && sameRoutes(this.currentRoutes(), desired) || desiredEnabled && desired.length === 0) return;
1351
+ if (this.draftRoutes !== void 0 && snapshot.revision !== this.draftRevision) {
1352
+ this.conflicted = true;
1353
+ this.publish();
1354
+ return;
1355
+ }
1356
+ const generation = this.saveGeneration;
1357
+ this.saving = true;
1358
+ this.failed = false;
1359
+ this.conflicted = false;
1360
+ this.publish();
1361
+ await this.scope.mutate([{
1362
+ op: "set",
1363
+ path: ["enabled"],
1364
+ value: desiredEnabled
1365
+ }, {
1366
+ op: "set",
1367
+ path: ["allowedModels"],
1368
+ value: desired.map((route) => ({
1369
+ provider: route.provider,
1370
+ model: route.model
1371
+ }))
1372
+ }], this.draftRevision);
1373
+ if (generation !== this.saveGeneration) return;
1374
+ const landed = this.currentEnabled() === desiredEnabled && sameRoutes(this.currentRoutes(), desired);
1375
+ this.saving = false;
1376
+ this.failed = !landed;
1377
+ if (landed) this.clearDraft();
1378
+ this.publish();
1379
+ }
1380
+ /** Invalidate and reload model candidates after a Host model input changes. */
1381
+ refreshCatalog() {
1382
+ if (this.disposed) return;
1383
+ this.catalogGeneration += 1;
1384
+ this.catalogStatus = "idle";
1385
+ this.catalogPartial = false;
1386
+ if (this.enabled()) this.loadCatalog();
1387
+ else this.publish();
1388
+ }
1389
+ /** Drop Host-specific candidates and drafts, then reload after reconnecting. */
1390
+ resetConnection() {
1391
+ if (this.disposed) return;
1392
+ this.saveGeneration += 1;
1393
+ this.saving = false;
1394
+ this.clearDraft();
1395
+ this.catalogGroups = [];
1396
+ this.refreshCatalog();
1397
+ }
1398
+ async loadCatalog() {
1399
+ if (this.disposed || this.catalogStatus === "loading") return;
1400
+ const generation = this.catalogGeneration;
1401
+ this.catalogStatus = "loading";
1402
+ this.catalogPartial = false;
1403
+ this.publish();
1404
+ try {
1405
+ const response = await this.session.modelCatalog();
1406
+ if (generation !== this.catalogGeneration) return;
1407
+ if (!response.ok) throw new Error(response.error.message);
1408
+ this.catalogGroups = response.value.groups;
1409
+ this.catalogPartial = response.value.failures.length > 0;
1410
+ this.catalogStatus = "ready";
1411
+ } catch {
1412
+ if (generation !== this.catalogGeneration) return;
1413
+ this.catalogStatus = "error";
1414
+ }
1415
+ this.publish();
1416
+ }
1417
+ projection() {
1418
+ const snapshot = this.scope.getSnapshot();
1419
+ const current = this.currentRoutes();
1420
+ const desired = this.desiredRoutes();
1421
+ const enabled = this.enabled();
1422
+ return {
1423
+ available: snapshot.status === "ready",
1424
+ writable: snapshot.writable,
1425
+ dirty: this.currentEnabled() !== enabled || !sameRoutes(current, desired),
1426
+ invalid: enabled && desired.length === 0,
1427
+ saving: this.saving,
1428
+ failed: this.failed,
1429
+ enabled,
1430
+ candidates: this.candidates(),
1431
+ catalogStatus: this.catalogStatus,
1432
+ catalogPartial: this.catalogPartial,
1433
+ conflicted: this.conflicted
1434
+ };
1435
+ }
1436
+ publish() {
1437
+ this.store.set(this.projection());
1438
+ }
1439
+ };
1440
+ //#endregion
1441
+ //#region lib/types/client/web-search-card-controller.js
1442
+ /**
1443
+ * The web-search card's staged form over the `web-search-deepseek` settings
1444
+ * namespace.
1445
+ *
1446
+ * The key is the one control that does not live in the section: its literal
1447
+ * never rides a response, so the card learns only whether one is configured
1448
+ * and writes it through the credentials domain, addressed by the reference the
1449
+ * section names. It is still staged with the rest of the form, so one save
1450
+ * covers everything the card shows.
1451
+ */
1452
+ /**
1453
+ * Namespace of the DeepSeek search provider. Spelled here rather than
1454
+ * imported: a client package must not depend on a Host package.
1455
+ */
1456
+ const WEB_SEARCH_NS = "web-search-deepseek";
1457
+ /** Credential reference the provider resolves when the section names none. */
1458
+ const DEFAULT_API_KEY_REF = "DEEPSEEK_API_KEY";
1459
+ /** Form field the credential control stages under. */
1460
+ const API_KEY_FIELD = "apiKey";
1461
+ /** Bridges the `web-search-deepseek` scope and the credentials domain onto the card. */
1462
+ var WebSearchCardController = class {
1463
+ scope;
1464
+ credentials;
1465
+ form;
1466
+ store;
1467
+ credential = {
1468
+ ref: "",
1469
+ configured: false,
1470
+ writable: true
1471
+ };
1472
+ /**
1473
+ * @param scope - the bound settings scope for the `web-search-deepseek` namespace.
1474
+ * @param credentials - Remote face used for the credential the section references.
1475
+ */
1476
+ constructor(scope, credentials) {
1477
+ this.scope = scope;
1478
+ this.credentials = credentials;
1479
+ this.form = new CardForm(scope, [textField("baseURL"), numberField("maxUses")], [{
1480
+ field: API_KEY_FIELD,
1481
+ write: (text) => this.writeKey(text)
1482
+ }]);
1483
+ this.store = this.form.bind(() => this.projection());
1484
+ scope.subscribe(() => {
1485
+ this.readCredential();
1486
+ });
1487
+ this.readCredential();
1488
+ }
1489
+ projection() {
1490
+ return {
1491
+ ...this.form.shell(),
1492
+ baseURL: this.form.field("baseURL"),
1493
+ maxUses: this.form.field("maxUses"),
1494
+ apiKey: this.form.field(API_KEY_FIELD),
1495
+ apiKeyConfigured: this.credential.configured,
1496
+ apiKeyWritable: this.credential.writable
1497
+ };
1498
+ }
1499
+ /**
1500
+ * Ask the credentials domain about the reference the section currently names.
1501
+ *
1502
+ * The answer is stored with the reference it describes: `apiKeyEnv` can
1503
+ * change between the request and its response, and two reads can settle out
1504
+ * of order, so a response is published only while it still answers for the
1505
+ * reference in force.
1506
+ */
1507
+ async readCredential() {
1508
+ const ref = refOf(this.scope.getSnapshot());
1509
+ if (ref !== this.credential.ref) {
1510
+ this.credential = {
1511
+ ref,
1512
+ configured: false,
1513
+ writable: true
1514
+ };
1515
+ this.store.set(this.projection());
1516
+ }
1517
+ let response;
1518
+ try {
1519
+ response = await this.credentials.describe([ref]);
1520
+ } catch (_credentialReadFailure) {
1521
+ return;
1522
+ }
1523
+ if (!response.ok || ref !== refOf(this.scope.getSnapshot())) return;
1524
+ const view = response.value[ref];
1525
+ const next = {
1526
+ ref,
1527
+ configured: view?.configured ?? false,
1528
+ writable: view?.writable ?? true
1529
+ };
1530
+ if (next.configured === this.credential.configured && next.writable === this.credential.writable) return;
1531
+ this.credential = next;
1532
+ this.store.set(this.projection());
1533
+ }
1534
+ /**
1535
+ * Re-read after the Host reports a change to the reference this card watches.
1536
+ *
1537
+ * A key can be written from somewhere else — the Models page addresses the
1538
+ * same reference — and the settings section does not change when it is, so
1539
+ * without this the badge keeps reporting a state the Host already replaced.
1540
+ * @param ref - the reference the Host reports as changed.
1541
+ */
1542
+ refreshCredential(ref) {
1543
+ if (ref !== this.credential.ref) return;
1544
+ this.readCredential();
1545
+ }
1546
+ /**
1547
+ * Build the face the card's slot registration injects.
1548
+ * @returns the card's snapshot and its form actions.
1549
+ */
1550
+ inject() {
1551
+ return {
1552
+ hooks: { webSearchCard: this.store },
1553
+ ...this.form.actions()
1554
+ };
1555
+ }
1556
+ /**
1557
+ * Write the staged key, then re-read whether the Host now holds one.
1558
+ * @param value - the staged credential literal.
1559
+ * @returns whether the Host reports a configured credential afterwards.
1560
+ */
1561
+ async writeKey(value) {
1562
+ try {
1563
+ await this.credentials.set(refOf(this.scope.getSnapshot()), value);
1564
+ } catch (_credentialWriteFailure) {}
1565
+ await this.readCredential();
1566
+ return this.credential.configured;
1567
+ }
1568
+ };
1569
+ /**
1570
+ * The credential reference the section names, or the provider's default.
1571
+ * @param snapshot - the current scope snapshot.
1572
+ * @returns the reference to address.
1573
+ */
1574
+ function refOf(snapshot) {
1575
+ const declared = snapshot.value?.apiKeyEnv;
1576
+ return declared !== void 0 && declared.length > 0 ? declared : DEFAULT_API_KEY_REF;
1577
+ }
1578
+ //#endregion
1579
+ //#region lib/types/client/locales.js
1580
+ /** Locale bundles for the plugin configuration section and its plugin cards. */
1581
+ /** English copy. */
1582
+ const en = {
1583
+ nav: "Plugins",
1584
+ title: "Plugins",
1585
+ intro: "Configure and inspect the plugins installed in this deployment.",
1586
+ tabs: "Plugin views",
1587
+ configurableTab: "Plugin configuration",
1588
+ empty: "This deployment exposes no plugin settings.",
1589
+ overridden: "Overridden",
1590
+ reset: "Reset to default",
1591
+ readOnly: "This deployment stores settings read-only.",
1592
+ expand: "Show settings",
1593
+ collapse: "Hide settings",
1594
+ save: "Save",
1595
+ saving: "Saving…",
1596
+ discard: "Discard",
1597
+ unsaved: "Unsaved",
1598
+ saveFailed: "The deployment did not accept these values; they were left for you to correct.",
1599
+ invalidNumber: "Enter a number, or leave blank to use the default.",
1600
+ bashTitle: "Shell",
1601
+ bashDescription: "Limits every command the agent runs.",
1602
+ bashTimeoutMs: "Command timeout (ms)",
1603
+ bashTimeoutMsHint: "How long one command may run before it is terminated.",
1604
+ bashMaxOutputBytes: "Output cap per stream (bytes)",
1605
+ bashMaxOutputBytesHint: "Output beyond this spills to a temporary file rather than being lost.",
1606
+ agentLoopTitle: "Agent loop",
1607
+ agentLoopDescription: "How the agent dispatches tool calls.",
1608
+ agentLoopMaxParallel: "Parallel tool calls",
1609
+ agentLoopMaxParallelHint: "Upper bound on parallel-safe calls running at once within one step.",
1610
+ webSearchTitle: "Web search",
1611
+ webSearchDescription: "The DeepSeek search provider.",
1612
+ webSearchApiKey: "API key",
1613
+ webSearchApiKeyHint: "Stored outside the settings file. Leave blank to keep the current key.",
1614
+ webSearchApiKeySet: "A key is configured.",
1615
+ webSearchApiKeyUnset: "No key is configured; search is unavailable until one is.",
1616
+ webSearchBaseUrl: "Endpoint",
1617
+ webSearchBaseUrlHint: "Leave blank to use the provider default.",
1618
+ webSearchMaxUses: "Max searches per request",
1619
+ webSearchMaxUsesHint: "How many times one request may search before it must answer.",
1620
+ subagentModelSelectionTitle: "Subagent",
1621
+ subagentModelSelectionDescription: "Control which models agents may choose for subagents.",
1622
+ subagentModelSelectionToggle: "Allow agents to choose models for subagents",
1623
+ subagentModelSelectionChoose: "When enabled, agents can choose a provider, model, and reasoning effort for each subagent from the authorized models below. Applies only to new sessions.",
1624
+ subagentModelSelectionAllowed: "Models agents may choose",
1625
+ subagentModelSelectionLoading: "Loading models…",
1626
+ subagentModelSelectionLoadFailed: "Models could not be loaded.",
1627
+ subagentModelSelectionRetry: "Retry",
1628
+ subagentModelSelectionPartial: "Some model providers could not be loaded; saved choices remain removable.",
1629
+ subagentModelSelectionUnavailable: "Currently unavailable",
1630
+ subagentModelSelectionUnavailableGroup: "Saved but currently unavailable",
1631
+ subagentModelSelectionEmpty: "No model provider currently advertises a model.",
1632
+ subagentModelSelectionRequired: "Select at least one model before saving.",
1633
+ subagentModelSelectionConflict: "Settings changed elsewhere. Discard your draft and try again.",
1634
+ subagentModelSelectionOff: "Subagents use configured defaults or inherit the parent agent's model. Saved model choices are retained."
1635
+ };
1636
+ /** Simplified Chinese copy. */
1637
+ const zh = {
1638
+ nav: "插件",
1639
+ title: "插件",
1640
+ intro: "配置和查看本部署已安装的插件。",
1641
+ tabs: "插件视图",
1642
+ configurableTab: "插件配置",
1643
+ empty: "本部署没有开放任何插件设置。",
1644
+ overridden: "已覆盖",
1645
+ reset: "恢复默认",
1646
+ readOnly: "本部署的设置为只读。",
1647
+ expand: "展开设置",
1648
+ collapse: "收起设置",
1649
+ save: "保存",
1650
+ saving: "保存中…",
1651
+ discard: "放弃修改",
1652
+ unsaved: "未保存",
1653
+ saveFailed: "本部署没有接受这些值,已保留供你修改。",
1654
+ invalidNumber: "请填数字;留空表示使用默认值。",
1655
+ bashTitle: "终端",
1656
+ bashDescription: "限制 agent 运行的每一条命令。",
1657
+ bashTimeoutMs: "命令超时(毫秒)",
1658
+ bashTimeoutMsHint: "单条命令允许运行多久,超时即终止。",
1659
+ bashMaxOutputBytes: "单流输出上限(字节)",
1660
+ bashMaxOutputBytesHint: "超出部分会转存到临时文件,而不是被丢弃。",
1661
+ agentLoopTitle: "Agent 循环",
1662
+ agentLoopDescription: "Agent 如何派发工具调用。",
1663
+ agentLoopMaxParallel: "并行工具调用数",
1664
+ agentLoopMaxParallelHint: "同一步内最多同时运行多少个可并行的调用。",
1665
+ webSearchTitle: "网页搜索",
1666
+ webSearchDescription: "DeepSeek 搜索提供方。",
1667
+ webSearchApiKey: "API Key",
1668
+ webSearchApiKeyHint: "不写入设置文件。留空表示保持当前密钥。",
1669
+ webSearchApiKeySet: "已配置密钥。",
1670
+ webSearchApiKeyUnset: "未配置密钥;配置之前搜索不可用。",
1671
+ webSearchBaseUrl: "接口地址",
1672
+ webSearchBaseUrlHint: "留空则使用提供方默认地址。",
1673
+ webSearchMaxUses: "单次请求最多搜索次数",
1674
+ webSearchMaxUsesHint: "一次请求在必须作答前最多可以搜索多少次。",
1675
+ subagentModelSelectionTitle: "Subagent",
1676
+ subagentModelSelectionDescription: "控制 Agent 为 Subagent 选择模型的权限。",
1677
+ subagentModelSelectionToggle: "允许 Agent 为 Subagent 选择模型",
1678
+ subagentModelSelectionChoose: "开启后,Agent 可以从下方授权模型中,为每个 Subagent 选择提供方、模型和推理强度。仅影响新会话。",
1679
+ subagentModelSelectionAllowed: "Agent 可选择的模型",
1680
+ subagentModelSelectionLoading: "正在加载模型…",
1681
+ subagentModelSelectionLoadFailed: "无法加载模型。",
1682
+ subagentModelSelectionRetry: "重试",
1683
+ subagentModelSelectionPartial: "部分模型提供方暂时无法加载;已保存的选择仍可移除。",
1684
+ subagentModelSelectionUnavailable: "当前不可用",
1685
+ subagentModelSelectionUnavailableGroup: "已保存但当前不可用",
1686
+ subagentModelSelectionEmpty: "当前没有模型提供方公布模型。",
1687
+ subagentModelSelectionRequired: "保存前请至少选择一个模型。",
1688
+ subagentModelSelectionConflict: "设置已在其他位置更新。请放弃修改后重试。",
1689
+ subagentModelSelectionOff: "关闭后,Subagent 使用配置的默认模型或继承父 Agent 的模型;已选模型会保留。"
1690
+ };
1691
+ //#endregion
1692
+ //#region lib/types/client/index.js
1693
+ /**
1694
+ * Plugins settings surface, browser half — one section whose feature-owned
1695
+ * tabs include configurable Host plugin cards and read-only inventory.
1696
+ *
1697
+ * The section declares `settings.plugins.tab`; its own `configurable` tab then
1698
+ * declares `settings.plugin.item` and renders whatever cards were registered
1699
+ * into it. The cards this package ships are the host-plane sections the
1700
+ * deployment already exposes; each binds its namespace through the client
1701
+ * settings scope, which keeps them unaware of one another and of other tabs.
1702
+ */
1703
+ /** Dictionary namespace owned by this plugin. */
1704
+ const NS = "settings.plugins";
1705
+ /** Required services (cordis fiber inject). */
1706
+ const inject = [
1707
+ "slots",
1708
+ "locale",
1709
+ "connection",
1710
+ "remote",
1711
+ "remote.credentials",
1712
+ "remote.session",
1713
+ "settingsScope"
1714
+ ];
1715
+ /**
1716
+ * Mount the plugin configuration section and the cards this package ships.
1717
+ * @param ctx - the browser plugin context.
1718
+ */
1719
+ function apply(ctx) {
1720
+ const t = ctx.locale.bind(NS);
1721
+ ctx.effect(() => ctx.locale.register(NS, {
1722
+ zh,
1723
+ en
1724
+ }), "ui-settings-plugins: section dictionaries");
1725
+ const bash = new BashCardController(ctx.settingsScope.bind({ namespace: SHELL_NS }));
1726
+ const agentLoop = new AgentLoopCardController(ctx.settingsScope.bind({ namespace: AGENT_LOOP_NS }));
1727
+ const webSearch = new WebSearchCardController(ctx.settingsScope.bind({ namespace: WEB_SEARCH_NS }), ctx.remote.credentials);
1728
+ const subagentModelSelection = new SubagentModelSelectionCardController(ctx.settingsScope.bind({ namespace: SUBAGENT_MODEL_SELECTION_NS }), ctx.remote.session);
1729
+ ctx.effect(() => ctx.remote.$on("credentials/reference-updated", (ref) => {
1730
+ webSearch.refreshCredential(ref);
1731
+ }), "ui-settings-plugins: credential invalidations");
1732
+ ctx.effect(() => ctx.remote.$on("llm/adapters-updated", () => {
1733
+ subagentModelSelection.refreshCatalog();
1734
+ }), "ui-settings-plugins: subagent adapter invalidations");
1735
+ ctx.effect(() => ctx.remote.$on("settings/document-updated", () => {
1736
+ subagentModelSelection.refreshCatalog();
1737
+ }), "ui-settings-plugins: subagent settings invalidations");
1738
+ ctx.effect(() => ctx.on("connection/reset", () => {
1739
+ subagentModelSelection.resetConnection();
1740
+ }), "ui-settings-plugins: subagent connection generation");
1741
+ ctx.effect(() => () => {
1742
+ subagentModelSelection.dispose();
1743
+ }, "ui-settings-plugins: subagent preference");
1744
+ const configurable = new ConfigurablePluginsTabController(ctx.settingsScope.describe(), () => ctx.slots.entries("settings.plugin.item"));
1745
+ ctx.effect(() => () => {
1746
+ configurable.dispose();
1747
+ }, "ui-settings-plugins: tab directory");
1748
+ ctx.effect(() => ctx.slots.subscribe("settings.plugin.item", () => {
1749
+ configurable.refresh();
1750
+ }), "ui-settings-plugins: card ledger");
1751
+ let tabsVersion = -1;
1752
+ let tabsRevision = -1;
1753
+ let tabs = [];
1754
+ const sectionInjected = () => ({ hooks: { tabs: {
1755
+ getSnapshot: () => {
1756
+ const version = ctx.slots.getVersion("settings.plugins.tab");
1757
+ const revision = ctx.locale.getSnapshot().revision;
1758
+ if (version !== tabsVersion || revision !== tabsRevision) {
1759
+ tabsVersion = version;
1760
+ tabsRevision = revision;
1761
+ tabs = ctx.slots.entries("settings.plugins.tab").map((entry) => ({
1762
+ /* v8 ignore next -- list-slot registration requires id */
1763
+ id: entry.options.id ?? "",
1764
+ order: entry.options.order ?? 0,
1765
+ label: (0, _prettier_ai_dsh_client_ui_slots.resolveSlotLabel)(entry.options.label) ?? ""
1766
+ })).sort((a, b) => a.order - b.order);
1767
+ }
1768
+ return tabs;
1769
+ },
1770
+ subscribe: (listener) => {
1771
+ const offLedger = ctx.slots.subscribe("settings.plugins.tab", listener);
1772
+ const offLocale = ctx.locale.subscribe(listener);
1773
+ return () => {
1774
+ offLedger();
1775
+ offLocale();
1776
+ };
1777
+ }
1778
+ } } });
1779
+ ctx.slots.inject("settings.section", () => ctx.slots.register({
1780
+ name: "settings.section",
1781
+ id: "plugins",
1782
+ order: 15,
1783
+ label: () => t("nav"),
1784
+ locale: NS,
1785
+ inject: sectionInjected,
1786
+ children: { "settings.plugins.tab": {
1787
+ kind: "list",
1788
+ scope: "root"
1789
+ } }
1790
+ }, PluginsSettingsSection));
1791
+ ctx.slots.inject("settings.plugins.tab", () => ctx.slots.register({
1792
+ name: "settings.plugins.tab",
1793
+ id: "configurable",
1794
+ order: 0,
1795
+ label: () => t("configurableTab"),
1796
+ locale: NS,
1797
+ inject: () => configurable.inject(),
1798
+ children: { "settings.plugin.item": {
1799
+ kind: "keyed",
1800
+ scope: "root"
1801
+ } }
1802
+ }, ConfigurablePluginsTab));
1803
+ ctx.slots.inject("settings.plugin.item", function* () {
1804
+ yield ctx.slots.register({
1805
+ name: "settings.plugin.item",
1806
+ key: SHELL_NS,
1807
+ locale: NS,
1808
+ inject: () => bash.inject()
1809
+ }, BashCard);
1810
+ yield ctx.slots.register({
1811
+ name: "settings.plugin.item",
1812
+ key: AGENT_LOOP_NS,
1813
+ locale: NS,
1814
+ inject: () => agentLoop.inject()
1815
+ }, AgentLoopCard);
1816
+ yield ctx.slots.register({
1817
+ name: "settings.plugin.item",
1818
+ key: SUBAGENT_MODEL_SELECTION_NS,
1819
+ locale: NS,
1820
+ inject: () => subagentModelSelection.inject()
1821
+ }, SubagentModelSelectionCard);
1822
+ yield ctx.slots.register({
1823
+ name: "settings.plugin.item",
1824
+ key: WEB_SEARCH_NS,
1825
+ locale: NS,
1826
+ inject: () => webSearch.inject()
1827
+ }, WebSearchCard);
1828
+ });
1829
+ }
1830
+ //#endregion
1831
+ exports.apply = apply;
1832
+ exports.inject = inject;
1833
+ return module.exports;
1834
+ }
1835
+ });
1836
+
1837
+ //# sourceMappingURL=client.js.map