@monotykamary/dsh-client-ui-settings-plugins 0.1.0-rc.5
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/LICENSE +21 -0
- package/README.i18n.yaml +6 -0
- package/README.md +40 -0
- package/README.zh.md +40 -0
- package/lib/client.js +1241 -0
- package/lib/index.js +13 -0
- package/lib/invariant.js +24 -0
- package/lib/types/client/AgentLoopCard.d.ts +12 -0
- package/lib/types/client/BashCard.d.ts +12 -0
- package/lib/types/client/ConfigurablePluginsTab.d.ts +12 -0
- package/lib/types/client/PluginCard.d.ts +42 -0
- package/lib/types/client/PluginsSettingsSection.d.ts +27 -0
- package/lib/types/client/WebSearchCard.d.ts +16 -0
- package/lib/types/client/agent-loop-card-controller.d.ts +42 -0
- package/lib/types/client/bash-card-controller.d.ts +44 -0
- package/lib/types/client/card-form.d.ts +171 -0
- package/lib/types/client/fields.d.ts +61 -0
- package/lib/types/client/index.d.ts +28 -0
- package/lib/types/client/locales.d.ts +8 -0
- package/lib/types/client/slot-contract.d.ts +28 -0
- package/lib/types/client/web-search-card-controller.d.ts +91 -0
- package/lib/types/index.d.ts +11 -0
- package/lib/types/invariant.d.ts +16 -0
- package/package.json +86 -0
package/lib/client.js
ADDED
|
@@ -0,0 +1,1241 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "@monotykamary/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 _monotykamary_dsh_client_ui_slots = require("@monotykamary/dsh-client-ui-slots");
|
|
8
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
9
|
+
let react = require("react");
|
|
10
|
+
let _monotykamary_dsh_client_ui_primitives = require("@monotykamary/dsh-client-ui-primitives");
|
|
11
|
+
let _monotykamary_dsh_client_runtime_client = require("@monotykamary/dsh-client-runtime/client");
|
|
12
|
+
//#region \0dsh-css:/Users/monotykamary/VCS/working-remote/open-source/deepseek-harness/packages/client/ui-settings-plugins/src/client/fields.module.css.mjs
|
|
13
|
+
const css$2 = ".tp98LG_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.tp98LG_field+.tp98LG_field{border-top:1px solid var(--dsw-alias-border-l2)}.tp98LG_head{align-items:center;gap:8px;display:flex}.tp98LG_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.tp98LG_badges{align-items:center;gap:8px;display:inline-flex}.tp98LG_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}.tp98LG_badgeMuted{white-space:nowrap;color:var(--dsw-alias-label-tertiary);border-radius:999px;padding:1px 8px;font-size:11px;line-height:17px}.tp98LG_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}.tp98LG_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.tp98LG_reset:disabled{cursor:default}.tp98LG_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}.tp98LG_input:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.tp98LG_input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.tp98LG_inputInvalid{border-color:var(--dsw-alias-label-error);}.tp98LG_invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.tp98LG_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}";
|
|
14
|
+
const tagId$2 = "@monotykamary/dsh-client-ui-settings-plugins/fields.module.css";
|
|
15
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
|
|
16
|
+
const tag = document.createElement("style");
|
|
17
|
+
tag.dataset.plugin = "@monotykamary/dsh-client-ui-settings-plugins";
|
|
18
|
+
tag.dataset.pluginCss = tagId$2;
|
|
19
|
+
tag.textContent = css$2;
|
|
20
|
+
document.head.appendChild(tag);
|
|
21
|
+
}
|
|
22
|
+
var fields_module_css_default = {
|
|
23
|
+
"reset": "tp98LG_reset",
|
|
24
|
+
"field": "tp98LG_field",
|
|
25
|
+
"head": "tp98LG_head",
|
|
26
|
+
"badgeMuted": "tp98LG_badgeMuted",
|
|
27
|
+
"badges": "tp98LG_badges",
|
|
28
|
+
"inputInvalid": "tp98LG_inputInvalid",
|
|
29
|
+
"hint": "tp98LG_hint",
|
|
30
|
+
"label": "tp98LG_label",
|
|
31
|
+
"badge": "tp98LG_badge",
|
|
32
|
+
"input": "tp98LG_input",
|
|
33
|
+
"invalid": "tp98LG_invalid"
|
|
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 ../../../../../../../Library/pnpm/store/v11/links/@/clsx/2.1.1/2b14a624e52246853e8086215d43767b3f05a70db70d9393672d28f32b6c54a6/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:/Users/monotykamary/VCS/working-remote/open-source/deepseek-harness/packages/client/ui-settings-plugins/src/client/PluginCard.module.css.mjs
|
|
155
|
+
const css$1 = ".fgHXXa_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}.fgHXXa_card:hover{border-color:var(--dsw-alias-label-dimmed)}.fgHXXa_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.fgHXXa_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}.fgHXXa_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.fgHXXa_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.fgHXXa_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.fgHXXa_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.fgHXXa_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.fgHXXa_chevronOpen{transform:rotate(180deg)}.fgHXXa_body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.fgHXXa_readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}.fgHXXa_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}.fgHXXa_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}.fgHXXa_failed{min-width:0;color:var(--dsw-alias-label-error);flex:1;margin:0;font-size:12px;line-height:1.5}.fgHXXa_discard,.fgHXXa_save{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.fgHXXa_discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}.fgHXXa_discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.fgHXXa_save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}.fgHXXa_discard:disabled,.fgHXXa_save:disabled{opacity:.4;cursor:default}.fgHXXa_discard:focus-visible,.fgHXXa_save:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}";
|
|
156
|
+
const tagId$1 = "@monotykamary/dsh-client-ui-settings-plugins/PluginCard.module.css";
|
|
157
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
158
|
+
const tag = document.createElement("style");
|
|
159
|
+
tag.dataset.plugin = "@monotykamary/dsh-client-ui-settings-plugins";
|
|
160
|
+
tag.dataset.pluginCss = tagId$1;
|
|
161
|
+
tag.textContent = css$1;
|
|
162
|
+
document.head.appendChild(tag);
|
|
163
|
+
}
|
|
164
|
+
var PluginCard_module_css_default = {
|
|
165
|
+
"pending": "fgHXXa_pending",
|
|
166
|
+
"chevron": "fgHXXa_chevron",
|
|
167
|
+
"cardOpen": "fgHXXa_cardOpen",
|
|
168
|
+
"chevronOpen": "fgHXXa_chevronOpen",
|
|
169
|
+
"card": "fgHXXa_card",
|
|
170
|
+
"body": "fgHXXa_body",
|
|
171
|
+
"failed": "fgHXXa_failed",
|
|
172
|
+
"discard": "fgHXXa_discard",
|
|
173
|
+
"readOnly": "fgHXXa_readOnly",
|
|
174
|
+
"footer": "fgHXXa_footer",
|
|
175
|
+
"save": "fgHXXa_save",
|
|
176
|
+
"headText": "fgHXXa_headText",
|
|
177
|
+
"description": "fgHXXa_description",
|
|
178
|
+
"name": "fgHXXa_name",
|
|
179
|
+
"header": "fgHXXa_header"
|
|
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 { state } = props;
|
|
206
|
+
if (!state.available) return null;
|
|
207
|
+
const title = props.t(props.titleKey);
|
|
208
|
+
const blocked = !state.dirty || state.invalid || state.saving;
|
|
209
|
+
return (0, react_jsx_runtime.jsxs)("li", {
|
|
210
|
+
className: clsx(PluginCard_module_css_default.card, open && PluginCard_module_css_default.cardOpen),
|
|
211
|
+
children: [(0, react_jsx_runtime.jsxs)("button", {
|
|
212
|
+
type: "button",
|
|
213
|
+
className: PluginCard_module_css_default.header,
|
|
214
|
+
"aria-expanded": open,
|
|
215
|
+
"aria-label": `${props.t(open ? "collapse" : "expand")}: ${title}`,
|
|
216
|
+
onClick: () => {
|
|
217
|
+
setOpen(!open);
|
|
218
|
+
},
|
|
219
|
+
children: [
|
|
220
|
+
(0, react_jsx_runtime.jsxs)("span", {
|
|
221
|
+
className: PluginCard_module_css_default.headText,
|
|
222
|
+
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
223
|
+
className: PluginCard_module_css_default.name,
|
|
224
|
+
children: title
|
|
225
|
+
}), (0, react_jsx_runtime.jsx)("span", {
|
|
226
|
+
className: PluginCard_module_css_default.description,
|
|
227
|
+
children: props.t(props.descriptionKey)
|
|
228
|
+
})]
|
|
229
|
+
}),
|
|
230
|
+
state.dirty ? (0, react_jsx_runtime.jsx)("span", {
|
|
231
|
+
className: PluginCard_module_css_default.pending,
|
|
232
|
+
children: props.t("unsaved")
|
|
233
|
+
}) : null,
|
|
234
|
+
(0, react_jsx_runtime.jsx)(_monotykamary_dsh_client_ui_primitives.IconChevronDownOutline14, { className: clsx(PluginCard_module_css_default.chevron, open && PluginCard_module_css_default.chevronOpen) })
|
|
235
|
+
]
|
|
236
|
+
}), open ? (0, react_jsx_runtime.jsxs)("div", {
|
|
237
|
+
className: PluginCard_module_css_default.body,
|
|
238
|
+
children: [
|
|
239
|
+
!state.writable ? (0, react_jsx_runtime.jsx)("p", {
|
|
240
|
+
className: PluginCard_module_css_default.readOnly,
|
|
241
|
+
role: "status",
|
|
242
|
+
children: props.t("readOnly")
|
|
243
|
+
}) : null,
|
|
244
|
+
props.children,
|
|
245
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
246
|
+
className: PluginCard_module_css_default.footer,
|
|
247
|
+
children: [
|
|
248
|
+
state.failed ? (0, react_jsx_runtime.jsx)("p", {
|
|
249
|
+
className: PluginCard_module_css_default.failed,
|
|
250
|
+
role: "status",
|
|
251
|
+
children: props.t("saveFailed")
|
|
252
|
+
}) : null,
|
|
253
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
254
|
+
type: "button",
|
|
255
|
+
className: PluginCard_module_css_default.discard,
|
|
256
|
+
disabled: !state.dirty || state.saving,
|
|
257
|
+
onClick: props.onDiscard,
|
|
258
|
+
children: props.t("discard")
|
|
259
|
+
}),
|
|
260
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
261
|
+
type: "button",
|
|
262
|
+
className: PluginCard_module_css_default.save,
|
|
263
|
+
disabled: blocked,
|
|
264
|
+
onClick: props.onSave,
|
|
265
|
+
children: props.t(state.saving ? "saving" : "save")
|
|
266
|
+
})
|
|
267
|
+
]
|
|
268
|
+
})
|
|
269
|
+
]
|
|
270
|
+
}) : null]
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
//#endregion
|
|
274
|
+
//#region lib/types/client/AgentLoopCard.js
|
|
275
|
+
/**
|
|
276
|
+
* Render the agent-loop card.
|
|
277
|
+
* @param props - locale copy, the card snapshot, and its form actions.
|
|
278
|
+
* @returns the card.
|
|
279
|
+
*/
|
|
280
|
+
function AgentLoopCard(props) {
|
|
281
|
+
const { t } = props;
|
|
282
|
+
const state = props.useAgentLoopCard((snapshot) => snapshot);
|
|
283
|
+
return (0, react_jsx_runtime.jsx)(PluginCard, {
|
|
284
|
+
t,
|
|
285
|
+
titleKey: "agentLoopTitle",
|
|
286
|
+
descriptionKey: "agentLoopDescription",
|
|
287
|
+
state,
|
|
288
|
+
onSave: props.save,
|
|
289
|
+
onDiscard: props.discard,
|
|
290
|
+
children: (0, react_jsx_runtime.jsx)(ValueField, {
|
|
291
|
+
id: "plugin-config-agent-loop-parallel",
|
|
292
|
+
label: t("agentLoopMaxParallel"),
|
|
293
|
+
hint: t("agentLoopMaxParallelHint"),
|
|
294
|
+
overriddenLabel: t("overridden"),
|
|
295
|
+
resetLabel: t("reset"),
|
|
296
|
+
invalidLabel: t("invalidNumber"),
|
|
297
|
+
numeric: true,
|
|
298
|
+
disabled: !state.writable,
|
|
299
|
+
...state.maxParallelToolCalls,
|
|
300
|
+
onEdit: (text) => {
|
|
301
|
+
props.edit("maxParallelToolCalls", text);
|
|
302
|
+
},
|
|
303
|
+
onReset: () => {
|
|
304
|
+
props.resetField("maxParallelToolCalls");
|
|
305
|
+
}
|
|
306
|
+
})
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
//#endregion
|
|
310
|
+
//#region lib/types/client/BashCard.js
|
|
311
|
+
/**
|
|
312
|
+
* Render the shell card.
|
|
313
|
+
* @param props - locale copy, the card snapshot, and its form actions.
|
|
314
|
+
* @returns the card.
|
|
315
|
+
*/
|
|
316
|
+
function BashCard(props) {
|
|
317
|
+
const { t } = props;
|
|
318
|
+
const state = props.useBashCard((snapshot) => snapshot);
|
|
319
|
+
const disabled = !state.writable;
|
|
320
|
+
return (0, react_jsx_runtime.jsxs)(PluginCard, {
|
|
321
|
+
t,
|
|
322
|
+
titleKey: "bashTitle",
|
|
323
|
+
descriptionKey: "bashDescription",
|
|
324
|
+
state,
|
|
325
|
+
onSave: props.save,
|
|
326
|
+
onDiscard: props.discard,
|
|
327
|
+
children: [(0, react_jsx_runtime.jsx)(ValueField, {
|
|
328
|
+
id: "plugin-config-bash-timeout",
|
|
329
|
+
label: t("bashTimeoutMs"),
|
|
330
|
+
hint: t("bashTimeoutMsHint"),
|
|
331
|
+
overriddenLabel: t("overridden"),
|
|
332
|
+
resetLabel: t("reset"),
|
|
333
|
+
invalidLabel: t("invalidNumber"),
|
|
334
|
+
numeric: true,
|
|
335
|
+
disabled,
|
|
336
|
+
...state.timeoutMs,
|
|
337
|
+
onEdit: (text) => {
|
|
338
|
+
props.edit("timeoutMs", text);
|
|
339
|
+
},
|
|
340
|
+
onReset: () => {
|
|
341
|
+
props.resetField("timeoutMs");
|
|
342
|
+
}
|
|
343
|
+
}), (0, react_jsx_runtime.jsx)(ValueField, {
|
|
344
|
+
id: "plugin-config-bash-output",
|
|
345
|
+
label: t("bashMaxOutputBytes"),
|
|
346
|
+
hint: t("bashMaxOutputBytesHint"),
|
|
347
|
+
overriddenLabel: t("overridden"),
|
|
348
|
+
resetLabel: t("reset"),
|
|
349
|
+
invalidLabel: t("invalidNumber"),
|
|
350
|
+
numeric: true,
|
|
351
|
+
disabled,
|
|
352
|
+
...state.maxOutputBytes,
|
|
353
|
+
onEdit: (text) => {
|
|
354
|
+
props.edit("maxOutputBytes", text);
|
|
355
|
+
},
|
|
356
|
+
onReset: () => {
|
|
357
|
+
props.resetField("maxOutputBytes");
|
|
358
|
+
}
|
|
359
|
+
})]
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
//#endregion
|
|
363
|
+
//#region \0dsh-css:/Users/monotykamary/VCS/working-remote/open-source/deepseek-harness/packages/client/ui-settings-plugins/src/client/PluginsSettingsSection.module.css.mjs
|
|
364
|
+
const css = ".NYK7ya_section{max-width:760px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:12px;display:flex}.NYK7ya_heading{margin:0;font-size:18px;font-weight:600}.NYK7ya_intro{color:var(--dsw-alias-label-tertiary);margin:0;font-size:13px}.NYK7ya_tabs{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:flex-end;gap:22px;margin-top:2px;display:flex}.NYK7ya_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}.NYK7ya_tab:hover,.NYK7ya_tab[data-active=true]{color:var(--dsw-alias-label-primary)}.NYK7ya_tab[data-active=true]:after,.NYK7ya_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}.NYK7ya_tab:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px;color:var(--dsw-alias-label-primary);border-radius:2px}.NYK7ya_panel{min-width:0;padding-top:2px}.NYK7ya_cards{flex-direction:column;gap:10px;margin:0;padding:0;list-style:none;display:flex}.NYK7ya_empty{color:var(--dsw-alias-label-tertiary);margin:0;font-size:13px}";
|
|
365
|
+
const tagId = "@monotykamary/dsh-client-ui-settings-plugins/PluginsSettingsSection.module.css";
|
|
366
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
367
|
+
const tag = document.createElement("style");
|
|
368
|
+
tag.dataset.plugin = "@monotykamary/dsh-client-ui-settings-plugins";
|
|
369
|
+
tag.dataset.pluginCss = tagId;
|
|
370
|
+
tag.textContent = css;
|
|
371
|
+
document.head.appendChild(tag);
|
|
372
|
+
}
|
|
373
|
+
var PluginsSettingsSection_module_css_default = {
|
|
374
|
+
"panel": "NYK7ya_panel",
|
|
375
|
+
"heading": "NYK7ya_heading",
|
|
376
|
+
"section": "NYK7ya_section",
|
|
377
|
+
"tab": "NYK7ya_tab",
|
|
378
|
+
"cards": "NYK7ya_cards",
|
|
379
|
+
"intro": "NYK7ya_intro",
|
|
380
|
+
"tabs": "NYK7ya_tabs",
|
|
381
|
+
"empty": "NYK7ya_empty"
|
|
382
|
+
};
|
|
383
|
+
//#endregion
|
|
384
|
+
//#region lib/types/client/ConfigurablePluginsTab.js
|
|
385
|
+
/** Render cards registered by plugins that expose editable settings. */
|
|
386
|
+
function ConfigurablePluginsTab({ t, renderSlot, cardCount }) {
|
|
387
|
+
return cardCount === 0 ? (0, react_jsx_runtime.jsx)("p", {
|
|
388
|
+
className: PluginsSettingsSection_module_css_default.empty,
|
|
389
|
+
children: t("empty")
|
|
390
|
+
}) : (0, react_jsx_runtime.jsx)("ul", {
|
|
391
|
+
className: PluginsSettingsSection_module_css_default.cards,
|
|
392
|
+
children: renderSlot("settings.plugin.item", {})
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
//#endregion
|
|
396
|
+
//#region lib/types/client/PluginsSettingsSection.js
|
|
397
|
+
/** Plugins settings section: localized tabs around feature-owned pages. */
|
|
398
|
+
/** Render one Plugins page whose contents arrive from feature-owned tabs. */
|
|
399
|
+
function PluginsSettingsSection({ t, renderSlot, useTabs }) {
|
|
400
|
+
const tabsId = (0, react.useId)();
|
|
401
|
+
const tabRefs = (0, react.useRef)([]);
|
|
402
|
+
const rows = useTabs((value) => value);
|
|
403
|
+
const [activeId, setActiveId] = (0, react.useState)();
|
|
404
|
+
const [visitedIds, setVisitedIds] = (0, react.useState)(() => /* @__PURE__ */ new Set());
|
|
405
|
+
const active = rows.find((row) => row.id === activeId)?.id ?? rows[0]?.id;
|
|
406
|
+
(0, react.useEffect)(() => {
|
|
407
|
+
if (active === void 0) return;
|
|
408
|
+
setVisitedIds((previous) => {
|
|
409
|
+
if (previous.has(active)) return previous;
|
|
410
|
+
return new Set([...previous, active]);
|
|
411
|
+
});
|
|
412
|
+
}, [active]);
|
|
413
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
414
|
+
className: PluginsSettingsSection_module_css_default.section,
|
|
415
|
+
children: [
|
|
416
|
+
(0, react_jsx_runtime.jsx)("h2", {
|
|
417
|
+
className: PluginsSettingsSection_module_css_default.heading,
|
|
418
|
+
children: t("title")
|
|
419
|
+
}),
|
|
420
|
+
(0, react_jsx_runtime.jsx)("p", {
|
|
421
|
+
className: PluginsSettingsSection_module_css_default.intro,
|
|
422
|
+
children: t("intro")
|
|
423
|
+
}),
|
|
424
|
+
rows.length === 0 ? (0, react_jsx_runtime.jsx)("p", {
|
|
425
|
+
className: PluginsSettingsSection_module_css_default.empty,
|
|
426
|
+
children: t("empty")
|
|
427
|
+
}) : (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("div", {
|
|
428
|
+
className: PluginsSettingsSection_module_css_default.tabs,
|
|
429
|
+
role: "tablist",
|
|
430
|
+
"aria-label": t("tabs"),
|
|
431
|
+
children: rows.map((row, index) => {
|
|
432
|
+
const selected = row.id === active;
|
|
433
|
+
return (0, react_jsx_runtime.jsx)("button", {
|
|
434
|
+
ref: (element) => {
|
|
435
|
+
tabRefs.current[index] = element;
|
|
436
|
+
},
|
|
437
|
+
id: `${tabsId}-tab-${row.id}`,
|
|
438
|
+
type: "button",
|
|
439
|
+
role: "tab",
|
|
440
|
+
className: PluginsSettingsSection_module_css_default.tab,
|
|
441
|
+
"aria-selected": selected,
|
|
442
|
+
"aria-controls": `${tabsId}-panel-${row.id}`,
|
|
443
|
+
"data-active": selected ? "true" : void 0,
|
|
444
|
+
tabIndex: selected ? 0 : -1,
|
|
445
|
+
onClick: () => {
|
|
446
|
+
setActiveId(row.id);
|
|
447
|
+
},
|
|
448
|
+
onKeyDown: (event) => {
|
|
449
|
+
let nextIndex;
|
|
450
|
+
switch (event.key) {
|
|
451
|
+
case "ArrowRight":
|
|
452
|
+
nextIndex = (index + 1) % rows.length;
|
|
453
|
+
break;
|
|
454
|
+
case "ArrowLeft":
|
|
455
|
+
nextIndex = (index - 1 + rows.length) % rows.length;
|
|
456
|
+
break;
|
|
457
|
+
case "Home":
|
|
458
|
+
nextIndex = 0;
|
|
459
|
+
break;
|
|
460
|
+
case "End":
|
|
461
|
+
nextIndex = rows.length - 1;
|
|
462
|
+
break;
|
|
463
|
+
default: return;
|
|
464
|
+
}
|
|
465
|
+
event.preventDefault();
|
|
466
|
+
const nextRow = rows[nextIndex];
|
|
467
|
+
const nextTab = tabRefs.current[nextIndex];
|
|
468
|
+
setActiveId(nextRow.id);
|
|
469
|
+
nextTab.focus();
|
|
470
|
+
},
|
|
471
|
+
children: row.label
|
|
472
|
+
}, row.id);
|
|
473
|
+
})
|
|
474
|
+
}), rows.filter((row) => row.id === active || visitedIds.has(row.id)).map((row) => {
|
|
475
|
+
const selected = row.id === active;
|
|
476
|
+
return (0, react_jsx_runtime.jsx)("div", {
|
|
477
|
+
id: `${tabsId}-panel-${row.id}`,
|
|
478
|
+
className: PluginsSettingsSection_module_css_default.panel,
|
|
479
|
+
role: "tabpanel",
|
|
480
|
+
"aria-labelledby": `${tabsId}-tab-${row.id}`,
|
|
481
|
+
hidden: !selected,
|
|
482
|
+
children: renderSlot("settings.plugins.tab", {}, { only: row.id })
|
|
483
|
+
}, row.id);
|
|
484
|
+
})] })
|
|
485
|
+
]
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
//#endregion
|
|
489
|
+
//#region lib/types/client/WebSearchCard.js
|
|
490
|
+
/**
|
|
491
|
+
* Render the web-search card.
|
|
492
|
+
* @param props - locale copy, the card snapshot, and its form actions.
|
|
493
|
+
* @returns the card.
|
|
494
|
+
*/
|
|
495
|
+
function WebSearchCard(props) {
|
|
496
|
+
const { t } = props;
|
|
497
|
+
const state = props.useWebSearchCard((snapshot) => snapshot);
|
|
498
|
+
const disabled = !state.writable;
|
|
499
|
+
return (0, react_jsx_runtime.jsxs)(PluginCard, {
|
|
500
|
+
t,
|
|
501
|
+
titleKey: "webSearchTitle",
|
|
502
|
+
descriptionKey: "webSearchDescription",
|
|
503
|
+
state,
|
|
504
|
+
onSave: props.save,
|
|
505
|
+
onDiscard: props.discard,
|
|
506
|
+
children: [
|
|
507
|
+
(0, react_jsx_runtime.jsx)(SecretField, {
|
|
508
|
+
id: "plugin-config-web-search-key",
|
|
509
|
+
label: t("webSearchApiKey"),
|
|
510
|
+
hint: t("webSearchApiKeyHint"),
|
|
511
|
+
disabled: !state.apiKeyWritable,
|
|
512
|
+
text: state.apiKey.text,
|
|
513
|
+
configured: state.apiKeyConfigured,
|
|
514
|
+
stateLabel: state.apiKeyConfigured ? t("webSearchApiKeySet") : t("webSearchApiKeyUnset"),
|
|
515
|
+
onEdit: (text) => {
|
|
516
|
+
props.edit("apiKey", text);
|
|
517
|
+
}
|
|
518
|
+
}),
|
|
519
|
+
(0, react_jsx_runtime.jsx)(ValueField, {
|
|
520
|
+
id: "plugin-config-web-search-endpoint",
|
|
521
|
+
label: t("webSearchBaseUrl"),
|
|
522
|
+
hint: t("webSearchBaseUrlHint"),
|
|
523
|
+
overriddenLabel: t("overridden"),
|
|
524
|
+
resetLabel: t("reset"),
|
|
525
|
+
invalidLabel: t("invalidNumber"),
|
|
526
|
+
disabled,
|
|
527
|
+
...state.baseURL,
|
|
528
|
+
onEdit: (text) => {
|
|
529
|
+
props.edit("baseURL", text);
|
|
530
|
+
},
|
|
531
|
+
onReset: () => {
|
|
532
|
+
props.resetField("baseURL");
|
|
533
|
+
}
|
|
534
|
+
}),
|
|
535
|
+
(0, react_jsx_runtime.jsx)(ValueField, {
|
|
536
|
+
id: "plugin-config-web-search-max-uses",
|
|
537
|
+
label: t("webSearchMaxUses"),
|
|
538
|
+
hint: t("webSearchMaxUsesHint"),
|
|
539
|
+
overriddenLabel: t("overridden"),
|
|
540
|
+
resetLabel: t("reset"),
|
|
541
|
+
invalidLabel: t("invalidNumber"),
|
|
542
|
+
numeric: true,
|
|
543
|
+
disabled,
|
|
544
|
+
...state.maxUses,
|
|
545
|
+
onEdit: (text) => {
|
|
546
|
+
props.edit("maxUses", text);
|
|
547
|
+
},
|
|
548
|
+
onReset: () => {
|
|
549
|
+
props.resetField("maxUses");
|
|
550
|
+
}
|
|
551
|
+
})
|
|
552
|
+
]
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
//#endregion
|
|
556
|
+
//#region lib/types/client/card-form.js
|
|
557
|
+
/**
|
|
558
|
+
* Shared form model behind every plugin card.
|
|
559
|
+
*
|
|
560
|
+
* A card stages what the user types and writes it only when they save. Each
|
|
561
|
+
* settings write is a durable, revision-fenced document mutation, so a control
|
|
562
|
+
* that committed as it settled turned one edit into a write the user never
|
|
563
|
+
* asked for and could not preview; staged text makes what is on screen exactly
|
|
564
|
+
* what a save would store.
|
|
565
|
+
*
|
|
566
|
+
* A field shows its effective value — the user layer over the composition
|
|
567
|
+
* layer over the schema default — and whether the user layer carries it. That
|
|
568
|
+
* presence, not a value comparison, is what marks a field overridden: an
|
|
569
|
+
* override equal to the composition default is still an override.
|
|
570
|
+
*/
|
|
571
|
+
/**
|
|
572
|
+
* A whole-number field. An empty draft clears the field; any other draft that
|
|
573
|
+
* is not a finite number blocks the save.
|
|
574
|
+
* @param field - field name inside the namespace section.
|
|
575
|
+
* @returns the field's conversion spec.
|
|
576
|
+
*/
|
|
577
|
+
function numberField(field) {
|
|
578
|
+
return {
|
|
579
|
+
field,
|
|
580
|
+
format: (value) => typeof value === "number" ? String(value) : "",
|
|
581
|
+
parse: (text) => {
|
|
582
|
+
const trimmed = text.trim();
|
|
583
|
+
if (trimmed === "") return { kind: "clear" };
|
|
584
|
+
const parsed = Number(trimmed);
|
|
585
|
+
return Number.isFinite(parsed) ? {
|
|
586
|
+
kind: "set",
|
|
587
|
+
value: parsed
|
|
588
|
+
} : void 0;
|
|
589
|
+
}
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* A free-text field. An empty draft clears the field, so emptying the control
|
|
594
|
+
* and saving is the same gesture as resetting it.
|
|
595
|
+
* @param field - field name inside the namespace section.
|
|
596
|
+
* @returns the field's conversion spec.
|
|
597
|
+
*/
|
|
598
|
+
function textField(field) {
|
|
599
|
+
return {
|
|
600
|
+
field,
|
|
601
|
+
format: (value) => typeof value === "string" ? value : "",
|
|
602
|
+
parse: (text) => {
|
|
603
|
+
const trimmed = text.trim();
|
|
604
|
+
return trimmed === "" ? { kind: "clear" } : {
|
|
605
|
+
kind: "set",
|
|
606
|
+
value: trimmed
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Stages one card's edits over one settings namespace and writes them on save.
|
|
613
|
+
*
|
|
614
|
+
* The form publishes through a snapshot store because slot components read
|
|
615
|
+
* through a snapshot selector, while both the scope and the local drafts
|
|
616
|
+
* change underneath; every projection is rebuilt from the two together.
|
|
617
|
+
*/
|
|
618
|
+
var CardForm = class {
|
|
619
|
+
scope;
|
|
620
|
+
specs;
|
|
621
|
+
secretSpecs;
|
|
622
|
+
staged = /* @__PURE__ */ new Map();
|
|
623
|
+
listeners = /* @__PURE__ */ new Set();
|
|
624
|
+
saving = false;
|
|
625
|
+
failed = false;
|
|
626
|
+
/**
|
|
627
|
+
* @param scope - the bound settings scope for this card's namespace.
|
|
628
|
+
* @param specs - the section fields this card edits.
|
|
629
|
+
* @param secrets - the card's write-only controls, written outside the section.
|
|
630
|
+
*/
|
|
631
|
+
constructor(scope, specs, secrets = []) {
|
|
632
|
+
this.scope = scope;
|
|
633
|
+
this.specs = new Map(specs.map((spec) => [spec.field, spec]));
|
|
634
|
+
this.secretSpecs = new Map(secrets.map((spec) => [spec.field, spec]));
|
|
635
|
+
scope.subscribe(() => {
|
|
636
|
+
this.publish();
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Publish a projection of this form, rebuilt whenever the scope or a draft changes.
|
|
641
|
+
* @param project - build the card's state from the form's current reads.
|
|
642
|
+
* @returns the store the card's component reads through its bound selector.
|
|
643
|
+
*/
|
|
644
|
+
bind(project) {
|
|
645
|
+
const store = (0, _monotykamary_dsh_client_runtime_client.createSnapshotStore)(project());
|
|
646
|
+
this.listeners.add(() => {
|
|
647
|
+
store.set(project());
|
|
648
|
+
});
|
|
649
|
+
return store;
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Read the card-level state: what the Host serves, and what a save would do.
|
|
653
|
+
* @returns the form state every card shares.
|
|
654
|
+
*/
|
|
655
|
+
shell() {
|
|
656
|
+
const snapshot = this.scope.getSnapshot();
|
|
657
|
+
const plan = this.plan();
|
|
658
|
+
return {
|
|
659
|
+
available: snapshot.status === "ready",
|
|
660
|
+
writable: snapshot.writable,
|
|
661
|
+
dirty: plan.length > 0,
|
|
662
|
+
invalid: plan.some((item) => item.run === void 0),
|
|
663
|
+
saving: this.saving,
|
|
664
|
+
failed: this.failed
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Read one control's state.
|
|
669
|
+
* @param field - field name of a section field or of a write-only control.
|
|
670
|
+
* @returns the draft text, whether a save would leave an override, and whether it is invalid.
|
|
671
|
+
*/
|
|
672
|
+
field(field) {
|
|
673
|
+
const staged = this.staged.get(field);
|
|
674
|
+
if (this.secretSpecs.has(field)) return {
|
|
675
|
+
text: staged?.text ?? "",
|
|
676
|
+
overridden: false,
|
|
677
|
+
invalid: false
|
|
678
|
+
};
|
|
679
|
+
const spec = this.spec(field);
|
|
680
|
+
if (staged === void 0) return {
|
|
681
|
+
text: spec.format(this.sectionValue(field)),
|
|
682
|
+
overridden: this.stored(field),
|
|
683
|
+
invalid: false
|
|
684
|
+
};
|
|
685
|
+
const write = staged.clear ? { kind: "clear" } : spec.parse(staged.text);
|
|
686
|
+
return {
|
|
687
|
+
text: staged.text,
|
|
688
|
+
overridden: write?.kind === "set",
|
|
689
|
+
invalid: write === void 0
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Build the edit, reset, save, and discard actions bound to this form.
|
|
694
|
+
* @returns the actions a card's slot entry injects.
|
|
695
|
+
*/
|
|
696
|
+
actions() {
|
|
697
|
+
return {
|
|
698
|
+
edit: (field, text) => {
|
|
699
|
+
this.stage(field, {
|
|
700
|
+
text,
|
|
701
|
+
clear: false
|
|
702
|
+
});
|
|
703
|
+
},
|
|
704
|
+
resetField: (field) => {
|
|
705
|
+
this.stage(field, {
|
|
706
|
+
text: this.spec(field).format(this.baseValue(field)),
|
|
707
|
+
clear: true
|
|
708
|
+
});
|
|
709
|
+
},
|
|
710
|
+
save: () => {
|
|
711
|
+
this.save();
|
|
712
|
+
},
|
|
713
|
+
discard: () => {
|
|
714
|
+
if (this.staged.size === 0 && !this.failed) return;
|
|
715
|
+
this.staged.clear();
|
|
716
|
+
this.failed = false;
|
|
717
|
+
this.publish();
|
|
718
|
+
}
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
/**
|
|
722
|
+
* Write every staged edit, then re-seed from what the Host accepted.
|
|
723
|
+
*
|
|
724
|
+
* The Host is the only authority on whether a value was accepted — its
|
|
725
|
+
* validators own the constraints no schema can express — so the outcome is
|
|
726
|
+
* read back from the section rather than predicted here. A save that did not
|
|
727
|
+
* land keeps its drafts, so the user can correct them instead of retyping.
|
|
728
|
+
* @returns settlement after every write and the read-back.
|
|
729
|
+
*/
|
|
730
|
+
async save() {
|
|
731
|
+
const plan = this.plan();
|
|
732
|
+
const writes = plan.flatMap((item) => item.run === void 0 ? [] : [item.run]);
|
|
733
|
+
if (plan.length === 0 || this.saving || writes.length !== plan.length) return;
|
|
734
|
+
this.saving = true;
|
|
735
|
+
this.failed = false;
|
|
736
|
+
this.publish();
|
|
737
|
+
let landed = true;
|
|
738
|
+
for (const write of writes) landed = await write() && landed;
|
|
739
|
+
if (landed) this.staged.clear();
|
|
740
|
+
this.saving = false;
|
|
741
|
+
this.failed = !landed;
|
|
742
|
+
this.publish();
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Every staged edit a save would write. An entry whose draft is not a value
|
|
746
|
+
* its field accepts carries no write: the form is still dirty, and the save
|
|
747
|
+
* refuses rather than dropping the edit.
|
|
748
|
+
* @returns the planned writes, in the order the fields were staged.
|
|
749
|
+
*/
|
|
750
|
+
plan() {
|
|
751
|
+
const plan = [];
|
|
752
|
+
for (const [field, staged] of this.staged) {
|
|
753
|
+
const secret = this.secretSpecs.get(field);
|
|
754
|
+
if (secret !== void 0) {
|
|
755
|
+
const value = staged.text.trim();
|
|
756
|
+
if (value !== "") plan.push({
|
|
757
|
+
field,
|
|
758
|
+
run: () => secret.write(value)
|
|
759
|
+
});
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
const spec = this.spec(field);
|
|
763
|
+
if (staged.clear) {
|
|
764
|
+
if (this.stored(field)) plan.push({
|
|
765
|
+
field,
|
|
766
|
+
run: () => this.clear(field)
|
|
767
|
+
});
|
|
768
|
+
continue;
|
|
769
|
+
}
|
|
770
|
+
if (staged.text === spec.format(this.sectionValue(field))) continue;
|
|
771
|
+
const write = spec.parse(staged.text);
|
|
772
|
+
if (write === void 0) plan.push({
|
|
773
|
+
field,
|
|
774
|
+
run: void 0
|
|
775
|
+
});
|
|
776
|
+
else if (write.kind === "clear") plan.push({
|
|
777
|
+
field,
|
|
778
|
+
run: () => this.clear(field)
|
|
779
|
+
});
|
|
780
|
+
else plan.push({
|
|
781
|
+
field,
|
|
782
|
+
run: () => this.store(field, write.value)
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
return plan;
|
|
786
|
+
}
|
|
787
|
+
async clear(field) {
|
|
788
|
+
await this.scope.unset(field);
|
|
789
|
+
return !this.stored(field);
|
|
790
|
+
}
|
|
791
|
+
async store(field, value) {
|
|
792
|
+
await this.scope.set(field, value);
|
|
793
|
+
return this.userLayer()?.[field] === value;
|
|
794
|
+
}
|
|
795
|
+
stage(field, edit) {
|
|
796
|
+
this.staged.set(field, edit);
|
|
797
|
+
this.failed = false;
|
|
798
|
+
this.publish();
|
|
799
|
+
}
|
|
800
|
+
spec(field) {
|
|
801
|
+
const spec = this.specs.get(field);
|
|
802
|
+
if (spec === void 0) throw new Error(`plugin card has no field ${field}`);
|
|
803
|
+
return spec;
|
|
804
|
+
}
|
|
805
|
+
snapshotOf() {
|
|
806
|
+
return this.scope.getSnapshot();
|
|
807
|
+
}
|
|
808
|
+
sectionValue(field) {
|
|
809
|
+
return this.snapshotOf().value?.[field];
|
|
810
|
+
}
|
|
811
|
+
baseValue(field) {
|
|
812
|
+
return this.snapshotOf().base?.[field];
|
|
813
|
+
}
|
|
814
|
+
userLayer() {
|
|
815
|
+
return this.snapshotOf().user;
|
|
816
|
+
}
|
|
817
|
+
stored(field) {
|
|
818
|
+
const user = this.userLayer();
|
|
819
|
+
return user !== void 0 && Object.hasOwn(user, field);
|
|
820
|
+
}
|
|
821
|
+
publish() {
|
|
822
|
+
for (const listener of this.listeners) listener();
|
|
823
|
+
}
|
|
824
|
+
};
|
|
825
|
+
//#endregion
|
|
826
|
+
//#region lib/types/client/agent-loop-card-controller.js
|
|
827
|
+
/** The agent-loop card's staged form over the `agent-loop` settings namespace. */
|
|
828
|
+
/**
|
|
829
|
+
* Namespace of the agent loop's user-owned settings. Spelled here rather than
|
|
830
|
+
* imported: a client package must not depend on a Host package.
|
|
831
|
+
*/
|
|
832
|
+
const AGENT_LOOP_NS = "agent-loop";
|
|
833
|
+
/** Bridges the `agent-loop` scope onto the card's staged form. */
|
|
834
|
+
var AgentLoopCardController = class {
|
|
835
|
+
form;
|
|
836
|
+
store;
|
|
837
|
+
/** @param scope - the bound settings scope for the `agent-loop` namespace. */
|
|
838
|
+
constructor(scope) {
|
|
839
|
+
this.form = new CardForm(scope, [numberField("maxParallelToolCalls")]);
|
|
840
|
+
this.store = this.form.bind(() => this.projection());
|
|
841
|
+
}
|
|
842
|
+
projection() {
|
|
843
|
+
return {
|
|
844
|
+
...this.form.shell(),
|
|
845
|
+
maxParallelToolCalls: this.form.field("maxParallelToolCalls")
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
/**
|
|
849
|
+
* Build the face the card's slot registration injects.
|
|
850
|
+
* @returns the card's snapshot and its form actions.
|
|
851
|
+
*/
|
|
852
|
+
inject() {
|
|
853
|
+
return {
|
|
854
|
+
hooks: { agentLoopCard: this.store },
|
|
855
|
+
...this.form.actions()
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
};
|
|
859
|
+
//#endregion
|
|
860
|
+
//#region lib/types/client/bash-card-controller.js
|
|
861
|
+
/** The shell card's staged form over the `bash` settings namespace. */
|
|
862
|
+
/**
|
|
863
|
+
* Namespace of the shell capability. Spelled here rather than imported: a
|
|
864
|
+
* client package must not depend on a Host package, and the executor families
|
|
865
|
+
* that own it spell the same value.
|
|
866
|
+
*/
|
|
867
|
+
const SHELL_NS = "shell";
|
|
868
|
+
/** Bridges the `bash` scope onto the shell card's staged form. */
|
|
869
|
+
var BashCardController = class {
|
|
870
|
+
form;
|
|
871
|
+
store;
|
|
872
|
+
/** @param scope - the bound settings scope for the `bash` namespace. */
|
|
873
|
+
constructor(scope) {
|
|
874
|
+
this.form = new CardForm(scope, [numberField("timeoutMs"), numberField("maxOutputBytes")]);
|
|
875
|
+
this.store = this.form.bind(() => this.projection());
|
|
876
|
+
}
|
|
877
|
+
projection() {
|
|
878
|
+
return {
|
|
879
|
+
...this.form.shell(),
|
|
880
|
+
timeoutMs: this.form.field("timeoutMs"),
|
|
881
|
+
maxOutputBytes: this.form.field("maxOutputBytes")
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
/**
|
|
885
|
+
* Build the face the card's slot registration injects.
|
|
886
|
+
* @returns the card's snapshot and its form actions.
|
|
887
|
+
*/
|
|
888
|
+
inject() {
|
|
889
|
+
return {
|
|
890
|
+
hooks: { bashCard: this.store },
|
|
891
|
+
...this.form.actions()
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
};
|
|
895
|
+
//#endregion
|
|
896
|
+
//#region lib/types/client/web-search-card-controller.js
|
|
897
|
+
/**
|
|
898
|
+
* The web-search card's staged form over the `web-search-deepseek` settings
|
|
899
|
+
* namespace.
|
|
900
|
+
*
|
|
901
|
+
* The key is the one control that does not live in the section: its literal
|
|
902
|
+
* never rides a response, so the card learns only whether one is configured
|
|
903
|
+
* and writes it through the credentials domain, addressed by the reference the
|
|
904
|
+
* section names. It is still staged with the rest of the form, so one save
|
|
905
|
+
* covers everything the card shows.
|
|
906
|
+
*/
|
|
907
|
+
/**
|
|
908
|
+
* Namespace of the DeepSeek search provider. Spelled here rather than
|
|
909
|
+
* imported: a client package must not depend on a Host package.
|
|
910
|
+
*/
|
|
911
|
+
const WEB_SEARCH_NS = "web-search-deepseek";
|
|
912
|
+
/** Credential reference the provider resolves when the section names none. */
|
|
913
|
+
const DEFAULT_API_KEY_REF = "DEEPSEEK_API_KEY";
|
|
914
|
+
/** Form field the credential control stages under. */
|
|
915
|
+
const API_KEY_FIELD = "apiKey";
|
|
916
|
+
/** Bridges the `web-search-deepseek` scope and the credentials domain onto the card. */
|
|
917
|
+
var WebSearchCardController = class {
|
|
918
|
+
scope;
|
|
919
|
+
api;
|
|
920
|
+
form;
|
|
921
|
+
store;
|
|
922
|
+
credential = {
|
|
923
|
+
ref: "",
|
|
924
|
+
configured: false,
|
|
925
|
+
writable: true
|
|
926
|
+
};
|
|
927
|
+
/**
|
|
928
|
+
* @param scope - the bound settings scope for the `web-search-deepseek` namespace.
|
|
929
|
+
* @param api - wire face used for the credential the section references.
|
|
930
|
+
*/
|
|
931
|
+
constructor(scope, api) {
|
|
932
|
+
this.scope = scope;
|
|
933
|
+
this.api = api;
|
|
934
|
+
this.form = new CardForm(scope, [textField("baseURL"), numberField("maxUses")], [{
|
|
935
|
+
field: API_KEY_FIELD,
|
|
936
|
+
write: (text) => this.writeKey(text)
|
|
937
|
+
}]);
|
|
938
|
+
this.store = this.form.bind(() => this.projection());
|
|
939
|
+
scope.subscribe(() => {
|
|
940
|
+
this.readCredential();
|
|
941
|
+
});
|
|
942
|
+
this.readCredential();
|
|
943
|
+
}
|
|
944
|
+
projection() {
|
|
945
|
+
return {
|
|
946
|
+
...this.form.shell(),
|
|
947
|
+
baseURL: this.form.field("baseURL"),
|
|
948
|
+
maxUses: this.form.field("maxUses"),
|
|
949
|
+
apiKey: this.form.field(API_KEY_FIELD),
|
|
950
|
+
apiKeyConfigured: this.credential.configured,
|
|
951
|
+
apiKeyWritable: this.credential.writable
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
/**
|
|
955
|
+
* Ask the credentials domain about the reference the section currently names.
|
|
956
|
+
*
|
|
957
|
+
* The answer is stored with the reference it describes: `apiKeyEnv` can
|
|
958
|
+
* change between the request and its response, and two reads can settle out
|
|
959
|
+
* of order, so a response is published only while it still answers for the
|
|
960
|
+
* reference in force.
|
|
961
|
+
*/
|
|
962
|
+
async readCredential() {
|
|
963
|
+
const ref = refOf(this.scope.getSnapshot());
|
|
964
|
+
if (ref !== this.credential.ref) {
|
|
965
|
+
this.credential = {
|
|
966
|
+
ref,
|
|
967
|
+
configured: false,
|
|
968
|
+
writable: true
|
|
969
|
+
};
|
|
970
|
+
this.store.set(this.projection());
|
|
971
|
+
}
|
|
972
|
+
let response;
|
|
973
|
+
try {
|
|
974
|
+
response = await this.api.credentials.describe({ refs: [ref] });
|
|
975
|
+
} catch (_credentialReadFailure) {
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
if (!response.result.ok || ref !== refOf(this.scope.getSnapshot())) return;
|
|
979
|
+
const view = response.result.value.credentials[ref];
|
|
980
|
+
const next = {
|
|
981
|
+
ref,
|
|
982
|
+
configured: view?.configured ?? false,
|
|
983
|
+
writable: view?.writable ?? true
|
|
984
|
+
};
|
|
985
|
+
if (next.configured === this.credential.configured && next.writable === this.credential.writable) return;
|
|
986
|
+
this.credential = next;
|
|
987
|
+
this.store.set(this.projection());
|
|
988
|
+
}
|
|
989
|
+
/**
|
|
990
|
+
* Re-read after the Host reports a change to the reference this card watches.
|
|
991
|
+
*
|
|
992
|
+
* A key can be written from somewhere else — the Models page addresses the
|
|
993
|
+
* same reference — and the settings section does not change when it is, so
|
|
994
|
+
* without this the badge keeps reporting a state the Host already replaced.
|
|
995
|
+
* @param ref - the reference the Host reports as changed.
|
|
996
|
+
*/
|
|
997
|
+
refreshCredential(ref) {
|
|
998
|
+
if (ref !== this.credential.ref) return;
|
|
999
|
+
this.readCredential();
|
|
1000
|
+
}
|
|
1001
|
+
/**
|
|
1002
|
+
* Build the face the card's slot registration injects.
|
|
1003
|
+
* @returns the card's snapshot and its form actions.
|
|
1004
|
+
*/
|
|
1005
|
+
inject() {
|
|
1006
|
+
return {
|
|
1007
|
+
hooks: { webSearchCard: this.store },
|
|
1008
|
+
...this.form.actions()
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
/**
|
|
1012
|
+
* Write the staged key, then re-read whether the Host now holds one.
|
|
1013
|
+
* @param value - the staged credential literal.
|
|
1014
|
+
* @returns whether the Host reports a configured credential afterwards.
|
|
1015
|
+
*/
|
|
1016
|
+
async writeKey(value) {
|
|
1017
|
+
try {
|
|
1018
|
+
await this.api.credentials.set({
|
|
1019
|
+
ref: refOf(this.scope.getSnapshot()),
|
|
1020
|
+
value
|
|
1021
|
+
});
|
|
1022
|
+
} catch (_credentialWriteFailure) {}
|
|
1023
|
+
await this.readCredential();
|
|
1024
|
+
return this.credential.configured;
|
|
1025
|
+
}
|
|
1026
|
+
};
|
|
1027
|
+
/**
|
|
1028
|
+
* The credential reference the section names, or the provider's default.
|
|
1029
|
+
* @param snapshot - the current scope snapshot.
|
|
1030
|
+
* @returns the reference to address.
|
|
1031
|
+
*/
|
|
1032
|
+
function refOf(snapshot) {
|
|
1033
|
+
const declared = snapshot.value?.apiKeyEnv;
|
|
1034
|
+
return declared !== void 0 && declared.length > 0 ? declared : DEFAULT_API_KEY_REF;
|
|
1035
|
+
}
|
|
1036
|
+
//#endregion
|
|
1037
|
+
//#region lib/types/client/locales.js
|
|
1038
|
+
/** Locale bundles for the plugin configuration section and its plugin cards. */
|
|
1039
|
+
/** English copy. */
|
|
1040
|
+
const en = {
|
|
1041
|
+
nav: "Plugins",
|
|
1042
|
+
title: "Plugins",
|
|
1043
|
+
intro: "Configure and inspect the plugins installed in this deployment.",
|
|
1044
|
+
tabs: "Plugin views",
|
|
1045
|
+
configurableTab: "Plugin configuration",
|
|
1046
|
+
empty: "This deployment exposes no plugin settings.",
|
|
1047
|
+
overridden: "Overridden",
|
|
1048
|
+
reset: "Reset to default",
|
|
1049
|
+
readOnly: "This deployment stores settings read-only.",
|
|
1050
|
+
expand: "Show settings",
|
|
1051
|
+
collapse: "Hide settings",
|
|
1052
|
+
save: "Save",
|
|
1053
|
+
saving: "Saving…",
|
|
1054
|
+
discard: "Discard",
|
|
1055
|
+
unsaved: "Unsaved",
|
|
1056
|
+
saveFailed: "The deployment did not accept these values; they were left for you to correct.",
|
|
1057
|
+
invalidNumber: "Enter a number, or leave blank to use the default.",
|
|
1058
|
+
bashTitle: "Shell",
|
|
1059
|
+
bashDescription: "Limits every command the agent runs.",
|
|
1060
|
+
bashTimeoutMs: "Command timeout (ms)",
|
|
1061
|
+
bashTimeoutMsHint: "How long one command may run before it is terminated.",
|
|
1062
|
+
bashMaxOutputBytes: "Output cap per stream (bytes)",
|
|
1063
|
+
bashMaxOutputBytesHint: "Output beyond this spills to a temporary file rather than being lost.",
|
|
1064
|
+
agentLoopTitle: "Agent loop",
|
|
1065
|
+
agentLoopDescription: "How the agent dispatches tool calls.",
|
|
1066
|
+
agentLoopMaxParallel: "Parallel tool calls",
|
|
1067
|
+
agentLoopMaxParallelHint: "Upper bound on parallel-safe calls running at once within one step.",
|
|
1068
|
+
webSearchTitle: "Web search",
|
|
1069
|
+
webSearchDescription: "The DeepSeek search provider.",
|
|
1070
|
+
webSearchApiKey: "API key",
|
|
1071
|
+
webSearchApiKeyHint: "Stored outside the settings file. Leave blank to keep the current key.",
|
|
1072
|
+
webSearchApiKeySet: "A key is configured.",
|
|
1073
|
+
webSearchApiKeyUnset: "No key is configured; search is unavailable until one is.",
|
|
1074
|
+
webSearchBaseUrl: "Endpoint",
|
|
1075
|
+
webSearchBaseUrlHint: "Leave blank to use the provider default.",
|
|
1076
|
+
webSearchMaxUses: "Max searches per request",
|
|
1077
|
+
webSearchMaxUsesHint: "How many times one request may search before it must answer."
|
|
1078
|
+
};
|
|
1079
|
+
/** Simplified Chinese copy. */
|
|
1080
|
+
const zh = {
|
|
1081
|
+
nav: "插件",
|
|
1082
|
+
title: "插件",
|
|
1083
|
+
intro: "配置和查看本部署已安装的插件。",
|
|
1084
|
+
tabs: "插件视图",
|
|
1085
|
+
configurableTab: "插件配置",
|
|
1086
|
+
empty: "本部署没有开放任何插件设置。",
|
|
1087
|
+
overridden: "已覆盖",
|
|
1088
|
+
reset: "恢复默认",
|
|
1089
|
+
readOnly: "本部署的设置为只读。",
|
|
1090
|
+
expand: "展开设置",
|
|
1091
|
+
collapse: "收起设置",
|
|
1092
|
+
save: "保存",
|
|
1093
|
+
saving: "保存中…",
|
|
1094
|
+
discard: "放弃修改",
|
|
1095
|
+
unsaved: "未保存",
|
|
1096
|
+
saveFailed: "本部署没有接受这些值,已保留供你修改。",
|
|
1097
|
+
invalidNumber: "请填数字;留空表示使用默认值。",
|
|
1098
|
+
bashTitle: "终端",
|
|
1099
|
+
bashDescription: "限制 agent 运行的每一条命令。",
|
|
1100
|
+
bashTimeoutMs: "命令超时(毫秒)",
|
|
1101
|
+
bashTimeoutMsHint: "单条命令允许运行多久,超时即终止。",
|
|
1102
|
+
bashMaxOutputBytes: "单流输出上限(字节)",
|
|
1103
|
+
bashMaxOutputBytesHint: "超出部分会转存到临时文件,而不是被丢弃。",
|
|
1104
|
+
agentLoopTitle: "Agent 循环",
|
|
1105
|
+
agentLoopDescription: "Agent 如何派发工具调用。",
|
|
1106
|
+
agentLoopMaxParallel: "并行工具调用数",
|
|
1107
|
+
agentLoopMaxParallelHint: "同一步内最多同时运行多少个可并行的调用。",
|
|
1108
|
+
webSearchTitle: "网页搜索",
|
|
1109
|
+
webSearchDescription: "DeepSeek 搜索提供方。",
|
|
1110
|
+
webSearchApiKey: "API Key",
|
|
1111
|
+
webSearchApiKeyHint: "不写入设置文件。留空表示保持当前密钥。",
|
|
1112
|
+
webSearchApiKeySet: "已配置密钥。",
|
|
1113
|
+
webSearchApiKeyUnset: "未配置密钥;配置之前搜索不可用。",
|
|
1114
|
+
webSearchBaseUrl: "接口地址",
|
|
1115
|
+
webSearchBaseUrlHint: "留空则使用提供方默认地址。",
|
|
1116
|
+
webSearchMaxUses: "单次请求最多搜索次数",
|
|
1117
|
+
webSearchMaxUsesHint: "一次请求在必须作答前最多可以搜索多少次。"
|
|
1118
|
+
};
|
|
1119
|
+
//#endregion
|
|
1120
|
+
//#region lib/types/client/index.js
|
|
1121
|
+
/**
|
|
1122
|
+
* Plugins settings surface, browser half — one section whose feature-owned
|
|
1123
|
+
* tabs include configurable Host plugin cards and read-only inventory.
|
|
1124
|
+
*
|
|
1125
|
+
* The section declares `settings.plugins.tab`; its own `configurable` tab then
|
|
1126
|
+
* declares `settings.plugin.item` and renders whatever cards were registered
|
|
1127
|
+
* into it. The three cards this package ships are the host-plane sections the
|
|
1128
|
+
* deployment already exposes; each binds its namespace through the client
|
|
1129
|
+
* settings scope, which keeps them unaware of one another and of other tabs.
|
|
1130
|
+
*/
|
|
1131
|
+
/** Dictionary namespace owned by this plugin. */
|
|
1132
|
+
const NS = "settings.plugins";
|
|
1133
|
+
/** Required services (cordis fiber inject). */
|
|
1134
|
+
const inject = [
|
|
1135
|
+
"slots",
|
|
1136
|
+
"locale",
|
|
1137
|
+
"connection",
|
|
1138
|
+
"remote",
|
|
1139
|
+
"settingsScope"
|
|
1140
|
+
];
|
|
1141
|
+
/**
|
|
1142
|
+
* Mount the plugin configuration section and the cards this package ships.
|
|
1143
|
+
* @param ctx - the browser plugin context.
|
|
1144
|
+
*/
|
|
1145
|
+
function apply(ctx) {
|
|
1146
|
+
const { api } = ctx.get("connection");
|
|
1147
|
+
const t = ctx.locale.bind(NS);
|
|
1148
|
+
ctx.effect(() => ctx.locale.register(NS, {
|
|
1149
|
+
zh,
|
|
1150
|
+
en
|
|
1151
|
+
}), "ui-settings-plugins: section dictionaries");
|
|
1152
|
+
const bash = new BashCardController(ctx.settingsScope.bind({ namespace: SHELL_NS }));
|
|
1153
|
+
const agentLoop = new AgentLoopCardController(ctx.settingsScope.bind({ namespace: AGENT_LOOP_NS }));
|
|
1154
|
+
const webSearch = new WebSearchCardController(ctx.settingsScope.bind({ namespace: WEB_SEARCH_NS }), api);
|
|
1155
|
+
ctx.effect(() => ctx.remote.$on("credentials/updated", (ref) => {
|
|
1156
|
+
webSearch.refreshCredential(ref);
|
|
1157
|
+
}), "ui-settings-plugins: credential invalidations");
|
|
1158
|
+
let tabsVersion = -1;
|
|
1159
|
+
let tabsRevision = -1;
|
|
1160
|
+
let tabs = [];
|
|
1161
|
+
const sectionInjected = () => ({ hooks: { tabs: {
|
|
1162
|
+
getSnapshot: () => {
|
|
1163
|
+
const version = ctx.slots.getVersion("settings.plugins.tab");
|
|
1164
|
+
const revision = ctx.locale.getSnapshot().revision;
|
|
1165
|
+
if (version !== tabsVersion || revision !== tabsRevision) {
|
|
1166
|
+
tabsVersion = version;
|
|
1167
|
+
tabsRevision = revision;
|
|
1168
|
+
tabs = ctx.slots.entries("settings.plugins.tab").map((entry) => ({
|
|
1169
|
+
/* v8 ignore next -- list-slot registration requires id */
|
|
1170
|
+
id: entry.options.id ?? "",
|
|
1171
|
+
order: entry.options.order ?? 0,
|
|
1172
|
+
label: (0, _monotykamary_dsh_client_ui_slots.resolveSlotLabel)(entry.options.label) ?? ""
|
|
1173
|
+
})).sort((a, b) => a.order - b.order);
|
|
1174
|
+
}
|
|
1175
|
+
return tabs;
|
|
1176
|
+
},
|
|
1177
|
+
subscribe: (listener) => {
|
|
1178
|
+
const offLedger = ctx.slots.subscribe("settings.plugins.tab", listener);
|
|
1179
|
+
const offLocale = ctx.locale.subscribe(listener);
|
|
1180
|
+
return () => {
|
|
1181
|
+
offLedger();
|
|
1182
|
+
offLocale();
|
|
1183
|
+
};
|
|
1184
|
+
}
|
|
1185
|
+
} } });
|
|
1186
|
+
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
1187
|
+
name: "settings.section",
|
|
1188
|
+
id: "plugins",
|
|
1189
|
+
order: 15,
|
|
1190
|
+
label: () => t("nav"),
|
|
1191
|
+
locale: NS,
|
|
1192
|
+
inject: sectionInjected,
|
|
1193
|
+
children: { "settings.plugins.tab": {
|
|
1194
|
+
kind: "list",
|
|
1195
|
+
scope: "root"
|
|
1196
|
+
} }
|
|
1197
|
+
}, PluginsSettingsSection));
|
|
1198
|
+
ctx.slots.inject("settings.plugins.tab", () => ctx.slots.register({
|
|
1199
|
+
name: "settings.plugins.tab",
|
|
1200
|
+
id: "configurable",
|
|
1201
|
+
order: 0,
|
|
1202
|
+
label: () => t("configurableTab"),
|
|
1203
|
+
locale: NS,
|
|
1204
|
+
inject: () => ({ cardCount: ctx.slots.entries("settings.plugin.item").length }),
|
|
1205
|
+
children: { "settings.plugin.item": {
|
|
1206
|
+
kind: "list",
|
|
1207
|
+
scope: "root"
|
|
1208
|
+
} }
|
|
1209
|
+
}, ConfigurablePluginsTab));
|
|
1210
|
+
ctx.slots.inject("settings.plugin.item", function* () {
|
|
1211
|
+
yield ctx.slots.register({
|
|
1212
|
+
name: "settings.plugin.item",
|
|
1213
|
+
id: "bash",
|
|
1214
|
+
order: 0,
|
|
1215
|
+
locale: NS,
|
|
1216
|
+
inject: () => bash.inject()
|
|
1217
|
+
}, BashCard);
|
|
1218
|
+
yield ctx.slots.register({
|
|
1219
|
+
name: "settings.plugin.item",
|
|
1220
|
+
id: "agent-loop",
|
|
1221
|
+
order: 10,
|
|
1222
|
+
locale: NS,
|
|
1223
|
+
inject: () => agentLoop.inject()
|
|
1224
|
+
}, AgentLoopCard);
|
|
1225
|
+
yield ctx.slots.register({
|
|
1226
|
+
name: "settings.plugin.item",
|
|
1227
|
+
id: "web-search",
|
|
1228
|
+
order: 20,
|
|
1229
|
+
locale: NS,
|
|
1230
|
+
inject: () => webSearch.inject()
|
|
1231
|
+
}, WebSearchCard);
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1234
|
+
//#endregion
|
|
1235
|
+
exports.apply = apply;
|
|
1236
|
+
exports.inject = inject;
|
|
1237
|
+
return module.exports;
|
|
1238
|
+
}
|
|
1239
|
+
});
|
|
1240
|
+
|
|
1241
|
+
//# sourceMappingURL=client.js.map
|