dsh-prompt-customizer 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DreamsTOF
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.en.md ADDED
@@ -0,0 +1,114 @@
1
+ # dsh-prompt-customizer
2
+
3
+ > **English** | [简体中文](README.md)
4
+
5
+ A [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (dsh) plugin that lets you control the **system prompt** and the **tool catalog** from a settings-panel UI.
6
+
7
+ Plugin-injected prompt sections (from other plugins) can pollute your system prompt. This plugin lets you **block**, **replace**, **inject**, and **reorder** prompt sections by name, and **hide tools** from the model catalog — all live, without touching the other plugins.
8
+
9
+ ## Features
10
+
11
+ - **Prompt sections**
12
+ - **Block** a section by name (removed from the assembled system prompt).
13
+ - **Replace** a section's text (the original text is echoed for editing, including dynamically generated sections).
14
+ - **Inject** brand-new sections.
15
+ - **Reorder** sections with ↑/↓ arrows or **drag & drop** (HTML5). Order is stored as a virtual 0-based index, so there are never duplicate or fractional orders.
16
+ - **Tools**
17
+ - **Blacklist** (`exclude`): hide the listed tools.
18
+ - **Whitelist** (`include`): keep only the listed tools (wins over exclude).
19
+ - Only the model-facing catalog is affected — tools and routes keep working.
20
+ - **Presets**
21
+ - **Save** the current customization as a preset (full snapshot).
22
+ - **Apply** a preset: same-name sections are overridden, preset-only sections are added, and current sections outside the preset list are disabled by default.
23
+ - **Export / Import** presets as JSON (full snapshot).
24
+ - Presets store **relative order** (each section records the section it follows), so they stay portable across prompts with different section sets.
25
+ - Multiple presets can be stored locally; only one is active at a time.
26
+
27
+ ## Installation
28
+
29
+ Install from npm:
30
+
31
+ ```bash
32
+ dsh plugin add dsh-prompt-customizer
33
+ ```
34
+
35
+ Or install from a local checkout:
36
+
37
+ ```bash
38
+ dsh plugin add /path/to/dsh-prompt-customizer
39
+ ```
40
+
41
+ After installation, open the dsh web UI → **Settings** → **提示词定制** (Prompt Customizer) in the sidebar.
42
+
43
+ ## Usage
44
+
45
+ ### Prompt sections tab
46
+
47
+ Each row shows a prompt section with:
48
+
49
+ - A **checkbox** to block/unblock it.
50
+ - A **replace** button to edit its text (the original text is pre-filled).
51
+ - **↑/↓ arrows** and a **drag handle** to reorder it.
52
+ - The `#N` badge shows the section's virtual order (0-based position).
53
+
54
+ The **注入新段** (Inject section) box at the bottom adds a brand-new section by name, order, and text.
55
+
56
+ ### Tools tab
57
+
58
+ Toggle tools to hide them (blacklist), or switch to whitelist mode to keep only the checked tools.
59
+
60
+ ### Presets tab
61
+
62
+ - **Save current as preset** — capture the current sections/tools customization.
63
+ - **Apply** — activate a preset (overrides same-name sections, adds preset-only sections, disables current sections outside the preset).
64
+ - **Export** — download the preset as JSON.
65
+ - **Import** — load a preset JSON file (same-name presets are skipped).
66
+
67
+ ## Configuration
68
+
69
+ The plugin stores its config in the dsh settings document under the `prompt-customizer` namespace (`~/.dsh/settings.yaml`). You can edit it directly:
70
+
71
+ ```yaml
72
+ prompt-customizer:
73
+ # Block these prompt sections by name.
74
+ sections:
75
+ - plugin:some-plugin
76
+ # Replace a section's text by name (original order kept).
77
+ replace:
78
+ system: 'You are a helpful coding assistant.'
79
+ # Inject / override sections (name + order + text).
80
+ inject:
81
+ - name: tool:read
82
+ order: 0
83
+ text: ''
84
+ # Tool catalog filtering.
85
+ tools:
86
+ exclude: []
87
+ include: []
88
+ ```
89
+
90
+ | Field | Type | Description |
91
+ | --- | --- | --- |
92
+ | `sections` | `string[]` | Prompt section names to block. |
93
+ | `replace` | `Record<string, string>` | Replacement text per section name. |
94
+ | `inject` | `{name, order, text}[]` | Sections to inject or override (order controls splice position). |
95
+ | `tools.exclude` | `string[]` | Tool names to hide. |
96
+ | `tools.include` | `string[]` | When non-empty, keep only these tools (wins over `exclude`). |
97
+
98
+ ## Development
99
+
100
+ ```bash
101
+ npm install
102
+ npm run check # typecheck + build
103
+ ```
104
+
105
+ The browser half is built with [tsdown](https://github.com/rolldown/tsdown) into `client/client.js` (a `__ModuleLoader__` factory bundle). The host half lives in `lib/`.
106
+
107
+ ## Known limitations
108
+
109
+ - **Replacing a dynamically generated section freezes its content**: sections like `app:web-surface` are generated live at assembly time (e.g. they embed the current Web server URL). After replacing, the text becomes fixed at the value you edited and no longer tracks runtime state such as the port. To keep following runtime changes, block the section instead of replacing it.
110
+ - **Dynamic text resolution is best-effort**: the panel tries to call a dynamic section's generator function (with a minimal context) to echo its real content. A few dynamic sections that depend on richer context and throw when called fall back to showing `<动态生成>`; the rest of the features are unaffected.
111
+
112
+ ## License
113
+
114
+ [MIT](LICENSE) © 2026 DreamsTOF
@@ -0,0 +1,2 @@
1
+ en: README.en.md
2
+ zh-CN: README.md
package/README.md CHANGED
@@ -1,107 +1,114 @@
1
1
  # dsh-prompt-customizer
2
2
 
3
- A [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (dsh) plugin that lets you control the **system prompt** and the **tool catalog** from a settings-panel UI.
3
+ > [English](README.en.md) | **简体中文**
4
4
 
5
- Plugin-injected prompt sections (from other plugins) can pollute your system prompt. This plugin lets you **block**, **replace**, **inject**, and **reorder** prompt sections by name, and **hide tools** from the model catalog — all live, without touching the other plugins.
5
+ 一个 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (dsh) 插件,让你通过设置面板 UI 控制**系统提示词**和**工具目录**。
6
6
 
7
- ## Features
7
+ 其他插件注入的提示词段可能会污染你的系统提示词。本插件让你可以按名称**屏蔽**、**替换**、**注入**和**排序**提示词段,并从模型目录中**隐藏工具**——全部实时生效,无需改动其他插件。
8
8
 
9
- - **Prompt sections**
10
- - **Block** a section by name (removed from the assembled system prompt).
11
- - **Replace** a section's text (the original text is echoed for editing).
12
- - **Inject** brand-new sections.
13
- - **Reorder** sections with ↑/↓ arrows or **drag & drop** (HTML5). Order is stored as a virtual 0-based index, so there are never duplicate or fractional orders.
14
- - **Tools**
15
- - **Blacklist** (`exclude`): hide the listed tools.
16
- - **Whitelist** (`include`): keep only the listed tools (wins over exclude).
17
- - Only the model-facing catalog is affected — tools and routes keep working.
18
- - **Presets**
19
- - **Save** the current customization as a preset (full snapshot).
20
- - **Apply** a preset: same-name sections are overridden, preset-only sections are added, and current sections outside the preset list are disabled by default.
21
- - **Export / Import** presets as JSON (full snapshot).
22
- - Presets store **relative order** (each section records the section it follows), so they stay portable across prompts with different section sets.
23
- - Multiple presets can be stored locally; only one is active at a time.
9
+ ## 功能特性
24
10
 
25
- ## Installation
11
+ - **提示词段**
12
+ - **屏蔽**指定名称的段(从组装后的系统提示词中移除)。
13
+ - **替换**某段的文本(编辑时会回显原始文本,包括动态生成的段)。
14
+ - **注入**全新的段。
15
+ - 通过 **↑/↓ 箭头**或**拖拽**(HTML5)排序。顺序以虚拟的 0 起始下标存储,因此不会出现重复或小数 order。
16
+ - **工具**
17
+ - **黑名单**(`exclude`):隐藏列出的工具。
18
+ - **白名单**(`include`):仅保留列出的工具(优先于 exclude)。
19
+ - 只影响面向模型的目录,工具与路由仍正常工作。
20
+ - **预设**
21
+ - **保存**当前定制为预设(完整快照)。
22
+ - **应用**预设:同名段被覆盖、预设独有段被添加、预设名单之外的当前段默认被屏蔽。
23
+ - **导出 / 导入**预设为 JSON(完整快照)。
24
+ - 预设存储**相对顺序**(每段记录它跟在哪个段之后),因此可跨不同段集合的提示词移植。
25
+ - 本地可保存多个预设,同一时间只能激活一个。
26
26
 
27
- Install from npm:
27
+ ## 安装
28
+
29
+ 从 npm 安装:
28
30
 
29
31
  ```bash
30
32
  dsh plugin add dsh-prompt-customizer
31
33
  ```
32
34
 
33
- Or install from a local checkout:
35
+ 或从本地目录安装:
34
36
 
35
37
  ```bash
36
38
  dsh plugin add /path/to/dsh-prompt-customizer
37
39
  ```
38
40
 
39
- After installation, open the dsh web UI → **Settings** → **提示词定制** (Prompt Customizer) in the sidebar.
41
+ 安装后,打开 dsh Web UI → **设置** → 侧边栏中的 **提示词定制**。
40
42
 
41
- ## Usage
43
+ ## 使用说明
42
44
 
43
- ### Prompt sections tab
45
+ ### 提示词 Tab
44
46
 
45
- Each row shows a prompt section with:
47
+ 每一行代表一个提示词段,包含:
46
48
 
47
- - A **checkbox** to block/unblock it.
48
- - A **replace** button to edit its text (the original text is pre-filled).
49
- - **↑/↓ arrows** and a **drag handle** to reorder it.
50
- - The `#N` badge shows the section's virtual order (0-based position).
49
+ - **复选框**:屏蔽 / 取消屏蔽。
50
+ - **替换**按钮:编辑该段文本(默认回显原始文本)。
51
+ - **↑/↓ 箭头**和**拖拽手柄**:调整顺序。
52
+ - `#N` 徽标显示该段的虚拟顺序(0 起始的位置)。
51
53
 
52
- The **注入新段** (Inject section) box at the bottom adds a brand-new section by name, order, and text.
54
+ 底部的 **注入新段** 区域可按名称、顺序和文本添加全新段。
53
55
 
54
- ### Tools tab
56
+ ### 工具 Tab
55
57
 
56
- Toggle tools to hide them (blacklist), or switch to whitelist mode to keep only the checked tools.
58
+ 勾选工具以隐藏(黑名单),或切换到白名单模式仅保留勾选的工具。
57
59
 
58
- ### Presets tab
60
+ ### 预设 Tab
59
61
 
60
- - **Save current as preset** — capture the current sections/tools customization.
61
- - **Apply** — activate a preset (overrides same-name sections, adds preset-only sections, disables current sections outside the preset).
62
- - **Export** — download the preset as JSON.
63
- - **Import** — load a preset JSON file (same-name presets are skipped).
62
+ - **保存当前为预设**:捕获当前的提示词 / 工具定制。
63
+ - **应用**:激活某个预设(覆盖同名段、添加预设独有段、屏蔽预设名单之外的当前段)。
64
+ - **导出**:将预设下载为 JSON。
65
+ - **导入**:加载预设 JSON 文件(同名预设会被跳过)。
64
66
 
65
- ## Configuration
67
+ ## 配置
66
68
 
67
- The plugin stores its config in the dsh settings document under the `prompt-customizer` namespace (`~/.dsh/settings.yaml`). You can edit it directly:
69
+ 插件配置存储在 dsh 设置文档的 `prompt-customizer` 命名空间下(`~/.dsh/settings.yaml`)。你也可以直接编辑:
68
70
 
69
71
  ```yaml
70
72
  prompt-customizer:
71
- # Block these prompt sections by name.
73
+ # 按名称屏蔽这些提示词段。
72
74
  sections:
73
75
  - plugin:some-plugin
74
- # Replace a section's text by name (original order kept).
76
+ # 按名称替换某段文本(保留原始顺序)。
75
77
  replace:
76
78
  system: 'You are a helpful coding assistant.'
77
- # Inject / override sections (name + order + text).
79
+ # 注入 / 覆盖段(名称 + 顺序 + 文本)。
78
80
  inject:
79
81
  - name: tool:read
80
82
  order: 0
81
83
  text: ''
82
- # Tool catalog filtering.
84
+ # 工具目录过滤。
83
85
  tools:
84
86
  exclude: []
85
87
  include: []
86
88
  ```
87
89
 
88
- | Field | Type | Description |
90
+ | 字段 | 类型 | 说明 |
89
91
  | --- | --- | --- |
90
- | `sections` | `string[]` | Prompt section names to block. |
91
- | `replace` | `Record<string, string>` | Replacement text per section name. |
92
- | `inject` | `{name, order, text}[]` | Sections to inject or override (order controls splice position). |
93
- | `tools.exclude` | `string[]` | Tool names to hide. |
94
- | `tools.include` | `string[]` | When non-empty, keep only these tools (wins over `exclude`). |
92
+ | `sections` | `string[]` | 要屏蔽的提示词段名称。 |
93
+ | `replace` | `Record<string, string>` | 每个段名称对应的替换文本。 |
94
+ | `inject` | `{name, order, text}[]` | 要注入或覆盖的段(order 控制拼接位置)。 |
95
+ | `tools.exclude` | `string[]` | 要隐藏的工具名称。 |
96
+ | `tools.include` | `string[]` | 非空时仅保留这些工具(优先于 `exclude`)。 |
95
97
 
96
- ## Development
98
+ ## 开发
97
99
 
98
100
  ```bash
99
101
  npm install
100
102
  npm run check # typecheck + build
101
103
  ```
102
104
 
103
- The browser half is built with [tsdown](https://github.com/rolldown/tsdown) into `client/client.js` (a `__ModuleLoader__` factory bundle). The host half lives in `lib/`.
105
+ 浏览器端使用 [tsdown](https://github.com/rolldown/tsdown) 打包为 `client/client.js`(`__ModuleLoader__` factory bundle)。宿主端代码位于 `lib/`。
106
+
107
+ ## 已知限制
108
+
109
+ - **替换动态生成的段会固定其生成内容**:像 `app:web-surface` 这类段在装配时实时生成(如嵌入当前 Web 服务器 URL)。替换后,文本会固定为您编辑时的值,不再随端口等运行时状态自动更新;如需跟随运行时变化,请勿替换该段,改用屏蔽。
110
+ - **动态文本解析是尽力而为**:面板读取动态段时会尝试调用其生成函数并传入最小上下文,以便回显真实内容。个别依赖更复杂上下文、调用时会抛错的动态段会回退显示 `<动态生成>`,不影响其余功能。
104
111
 
105
112
  ## License
106
113
 
107
- MIT
114
+ [MIT](LICENSE) © 2026 DreamsTOF
package/client/client.js CHANGED
@@ -813,15 +813,6 @@ window.__ModuleLoader__.load({
813
813
  scope,
814
814
  t
815
815
  })));
816
- scoped.slots.inject("settings.plugin.item", () => scoped.slots.register({
817
- name: "settings.plugin.item",
818
- key: NS,
819
- locale: NS,
820
- inject: () => ({ t })
821
- }, () => (0, react.createElement)(Panel, {
822
- scope,
823
- t
824
- })));
825
816
  });
826
817
  }
