dsh-custom-mode 1.7.1 → 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 +258 -38
- package/locales.mjs +32 -0
- package/package.json +1 -1
package/client.js
CHANGED
|
@@ -98,6 +98,22 @@ 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": "收起完整说明",
|
|
108
|
+
"aria.expand": "展开详情",
|
|
109
|
+
"aria.collapse": "收起详情",
|
|
110
|
+
"detail.id": "行 id",
|
|
111
|
+
"detail.note": "说明",
|
|
112
|
+
"detail.state": "开关状态",
|
|
113
|
+
"detail.explicitOn": "已手动启用",
|
|
114
|
+
"detail.explicitOff": "已手动停用",
|
|
115
|
+
"detail.untouched": "未改动(跟随官方默认)",
|
|
116
|
+
"detail.platform": "平台条件",
|
|
101
117
|
"api.saved": "已保存({name},基础模式 {mode})。新建会话即生效,当前会话保持原配置。",
|
|
102
118
|
"api.created": "已创建「{name}」。现在可以为它写系统提示词。",
|
|
103
119
|
"api.duplicated": "已复制自「{from}」。两份从此各改各的。",
|
|
@@ -272,6 +288,22 @@ try {
|
|
|
272
288
|
"api.alreadyLast": "「{name}」 is already last.",
|
|
273
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.",
|
|
274
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",
|
|
298
|
+
"aria.expand": "Show details",
|
|
299
|
+
"aria.collapse": "Hide details",
|
|
300
|
+
"detail.id": "Row id",
|
|
301
|
+
"detail.note": "Note",
|
|
302
|
+
"detail.state": "Switch",
|
|
303
|
+
"detail.explicitOn": "Set to on by you",
|
|
304
|
+
"detail.explicitOff": "Set to off by you",
|
|
305
|
+
"detail.untouched": "Untouched (follows the shipped default)",
|
|
306
|
+
"detail.platform": "Platform condition",
|
|
275
307
|
"api.saved": "Saved ({name}, base mode {mode}). A new session picks it up; the current one keeps its configuration.",
|
|
276
308
|
"api.created": "Created 「{name}」. You can write its system prompt now.",
|
|
277
309
|
"api.duplicated": "Copied from 「{from}」. The two are independent from now on.",
|
|
@@ -560,6 +592,14 @@ try {
|
|
|
560
592
|
".cpfe{--g:8px;display:flex;flex-direction:column;gap:24px;width:100%;max-width:900px;box-sizing:border-box;padding-bottom:16px}",
|
|
561
593
|
".cpfe-h{margin:0 0 4px;font-size:15px;line-height:22px;color:var(--dsw-alias-label-primary)}",
|
|
562
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}",
|
|
563
603
|
".cpfe-note{display:block;font-size:11px;line-height:16px;color:var(--dsw-alias-label-secondary)}",
|
|
564
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}",
|
|
565
605
|
".cpfe-pills{display:flex;flex-wrap:wrap;gap:6px;align-items:center}",
|
|
@@ -567,14 +607,25 @@ try {
|
|
|
567
607
|
".cpfe-actions{display:flex;flex-wrap:wrap;gap:var(--g);align-items:center;margin-top:4px}",
|
|
568
608
|
".cpfe-newrow{display:flex;gap:var(--g);align-items:center;flex-wrap:wrap;margin-top:10px}",
|
|
569
609
|
".cpfe-field{display:flex;width:100%;margin-bottom:8px}",
|
|
570
|
-
".cpfe-row-head{
|
|
571
|
-
".cpfe-row-switch{flex:0 0 auto
|
|
610
|
+
".cpfe-row-head{font-size:13px;line-height:18px;color:var(--dsw-alias-label-primary)}",
|
|
611
|
+
".cpfe-row-switch{flex:0 0 auto}",
|
|
612
|
+
".cpfe-row-line{display:flex;align-items:center;gap:8px;flex-wrap:wrap;min-width:0}",
|
|
613
|
+
".cpfe-row-note{min-height:16px;font-size:12px;line-height:16px;color:var(--dsw-alias-label-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}",
|
|
614
|
+
".cpfe-row-toggle{flex:0 0 auto;width:22px;height:22px;padding:0;border:0;border-radius:6px;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;font-size:11px;line-height:1}",
|
|
615
|
+
".cpfe-row-toggle:hover{background:var(--dsw-alias-bg-layer-2)}",
|
|
616
|
+
".cpfe-row-open{border-color:var(--dsw-alias-border-l2)}",
|
|
617
|
+
".cpfe-row-detail{display:flex;flex-direction:column;gap:4px;margin:0 0 2px 14px;padding:8px 12px;border-left:.5px solid var(--dsw-alias-border-l1);font-size:12px;line-height:17px;color:var(--dsw-alias-label-secondary)}",
|
|
618
|
+
".cpfe-detail-line{display:flex;gap:8px;min-width:0}",
|
|
619
|
+
".cpfe-detail-key{flex:0 0 62px;color:var(--dsw-alias-label-secondary)}",
|
|
620
|
+
".cpfe-detail-value{min-width:0;overflow-wrap:anywhere}",
|
|
572
621
|
".cpfe-sec-head{display:flex;align-items:center;justify-content:space-between;gap:var(--g);flex-wrap:wrap}",
|
|
573
622
|
".cpfe-newinput{flex:1;min-width:200px}",
|
|
574
|
-
|
|
575
|
-
".cpfe-
|
|
576
|
-
".cpfe-
|
|
577
|
-
".cpfe-
|
|
623
|
+
// 单列、行高统一(对齐官方插件页的形态);之前的自适应多列网格会让行高参差不齐。
|
|
624
|
+
".cpfe-rows{display:flex;flex-direction:column;gap:6px;margin-top:4px}",
|
|
625
|
+
".cpfe-line{display:flex;flex-direction:column;gap:6px;min-width:0}",
|
|
626
|
+
".cpfe-group{display:flex;flex-direction:column;gap:6px;margin-top:8px;min-width:0}",
|
|
627
|
+
".cpfe-kids{display:flex;flex-direction:column;gap:6px;margin-left:14px;padding-left:14px;border-left:.5px solid var(--dsw-alias-border-l1)}",
|
|
628
|
+
".cpfe-row{display:flex;gap:10px;align-items:center;box-sizing:border-box;min-height:52px;padding:8px 12px;border-radius:10px;border:.5px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1)}",
|
|
578
629
|
".cpfe-row-meta{display:flex;flex-direction:column;gap:3px;min-width:0;flex:1}",
|
|
579
630
|
".cpfe-row-badges{display:flex;align-items:center;gap:6px;flex-wrap:wrap}",
|
|
580
631
|
".cpfe-editor{box-sizing:border-box;width:100%;min-height:240px;resize:vertical;padding: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-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;line-height:20px}",
|
|
@@ -704,9 +755,52 @@ try {
|
|
|
704
755
|
return String(template).replace("{name}", name)
|
|
705
756
|
}
|
|
706
757
|
|
|
707
|
-
/**
|
|
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
|
+
|
|
793
|
+
/**
|
|
794
|
+
* One row of the plugin switch list.
|
|
795
|
+
*
|
|
796
|
+
* Laid out like the shell's own plugin page: **one compact line per row** — title, status tags, a
|
|
797
|
+
* one-line truncated description and the switch on the right — with everything else (the row id, the
|
|
798
|
+
* full note, the tri-state detail, the platform condition) behind a per-row disclosure. Measured before
|
|
799
|
+
* this change: 33 rows at 274px wide and 84–117px tall with a bare `tool-bash` line each, i.e. uneven
|
|
800
|
+
* heights and a lot of vertical noise for information most users never read.
|
|
801
|
+
*/
|
|
708
802
|
function RowList(props) {
|
|
709
|
-
const { rows, overrides, onToggle, depth, t } = props
|
|
803
|
+
const { rows, overrides, onToggle, expanded, onToggleExpand, depth, t } = props
|
|
710
804
|
return react.createElement(
|
|
711
805
|
"div",
|
|
712
806
|
{ className: depth === 0 ? "cpfe-rows" : "cpfe-kids" },
|
|
@@ -716,49 +810,109 @@ try {
|
|
|
716
810
|
const effective = overrides[row.id] !== undefined ? overrides[row.id] : !row.disabled
|
|
717
811
|
const changed = overrides[row.id] !== undefined
|
|
718
812
|
const rowTitle = t("row." + row.id + ".label", row.label)
|
|
813
|
+
const note = row.note === null || row.note === undefined ? null : t("row." + row.id + ".note", row.note)
|
|
814
|
+
const open = expanded !== undefined && expanded[row.id] === true
|
|
719
815
|
return react.createElement(
|
|
720
816
|
"div",
|
|
721
|
-
{ key: row.id, className: row.children.length > 0 ? "cpfe-group" :
|
|
817
|
+
{ key: row.id, className: row.children.length > 0 ? "cpfe-group" : "cpfe-line" },
|
|
722
818
|
react.createElement(
|
|
723
819
|
"div",
|
|
724
|
-
{ className: "cpfe-row" },
|
|
820
|
+
{ className: "cpfe-row" + (open ? " cpfe-row-open" : "") },
|
|
821
|
+
react.createElement(
|
|
822
|
+
"div",
|
|
823
|
+
{ className: "cpfe-row-meta" },
|
|
824
|
+
react.createElement(
|
|
825
|
+
"div",
|
|
826
|
+
{ className: "cpfe-row-line" },
|
|
827
|
+
react.createElement("span", { className: "cpfe-row-head" }, rowTitle),
|
|
828
|
+
react.createElement(
|
|
829
|
+
"div",
|
|
830
|
+
{ className: "cpfe-row-badges" },
|
|
831
|
+
react.createElement(
|
|
832
|
+
A.Tag,
|
|
833
|
+
{ tone: effective ? "success" : "neutral" },
|
|
834
|
+
effective ? t("status.enabled") : t("status.disabled"),
|
|
835
|
+
),
|
|
836
|
+
row.essential ? react.createElement(A.Tag, { tone: "warning" }, t("tag.essential")) : null,
|
|
837
|
+
row.disabledExpression !== null && row.disabledExpression !== undefined
|
|
838
|
+
? react.createElement(A.Tag, { tone: "outline" }, t("tag.followPlatform"))
|
|
839
|
+
: null,
|
|
840
|
+
changed ? react.createElement(A.Tag, { tone: "info" }, t("status.changed")) : null,
|
|
841
|
+
),
|
|
842
|
+
),
|
|
843
|
+
// 折叠时只留一行说明(超出截断,悬停给全文)。**没有说明就留空**:裸露的行 id
|
|
844
|
+
// 是开发者信息,已经在「详情」里 —— 之前它作为副标题占了每行一行。
|
|
845
|
+
react.createElement("span", { className: "cpfe-row-note", title: note ?? "" }, note ?? ""),
|
|
846
|
+
),
|
|
847
|
+
react.createElement(
|
|
848
|
+
"button",
|
|
849
|
+
{
|
|
850
|
+
type: "button",
|
|
851
|
+
className: "cpfe-row-toggle",
|
|
852
|
+
"aria-expanded": open,
|
|
853
|
+
"aria-label": t(open ? "aria.collapse" : "aria.expand"),
|
|
854
|
+
onClick: () => onToggleExpand(row.id),
|
|
855
|
+
},
|
|
856
|
+
open ? "▾" : "▸",
|
|
857
|
+
),
|
|
725
858
|
react.createElement(A.Switch, {
|
|
726
859
|
checked: effective,
|
|
727
860
|
onChange: (next) => onToggle(row.id, next),
|
|
728
861
|
// `Switch` renders NO text: its `label` is the accessible name only, so the
|
|
729
|
-
// visible title is drawn
|
|
862
|
+
// visible title is drawn next to it (an unlabelled toggle is unusable).
|
|
730
863
|
label: rowTitle,
|
|
731
864
|
className: "cpfe-row-switch",
|
|
732
865
|
}),
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
react.createElement("span", { className: "cpfe-row-head" }, rowTitle),
|
|
737
|
-
react.createElement(
|
|
866
|
+
),
|
|
867
|
+
open
|
|
868
|
+
? react.createElement(
|
|
738
869
|
"div",
|
|
739
|
-
{ className: "cpfe-row-
|
|
870
|
+
{ className: "cpfe-row-detail" },
|
|
740
871
|
react.createElement(
|
|
741
|
-
|
|
742
|
-
{
|
|
743
|
-
|
|
872
|
+
"div",
|
|
873
|
+
{ className: "cpfe-detail-line" },
|
|
874
|
+
react.createElement("span", { className: "cpfe-detail-key" }, t("detail.id")),
|
|
875
|
+
react.createElement("span", { className: "cpfe-mono" }, row.id),
|
|
876
|
+
),
|
|
877
|
+
note === null
|
|
878
|
+
? null
|
|
879
|
+
: react.createElement(
|
|
880
|
+
"div",
|
|
881
|
+
{ className: "cpfe-detail-line" },
|
|
882
|
+
react.createElement("span", { className: "cpfe-detail-key" }, t("detail.note")),
|
|
883
|
+
react.createElement("span", { className: "cpfe-detail-value" }, note),
|
|
884
|
+
),
|
|
885
|
+
react.createElement(
|
|
886
|
+
"div",
|
|
887
|
+
{ className: "cpfe-detail-line" },
|
|
888
|
+
react.createElement("span", { className: "cpfe-detail-key" }, t("detail.state")),
|
|
889
|
+
react.createElement(
|
|
890
|
+
"span",
|
|
891
|
+
{ className: "cpfe-detail-value" },
|
|
892
|
+
changed
|
|
893
|
+
? effective
|
|
894
|
+
? t("detail.explicitOn")
|
|
895
|
+
: t("detail.explicitOff")
|
|
896
|
+
: t("detail.untouched"),
|
|
897
|
+
),
|
|
744
898
|
),
|
|
745
|
-
row.
|
|
746
|
-
|
|
747
|
-
|
|
899
|
+
row.disabledExpression !== null && row.disabledExpression !== undefined
|
|
900
|
+
? react.createElement(
|
|
901
|
+
"div",
|
|
902
|
+
{ className: "cpfe-detail-line" },
|
|
903
|
+
react.createElement("span", { className: "cpfe-detail-key" }, t("detail.platform")),
|
|
904
|
+
react.createElement("span", { className: "cpfe-mono" }, String(row.disabledExpression)),
|
|
905
|
+
)
|
|
748
906
|
: null,
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
react.createElement("span", { className: "cpfe-mono" }, row.id),
|
|
752
|
-
row.note !== null
|
|
753
|
-
? react.createElement("span", { className: "cpfe-note" }, t("row." + row.id + ".note", row.note))
|
|
754
|
-
: null,
|
|
755
|
-
),
|
|
756
|
-
),
|
|
907
|
+
)
|
|
908
|
+
: null,
|
|
757
909
|
row.children.length > 0
|
|
758
910
|
? react.createElement(RowList, {
|
|
759
911
|
rows: row.children,
|
|
760
912
|
overrides: overrides,
|
|
761
913
|
onToggle: onToggle,
|
|
914
|
+
expanded: expanded,
|
|
915
|
+
onToggleExpand: onToggleExpand,
|
|
762
916
|
depth: depth + 1,
|
|
763
917
|
t: t,
|
|
764
918
|
})
|
|
@@ -818,11 +972,36 @@ try {
|
|
|
818
972
|
const [newName, setNewName] = react.useState("")
|
|
819
973
|
const [deleteOpen, setDeleteOpen] = react.useState(false)
|
|
820
974
|
const [acknowledged, setAcknowledged] = react.useState(false)
|
|
975
|
+
/**
|
|
976
|
+
* Which rows have their detail open, by row id.
|
|
977
|
+
*
|
|
978
|
+
* Per-row and local to the section: the list is long (33 rows measured) and the interesting detail
|
|
979
|
+
* differs per user, so nothing is expanded by default and nothing is persisted.
|
|
980
|
+
*/
|
|
981
|
+
const [expandedRows, setExpandedRows] = react.useState({})
|
|
982
|
+
const toggleRowExpanded = (id) =>
|
|
983
|
+
setExpandedRows((previous) => {
|
|
984
|
+
const next = { ...previous }
|
|
985
|
+
if (next[id] === true) delete next[id]
|
|
986
|
+
else next[id] = true
|
|
987
|
+
return next
|
|
988
|
+
})
|
|
821
989
|
/** Hidden `<input type="file">` behind the 「导入」 button. */
|
|
822
990
|
const fileInput = react.useRef(null)
|
|
823
991
|
|
|
824
992
|
const entry = selected === "" ? undefined : entries[selected]
|
|
825
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
|
+
|
|
826
1005
|
const payload = entry === undefined ? null : entry.payload
|
|
827
1006
|
const editorReady = draft !== null && payload !== null
|
|
828
1007
|
const dirty = editorReady && !sameDraft(draft, entry.saved)
|
|
@@ -1216,7 +1395,14 @@ try {
|
|
|
1216
1395
|
"section",
|
|
1217
1396
|
null,
|
|
1218
1397
|
react.createElement("h2", { className: "cpfe-h" }, t("assistant.heading")),
|
|
1219
|
-
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
|
+
}),
|
|
1220
1406
|
list === null
|
|
1221
1407
|
? react.createElement("p", { className: "cpfe-sub" }, t("assistant.loadingList"))
|
|
1222
1408
|
: assistants.length === 0
|
|
@@ -1284,7 +1470,14 @@ try {
|
|
|
1284
1470
|
"section",
|
|
1285
1471
|
{ key: "name" },
|
|
1286
1472
|
react.createElement("h2", { className: "cpfe-h" }, t("name.heading")),
|
|
1287
|
-
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
|
+
}),
|
|
1288
1481
|
react.createElement(A.Input, {
|
|
1289
1482
|
className: "cpfe-field",
|
|
1290
1483
|
value: draft.name,
|
|
@@ -1292,9 +1485,13 @@ try {
|
|
|
1292
1485
|
"aria-label": t("name.heading"),
|
|
1293
1486
|
onChange: (event) => update({ name: event.target.value }),
|
|
1294
1487
|
}),
|
|
1295
|
-
|
|
1296
|
-
|
|
1488
|
+
// 描述用 textarea 而不是单行 Input:shell 的组件里没有多行输入,而双语描述在单行框里
|
|
1489
|
+
// 会被截断(实测界面上只看到 "… / Ful")。高度随内容自适应,样式沿用同一批语义变量。
|
|
1490
|
+
react.createElement("textarea", {
|
|
1491
|
+
ref: descriptionRef,
|
|
1492
|
+
className: "cpfe-field cpfe-desc",
|
|
1297
1493
|
value: draft.description,
|
|
1494
|
+
rows: 2,
|
|
1298
1495
|
placeholder: t("name.descriptionPlaceholder"),
|
|
1299
1496
|
"aria-label": t("name.descriptionPlaceholder"),
|
|
1300
1497
|
onChange: (event) => update({ description: event.target.value }),
|
|
@@ -1353,7 +1550,14 @@ try {
|
|
|
1353
1550
|
"section",
|
|
1354
1551
|
{ key: "mode" },
|
|
1355
1552
|
react.createElement("h2", { className: "cpfe-h" }, t("mode.heading")),
|
|
1356
|
-
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
|
+
}),
|
|
1357
1561
|
react.createElement(
|
|
1358
1562
|
"div",
|
|
1359
1563
|
{ className: "cpfe-pills" },
|
|
@@ -1375,8 +1579,17 @@ try {
|
|
|
1375
1579
|
"section",
|
|
1376
1580
|
{ key: "rows" },
|
|
1377
1581
|
react.createElement("h2", { className: "cpfe-h" }, t("rows.heading")),
|
|
1378
|
-
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
|
+
}),
|
|
1379
1590
|
react.createElement(RowList, {
|
|
1591
|
+
expanded: expandedRows,
|
|
1592
|
+
onToggleExpand: toggleRowExpanded,
|
|
1380
1593
|
rows: payload.rows,
|
|
1381
1594
|
overrides: draft.overrides,
|
|
1382
1595
|
onToggle: (id, next) => update({ overrides: { ...draft.overrides, [id]: next } }),
|
|
@@ -1447,7 +1660,14 @@ try {
|
|
|
1447
1660
|
}),
|
|
1448
1661
|
),
|
|
1449
1662
|
),
|
|
1450
|
-
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
|
+
}),
|
|
1451
1671
|
react.createElement("textarea", {
|
|
1452
1672
|
className: "cpfe-editor",
|
|
1453
1673
|
value: draft.prompt,
|
package/locales.mjs
CHANGED
|
@@ -57,6 +57,22 @@ 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': '收起完整说明',
|
|
67
|
+
'aria.expand': '展开详情',
|
|
68
|
+
'aria.collapse': '收起详情',
|
|
69
|
+
'detail.id': '行 id',
|
|
70
|
+
'detail.note': '说明',
|
|
71
|
+
'detail.state': '开关状态',
|
|
72
|
+
'detail.explicitOn': '已手动启用',
|
|
73
|
+
'detail.explicitOff': '已手动停用',
|
|
74
|
+
'detail.untouched': '未改动(跟随官方默认)',
|
|
75
|
+
'detail.platform': '平台条件',
|
|
60
76
|
'api.saved': '已保存({name},基础模式 {mode})。新建会话即生效,当前会话保持原配置。',
|
|
61
77
|
'api.created': '已创建「{name}」。现在可以为它写系统提示词。',
|
|
62
78
|
'api.duplicated': '已复制自「{from}」。两份从此各改各的。',
|
|
@@ -247,6 +263,22 @@ export const en = {
|
|
|
247
263
|
'api.alreadyLast': '「{name}」 is already last.',
|
|
248
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.',
|
|
249
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',
|
|
273
|
+
'aria.expand': 'Show details',
|
|
274
|
+
'aria.collapse': 'Hide details',
|
|
275
|
+
'detail.id': 'Row id',
|
|
276
|
+
'detail.note': 'Note',
|
|
277
|
+
'detail.state': 'Switch',
|
|
278
|
+
'detail.explicitOn': 'Set to on by you',
|
|
279
|
+
'detail.explicitOff': 'Set to off by you',
|
|
280
|
+
'detail.untouched': 'Untouched (follows the shipped default)',
|
|
281
|
+
'detail.platform': 'Platform condition',
|
|
250
282
|
'api.saved': 'Saved ({name}, base mode {mode}). A new session picks it up; the current one keeps its configuration.',
|
|
251
283
|
'api.created': 'Created 「{name}」. You can write its system prompt now.',
|
|
252
284
|
'api.duplicated': 'Copied from 「{from}」. The two are independent from now on.',
|
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",
|