@xiaoso/dsh-tool-plus 0.1.10-beta.1 → 0.1.10-beta.2

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 CHANGED
@@ -6,8 +6,8 @@ window.__ModuleLoader__.load({
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
7
  let react = require("react");
8
8
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
9
- let react_jsx_runtime = require("react/jsx-runtime");
10
9
  let _deepseek_ai_dsh_client_connection_client = require("@deepseek-ai/dsh-client-connection/client");
10
+ let react_jsx_runtime = require("react/jsx-runtime");
11
11
  let react_dom = require("react-dom");
12
12
  //#region src/config/fields.ts
13
13
  /** Field group ids -> locale key of the group heading. */
@@ -783,15 +783,133 @@ window.__ModuleLoader__.load({
783
783
  fields: TOOL_PLUS_FIELDS.filter((f) => f.tool === "astEdit").map((f) => f.name)
784
784
  }
785
785
  ];
786
- /** Look up one field definition by its schema key. */
787
- function toolPlusField(name) {
788
- return TOOL_PLUS_FIELDS.find((field) => field.name === name);
786
+ TOOL_PLUS_FIELDS.map((field) => field.name);
787
+ //#endregion
788
+ //#region src/tools/shared/browser-rpc-channel.ts
789
+ /**
790
+ * Shared RPC channel constants for the browser-detection round trip between
791
+ * the settings panel (client half) and the host. Both halves import this
792
+ * module so the channel/endpoint never drift.
793
+ *
794
+ * The channel rides the official `@deepseek-ai/dsh-client-connection` generic
795
+ * RPC transport (host registers with `ctx.connection.rpc.handle`; the client
796
+ * calls via `createWebConnectionRpc().call`). Channel names must match
797
+ * `/^\/[A-Za-z0-9._~-]+$/` and `/api` is reserved — `/tool-plus` is ours.
798
+ * @module @xiaoso/dsh-tool-plus/browser-rpc-channel
799
+ */
800
+ /** Logical RPC channel registered by the host half of this plugin. */
801
+ const TOOL_PLUS_RPC_CHANNEL = "/tool-plus";
802
+ /** Endpoint that reports the rmSafe injection status (query = ensure + verify). */
803
+ const RM_SAFE_STATUS_ENDPOINT = "rmSafe/status";
804
+ /** Endpoint that lists agent presets with their status (roster + conflicts + template diff). */
805
+ const PRESET_STATUS_ENDPOINT = "presets/status";
806
+ /**
807
+ * The preset ids this plugin **declares**. Single source of truth for both
808
+ * halves: the host picks its own declarations out of the profile's composed
809
+ * configuration, and the panel uses the list to tell "ours" from everything else.
810
+ *
811
+ * They ship as `@deepseek-ai/dsh-agent-preset` declaration rows in this
812
+ * package's own bundle patches (`presets/<id>.patch.yml`), so installing the
813
+ * plugin as a profile bundle declares them: no install step, no file copy, and
814
+ * nothing written at startup.
815
+ */
816
+ const BUNDLED_PRESET_IDS = ["tool-plus-standard", "tool-plus-ptc"];
817
+ /** Endpoint that applies one preset action to the active profile. */
818
+ const PRESET_ACTION_ENDPOINT = "presets/apply";
819
+ /** Endpoint that compares one preset against one of our declarations (read-only). */
820
+ const PRESET_COMPARE_ENDPOINT = "presets/compare";
821
+ //#endregion
822
+ //#region src/client/web-connection-rpc.ts
823
+ /**
824
+ * Browser caller for the plugin's Connection RPC channel.
825
+ *
826
+ * The official `@deepseek-ai/dsh-client-connection/client` package ships the
827
+ * protocol primitives as types (`RpcFetch`, `ClientConnectionRpc`, `RpcResult`)
828
+ * and the `RpcId` brand, but its published `createWebConnectionRpc` factory is
829
+ * internal to the bundle — not re-exported. This module re-implements that
830
+ * factory against the same public wire contract (docs/api-gateway.md, the
831
+ * client `rpc.ts` source): a `POST {origin}/{channel}/{endpoint}` with a
832
+ * `client-request` JSON envelope, answered by a `server-response` envelope
833
+ * echoing the rpcId. Host-side registration uses the official
834
+ * `ctx.connection.rpc.handle` — this file only mirrors the caller half.
835
+ * @module @xiaoso/dsh-tool-plus/client/web-connection-rpc
836
+ */
837
+ const INTERNAL_BASE = "http://dsh.internal";
838
+ const CHANNEL_PATTERN = /^\/[A-Za-z0-9._~-]+$/;
839
+ const ENDPOINT_SEGMENT_PATTERN = /^[A-Za-z0-9_$.-]+$/;
840
+ function randomUuid() {
841
+ const g = globalThis;
842
+ if (g.crypto?.randomUUID !== void 0) return g.crypto.randomUUID();
843
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
844
+ const r = Math.random() * 16 | 0;
845
+ return (c === "x" ? r : r & 3 | 8).toString(16);
846
+ });
789
847
  }
790
- /** Fields of one tool tab, in table order. */
791
- function toolPlusFieldsOf(tool) {
792
- return TOOL_PLUS_FIELDS.filter((field) => field.tool === tool);
848
+ function isRecord$4(value) {
849
+ return typeof value === "object" && value !== null && !Array.isArray(value);
850
+ }
851
+ function parseConnectionResponse(value) {
852
+ if (!isRecord$4(value) || value.type !== "server-response" || typeof value.rpcId !== "string") throw new TypeError("connection: invalid server-response envelope");
853
+ const result = value.result;
854
+ if (!isRecord$4(result)) throw new TypeError("connection: invalid server-response result");
855
+ if (result.ok === true) return {
856
+ rpcId: value.rpcId,
857
+ result: {
858
+ ok: true,
859
+ value: result.value
860
+ }
861
+ };
862
+ if (result.ok !== false || !isRecord$4(result.error)) throw new TypeError("connection: invalid server-response result");
863
+ const error = result.error;
864
+ if (typeof error.code !== "string" || typeof error.message !== "string" || !isRecord$4(error.details)) throw new TypeError("connection: invalid server-response failure");
865
+ return {
866
+ rpcId: value.rpcId,
867
+ result: {
868
+ ok: false,
869
+ error: {
870
+ code: error.code,
871
+ message: error.message,
872
+ details: error.details
873
+ }
874
+ }
875
+ };
876
+ }
877
+ function resolveBase() {
878
+ const location = globalThis.location;
879
+ return location?.origin !== void 0 && location.origin !== "null" ? location.origin : INTERNAL_BASE;
880
+ }
881
+ function assertTarget(channel, endpoint) {
882
+ const segments = endpoint.split("/");
883
+ if (!CHANNEL_PATTERN.test(channel) || segments.some((segment) => segment === "" || segment === "." || segment === ".." || !ENDPOINT_SEGMENT_PATTERN.test(segment))) throw new Error(`connection: invalid RPC target ${JSON.stringify(`${channel}/${endpoint}`)}`);
884
+ }
885
+ /**
886
+ * Create the browser-backed generic RPC caller for the plugin's channel
887
+ * (the `/tool-plus` host registration). Wire-compatible with the official
888
+ * ClientConnectionRpc contract; only the transport override differs.
889
+ * @param doFetch - transport override; defaults to the page's global fetch.
890
+ */
891
+ function createWebConnectionRpc(doFetch) {
892
+ const send = doFetch ?? ((input, init) => globalThis.fetch(input, init));
893
+ return { async call(channel, endpoint, payload, signal) {
894
+ assertTarget(channel, endpoint);
895
+ const rpcId = (0, _deepseek_ai_dsh_client_connection_client.RpcId)(randomUuid());
896
+ const response = await send(new URL(`${channel}/${endpoint}`, resolveBase()), {
897
+ method: "POST",
898
+ headers: { "content-type": "application/json" },
899
+ body: JSON.stringify({
900
+ type: "client-request",
901
+ rpcId,
902
+ method: endpoint,
903
+ payload
904
+ }),
905
+ ...signal === void 0 ? {} : { signal }
906
+ });
907
+ if (!response.ok) throw new Error(`transport failure for ${channel}/${endpoint}: HTTP ${response.status}`);
908
+ const full = parseConnectionResponse(await response.json());
909
+ if (full.rpcId !== rpcId) throw new Error(`rpcId mismatch for ${endpoint}: sent ${rpcId}, got ${full.rpcId}`);
910
+ return full.result;
911
+ } };
793
912
  }
794
- TOOL_PLUS_FIELDS.map((field) => field.name);
795
913
  //#endregion
796
914
  //#region src/client/rows.tsx
797
915
  /**
@@ -1081,735 +1199,6 @@ window.__ModuleLoader__.load({
1081
1199
  });
1082
1200
  }
1083
1201
  //#endregion
1084
- //#region src/client/BashPlusCard.tsx
1085
- /**
1086
- * The tool-plus settings card — a collapsible disclosure card in the Plugins
1087
- * settings tab styled against the app's `--dsw-alias-*` design tokens and the
1088
- * official card interaction (header button with unsaved badge + rotating
1089
- * chevron, smooth expand/collapse respecting reduced-motion, staged edits that
1090
- * survive collapsing, one Save write, nothing rendered while the namespace is
1091
- * unavailable). Copy is fully localized through the slot-injected `t` seat
1092
- * (`locale: 'tool-plus'`), which re-renders on a locale switch.
1093
- *
1094
- * The card edits the FLAT settings surface (OMP `bash*`-style scalar keys) —
1095
- * the client form only writes scalar fields — grouped as timeouts,
1096
- * backgrounding, output, completion truncation (subdivided into byte and line
1097
- * settings), and behavior. Field definitions, defaults, and copy keys come
1098
- * from the single-source field table (`src/config/fields.ts`), shared with
1099
- * the Tool Plus settings section.
1100
- * @module @xiaoso/dsh-tool-plus/client
1101
- */
1102
- /** Bash fields derived from the single-source table, split by subgroup. */
1103
- const BASH_FIELDS = toolPlusFieldsOf("bash");
1104
- const TIMING_FIELDS = BASH_FIELDS.filter((f) => f.group === "timing").map((f) => ({
1105
- field: f.name,
1106
- labelKey: f.labelKey,
1107
- hintKey: f.hintKey
1108
- }));
1109
- const OUTPUT_FIELDS = BASH_FIELDS.filter((f) => f.group === "output").map((f) => ({
1110
- field: f.name,
1111
- labelKey: f.labelKey,
1112
- hintKey: f.hintKey
1113
- }));
1114
- const TRUNCATION_FIELDS = BASH_FIELDS.filter((f) => f.group === "truncation");
1115
- const STRATEGY_SELECT = {
1116
- field: "outputTruncateStrategy",
1117
- labelKey: "outputTruncateStrategy",
1118
- hintKey: "outputTruncateStrategyHint",
1119
- options: toolPlusField("outputTruncateStrategy")?.options ?? []
1120
- };
1121
- const BYTE_SELECT = {
1122
- field: "outputTruncateByteMode",
1123
- labelKey: "outputTruncateByteMode",
1124
- hintKey: "outputTruncateByteModeHint",
1125
- options: toolPlusField("outputTruncateByteMode")?.options ?? []
1126
- };
1127
- const BYTE_NUMBER_FIELDS = TRUNCATION_FIELDS.filter((f) => f.name.startsWith("outputTruncateByte") && f.name !== "outputTruncateByteMode").map((f) => ({
1128
- field: f.name,
1129
- labelKey: f.labelKey,
1130
- hintKey: f.hintKey
1131
- }));
1132
- const LINE_SELECT = {
1133
- field: "outputTruncateLineMode",
1134
- labelKey: "outputTruncateLineMode",
1135
- hintKey: "outputTruncateLineModeHint",
1136
- options: toolPlusField("outputTruncateLineMode")?.options ?? []
1137
- };
1138
- const LINE_NUMBER_FIELDS = TRUNCATION_FIELDS.filter((f) => f.name.startsWith("outputTruncateLine") && f.name !== "outputTruncateLineMode").map((f) => ({
1139
- field: f.name,
1140
- labelKey: f.labelKey,
1141
- hintKey: f.hintKey
1142
- }));
1143
- const TOGGLE_FIELDS = BASH_FIELDS.filter((f) => f.group === "behavior").map((f) => ({
1144
- field: f.name,
1145
- labelKey: f.labelKey,
1146
- hintKey: f.hintKey
1147
- }));
1148
- /** Schema defaults sourced from the single-source field table. */
1149
- const SCHEMA_DEFAULTS = Object.fromEntries(TOOL_PLUS_FIELDS.map((f) => [f.name, f.default]));
1150
- /** Value a cleared field will revert to: the composition base when present, else the schema default. */
1151
- function revertValue$1(snap, field) {
1152
- if (hasOwn(snap.base, field)) return snap.base[field];
1153
- return SCHEMA_DEFAULTS[field];
1154
- }
1155
- /** Card chrome CSS, keyed by `data-plugin-css` and injected once (design-token driven). */
1156
- const CSS$3 = `
1157
- .tp-card{list-style:none;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-3);transition:border-color .16s,background .16s;margin:0 0 8px}
1158
- .tp-card:hover{border-color:var(--dsw-alias-label-dimmed)}
1159
- .tp-card.tp-open{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}
1160
- .tp-header{width:100%;appearance:none;border:0;background:none;font:inherit;color:inherit;text-align:left;cursor:pointer;display:flex;align-items:center;gap:12px;padding:14px 16px;border-radius:12px}
1161
- .tp-header:active{background:var(--dsw-alias-bg-layer-2)}
1162
- .tp-header:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-2px}
1163
- .tp-headText{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}
1164
- .tp-name{font-size:15px;font-weight:600;line-height:1.4;color:var(--dsw-alias-label-primary)}
1165
- .tp-desc{font-size:13px;line-height:1.5;color:var(--dsw-alias-label-tertiary)}
1166
- .tp-pending{display:inline-flex;align-items:center;gap:6px;flex:none;border-radius:999px;padding:2px 10px;font-size:11px;line-height:17px;font-weight:500;white-space:nowrap;background:var(--dsw-alias-state-business-tertiary);color:var(--dsw-alias-state-business-primary)}
1167
- .tp-pending::before{content:'';flex:none;width:6px;height:6px;border-radius:999px;background:var(--dsw-alias-state-business-primary);animation:tp-pulse 1.8s ease-in-out infinite}
1168
- @keyframes tp-pulse{0%,100%{opacity:.35;transform:scale(.85)}50%{opacity:1;transform:scale(1)}}
1169
- .tp-chevron{flex:none;color:var(--dsw-alias-label-tertiary);transition:transform .16s;display:inline-flex}
1170
- .tp-chevron.tp-open{transform:rotate(180deg)}
1171
- .tp-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-top:4px;display:grid;grid-template-rows:0fr;opacity:0;transition:grid-template-rows .22s cubic-bezier(.22,.61,.36,1),opacity .16s ease}
1172
- .tp-body.tp-open{grid-template-rows:1fr;opacity:1}
1173
- .tp-bodyInner{overflow:hidden}
1174
- .tp-readOnly{margin:12px 0 0;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-tertiary)}
1175
- .tp-group{display:flex;flex-direction:column;gap:0;padding:12px 0 4px}
1176
- .tp-group+.tp-group{border-top:1px solid var(--dsw-alias-border-l2)}
1177
- .tp-groupTitle{font-size:13px;font-weight:600;line-height:20px;color:var(--dsw-alias-label-primary);margin:0 0 4px}
1178
- .tp-footer{display:flex;align-items:center;justify-content:flex-end;gap:8px;padding:12px 0 4px;border-top:1px solid var(--dsw-alias-border-l2)}
1179
- .tp-failed{flex:1;min-width:0;margin:0;font-size:12px;line-height:1.5;color:var(--dsw-alias-state-error-primary)}
1180
- .tp-applies{flex:1;min-width:0;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-tertiary)}
1181
- .tp-discard,.tp-save{appearance:none;border:1px solid transparent;border-radius:8px;padding:5px 14px;font:inherit;font-size:13px;line-height:1.5;cursor:pointer;transition:transform .1s ease}
1182
- .tp-discard:active:not(:disabled),.tp-save:active:not(:disabled){transform:scale(.97)}
1183
- .tp-discard{border-color:var(--dsw-alias-border-l2);background:none;color:var(--dsw-alias-label-secondary)}
1184
- .tp-discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}
1185
- .tp-save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}
1186
- .tp-save:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}
1187
- .tp-discard:disabled,.tp-save:disabled{opacity:.4;cursor:default}
1188
- .tp-discard:focus-visible,.tp-save:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:1px}
1189
- @media (prefers-reduced-motion: reduce){
1190
- .tp-card,.tp-header,.tp-chevron,.tp-body,.tp-thumb,.tp-switchTrack,.tp-discard,.tp-save{transition:none}
1191
- .tp-pending::before{animation:none}
1192
- }
1193
- `;
1194
- /** Inject the card stylesheet once per page; the loader removes plugin-owned style tags on unload. */
1195
- let cssInjected$2 = false;
1196
- function injectCardCss() {
1197
- if (cssInjected$2 || typeof document === "undefined") return;
1198
- cssInjected$2 = true;
1199
- const id = "tool-plus-card";
1200
- if (document.querySelector("style[data-plugin-css=\"tool-plus-card\"]") !== null) return;
1201
- const tag = document.createElement("style");
1202
- tag.dataset.plugin = "@xiaoso/dsh-tool-plus";
1203
- tag.dataset.pluginCss = id;
1204
- tag.textContent = CSS$3;
1205
- document.head.appendChild(tag);
1206
- }
1207
- /** Text of a staged (or resolved) value for the number controls. */
1208
- function numberText$1(staged, hasStaged, resolved, snap, field) {
1209
- if (hasStaged) return (staged ?? "").trim() === "" ? String(revertValue$1(snap, field) ?? "") : staged ?? "";
1210
- return resolved === void 0 || typeof resolved === "boolean" ? "" : String(resolved);
1211
- }
1212
- /** Stage card edits over the shared configuration form; only a Save writes the document. */
1213
- function useBashPlusForm(scope) {
1214
- const [snap, setSnap] = (0, react.useState)(() => scope.getSnapshot());
1215
- const [numbers, setNumbers] = (0, react.useState)({});
1216
- const [selects, setSelects] = (0, react.useState)({});
1217
- const [toggles, setToggles] = (0, react.useState)({});
1218
- const [saving, setSaving] = (0, react.useState)(false);
1219
- const [failed, setFailed] = (0, react.useState)(false);
1220
- (0, react.useEffect)(() => scope.subscribe(() => {
1221
- setSnap(scope.getSnapshot());
1222
- setFailed(false);
1223
- }), [scope]);
1224
- const value = snap.value ?? {};
1225
- const writable = snap.writable;
1226
- const timing = (0, react.useMemo)(() => TIMING_FIELDS.map((def) => renderNumber$1(def, numbers, value, snap)), [numbers, snap]);
1227
- const output = (0, react.useMemo)(() => OUTPUT_FIELDS.map((def) => renderNumber$1(def, numbers, value, snap)), [numbers, snap]);
1228
- const strategySelect = (0, react.useMemo)(() => renderSelect$1(STRATEGY_SELECT, selects, value, snap), [selects, snap]);
1229
- const byteSelect = (0, react.useMemo)(() => renderSelect$1(BYTE_SELECT, selects, value, snap), [selects, snap]);
1230
- const byteNumbers = (0, react.useMemo)(() => BYTE_NUMBER_FIELDS.map((def) => renderNumber$1(def, numbers, value, snap)), [numbers, snap]);
1231
- const lineSelect = (0, react.useMemo)(() => renderSelect$1(LINE_SELECT, selects, value, snap), [selects, snap]);
1232
- const lineNumbers = (0, react.useMemo)(() => LINE_NUMBER_FIELDS.map((def) => renderNumber$1(def, numbers, value, snap)), [numbers, snap]);
1233
- const togglesView = (0, react.useMemo)(() => TOGGLE_FIELDS.map((def) => {
1234
- const hasStaged = Object.prototype.hasOwnProperty.call(toggles, def.field);
1235
- const staged = toggles[def.field];
1236
- const baseVal = Boolean(revertValue$1(snap, def.field));
1237
- const userStored = hasOwn(snap.user, def.field);
1238
- snap.user?.[def.field];
1239
- const effectiveVal = hasStaged ? staged ?? baseVal : Boolean(value[def.field]);
1240
- let overridden = false;
1241
- if (hasStaged) {
1242
- if (staged === null || staged === baseVal) overridden = false;
1243
- else overridden = true;
1244
- } else overridden = userStored;
1245
- return {
1246
- field: def.field,
1247
- labelKey: def.labelKey,
1248
- hintKey: def.hintKey,
1249
- checked: effectiveVal,
1250
- overridden
1251
- };
1252
- }), [toggles, snap]);
1253
- const invalid = (0, react.useMemo)(() => [
1254
- ...timing,
1255
- ...output,
1256
- ...byteNumbers,
1257
- ...lineNumbers
1258
- ], [
1259
- timing,
1260
- output,
1261
- byteNumbers,
1262
- lineNumbers
1263
- ]).some((c) => c.invalid);
1264
- const isDirty = (0, react.useMemo)(() => {
1265
- for (const [field, staged] of Object.entries(selects)) {
1266
- const baseVal = String(revertValue$1(snap, field) ?? "");
1267
- const userStored = hasOwn(snap.user, field);
1268
- const userVal = snap.user?.[field];
1269
- if (staged === null) {
1270
- if (userStored) return true;
1271
- } else if (staged === baseVal) {
1272
- if (userStored) return true;
1273
- } else if (!userStored || userVal !== staged) return true;
1274
- }
1275
- for (const [field, staged] of Object.entries(numbers)) {
1276
- const baseVal = revertValue$1(snap, field);
1277
- const userStored = hasOwn(snap.user, field);
1278
- const userVal = snap.user?.[field];
1279
- const trimmed = staged.trim();
1280
- if (trimmed === "") {
1281
- if (userStored) return true;
1282
- } else {
1283
- const num = Number(trimmed);
1284
- if (Number.isFinite(num)) {
1285
- if (num === baseVal) {
1286
- if (userStored) return true;
1287
- } else if (!userStored || userVal !== num) return true;
1288
- } else return true;
1289
- }
1290
- }
1291
- for (const [field, staged] of Object.entries(toggles)) {
1292
- const baseVal = Boolean(revertValue$1(snap, field));
1293
- const userStored = hasOwn(snap.user, field);
1294
- const userVal = snap.user?.[field];
1295
- if (staged === null) {
1296
- if (userStored) return true;
1297
- } else if (staged === baseVal) {
1298
- if (userStored) return true;
1299
- } else if (!userStored || userVal !== staged) return true;
1300
- }
1301
- return false;
1302
- }, [
1303
- selects,
1304
- numbers,
1305
- toggles,
1306
- snap
1307
- ]);
1308
- const edit = (0, react.useCallback)((field, text) => {
1309
- setFailed(false);
1310
- setNumbers((prev) => ({
1311
- ...prev,
1312
- [field]: text
1313
- }));
1314
- }, []);
1315
- const editSelect = (0, react.useCallback)((field, value) => {
1316
- setFailed(false);
1317
- setSelects((prev) => ({
1318
- ...prev,
1319
- [field]: value
1320
- }));
1321
- }, []);
1322
- const onToggle = (0, react.useCallback)((field, checked) => {
1323
- setFailed(false);
1324
- setToggles((prev) => ({
1325
- ...prev,
1326
- [field]: checked
1327
- }));
1328
- }, []);
1329
- const resetField = (0, react.useCallback)((field) => {
1330
- setFailed(false);
1331
- setNumbers((prev) => ({
1332
- ...prev,
1333
- [field]: ""
1334
- }));
1335
- setSelects((prev) => ({
1336
- ...prev,
1337
- [field]: null
1338
- }));
1339
- setToggles((prev) => ({
1340
- ...prev,
1341
- [field]: null
1342
- }));
1343
- }, []);
1344
- const save = (0, react.useCallback)(async () => {
1345
- if (!writable || invalid) return;
1346
- setSaving(true);
1347
- setFailed(false);
1348
- try {
1349
- for (const [field, staged] of Object.entries(numbers)) {
1350
- const baseVal = revertValue$1(snap, field);
1351
- const trimmed = staged.trim();
1352
- if (trimmed === "" || Number(trimmed) === baseVal) {
1353
- if (hasOwn(snap.user, field)) await scope.unset(field);
1354
- } else await scope.set(field, Number(trimmed));
1355
- }
1356
- for (const [field, staged] of Object.entries(selects)) {
1357
- const baseVal = String(revertValue$1(snap, field) ?? "");
1358
- if (staged === null || staged === baseVal) {
1359
- if (hasOwn(snap.user, field)) await scope.unset(field);
1360
- } else await scope.set(field, staged);
1361
- }
1362
- for (const [field, staged] of Object.entries(toggles)) {
1363
- const baseVal = Boolean(revertValue$1(snap, field));
1364
- if (staged === null || staged === baseVal) {
1365
- if (hasOwn(snap.user, field)) await scope.unset(field);
1366
- } else await scope.set(field, staged);
1367
- }
1368
- } catch {
1369
- setFailed(true);
1370
- } finally {
1371
- setSaving(false);
1372
- setNumbers({});
1373
- setSelects({});
1374
- setToggles({});
1375
- }
1376
- }, [
1377
- scope,
1378
- writable,
1379
- invalid,
1380
- numbers,
1381
- selects,
1382
- toggles,
1383
- snap
1384
- ]);
1385
- const discard = (0, react.useCallback)(() => {
1386
- setFailed(false);
1387
- setNumbers({});
1388
- setSelects({});
1389
- setToggles({});
1390
- }, []);
1391
- return {
1392
- shell: {
1393
- available: snap.status === "ready",
1394
- writable,
1395
- dirty: isDirty,
1396
- invalid,
1397
- saving,
1398
- failed
1399
- },
1400
- timing,
1401
- output,
1402
- strategySelect,
1403
- byteSelect,
1404
- byteNumbers,
1405
- lineSelect,
1406
- lineNumbers,
1407
- toggles: togglesView,
1408
- actions: {
1409
- edit,
1410
- resetField,
1411
- save,
1412
- discard
1413
- },
1414
- editSelect,
1415
- onToggle
1416
- };
1417
- }
1418
- function renderNumber$1(def, numbers, value, snap) {
1419
- const hasStaged = Object.prototype.hasOwnProperty.call(numbers, def.field);
1420
- const staged = numbers[def.field];
1421
- const resolved = value[def.field];
1422
- const text = numberText$1(staged, hasStaged, typeof resolved === "boolean" ? void 0 : resolved, snap, def.field);
1423
- const baseVal = revertValue$1(snap, def.field);
1424
- const userStored = hasOwn(snap.user, def.field);
1425
- let overridden = false;
1426
- let invalid = false;
1427
- if (hasStaged) {
1428
- const trimmed = (staged ?? "").trim();
1429
- if (trimmed === "") overridden = false;
1430
- else {
1431
- const num = Number(trimmed);
1432
- if (Number.isFinite(num)) overridden = num !== baseVal;
1433
- else invalid = true;
1434
- }
1435
- } else overridden = userStored;
1436
- return {
1437
- field: def.field,
1438
- labelKey: def.labelKey,
1439
- hintKey: def.hintKey,
1440
- text,
1441
- overridden,
1442
- invalid
1443
- };
1444
- }
1445
- function renderSelect$1(def, selects, value, snap) {
1446
- const hasStaged = Object.prototype.hasOwnProperty.call(selects, def.field);
1447
- const staged = selects[def.field];
1448
- const resolved = value[def.field];
1449
- const baseVal = String(revertValue$1(snap, def.field) ?? "");
1450
- const v = hasStaged ? staged ?? baseVal : typeof resolved === "string" ? resolved : baseVal;
1451
- const userStored = hasOwn(snap.user, def.field);
1452
- let overridden = false;
1453
- if (hasStaged) {
1454
- if (staged === null || staged === baseVal) overridden = false;
1455
- else overridden = true;
1456
- } else overridden = userStored;
1457
- return {
1458
- field: def.field,
1459
- labelKey: def.labelKey,
1460
- hintKey: def.hintKey,
1461
- options: def.options,
1462
- value: v,
1463
- overridden
1464
- };
1465
- }
1466
- function BashPlusCard(props) {
1467
- const { t, form: configForm } = props;
1468
- const form = useBashPlusForm(configForm);
1469
- const [open, setOpen] = (0, react.useState)(false);
1470
- const [openSelect, setOpenSelect] = (0, react.useState)(null);
1471
- (0, react.useEffect)(() => {
1472
- injectCardCss();
1473
- injectSettingsRowsCss();
1474
- }, []);
1475
- if (!form.shell.available) return null;
1476
- const blocked = !form.shell.dirty || form.shell.invalid || form.shell.saving;
1477
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1478
- "data-plugin-settings": "tool-plus",
1479
- className: "tp-card" + (open ? " tp-open" : ""),
1480
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1481
- type: "button",
1482
- className: "tp-header",
1483
- "aria-expanded": open,
1484
- "aria-label": `${t(open ? "collapse" : "expand")}: ${t("title")}`,
1485
- onClick: () => setOpen((o) => !o),
1486
- children: [
1487
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1488
- className: "tp-headText",
1489
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1490
- className: "tp-name",
1491
- children: t("title")
1492
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1493
- className: "tp-desc",
1494
- children: t("description")
1495
- })]
1496
- }),
1497
- form.shell.dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1498
- className: "tp-pending",
1499
- role: "status",
1500
- children: t("unsaved")
1501
- }) : null,
1502
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1503
- "aria-hidden": "true",
1504
- className: "tp-chevron" + (open ? " tp-open" : ""),
1505
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
1506
- width: "14",
1507
- height: "14",
1508
- viewBox: "0 0 14 14",
1509
- fill: "none",
1510
- "aria-hidden": "true",
1511
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
1512
- d: "M3.5 5.25 L7 8.75 L10.5 5.25",
1513
- stroke: "currentColor",
1514
- strokeWidth: "1.5",
1515
- strokeLinecap: "round",
1516
- strokeLinejoin: "round"
1517
- })
1518
- })
1519
- })
1520
- ]
1521
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1522
- className: "tp-body" + (open ? " tp-open" : ""),
1523
- "aria-hidden": !open,
1524
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1525
- className: "tp-bodyInner",
1526
- children: [
1527
- !form.shell.writable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1528
- className: "tp-readOnly",
1529
- role: "status",
1530
- children: t("readOnly")
1531
- }) : null,
1532
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1533
- className: "tp-group",
1534
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
1535
- className: "tp-groupTitle",
1536
- children: t("groupBehavior")
1537
- }), form.toggles.map((c) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleRow, {
1538
- t,
1539
- control: c,
1540
- disabled: !form.shell.writable,
1541
- onToggle: form.onToggle,
1542
- onReset: form.actions.resetField
1543
- }, c.field))]
1544
- }),
1545
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1546
- className: "tp-group",
1547
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
1548
- className: "tp-groupTitle",
1549
- children: t("groupTiming")
1550
- }), form.timing.map((c) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NumberRow, {
1551
- t,
1552
- control: c,
1553
- disabled: !form.shell.writable,
1554
- onEdit: form.actions.edit,
1555
- onReset: form.actions.resetField
1556
- }, c.field))]
1557
- }),
1558
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1559
- className: "tp-group",
1560
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
1561
- className: "tp-groupTitle",
1562
- children: t("groupOutput")
1563
- }), form.output.map((c) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NumberRow, {
1564
- t,
1565
- control: c,
1566
- disabled: !form.shell.writable,
1567
- onEdit: form.actions.edit,
1568
- onReset: form.actions.resetField
1569
- }, c.field))]
1570
- }),
1571
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1572
- className: "tp-group",
1573
- children: [
1574
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
1575
- className: "tp-groupTitle",
1576
- children: t("groupTruncation")
1577
- }),
1578
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectRow, {
1579
- t,
1580
- control: form.strategySelect,
1581
- disabled: !form.shell.writable,
1582
- open: openSelect === form.strategySelect.field,
1583
- onOpenChange: setOpenSelect,
1584
- onEdit: form.editSelect,
1585
- onReset: form.actions.resetField
1586
- }),
1587
- form.strategySelect.value !== "lines" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
1588
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NumberRow, {
1589
- t,
1590
- control: form.byteNumbers[0],
1591
- disabled: !form.shell.writable,
1592
- onEdit: form.actions.edit,
1593
- onReset: form.actions.resetField
1594
- }),
1595
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectRow, {
1596
- t,
1597
- control: form.byteSelect,
1598
- disabled: !form.shell.writable,
1599
- open: openSelect === form.byteSelect.field,
1600
- onOpenChange: setOpenSelect,
1601
- onEdit: form.editSelect,
1602
- onReset: form.actions.resetField
1603
- }),
1604
- form.byteSelect.value !== "tail" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NumberRow, {
1605
- t,
1606
- control: form.byteNumbers[1],
1607
- disabled: !form.shell.writable,
1608
- onEdit: form.actions.edit,
1609
- onReset: form.actions.resetField
1610
- }) : null,
1611
- form.byteSelect.value !== "head" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NumberRow, {
1612
- t,
1613
- control: form.byteNumbers[2],
1614
- disabled: !form.shell.writable,
1615
- onEdit: form.actions.edit,
1616
- onReset: form.actions.resetField
1617
- }) : null
1618
- ] }) : null,
1619
- form.strategySelect.value === "lines" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
1620
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NumberRow, {
1621
- t,
1622
- control: form.lineNumbers[0],
1623
- disabled: !form.shell.writable,
1624
- onEdit: form.actions.edit,
1625
- onReset: form.actions.resetField
1626
- }),
1627
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectRow, {
1628
- t,
1629
- control: form.lineSelect,
1630
- disabled: !form.shell.writable,
1631
- open: openSelect === form.lineSelect.field,
1632
- onOpenChange: setOpenSelect,
1633
- onEdit: form.editSelect,
1634
- onReset: form.actions.resetField
1635
- }),
1636
- form.lineSelect.value !== "tail" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NumberRow, {
1637
- t,
1638
- control: form.lineNumbers[1],
1639
- disabled: !form.shell.writable,
1640
- onEdit: form.actions.edit,
1641
- onReset: form.actions.resetField
1642
- }) : null,
1643
- form.lineSelect.value !== "head" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NumberRow, {
1644
- t,
1645
- control: form.lineNumbers[2],
1646
- disabled: !form.shell.writable,
1647
- onEdit: form.actions.edit,
1648
- onReset: form.actions.resetField
1649
- }) : null
1650
- ] }) : null
1651
- ]
1652
- }),
1653
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1654
- className: "tp-footer",
1655
- children: [
1656
- form.shell.failed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1657
- className: "tp-failed",
1658
- role: "status",
1659
- children: t("saveFailed")
1660
- }) : null,
1661
- !form.shell.failed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1662
- className: "tp-applies",
1663
- children: t("appliesTo")
1664
- }) : null,
1665
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1666
- type: "button",
1667
- className: "tp-discard",
1668
- disabled: !form.shell.dirty || form.shell.saving,
1669
- onClick: form.actions.discard,
1670
- children: t("discard")
1671
- }),
1672
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1673
- type: "button",
1674
- className: "tp-save",
1675
- disabled: blocked,
1676
- onClick: () => void form.actions.save(),
1677
- children: form.shell.saving ? t("saving") : t("save")
1678
- })
1679
- ]
1680
- })
1681
- ]
1682
- })
1683
- })]
1684
- });
1685
- }
1686
- //#endregion
1687
- //#region src/tools/shared/browser-rpc-channel.ts
1688
- /**
1689
- * Shared RPC channel constants for the browser-detection round trip between
1690
- * the settings panel (client half) and the host. Both halves import this
1691
- * module so the channel/endpoint never drift.
1692
- *
1693
- * The channel rides the official `@deepseek-ai/dsh-client-connection` generic
1694
- * RPC transport (host registers with `ctx.connection.rpc.handle`; the client
1695
- * calls via `createWebConnectionRpc().call`). Channel names must match
1696
- * `/^\/[A-Za-z0-9._~-]+$/` and `/api` is reserved — `/tool-plus` is ours.
1697
- * @module @xiaoso/dsh-tool-plus/browser-rpc-channel
1698
- */
1699
- /** Logical RPC channel registered by the host half of this plugin. */
1700
- const TOOL_PLUS_RPC_CHANNEL = "/tool-plus";
1701
- /** Endpoint that reports the rmSafe injection status (query = ensure + verify). */
1702
- const RM_SAFE_STATUS_ENDPOINT = "rmSafe/status";
1703
- /** Endpoint that lists agent presets with their status (roster + conflicts + template diff). */
1704
- const PRESET_STATUS_ENDPOINT = "presets/status";
1705
- /**
1706
- * The preset ids this plugin **declares**. Single source of truth for both
1707
- * halves: the host picks its own declarations out of the profile's composed
1708
- * configuration, and the panel uses the list to tell "ours" from everything else.
1709
- *
1710
- * They ship as `@deepseek-ai/dsh-agent-preset` declaration rows in this
1711
- * package's own bundle patches (`presets/<id>.patch.yml`), so installing the
1712
- * plugin as a profile bundle declares them: no install step, no file copy, and
1713
- * nothing written at startup.
1714
- */
1715
- const BUNDLED_PRESET_IDS = ["tool-plus-standard", "tool-plus-ptc"];
1716
- /** Endpoint that applies one preset action to the active profile. */
1717
- const PRESET_ACTION_ENDPOINT = "presets/apply";
1718
- /** Endpoint that compares one preset against one of our declarations (read-only). */
1719
- const PRESET_COMPARE_ENDPOINT = "presets/compare";
1720
- //#endregion
1721
- //#region src/client/web-connection-rpc.ts
1722
- /**
1723
- * Browser caller for the plugin's Connection RPC channel.
1724
- *
1725
- * The official `@deepseek-ai/dsh-client-connection/client` package ships the
1726
- * protocol primitives as types (`RpcFetch`, `ClientConnectionRpc`, `RpcResult`)
1727
- * and the `RpcId` brand, but its published `createWebConnectionRpc` factory is
1728
- * internal to the bundle — not re-exported. This module re-implements that
1729
- * factory against the same public wire contract (docs/api-gateway.md, the
1730
- * client `rpc.ts` source): a `POST {origin}/{channel}/{endpoint}` with a
1731
- * `client-request` JSON envelope, answered by a `server-response` envelope
1732
- * echoing the rpcId. Host-side registration uses the official
1733
- * `ctx.connection.rpc.handle` — this file only mirrors the caller half.
1734
- * @module @xiaoso/dsh-tool-plus/client/web-connection-rpc
1735
- */
1736
- const INTERNAL_BASE = "http://dsh.internal";
1737
- const CHANNEL_PATTERN = /^\/[A-Za-z0-9._~-]+$/;
1738
- const ENDPOINT_SEGMENT_PATTERN = /^[A-Za-z0-9_$.-]+$/;
1739
- function randomUuid() {
1740
- const g = globalThis;
1741
- if (g.crypto?.randomUUID !== void 0) return g.crypto.randomUUID();
1742
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
1743
- const r = Math.random() * 16 | 0;
1744
- return (c === "x" ? r : r & 3 | 8).toString(16);
1745
- });
1746
- }
1747
- function isRecord$4(value) {
1748
- return typeof value === "object" && value !== null && !Array.isArray(value);
1749
- }
1750
- function parseConnectionResponse(value) {
1751
- if (!isRecord$4(value) || value.type !== "server-response" || typeof value.rpcId !== "string") throw new TypeError("connection: invalid server-response envelope");
1752
- const result = value.result;
1753
- if (!isRecord$4(result)) throw new TypeError("connection: invalid server-response result");
1754
- if (result.ok === true) return {
1755
- rpcId: value.rpcId,
1756
- result: {
1757
- ok: true,
1758
- value: result.value
1759
- }
1760
- };
1761
- if (result.ok !== false || !isRecord$4(result.error)) throw new TypeError("connection: invalid server-response result");
1762
- const error = result.error;
1763
- if (typeof error.code !== "string" || typeof error.message !== "string" || !isRecord$4(error.details)) throw new TypeError("connection: invalid server-response failure");
1764
- return {
1765
- rpcId: value.rpcId,
1766
- result: {
1767
- ok: false,
1768
- error: {
1769
- code: error.code,
1770
- message: error.message,
1771
- details: error.details
1772
- }
1773
- }
1774
- };
1775
- }
1776
- function resolveBase() {
1777
- const location = globalThis.location;
1778
- return location?.origin !== void 0 && location.origin !== "null" ? location.origin : INTERNAL_BASE;
1779
- }
1780
- function assertTarget(channel, endpoint) {
1781
- const segments = endpoint.split("/");
1782
- if (!CHANNEL_PATTERN.test(channel) || segments.some((segment) => segment === "" || segment === "." || segment === ".." || !ENDPOINT_SEGMENT_PATTERN.test(segment))) throw new Error(`connection: invalid RPC target ${JSON.stringify(`${channel}/${endpoint}`)}`);
1783
- }
1784
- /**
1785
- * Create the browser-backed generic RPC caller for the plugin's channel
1786
- * (the `/tool-plus` host registration). Wire-compatible with the official
1787
- * ClientConnectionRpc contract; only the transport override differs.
1788
- * @param doFetch - transport override; defaults to the page's global fetch.
1789
- */
1790
- function createWebConnectionRpc(doFetch) {
1791
- const send = doFetch ?? ((input, init) => globalThis.fetch(input, init));
1792
- return { async call(channel, endpoint, payload, signal) {
1793
- assertTarget(channel, endpoint);
1794
- const rpcId = (0, _deepseek_ai_dsh_client_connection_client.RpcId)(randomUuid());
1795
- const response = await send(new URL(`${channel}/${endpoint}`, resolveBase()), {
1796
- method: "POST",
1797
- headers: { "content-type": "application/json" },
1798
- body: JSON.stringify({
1799
- type: "client-request",
1800
- rpcId,
1801
- method: endpoint,
1802
- payload
1803
- }),
1804
- ...signal === void 0 ? {} : { signal }
1805
- });
1806
- if (!response.ok) throw new Error(`transport failure for ${channel}/${endpoint}: HTTP ${response.status}`);
1807
- const full = parseConnectionResponse(await response.json());
1808
- if (full.rpcId !== rpcId) throw new Error(`rpcId mismatch for ${endpoint}: sent ${rpcId}, got ${full.rpcId}`);
1809
- return full.result;
1810
- } };
1811
- }
1812
- //#endregion
1813
1202
  //#region src/client/forms.ts