827
818
  function Panel({ scope, t }) {
@@ -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) AND a plugin\n // settings card. Both go through a NESTED inject of settingsScope so the\n // panel can read/write the namespace config; on hosts without the settings\n // transport neither appears 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 scoped.slots.inject('settings.plugin.item', () => scoped.slots.register({\n name: 'settings.plugin.item',\n key: NS,\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/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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"}
package/lib/index.js CHANGED
@@ -107,12 +107,24 @@ async function buildInventory(ctx, cfg) {
107
107
 
108
108
  const scopeKey = await resolveStandardScope(ctx)
109
109
 
110
+ // Resolve a section's text. Dynamic sections register a function; call it
111
+ // with a best-effort context (same shape the assemble passes) so the
112
+ // inventory can echo the real generated content for replacement. Fall back
113
+ // to a marker if the function throws.
114
+ const resolveText = (section) => {
115
+ if (typeof section.text === 'function') {
116
+ try { return String(section.text({ scope: scopeKey }) ?? '') }
117
+ catch { return '<动态生成>' }
118
+ }
119
+ return String(section.text ?? '')
120
+ }
121
+
110
122
  const sectionsMap = ctx.systemPrompt.layers.merge(scopeKey, (layer) => layer.sections)
111
123
  const sections = [...sectionsMap.entries()]
112
124
  .map(([name, section]) => ({
113
125
  name,
114
126
  order: injectOrder.has(name) ? injectOrder.get(name) : (typeof section.order === 'number' ? section.order : 0),
115
- text: typeof section.text === 'function' ? '<动态生成>' : String(section.text ?? ''),
127
+ text: resolveText(section),
116
128
  active: !denied.has(name),
117
129
  replaced: Object.hasOwn(replace, name),
118
130
  }))
package/lib/schema.js CHANGED
@@ -24,4 +24,25 @@ export const Config = Schema.object({
24
24
  exclude: Schema.array(Schema.string()).default([]),
25
25
  include: Schema.array(Schema.string()).default([]),
26
26
  }).default({}),
27
+ // Presets: complete customization snapshots (full prompt + tool config).
28
+ presets: Schema.array(Schema.object({
29
+ id: Schema.string().required(),
30
+ name: Schema.string().required(),
31
+ data: Schema.object({
32
+ sections: Schema.array(Schema.string()),
33
+ replace: Schema.dict(Schema.string()),
34
+ // Relative order: each entry records the section it should follow.
35
+ order: Schema.array(Schema.object({
36
+ name: Schema.string().required(),
37
+ after: Schema.string(),
38
+ text: Schema.string(),
39
+ })),
40
+ tools: Schema.object({
41
+ exclude: Schema.array(Schema.string()),
42
+ include: Schema.array(Schema.string()),
43
+ }),
44
+ }),
45
+ })).default([]),
46
+ // The id of the currently applied preset (only one active at a time).
47
+ activePreset: Schema.string(),
27
48
  })
