dsh-prompt-customizer 0.2.7 → 0.3.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 +21 -21
- package/README.en.md +13 -32
- package/README.i18n.yaml +2 -2
- package/README.md +45 -38
- package/client/client.js +42 -25
- package/client/client.js.map +1 -1
- package/cordis.patch.yml +8 -8
- package/lib/index.js +270 -339
- package/lib/schema.js +51 -69
- package/package.json +61 -61
- package/src/client/PresetsTab.tsx +110 -110
- package/src/client/PreviewTab.tsx +61 -66
- package/src/client/PreviewTools.tsx +31 -31
- package/src/client/SectionsTab.tsx +202 -201
- package/src/client/ToolsTab.tsx +48 -48
- package/src/client/index.ts +116 -115
- package/src/client/locales.ts +101 -97
- package/src/client/presets.ts +268 -268
- package/src/client/styles.ts +41 -41
- package/src/client/types.ts +50 -75
- package/lib/client.js +0 -344
package/src/client/index.ts
CHANGED
|
@@ -1,115 +1,116 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* dsh-prompt-customizer — browser half (TypeScript source).
|
|
3
|
-
*
|
|
4
|
-
* Registers a "提示词定制" settings section (sidebar entry) and a plugin
|
|
5
|
-
* settings card. The panel fetches the effective section / tool inventory
|
|
6
|
-
* from the host `/api/prompt-customizer/inventory` and lets the user block,
|
|
7
|
-
* replace, inject prompt sections and hide tools. All writes go through the
|
|
8
|
-
* bound settings scope (`prompt-customizer` namespace) and apply live.
|
|
9
|
-
*
|
|
10
|
-
* Built by tsdown into the __ModuleLoader__ factory bundle at client/client.js;
|
|
11
|
-
* the only external is the loader module table's react entry.
|
|
12
|
-
*/
|
|
13
|
-
import { createElement as h, useEffect, useState, type ReactElement } from 'react'
|
|
14
|
-
import { DICT, type Translate } from './locales.ts'
|
|
15
|
-
import { s } from './styles.ts'
|
|
16
|
-
import type { Config, Inventory, SettingsScope } from './types.ts'
|
|
17
|
-
import { SectionsTab } from './SectionsTab.tsx'
|
|
18
|
-
import { ToolsTab } from './ToolsTab.tsx'
|
|
19
|
-
import { PresetsTab } from './PresetsTab.tsx'
|
|
20
|
-
import { PreviewTab } from './PreviewTab.tsx'
|
|
21
|
-
|
|
22
|
-
const NS = 'prompt-customizer'
|
|
23
|
-
const INVENTORY_URL = '/api/prompt-customizer/inventory'
|
|
24
|
-
|
|
25
|
-
export const name = 'prompt-customizer'
|
|
26
|
-
export const inject = ['slots', 'locale']
|
|
27
|
-
|
|
28
|
-
/** The subset of the client cordis context this plugin touches. */
|
|
29
|
-
interface ClientContext {
|
|
30
|
-
effect(callback: () => unknown, label?: string): void
|
|
31
|
-
locale: {
|
|
32
|
-
register(namespace: string, dicts: { zh: Record<string, string>; en: Record<string, string> }): unknown
|
|
33
|
-
bind(namespace: string): Translate
|
|
34
|
-
}
|
|
35
|
-
inject(services: string[], callback: (scoped: SettingsScopeHost) => void): void
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/** The host surface the settings card needs (settingsScope transport). */
|
|
39
|
-
interface SettingsScopeHost {
|
|
40
|
-
settingsScope: {
|
|
41
|
-
bind(options: { namespace: string }): SettingsScope
|
|
42
|
-
}
|
|
43
|
-
slots: {
|
|
44
|
-
inject(name: string, register: () => unknown): void
|
|
45
|
-
register(meta: Record<string, unknown>, render: () => unknown): unknown
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function apply(ctx: ClientContext): void {
|
|
50
|
-
ctx.effect(() => ctx.locale.register(NS, DICT), 'prompt-customizer: locale')
|
|
51
|
-
const t = ctx.locale.bind(NS)
|
|
52
|
-
|
|
53
|
-
// Register a standalone settings section (sidebar menu entry) so the panel
|
|
54
|
-
// only appears under "提示词定制", not as a card inside the "插件" settings
|
|
55
|
-
// list. Goes through a NESTED inject of settingsScope so the panel can
|
|
56
|
-
// read/write the namespace config; on hosts without the settings transport
|
|
57
|
-
// it stays hidden instead of unmounting the whole plugin.
|
|
58
|
-
ctx.inject(['settingsScope'], (scoped) => {
|
|
59
|
-
const scope = scoped.settingsScope.bind({ namespace: NS })
|
|
60
|
-
|
|
61
|
-
scoped.slots.inject('settings.section', () => scoped.slots.register({
|
|
62
|
-
name: 'settings.section',
|
|
63
|
-
id: 'prompt-customizer',
|
|
64
|
-
order: 50,
|
|
65
|
-
label: () => t('nav'),
|
|
66
|
-
locale: NS,
|
|
67
|
-
inject: () => ({ t }),
|
|
68
|
-
}, () => h(Panel, { scope, t })))
|
|
69
|
-
})
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// ── Root panel ──────────────────────────────────────────────────────────────
|
|
73
|
-
|
|
74
|
-
function Panel({ scope, t }: { scope: SettingsScope; t: Translate }): ReactElement {
|
|
75
|
-
const [snap, setSnap] = useState(() => scope.getSnapshot())
|
|
76
|
-
const [inv, setInv] = useState<Inventory | null>(null)
|
|
77
|
-
const [tab, setTab] = useState<'sections' | 'tools' | 'presets' | 'preview'>('sections')
|
|
78
|
-
const [error, setError] = useState<string | null>(null)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
.then((
|
|
86
|
-
.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
h('button', { style: tab === '
|
|
102
|
-
h('button', { style: tab === '
|
|
103
|
-
h('button', { style: tab === '
|
|
104
|
-
h('button', { style: s.
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
1
|
+
/**
|
|
2
|
+
* dsh-prompt-customizer — browser half (TypeScript source).
|
|
3
|
+
*
|
|
4
|
+
* Registers a "提示词定制" settings section (sidebar entry) and a plugin
|
|
5
|
+
* settings card. The panel fetches the effective section / tool inventory
|
|
6
|
+
* from the host `/api/prompt-customizer/inventory` and lets the user block,
|
|
7
|
+
* replace, inject prompt sections and hide tools. All writes go through the
|
|
8
|
+
* bound settings scope (`prompt-customizer` namespace) and apply live.
|
|
9
|
+
*
|
|
10
|
+
* Built by tsdown into the __ModuleLoader__ factory bundle at client/client.js;
|
|
11
|
+
* the only external is the loader module table's react entry.
|
|
12
|
+
*/
|
|
13
|
+
import { createElement as h, useEffect, useState, type ReactElement } from 'react'
|
|
14
|
+
import { DICT, type Translate } from './locales.ts'
|
|
15
|
+
import { s } from './styles.ts'
|
|
16
|
+
import type { Config, Inventory, SettingsScope } from './types.ts'
|
|
17
|
+
import { SectionsTab } from './SectionsTab.tsx'
|
|
18
|
+
import { ToolsTab } from './ToolsTab.tsx'
|
|
19
|
+
import { PresetsTab } from './PresetsTab.tsx'
|
|
20
|
+
import { PreviewTab } from './PreviewTab.tsx'
|
|
21
|
+
|
|
22
|
+
const NS = 'prompt-customizer'
|
|
23
|
+
const INVENTORY_URL = '/api/prompt-customizer/inventory'
|
|
24
|
+
|
|
25
|
+
export const name = 'prompt-customizer'
|
|
26
|
+
export const inject = ['slots', 'locale']
|
|
27
|
+
|
|
28
|
+
/** The subset of the client cordis context this plugin touches. */
|
|
29
|
+
interface ClientContext {
|
|
30
|
+
effect(callback: () => unknown, label?: string): void
|
|
31
|
+
locale: {
|
|
32
|
+
register(namespace: string, dicts: { zh: Record<string, string>; en: Record<string, string> }): unknown
|
|
33
|
+
bind(namespace: string): Translate
|
|
34
|
+
}
|
|
35
|
+
inject(services: string[], callback: (scoped: SettingsScopeHost) => void): void
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** The host surface the settings card needs (settingsScope transport). */
|
|
39
|
+
interface SettingsScopeHost {
|
|
40
|
+
settingsScope: {
|
|
41
|
+
bind(options: { namespace: string }): SettingsScope
|
|
42
|
+
}
|
|
43
|
+
slots: {
|
|
44
|
+
inject(name: string, register: () => unknown): void
|
|
45
|
+
register(meta: Record<string, unknown>, render: () => unknown): unknown
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function apply(ctx: ClientContext): void {
|
|
50
|
+
ctx.effect(() => ctx.locale.register(NS, DICT), 'prompt-customizer: locale')
|
|
51
|
+
const t = ctx.locale.bind(NS)
|
|
52
|
+
|
|
53
|
+
// Register a standalone settings section (sidebar menu entry) so the panel
|
|
54
|
+
// only appears under "提示词定制", not as a card inside the "插件" settings
|
|
55
|
+
// list. Goes through a NESTED inject of settingsScope so the panel can
|
|
56
|
+
// read/write the namespace config; on hosts without the settings transport
|
|
57
|
+
// it stays hidden instead of unmounting the whole plugin.
|
|
58
|
+
ctx.inject(['settingsScope'], (scoped) => {
|
|
59
|
+
const scope = scoped.settingsScope.bind({ namespace: NS })
|
|
60
|
+
|
|
61
|
+
scoped.slots.inject('settings.section', () => scoped.slots.register({
|
|
62
|
+
name: 'settings.section',
|
|
63
|
+
id: 'prompt-customizer',
|
|
64
|
+
order: 50,
|
|
65
|
+
label: () => t('nav'),
|
|
66
|
+
locale: NS,
|
|
67
|
+
inject: () => ({ t }),
|
|
68
|
+
}, () => h(Panel, { scope, t })))
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ── Root panel ──────────────────────────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
function Panel({ scope, t }: { scope: SettingsScope; t: Translate }): ReactElement {
|
|
75
|
+
const [snap, setSnap] = useState(() => scope.getSnapshot())
|
|
76
|
+
const [inv, setInv] = useState<Inventory | null>(null)
|
|
77
|
+
const [tab, setTab] = useState<'sections' | 'tools' | 'presets' | 'preview'>('sections')
|
|
78
|
+
const [error, setError] = useState<string | null>(null)
|
|
79
|
+
const [refreshId, setRefreshId] = useState(0)
|
|
80
|
+
|
|
81
|
+
useEffect(() => scope.subscribe(() => setSnap(scope.getSnapshot())), [scope])
|
|
82
|
+
|
|
83
|
+
const refresh = (): void => {
|
|
84
|
+
fetch(INVENTORY_URL)
|
|
85
|
+
.then((r) => r.json())
|
|
86
|
+
.then((data: Inventory) => { setInv(data); setError(null); setRefreshId((n) => n + 1) })
|
|
87
|
+
.catch((e: unknown) => setError(String(e instanceof Error ? e.message : e)))
|
|
88
|
+
}
|
|
89
|
+
useEffect(refresh, [])
|
|
90
|
+
|
|
91
|
+
if (snap.status !== 'ready' && snap.value === undefined) {
|
|
92
|
+
return h('div', { style: s.muted }, t('loading'))
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const cfg: Config = snap.value && typeof snap.value === 'object'
|
|
96
|
+
? snap.value as Config
|
|
97
|
+
: { sections: [], replace: {}, inject: [], tools: { exclude: [], include: [] } }
|
|
98
|
+
|
|
99
|
+
return h('div', { style: s.root }, [
|
|
100
|
+
h('div', { style: s.bar }, [
|
|
101
|
+
h('button', { style: tab === 'sections' ? s.tabActive : s.tab, onClick: () => setTab('sections') }, t('tabsSections')),
|
|
102
|
+
h('button', { style: tab === 'tools' ? s.tabActive : s.tab, onClick: () => setTab('tools') }, t('tabsTools')),
|
|
103
|
+
h('button', { style: tab === 'presets' ? s.tabActive : s.tab, onClick: () => setTab('presets') }, t('tabsPresets')),
|
|
104
|
+
h('button', { style: tab === 'preview' ? s.tabActive : s.tab, onClick: () => setTab('preview') }, t('tabsPreview')),
|
|
105
|
+
h('button', { style: s.refresh, onClick: refresh }, t('refresh')),
|
|
106
|
+
]),
|
|
107
|
+
error ? h('div', { style: s.error }, String(error)) : null,
|
|
108
|
+
tab === 'sections'
|
|
109
|
+
? h(SectionsTab, { cfg, inv, scope, t })
|
|
110
|
+
: tab === 'tools'
|
|
111
|
+
? h(ToolsTab, { cfg, inv, scope, t })
|
|
112
|
+
: tab === 'presets'
|
|
113
|
+
? h(PresetsTab, { cfg, inv, scope, t })
|
|
114
|
+
: h(PreviewTab, { scope, t, active: tab === 'preview', refreshId }),
|
|
115
|
+
])
|
|
116
|
+
}
|
package/src/client/locales.ts
CHANGED
|
@@ -1,97 +1,101 @@
|
|
|
1
|
-
/** Locale dictionaries for the prompt-customizer panel. */
|
|
2
|
-
export const DICT = {
|
|
3
|
-
zh: {
|
|
4
|
-
nav: '提示词定制',
|
|
5
|
-
loading: '加载中…',
|
|
6
|
-
unavailable: '当前环境未启用本命名空间。',
|
|
7
|
-
tabsSections: '提示词',
|
|
8
|
-
tabsTools: '工具',
|
|
9
|
-
tabsPresets: '预设',
|
|
10
|
-
tabsPreview: '预览',
|
|
11
|
-
refresh: '刷新',
|
|
12
|
-
blockedOff: '未屏蔽',
|
|
13
|
-
blockedOn: '已屏蔽',
|
|
14
|
-
replaced: '已替换',
|
|
15
|
-
manual: '手动',
|
|
16
|
-
system: '系统',
|
|
17
|
-
replace: '编辑',
|
|
18
|
-
clearReplace: '清除',
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
1
|
+
/** Locale dictionaries for the prompt-customizer panel. */
|
|
2
|
+
export const DICT = {
|
|
3
|
+
zh: {
|
|
4
|
+
nav: '提示词定制',
|
|
5
|
+
loading: '加载中…',
|
|
6
|
+
unavailable: '当前环境未启用本命名空间。',
|
|
7
|
+
tabsSections: '提示词',
|
|
8
|
+
tabsTools: '工具',
|
|
9
|
+
tabsPresets: '预设',
|
|
10
|
+
tabsPreview: '预览',
|
|
11
|
+
refresh: '刷新',
|
|
12
|
+
blockedOff: '未屏蔽',
|
|
13
|
+
blockedOn: '已屏蔽',
|
|
14
|
+
replaced: '已替换',
|
|
15
|
+
manual: '手动',
|
|
16
|
+
system: '系统',
|
|
17
|
+
replace: '编辑',
|
|
18
|
+
clearReplace: '清除',
|
|
19
|
+
clearInput: '清空',
|
|
20
|
+
injectNew: '注入新段',
|
|
21
|
+
name: '名称',
|
|
22
|
+
order: '顺序',
|
|
23
|
+
text: '文本',
|
|
24
|
+
add: '添加',
|
|
25
|
+
hiddenOff: '可见',
|
|
26
|
+
hiddenOn: '已隐藏',
|
|
27
|
+
selectAll: '全选',
|
|
28
|
+
selectNone: '全不选',
|
|
29
|
+
empty: '(空)',
|
|
30
|
+
dynamic: '<动态生成>',
|
|
31
|
+
drag: '拖动排序',
|
|
32
|
+
moveUp: '上移',
|
|
33
|
+
moveDown: '下移',
|
|
34
|
+
previewHint: '以下为屏蔽/替换/注入后的最终系统提示词:',
|
|
35
|
+
previewPrompt: '提示词预览',
|
|
36
|
+
previewSections: '段',
|
|
37
|
+
previewTools: '工具预览',
|
|
38
|
+
previewToolCount: '个工具',
|
|
39
|
+
savePreset: '保存当前为预设',
|
|
40
|
+
presetName: '预设名称',
|
|
41
|
+
save: '保存',
|
|
42
|
+
importPreset: '导入预设',
|
|
43
|
+
import: '导入',
|
|
44
|
+
apply: '应用',
|
|
45
|
+
export: '导出',
|
|
46
|
+
delete: '删除',
|
|
47
|
+
restore: '还原',
|
|
48
|
+
active: '使用中',
|
|
49
|
+
preset: '预设',
|
|
50
|
+
},
|
|
51
|
+
en: {
|
|
52
|
+
nav: 'Prompt Customizer',
|
|
53
|
+
loading: 'Loading…',
|
|
54
|
+
unavailable: 'Namespace unavailable in this environment.',
|
|
55
|
+
tabsSections: 'Sections',
|
|
56
|
+
tabsTools: 'Tools',
|
|
57
|
+
tabsPresets: 'Presets',
|
|
58
|
+
tabsPreview: 'Preview',
|
|
59
|
+
refresh: 'Refresh',
|
|
60
|
+
blockedOff: 'Active',
|
|
61
|
+
blockedOn: 'Blocked',
|
|
62
|
+
replaced: 'Replaced',
|
|
63
|
+
manual: 'Manual',
|
|
64
|
+
system: 'System',
|
|
65
|
+
replace: 'Replace',
|
|
66
|
+
clearReplace: 'Clear',
|
|
67
|
+
clearInput: 'Clear',
|
|
68
|
+
injectNew: 'Inject section',
|
|
69
|
+
name: 'Name',
|
|
70
|
+
order: 'Order',
|
|
71
|
+
text: 'Text',
|
|
72
|
+
add: 'Add',
|
|
73
|
+
hiddenOff: 'Visible',
|
|
74
|
+
hiddenOn: 'Hidden',
|
|
75
|
+
selectAll: 'Select all',
|
|
76
|
+
selectNone: 'Select none',
|
|
77
|
+
empty: '(empty)',
|
|
78
|
+
dynamic: '<dynamic>',
|
|
79
|
+
drag: 'Drag to reorder',
|
|
80
|
+
moveUp: 'Move up',
|
|
81
|
+
moveDown: 'Move down',
|
|
82
|
+
previewHint: 'Final system prompt after block/replace/inject:',
|
|
83
|
+
previewPrompt: 'Prompt preview',
|
|
84
|
+
previewSections: 'sections',
|
|
85
|
+
previewTools: 'Tools preview',
|
|
86
|
+
previewToolCount: 'tools',
|
|
87
|
+
savePreset: 'Save current as preset',
|
|
88
|
+
presetName: 'Preset name',
|
|
89
|
+
save: 'Save',
|
|
90
|
+
importPreset: 'Import preset',
|
|
91
|
+
import: 'Import',
|
|
92
|
+
apply: 'Apply',
|
|
93
|
+
export: 'Export',
|
|
94
|
+
delete: 'Delete',
|
|
95
|
+
restore: 'Restore',
|
|
96
|
+
active: 'Active',
|
|
97
|
+
preset: 'Preset',
|
|
98
|
+
},
|
|
99
|
+
} as const
|
|
100
|
+
|
|
101
|
+
export type Translate = (key: string) => string
|