dsh-custom-mode 1.8.0 → 1.9.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/client.js +114 -7
- package/locales.mjs +14 -0
- package/package.json +1 -1
package/client.js
CHANGED
|
@@ -98,6 +98,13 @@ try {
|
|
|
98
98
|
"api.alreadyLast": "「{name}」已经在最后面。",
|
|
99
99
|
"api.badVariableName": "变量引用的写法不合法:{variable} 里的名字只能用小写字母、数字、下划线,且以字母开头。要写字面量花括号,请用单个左花括号,或不闭合的双左花括号。",
|
|
100
100
|
"api.unknownVariable": "{variable} 不是已注册的变量,渲染会报错并让本模式每个请求都失败。可用:{known}。",
|
|
101
|
+
"assistant.short": "每个助手是一个独立模式:自己的系统提示词、基础模式与插件开关。",
|
|
102
|
+
"name.short": "改名只影响显示,内部标识与已有会话不受影响。",
|
|
103
|
+
"mode.short": "底子决定「行集合」与工具能力;persona 行始终由本模式替换。",
|
|
104
|
+
"rows.short": "逐行控制挂载哪些插件;没拨过的行保持官方默认。",
|
|
105
|
+
"prompt.short": "这段文本就是本模式的系统提示词,保存后下一步生效。",
|
|
106
|
+
"aria.expandHint": "展开完整说明",
|
|
107
|
+
"aria.collapseHint": "收起完整说明",
|
|
101
108
|
"aria.expand": "展开详情",
|
|
102
109
|
"aria.collapse": "收起详情",
|
|
103
110
|
"detail.id": "行 id",
|
|
@@ -281,6 +288,13 @@ try {
|
|
|
281
288
|
"api.alreadyLast": "「{name}」 is already last.",
|
|
282
289
|
"api.badVariableName": "{variable} is not a valid variable reference: names may use lower-case letters, digits and underscores, and must start with a letter. For a literal brace, use a single opening brace or an unclosed double brace.",
|
|
283
290
|
"api.unknownVariable": "{variable} is not a registered variable — rendering would fail every request in this mode. Available: {known}.",
|
|
291
|
+
"assistant.short": "Each assistant is its own mode: its own system prompt, base mode and plugin switches.",
|
|
292
|
+
"name.short": "Renaming only changes what is displayed — not the internal id or existing sessions.",
|
|
293
|
+
"mode.short": "The base decides the row set and tool abilities; the persona row is always replaced by this mode.",
|
|
294
|
+
"rows.short": "Control which plugins this mode mounts, row by row; untouched rows keep the shipped default.",
|
|
295
|
+
"prompt.short": "Saving this text makes it the system prompt of this mode, and it takes effect on the next step.",
|
|
296
|
+
"aria.expandHint": "Show the full explanation",
|
|
297
|
+
"aria.collapseHint": "Hide the full explanation",
|
|
284
298
|
"aria.expand": "Show details",
|
|
285
299
|
"aria.collapse": "Hide details",
|
|
286
300
|
"detail.id": "Row id",
|
|
@@ -578,6 +592,14 @@ try {
|
|
|
578
592
|
".cpfe{--g:8px;display:flex;flex-direction:column;gap:24px;width:100%;max-width:900px;box-sizing:border-box;padding-bottom:16px}",
|
|
579
593
|
".cpfe-h{margin:0 0 4px;font-size:15px;line-height:22px;color:var(--dsw-alias-label-primary)}",
|
|
580
594
|
".cpfe-sub{margin:0 0 12px;font-size:13px;line-height:20px;color:var(--dsw-alias-label-secondary)}",
|
|
595
|
+
// 一行提示 + 详情下拉(与插件行同一套语言)
|
|
596
|
+
".cpfe-hint{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin:0 0 10px}",
|
|
597
|
+
".cpfe-hint-line{flex:1;min-width:0;font-size:12px;line-height:17px;color:var(--dsw-alias-label-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}",
|
|
598
|
+
".cpfe-hint-toggle{flex:0 0 auto;width:20px;height:20px;padding:0;border:0;border-radius:6px;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;font-size:11px;line-height:1}",
|
|
599
|
+
".cpfe-hint-toggle:hover{background:var(--dsw-alias-bg-layer-2)}",
|
|
600
|
+
".cpfe-hint-detail{flex:1 0 100%;margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary)}",
|
|
601
|
+
// 描述:多行、自适应高度(没有多行输入组件,所以用 textarea + 同一批语义变量)
|
|
602
|
+
".cpfe-desc{box-sizing:border-box;min-height:56px;max-height:200px;resize:vertical;padding:8px 12px;border-radius:10px;border:.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;line-height:20px;margin-bottom:8px}",
|
|
581
603
|
".cpfe-note{display:block;font-size:11px;line-height:16px;color:var(--dsw-alias-label-secondary)}",
|
|
582
604
|
".cpfe-mono{display:block;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;line-height:16px;color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere}",
|
|
583
605
|
".cpfe-pills{display:flex;flex-wrap:wrap;gap:6px;align-items:center}",
|
|
@@ -733,6 +755,41 @@ try {
|
|
|
733
755
|
return String(template).replace("{name}", name)
|
|
734
756
|
}
|
|
735
757
|
|
|
758
|
+
/**
|
|
759
|
+
* A section's one-line hint, with the full explanation behind a disclosure.
|
|
760
|
+
*
|
|
761
|
+
* The sections used to open with a paragraph each. On a real screen that is a wall of text above the
|
|
762
|
+
* controls the user came for, and the same "compact line + open on demand" language as the row list keeps
|
|
763
|
+
* the page scannable without dropping a word of the explanation.
|
|
764
|
+
*/
|
|
765
|
+
function SectionHint(props) {
|
|
766
|
+
const { id, hint, detail, expanded, onToggleExpand, t } = props
|
|
767
|
+
const open = expanded[id] === true
|
|
768
|
+
const collapsible = typeof detail === "string" && detail !== "" && detail !== hint
|
|
769
|
+
return react.createElement(
|
|
770
|
+
"div",
|
|
771
|
+
{ className: "cpfe-hint" },
|
|
772
|
+
react.createElement("span", { className: "cpfe-hint-line", title: hint }, hint),
|
|
773
|
+
collapsible === false
|
|
774
|
+
? null
|
|
775
|
+
: react.createElement(
|
|
776
|
+
"button",
|
|
777
|
+
{
|
|
778
|
+
type: "button",
|
|
779
|
+
className: "cpfe-hint-toggle",
|
|
780
|
+
"aria-expanded": open,
|
|
781
|
+
"aria-label": t(open ? "aria.collapse" : "aria.expandHint"),
|
|
782
|
+
title: t(open ? "aria.collapse" : "aria.expandHint"),
|
|
783
|
+
onClick: () => onToggleExpand(id),
|
|
784
|
+
},
|
|
785
|
+
open ? "▾" : "▸",
|
|
786
|
+
),
|
|
787
|
+
open === true && collapsible === true
|
|
788
|
+
? react.createElement("p", { className: "cpfe-hint-detail" }, detail)
|
|
789
|
+
: null,
|
|
790
|
+
)
|
|
791
|
+
}
|
|
792
|
+
|
|
736
793
|
/**
|
|
737
794
|
* One row of the plugin switch list.
|
|
738
795
|
*
|
|
@@ -934,6 +991,17 @@ try {
|
|
|
934
991
|
|
|
935
992
|
const entry = selected === "" ? undefined : entries[selected]
|
|
936
993
|
const draft = entry === undefined ? null : entry.value
|
|
994
|
+
|
|
995
|
+
/** 描述框:随内容长高,避免双语描述被单行截断。draft 在未选中助手时是 null,依赖项要先取出来。 */
|
|
996
|
+
const descriptionRef = react.useRef(null)
|
|
997
|
+
const draftDescription = draft === null ? "" : draft.description
|
|
998
|
+
react.useEffect(() => {
|
|
999
|
+
const el = descriptionRef.current
|
|
1000
|
+
if (el === null || el === undefined) return
|
|
1001
|
+
el.style.height = "auto"
|
|
1002
|
+
el.style.height = String(Math.min(el.scrollHeight, 200)) + "px"
|
|
1003
|
+
}, [draftDescription, selected])
|
|
1004
|
+
|
|
937
1005
|
const payload = entry === undefined ? null : entry.payload
|
|
938
1006
|
const editorReady = draft !== null && payload !== null
|
|
939
1007
|
const dirty = editorReady && !sameDraft(draft, entry.saved)
|
|
@@ -1327,7 +1395,14 @@ try {
|
|
|
1327
1395
|
"section",
|
|
1328
1396
|
null,
|
|
1329
1397
|
react.createElement("h2", { className: "cpfe-h" }, t("assistant.heading")),
|
|
1330
|
-
react.createElement(
|
|
1398
|
+
react.createElement(SectionHint, {
|
|
1399
|
+
id: "hint:assistant",
|
|
1400
|
+
hint: t("assistant.short", t("assistant.hint")),
|
|
1401
|
+
detail: t("assistant.hint"),
|
|
1402
|
+
expanded: expandedRows,
|
|
1403
|
+
onToggleExpand: toggleRowExpanded,
|
|
1404
|
+
t: t,
|
|
1405
|
+
}),
|
|
1331
1406
|
list === null
|
|
1332
1407
|
? react.createElement("p", { className: "cpfe-sub" }, t("assistant.loadingList"))
|
|
1333
1408
|
: assistants.length === 0
|
|
@@ -1395,7 +1470,14 @@ try {
|
|
|
1395
1470
|
"section",
|
|
1396
1471
|
{ key: "name" },
|
|
1397
1472
|
react.createElement("h2", { className: "cpfe-h" }, t("name.heading")),
|
|
1398
|
-
react.createElement(
|
|
1473
|
+
react.createElement(SectionHint, {
|
|
1474
|
+
id: "hint:name",
|
|
1475
|
+
hint: t("name.short", t("name.hint")),
|
|
1476
|
+
detail: t("name.hint"),
|
|
1477
|
+
expanded: expandedRows,
|
|
1478
|
+
onToggleExpand: toggleRowExpanded,
|
|
1479
|
+
t: t,
|
|
1480
|
+
}),
|
|
1399
1481
|
react.createElement(A.Input, {
|
|
1400
1482
|
className: "cpfe-field",
|
|
1401
1483
|
value: draft.name,
|
|
@@ -1403,9 +1485,13 @@ try {
|
|
|
1403
1485
|
"aria-label": t("name.heading"),
|
|
1404
1486
|
onChange: (event) => update({ name: event.target.value }),
|
|
1405
1487
|
}),
|
|
1406
|
-
|
|
1407
|
-
|
|
1488
|
+
// 描述用 textarea 而不是单行 Input:shell 的组件里没有多行输入,而双语描述在单行框里
|
|
1489
|
+
// 会被截断(实测界面上只看到 "… / Ful")。高度随内容自适应,样式沿用同一批语义变量。
|
|
1490
|
+
react.createElement("textarea", {
|
|
1491
|
+
ref: descriptionRef,
|
|
1492
|
+
className: "cpfe-field cpfe-desc",
|
|
1408
1493
|
value: draft.description,
|
|
1494
|
+
rows: 2,
|
|
1409
1495
|
placeholder: t("name.descriptionPlaceholder"),
|
|
1410
1496
|
"aria-label": t("name.descriptionPlaceholder"),
|
|
1411
1497
|
onChange: (event) => update({ description: event.target.value }),
|
|
@@ -1464,7 +1550,14 @@ try {
|
|
|
1464
1550
|
"section",
|
|
1465
1551
|
{ key: "mode" },
|
|
1466
1552
|
react.createElement("h2", { className: "cpfe-h" }, t("mode.heading")),
|
|
1467
|
-
react.createElement(
|
|
1553
|
+
react.createElement(SectionHint, {
|
|
1554
|
+
id: "hint:mode",
|
|
1555
|
+
hint: t("mode.short", t("mode.hint")),
|
|
1556
|
+
detail: t("mode.hint"),
|
|
1557
|
+
expanded: expandedRows,
|
|
1558
|
+
onToggleExpand: toggleRowExpanded,
|
|
1559
|
+
t: t,
|
|
1560
|
+
}),
|
|
1468
1561
|
react.createElement(
|
|
1469
1562
|
"div",
|
|
1470
1563
|
{ className: "cpfe-pills" },
|
|
@@ -1486,7 +1579,14 @@ try {
|
|
|
1486
1579
|
"section",
|
|
1487
1580
|
{ key: "rows" },
|
|
1488
1581
|
react.createElement("h2", { className: "cpfe-h" }, t("rows.heading")),
|
|
1489
|
-
react.createElement(
|
|
1582
|
+
react.createElement(SectionHint, {
|
|
1583
|
+
id: "hint:rows",
|
|
1584
|
+
hint: t("rows.short", t("rows.hint")),
|
|
1585
|
+
detail: t("rows.hint"),
|
|
1586
|
+
expanded: expandedRows,
|
|
1587
|
+
onToggleExpand: toggleRowExpanded,
|
|
1588
|
+
t: t,
|
|
1589
|
+
}),
|
|
1490
1590
|
react.createElement(RowList, {
|
|
1491
1591
|
expanded: expandedRows,
|
|
1492
1592
|
onToggleExpand: toggleRowExpanded,
|
|
@@ -1560,7 +1660,14 @@ try {
|
|
|
1560
1660
|
}),
|
|
1561
1661
|
),
|
|
1562
1662
|
),
|
|
1563
|
-
react.createElement(
|
|
1663
|
+
react.createElement(SectionHint, {
|
|
1664
|
+
id: "hint:prompt",
|
|
1665
|
+
hint: t("prompt.short", t("prompt.hint")),
|
|
1666
|
+
detail: t("prompt.hint"),
|
|
1667
|
+
expanded: expandedRows,
|
|
1668
|
+
onToggleExpand: toggleRowExpanded,
|
|
1669
|
+
t: t,
|
|
1670
|
+
}),
|
|
1564
1671
|
react.createElement("textarea", {
|
|
1565
1672
|
className: "cpfe-editor",
|
|
1566
1673
|
value: draft.prompt,
|
package/locales.mjs
CHANGED
|
@@ -57,6 +57,13 @@ export const zh = {
|
|
|
57
57
|
'api.alreadyLast': '「{name}」已经在最后面。',
|
|
58
58
|
'api.badVariableName': '变量引用的写法不合法:{variable} 里的名字只能用小写字母、数字、下划线,且以字母开头。要写字面量花括号,请用单个左花括号,或不闭合的双左花括号。',
|
|
59
59
|
'api.unknownVariable': '{variable} 不是已注册的变量,渲染会报错并让本模式每个请求都失败。可用:{known}。',
|
|
60
|
+
'assistant.short': '每个助手是一个独立模式:自己的系统提示词、基础模式与插件开关。',
|
|
61
|
+
'name.short': '改名只影响显示,内部标识与已有会话不受影响。',
|
|
62
|
+
'mode.short': '底子决定「行集合」与工具能力;persona 行始终由本模式替换。',
|
|
63
|
+
'rows.short': '逐行控制挂载哪些插件;没拨过的行保持官方默认。',
|
|
64
|
+
'prompt.short': '这段文本就是本模式的系统提示词,保存后下一步生效。',
|
|
65
|
+
'aria.expandHint': '展开完整说明',
|
|
66
|
+
'aria.collapseHint': '收起完整说明',
|
|
60
67
|
'aria.expand': '展开详情',
|
|
61
68
|
'aria.collapse': '收起详情',
|
|
62
69
|
'detail.id': '行 id',
|
|
@@ -256,6 +263,13 @@ export const en = {
|
|
|
256
263
|
'api.alreadyLast': '「{name}」 is already last.',
|
|
257
264
|
'api.badVariableName': '{variable} is not a valid variable reference: names may use lower-case letters, digits and underscores, and must start with a letter. For a literal brace, use a single opening brace or an unclosed double brace.',
|
|
258
265
|
'api.unknownVariable': '{variable} is not a registered variable — rendering would fail every request in this mode. Available: {known}.',
|
|
266
|
+
'assistant.short': 'Each assistant is its own mode: its own system prompt, base mode and plugin switches.',
|
|
267
|
+
'name.short': 'Renaming only changes what is displayed — not the internal id or existing sessions.',
|
|
268
|
+
'mode.short': 'The base decides the row set and tool abilities; the persona row is always replaced by this mode.',
|
|
269
|
+
'rows.short': 'Control which plugins this mode mounts, row by row; untouched rows keep the shipped default.',
|
|
270
|
+
'prompt.short': 'Saving this text makes it the system prompt of this mode, and it takes effect on the next step.',
|
|
271
|
+
'aria.expandHint': 'Show the full explanation',
|
|
272
|
+
'aria.collapseHint': 'Hide the full explanation',
|
|
259
273
|
'aria.expand': 'Show details',
|
|
260
274
|
'aria.collapse': 'Hide details',
|
|
261
275
|
'detail.id': 'Row id',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-custom-mode",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "Custom modes and custom prompts for DeepSeek Harness (dsh): edit a mode's system prompt on the settings page (it takes effect on the next model step), choose its base mode, switch plugins row by row, and keep several assistants side by side.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|