package/package.json CHANGED
@@ -1,14 +1,15 @@
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.1.0",
4
+ "version": "0.2.0",
5
5
  "type": "module",
6
6
  "private": false,
7
7
  "main": "lib/index.js",
8
8
  "scripts": {
9
9
  "typecheck": "tsc -p tsconfig.client.json --noEmit",
10
10
  "build": "tsdown",
11
- "check": "npm run typecheck && npm run build",
11
+ "test": "node --test",
12
+ "check": "npm run typecheck && npm run build && npm run test",
12
13
  "prepack": "npm run build"
13
14
  },
14
15
  "exports": {
@@ -49,10 +49,11 @@ export function apply(ctx: ClientContext): void {
49
49
  ctx.effect(() => ctx.locale.register(NS, DICT), 'prompt-customizer: locale')
50
50
  const t = ctx.locale.bind(NS)
51
51
 
52
- // Register a standalone settings section (sidebar menu entry) AND a plugin
53
- // settings card. Both go through a NESTED inject of settingsScope so the
54
- // panel can read/write the namespace config; on hosts without the settings
55
- // transport neither appears instead of unmounting the whole plugin.
52
+ // Register a standalone settings section (sidebar menu entry) so the panel
53
+ // only appears under "提示词定制", not as a card inside the "插件" settings
54
+ // list. Goes through a NESTED inject of settingsScope so the panel can
55
+ // read/write the namespace config; on hosts without the settings transport
56
+ // it stays hidden instead of unmounting the whole plugin.
56
57
  ctx.inject(['settingsScope'], (scoped) => {
57
58
  const scope = scoped.settingsScope.bind({ namespace: NS })
58
59
 
@@ -64,13 +65,6 @@ export function apply(ctx: ClientContext): void {
64
65
  locale: NS,
65
66
  inject: () => ({ t }),
66
67
  }, () => h(Panel, { scope, t })))
67
-
68
- scoped.slots.inject('settings.plugin.item', () => scoped.slots.register({
69
- name: 'settings.plugin.item',
70
- key: NS,
71
- locale: NS,
72
- inject: () => ({ t }),
73
- }, () => h(Panel, { scope, t })))
74
68
  })
