dsh-prompt-customizer 0.2.3 → 0.2.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/client/client.js +120 -20
- package/client/client.js.map +1 -1
- package/lib/index.js +37 -8
- package/lib/schema.js +5 -1
- package/package.json +1 -1
- package/src/client/SectionsTab.tsx +23 -7
- package/src/client/locales.ts +6 -2
- package/src/client/presets.ts +86 -16
- package/src/client/styles.ts +2 -0
- package/src/client/types.ts +2 -2
package/client/client.js
CHANGED
|
@@ -19,8 +19,10 @@ window.__ModuleLoader__.load({
|
|
|
19
19
|
blockedOff: "未屏蔽",
|
|
20
20
|
blockedOn: "已屏蔽",
|
|
21
21
|
replaced: "已替换",
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
manual: "手动",
|
|
23
|
+
system: "系统",
|
|
24
|
+
replace: "编辑",
|
|
25
|
+
clearReplace: "清除",
|
|
24
26
|
injectNew: "注入新段",
|
|
25
27
|
name: "名称",
|
|
26
28
|
order: "顺序",
|
|
@@ -57,6 +59,8 @@ window.__ModuleLoader__.load({
|
|
|
57
59
|
blockedOff: "Active",
|
|
58
60
|
blockedOn: "Blocked",
|
|
59
61
|
replaced: "Replaced",
|
|
62
|
+
manual: "Manual",
|
|
63
|
+
system: "System",
|
|
60
64
|
replace: "Replace",
|
|
61
65
|
clearReplace: "Clear",
|
|
62
66
|
injectNew: "Inject section",
|
|
@@ -196,6 +200,20 @@ window.__ModuleLoader__.load({
|
|
|
196
200
|
borderRadius: 999,
|
|
197
201
|
padding: "0 6px"
|
|
198
202
|
},
|
|
203
|
+
badgeCustom: {
|
|
204
|
+
color: "#58a6ff",
|
|
205
|
+
fontSize: 11,
|
|
206
|
+
border: "1px solid rgba(88,166,255,.5)",
|
|
207
|
+
borderRadius: 999,
|
|
208
|
+
padding: "0 6px"
|
|
209
|
+
},
|
|
210
|
+
badgeSystem: {
|
|
211
|
+
color: "rgba(128,128,128,.85)",
|
|
212
|
+
fontSize: 11,
|
|
213
|
+
border: "1px solid rgba(128,128,128,.4)",
|
|
214
|
+
borderRadius: 999,
|
|
215
|
+
padding: "0 6px"
|
|
216
|
+
},
|
|
199
217
|
mini: {
|
|
200
218
|
padding: "3px 8px",
|
|
201
219
|
border: "1px solid rgba(128,128,128,.35)",
|
|
@@ -282,22 +300,41 @@ window.__ModuleLoader__.load({
|
|
|
282
300
|
})();
|
|
283
301
|
//#endregion
|
|
284
302
|
//#region src/client/presets.ts
|
|
285
|
-
/**
|
|
303
|
+
/**
|
|
304
|
+
* Merge the inventory sections with the user's inject list, sorted by order.
|
|
305
|
+
* Sections read from the inventory (other plugins) are `system`; sections this
|
|
306
|
+
* plugin generated carry a hidden `custom` marker and are `custom`. The source
|
|
307
|
+
* is decided by the hidden marker — never by name collisions — so a custom
|
|
308
|
+
* section keeps its identity (and its deletability) even when its name happens
|
|
309
|
+
* to match an inventory section, and across preset switches. Custom sections
|
|
310
|
+
* always render their own text; they can never be "<动态生成>".
|
|
311
|
+
*/
|
|
286
312
|
function mergeSections(inv, cfg, blockedNames) {
|
|
287
313
|
const map = /* @__PURE__ */ new Map();
|
|
288
|
-
for (const sec of inv?.sections ?? []) map.set(sec.name,
|
|
314
|
+
for (const sec of inv?.sections ?? []) map.set(sec.name, {
|
|
315
|
+
...sec,
|
|
316
|
+
source: "system"
|
|
317
|
+
});
|
|
289
318
|
for (const item of cfg.inject ?? []) {
|
|
319
|
+
const isCustom = item.custom === true;
|
|
290
320
|
const existing = map.get(item.name);
|
|
291
|
-
if (existing) map.set(item.name, {
|
|
321
|
+
if (existing) map.set(item.name, isCustom ? {
|
|
322
|
+
...existing,
|
|
323
|
+
order: item.order,
|
|
324
|
+
text: item.text ?? "",
|
|
325
|
+
source: "custom"
|
|
326
|
+
} : {
|
|
292
327
|
...existing,
|
|
293
|
-
order: item.order
|
|
328
|
+
order: item.order,
|
|
329
|
+
source: "system"
|
|
294
330
|
});
|
|
295
331
|
else map.set(item.name, {
|
|
296
332
|
name: item.name,
|
|
297
333
|
order: item.order,
|
|
298
|
-
text: item.text
|
|
334
|
+
text: item.text ?? "",
|
|
299
335
|
active: !blockedNames.has(item.name),
|
|
300
|
-
replaced: false
|
|
336
|
+
replaced: false,
|
|
337
|
+
source: isCustom ? "custom" : "system"
|
|
301
338
|
});
|
|
302
339
|
}
|
|
303
340
|
return [...map.values()].sort((a, b) => a.order - b.order);
|
|
@@ -312,9 +349,11 @@ window.__ModuleLoader__.load({
|
|
|
312
349
|
const list = presetOrder ?? [];
|
|
313
350
|
const afterMap = /* @__PURE__ */ new Map();
|
|
314
351
|
const textMap = /* @__PURE__ */ new Map();
|
|
352
|
+
const customMap = /* @__PURE__ */ new Map();
|
|
315
353
|
for (const sec of list) {
|
|
316
354
|
afterMap.set(sec.name, sec.after);
|
|
317
355
|
textMap.set(sec.name, sec.text);
|
|
356
|
+
customMap.set(sec.name, sec.custom === true);
|
|
318
357
|
}
|
|
319
358
|
const result = [];
|
|
320
359
|
const placed = /* @__PURE__ */ new Set();
|
|
@@ -345,7 +384,8 @@ window.__ModuleLoader__.load({
|
|
|
345
384
|
return result.map((name, i) => ({
|
|
346
385
|
name,
|
|
347
386
|
order: i,
|
|
348
|
-
text: textMap.get(name) ?? ""
|
|
387
|
+
text: textMap.get(name) ?? "",
|
|
388
|
+
custom: customMap.get(name)
|
|
349
389
|
}));
|
|
350
390
|
}
|
|
351
391
|
/**
|
|
@@ -358,7 +398,8 @@ window.__ModuleLoader__.load({
|
|
|
358
398
|
const order = merged.map((sec, i) => ({
|
|
359
399
|
name: sec.name,
|
|
360
400
|
after: i > 0 ? merged[i - 1].name : void 0,
|
|
361
|
-
text: sec.text ?? ""
|
|
401
|
+
text: sec.source === "custom" ? sec.text ?? "" : "",
|
|
402
|
+
custom: sec.source === "custom"
|
|
362
403
|
}));
|
|
363
404
|
return {
|
|
364
405
|
sections: cfg.sections,
|
|
@@ -374,11 +415,29 @@ window.__ModuleLoader__.load({
|
|
|
374
415
|
* - current sections NOT in the preset's ordered list are disabled by default
|
|
375
416
|
* - only the preset's ACTIVE sections (in the order list but not in its block
|
|
376
417
|
* list) are unblocked; the preset's own blocked sections stay blocked.
|
|
418
|
+
*
|
|
419
|
+
* Custom injected sections that exist in the current config but are NOT in the
|
|
420
|
+
* preset's ordered list are preserved in the resulting inject list (so they
|
|
421
|
+
* stay visible) and disabled (added to the blocked set), rather than silently
|
|
422
|
+
* dropped — a preset must not make the user's own injected sections vanish.
|
|
377
423
|
*/
|
|
378
424
|
function applyPresetData(data, cfg, currentNames) {
|
|
379
425
|
const presetOrder = data.order ?? [];
|
|
380
426
|
const presetNames = new Set(presetOrder.map((x) => x.name));
|
|
381
427
|
const blocked = new Set(data.sections ?? []);
|
|
428
|
+
const inject = resolveOrder(presetOrder);
|
|
429
|
+
const kept = new Set(inject.map((x) => x.name));
|
|
430
|
+
let order = inject.length;
|
|
431
|
+
for (const item of cfg.inject ?? []) {
|
|
432
|
+
if (!item || !item.name || presetNames.has(item.name) || kept.has(item.name)) continue;
|
|
433
|
+
kept.add(item.name);
|
|
434
|
+
inject.push({
|
|
435
|
+
name: item.name,
|
|
436
|
+
order: order++,
|
|
437
|
+
text: item.text ?? "",
|
|
438
|
+
custom: item.custom === true
|
|
439
|
+
});
|
|
440
|
+
}
|
|
382
441
|
if (presetOrder.length > 0) {
|
|
383
442
|
const activeNames = new Set([...presetNames].filter((n) => !blocked.has(n)));
|
|
384
443
|
for (const name of currentNames) if (!presetNames.has(name)) blocked.add(name);
|
|
@@ -390,13 +449,35 @@ window.__ModuleLoader__.load({
|
|
|
390
449
|
...cfg.replace ?? {},
|
|
391
450
|
...data.replace ?? {}
|
|
392
451
|
},
|
|
393
|
-
inject
|
|
452
|
+
inject,
|
|
394
453
|
tools: {
|
|
395
454
|
exclude: data.tools?.exclude ?? [],
|
|
396
455
|
include: data.tools?.include ?? []
|
|
397
456
|
}
|
|
398
457
|
};
|
|
399
458
|
}
|
|
459
|
+
/**
|
|
460
|
+
* Remove a custom (plugin-added) section from the config: it disappears from
|
|
461
|
+
* the inject list, is no longer forced into the blocked set, and any replace
|
|
462
|
+
* text for it is cleared. The remaining inject entries are re-indexed to a
|
|
463
|
+
* contiguous 0..n-1 order (the virtual index model), so removal never leaves
|
|
464
|
+
* holes. System sections read from the host inventory cannot be removed this
|
|
465
|
+
* way (they would just come back on the next inventory read).
|
|
466
|
+
*/
|
|
467
|
+
function removeSection(name, cfg) {
|
|
468
|
+
const sections = (cfg.sections ?? []).filter((n) => n !== name);
|
|
469
|
+
const inject = (cfg.inject ?? []).filter((item) => item.name !== name).map((item, i) => ({
|
|
470
|
+
...item,
|
|
471
|
+
order: i
|
|
472
|
+
}));
|
|
473
|
+
const replace = { ...cfg.replace ?? {} };
|
|
474
|
+
delete replace[name];
|
|
475
|
+
return {
|
|
476
|
+
sections,
|
|
477
|
+
inject,
|
|
478
|
+
replace
|
|
479
|
+
};
|
|
480
|
+
}
|
|
400
481
|
/** Whether a tool is hidden under the current include/exclude config. */
|
|
401
482
|
function isToolHidden(name, toolsCfg) {
|
|
402
483
|
const include = toolsCfg?.include ?? [];
|
|
@@ -409,11 +490,15 @@ window.__ModuleLoader__.load({
|
|
|
409
490
|
const exclude = toolsCfg?.exclude ?? [];
|
|
410
491
|
if (include.length > 0) {
|
|
411
492
|
const next = include.slice();
|
|
412
|
-
if (currentlyHidden)
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
493
|
+
if (currentlyHidden) {
|
|
494
|
+
next.push(name);
|
|
495
|
+
return {
|
|
496
|
+
exclude: exclude.filter((n) => n !== name),
|
|
497
|
+
include: next
|
|
498
|
+
};
|
|
416
499
|
}
|
|
500
|
+
const i = next.indexOf(name);
|
|
501
|
+
if (i >= 0) next.splice(i, 1);
|
|
417
502
|
return {
|
|
418
503
|
exclude,
|
|
419
504
|
include: next
|
|
@@ -513,14 +598,22 @@ window.__ModuleLoader__.load({
|
|
|
513
598
|
const persistOrder = (ordered) => {
|
|
514
599
|
const list = ordered.map((sec, i) => {
|
|
515
600
|
const existing = (cfg.inject ?? []).find((x) => x.name === sec.name);
|
|
601
|
+
const isCustom = sec.source === "custom";
|
|
516
602
|
return {
|
|
517
603
|
name: sec.name,
|
|
518
604
|
order: i,
|
|
519
|
-
text:
|
|
605
|
+
text: isCustom ? sec.text ?? "" : existing?.text ?? "",
|
|
606
|
+
custom: isCustom
|
|
520
607
|
};
|
|
521
608
|
});
|
|
522
609
|
scope.set("inject", list);
|
|
523
610
|
};
|
|
611
|
+
const removeCustom = (name) => {
|
|
612
|
+
const patch = removeSection(name, cfg);
|
|
613
|
+
scope.set("sections", patch.sections);
|
|
614
|
+
scope.set("inject", patch.inject);
|
|
615
|
+
scope.set("replace", patch.replace);
|
|
616
|
+
};
|
|
524
617
|
const moveOrder = (index, dir) => {
|
|
525
618
|
const target = index + dir;
|
|
526
619
|
if (target < 0 || target >= merged.length) return;
|
|
@@ -577,9 +670,10 @@ window.__ModuleLoader__.load({
|
|
|
577
670
|
const entry = {
|
|
578
671
|
name,
|
|
579
672
|
order,
|
|
580
|
-
text
|
|
673
|
+
text,
|
|
581
674
|
active: true,
|
|
582
|
-
replaced: false
|
|
675
|
+
replaced: false,
|
|
676
|
+
source: "custom"
|
|
583
677
|
};
|
|
584
678
|
const existing = next.findIndex((x) => x.name === name);
|
|
585
679
|
if (existing >= 0) next[existing] = entry;
|
|
@@ -624,6 +718,7 @@ window.__ModuleLoader__.load({
|
|
|
624
718
|
(0, react.createElement)("div", { style: s.rowBody }, [(0, react.createElement)("div", { style: s.rowTitle }, [
|
|
625
719
|
(0, react.createElement)("span", { style: s.code }, sec.name),
|
|
626
720
|
(0, react.createElement)("span", { style: s.orderTag }, "#" + index),
|
|
721
|
+
(0, react.createElement)("span", { style: sec.source === "custom" ? s.badgeCustom : s.badgeSystem }, sec.source === "custom" ? t("manual") : t("system")),
|
|
627
722
|
sec.replaced ? (0, react.createElement)("span", { style: s.badgeReplaced }, t("replaced")) : null
|
|
628
723
|
]), isEditing ? (0, react.createElement)("div", { style: s.editBox }, [(0, react.createElement)("textarea", {
|
|
629
724
|
style: s.editInput,
|
|
@@ -636,7 +731,7 @@ window.__ModuleLoader__.load({
|
|
|
636
731
|
}, t("add")), (0, react.createElement)("button", {
|
|
637
732
|
style: s.mini,
|
|
638
733
|
onClick: () => setEditing(null)
|
|
639
|
-
}, t("clearReplace"))])]) : (0, react.createElement)("div", { style: s.preview }, String(sec.text ?? "").slice(0, 140) || t("dynamic"))]),
|
|
734
|
+
}, t("clearReplace"))])]) : (0, react.createElement)("div", { style: s.preview }, String(sec.text ?? "").slice(0, 140) || (sec.source === "custom" ? t("empty") : t("dynamic")))]),
|
|
640
735
|
(0, react.createElement)("div", { style: s.arrowCol }, [(0, react.createElement)("button", {
|
|
641
736
|
style: s.arrow,
|
|
642
737
|
disabled: index === 0,
|
|
@@ -651,7 +746,12 @@ window.__ModuleLoader__.load({
|
|
|
651
746
|
(0, react.createElement)("button", {
|
|
652
747
|
style: s.mini,
|
|
653
748
|
onClick: () => replace[sec.name] ? clearReplace(sec.name) : startReplace(sec.name, String(sec.text ?? ""))
|
|
654
|
-
}, replace[sec.name] ? t("clearReplace") : t("replace"))
|
|
749
|
+
}, replace[sec.name] ? t("clearReplace") : t("replace")),
|
|
750
|
+
sec.source === "custom" ? (0, react.createElement)("button", {
|
|
751
|
+
style: s.mini,
|
|
752
|
+
onClick: () => removeCustom(sec.name),
|
|
753
|
+
title: t("delete")
|
|
754
|
+
}, t("delete")) : null
|
|
655
755
|
]);
|
|
656
756
|
}),
|
|
657
757
|
(0, react.createElement)("div", { style: s.injectBox }, [(0, react.createElement)("div", { style: s.rowTitle }, t("injectNew")), (0, react.createElement)(InjectForm, {
|
package/client/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["useState","h","h","useState","useRef","h","h","useState"],"sources":["../src/client/locales.ts","../src/client/styles.ts","../src/client/presets.ts","../src/client/SectionsTab.tsx","../src/client/ToolsTab.tsx","../src/client/PresetsTab.tsx","../src/client/index.ts"],"sourcesContent":["/** Locale dictionaries for the prompt-customizer panel. */\nexport const DICT = {\n zh: {\n nav: '提示词定制',\n loading: '加载中…',\n unavailable: '当前环境未启用本命名空间。',\n tabsSections: '提示词',\n tabsTools: '工具',\n tabsPresets: '预设',\n refresh: '刷新',\n blockedOff: '未屏蔽',\n blockedOn: '已屏蔽',\n replaced: '已替换',\n replace: '替换',\n clearReplace: '清除替换',\n injectNew: '注入新段',\n name: '名称',\n order: '顺序',\n text: '文本',\n add: '添加',\n hiddenOff: '可见',\n hiddenOn: '已隐藏',\n includeMode: '白名单模式:仅保留勾选项',\n excludeMode: '黑名单模式:勾选 = 隐藏',\n empty: '(空)',\n dynamic: '<动态生成>',\n drag: '拖动排序',\n moveUp: '上移',\n moveDown: '下移',\n savePreset: '保存当前为预设',\n presetName: '预设名称',\n save: '保存',\n importPreset: '导入预设',\n import: '导入',\n apply: '应用',\n export: '导出',\n delete: '删除',\n active: '使用中',\n preset: '预设',\n },\n en: {\n nav: 'Prompt Customizer',\n loading: 'Loading…',\n unavailable: 'Namespace unavailable in this environment.',\n tabsSections: 'Sections',\n tabsTools: 'Tools',\n tabsPresets: 'Presets',\n refresh: 'Refresh',\n blockedOff: 'Active',\n blockedOn: 'Blocked',\n replaced: 'Replaced',\n replace: 'Replace',\n clearReplace: 'Clear',\n injectNew: 'Inject section',\n name: 'Name',\n order: 'Order',\n text: 'Text',\n add: 'Add',\n hiddenOff: 'Visible',\n hiddenOn: 'Hidden',\n includeMode: 'Whitelist: keep only checked',\n excludeMode: 'Blacklist: check to hide',\n empty: '(empty)',\n dynamic: '<dynamic>',\n drag: 'Drag to reorder',\n moveUp: 'Move up',\n moveDown: 'Move down',\n savePreset: 'Save current as preset',\n presetName: 'Preset name',\n save: 'Save',\n importPreset: 'Import preset',\n import: 'Import',\n apply: 'Apply',\n export: 'Export',\n delete: 'Delete',\n active: 'Active',\n preset: 'Preset',\n },\n} as const\n\nexport type Translate = (key: string) => string\n","/** Inline styles shared by the prompt-customizer panel components. */\nexport const s = (() => {\n const flex = { display: 'flex', alignItems: 'center' }\n return {\n root: { fontFamily: 'inherit', fontSize: 13, lineHeight: 1.5 },\n bar: { ...flex, gap: 6, marginBottom: 8, flexWrap: 'wrap' },\n tab: { padding: '4px 10px', border: '1px solid rgba(128,128,128,.35)', borderRadius: 6, background: 'transparent', cursor: 'pointer', color: 'inherit', opacity: 0.75 },\n tabActive: { padding: '4px 10px', border: '1px solid rgba(128,128,128,.7)', borderRadius: 6, background: 'rgba(128,128,128,.18)', cursor: 'pointer', color: 'inherit' },\n refresh: { marginLeft: 'auto', padding: '4px 10px', border: '1px solid rgba(128,128,128,.35)', borderRadius: 6, background: 'transparent', cursor: 'pointer', color: 'inherit' },\n list: { display: 'flex', flexDirection: 'column', gap: 6 },\n row: { ...flex, gap: 8, padding: '6px 8px', border: '1px solid rgba(128,128,128,.2)', borderRadius: 8, background: 'rgba(128,128,128,.06)' },\n switchWrap: { ...flex, gap: 6, minWidth: 90 },\n rowBody: { flex: 1, minWidth: 0 },\n rowTitle: { ...flex, gap: 6, alignItems: 'baseline', flexWrap: 'wrap' },\n code: { fontFamily: 'monospace', fontSize: 12, wordBreak: 'break-all' },\n orderTag: { color: 'rgba(128,128,128,.8)', fontSize: 11 },\n preview: { color: 'rgba(128,128,128,.85)', fontSize: 12, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' },\n badgeOk: { color: '#3fb950', fontSize: 11, border: '1px solid rgba(63,185,80,.5)', borderRadius: 999, padding: '0 6px' },\n badgeBlocked: { color: '#f85149', fontSize: 11, border: '1px solid rgba(248,81,73,.5)', borderRadius: 999, padding: '0 6px' },\n badgeReplaced: { color: '#d29922', fontSize: 11, border: '1px solid rgba(210,153,34,.5)', borderRadius: 999, padding: '0 6px' },\n mini: { padding: '3px 8px', border: '1px solid rgba(128,128,128,.35)', borderRadius: 6, background: 'transparent', cursor: 'pointer', color: 'inherit', whiteSpace: 'nowrap' },\n arrowCol: { display: 'flex', flexDirection: 'column', gap: 2 },\n arrow: { padding: '0 6px', border: '1px solid rgba(128,128,128,.3)', borderRadius: 4, background: 'transparent', cursor: 'pointer', color: 'inherit', fontSize: 11, lineHeight: 1.4, opacity: 0.8 },\n rowDragging: { opacity: 0.4 },\n rowDropAbove: { boxShadow: '0 -2px 0 0 #58a6ff' },\n rowDropBelow: { boxShadow: '0 2px 0 0 #58a6ff' },\n dragHandle: { cursor: 'grab', color: 'rgba(128,128,128,.7)', fontSize: 13, padding: '0 2px', userSelect: 'none' },\n injectBox: { marginTop: 10, padding: '8px', border: '1px dashed rgba(128,128,128,.4)', borderRadius: 8, display: 'flex', flexDirection: 'column', gap: 6 },\n injectRow: { ...flex, gap: 6 },\n editBox: { display: 'flex', flexDirection: 'column', gap: 6, marginTop: 4 },\n editInput: { padding: '6px', border: '1px solid rgba(128,128,128,.35)', borderRadius: 6, background: 'transparent', color: 'inherit', fontFamily: 'inherit', fontSize: 12, resize: 'vertical', width: '100%', boxSizing: 'border-box' },\n input: { padding: '4px 6px', border: '1px solid rgba(128,128,128,.35)', borderRadius: 6, background: 'transparent', color: 'inherit' },\n muted: { color: 'rgba(128,128,128,.75)', fontSize: 12 },\n error: { color: '#f85149', fontSize: 12, marginBottom: 6 },\n }\n})()\n","/** Pure preset / section helpers shared by the client UI and the node tests. */\nimport type { Config, Inventory, Preset, PresetData } from './types.ts'\n\nexport interface Section {\n name: string\n order: number\n text?: string\n active: boolean\n replaced: boolean\n}\n\n/** The config fields a preset apply must write back to the settings scope. */\nexport interface ConfigPatch {\n sections: string[]\n replace: Record<string, string>\n inject: Array<{ name: string; order: number; text: string }>\n tools: { exclude: string[]; include: string[] }\n}\n\n/** Merge the inventory sections with the user's inject list, sorted by order. */\nexport function mergeSections(inv: Inventory | null, cfg: Config, blockedNames: ReadonlySet<string>): Section[] {\n const map = new Map<string, Section>()\n for (const sec of inv?.sections ?? []) map.set(sec.name, sec)\n for (const item of cfg.inject ?? []) {\n const existing = map.get(item.name)\n if (existing) map.set(item.name, { ...existing, order: item.order })\n else map.set(item.name, {\n name: item.name,\n order: item.order,\n text: item.text || '<动态生成>',\n active: !blockedNames.has(item.name),\n replaced: false,\n })\n }\n return [...map.values()].sort((a, b) => a.order - b.order)\n}\n\n/**\n * Resolve a preset's relative order into an absolute ordered list (0..n-1).\n * Sections with no anchor (or whose anchor is absent) come first in preset\n * order; each anchored section is inserted right after its anchor; any\n * remaining sections (cycles) are appended.\n */\nexport function resolveOrder(presetOrder: PresetData['order']): Array<{ name: string; order: number; text: string }> {\n const list = presetOrder ?? []\n const afterMap = new Map<string, string | undefined>()\n const textMap = new Map<string, string>()\n for (const sec of list) {\n afterMap.set(sec.name, sec.after)\n textMap.set(sec.name, sec.text)\n }\n\n const result: string[] = []\n const placed = new Set<string>()\n\n for (const sec of list) {\n const anchor = afterMap.get(sec.name)\n if (!anchor || !afterMap.has(anchor)) { result.push(sec.name); placed.add(sec.name) }\n }\n\n let changed = true\n while (changed) {\n changed = false\n for (const sec of list) {\n if (placed.has(sec.name)) continue\n const anchor = afterMap.get(sec.name)\n if (anchor && placed.has(anchor)) {\n result.splice(result.indexOf(anchor) + 1, 0, sec.name)\n placed.add(sec.name)\n changed = true\n }\n }\n }\n\n for (const sec of list) {\n if (!placed.has(sec.name)) { result.push(sec.name); placed.add(sec.name) }\n }\n\n return result.map((name, i) => ({ name, order: i, text: textMap.get(name) ?? '' }))\n}\n\n/**\n * Build a preset snapshot from the current config. The order list is derived\n * from the MERGED section list (not just `cfg.inject`) so that a preset saved\n * after only blocking sections (without reordering) still carries the full\n * ordered set, and thus a meaningful \"active list\" when applied.\n */\nexport function buildPresetData(cfg: Config, merged: Section[]): PresetData {\n const order = merged.map((sec, i) => ({\n name: sec.name,\n after: i > 0 ? merged[i - 1].name : undefined,\n text: sec.text ?? '',\n }))\n return {\n sections: cfg.sections,\n replace: cfg.replace,\n order,\n tools: cfg.tools,\n }\n}\n\n/**\n * Compute the config patch when applying a preset:\n * - same-name sections are overridden (the order list drives this at runtime)\n * - preset sections missing from the current prompt are added\n * - current sections NOT in the preset's ordered list are disabled by default\n * - only the preset's ACTIVE sections (in the order list but not in its block\n * list) are unblocked; the preset's own blocked sections stay blocked.\n */\nexport function applyPresetData(data: PresetData, cfg: Config, currentNames: ReadonlySet<string>): ConfigPatch {\n const presetOrder = data.order ?? []\n const presetNames = new Set(presetOrder.map((x) => x.name))\n const blocked = new Set(data.sections ?? [])\n\n // Only enforce the \"active set\" when the preset actually defines an order.\n // An empty order (e.g. a hand-authored / imported preset that only blocks a\n // few sections) must not default-disable every current section.\n if (presetOrder.length > 0) {\n const activeNames = new Set([...presetNames].filter((n) => !blocked.has(n)))\n for (const name of currentNames) if (!presetNames.has(name)) blocked.add(name)\n for (const name of activeNames) blocked.delete(name)\n }\n\n return {\n sections: [...blocked],\n replace: { ...(cfg.replace ?? {}), ...(data.replace ?? {}) },\n inject: resolveOrder(presetOrder),\n tools: { exclude: data.tools?.exclude ?? [], include: data.tools?.include ?? [] },\n }\n}\n\n// ── Tool filtering ──────────────────────────────────────────────────────────\n\nexport type ToolsCfg = { exclude?: string[]; include?: string[] }\n\n/** Whether a tool is hidden under the current include/exclude config. */\nexport function isToolHidden(name: string, toolsCfg: ToolsCfg | undefined): boolean {\n const include = toolsCfg?.include ?? []\n const exclude = toolsCfg?.exclude ?? []\n return include.length > 0 ? !include.includes(name) : exclude.includes(name)\n}\n\n/** Toggle a tool's hidden state, returning a new tools config. */\nexport function toggleTool(name: string, currentlyHidden: boolean, toolsCfg: ToolsCfg | undefined): ToolsCfg {\n const include = toolsCfg?.include ?? []\n const exclude = toolsCfg?.exclude ?? []\n if (include.length > 0) {\n const next = include.slice()\n if (currentlyHidden) next.push(name)\n else { const i = next.indexOf(name); if (i >= 0) next.splice(i, 1) }\n return { exclude, include: next }\n }\n const next = exclude.slice()\n if (currentlyHidden) { const i = next.indexOf(name); if (i >= 0) next.splice(i, 1) }\n else next.push(name)\n return { exclude: next, include }\n}\n\n/**\n * Switch between include (whitelist) and exclude (blacklist) modes. Turning\n * include mode on discards names unknown to the current tool set and seeds the\n * include list from the currently non-excluded tools; turning it off clears the\n * include list (keep only known exclude names).\n */\nexport function setToolMode(on: boolean, toolsCfg: ToolsCfg | undefined, toolNames: string[]): ToolsCfg {\n const known = new Set(toolNames)\n const exclude = (toolsCfg?.exclude ?? []).filter((n) => known.has(n))\n if (on) return { exclude, include: toolNames.filter((n) => !exclude.includes(n)) }\n return { exclude, include: [] }\n}\n\n// ── Preset list operations ──────────────────────────────────────────────────\n\n/** Serialize a preset for export (JSON-safe full snapshot). */\nexport function serializePreset(preset: Preset): { name: string; data: PresetData } {\n return { name: preset.name, data: preset.data }\n}\n\n/**\n * Import preset(s): same-name presets are skipped, only distinct names are\n * added. Returns a new presets list (or the original if nothing changed).\n */\nexport function addImportedPresets(existing: Preset[], parsed: unknown, makeId: () => string): Preset[] {\n const incoming = Array.isArray(parsed) ? parsed : [parsed]\n const existingNames = new Set(existing.map((p) => p.name))\n const added = incoming\n .filter((p) => p && typeof (p as { name?: unknown }).name === 'string' && !existingNames.has((p as Preset).name))\n .map((p) => ({ id: makeId(), name: (p as Preset).name, data: ((p as Preset).data ?? {}) as PresetData }))\n return added.length > 0 ? [...existing, ...added] : existing\n}\n\n/** Delete a preset, clearing the active id if it pointed at the removed one. */\nexport function removePreset(presets: Preset[], id: string, activeId?: string): { presets: Preset[]; activeId?: string } {\n return {\n presets: presets.filter((p) => p.id !== id),\n activeId: activeId === id ? undefined : activeId,\n }\n}\n","/** Prompt-sections tab: block / replace / inject / reorder prompt sections. */\nimport { createElement as h, useState, type ReactElement, type DragEvent, type ChangeEvent } from 'react'\nimport type { Config, Inventory, SettingsScope } from './types.ts'\nimport type { Translate } from './locales.ts'\nimport { mergeSections, type Section } from './presets.ts'\nimport { s } from './styles.ts'\n\nexport function SectionsTab({ cfg, inv, scope, t }: { cfg: Config; inv: Inventory | null; scope: SettingsScope; t: Translate }): ReactElement {\n const replace = cfg.replace ?? {}\n const blockedNames = new Set(cfg.sections ?? [])\n const [editing, setEditing] = useState<string | null>(null)\n const [draft, setDraft] = useState('')\n const [dragName, setDragName] = useState<string | null>(null)\n const [dropTarget, setDropTarget] = useState<{ name: string; pos: 'above' | 'below' } | null>(null)\n\n // Merge inventory sections with user-injected sections, sorted by order.\n // Injected sections that don't exist in the original prompt appear here too,\n // positioned by their order value.\n const merged: Section[] = mergeSections(inv, cfg, blockedNames)\n\n const toggleBlock = (name: string, currentlyBlocked: boolean): void => {\n const list = (cfg.sections ?? []).slice()\n if (currentlyBlocked) { const i = list.indexOf(name); if (i >= 0) list.splice(i, 1) }\n else list.push(name)\n scope.set('sections', list)\n }\n const startReplace = (name: string, original: string): void => {\n // Echo the ORIGINAL prompt text so the user edits from the current value.\n setEditing(name)\n setDraft(replace[name] ?? original)\n }\n const commitReplace = (name: string): void => {\n const next = { ...replace }\n if (draft.trim() === '') delete next[name]\n else next[name] = draft\n scope.set('replace', next)\n setEditing(null)\n }\n const clearReplace = (name: string): void => {\n const next = { ...replace }\n delete next[name]\n scope.set('replace', next)\n }\n\n // Persist the full ordered list. We treat the sorted list as an array and\n // re-index every section with a sequential integer order (0, 1, 2, …), so\n // there are never duplicate or fractional orders. text is left empty for\n // original sections (keeps their text); injected sections keep their own.\n const persistOrder = (ordered: Section[]): void => {\n const list = ordered.map((sec, i) => {\n const existing = (cfg.inject ?? []).find((x) => x.name === sec.name)\n return { name: sec.name, order: i, text: existing ? existing.text : '' }\n })\n scope.set('inject', list)\n }\n\n // Move a section up/down one slot (array-index swap), then re-index.\n const moveOrder = (index: number, dir: -1 | 1): void => {\n const target = index + dir\n if (target < 0 || target >= merged.length) return\n const next = merged.slice()\n const [item] = next.splice(index, 1)\n next.splice(target, 0, item)\n persistOrder(next)\n }\n\n const onDragStart = (e: DragEvent, name: string): void => {\n setDragName(name)\n e.dataTransfer.effectAllowed = 'move'\n try { e.dataTransfer.setData('text/plain', name) } catch { /* ignore */ }\n }\n const onDragOver = (e: DragEvent, name: string): void => {\n if (!dragName || dragName === name) return\n e.preventDefault()\n e.dataTransfer.dropEffect = 'move'\n const rect = e.currentTarget.getBoundingClientRect()\n const pos = e.clientY < rect.top + rect.height / 2 ? 'above' : 'below'\n setDropTarget({ name, pos })\n }\n const onDrop = (e: DragEvent, target: Section): void => {\n e.preventDefault()\n if (!dragName || dragName === target.name) { setDragName(null); setDropTarget(null); return }\n const dragged = merged.find((x) => x.name === dragName)\n if (!dragged) { setDragName(null); setDropTarget(null); return }\n const next = merged.filter((x) => x.name !== dragName)\n const idx = next.findIndex((x) => x.name === target.name)\n const pos = dropTarget?.name === target.name ? dropTarget.pos : 'below'\n next.splice(pos === 'above' ? idx : idx + 1, 0, dragged)\n persistOrder(next)\n setDragName(null)\n setDropTarget(null)\n }\n const onDragEnd = (): void => { setDragName(null); setDropTarget(null) }\n\n // Add a new injected section, placed by its order hint, then re-index.\n const addSection = (name: string, order: number, text: string): void => {\n const next = merged.slice()\n const entry: Section = { name, order, text: text || '<动态生成>', active: true, replaced: false }\n const existing = next.findIndex((x) => x.name === name)\n if (existing >= 0) next[existing] = entry\n else {\n const idx = next.findIndex((x) => x.order > order)\n if (idx < 0) next.push(entry)\n else next.splice(idx, 0, entry)\n }\n persistOrder(next)\n }\n\n return h('div', { style: s.list }, [\n merged.length === 0 ? h('div', { style: s.muted }, t('empty')) : null,\n merged.map((sec, index) => {\n const blocked = blockedNames.has(sec.name)\n const isEditing = editing === sec.name\n const isDragging = dragName === sec.name\n const isDropTarget = dropTarget?.name === sec.name\n const rowStyle = {\n ...s.row,\n ...(isDragging ? s.rowDragging : {}),\n ...(isDropTarget && dropTarget.pos === 'above' ? s.rowDropAbove : {}),\n ...(isDropTarget && dropTarget.pos === 'below' ? s.rowDropBelow : {}),\n }\n return h('div', {\n key: sec.name,\n style: rowStyle,\n draggable: true,\n onDragStart: (e: DragEvent) => onDragStart(e, sec.name),\n onDragOver: (e: DragEvent) => onDragOver(e, sec.name),\n onDrop: (e: DragEvent) => onDrop(e, sec),\n onDragEnd,\n }, [\n h('span', { style: s.dragHandle, title: t('drag') }, '⠿'),\n h('label', { style: s.switchWrap }, [\n h('input', { type: 'checkbox', checked: blocked, onChange: () => toggleBlock(sec.name, blocked) }),\n h('span', { style: blocked ? s.badgeBlocked : s.badgeOk }, blocked ? t('blockedOn') : t('blockedOff')),\n ]),\n h('div', { style: s.rowBody }, [\n h('div', { style: s.rowTitle }, [\n h('span', { style: s.code }, sec.name),\n h('span', { style: s.orderTag }, '#' + index),\n sec.replaced ? h('span', { style: s.badgeReplaced }, t('replaced')) : null,\n ]),\n isEditing\n ? h('div', { style: s.editBox }, [\n h('textarea', { style: s.editInput, value: draft, onChange: (e: ChangeEvent<HTMLTextAreaElement>) => setDraft(e.target.value), rows: 3 }),\n h('div', { style: s.injectRow }, [\n h('button', { style: s.mini, onClick: () => commitReplace(sec.name) }, t('add')),\n h('button', { style: s.mini, onClick: () => setEditing(null) }, t('clearReplace')),\n ]),\n ])\n : h('div', { style: s.preview }, String(sec.text ?? '').slice(0, 140) || t('dynamic')),\n ]),\n h('div', { style: s.arrowCol }, [\n h('button', { style: s.arrow, disabled: index === 0, onClick: () => moveOrder(index, -1), title: t('moveUp') }, '↑'),\n h('button', { style: s.arrow, disabled: index === merged.length - 1, onClick: () => moveOrder(index, 1), title: t('moveDown') }, '↓'),\n ]),\n h('button', { style: s.mini, onClick: () => replace[sec.name] ? clearReplace(sec.name) : startReplace(sec.name, String(sec.text ?? '')) }, replace[sec.name] ? t('clearReplace') : t('replace')),\n ])\n }),\n h('div', { style: s.injectBox }, [\n h('div', { style: s.rowTitle }, t('injectNew')),\n h(InjectForm, { onAdd: addSection, t }),\n ]),\n ])\n}\n\nfunction InjectForm({ onAdd, t }: { onAdd: (name: string, order: number, text: string) => void; t: Translate }): ReactElement {\n const [name, setName] = useState('')\n const [order, setOrder] = useState('120')\n const [text, setText] = useState('')\n\n const submit = (): void => {\n if (!name.trim()) return\n onAdd(name.trim(), Number(order) || 120, text)\n setName(''); setText(''); setOrder('120')\n }\n\n return h('div', {}, [\n h('div', { style: s.injectRow }, [\n h('input', { style: { ...s.input, width: '22%' }, placeholder: t('name'), value: name, onChange: (e) => setName(e.target.value) }),\n h('input', { style: { ...s.input, width: '12%' }, type: 'number', placeholder: t('order'), value: order, onChange: (e) => setOrder(e.target.value) }),\n h('input', { style: { ...s.input, flex: 1 }, placeholder: t('text'), value: text, onChange: (e) => setText(e.target.value) }),\n h('button', { style: s.mini, onClick: submit }, t('add')),\n ]),\n ])\n}\n","/** Tools tab: hide tools from the model catalog by exclude/include. */\nimport { createElement as h, type ReactElement } from 'react'\nimport type { Config, Inventory, SettingsScope } from './types.ts'\nimport type { Translate } from './locales.ts'\nimport { isToolHidden, setToolMode, toggleTool } from './presets.ts'\nimport { s } from './styles.ts'\n\nexport function ToolsTab({ cfg, inv, scope, t }: { cfg: Config; inv: Inventory | null; scope: SettingsScope; t: Translate }): ReactElement {\n const tools = inv?.tools ?? []\n const toolsCfg = cfg.tools ?? { exclude: [], include: [] }\n const includeMode = (toolsCfg.include ?? []).length > 0\n\n const toggle = (name: string, currentlyHidden: boolean): void => {\n scope.set('tools', toggleTool(name, currentlyHidden, toolsCfg))\n }\n const setIncludeMode = (on: boolean): void => {\n scope.set('tools', setToolMode(on, toolsCfg, tools.map((tool) => tool.name)))\n }\n\n return h('div', {}, [\n h('div', { style: s.bar }, [\n h('button', { style: includeMode ? s.tabActive : s.tab, onClick: () => setIncludeMode(true) }, t('includeMode')),\n h('button', { style: includeMode ? s.tab : s.tabActive, onClick: () => setIncludeMode(false) }, t('excludeMode')),\n ]),\n h('div', { style: s.list }, [\n tools.length === 0 ? h('div', { style: s.muted }, t('empty')) : null,\n tools.map((tool) => {\n // Hidden state comes from the CONFIG (exclude/include lists), not the\n // inventory snapshot, so toggling updates the checkbox immediately.\n const hidden = isToolHidden(tool.name, toolsCfg)\n return h('div', { key: tool.name, style: s.row }, [\n h('label', { style: s.switchWrap }, [\n h('input', { type: 'checkbox', checked: hidden, onChange: () => toggle(tool.name, hidden) }),\n h('span', { style: hidden ? s.badgeBlocked : s.badgeOk }, hidden ? t('hiddenOn') : t('hiddenOff')),\n ]),\n h('div', { style: s.rowBody }, [\n h('div', { style: s.rowTitle }, h('span', { style: s.code }, tool.name)),\n h('div', { style: s.preview }, String(tool.description ?? '').slice(0, 140)),\n ]),\n ])\n }),\n ]),\n ])\n}\n","/** Presets tab: save / apply / export / import complete customization snapshots. */\nimport { createElement as h, useRef, useState, type ReactElement, type ChangeEvent } from 'react'\nimport type { Config, Inventory, Preset, PresetData, SettingsScope } from './types.ts'\nimport type { Translate } from './locales.ts'\nimport { addImportedPresets, applyPresetData, buildPresetData, mergeSections, removePreset, serializePreset } from './presets.ts'\nimport { s } from './styles.ts'\n\nexport function PresetsTab({ cfg, inv, scope, t }: { cfg: Config; inv: Inventory | null; scope: SettingsScope; t: Translate }): ReactElement {\n const presets = cfg.presets ?? []\n const [name, setName] = useState('')\n const fileRef = useRef<HTMLInputElement | null>(null)\n\n const blockedNames = new Set(cfg.sections ?? [])\n const merged = mergeSections(inv, cfg, blockedNames)\n const currentNames = new Set(merged.map((sec) => sec.name))\n\n // Capture the current customization as a new preset (full snapshot). The\n // absolute order is converted to a relative form (each section records the\n // section it should follow) so the preset stays portable across prompts.\n const saveCurrent = (): void => {\n const presetName = name.trim() || `${t('preset')} ${presets.length + 1}`\n const data = buildPresetData(cfg, merged)\n scope.set('presets', [...presets, { id: genId(), name: presetName, data }])\n setName('')\n }\n\n // Apply a preset. The patch is computed by the pure helper:\n // - same-name sections are overridden (content + order)\n // - preset sections missing from the current prompt are added\n // - current sections NOT in the preset list are disabled (blocked)\n // - only the preset's ACTIVE sections are unblocked (its own blocked list\n // is preserved)\n const applyPreset = (preset: Preset): void => {\n const patch = applyPresetData(preset.data, cfg, currentNames)\n scope.set('inject', patch.inject)\n scope.set('replace', patch.replace)\n scope.set('sections', patch.sections)\n scope.set('tools', patch.tools)\n scope.set('activePreset', preset.id)\n }\n\n const deletePreset = (id: string): void => {\n const next = removePreset(presets, id, cfg.activePreset)\n scope.set('presets', next.presets)\n if (next.activeId === undefined && cfg.activePreset === id) scope.unset('activePreset')\n }\n\n // Serialize a preset to a downloadable JSON file.\n const exportPreset = (preset: Preset): void => {\n const blob = new Blob([JSON.stringify(serializePreset(preset), null, 2)], { type: 'application/json' })\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n a.download = `${preset.name}.json`\n a.click()\n URL.revokeObjectURL(url)\n }\n\n // Import preset(s) from a JSON file. Same-name presets are skipped; only\n // presets with new names are added to the local list.\n const onImportFile = (e: ChangeEvent<HTMLInputElement>): void => {\n const file = e.target.files?.[0]\n if (!file) return\n const reader = new FileReader()\n reader.onload = () => {\n try {\n const parsed = JSON.parse(String(reader.result))\n scope.set('presets', addImportedPresets(presets, parsed, genId))\n } catch { /* ignore invalid json */ }\n }\n reader.readAsText(file)\n e.target.value = ''\n }\n\n return h('div', { style: s.list }, [\n h('div', { style: s.injectBox }, [\n h('div', { style: s.rowTitle }, t('savePreset')),\n h('div', { style: s.injectRow }, [\n h('input', { style: { ...s.input, flex: 1 }, placeholder: t('presetName'), value: name, onChange: (e: ChangeEvent<HTMLInputElement>) => setName(e.target.value) }),\n h('button', { style: s.mini, onClick: saveCurrent }, t('save')),\n ]),\n ]),\n h('div', { style: s.injectBox }, [\n h('div', { style: s.rowTitle }, t('importPreset')),\n h('div', { style: s.injectRow }, [\n h('button', { style: s.mini, onClick: () => fileRef.current?.click() }, t('import')),\n h('input', { ref: fileRef, type: 'file', accept: '.json,application/json', style: { display: 'none' }, onChange: onImportFile }),\n ]),\n ]),\n presets.length === 0 ? h('div', { style: s.muted }, t('empty')) : null,\n presets.map((preset) => {\n const active = cfg.activePreset === preset.id\n return h('div', { key: preset.id, style: s.row }, [\n h('div', { style: s.rowBody }, [\n h('div', { style: s.rowTitle }, [\n h('span', { style: s.code }, preset.name),\n active ? h('span', { style: s.badgeOk }, t('active')) : null,\n ]),\n ]),\n h('button', { style: s.mini, onClick: () => applyPreset(preset) }, t('apply')),\n h('button', { style: s.mini, onClick: () => exportPreset(preset) }, t('export')),\n h('button', { style: s.mini, onClick: () => deletePreset(preset.id) }, t('delete')),\n ])\n }),\n ])\n}\n\nfunction genId(): string {\n return 'p_' + Date.now().toString(36) + Math.random().toString(36).slice(2, 7)\n}\n","/**\n * dsh-prompt-customizer — browser half (TypeScript source).\n *\n * Registers a \"提示词定制\" settings section (sidebar entry) and a plugin\n * settings card. The panel fetches the effective section / tool inventory\n * from the host `/api/prompt-customizer/inventory` and lets the user block,\n * replace, inject prompt sections and hide tools. All writes go through the\n * bound settings scope (`prompt-customizer` namespace) and apply live.\n *\n * Built by tsdown into the __ModuleLoader__ factory bundle at client/client.js;\n * the only external is the loader module table's react entry.\n */\nimport { createElement as h, useEffect, useState, type ReactElement } from 'react'\nimport { DICT, type Translate } from './locales.ts'\nimport { s } from './styles.ts'\nimport type { Config, Inventory, SettingsScope } from './types.ts'\nimport { SectionsTab } from './SectionsTab.tsx'\nimport { ToolsTab } from './ToolsTab.tsx'\nimport { PresetsTab } from './PresetsTab.tsx'\n\nconst NS = 'prompt-customizer'\nconst INVENTORY_URL = '/api/prompt-customizer/inventory'\n\nexport const name = 'prompt-customizer'\nexport const inject = ['slots', 'locale']\n\n/** The subset of the client cordis context this plugin touches. */\ninterface ClientContext {\n effect(callback: () => unknown, label?: string): void\n locale: {\n register(namespace: string, dicts: { zh: Record<string, string>; en: Record<string, string> }): unknown\n bind(namespace: string): Translate\n }\n inject(services: string[], callback: (scoped: SettingsScopeHost) => void): void\n}\n\n/** The host surface the settings card needs (settingsScope transport). */\ninterface SettingsScopeHost {\n settingsScope: {\n bind(options: { namespace: string }): SettingsScope\n }\n slots: {\n inject(name: string, register: () => unknown): void\n register(meta: Record<string, unknown>, render: () => unknown): unknown\n }\n}\n\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => ctx.locale.register(NS, DICT), 'prompt-customizer: locale')\n const t = ctx.locale.bind(NS)\n\n // Register a standalone settings section (sidebar menu entry) so the panel\n // only appears under \"提示词定制\", not as a card inside the \"插件\" settings\n // list. Goes through a NESTED inject of settingsScope so the panel can\n // read/write the namespace config; on hosts without the settings transport\n // it stays hidden instead of unmounting the whole plugin.\n ctx.inject(['settingsScope'], (scoped) => {\n const scope = scoped.settingsScope.bind({ namespace: NS })\n\n scoped.slots.inject('settings.section', () => scoped.slots.register({\n name: 'settings.section',\n id: 'prompt-customizer',\n order: 50,\n label: () => t('nav'),\n locale: NS,\n inject: () => ({ t }),\n }, () => h(Panel, { scope, t })))\n })\n}\n\n// ── Root panel ──────────────────────────────────────────────────────────────\n\nfunction Panel({ scope, t }: { scope: SettingsScope; t: Translate }): ReactElement {\n const [snap, setSnap] = useState(() => scope.getSnapshot())\n const [inv, setInv] = useState<Inventory | null>(null)\n const [tab, setTab] = useState<'sections' | 'tools' | 'presets'>('sections')\n const [error, setError] = useState<string | null>(null)\n\n useEffect(() => scope.subscribe(() => setSnap(scope.getSnapshot())), [scope])\n\n const refresh = (): void => {\n fetch(INVENTORY_URL)\n .then((r) => r.json())\n .then((data: Inventory) => { setInv(data); setError(null) })\n .catch((e: unknown) => setError(String(e instanceof Error ? e.message : e)))\n }\n useEffect(refresh, [])\n\n if (snap.status !== 'ready' && snap.value === undefined) {\n return h('div', { style: s.muted }, t('loading'))\n }\n\n const cfg: Config = snap.value && typeof snap.value === 'object'\n ? snap.value as Config\n : { sections: [], replace: {}, inject: [], tools: { exclude: [], include: [] } }\n\n return h('div', { style: s.root }, [\n h('div', { style: s.bar }, [\n h('button', { style: tab === 'sections' ? s.tabActive : s.tab, onClick: () => setTab('sections') }, t('tabsSections')),\n h('button', { style: tab === 'tools' ? s.tabActive : s.tab, onClick: () => setTab('tools') }, t('tabsTools')),\n h('button', { style: tab === 'presets' ? s.tabActive : s.tab, onClick: () => setTab('presets') }, t('tabsPresets')),\n h('button', { style: s.refresh, onClick: refresh }, t('refresh')),\n ]),\n error ? h('div', { style: s.error }, String(error)) : null,\n tab === 'sections'\n ? h(SectionsTab, { cfg, inv, scope, t })\n : tab === 'tools'\n ? h(ToolsTab, { cfg, inv, scope, t })\n : h(PresetsTab, { cfg, inv, scope, t }),\n ])\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["useState","h","h","useState","useRef","h","h","useState"],"sources":["../src/client/locales.ts","../src/client/styles.ts","../src/client/presets.ts","../src/client/SectionsTab.tsx","../src/client/ToolsTab.tsx","../src/client/PresetsTab.tsx","../src/client/index.ts"],"sourcesContent":["/** Locale dictionaries for the prompt-customizer panel. */\nexport const DICT = {\n zh: {\n nav: '提示词定制',\n loading: '加载中…',\n unavailable: '当前环境未启用本命名空间。',\n tabsSections: '提示词',\n tabsTools: '工具',\n tabsPresets: '预设',\n refresh: '刷新',\n blockedOff: '未屏蔽',\n blockedOn: '已屏蔽',\n replaced: '已替换',\n manual: '手动',\n system: '系统',\n replace: '编辑',\n clearReplace: '清除',\n injectNew: '注入新段',\n name: '名称',\n order: '顺序',\n text: '文本',\n add: '添加',\n hiddenOff: '可见',\n hiddenOn: '已隐藏',\n includeMode: '白名单模式:仅保留勾选项',\n excludeMode: '黑名单模式:勾选 = 隐藏',\n empty: '(空)',\n dynamic: '<动态生成>',\n drag: '拖动排序',\n moveUp: '上移',\n moveDown: '下移',\n savePreset: '保存当前为预设',\n presetName: '预设名称',\n save: '保存',\n importPreset: '导入预设',\n import: '导入',\n apply: '应用',\n export: '导出',\n delete: '删除',\n active: '使用中',\n preset: '预设',\n },\n en: {\n nav: 'Prompt Customizer',\n loading: 'Loading…',\n unavailable: 'Namespace unavailable in this environment.',\n tabsSections: 'Sections',\n tabsTools: 'Tools',\n tabsPresets: 'Presets',\n refresh: 'Refresh',\n blockedOff: 'Active',\n blockedOn: 'Blocked',\n replaced: 'Replaced',\n manual: 'Manual',\n system: 'System',\n replace: 'Replace',\n clearReplace: 'Clear',\n injectNew: 'Inject section',\n name: 'Name',\n order: 'Order',\n text: 'Text',\n add: 'Add',\n hiddenOff: 'Visible',\n hiddenOn: 'Hidden',\n includeMode: 'Whitelist: keep only checked',\n excludeMode: 'Blacklist: check to hide',\n empty: '(empty)',\n dynamic: '<dynamic>',\n drag: 'Drag to reorder',\n moveUp: 'Move up',\n moveDown: 'Move down',\n savePreset: 'Save current as preset',\n presetName: 'Preset name',\n save: 'Save',\n importPreset: 'Import preset',\n import: 'Import',\n apply: 'Apply',\n export: 'Export',\n delete: 'Delete',\n active: 'Active',\n preset: 'Preset',\n },\n} as const\n\nexport type Translate = (key: string) => string\n","/** Inline styles shared by the prompt-customizer panel components. */\nexport const s = (() => {\n const flex = { display: 'flex', alignItems: 'center' }\n return {\n root: { fontFamily: 'inherit', fontSize: 13, lineHeight: 1.5 },\n bar: { ...flex, gap: 6, marginBottom: 8, flexWrap: 'wrap' },\n tab: { padding: '4px 10px', border: '1px solid rgba(128,128,128,.35)', borderRadius: 6, background: 'transparent', cursor: 'pointer', color: 'inherit', opacity: 0.75 },\n tabActive: { padding: '4px 10px', border: '1px solid rgba(128,128,128,.7)', borderRadius: 6, background: 'rgba(128,128,128,.18)', cursor: 'pointer', color: 'inherit' },\n refresh: { marginLeft: 'auto', padding: '4px 10px', border: '1px solid rgba(128,128,128,.35)', borderRadius: 6, background: 'transparent', cursor: 'pointer', color: 'inherit' },\n list: { display: 'flex', flexDirection: 'column', gap: 6 },\n row: { ...flex, gap: 8, padding: '6px 8px', border: '1px solid rgba(128,128,128,.2)', borderRadius: 8, background: 'rgba(128,128,128,.06)' },\n switchWrap: { ...flex, gap: 6, minWidth: 90 },\n rowBody: { flex: 1, minWidth: 0 },\n rowTitle: { ...flex, gap: 6, alignItems: 'baseline', flexWrap: 'wrap' },\n code: { fontFamily: 'monospace', fontSize: 12, wordBreak: 'break-all' },\n orderTag: { color: 'rgba(128,128,128,.8)', fontSize: 11 },\n preview: { color: 'rgba(128,128,128,.85)', fontSize: 12, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' },\n badgeOk: { color: '#3fb950', fontSize: 11, border: '1px solid rgba(63,185,80,.5)', borderRadius: 999, padding: '0 6px' },\n badgeBlocked: { color: '#f85149', fontSize: 11, border: '1px solid rgba(248,81,73,.5)', borderRadius: 999, padding: '0 6px' },\n badgeReplaced: { color: '#d29922', fontSize: 11, border: '1px solid rgba(210,153,34,.5)', borderRadius: 999, padding: '0 6px' },\n badgeCustom: { color: '#58a6ff', fontSize: 11, border: '1px solid rgba(88,166,255,.5)', borderRadius: 999, padding: '0 6px' },\n badgeSystem: { color: 'rgba(128,128,128,.85)', fontSize: 11, border: '1px solid rgba(128,128,128,.4)', borderRadius: 999, padding: '0 6px' },\n mini: { padding: '3px 8px', border: '1px solid rgba(128,128,128,.35)', borderRadius: 6, background: 'transparent', cursor: 'pointer', color: 'inherit', whiteSpace: 'nowrap' },\n arrowCol: { display: 'flex', flexDirection: 'column', gap: 2 },\n arrow: { padding: '0 6px', border: '1px solid rgba(128,128,128,.3)', borderRadius: 4, background: 'transparent', cursor: 'pointer', color: 'inherit', fontSize: 11, lineHeight: 1.4, opacity: 0.8 },\n rowDragging: { opacity: 0.4 },\n rowDropAbove: { boxShadow: '0 -2px 0 0 #58a6ff' },\n rowDropBelow: { boxShadow: '0 2px 0 0 #58a6ff' },\n dragHandle: { cursor: 'grab', color: 'rgba(128,128,128,.7)', fontSize: 13, padding: '0 2px', userSelect: 'none' },\n injectBox: { marginTop: 10, padding: '8px', border: '1px dashed rgba(128,128,128,.4)', borderRadius: 8, display: 'flex', flexDirection: 'column', gap: 6 },\n injectRow: { ...flex, gap: 6 },\n editBox: { display: 'flex', flexDirection: 'column', gap: 6, marginTop: 4 },\n editInput: { padding: '6px', border: '1px solid rgba(128,128,128,.35)', borderRadius: 6, background: 'transparent', color: 'inherit', fontFamily: 'inherit', fontSize: 12, resize: 'vertical', width: '100%', boxSizing: 'border-box' },\n input: { padding: '4px 6px', border: '1px solid rgba(128,128,128,.35)', borderRadius: 6, background: 'transparent', color: 'inherit' },\n muted: { color: 'rgba(128,128,128,.75)', fontSize: 12 },\n error: { color: '#f85149', fontSize: 12, marginBottom: 6 },\n }\n})()\n","/** Pure preset / section helpers shared by the client UI and the node tests. */\nimport type { Config, Inventory, Preset, PresetData } from './types.ts'\n\nexport interface Section {\n name: string\n order: number\n text?: string\n active: boolean\n replaced: boolean\n /** `system` = read from the host prompt inventory (not deletable); `custom` = added by this plugin (deletable). */\n source?: 'system' | 'custom'\n}\n\n/** The config fields a preset apply must write back to the settings scope. */\nexport interface ConfigPatch {\n sections: string[]\n replace: Record<string, string>\n inject: Array<{ name: string; order: number; text: string }>\n tools: { exclude: string[]; include: string[] }\n}\n\n/**\n * Merge the inventory sections with the user's inject list, sorted by order.\n * Sections read from the inventory (other plugins) are `system`; sections this\n * plugin generated carry a hidden `custom` marker and are `custom`. The source\n * is decided by the hidden marker — never by name collisions — so a custom\n * section keeps its identity (and its deletability) even when its name happens\n * to match an inventory section, and across preset switches. Custom sections\n * always render their own text; they can never be \"<动态生成>\".\n */\nexport function mergeSections(inv: Inventory | null, cfg: Config, blockedNames: ReadonlySet<string>): Section[] {\n const map = new Map<string, Section>()\n for (const sec of inv?.sections ?? []) map.set(sec.name, { ...sec, source: 'system' })\n for (const item of cfg.inject ?? []) {\n const isCustom = item.custom === true\n const existing = map.get(item.name)\n if (existing) {\n map.set(item.name, isCustom\n ? { ...existing, order: item.order, text: item.text ?? '', source: 'custom' }\n : { ...existing, order: item.order, source: 'system' })\n } else {\n map.set(item.name, {\n name: item.name,\n order: item.order,\n text: item.text ?? '',\n active: !blockedNames.has(item.name),\n replaced: false,\n source: isCustom ? 'custom' : 'system',\n })\n }\n }\n return [...map.values()].sort((a, b) => a.order - b.order)\n}\n\n/**\n * Resolve a preset's relative order into an absolute ordered list (0..n-1).\n * Sections with no anchor (or whose anchor is absent) come first in preset\n * order; each anchored section is inserted right after its anchor; any\n * remaining sections (cycles) are appended.\n */\nexport function resolveOrder(presetOrder: PresetData['order']): Array<{ name: string; order: number; text: string; custom?: boolean }> {\n const list = presetOrder ?? []\n const afterMap = new Map<string, string | undefined>()\n const textMap = new Map<string, string>()\n const customMap = new Map<string, boolean>()\n for (const sec of list) {\n afterMap.set(sec.name, sec.after)\n textMap.set(sec.name, sec.text)\n customMap.set(sec.name, sec.custom === true)\n }\n\n const result: string[] = []\n const placed = new Set<string>()\n\n for (const sec of list) {\n const anchor = afterMap.get(sec.name)\n if (!anchor || !afterMap.has(anchor)) { result.push(sec.name); placed.add(sec.name) }\n }\n\n let changed = true\n while (changed) {\n changed = false\n for (const sec of list) {\n if (placed.has(sec.name)) continue\n const anchor = afterMap.get(sec.name)\n if (anchor && placed.has(anchor)) {\n result.splice(result.indexOf(anchor) + 1, 0, sec.name)\n placed.add(sec.name)\n changed = true\n }\n }\n }\n\n for (const sec of list) {\n if (!placed.has(sec.name)) { result.push(sec.name); placed.add(sec.name) }\n }\n\n return result.map((name, i) => ({ name, order: i, text: textMap.get(name) ?? '', custom: customMap.get(name) }))\n}\n\n/**\n * Build a preset snapshot from the current config. The order list is derived\n * from the MERGED section list (not just `cfg.inject`) so that a preset saved\n * after only blocking sections (without reordering) still carries the full\n * ordered set, and thus a meaningful \"active list\" when applied.\n */\nexport function buildPresetData(cfg: Config, merged: Section[]): PresetData {\n // Only custom (plugin-added) sections carry their own text into the preset's\n // order list. System sections keep an empty text so applying the preset only\n // reorders them and never freezes their dynamically generated content.\n const order = merged.map((sec, i) => ({\n name: sec.name,\n after: i > 0 ? merged[i - 1].name : undefined,\n text: sec.source === 'custom' ? (sec.text ?? '') : '',\n custom: sec.source === 'custom',\n }))\n return {\n sections: cfg.sections,\n replace: cfg.replace,\n order,\n tools: cfg.tools,\n }\n}\n\n/**\n * Compute the config patch when applying a preset:\n * - same-name sections are overridden (the order list drives this at runtime)\n * - preset sections missing from the current prompt are added\n * - current sections NOT in the preset's ordered list are disabled by default\n * - only the preset's ACTIVE sections (in the order list but not in its block\n * list) are unblocked; the preset's own blocked sections stay blocked.\n *\n * Custom injected sections that exist in the current config but are NOT in the\n * preset's ordered list are preserved in the resulting inject list (so they\n * stay visible) and disabled (added to the blocked set), rather than silently\n * dropped — a preset must not make the user's own injected sections vanish.\n */\nexport function applyPresetData(data: PresetData, cfg: Config, currentNames: ReadonlySet<string>): ConfigPatch {\n const presetOrder = data.order ?? []\n const presetNames = new Set(presetOrder.map((x) => x.name))\n const blocked = new Set(data.sections ?? [])\n\n // Inject list = the preset's resolved order, then any custom injected\n // sections present in the current config but absent from the preset list.\n // Keeping them in `inject` makes them visible; they are disabled below.\n // Kept customs continue after the preset's own indexes; if the preset covers\n // only a small subset they may tie with the inventory's native orders — the\n // merge view sorts stably (inventory first), so ordering stays deterministic,\n // and the next reorder/persist re-indexes everything contiguously anyway.\n const inject = resolveOrder(presetOrder)\n const kept = new Set(inject.map((x) => x.name))\n let order = inject.length\n for (const item of cfg.inject ?? []) {\n if (!item || !item.name || presetNames.has(item.name) || kept.has(item.name)) continue\n kept.add(item.name)\n inject.push({ name: item.name, order: order++, text: item.text ?? '', custom: item.custom === true })\n }\n\n // Only enforce the \"active set\" when the preset actually defines an order.\n // An empty order (e.g. a hand-authored / imported preset that only blocks a\n // few sections) must not default-disable every current section.\n if (presetOrder.length > 0) {\n const activeNames = new Set([...presetNames].filter((n) => !blocked.has(n)))\n for (const name of currentNames) if (!presetNames.has(name)) blocked.add(name)\n for (const name of activeNames) blocked.delete(name)\n }\n\n return {\n sections: [...blocked],\n replace: { ...(cfg.replace ?? {}), ...(data.replace ?? {}) },\n inject,\n tools: { exclude: data.tools?.exclude ?? [], include: data.tools?.include ?? [] },\n }\n}\n\n/**\n * Remove a custom (plugin-added) section from the config: it disappears from\n * the inject list, is no longer forced into the blocked set, and any replace\n * text for it is cleared. The remaining inject entries are re-indexed to a\n * contiguous 0..n-1 order (the virtual index model), so removal never leaves\n * holes. System sections read from the host inventory cannot be removed this\n * way (they would just come back on the next inventory read).\n */\nexport function removeSection(name: string, cfg: Config): Pick<Config, 'sections' | 'inject' | 'replace'> {\n const sections = (cfg.sections ?? []).filter((n) => n !== name)\n const inject = (cfg.inject ?? [])\n .filter((item) => item.name !== name)\n .map((item, i) => ({ ...item, order: i }))\n const replace = { ...(cfg.replace ?? {}) }\n delete replace[name]\n return { sections, inject, replace }\n}\n\n// ── Tool filtering ──────────────────────────────────────────────────────────\n\nexport type ToolsCfg = { exclude?: string[]; include?: string[] }\n\n/** Whether a tool is hidden under the current include/exclude config. */\nexport function isToolHidden(name: string, toolsCfg: ToolsCfg | undefined): boolean {\n const include = toolsCfg?.include ?? []\n const exclude = toolsCfg?.exclude ?? []\n return include.length > 0 ? !include.includes(name) : exclude.includes(name)\n}\n\n/** Toggle a tool's hidden state, returning a new tools config. */\nexport function toggleTool(name: string, currentlyHidden: boolean, toolsCfg: ToolsCfg | undefined): ToolsCfg {\n const include = toolsCfg?.include ?? []\n const exclude = toolsCfg?.exclude ?? []\n if (include.length > 0) {\n const next = include.slice()\n if (currentlyHidden) {\n next.push(name)\n // Re-showing a tool in whitelist mode must also clear any stale blacklist\n // entry, otherwise the name lingers in both lists and reappears as hidden\n // if the user later switches back to blacklist mode.\n const nextExclude = exclude.filter((n) => n !== name)\n return { exclude: nextExclude, include: next }\n }\n const i = next.indexOf(name)\n if (i >= 0) next.splice(i, 1)\n return { exclude, include: next }\n }\n const next = exclude.slice()\n if (currentlyHidden) { const i = next.indexOf(name); if (i >= 0) next.splice(i, 1) }\n else next.push(name)\n return { exclude: next, include }\n}\n\n/**\n * Switch between include (whitelist) and exclude (blacklist) modes. Turning\n * include mode on discards names unknown to the current tool set and seeds the\n * include list from the currently non-excluded tools; turning it off clears the\n * include list (keep only known exclude names).\n */\nexport function setToolMode(on: boolean, toolsCfg: ToolsCfg | undefined, toolNames: string[]): ToolsCfg {\n const known = new Set(toolNames)\n const exclude = (toolsCfg?.exclude ?? []).filter((n) => known.has(n))\n if (on) return { exclude, include: toolNames.filter((n) => !exclude.includes(n)) }\n return { exclude, include: [] }\n}\n\n// ── Preset list operations ──────────────────────────────────────────────────\n\n/** Serialize a preset for export (JSON-safe full snapshot). */\nexport function serializePreset(preset: Preset): { name: string; data: PresetData } {\n return { name: preset.name, data: preset.data }\n}\n\n/**\n * Import preset(s): same-name presets are skipped, only distinct names are\n * added. Returns a new presets list (or the original if nothing changed).\n */\nexport function addImportedPresets(existing: Preset[], parsed: unknown, makeId: () => string): Preset[] {\n const incoming = Array.isArray(parsed) ? parsed : [parsed]\n const existingNames = new Set(existing.map((p) => p.name))\n const added = incoming\n .filter((p) => p && typeof (p as { name?: unknown }).name === 'string' && !existingNames.has((p as Preset).name))\n .map((p) => ({ id: makeId(), name: (p as Preset).name, data: ((p as Preset).data ?? {}) as PresetData }))\n return added.length > 0 ? [...existing, ...added] : existing\n}\n\n/** Delete a preset, clearing the active id if it pointed at the removed one. */\nexport function removePreset(presets: Preset[], id: string, activeId?: string): { presets: Preset[]; activeId?: string } {\n return {\n presets: presets.filter((p) => p.id !== id),\n activeId: activeId === id ? undefined : activeId,\n }\n}\n","/** Prompt-sections tab: block / replace / inject / reorder prompt sections. */\nimport { createElement as h, useState, type ReactElement, type DragEvent, type ChangeEvent } from 'react'\nimport type { Config, Inventory, SettingsScope } from './types.ts'\nimport type { Translate } from './locales.ts'\nimport { mergeSections, removeSection, type Section } from './presets.ts'\nimport { s } from './styles.ts'\n\nexport function SectionsTab({ cfg, inv, scope, t }: { cfg: Config; inv: Inventory | null; scope: SettingsScope; t: Translate }): ReactElement {\n const replace = cfg.replace ?? {}\n const blockedNames = new Set(cfg.sections ?? [])\n const [editing, setEditing] = useState<string | null>(null)\n const [draft, setDraft] = useState('')\n const [dragName, setDragName] = useState<string | null>(null)\n const [dropTarget, setDropTarget] = useState<{ name: string; pos: 'above' | 'below' } | null>(null)\n\n // Merge inventory sections with user-injected sections, sorted by order.\n // Injected sections that don't exist in the original prompt appear here too,\n // positioned by their order value.\n const merged: Section[] = mergeSections(inv, cfg, blockedNames)\n\n const toggleBlock = (name: string, currentlyBlocked: boolean): void => {\n const list = (cfg.sections ?? []).slice()\n if (currentlyBlocked) { const i = list.indexOf(name); if (i >= 0) list.splice(i, 1) }\n else list.push(name)\n scope.set('sections', list)\n }\n const startReplace = (name: string, original: string): void => {\n // Echo the ORIGINAL prompt text so the user edits from the current value.\n setEditing(name)\n setDraft(replace[name] ?? original)\n }\n const commitReplace = (name: string): void => {\n const next = { ...replace }\n if (draft.trim() === '') delete next[name]\n else next[name] = draft\n scope.set('replace', next)\n setEditing(null)\n }\n const clearReplace = (name: string): void => {\n const next = { ...replace }\n delete next[name]\n scope.set('replace', next)\n }\n\n // Persist the full ordered list. We treat the sorted list as an array and\n // re-index every section with a sequential integer order (0, 1, 2, …), so\n // there are never duplicate or fractional orders. System sections keep empty\n // text (so we never freeze their dynamically generated content); custom\n // (plugin-generated) sections keep their own text and the hidden `custom`\n // marker, so they render real content and stay identifiable/deletable.\n const persistOrder = (ordered: Section[]): void => {\n const list = ordered.map((sec, i) => {\n const existing = (cfg.inject ?? []).find((x) => x.name === sec.name)\n const isCustom = sec.source === 'custom'\n return { name: sec.name, order: i, text: isCustom ? (sec.text ?? '') : (existing?.text ?? ''), custom: isCustom }\n })\n scope.set('inject', list)\n }\n\n // Delete a custom (plugin-added) section: remove it from inject / sections /\n // replace so it no longer appears in the panel at all.\n const removeCustom = (name: string): void => {\n const patch = removeSection(name, cfg)\n scope.set('sections', patch.sections)\n scope.set('inject', patch.inject)\n scope.set('replace', patch.replace)\n }\n\n // Move a section up/down one slot (array-index swap), then re-index.\n const moveOrder = (index: number, dir: -1 | 1): void => {\n const target = index + dir\n if (target < 0 || target >= merged.length) return\n const next = merged.slice()\n const [item] = next.splice(index, 1)\n next.splice(target, 0, item)\n persistOrder(next)\n }\n\n const onDragStart = (e: DragEvent, name: string): void => {\n setDragName(name)\n e.dataTransfer.effectAllowed = 'move'\n try { e.dataTransfer.setData('text/plain', name) } catch { /* ignore */ }\n }\n const onDragOver = (e: DragEvent, name: string): void => {\n if (!dragName || dragName === name) return\n e.preventDefault()\n e.dataTransfer.dropEffect = 'move'\n const rect = e.currentTarget.getBoundingClientRect()\n const pos = e.clientY < rect.top + rect.height / 2 ? 'above' : 'below'\n setDropTarget({ name, pos })\n }\n const onDrop = (e: DragEvent, target: Section): void => {\n e.preventDefault()\n if (!dragName || dragName === target.name) { setDragName(null); setDropTarget(null); return }\n const dragged = merged.find((x) => x.name === dragName)\n if (!dragged) { setDragName(null); setDropTarget(null); return }\n const next = merged.filter((x) => x.name !== dragName)\n const idx = next.findIndex((x) => x.name === target.name)\n const pos = dropTarget?.name === target.name ? dropTarget.pos : 'below'\n next.splice(pos === 'above' ? idx : idx + 1, 0, dragged)\n persistOrder(next)\n setDragName(null)\n setDropTarget(null)\n }\n const onDragEnd = (): void => { setDragName(null); setDropTarget(null) }\n\n // Add a new injected section, placed by its order hint, then re-index. The\n // section is marked `custom` (the hidden marker) so it is identified as\n // plugin-generated and thus deletable, and its own text is preserved.\n const addSection = (name: string, order: number, text: string): void => {\n const next = merged.slice()\n const entry: Section = { name, order, text, active: true, replaced: false, source: 'custom' }\n const existing = next.findIndex((x) => x.name === name)\n if (existing >= 0) next[existing] = entry\n else {\n const idx = next.findIndex((x) => x.order > order)\n if (idx < 0) next.push(entry)\n else next.splice(idx, 0, entry)\n }\n persistOrder(next)\n }\n\n return h('div', { style: s.list }, [\n merged.length === 0 ? h('div', { style: s.muted }, t('empty')) : null,\n merged.map((sec, index) => {\n const blocked = blockedNames.has(sec.name)\n const isEditing = editing === sec.name\n const isDragging = dragName === sec.name\n const isDropTarget = dropTarget?.name === sec.name\n const rowStyle = {\n ...s.row,\n ...(isDragging ? s.rowDragging : {}),\n ...(isDropTarget && dropTarget.pos === 'above' ? s.rowDropAbove : {}),\n ...(isDropTarget && dropTarget.pos === 'below' ? s.rowDropBelow : {}),\n }\n return h('div', {\n key: sec.name,\n style: rowStyle,\n draggable: true,\n onDragStart: (e: DragEvent) => onDragStart(e, sec.name),\n onDragOver: (e: DragEvent) => onDragOver(e, sec.name),\n onDrop: (e: DragEvent) => onDrop(e, sec),\n onDragEnd,\n }, [\n h('span', { style: s.dragHandle, title: t('drag') }, '⠿'),\n h('label', { style: s.switchWrap }, [\n h('input', { type: 'checkbox', checked: blocked, onChange: () => toggleBlock(sec.name, blocked) }),\n h('span', { style: blocked ? s.badgeBlocked : s.badgeOk }, blocked ? t('blockedOn') : t('blockedOff')),\n ]),\n h('div', { style: s.rowBody }, [\n h('div', { style: s.rowTitle }, [\n h('span', { style: s.code }, sec.name),\n h('span', { style: s.orderTag }, '#' + index),\n h('span', { style: sec.source === 'custom' ? s.badgeCustom : s.badgeSystem }, sec.source === 'custom' ? t('manual') : t('system')),\n sec.replaced ? h('span', { style: s.badgeReplaced }, t('replaced')) : null,\n ]),\n isEditing\n ? h('div', { style: s.editBox }, [\n h('textarea', { style: s.editInput, value: draft, onChange: (e: ChangeEvent<HTMLTextAreaElement>) => setDraft(e.target.value), rows: 3 }),\n h('div', { style: s.injectRow }, [\n h('button', { style: s.mini, onClick: () => commitReplace(sec.name) }, t('add')),\n h('button', { style: s.mini, onClick: () => setEditing(null) }, t('clearReplace')),\n ]),\n ])\n : h('div', { style: s.preview }, String(sec.text ?? '').slice(0, 140) || (sec.source === 'custom' ? t('empty') : t('dynamic'))),\n ]),\n h('div', { style: s.arrowCol }, [\n h('button', { style: s.arrow, disabled: index === 0, onClick: () => moveOrder(index, -1), title: t('moveUp') }, '↑'),\n h('button', { style: s.arrow, disabled: index === merged.length - 1, onClick: () => moveOrder(index, 1), title: t('moveDown') }, '↓'),\n ]),\n h('button', { style: s.mini, onClick: () => replace[sec.name] ? clearReplace(sec.name) : startReplace(sec.name, String(sec.text ?? '')) }, replace[sec.name] ? t('clearReplace') : t('replace')),\n sec.source === 'custom' ? h('button', { style: s.mini, onClick: () => removeCustom(sec.name), title: t('delete') }, t('delete')) : null,\n ])\n }),\n h('div', { style: s.injectBox }, [\n h('div', { style: s.rowTitle }, t('injectNew')),\n h(InjectForm, { onAdd: addSection, t }),\n ]),\n ])\n}\n\nfunction InjectForm({ onAdd, t }: { onAdd: (name: string, order: number, text: string) => void; t: Translate }): ReactElement {\n const [name, setName] = useState('')\n const [order, setOrder] = useState('120')\n const [text, setText] = useState('')\n\n const submit = (): void => {\n if (!name.trim()) return\n onAdd(name.trim(), Number(order) || 120, text)\n setName(''); setText(''); setOrder('120')\n }\n\n return h('div', {}, [\n h('div', { style: s.injectRow }, [\n h('input', { style: { ...s.input, width: '22%' }, placeholder: t('name'), value: name, onChange: (e) => setName(e.target.value) }),\n h('input', { style: { ...s.input, width: '12%' }, type: 'number', placeholder: t('order'), value: order, onChange: (e) => setOrder(e.target.value) }),\n h('input', { style: { ...s.input, flex: 1 }, placeholder: t('text'), value: text, onChange: (e) => setText(e.target.value) }),\n h('button', { style: s.mini, onClick: submit }, t('add')),\n ]),\n ])\n}\n","/** Tools tab: hide tools from the model catalog by exclude/include. */\nimport { createElement as h, type ReactElement } from 'react'\nimport type { Config, Inventory, SettingsScope } from './types.ts'\nimport type { Translate } from './locales.ts'\nimport { isToolHidden, setToolMode, toggleTool } from './presets.ts'\nimport { s } from './styles.ts'\n\nexport function ToolsTab({ cfg, inv, scope, t }: { cfg: Config; inv: Inventory | null; scope: SettingsScope; t: Translate }): ReactElement {\n const tools = inv?.tools ?? []\n const toolsCfg = cfg.tools ?? { exclude: [], include: [] }\n const includeMode = (toolsCfg.include ?? []).length > 0\n\n const toggle = (name: string, currentlyHidden: boolean): void => {\n scope.set('tools', toggleTool(name, currentlyHidden, toolsCfg))\n }\n const setIncludeMode = (on: boolean): void => {\n scope.set('tools', setToolMode(on, toolsCfg, tools.map((tool) => tool.name)))\n }\n\n return h('div', {}, [\n h('div', { style: s.bar }, [\n h('button', { style: includeMode ? s.tabActive : s.tab, onClick: () => setIncludeMode(true) }, t('includeMode')),\n h('button', { style: includeMode ? s.tab : s.tabActive, onClick: () => setIncludeMode(false) }, t('excludeMode')),\n ]),\n h('div', { style: s.list }, [\n tools.length === 0 ? h('div', { style: s.muted }, t('empty')) : null,\n tools.map((tool) => {\n // Hidden state comes from the CONFIG (exclude/include lists), not the\n // inventory snapshot, so toggling updates the checkbox immediately.\n const hidden = isToolHidden(tool.name, toolsCfg)\n return h('div', { key: tool.name, style: s.row }, [\n h('label', { style: s.switchWrap }, [\n h('input', { type: 'checkbox', checked: hidden, onChange: () => toggle(tool.name, hidden) }),\n h('span', { style: hidden ? s.badgeBlocked : s.badgeOk }, hidden ? t('hiddenOn') : t('hiddenOff')),\n ]),\n h('div', { style: s.rowBody }, [\n h('div', { style: s.rowTitle }, h('span', { style: s.code }, tool.name)),\n h('div', { style: s.preview }, String(tool.description ?? '').slice(0, 140)),\n ]),\n ])\n }),\n ]),\n ])\n}\n","/** Presets tab: save / apply / export / import complete customization snapshots. */\nimport { createElement as h, useRef, useState, type ReactElement, type ChangeEvent } from 'react'\nimport type { Config, Inventory, Preset, PresetData, SettingsScope } from './types.ts'\nimport type { Translate } from './locales.ts'\nimport { addImportedPresets, applyPresetData, buildPresetData, mergeSections, removePreset, serializePreset } from './presets.ts'\nimport { s } from './styles.ts'\n\nexport function PresetsTab({ cfg, inv, scope, t }: { cfg: Config; inv: Inventory | null; scope: SettingsScope; t: Translate }): ReactElement {\n const presets = cfg.presets ?? []\n const [name, setName] = useState('')\n const fileRef = useRef<HTMLInputElement | null>(null)\n\n const blockedNames = new Set(cfg.sections ?? [])\n const merged = mergeSections(inv, cfg, blockedNames)\n const currentNames = new Set(merged.map((sec) => sec.name))\n\n // Capture the current customization as a new preset (full snapshot). The\n // absolute order is converted to a relative form (each section records the\n // section it should follow) so the preset stays portable across prompts.\n const saveCurrent = (): void => {\n const presetName = name.trim() || `${t('preset')} ${presets.length + 1}`\n const data = buildPresetData(cfg, merged)\n scope.set('presets', [...presets, { id: genId(), name: presetName, data }])\n setName('')\n }\n\n // Apply a preset. The patch is computed by the pure helper:\n // - same-name sections are overridden (content + order)\n // - preset sections missing from the current prompt are added\n // - current sections NOT in the preset list are disabled (blocked)\n // - only the preset's ACTIVE sections are unblocked (its own blocked list\n // is preserved)\n const applyPreset = (preset: Preset): void => {\n const patch = applyPresetData(preset.data, cfg, currentNames)\n scope.set('inject', patch.inject)\n scope.set('replace', patch.replace)\n scope.set('sections', patch.sections)\n scope.set('tools', patch.tools)\n scope.set('activePreset', preset.id)\n }\n\n const deletePreset = (id: string): void => {\n const next = removePreset(presets, id, cfg.activePreset)\n scope.set('presets', next.presets)\n if (next.activeId === undefined && cfg.activePreset === id) scope.unset('activePreset')\n }\n\n // Serialize a preset to a downloadable JSON file.\n const exportPreset = (preset: Preset): void => {\n const blob = new Blob([JSON.stringify(serializePreset(preset), null, 2)], { type: 'application/json' })\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n a.download = `${preset.name}.json`\n a.click()\n URL.revokeObjectURL(url)\n }\n\n // Import preset(s) from a JSON file. Same-name presets are skipped; only\n // presets with new names are added to the local list.\n const onImportFile = (e: ChangeEvent<HTMLInputElement>): void => {\n const file = e.target.files?.[0]\n if (!file) return\n const reader = new FileReader()\n reader.onload = () => {\n try {\n const parsed = JSON.parse(String(reader.result))\n scope.set('presets', addImportedPresets(presets, parsed, genId))\n } catch { /* ignore invalid json */ }\n }\n reader.readAsText(file)\n e.target.value = ''\n }\n\n return h('div', { style: s.list }, [\n h('div', { style: s.injectBox }, [\n h('div', { style: s.rowTitle }, t('savePreset')),\n h('div', { style: s.injectRow }, [\n h('input', { style: { ...s.input, flex: 1 }, placeholder: t('presetName'), value: name, onChange: (e: ChangeEvent<HTMLInputElement>) => setName(e.target.value) }),\n h('button', { style: s.mini, onClick: saveCurrent }, t('save')),\n ]),\n ]),\n h('div', { style: s.injectBox }, [\n h('div', { style: s.rowTitle }, t('importPreset')),\n h('div', { style: s.injectRow }, [\n h('button', { style: s.mini, onClick: () => fileRef.current?.click() }, t('import')),\n h('input', { ref: fileRef, type: 'file', accept: '.json,application/json', style: { display: 'none' }, onChange: onImportFile }),\n ]),\n ]),\n presets.length === 0 ? h('div', { style: s.muted }, t('empty')) : null,\n presets.map((preset) => {\n const active = cfg.activePreset === preset.id\n return h('div', { key: preset.id, style: s.row }, [\n h('div', { style: s.rowBody }, [\n h('div', { style: s.rowTitle }, [\n h('span', { style: s.code }, preset.name),\n active ? h('span', { style: s.badgeOk }, t('active')) : null,\n ]),\n ]),\n h('button', { style: s.mini, onClick: () => applyPreset(preset) }, t('apply')),\n h('button', { style: s.mini, onClick: () => exportPreset(preset) }, t('export')),\n h('button', { style: s.mini, onClick: () => deletePreset(preset.id) }, t('delete')),\n ])\n }),\n ])\n}\n\nfunction genId(): string {\n return 'p_' + Date.now().toString(36) + Math.random().toString(36).slice(2, 7)\n}\n","/**\n * dsh-prompt-customizer — browser half (TypeScript source).\n *\n * Registers a \"提示词定制\" settings section (sidebar entry) and a plugin\n * settings card. The panel fetches the effective section / tool inventory\n * from the host `/api/prompt-customizer/inventory` and lets the user block,\n * replace, inject prompt sections and hide tools. All writes go through the\n * bound settings scope (`prompt-customizer` namespace) and apply live.\n *\n * Built by tsdown into the __ModuleLoader__ factory bundle at client/client.js;\n * the only external is the loader module table's react entry.\n */\nimport { createElement as h, useEffect, useState, type ReactElement } from 'react'\nimport { DICT, type Translate } from './locales.ts'\nimport { s } from './styles.ts'\nimport type { Config, Inventory, SettingsScope } from './types.ts'\nimport { SectionsTab } from './SectionsTab.tsx'\nimport { ToolsTab } from './ToolsTab.tsx'\nimport { PresetsTab } from './PresetsTab.tsx'\n\nconst NS = 'prompt-customizer'\nconst INVENTORY_URL = '/api/prompt-customizer/inventory'\n\nexport const name = 'prompt-customizer'\nexport const inject = ['slots', 'locale']\n\n/** The subset of the client cordis context this plugin touches. */\ninterface ClientContext {\n effect(callback: () => unknown, label?: string): void\n locale: {\n register(namespace: string, dicts: { zh: Record<string, string>; en: Record<string, string> }): unknown\n bind(namespace: string): Translate\n }\n inject(services: string[], callback: (scoped: SettingsScopeHost) => void): void\n}\n\n/** The host surface the settings card needs (settingsScope transport). */\ninterface SettingsScopeHost {\n settingsScope: {\n bind(options: { namespace: string }): SettingsScope\n }\n slots: {\n inject(name: string, register: () => unknown): void\n register(meta: Record<string, unknown>, render: () => unknown): unknown\n }\n}\n\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => ctx.locale.register(NS, DICT), 'prompt-customizer: locale')\n const t = ctx.locale.bind(NS)\n\n // Register a standalone settings section (sidebar menu entry) so the panel\n // only appears under \"提示词定制\", not as a card inside the \"插件\" settings\n // list. Goes through a NESTED inject of settingsScope so the panel can\n // read/write the namespace config; on hosts without the settings transport\n // it stays hidden instead of unmounting the whole plugin.\n ctx.inject(['settingsScope'], (scoped) => {\n const scope = scoped.settingsScope.bind({ namespace: NS })\n\n scoped.slots.inject('settings.section', () => scoped.slots.register({\n name: 'settings.section',\n id: 'prompt-customizer',\n order: 50,\n label: () => t('nav'),\n locale: NS,\n inject: () => ({ t }),\n }, () => h(Panel, { scope, t })))\n })\n}\n\n// ── Root panel ──────────────────────────────────────────────────────────────\n\nfunction Panel({ scope, t }: { scope: SettingsScope; t: Translate }): ReactElement {\n const [snap, setSnap] = useState(() => scope.getSnapshot())\n const [inv, setInv] = useState<Inventory | null>(null)\n const [tab, setTab] = useState<'sections' | 'tools' | 'presets'>('sections')\n const [error, setError] = useState<string | null>(null)\n\n useEffect(() => scope.subscribe(() => setSnap(scope.getSnapshot())), [scope])\n\n const refresh = (): void => {\n fetch(INVENTORY_URL)\n .then((r) => r.json())\n .then((data: Inventory) => { setInv(data); setError(null) })\n .catch((e: unknown) => setError(String(e instanceof Error ? e.message : e)))\n }\n useEffect(refresh, [])\n\n if (snap.status !== 'ready' && snap.value === undefined) {\n return h('div', { style: s.muted }, t('loading'))\n }\n\n const cfg: Config = snap.value && typeof snap.value === 'object'\n ? snap.value as Config\n : { sections: [], replace: {}, inject: [], tools: { exclude: [], include: [] } }\n\n return h('div', { style: s.root }, [\n h('div', { style: s.bar }, [\n h('button', { style: tab === 'sections' ? s.tabActive : s.tab, onClick: () => setTab('sections') }, t('tabsSections')),\n h('button', { style: tab === 'tools' ? s.tabActive : s.tab, onClick: () => setTab('tools') }, t('tabsTools')),\n h('button', { style: tab === 'presets' ? s.tabActive : s.tab, onClick: () => setTab('presets') }, t('tabsPresets')),\n h('button', { style: s.refresh, onClick: refresh }, t('refresh')),\n ]),\n error ? h('div', { style: s.error }, String(error)) : null,\n tab === 'sections'\n ? h(SectionsTab, { cfg, inv, scope, t })\n : tab === 'tools'\n ? h(ToolsTab, { cfg, inv, scope, t })\n : h(PresetsTab, { cfg, inv, scope, t }),\n ])\n}\n"],"mappings":"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"}
|
package/lib/index.js
CHANGED
|
@@ -84,12 +84,27 @@ export function apply(ctx, entry = {}) {
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
/** Resolve
|
|
87
|
+
/** Resolve `value` but never wait longer than `ms`; fall back to `fallback`. */
|
|
88
|
+
function withTimeout(value, ms, fallback) {
|
|
89
|
+
return Promise.race([
|
|
90
|
+
Promise.resolve(value),
|
|
91
|
+
new Promise((resolve) => setTimeout(() => resolve(fallback), ms)),
|
|
92
|
+
])
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Resolve the `standard` agent's standing scope key when a roster is present.
|
|
97
|
+
*
|
|
98
|
+
* `standingKeyFor` mounts a whole agent preset (compose plugins) which can be
|
|
99
|
+
* slow or depend on services that are not ready yet — for a read-only inventory
|
|
100
|
+
* browse we must never let that hang the request. If it does not settle within
|
|
101
|
+
* a short budget we fall back to the global layer (scopeKey = undefined).
|
|
102
|
+
*/
|
|
88
103
|
async function resolveStandardScope(ctx) {
|
|
89
104
|
try {
|
|
90
105
|
const presets = ctx.get('agentPresets')
|
|
91
106
|
if (presets && typeof presets.standingKeyFor === 'function') {
|
|
92
|
-
return await presets.standingKeyFor('standard')
|
|
107
|
+
return await withTimeout(presets.standingKeyFor('standard'), 1500, undefined)
|
|
93
108
|
}
|
|
94
109
|
} catch { /* no roster — fall back to the global layer */ }
|
|
95
110
|
return undefined
|
|
@@ -110,11 +125,15 @@ async function buildInventory(ctx, cfg) {
|
|
|
110
125
|
// Resolve a section's text. Dynamic sections register a function; call it
|
|
111
126
|
// with a best-effort context (same shape the assemble passes) so the
|
|
112
127
|
// inventory can echo the real generated content for replacement. Fall back
|
|
113
|
-
// to a marker if the function throws.
|
|
128
|
+
// to a marker if the function throws or returns a pending value.
|
|
114
129
|
const resolveText = (section) => {
|
|
115
130
|
if (typeof section.text === 'function') {
|
|
116
|
-
try {
|
|
117
|
-
|
|
131
|
+
try {
|
|
132
|
+
const value = section.text({ scope: scopeKey })
|
|
133
|
+
// Async dynamic sections return a thenable we cannot echo synchronously.
|
|
134
|
+
if (value && typeof value.then === 'function') return '<动态生成>'
|
|
135
|
+
return String(value ?? '')
|
|
136
|
+
} catch { return '<动态生成>' }
|
|
118
137
|
}
|
|
119
138
|
return String(section.text ?? '')
|
|
120
139
|
}
|
|
@@ -131,15 +150,25 @@ async function buildInventory(ctx, cfg) {
|
|
|
131
150
|
.sort((a, b) => a.order - b.order)
|
|
132
151
|
|
|
133
152
|
let tools = []
|
|
153
|
+
// Enumerate tools from the resolved scope, falling back to the global layer
|
|
154
|
+
// when the scope key was unavailable (e.g. the standing mount timed out), so
|
|
155
|
+
// the catalog is never empty just because agent composition was not ready.
|
|
134
156
|
try {
|
|
135
|
-
const
|
|
136
|
-
|
|
157
|
+
const byName = new Map()
|
|
158
|
+
for (const scope of [scopeKey, undefined]) {
|
|
159
|
+
let schemas = []
|
|
160
|
+
try { schemas = ctx.tools.schemas(scope) ?? [] } catch { schemas = [] }
|
|
161
|
+
for (const tool of schemas) {
|
|
162
|
+
if (tool.name && tool.name !== undefined && !byName.has(tool.name)) byName.set(tool.name, tool)
|
|
163
|
+
}
|
|
164
|
+
if (byName.size > 0) break
|
|
165
|
+
}
|
|
166
|
+
tools = [...byName.values()]
|
|
137
167
|
.map((tool) => ({
|
|
138
168
|
name: tool.name,
|
|
139
169
|
description: typeof tool.description === 'string' ? tool.description : '',
|
|
140
170
|
hidden: include ? !include.has(tool.name) : exclude.has(tool.name),
|
|
141
171
|
}))
|
|
142
|
-
.filter((tool) => tool.name && tool.name !== undefined)
|
|
143
172
|
.sort((a, b) => a.name.localeCompare(b.name))
|
|
144
173
|
} catch { /* tools registry unresolved — return empty list */ }
|
|
145
174
|
|
package/lib/schema.js
CHANGED
|
@@ -12,11 +12,14 @@ export const Config = Schema.object({
|
|
|
12
12
|
sections: Schema.array(Schema.string()).default([]),
|
|
13
13
|
// Replace a section's text by name (original order kept).
|
|
14
14
|
replace: Schema.dict(Schema.string()).default({}),
|
|
15
|
-
// Inject / overwrite sections (by name + order + text).
|
|
15
|
+
// Inject / overwrite sections (by name + order + text). `custom` marks a
|
|
16
|
+
// section this plugin generated (a hidden marker so it can be identified as
|
|
17
|
+
// deletable, even across presets, without relying on name collisions).
|
|
16
18
|
inject: Schema.array(Schema.object({
|
|
17
19
|
name: Schema.string().required(),
|
|
18
20
|
order: Schema.number().default(120),
|
|
19
21
|
text: Schema.string().required(),
|
|
22
|
+
custom: Schema.boolean().default(false),
|
|
20
23
|
})).default([]),
|
|
21
24
|
// Tool catalog filtering: exclude hides listed tools; include (when non-empty)
|
|
22
25
|
// keeps only listed tools and wins over exclude.
|
|
@@ -36,6 +39,7 @@ export const Config = Schema.object({
|
|
|
36
39
|
name: Schema.string().required(),
|
|
37
40
|
after: Schema.string(),
|
|
38
41
|
text: Schema.string(),
|
|
42
|
+
custom: Schema.boolean().default(false),
|
|
39
43
|
})),
|
|
40
44
|
tools: Schema.object({
|
|
41
45
|
exclude: Schema.array(Schema.string()),
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-prompt-customizer",
|
|
3
3
|
"description": "System-prompt & tool customizer for DeepSeek Harness with a settings-panel UI: block / replace / inject prompt sections by name, and hide tools from the model catalog by exclude/include. Config lives in the dsh settings document (namespace prompt-customizer).",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.5",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
7
7
|
"main": "lib/index.js",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { createElement as h, useState, type ReactElement, type DragEvent, type ChangeEvent } from 'react'
|
|
3
3
|
import type { Config, Inventory, SettingsScope } from './types.ts'
|
|
4
4
|
import type { Translate } from './locales.ts'
|
|
5
|
-
import { mergeSections, type Section } from './presets.ts'
|
|
5
|
+
import { mergeSections, removeSection, type Section } from './presets.ts'
|
|
6
6
|
import { s } from './styles.ts'
|
|
7
7
|
|
|
8
8
|
export function SectionsTab({ cfg, inv, scope, t }: { cfg: Config; inv: Inventory | null; scope: SettingsScope; t: Translate }): ReactElement {
|
|
@@ -44,16 +44,28 @@ export function SectionsTab({ cfg, inv, scope, t }: { cfg: Config; inv: Inventor
|
|
|
44
44
|
|
|
45
45
|
// Persist the full ordered list. We treat the sorted list as an array and
|
|
46
46
|
// re-index every section with a sequential integer order (0, 1, 2, …), so
|
|
47
|
-
// there are never duplicate or fractional orders.
|
|
48
|
-
//
|
|
47
|
+
// there are never duplicate or fractional orders. System sections keep empty
|
|
48
|
+
// text (so we never freeze their dynamically generated content); custom
|
|
49
|
+
// (plugin-generated) sections keep their own text and the hidden `custom`
|
|
50
|
+
// marker, so they render real content and stay identifiable/deletable.
|
|
49
51
|
const persistOrder = (ordered: Section[]): void => {
|
|
50
52
|
const list = ordered.map((sec, i) => {
|
|
51
53
|
const existing = (cfg.inject ?? []).find((x) => x.name === sec.name)
|
|
52
|
-
|
|
54
|
+
const isCustom = sec.source === 'custom'
|
|
55
|
+
return { name: sec.name, order: i, text: isCustom ? (sec.text ?? '') : (existing?.text ?? ''), custom: isCustom }
|
|
53
56
|
})
|
|
54
57
|
scope.set('inject', list)
|
|
55
58
|
}
|
|
56
59
|
|
|
60
|
+
// Delete a custom (plugin-added) section: remove it from inject / sections /
|
|
61
|
+
// replace so it no longer appears in the panel at all.
|
|
62
|
+
const removeCustom = (name: string): void => {
|
|
63
|
+
const patch = removeSection(name, cfg)
|
|
64
|
+
scope.set('sections', patch.sections)
|
|
65
|
+
scope.set('inject', patch.inject)
|
|
66
|
+
scope.set('replace', patch.replace)
|
|
67
|
+
}
|
|
68
|
+
|
|
57
69
|
// Move a section up/down one slot (array-index swap), then re-index.
|
|
58
70
|
const moveOrder = (index: number, dir: -1 | 1): void => {
|
|
59
71
|
const target = index + dir
|
|
@@ -92,10 +104,12 @@ export function SectionsTab({ cfg, inv, scope, t }: { cfg: Config; inv: Inventor
|
|
|
92
104
|
}
|
|
93
105
|
const onDragEnd = (): void => { setDragName(null); setDropTarget(null) }
|
|
94
106
|
|
|
95
|
-
// Add a new injected section, placed by its order hint, then re-index.
|
|
107
|
+
// Add a new injected section, placed by its order hint, then re-index. The
|
|
108
|
+
// section is marked `custom` (the hidden marker) so it is identified as
|
|
109
|
+
// plugin-generated and thus deletable, and its own text is preserved.
|
|
96
110
|
const addSection = (name: string, order: number, text: string): void => {
|
|
97
111
|
const next = merged.slice()
|
|
98
|
-
const entry: Section = { name, order, text
|
|
112
|
+
const entry: Section = { name, order, text, active: true, replaced: false, source: 'custom' }
|
|
99
113
|
const existing = next.findIndex((x) => x.name === name)
|
|
100
114
|
if (existing >= 0) next[existing] = entry
|
|
101
115
|
else {
|
|
@@ -137,6 +151,7 @@ export function SectionsTab({ cfg, inv, scope, t }: { cfg: Config; inv: Inventor
|
|
|
137
151
|
h('div', { style: s.rowTitle }, [
|
|
138
152
|
h('span', { style: s.code }, sec.name),
|
|
139
153
|
h('span', { style: s.orderTag }, '#' + index),
|
|
154
|
+
h('span', { style: sec.source === 'custom' ? s.badgeCustom : s.badgeSystem }, sec.source === 'custom' ? t('manual') : t('system')),
|
|
140
155
|
sec.replaced ? h('span', { style: s.badgeReplaced }, t('replaced')) : null,
|
|
141
156
|
]),
|
|
142
157
|
isEditing
|
|
@@ -147,13 +162,14 @@ export function SectionsTab({ cfg, inv, scope, t }: { cfg: Config; inv: Inventor
|
|
|
147
162
|
h('button', { style: s.mini, onClick: () => setEditing(null) }, t('clearReplace')),
|
|
148
163
|
]),
|
|
149
164
|
])
|
|
150
|
-
: h('div', { style: s.preview }, String(sec.text ?? '').slice(0, 140) || t('dynamic')),
|
|
165
|
+
: h('div', { style: s.preview }, String(sec.text ?? '').slice(0, 140) || (sec.source === 'custom' ? t('empty') : t('dynamic'))),
|
|
151
166
|
]),
|
|
152
167
|
h('div', { style: s.arrowCol }, [
|
|
153
168
|
h('button', { style: s.arrow, disabled: index === 0, onClick: () => moveOrder(index, -1), title: t('moveUp') }, '↑'),
|
|
154
169
|
h('button', { style: s.arrow, disabled: index === merged.length - 1, onClick: () => moveOrder(index, 1), title: t('moveDown') }, '↓'),
|
|
155
170
|
]),
|
|
156
171
|
h('button', { style: s.mini, onClick: () => replace[sec.name] ? clearReplace(sec.name) : startReplace(sec.name, String(sec.text ?? '')) }, replace[sec.name] ? t('clearReplace') : t('replace')),
|
|
172
|
+
sec.source === 'custom' ? h('button', { style: s.mini, onClick: () => removeCustom(sec.name), title: t('delete') }, t('delete')) : null,
|
|
157
173
|
])
|
|
158
174
|
}),
|
|
159
175
|
h('div', { style: s.injectBox }, [
|
package/src/client/locales.ts
CHANGED
|
@@ -11,8 +11,10 @@ export const DICT = {
|
|
|
11
11
|
blockedOff: '未屏蔽',
|
|
12
12
|
blockedOn: '已屏蔽',
|
|
13
13
|
replaced: '已替换',
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
manual: '手动',
|
|
15
|
+
system: '系统',
|
|
16
|
+
replace: '编辑',
|
|
17
|
+
clearReplace: '清除',
|
|
16
18
|
injectNew: '注入新段',
|
|
17
19
|
name: '名称',
|
|
18
20
|
order: '顺序',
|
|
@@ -49,6 +51,8 @@ export const DICT = {
|
|
|
49
51
|
blockedOff: 'Active',
|
|
50
52
|
blockedOn: 'Blocked',
|
|
51
53
|
replaced: 'Replaced',
|
|
54
|
+
manual: 'Manual',
|
|
55
|
+
system: 'System',
|
|
52
56
|
replace: 'Replace',
|
|
53
57
|
clearReplace: 'Clear',
|
|
54
58
|
injectNew: 'Inject section',
|
package/src/client/presets.ts
CHANGED
|
@@ -7,6 +7,8 @@ export interface Section {
|
|
|
7
7
|
text?: string
|
|
8
8
|
active: boolean
|
|
9
9
|
replaced: boolean
|
|
10
|
+
/** `system` = read from the host prompt inventory (not deletable); `custom` = added by this plugin (deletable). */
|
|
11
|
+
source?: 'system' | 'custom'
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
/** The config fields a preset apply must write back to the settings scope. */
|
|
@@ -17,20 +19,35 @@ export interface ConfigPatch {
|
|
|
17
19
|
tools: { exclude: string[]; include: string[] }
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Merge the inventory sections with the user's inject list, sorted by order.
|
|
24
|
+
* Sections read from the inventory (other plugins) are `system`; sections this
|
|
25
|
+
* plugin generated carry a hidden `custom` marker and are `custom`. The source
|
|
26
|
+
* is decided by the hidden marker — never by name collisions — so a custom
|
|
27
|
+
* section keeps its identity (and its deletability) even when its name happens
|
|
28
|
+
* to match an inventory section, and across preset switches. Custom sections
|
|
29
|
+
* always render their own text; they can never be "<动态生成>".
|
|
30
|
+
*/
|
|
21
31
|
export function mergeSections(inv: Inventory | null, cfg: Config, blockedNames: ReadonlySet<string>): Section[] {
|
|
22
32
|
const map = new Map<string, Section>()
|
|
23
|
-
for (const sec of inv?.sections ?? []) map.set(sec.name, sec)
|
|
33
|
+
for (const sec of inv?.sections ?? []) map.set(sec.name, { ...sec, source: 'system' })
|
|
24
34
|
for (const item of cfg.inject ?? []) {
|
|
35
|
+
const isCustom = item.custom === true
|
|
25
36
|
const existing = map.get(item.name)
|
|
26
|
-
if (existing)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
37
|
+
if (existing) {
|
|
38
|
+
map.set(item.name, isCustom
|
|
39
|
+
? { ...existing, order: item.order, text: item.text ?? '', source: 'custom' }
|
|
40
|
+
: { ...existing, order: item.order, source: 'system' })
|
|
41
|
+
} else {
|
|
42
|
+
map.set(item.name, {
|
|
43
|
+
name: item.name,
|
|
44
|
+
order: item.order,
|
|
45
|
+
text: item.text ?? '',
|
|
46
|
+
active: !blockedNames.has(item.name),
|
|
47
|
+
replaced: false,
|
|
48
|
+
source: isCustom ? 'custom' : 'system',
|
|
49
|
+
})
|
|
50
|
+
}
|
|
34
51
|
}
|
|
35
52
|
return [...map.values()].sort((a, b) => a.order - b.order)
|
|
36
53
|
}
|
|
@@ -41,13 +58,15 @@ export function mergeSections(inv: Inventory | null, cfg: Config, blockedNames:
|
|
|
41
58
|
* order; each anchored section is inserted right after its anchor; any
|
|
42
59
|
* remaining sections (cycles) are appended.
|
|
43
60
|
*/
|
|
44
|
-
export function resolveOrder(presetOrder: PresetData['order']): Array<{ name: string; order: number; text: string }> {
|
|
61
|
+
export function resolveOrder(presetOrder: PresetData['order']): Array<{ name: string; order: number; text: string; custom?: boolean }> {
|
|
45
62
|
const list = presetOrder ?? []
|
|
46
63
|
const afterMap = new Map<string, string | undefined>()
|
|
47
64
|
const textMap = new Map<string, string>()
|
|
65
|
+
const customMap = new Map<string, boolean>()
|
|
48
66
|
for (const sec of list) {
|
|
49
67
|
afterMap.set(sec.name, sec.after)
|
|
50
68
|
textMap.set(sec.name, sec.text)
|
|
69
|
+
customMap.set(sec.name, sec.custom === true)
|
|
51
70
|
}
|
|
52
71
|
|
|
53
72
|
const result: string[] = []
|
|
@@ -76,7 +95,7 @@ export function resolveOrder(presetOrder: PresetData['order']): Array<{ name: st
|
|
|
76
95
|
if (!placed.has(sec.name)) { result.push(sec.name); placed.add(sec.name) }
|
|
77
96
|
}
|
|
78
97
|
|
|
79
|
-
return result.map((name, i) => ({ name, order: i, text: textMap.get(name) ?? '' }))
|
|
98
|
+
return result.map((name, i) => ({ name, order: i, text: textMap.get(name) ?? '', custom: customMap.get(name) }))
|
|
80
99
|
}
|
|
81
100
|
|
|
82
101
|
/**
|
|
@@ -86,10 +105,14 @@ export function resolveOrder(presetOrder: PresetData['order']): Array<{ name: st
|
|
|
86
105
|
* ordered set, and thus a meaningful "active list" when applied.
|
|
87
106
|
*/
|
|
88
107
|
export function buildPresetData(cfg: Config, merged: Section[]): PresetData {
|
|
108
|
+
// Only custom (plugin-added) sections carry their own text into the preset's
|
|
109
|
+
// order list. System sections keep an empty text so applying the preset only
|
|
110
|
+
// reorders them and never freezes their dynamically generated content.
|
|
89
111
|
const order = merged.map((sec, i) => ({
|
|
90
112
|
name: sec.name,
|
|
91
113
|
after: i > 0 ? merged[i - 1].name : undefined,
|
|
92
|
-
text: sec.text ?? '',
|
|
114
|
+
text: sec.source === 'custom' ? (sec.text ?? '') : '',
|
|
115
|
+
custom: sec.source === 'custom',
|
|
93
116
|
}))
|
|
94
117
|
return {
|
|
95
118
|
sections: cfg.sections,
|
|
@@ -106,12 +129,33 @@ export function buildPresetData(cfg: Config, merged: Section[]): PresetData {
|
|
|
106
129
|
* - current sections NOT in the preset's ordered list are disabled by default
|
|
107
130
|
* - only the preset's ACTIVE sections (in the order list but not in its block
|
|
108
131
|
* list) are unblocked; the preset's own blocked sections stay blocked.
|
|
132
|
+
*
|
|
133
|
+
* Custom injected sections that exist in the current config but are NOT in the
|
|
134
|
+
* preset's ordered list are preserved in the resulting inject list (so they
|
|
135
|
+
* stay visible) and disabled (added to the blocked set), rather than silently
|
|
136
|
+
* dropped — a preset must not make the user's own injected sections vanish.
|
|
109
137
|
*/
|
|
110
138
|
export function applyPresetData(data: PresetData, cfg: Config, currentNames: ReadonlySet<string>): ConfigPatch {
|
|
111
139
|
const presetOrder = data.order ?? []
|
|
112
140
|
const presetNames = new Set(presetOrder.map((x) => x.name))
|
|
113
141
|
const blocked = new Set(data.sections ?? [])
|
|
114
142
|
|
|
143
|
+
// Inject list = the preset's resolved order, then any custom injected
|
|
144
|
+
// sections present in the current config but absent from the preset list.
|
|
145
|
+
// Keeping them in `inject` makes them visible; they are disabled below.
|
|
146
|
+
// Kept customs continue after the preset's own indexes; if the preset covers
|
|
147
|
+
// only a small subset they may tie with the inventory's native orders — the
|
|
148
|
+
// merge view sorts stably (inventory first), so ordering stays deterministic,
|
|
149
|
+
// and the next reorder/persist re-indexes everything contiguously anyway.
|
|
150
|
+
const inject = resolveOrder(presetOrder)
|
|
151
|
+
const kept = new Set(inject.map((x) => x.name))
|
|
152
|
+
let order = inject.length
|
|
153
|
+
for (const item of cfg.inject ?? []) {
|
|
154
|
+
if (!item || !item.name || presetNames.has(item.name) || kept.has(item.name)) continue
|
|
155
|
+
kept.add(item.name)
|
|
156
|
+
inject.push({ name: item.name, order: order++, text: item.text ?? '', custom: item.custom === true })
|
|
157
|
+
}
|
|
158
|
+
|
|
115
159
|
// Only enforce the "active set" when the preset actually defines an order.
|
|
116
160
|
// An empty order (e.g. a hand-authored / imported preset that only blocks a
|
|
117
161
|
// few sections) must not default-disable every current section.
|
|
@@ -124,11 +168,29 @@ export function applyPresetData(data: PresetData, cfg: Config, currentNames: Rea
|
|
|
124
168
|
return {
|
|
125
169
|
sections: [...blocked],
|
|
126
170
|
replace: { ...(cfg.replace ?? {}), ...(data.replace ?? {}) },
|
|
127
|
-
inject
|
|
171
|
+
inject,
|
|
128
172
|
tools: { exclude: data.tools?.exclude ?? [], include: data.tools?.include ?? [] },
|
|
129
173
|
}
|
|
130
174
|
}
|
|
131
175
|
|
|
176
|
+
/**
|
|
177
|
+
* Remove a custom (plugin-added) section from the config: it disappears from
|
|
178
|
+
* the inject list, is no longer forced into the blocked set, and any replace
|
|
179
|
+
* text for it is cleared. The remaining inject entries are re-indexed to a
|
|
180
|
+
* contiguous 0..n-1 order (the virtual index model), so removal never leaves
|
|
181
|
+
* holes. System sections read from the host inventory cannot be removed this
|
|
182
|
+
* way (they would just come back on the next inventory read).
|
|
183
|
+
*/
|
|
184
|
+
export function removeSection(name: string, cfg: Config): Pick<Config, 'sections' | 'inject' | 'replace'> {
|
|
185
|
+
const sections = (cfg.sections ?? []).filter((n) => n !== name)
|
|
186
|
+
const inject = (cfg.inject ?? [])
|
|
187
|
+
.filter((item) => item.name !== name)
|
|
188
|
+
.map((item, i) => ({ ...item, order: i }))
|
|
189
|
+
const replace = { ...(cfg.replace ?? {}) }
|
|
190
|
+
delete replace[name]
|
|
191
|
+
return { sections, inject, replace }
|
|
192
|
+
}
|
|
193
|
+
|
|
132
194
|
// ── Tool filtering ──────────────────────────────────────────────────────────
|
|
133
195
|
|
|
134
196
|
export type ToolsCfg = { exclude?: string[]; include?: string[] }
|
|
@@ -146,8 +208,16 @@ export function toggleTool(name: string, currentlyHidden: boolean, toolsCfg: Too
|
|
|
146
208
|
const exclude = toolsCfg?.exclude ?? []
|
|
147
209
|
if (include.length > 0) {
|
|
148
210
|
const next = include.slice()
|
|
149
|
-
if (currentlyHidden)
|
|
150
|
-
|
|
211
|
+
if (currentlyHidden) {
|
|
212
|
+
next.push(name)
|
|
213
|
+
// Re-showing a tool in whitelist mode must also clear any stale blacklist
|
|
214
|
+
// entry, otherwise the name lingers in both lists and reappears as hidden
|
|
215
|
+
// if the user later switches back to blacklist mode.
|
|
216
|
+
const nextExclude = exclude.filter((n) => n !== name)
|
|
217
|
+
return { exclude: nextExclude, include: next }
|
|
218
|
+
}
|
|
219
|
+
const i = next.indexOf(name)
|
|
220
|
+
if (i >= 0) next.splice(i, 1)
|
|
151
221
|
return { exclude, include: next }
|
|
152
222
|
}
|
|
153
223
|
const next = exclude.slice()
|
package/src/client/styles.ts
CHANGED
|
@@ -18,6 +18,8 @@ export const s = (() => {
|
|
|
18
18
|
badgeOk: { color: '#3fb950', fontSize: 11, border: '1px solid rgba(63,185,80,.5)', borderRadius: 999, padding: '0 6px' },
|
|
19
19
|
badgeBlocked: { color: '#f85149', fontSize: 11, border: '1px solid rgba(248,81,73,.5)', borderRadius: 999, padding: '0 6px' },
|
|
20
20
|
badgeReplaced: { color: '#d29922', fontSize: 11, border: '1px solid rgba(210,153,34,.5)', borderRadius: 999, padding: '0 6px' },
|
|
21
|
+
badgeCustom: { color: '#58a6ff', fontSize: 11, border: '1px solid rgba(88,166,255,.5)', borderRadius: 999, padding: '0 6px' },
|
|
22
|
+
badgeSystem: { color: 'rgba(128,128,128,.85)', fontSize: 11, border: '1px solid rgba(128,128,128,.4)', borderRadius: 999, padding: '0 6px' },
|
|
21
23
|
mini: { padding: '3px 8px', border: '1px solid rgba(128,128,128,.35)', borderRadius: 6, background: 'transparent', cursor: 'pointer', color: 'inherit', whiteSpace: 'nowrap' },
|
|
22
24
|
arrowCol: { display: 'flex', flexDirection: 'column', gap: 2 },
|
|
23
25
|
arrow: { padding: '0 6px', border: '1px solid rgba(128,128,128,.3)', borderRadius: 4, background: 'transparent', cursor: 'pointer', color: 'inherit', fontSize: 11, lineHeight: 1.4, opacity: 0.8 },
|
package/src/client/types.ts
CHANGED
|
@@ -18,7 +18,7 @@ export interface Inventory {
|
|
|
18
18
|
export interface Config {
|
|
19
19
|
sections?: string[]
|
|
20
20
|
replace?: Record<string, string>
|
|
21
|
-
inject?: Array<{ name: string; order: number; text: string }>
|
|
21
|
+
inject?: Array<{ name: string; order: number; text: string; custom?: boolean }>
|
|
22
22
|
tools?: { exclude?: string[]; include?: string[] }
|
|
23
23
|
presets?: Preset[]
|
|
24
24
|
activePreset?: string
|
|
@@ -36,6 +36,6 @@ export interface PresetData {
|
|
|
36
36
|
sections?: string[]
|
|
37
37
|
replace?: Record<string, string>
|
|
38
38
|
/** Relative order: each section records the section it should follow. */
|
|
39
|
-
order?: Array<{ name: string; after?: string; text: string }>
|
|
39
|
+
order?: Array<{ name: string; after?: string; text: string; custom?: boolean }>
|
|
40
40
|
tools?: { exclude?: string[]; include?: string[] }
|
|
41
41
|
}
|