dsh-prompt-customizer 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/client.js +124 -97
- package/client/client.js.map +1 -1
- package/package.json +1 -1
- package/src/client/PresetsTab.tsx +13 -83
- package/src/client/SectionsTab.tsx +2 -24
- package/src/client/presets.ts +125 -0
package/client/client.js
CHANGED
|
@@ -281,6 +281,121 @@ window.__ModuleLoader__.load({
|
|
|
281
281
|
};
|
|
282
282
|
})();
|
|
283
283
|
//#endregion
|
|
284
|
+
//#region src/client/presets.ts
|
|
285
|
+
/** Merge the inventory sections with the user's inject list, sorted by order. */
|
|
286
|
+
function mergeSections(inv, cfg, blockedNames) {
|
|
287
|
+
const map = /* @__PURE__ */ new Map();
|
|
288
|
+
for (const sec of inv?.sections ?? []) map.set(sec.name, sec);
|
|
289
|
+
for (const item of cfg.inject ?? []) {
|
|
290
|
+
const existing = map.get(item.name);
|
|
291
|
+
if (existing) map.set(item.name, {
|
|
292
|
+
...existing,
|
|
293
|
+
order: item.order
|
|
294
|
+
});
|
|
295
|
+
else map.set(item.name, {
|
|
296
|
+
name: item.name,
|
|
297
|
+
order: item.order,
|
|
298
|
+
text: item.text || "<动态生成>",
|
|
299
|
+
active: !blockedNames.has(item.name),
|
|
300
|
+
replaced: false
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
return [...map.values()].sort((a, b) => a.order - b.order);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Resolve a preset's relative order into an absolute ordered list (0..n-1).
|
|
307
|
+
* Sections with no anchor (or whose anchor is absent) come first in preset
|
|
308
|
+
* order; each anchored section is inserted right after its anchor; any
|
|
309
|
+
* remaining sections (cycles) are appended.
|
|
310
|
+
*/
|
|
311
|
+
function resolveOrder(presetOrder) {
|
|
312
|
+
const list = presetOrder ?? [];
|
|
313
|
+
const afterMap = /* @__PURE__ */ new Map();
|
|
314
|
+
const textMap = /* @__PURE__ */ new Map();
|
|
315
|
+
for (const sec of list) {
|
|
316
|
+
afterMap.set(sec.name, sec.after);
|
|
317
|
+
textMap.set(sec.name, sec.text);
|
|
318
|
+
}
|
|
319
|
+
const result = [];
|
|
320
|
+
const placed = /* @__PURE__ */ new Set();
|
|
321
|
+
for (const sec of list) {
|
|
322
|
+
const anchor = afterMap.get(sec.name);
|
|
323
|
+
if (!anchor || !afterMap.has(anchor)) {
|
|
324
|
+
result.push(sec.name);
|
|
325
|
+
placed.add(sec.name);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
let changed = true;
|
|
329
|
+
while (changed) {
|
|
330
|
+
changed = false;
|
|
331
|
+
for (const sec of list) {
|
|
332
|
+
if (placed.has(sec.name)) continue;
|
|
333
|
+
const anchor = afterMap.get(sec.name);
|
|
334
|
+
if (anchor && placed.has(anchor)) {
|
|
335
|
+
result.splice(result.indexOf(anchor) + 1, 0, sec.name);
|
|
336
|
+
placed.add(sec.name);
|
|
337
|
+
changed = true;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
for (const sec of list) if (!placed.has(sec.name)) {
|
|
342
|
+
result.push(sec.name);
|
|
343
|
+
placed.add(sec.name);
|
|
344
|
+
}
|
|
345
|
+
return result.map((name, i) => ({
|
|
346
|
+
name,
|
|
347
|
+
order: i,
|
|
348
|
+
text: textMap.get(name) ?? ""
|
|
349
|
+
}));
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Build a preset snapshot from the current config. The order list is derived
|
|
353
|
+
* from the MERGED section list (not just `cfg.inject`) so that a preset saved
|
|
354
|
+
* after only blocking sections (without reordering) still carries the full
|
|
355
|
+
* ordered set, and thus a meaningful "active list" when applied.
|
|
356
|
+
*/
|
|
357
|
+
function buildPresetData(cfg, merged) {
|
|
358
|
+
const order = merged.map((sec, i) => ({
|
|
359
|
+
name: sec.name,
|
|
360
|
+
after: i > 0 ? merged[i - 1].name : void 0,
|
|
361
|
+
text: sec.text ?? ""
|
|
362
|
+
}));
|
|
363
|
+
return {
|
|
364
|
+
sections: cfg.sections,
|
|
365
|
+
replace: cfg.replace,
|
|
366
|
+
order,
|
|
367
|
+
tools: cfg.tools
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Compute the config patch when applying a preset:
|
|
372
|
+
* - same-name sections are overridden (the order list drives this at runtime)
|
|
373
|
+
* - preset sections missing from the current prompt are added
|
|
374
|
+
* - current sections NOT in the preset's ordered list are disabled by default
|
|
375
|
+
* - only the preset's ACTIVE sections (in the order list but not in its block
|
|
376
|
+
* list) are unblocked; the preset's own blocked sections stay blocked.
|
|
377
|
+
*/
|
|
378
|
+
function applyPresetData(data, cfg, currentNames) {
|
|
379
|
+
const presetOrder = data.order ?? [];
|
|
380
|
+
const presetNames = new Set(presetOrder.map((x) => x.name));
|
|
381
|
+
const blocked = new Set(data.sections ?? []);
|
|
382
|
+
const activeNames = new Set([...presetNames].filter((n) => !blocked.has(n)));
|
|
383
|
+
for (const name of currentNames) if (!presetNames.has(name)) blocked.add(name);
|
|
384
|
+
for (const name of activeNames) blocked.delete(name);
|
|
385
|
+
return {
|
|
386
|
+
sections: [...blocked],
|
|
387
|
+
replace: {
|
|
388
|
+
...cfg.replace ?? {},
|
|
389
|
+
...data.replace ?? {}
|
|
390
|
+
},
|
|
391
|
+
inject: resolveOrder(presetOrder),
|
|
392
|
+
tools: {
|
|
393
|
+
exclude: data.tools?.exclude ?? [],
|
|
394
|
+
include: data.tools?.include ?? []
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
//#endregion
|
|
284
399
|
//#region src/client/SectionsTab.tsx
|
|
285
400
|
/** Prompt-sections tab: block / replace / inject / reorder prompt sections. */
|
|
286
401
|
function SectionsTab({ cfg, inv, scope, t }) {
|
|
@@ -290,25 +405,7 @@ window.__ModuleLoader__.load({
|
|
|
290
405
|
const [draft, setDraft] = (0, react.useState)("");
|
|
291
406
|
const [dragName, setDragName] = (0, react.useState)(null);
|
|
292
407
|
const [dropTarget, setDropTarget] = (0, react.useState)(null);
|
|
293
|
-
const merged = (
|
|
294
|
-
const map = /* @__PURE__ */ new Map();
|
|
295
|
-
for (const sec of inv?.sections ?? []) map.set(sec.name, sec);
|
|
296
|
-
for (const item of cfg.inject ?? []) {
|
|
297
|
-
const existing = map.get(item.name);
|
|
298
|
-
if (existing) map.set(item.name, {
|
|
299
|
-
...existing,
|
|
300
|
-
order: item.order
|
|
301
|
-
});
|
|
302
|
-
else map.set(item.name, {
|
|
303
|
-
name: item.name,
|
|
304
|
-
order: item.order,
|
|
305
|
-
text: item.text || "<动态生成>",
|
|
306
|
-
active: !blockedNames.has(item.name),
|
|
307
|
-
replaced: false
|
|
308
|
-
});
|
|
309
|
-
}
|
|
310
|
-
return [...map.values()].sort((a, b) => a.order - b.order);
|
|
311
|
-
})();
|
|
408
|
+
const merged = mergeSections(inv, cfg, blockedNames);
|
|
312
409
|
const toggleBlock = (name, currentlyBlocked) => {
|
|
313
410
|
const list = (cfg.sections ?? []).slice();
|
|
314
411
|
if (currentlyBlocked) {
|
|
@@ -603,26 +700,11 @@ window.__ModuleLoader__.load({
|
|
|
603
700
|
const presets = cfg.presets ?? [];
|
|
604
701
|
const [name, setName] = (0, react.useState)("");
|
|
605
702
|
const fileRef = (0, react.useRef)(null);
|
|
606
|
-
const
|
|
607
|
-
|
|
608
|
-
for (const sec of inv?.sections ?? []) set.add(sec.name);
|
|
609
|
-
for (const item of cfg.inject ?? []) set.add(item.name);
|
|
610
|
-
return set;
|
|
611
|
-
})();
|
|
703
|
+
const merged = mergeSections(inv, cfg, new Set(cfg.sections ?? []));
|
|
704
|
+
const currentNames = new Set(merged.map((sec) => sec.name));
|
|
612
705
|
const saveCurrent = () => {
|
|
613
706
|
const presetName = name.trim() || `${t("preset")} ${presets.length + 1}`;
|
|
614
|
-
const
|
|
615
|
-
const order = inject.map((sec, i) => ({
|
|
616
|
-
name: sec.name,
|
|
617
|
-
after: i > 0 ? inject[i - 1].name : void 0,
|
|
618
|
-
text: sec.text
|
|
619
|
-
}));
|
|
620
|
-
const data = {
|
|
621
|
-
sections: cfg.sections,
|
|
622
|
-
replace: cfg.replace,
|
|
623
|
-
order,
|
|
624
|
-
tools: cfg.tools
|
|
625
|
-
};
|
|
707
|
+
const data = buildPresetData(cfg, merged);
|
|
626
708
|
scope.set("presets", [...presets, {
|
|
627
709
|
id: genId(),
|
|
628
710
|
name: presetName,
|
|
@@ -631,21 +713,11 @@ window.__ModuleLoader__.load({
|
|
|
631
713
|
setName("");
|
|
632
714
|
};
|
|
633
715
|
const applyPreset = (preset) => {
|
|
634
|
-
const
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
scope.set("inject", resolveOrder(presetOrder));
|
|
640
|
-
scope.set("replace", {
|
|
641
|
-
...cfg.replace ?? {},
|
|
642
|
-
...preset.data.replace ?? {}
|
|
643
|
-
});
|
|
644
|
-
scope.set("sections", [...blocked]);
|
|
645
|
-
scope.set("tools", preset.data.tools ?? {
|
|
646
|
-
exclude: [],
|
|
647
|
-
include: []
|
|
648
|
-
});
|
|
716
|
+
const patch = applyPresetData(preset.data, cfg, currentNames);
|
|
717
|
+
scope.set("inject", patch.inject);
|
|
718
|
+
scope.set("replace", patch.replace);
|
|
719
|
+
scope.set("sections", patch.sections);
|
|
720
|
+
scope.set("tools", patch.tools);
|
|
649
721
|
scope.set("activePreset", preset.id);
|
|
650
722
|
};
|
|
651
723
|
const deletePreset = (id) => {
|
|
@@ -734,51 +806,6 @@ window.__ModuleLoader__.load({
|
|
|
734
806
|
function genId() {
|
|
735
807
|
return "p_" + Date.now().toString(36) + Math.random().toString(36).slice(2, 7);
|
|
736
808
|
}
|
|
737
|
-
/**
|
|
738
|
-
* Resolve a preset's relative order into an absolute ordered list (0..n-1).
|
|
739
|
-
* Sections with no anchor (or whose anchor is absent) come first in preset
|
|
740
|
-
* order; each anchored section is inserted right after its anchor; any
|
|
741
|
-
* remaining sections (cycles) are appended.
|
|
742
|
-
*/
|
|
743
|
-
function resolveOrder(presetOrder) {
|
|
744
|
-
const afterMap = /* @__PURE__ */ new Map();
|
|
745
|
-
const textMap = /* @__PURE__ */ new Map();
|
|
746
|
-
for (const sec of presetOrder) {
|
|
747
|
-
afterMap.set(sec.name, sec.after);
|
|
748
|
-
textMap.set(sec.name, sec.text);
|
|
749
|
-
}
|
|
750
|
-
const result = [];
|
|
751
|
-
const placed = /* @__PURE__ */ new Set();
|
|
752
|
-
for (const sec of presetOrder) {
|
|
753
|
-
const anchor = afterMap.get(sec.name);
|
|
754
|
-
if (!anchor || !afterMap.has(anchor)) {
|
|
755
|
-
result.push(sec.name);
|
|
756
|
-
placed.add(sec.name);
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
let changed = true;
|
|
760
|
-
while (changed) {
|
|
761
|
-
changed = false;
|
|
762
|
-
for (const sec of presetOrder) {
|
|
763
|
-
if (placed.has(sec.name)) continue;
|
|
764
|
-
const anchor = afterMap.get(sec.name);
|
|
765
|
-
if (anchor && placed.has(anchor)) {
|
|
766
|
-
result.splice(result.indexOf(anchor) + 1, 0, sec.name);
|
|
767
|
-
placed.add(sec.name);
|
|
768
|
-
changed = true;
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
for (const sec of presetOrder) if (!placed.has(sec.name)) {
|
|
773
|
-
result.push(sec.name);
|
|
774
|
-
placed.add(sec.name);
|
|
775
|
-
}
|
|
776
|
-
return result.map((name, i) => ({
|
|
777
|
-
name,
|
|
778
|
-
order: i,
|
|
779
|
-
text: textMap.get(name) ?? ""
|
|
780
|
-
}));
|
|
781
|
-
}
|
|
782
809
|
//#endregion
|
|
783
810
|
//#region src/client/index.ts
|
|
784
811
|
/**
|
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/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","/** 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 { s } from './styles.ts'\n\ninterface Section {\n name: string\n order: number\n text?: string\n active: boolean\n replaced: boolean\n}\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[] = (() => {\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 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 { 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 exclude = toolsCfg.exclude ?? []\n const include = toolsCfg.include ?? []\n const includeMode = include.length > 0\n\n const toggle = (name: string, currentlyHidden: boolean): void => {\n if (includeMode) {\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 scope.set('tools', { ...toolsCfg, include: next })\n } else {\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 scope.set('tools', { ...toolsCfg, exclude: next })\n }\n }\n const setIncludeMode = (on: boolean): void => {\n scope.set('tools', on\n ? { exclude: exclude.filter((n) => tools.some((tool) => tool.name === n)), include: tools.filter((tool) => !tool.hidden).map((tool) => tool.name) }\n : { exclude: ensureOnlyKnown(exclude, tools), include: [] })\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 = includeMode ? !include.includes(tool.name) : exclude.includes(tool.name)\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\nfunction ensureOnlyKnown(list: string[], known: Array<{ name: string }>): string[] {\n const names = known.map((tool) => tool.name)\n return list.filter((n) => names.includes(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 { 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 // Current section names (inventory + injected), used to decide what to block.\n const currentNames = (() => {\n const set = new Set<string>()\n for (const sec of inv?.sections ?? []) set.add(sec.name)\n for (const item of cfg.inject ?? []) set.add(item.name)\n return set\n })()\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 inject = cfg.inject ?? []\n const order = inject.map((sec, i) => ({\n name: sec.name,\n after: i > 0 ? inject[i - 1].name : undefined,\n text: sec.text,\n }))\n const data: PresetData = {\n sections: cfg.sections,\n replace: cfg.replace,\n order,\n tools: cfg.tools,\n }\n scope.set('presets', [...presets, { id: genId(), name: presetName, data }])\n setName('')\n }\n\n // Apply a preset. The preset's section list defines the active set:\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 // - the active sections are arranged by the preset's relative order\n const applyPreset = (preset: Preset): void => {\n const presetOrder = preset.data.order ?? []\n const presetNames = new Set(presetOrder.map((x) => x.name))\n\n // Blocked list: preset's own blocked names, plus any current section that\n // is not part of the preset's active set; preset's active names unblocked.\n const blocked = new Set(preset.data.sections ?? [])\n for (const name of currentNames) if (!presetNames.has(name)) blocked.add(name)\n for (const name of presetNames) blocked.delete(name)\n\n scope.set('inject', resolveOrder(presetOrder))\n scope.set('replace', { ...(cfg.replace ?? {}), ...(preset.data.replace ?? {}) })\n scope.set('sections', [...blocked])\n scope.set('tools', preset.data.tools ?? { exclude: [], include: [] })\n scope.set('activePreset', preset.id)\n }\n\n const deletePreset = (id: string): void => {\n scope.set('presets', presets.filter((p) => p.id !== id))\n if (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({ name: preset.name, data: preset.data }, 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 const incoming = Array.isArray(parsed) ? parsed : [parsed]\n const existing = new Set(presets.map((p) => p.name))\n const added = incoming\n .filter((p) => p && typeof p.name === 'string' && !existing.has(p.name))\n .map((p) => ({ id: genId(), name: p.name, data: (p.data ?? {}) as PresetData }))\n if (added.length > 0) scope.set('presets', [...presets, ...added])\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/**\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 */\nfunction resolveOrder(presetOrder: Array<{ name: string; after?: string; text: string }>): Array<{ name: string; order: number; text: string }> {\n const afterMap = new Map<string, string | undefined>()\n const textMap = new Map<string, string>()\n for (const sec of presetOrder) {\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 // Roots: no anchor, or anchor not part of the preset's own set.\n for (const sec of presetOrder) {\n const anchor = afterMap.get(sec.name)\n if (!anchor || !afterMap.has(anchor)) {\n result.push(sec.name)\n placed.add(sec.name)\n }\n }\n\n // Chain resolution: place each section right after its (already placed) anchor.\n let changed = true\n while (changed) {\n changed = false\n for (const sec of presetOrder) {\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 // Any remaining (cycle / unresolved) → append in preset order.\n for (const sec of presetOrder) {\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 * 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 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, 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 const activeNames = new Set([...presetNames].filter((n) => !blocked.has(n)))\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 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","/** 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 { 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 exclude = toolsCfg.exclude ?? []\n const include = toolsCfg.include ?? []\n const includeMode = include.length > 0\n\n const toggle = (name: string, currentlyHidden: boolean): void => {\n if (includeMode) {\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 scope.set('tools', { ...toolsCfg, include: next })\n } else {\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 scope.set('tools', { ...toolsCfg, exclude: next })\n }\n }\n const setIncludeMode = (on: boolean): void => {\n scope.set('tools', on\n ? { exclude: exclude.filter((n) => tools.some((tool) => tool.name === n)), include: tools.filter((tool) => !tool.hidden).map((tool) => tool.name) }\n : { exclude: ensureOnlyKnown(exclude, tools), include: [] })\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 = includeMode ? !include.includes(tool.name) : exclude.includes(tool.name)\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\nfunction ensureOnlyKnown(list: string[], known: Array<{ name: string }>): string[] {\n const names = known.map((tool) => tool.name)\n return list.filter((n) => names.includes(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 { applyPresetData, buildPresetData, mergeSections } 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 scope.set('presets', presets.filter((p) => p.id !== id))\n if (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({ name: preset.name, data: preset.data }, 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 const incoming = Array.isArray(parsed) ? parsed : [parsed]\n const existing = new Set(presets.map((p) => p.name))\n const added = incoming\n .filter((p) => p && typeof p.name === 'string' && !existing.has(p.name))\n .map((p) => ({ id: genId(), name: p.name, data: (p.data ?? {}) as PresetData }))\n if (added.length > 0) scope.set('presets', [...presets, ...added])\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/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.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
7
7
|
"main": "lib/index.js",
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { createElement as h, useRef, useState, type ReactElement, type ChangeEvent } from 'react'
|
|
3
3
|
import type { Config, Inventory, Preset, PresetData, SettingsScope } from './types.ts'
|
|
4
4
|
import type { Translate } from './locales.ts'
|
|
5
|
+
import { applyPresetData, buildPresetData, mergeSections } from './presets.ts'
|
|
5
6
|
import { s } from './styles.ts'
|
|
6
7
|
|
|
7
8
|
export function PresetsTab({ cfg, inv, scope, t }: { cfg: Config; inv: Inventory | null; scope: SettingsScope; t: Translate }): ReactElement {
|
|
@@ -9,54 +10,32 @@ export function PresetsTab({ cfg, inv, scope, t }: { cfg: Config; inv: Inventory
|
|
|
9
10
|
const [name, setName] = useState('')
|
|
10
11
|
const fileRef = useRef<HTMLInputElement | null>(null)
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
for (const sec of inv?.sections ?? []) set.add(sec.name)
|
|
16
|
-
for (const item of cfg.inject ?? []) set.add(item.name)
|
|
17
|
-
return set
|
|
18
|
-
})()
|
|
13
|
+
const blockedNames = new Set(cfg.sections ?? [])
|
|
14
|
+
const merged = mergeSections(inv, cfg, blockedNames)
|
|
15
|
+
const currentNames = new Set(merged.map((sec) => sec.name))
|
|
19
16
|
|
|
20
17
|
// Capture the current customization as a new preset (full snapshot). The
|
|
21
18
|
// absolute order is converted to a relative form (each section records the
|
|
22
19
|
// section it should follow) so the preset stays portable across prompts.
|
|
23
20
|
const saveCurrent = (): void => {
|
|
24
21
|
const presetName = name.trim() || `${t('preset')} ${presets.length + 1}`
|
|
25
|
-
const
|
|
26
|
-
const order = inject.map((sec, i) => ({
|
|
27
|
-
name: sec.name,
|
|
28
|
-
after: i > 0 ? inject[i - 1].name : undefined,
|
|
29
|
-
text: sec.text,
|
|
30
|
-
}))
|
|
31
|
-
const data: PresetData = {
|
|
32
|
-
sections: cfg.sections,
|
|
33
|
-
replace: cfg.replace,
|
|
34
|
-
order,
|
|
35
|
-
tools: cfg.tools,
|
|
36
|
-
}
|
|
22
|
+
const data = buildPresetData(cfg, merged)
|
|
37
23
|
scope.set('presets', [...presets, { id: genId(), name: presetName, data }])
|
|
38
24
|
setName('')
|
|
39
25
|
}
|
|
40
26
|
|
|
41
|
-
// Apply a preset. The
|
|
27
|
+
// Apply a preset. The patch is computed by the pure helper:
|
|
42
28
|
// - same-name sections are overridden (content + order)
|
|
43
29
|
// - preset sections missing from the current prompt are added
|
|
44
30
|
// - current sections NOT in the preset list are disabled (blocked)
|
|
45
|
-
// - the
|
|
31
|
+
// - only the preset's ACTIVE sections are unblocked (its own blocked list
|
|
32
|
+
// is preserved)
|
|
46
33
|
const applyPreset = (preset: Preset): void => {
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const blocked = new Set(preset.data.sections ?? [])
|
|
53
|
-
for (const name of currentNames) if (!presetNames.has(name)) blocked.add(name)
|
|
54
|
-
for (const name of presetNames) blocked.delete(name)
|
|
55
|
-
|
|
56
|
-
scope.set('inject', resolveOrder(presetOrder))
|
|
57
|
-
scope.set('replace', { ...(cfg.replace ?? {}), ...(preset.data.replace ?? {}) })
|
|
58
|
-
scope.set('sections', [...blocked])
|
|
59
|
-
scope.set('tools', preset.data.tools ?? { exclude: [], include: [] })
|
|
34
|
+
const patch = applyPresetData(preset.data, cfg, currentNames)
|
|
35
|
+
scope.set('inject', patch.inject)
|
|
36
|
+
scope.set('replace', patch.replace)
|
|
37
|
+
scope.set('sections', patch.sections)
|
|
38
|
+
scope.set('tools', patch.tools)
|
|
60
39
|
scope.set('activePreset', preset.id)
|
|
61
40
|
}
|
|
62
41
|
|
|
@@ -133,52 +112,3 @@ export function PresetsTab({ cfg, inv, scope, t }: { cfg: Config; inv: Inventory
|
|
|
133
112
|
function genId(): string {
|
|
134
113
|
return 'p_' + Date.now().toString(36) + Math.random().toString(36).slice(2, 7)
|
|
135
114
|
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Resolve a preset's relative order into an absolute ordered list (0..n-1).
|
|
139
|
-
* Sections with no anchor (or whose anchor is absent) come first in preset
|
|
140
|
-
* order; each anchored section is inserted right after its anchor; any
|
|
141
|
-
* remaining sections (cycles) are appended.
|
|
142
|
-
*/
|
|
143
|
-
function resolveOrder(presetOrder: Array<{ name: string; after?: string; text: string }>): Array<{ name: string; order: number; text: string }> {
|
|
144
|
-
const afterMap = new Map<string, string | undefined>()
|
|
145
|
-
const textMap = new Map<string, string>()
|
|
146
|
-
for (const sec of presetOrder) {
|
|
147
|
-
afterMap.set(sec.name, sec.after)
|
|
148
|
-
textMap.set(sec.name, sec.text)
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const result: string[] = []
|
|
152
|
-
const placed = new Set<string>()
|
|
153
|
-
|
|
154
|
-
// Roots: no anchor, or anchor not part of the preset's own set.
|
|
155
|
-
for (const sec of presetOrder) {
|
|
156
|
-
const anchor = afterMap.get(sec.name)
|
|
157
|
-
if (!anchor || !afterMap.has(anchor)) {
|
|
158
|
-
result.push(sec.name)
|
|
159
|
-
placed.add(sec.name)
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Chain resolution: place each section right after its (already placed) anchor.
|
|
164
|
-
let changed = true
|
|
165
|
-
while (changed) {
|
|
166
|
-
changed = false
|
|
167
|
-
for (const sec of presetOrder) {
|
|
168
|
-
if (placed.has(sec.name)) continue
|
|
169
|
-
const anchor = afterMap.get(sec.name)
|
|
170
|
-
if (anchor && placed.has(anchor)) {
|
|
171
|
-
result.splice(result.indexOf(anchor) + 1, 0, sec.name)
|
|
172
|
-
placed.add(sec.name)
|
|
173
|
-
changed = true
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// Any remaining (cycle / unresolved) → append in preset order.
|
|
179
|
-
for (const sec of presetOrder) {
|
|
180
|
-
if (!placed.has(sec.name)) { result.push(sec.name); placed.add(sec.name) }
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
return result.map((name, i) => ({ name, order: i, text: textMap.get(name) ?? '' }))
|
|
184
|
-
}
|
|
@@ -2,16 +2,9 @@
|
|
|
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
6
|
import { s } from './styles.ts'
|
|
6
7
|
|
|
7
|
-
interface Section {
|
|
8
|
-
name: string
|
|
9
|
-
order: number
|
|
10
|
-
text?: string
|
|
11
|
-
active: boolean
|
|
12
|
-
replaced: boolean
|
|
13
|
-
}
|
|
14
|
-
|
|
15
8
|
export function SectionsTab({ cfg, inv, scope, t }: { cfg: Config; inv: Inventory | null; scope: SettingsScope; t: Translate }): ReactElement {
|
|
16
9
|
const replace = cfg.replace ?? {}
|
|
17
10
|
const blockedNames = new Set(cfg.sections ?? [])
|
|
@@ -23,22 +16,7 @@ export function SectionsTab({ cfg, inv, scope, t }: { cfg: Config; inv: Inventor
|
|
|
23
16
|
// Merge inventory sections with user-injected sections, sorted by order.
|
|
24
17
|
// Injected sections that don't exist in the original prompt appear here too,
|
|
25
18
|
// positioned by their order value.
|
|
26
|
-
const merged: Section[] = (
|
|
27
|
-
const map = new Map<string, Section>()
|
|
28
|
-
for (const sec of inv?.sections ?? []) map.set(sec.name, sec)
|
|
29
|
-
for (const item of cfg.inject ?? []) {
|
|
30
|
-
const existing = map.get(item.name)
|
|
31
|
-
if (existing) map.set(item.name, { ...existing, order: item.order })
|
|
32
|
-
else map.set(item.name, {
|
|
33
|
-
name: item.name,
|
|
34
|
-
order: item.order,
|
|
35
|
-
text: item.text || '<动态生成>',
|
|
36
|
-
active: !blockedNames.has(item.name),
|
|
37
|
-
replaced: false,
|
|
38
|
-
})
|
|
39
|
-
}
|
|
40
|
-
return [...map.values()].sort((a, b) => a.order - b.order)
|
|
41
|
-
})()
|
|
19
|
+
const merged: Section[] = mergeSections(inv, cfg, blockedNames)
|
|
42
20
|
|
|
43
21
|
const toggleBlock = (name: string, currentlyBlocked: boolean): void => {
|
|
44
22
|
const list = (cfg.sections ?? []).slice()
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/** Pure preset / section helpers shared by the client UI and the node tests. */
|
|
2
|
+
import type { Config, Inventory, PresetData } from './types.ts'
|
|
3
|
+
|
|
4
|
+
export interface Section {
|
|
5
|
+
name: string
|
|
6
|
+
order: number
|
|
7
|
+
text?: string
|
|
8
|
+
active: boolean
|
|
9
|
+
replaced: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** The config fields a preset apply must write back to the settings scope. */
|
|
13
|
+
export interface ConfigPatch {
|
|
14
|
+
sections: string[]
|
|
15
|
+
replace: Record<string, string>
|
|
16
|
+
inject: Array<{ name: string; order: number; text: string }>
|
|
17
|
+
tools: { exclude: string[]; include: string[] }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Merge the inventory sections with the user's inject list, sorted by order. */
|
|
21
|
+
export function mergeSections(inv: Inventory | null, cfg: Config, blockedNames: ReadonlySet<string>): Section[] {
|
|
22
|
+
const map = new Map<string, Section>()
|
|
23
|
+
for (const sec of inv?.sections ?? []) map.set(sec.name, sec)
|
|
24
|
+
for (const item of cfg.inject ?? []) {
|
|
25
|
+
const existing = map.get(item.name)
|
|
26
|
+
if (existing) map.set(item.name, { ...existing, order: item.order })
|
|
27
|
+
else map.set(item.name, {
|
|
28
|
+
name: item.name,
|
|
29
|
+
order: item.order,
|
|
30
|
+
text: item.text || '<动态生成>',
|
|
31
|
+
active: !blockedNames.has(item.name),
|
|
32
|
+
replaced: false,
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
return [...map.values()].sort((a, b) => a.order - b.order)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Resolve a preset's relative order into an absolute ordered list (0..n-1).
|
|
40
|
+
* Sections with no anchor (or whose anchor is absent) come first in preset
|
|
41
|
+
* order; each anchored section is inserted right after its anchor; any
|
|
42
|
+
* remaining sections (cycles) are appended.
|
|
43
|
+
*/
|
|
44
|
+
export function resolveOrder(presetOrder: PresetData['order']): Array<{ name: string; order: number; text: string }> {
|
|
45
|
+
const list = presetOrder ?? []
|
|
46
|
+
const afterMap = new Map<string, string | undefined>()
|
|
47
|
+
const textMap = new Map<string, string>()
|
|
48
|
+
for (const sec of list) {
|
|
49
|
+
afterMap.set(sec.name, sec.after)
|
|
50
|
+
textMap.set(sec.name, sec.text)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const result: string[] = []
|
|
54
|
+
const placed = new Set<string>()
|
|
55
|
+
|
|
56
|
+
for (const sec of list) {
|
|
57
|
+
const anchor = afterMap.get(sec.name)
|
|
58
|
+
if (!anchor || !afterMap.has(anchor)) { result.push(sec.name); placed.add(sec.name) }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
let changed = true
|
|
62
|
+
while (changed) {
|
|
63
|
+
changed = false
|
|
64
|
+
for (const sec of list) {
|
|
65
|
+
if (placed.has(sec.name)) continue
|
|
66
|
+
const anchor = afterMap.get(sec.name)
|
|
67
|
+
if (anchor && placed.has(anchor)) {
|
|
68
|
+
result.splice(result.indexOf(anchor) + 1, 0, sec.name)
|
|
69
|
+
placed.add(sec.name)
|
|
70
|
+
changed = true
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
for (const sec of list) {
|
|
76
|
+
if (!placed.has(sec.name)) { result.push(sec.name); placed.add(sec.name) }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return result.map((name, i) => ({ name, order: i, text: textMap.get(name) ?? '' }))
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Build a preset snapshot from the current config. The order list is derived
|
|
84
|
+
* from the MERGED section list (not just `cfg.inject`) so that a preset saved
|
|
85
|
+
* after only blocking sections (without reordering) still carries the full
|
|
86
|
+
* ordered set, and thus a meaningful "active list" when applied.
|
|
87
|
+
*/
|
|
88
|
+
export function buildPresetData(cfg: Config, merged: Section[]): PresetData {
|
|
89
|
+
const order = merged.map((sec, i) => ({
|
|
90
|
+
name: sec.name,
|
|
91
|
+
after: i > 0 ? merged[i - 1].name : undefined,
|
|
92
|
+
text: sec.text ?? '',
|
|
93
|
+
}))
|
|
94
|
+
return {
|
|
95
|
+
sections: cfg.sections,
|
|
96
|
+
replace: cfg.replace,
|
|
97
|
+
order,
|
|
98
|
+
tools: cfg.tools,
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Compute the config patch when applying a preset:
|
|
104
|
+
* - same-name sections are overridden (the order list drives this at runtime)
|
|
105
|
+
* - preset sections missing from the current prompt are added
|
|
106
|
+
* - current sections NOT in the preset's ordered list are disabled by default
|
|
107
|
+
* - only the preset's ACTIVE sections (in the order list but not in its block
|
|
108
|
+
* list) are unblocked; the preset's own blocked sections stay blocked.
|
|
109
|
+
*/
|
|
110
|
+
export function applyPresetData(data: PresetData, cfg: Config, currentNames: ReadonlySet<string>): ConfigPatch {
|
|
111
|
+
const presetOrder = data.order ?? []
|
|
112
|
+
const presetNames = new Set(presetOrder.map((x) => x.name))
|
|
113
|
+
const blocked = new Set(data.sections ?? [])
|
|
114
|
+
const activeNames = new Set([...presetNames].filter((n) => !blocked.has(n)))
|
|
115
|
+
|
|
116
|
+
for (const name of currentNames) if (!presetNames.has(name)) blocked.add(name)
|
|
117
|
+
for (const name of activeNames) blocked.delete(name)
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
sections: [...blocked],
|
|
121
|
+
replace: { ...(cfg.replace ?? {}), ...(data.replace ?? {}) },
|
|
122
|
+
inject: resolveOrder(presetOrder),
|
|
123
|
+
tools: { exclude: data.tools?.exclude ?? [], include: data.tools?.include ?? [] },
|
|
124
|
+
}
|
|
125
|
+
}
|