75
69
  }
76
70
 
package/README.zh-CN.md DELETED
@@ -1,107 +0,0 @@
1
- # dsh-prompt-customizer
2
-
3
- 一个 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (dsh) 插件,让你通过设置面板 UI 控制**系统提示词**和**工具目录**。
4
-
5
- 其他插件注入的提示词段可能会污染你的系统提示词。本插件让你可以按名称**屏蔽**、**替换**、**注入**和**排序**提示词段,并从模型目录中**隐藏工具**——全部实时生效,无需改动其他插件。
6
-
7
- ## 功能特性
8
-
9
- - **提示词段**
10
- - **屏蔽**指定名称的段(从组装后的系统提示词中移除)。
11
- - **替换**某段的文本(编辑时会回显原始文本)。
12
- - **注入**全新的段。
13
- - 通过 **↑/↓ 箭头**或**拖拽**(HTML5)排序。顺序以虚拟的 0 起始下标存储,因此不会出现重复或小数 order。
14
- - **工具**
15
- - **黑名单**(`exclude`):隐藏列出的工具。
16
- - **白名单**(`include`):仅保留列出的工具(优先于 exclude)。
17
- - 只影响面向模型的目录,工具与路由仍正常工作。
18
- - **预设**
19
- - **保存**当前定制为预设(完整快照)。
20
- - **应用**预设:同名段被覆盖、预设独有段被添加、预设名单之外的当前段默认被屏蔽。
21
- - **导出 / 导入**预设为 JSON(完整快照)。
22
- - 预设存储**相对顺序**(每段记录它跟在哪个段之后),因此可跨不同段集合的提示词移植。
23
- - 本地可保存多个预设,同一时间只能激活一个。
24
-
25
- ## 安装
26
-
27
- 从 npm 安装:
28
-
29
- ```bash
30
- dsh plugin add dsh-prompt-customizer
31
- ```
32
-
33
- 或从本地目录安装:
34
-
35
- ```bash
36
- dsh plugin add /path/to/dsh-prompt-customizer
37
- ```
38
-
39
- 安装后,打开 dsh Web UI → **设置** → 侧边栏中的 **提示词定制**。
40
-
41
- ## 使用说明
42
-
43
- ### 提示词 Tab
44
-
45
- 每一行代表一个提示词段,包含:
46
-
47
- - **复选框**:屏蔽 / 取消屏蔽。
48
- - **替换**按钮:编辑该段文本(默认回显原始文本)。
49
- - **↑/↓ 箭头**和**拖拽手柄**:调整顺序。
50
- - `#N` 徽标显示该段的虚拟顺序(0 起始的位置)。
51
-
52
- 底部的 **注入新段** 区域可按名称、顺序和文本添加全新段。
53
-
54
- ### 工具 Tab
55
-
56
- 勾选工具以隐藏(黑名单),或切换到白名单模式仅保留勾选的工具。
57
-
58
- ### 预设 Tab
59
-
60
- - **保存当前为预设**:捕获当前的提示词 / 工具定制。
61
- - **应用**:激活某个预设(覆盖同名段、添加预设独有段、屏蔽预设名单之外的当前段)。
62
- - **导出**:将预设下载为 JSON。
63
- - **导入**:加载预设 JSON 文件(同名预设会被跳过)。
64
-
65
- ## 配置
66
-
67
- 插件配置存储在 dsh 设置文档的 `prompt-customizer` 命名空间下(`~/.dsh/settings.yaml`)。你也可以直接编辑:
68
-
69
- ```yaml
70
- prompt-customizer:
71
- # 按名称屏蔽这些提示词段。
72
- sections:
73
- - plugin:some-plugin
74
- # 按名称替换某段文本(保留原始顺序)。
75
- replace:
76
- system: 'You are a helpful coding assistant.'
77
- # 注入 / 覆盖段(名称 + 顺序 + 文本)。
78
- inject:
79
- - name: tool:read
80
- order: 0
81
- text: ''
82
- # 工具目录过滤。
83
- tools:
84
- exclude: []
85
- include: []
86
- ```
87
-
88
- | 字段 | 类型 | 说明 |
89
- | --- | --- | --- |
90
- | `sections` | `string[]` | 要屏蔽的提示词段名称。 |
91
- | `replace` | `Record<string, string>` | 每个段名称对应的替换文本。 |
92
- | `inject` | `{name, order, text}[]` | 要注入或覆盖的段(order 控制拼接位置)。 |
93
- | `tools.exclude` | `string[]` | 要隐藏的工具名称。 |
94
- | `tools.include` | `string[]` | 非空时仅保留这些工具(优先于 `exclude`)。 |
95
-
96
- ## 开发
97
-
98
- ```bash
99
- npm install
100
- npm run check # typecheck + build
101
- ```
102
-
103
- 浏览器端使用 [tsdown](https://github.com/rolldown/tsdown) 打包为 `client/client.js`(`__ModuleLoader__` factory bundle)。宿主端代码位于 `lib/`。
104
-
105
- ## 许可证
106
-
107
- MIT