1814
1203
  /**
1815
1204
  * Generic staged-form model over the plugin's flat settings namespace,
@@ -6472,7 +5861,7 @@ window.__ModuleLoader__.load({
6472
5861
  * spells the same value the Host plugin registers (src/config/settings.ts).
6473
5862
  */
6474
5863
  const BASH_PLUS_CLIENT_NS = "tool-plus";
6475
- /** Locale dictionary namespace of this package's card copy. */
5864
+ /** Locale dictionary namespace of this package's page copy. */
6476
5865
  const BASH_PLUS_LOCALE_NS = "tool-plus";
6477
5866
  /** Required services (cordis fiber inject): slots + locale + the shared configuration forms. */
6478
5867
  const inject = [
@@ -6481,24 +5870,15 @@ window.__ModuleLoader__.load({
6481
5870
  "configForms"
6482
5871
  ];
6483
5872
  /**
6484
- * Mount the tool-plus settings surfaces.
5873
+ * Mount the tool-plus settings page.
6485
5874
  * @param ctx - the browser plugin context.
6486
5875
  */
6487
5876
  function apply(ctx) {
6488
5877
  ctx.effect(() => ctx.locale.register(BASH_PLUS_LOCALE_NS, {
6489
5878
  zh: zh$1,
6490
5879
  en: en$1
6491
- }), "tool-plus: card dictionaries");
6492
- const cardForm = ctx.configForms.get(BASH_PLUS_CLIENT_NS);
6493
- const sectionForm = ctx.configForms.get(BASH_PLUS_CLIENT_NS);
6494
- ctx.effect(() => ctx.configForms.whileServed([BASH_PLUS_CLIENT_NS], () => ctx.slots.inject("settings.plugins.tab", () => ctx.slots.register({
6495
- name: "settings.plugins.tab",
6496
- id: BASH_PLUS_CLIENT_NS,
6497
- order: 20,
6498
- label: () => ctx.locale.bind(BASH_PLUS_LOCALE_NS)("title"),
6499
- locale: BASH_PLUS_LOCALE_NS,
6500
- inject: () => ({ form: cardForm })
6501
- }, BashPlusCard))), "tool-plus: plugins page");
5880
+ }), "tool-plus: page dictionaries");
5881
+ const form = ctx.configForms.get(BASH_PLUS_CLIENT_NS);
6502
5882
  registerToolCards(ctx);
6503
5883
  ctx.effect(() => ctx.configForms.whileServed([BASH_PLUS_CLIENT_NS], () => ctx.slots.inject("settings.section", () => ctx.slots.register({
6504
5884
  name: "settings.section",
@@ -6506,7 +5886,7 @@ window.__ModuleLoader__.load({
6506
5886
  order: 16,
6507
5887
  label: () => ctx.locale.bind(BASH_PLUS_LOCALE_NS)("nav"),
6508
5888
  locale: BASH_PLUS_LOCALE_NS,
6509
- inject: () => ({ form: sectionForm })
5889
+ inject: () => ({ form })
6510
5890
  }, ToolPlusSection))), "tool-plus: settings page");
6511
5891
  }
6512
5892
  //#endregion