dsh-custom-mode 1.13.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/client.js CHANGED
@@ -61,46 +61,54 @@ try {
61
61
  const ZH = {
62
62
  "nav": "自定义模式",
63
63
  "assistant.heading": "助手",
64
- "assistant.hint": "每个助手是一套独立模式,有自己的提示词、基础模式和插件开关。新建会话时在模式菜单里选用。",
64
+ "assistant.hint": "每个助手单独保存系统提示词、基础模式和插件开关。",
65
+ "posture.hint": "只用于新建,不改变当前助手。",
66
+ "posture.develop.label": "开发",
67
+ "posture.develop.note": "新建后:标准模式,工具按出厂。",
68
+ "posture.write.label": "写作",
69
+ "posture.write.note": "新建后:终端关闭,文件读写打开。",
70
+ "posture.chat.label": "聊天",
71
+ "posture.chat.note": "新建后:文件和终端关闭。",
65
72
  "assistant.empty": "还没有助手,用下面的输入框新建一个。",
66
73
  "assistant.loadingList": "正在读取助手列表…",
67
74
  "assistant.newPlaceholder": "新助手的名字(例如:写作助手)",
68
75
  "assistant.copyName": "「{name}」副本",
69
76
  "assistant.broken": "本机无法启动",
70
77
  "assistant.brokenRows": "{count} 行在本机不可用",
71
- "assistant.brokenHint": "平台无法挂载这个模式,所以它不会出现在新会话的选择器里。点下面的「按本线修复」只关掉那几行,其它选择不动。",
78
+ "assistant.brokenHint": "此模式在本机无法启动,不会出现在新会话菜单里。点「按本线修复」只关闭那些行。",
72
79
  "assistant.switchHint": "切换助手保留各自的未保存草稿;列表中的「未保存」标记即为此。",
73
80
  "btn.create": "新增助手",
81
+ "create.heading": "新建",
74
82
  "btn.creating": "创建中…",
75
- "btn.duplicate": "复制一份",
83
+ "btn.duplicate": "复制",
76
84
  "btn.moveUp": "上移",
77
85
  "btn.moveDown": "下移",
78
- "btn.import": "导入提示词",
79
- "btn.export": "导出提示词",
80
- "btn.reset": "恢复出厂提示词",
81
- "btn.resetHint": "把编辑器里的内容换成出厂模板(新建助手时得到的那一份)。它同样只改草稿,点保存才落盘 —— 误点可以用「重新读取」撤销。",
82
- "btn.delete": "删除这个助手",
86
+ "btn.import": "导入",
87
+ "btn.export": "导出",
88
+ "btn.reset": "恢复出厂",
89
+ "btn.resetHint": "把编辑器换回此助手新建时保存的提示词。只改草稿,保存后才写入。",
90
+ "btn.delete": "删除",
83
91
  "btn.cancel": "取消",
84
- "msg.created": "已创建。现在可以为它写系统提示词。新建会话的模式菜单要刷新一次才会出现它。",
92
+ "msg.created": "已创建。刷新后,新会话的模式菜单里会出现它。",
85
93
  "msg.duplicated": "已复制。两份配置彼此独立。",
86
94
  "msg.deleted": "已删除。",
87
95
  "msg.createFailed": "创建失败",
88
96
  "msg.deleteFailed": "删除失败",
89
97
  "msg.nameRequired": "请先给新助手起个名字。",
90
98
  "msg.readOnlyHint": "仅管理本插件创建的助手;手写 preset 不计入,也不会被改写。",
91
- "msg.reordered": "顺序已保存:新建会话时的模式选择器按这个顺序排列。",
99
+ "msg.reordered": "顺序已保存。新会话的模式菜单按此排列。",
92
100
  "msg.reorderFailed": "调整顺序失败",
93
- "msg.imported": "已导入到编辑器(还没有保存):检查后点「保存」。",
101
+ "msg.imported": "已导入到编辑器。尚未保存。",
94
102
  "msg.importFailed": "导入失败",
95
103
  "api.badDirection": "未知的排序方向:{direction}",
96
- "api.unknownAssistant": "找不到助手「{id}」:这一页只管理本工具创建的助手(目录里有 prompt.md,且组成文件用 prompt-reader.mjs 注入身份)。",
104
+ "api.unknownAssistant": "找不到助手「{id}」。此页只管理本插件创建的助手。",
97
105
  "api.alreadyFirst": "「{name}」已经在最前面。",
98
106
  "api.alreadyLast": "「{name}」已经在最后面。",
99
- "api.badVariableName": "变量引用的写法不合法:{variable} 里的名字只能用小写字母、数字、下划线,且以字母开头。要写字面量花括号,请用单个左花括号,或不闭合的双左花括号。",
100
- "api.unknownVariable": "{variable} 不是已注册的变量,渲染会报错并让本模式每个请求都失败。可用:{known}。",
101
- "mode.unavailable": "本机这条 DSH 线没有可读的出厂组成,基础模式暂时不能切换。",
102
- "rows.unavailable": "出厂组成取不到,插件开关暂时不能编辑;磁盘上原有的行状态仍然生效。",
103
- "mode.pendingRows": "基础模式已改为「{mode}」:保存后,下面的行列表会按新的基础模式重算。",
107
+ "api.badVariableName": "变量 {variable} 不合法。名字用小写字母、数字和下划线,且以字母开头。",
108
+ "api.unknownVariable": "{variable} 不能使用。可用:{known}。",
109
+ "mode.unavailable": "本机读不到出厂组成,基础模式暂不能切换。",
110
+ "rows.unavailable": "本机读不到出厂组成,插件开关暂不能改。磁盘上的现有设置仍然有效。",
111
+ "mode.pendingRows": "基础模式已改为「{mode}」。保存后,工具底换成这套官方预设。",
104
112
  "aria.expand": "展开详情",
105
113
  "aria.collapse": "收起详情",
106
114
  "detail.id": "行 id",
@@ -117,18 +125,20 @@ try {
117
125
  "btn.repair": "按本线修复",
118
126
  "msg.repaired": "已修复",
119
127
  "meta.version": "插件版本",
120
- "meta.versionHint": "安装时不钉版本号会受 pnpm 发布冷却期影响(默认 24 小时),可能装到较旧的版本。要换版本请按 README 的钉版本命令重装,然后重启 DSH。",
121
- "api.saved": "已保存({name},基础模式 {mode})。新建会话即生效,当前会话保持原配置。",
122
- "api.savedWithExclusiveRows": "已保存({name},基础模式 {mode})。两套壳注册同名工具、不能同时启用 —— 已自动关掉:{rows}。新建会话即生效,当前会话保持原配置。",
123
- "api.created": "已创建「{name}」。现在可以为它写系统提示词。新建会话的模式菜单要刷新一次才会出现它。",
128
+ "meta.versionHint": "未钉版本时,可能装到较旧的发布。要换版本,按 README 钉版本重装,然后重启。",
129
+ "api.saved": "已保存({name},{mode})。新会话使用新配置,当前会话不变。",
130
+ "api.savedWithExclusiveRows": "已保存({name},{mode})。已关闭不能同时启用的行:{rows}。",
131
+ "api.created": "已创建「{name}」。刷新后,新会话的模式菜单里会出现它。",
124
132
  "api.savedPrompt": "已保存系统提示词({name})。下一步模型调用即生效。",
125
- "api.duplicated": "已复制自「{from}」。两份配置彼此独立。",
126
- "api.deleted": "已删除「{name}」。正在使用它的会话不受影响;新建会话时它不再出现。",
127
- "api.reordered": "顺序已保存:新建会话时的模式选择器按这个顺序排列。",
133
+ "api.duplicated": "已从「{from}」复制。两份互不影响。",
134
+ "api.deleted": "已删除「{name}」。正在使用它的会话不受影响。",
135
+ "api.reordered": "顺序已保存。新会话的模式菜单按此排列。",
128
136
  "api.nameRequired": "请先给新助手起个名字。",
137
+ "api.postureUnknown": "没有这个起步:{posture}。",
138
+ "api.posturePromptMissing": "读不到「{posture}」的起步提示词。",
129
139
  "api.nameTooLong": "名字太长了(上限 {max} 个字符)。",
130
140
  "api.descriptionTooLong": "描述太长了(上限 {max} 个字符)。",
131
- "api.promptEmpty": "保存被拒绝:系统提示词为空。留空不会清空身份,读取器会沿用上一版。",
141
+ "api.promptEmpty": "不能保存空的系统提示词。",
132
142
  "api.badMode": "未知的基础模式:{mode}",
133
143
  "api.dirExists": "目录已存在,请换一个名字:{path}",
134
144
  "api.compositionMissing": "找不到组成文件:{path}",
@@ -138,68 +148,68 @@ try {
138
148
  "api.selfCheckFailed": "生成结果自检失败,已放弃写入:{detail}",
139
149
  "api.internalError": "宿主内部错误:{detail}",
140
150
  "api.bodyTooLarge": "请求体过大(上限 {max} 字节)",
141
- "api.repairNotNeeded": "这个助手在本机没有起不来的行,无需修复。",
142
- "api.repaired": "已按本机这条 dsh 线关闭 {count} 个起不来的行({ids})。现在这个模式能重新出现在选择器里。",
151
+ "api.repairNotNeeded": "没有需要修复的行。",
152
+ "api.repaired": "已关闭 {count} 个本机无法启动的行({ids})。",
143
153
  "api.badAssistantId": "助手标识不合法:{id}(只能用 a-z、0-9 与连字符)",
144
154
  "api.seedFailed": "复制模式模板失败:{detail}",
145
155
  "api.metaWriteFailed": "写入 preset.yml 失败:{detail}",
146
156
  "api.promptReadFailed": "读取提示词失败:{detail}",
147
- "api.noRemoveApi": "当前 DSH 版本没有 agentPresets.remove(),无法删除。",
148
- "api.noDirectory": "删除失败:本机找不到这个助手的目录,页面上的列表可能是旧的。刷新一次再试。",
157
+ "api.noRemoveApi": "当前版本无法删除助手。",
158
+ "api.noDirectory": "删除失败:磁盘上已没有这个目录。刷新后再试。",
149
159
  "api.deleteFailed": "删除失败:{detail}",
150
160
  "api.versionMissing": "找不到这个版本(历史可能已被上限裁剪)。",
151
161
  "api.badJson": "请求体不是合法 JSON",
152
- "api.savedPromptOnly": "已保存系统提示词(本机取不到基础模式的出厂组成,插件开关与基础模式未改动)。",
153
- "api.baseCompositionUnavailable": "本机取不到基础模式「{mode}」的出厂组成,无法改动插件开关或基础模式(系统提示词可以单独保存)。",
154
- "warn.approvalGateMissing": "审批闸门未启用:本机这个 DSH 版本没有 tools/pre-execute 事件,会话内改写系统提示词不会弹审批。见「详情」。",
155
- "warn.unresolvableRows": "有行在本机这条 DSH 线上跑不起来。平台会因此判整个模式为 broken,把它从新会话的选择器里丢掉(不报错)。点下方「按本线修复」只关掉那几行,其它选择不动。",
156
- "warn.presetBroken": "平台判这个模式为 broken,它会从新会话的选择器里消失 —— 而上面的「无法解析」检查解释不了原因(多半是某一行依赖的服务没起来)。点下方「按本线修复」把那几行关掉;平台自己给的原因见下面那行。",
157
- "warn.badPromptVariable": "系统提示词里有未注册或写错的变量引用,这个模式的每次请求都会失败。改掉那一处后再保存。",
158
- "warn.duplicateRowIds": "组成文件里有重复的行 id。开关只作用于第一处,请删掉重复的那一行。",
159
- "warn.predicateFailed": "有一行的平台条件求值失败,这一行已按关闭处理,避免整个模式无法挂载。",
160
- "warn.exclusiveRowsActive": "两条互斥的壳同时开着(它们注册同名工具),平台会判整个模式为 broken 并从新会话的选择器里丢掉。关掉其中一条即可;在设置页保存一次也会自动避让。",
161
- "warn.baseCompositionUnavailable": "取不到本机这条 DSH 线的出厂组成:基础模式与插件开关暂时不能改(系统提示词不受影响,照常可保存)。已尝试的途径写在宿主日志里。",
162
- "warn.personaOffWithPrompt": "「身份(系统提示词)」这一行是关的,所以 prompt.md 不会被注入 —— 所写的提示词不会生效。打开这一行,或清空提示词。",
163
- "warn.toolOff": "「custom_prompt 工具」这一行是关的:会话里无法让 agent 改提示词,只能在本页改。",
164
- "warn.noDescription": "没有描述:新建会话的模式选择器里会显示成「暂无描述」。",
165
- "warn.noName": "没有名字:模式选择器里会显示成目录 id(例如 custom)。",
162
+ "api.savedPromptOnly": "已保存系统提示词。基础模式和插件开关未改。",
163
+ "api.baseCompositionUnavailable": "读不到基础模式「{mode}」的出厂组成。开关未改。系统提示词可以单独保存。",
164
+ "warn.approvalGateMissing": "当前版本没有审批事件。会话内改写提示词不会请求确认。",
165
+ "warn.unresolvableRows": "有的行在本机无法启动,此模式不会出现在新会话菜单里。点「按本线修复」只关闭那些行。",
166
+ "warn.presetBroken": "此模式无法启动,不会出现在新会话菜单里。点「按本线修复」。原因见下一行。",
167
+ "warn.badPromptVariable": "提示词里有不可用的变量。改掉后再保存,否则此模式的请求会失败。",
168
+ "warn.duplicateRowIds": "组成文件里有重复的行 id。开关只作用于第一处。",
169
+ "warn.predicateFailed": "有一行的平台条件无法计算,已按关闭处理。",
170
+ "warn.exclusiveRowsActive": "两套终端不能同时打开,否则此模式无法启动。保存时会关掉其中一套。",
171
+ "warn.baseCompositionUnavailable": "读不到出厂组成。基础模式和插件开关暂不能改。系统提示词仍可保存。",
172
+ "warn.personaOffWithPrompt": "「身份」已关闭,这段提示词不会生效。",
173
+ "warn.toolOff": "custom_prompt 已关闭。会话里不能改提示词,只能在此页改。",
174
+ "warn.noDescription": "没有描述时,模式菜单显示「暂无描述」。",
175
+ "warn.noName": "没有名字时,模式菜单显示目录 id。",
166
176
  "history.label": "改动历史",
167
- "history.pick": "选择要载入的版本…",
168
- "history.load": "载入这一版",
169
- "history.hint": "载入只改草稿,保存前不会写入。",
170
- "history.by.settings": "设置页保存",
171
- "history.by.external": "会话内/手工改动",
177
+ "history.pick": "选择版本…",
178
+ "history.load": "载入",
179
+ "history.hint": "载入只改草稿。保存后才写入。",
180
+ "history.by.settings": "此页保存",
181
+ "history.by.external": "外部改动",
172
182
  "msg.loadFailed": "载入这一版失败",
173
183
  "msg.importEmpty": "这个文件是空的。",
174
184
  "msg.importTooLarge": "文件太大(上限 1MB)。",
175
185
  "msg.exported": "已导出为文件。",
176
186
  "msg.exportFailed": "导出失败",
177
- "delete.title": "永久删除「{name}」?",
178
- "delete.description": "这会删除磁盘上的模式目录,连同它的系统提示词一起消失,无法撤销。正在使用它的会话不受影响;新建会话时它不再出现在选择器里。({id})",
179
- "delete.acknowledge": "我明白这个助手的提示词会被永久删除",
180
- "delete.confirm": "永久删除",
187
+ "delete.title": "删除「{name}」?",
188
+ "delete.description": "将删除此助手的目录和系统提示词,不能撤销。正在使用它的会话不受影响。",
189
+ "delete.acknowledge": "我知道提示词会被删除",
190
+ "delete.confirm": "删除",
181
191
  "delete.close": "关闭",
182
- "delete.plainConfirm": "确定永久删除「{name}」吗?该操作无法撤销。",
183
- "name.heading": "模式名称",
184
- "name.hint": "显示在模式菜单里。内部标识不会变。",
192
+ "delete.plainConfirm": "删除「{name}」?不能撤销。",
193
+ "name.heading": "名称",
194
+ "name.hint": "显示在模式菜单里。内部标识不变。",
185
195
  "name.placeholder": "自定义模式",
186
- "name.descriptionPlaceholder": "模式描述(显示在模式选择器里,可留空)",
196
+ "name.descriptionPlaceholder": "描述,显示在模式菜单里,可留空",
187
197
  "mode.heading": "基础模式",
188
- "mode.hint": "选一套官方工具当底子。提示词仍以本页为准。",
198
+ "mode.hint": "选一套官方预设作为工具底。提示词以本页为准。",
189
199
  "rows.heading": "插件开关",
190
- "rows.filterPlaceholder": "筛选行(名称或 id)",
191
- "cap.heading": "工具",
192
- "cap.expand": "显示工具",
193
- "cap.collapse": "隐藏工具",
194
- "cap.hint": "不改的话,助手继续用现在这套工具。",
195
- "rows.hint": "关掉某一行,这个助手就不会用它。",
200
+ "rows.filterPlaceholder": "按名称或 id 筛选",
201
+ "cap.heading": "插件开关",
202
+ "cap.expand": "显示插件开关",
203
+ "cap.collapse": "隐藏插件开关",
204
+ "cap.hint": "关闭的行,这个助手不会使用。",
205
+ "rows.hint": "关闭的行,这个助手不会使用。",
196
206
  "prompt.heading": "系统提示词",
197
- "prompt.hint": "保存后,下一次模型调用就会用这段文字。",
207
+ "prompt.hint": "保存后,下一步模型调用即生效。",
198
208
  "status.enabled": "已启用",
199
209
  "status.disabled": "已停用",
200
210
  "status.changed": "已改",
201
- "tag.essential": "基础能力",
202
- "tag.followPlatform": "跟随平台",
211
+ "tag.essential": "核心",
212
+ "tag.followPlatform": "随平台",
203
213
  "btn.save": "保存",
204
214
  "btn.saving": "处理中…",
205
215
  "btn.reload": "重新读取",
@@ -208,10 +218,10 @@ try {
208
218
  "msg.unsaved": "有未保存的修改",
209
219
  "msg.readFailed": "读取失败",
210
220
  "msg.saveFailed": "保存失败",
211
- "msg.saved": "已保存。新建会话即生效,当前会话保持原配置。",
221
+ "msg.saved": "已保存。新会话使用新配置,当前会话不变。",
212
222
  "status.detail": "{message}:{detail}",
213
223
  "row.persona.label": "身份(系统提示词)",
214
- "row.persona.note": "提示词注入点;关掉后本模式用回部署默认身份",
224
+ "row.persona.note": "关闭后,此提示词不会生效。",
215
225
  "row.custom-prompt-tool.label": "custom_prompt 工具",
216
226
  "row.custom-prompt-tool.note": "关掉后无法用对话改提示词(设置页仍可用)",
217
227
  "row.agent-instructions.label": "项目指令 AGENTS.md",
@@ -223,7 +233,7 @@ try {
223
233
  "row.tool-fs-search.label": "文件搜索(glob/grep)",
224
234
  "row.tool-jobs.label": "后台任务",
225
235
  "row.planning.label": "计划模式(分组)",
226
- "row.planning.note": "含 isolate realm,关掉等于移除整个计划能力",
236
+ "row.planning.note": "关闭后,计划能力一并去掉。",
227
237
  "row.plan-mode.label": "计划模式实现",
228
238
  "row.compaction.label": "上下文压缩(分组)",
229
239
  "row.compaction.note": "含 isolate realm",
@@ -231,7 +241,7 @@ try {
231
241
  "row.command-compact.label": "/compact 命令",
232
242
  "row.tool-result-pruner.label": "工具结果裁剪",
233
243
  "row.delegation.label": "委派与工作流(分组)",
234
- "row.delegation.note": "含 isolate realm;关掉等于移除子代理与工作流",
244
+ "row.delegation.note": "关闭后,子代理和工作流一并去掉。",
235
245
  "row.tool-subagent.label": "子代理(spawn)",
236
246
  "row.tool-subagent-fork.label": "子代理(fork)",
237
247
  "row.tool-subagent-control.label": "子代理控制",
@@ -241,7 +251,7 @@ try {
241
251
  "row.tool-subagent-claude-code.label": "Claude Code 子代理",
242
252
  "row.tool-subagent-claude-code.note": "需要先安装对应 Bundle 才能用;出厂状态见「详情」",
243
253
  "row.time-context.label": "时间上下文(模型看得到时间)",
244
- "row.time-context.note": "在合适的步骤里附上当前时间、浏览器时区与距上一条消息的间隔;默认 10 分钟内不重复注入",
254
+ "row.time-context.note": "在部分步骤附上当前时间。",
245
255
  "row.tool-schedule.label": "定时提醒工具",
246
256
  "row.tool-schedule.note": "让 agent 创建/列出/修改/删除持久提醒;存储与投递由宿主的 schedule 服务负责",
247
257
  "row.workflow-ptc.label": "工作流引擎",
@@ -256,7 +266,7 @@ try {
256
266
  "row.tool-cordis.label": "Cordis 运行时工具",
257
267
  "row.tool-cordis.note": "可读写 harness 运行时",
258
268
  "row.tool-plugin-manager.label": "插件管理(安装 / 启停)",
259
- "row.tool-plugin-manager.note": "模型侧可安装/启停插件;标准与 PTC 模式里出厂即关闭(只有创造模式默认开),本模式可以显式打开它",
269
+ "row.tool-plugin-manager.note": "供模型安装或开关插件。",
260
270
  "row.tool-presentation.label": "PTC 工具呈现",
261
271
  "row.present.label": "交付文件(present)",
262
272
  "row.command-goal.label": "目标命令",
@@ -270,66 +280,68 @@ try {
270
280
  "row.terminal-pwsh.label": "终端(pwsh)",
271
281
  "row.persistent-pwsh.label": "持久 pwsh",
272
282
  "base.standard.label": "标准模式",
273
- "base.standard.note": "完整编码能力:Shell、文件、检索、技能、计划、目标、子代理、工作流",
283
+ "base.standard.note": "出厂的完整工具集。",
274
284
  "base.ptc.label": "PTC 模式",
275
- "base.ptc.note": "在标准模式基础上启用 PTC 工具呈现(tool-presentation)",
285
+ "base.ptc.note": "标准模式,另开 PTC 工具呈现。",
276
286
  "base.minimal.label": "极简模式",
277
- "base.minimal.note": "只有 Shell 与终端,共 7 行;没有文件、检索、技能、子代理。系统提示词不是极简那一套(persona 行被本模式替换)",
278
- // ★ 与官方对齐:cordis 这个模式在官方界面里叫「创造模式」(英文 Creator mode),
279
- // 不是「Cordis 模式」—— 实测 `dsh-client-ui-agent-preset` 的词典:
280
- // `presetCordisName: "创造模式"` / `"Creator mode"`(0.1.7-rc.2 与 0.2.0-rc.2 同名)。
281
- // 「Cordis」在官方用语里是**技术名**(Cordis 插件 / Cordis 运行时),不是模式名。
282
- // 此前这里写「Cordis 模式」、而同一份词典的 `base.all.note` 又写「创造模式」,
283
- // 同一个模式在一页里有两个名字:用户点了「Cordis 模式」药丸,读到的说明却说「创造模式」。
287
+ "base.minimal.note": "只有 Shell 与终端。提示词仍以本页为准。",
284
288
  "base.cordis.label": "创造模式",
285
- "base.cordis.note": "标准模式 + 读写运行时的 Cordis 工具集,可让 agent 自己改 harness",
289
+ "base.cordis.note": "标准模式,另开 Cordis 运行时工具。",
286
290
  "base.all.label": "全部出厂行",
287
- "base.all.note": "不继承任何单一原生模式:以标准模式的全套行为底,补上只有 PTC / 极简 / 创造模式才有的行,全部都能逐行开关"
291
+ "base.all.note": "四套官方预设的行合在一处,均可开关。"
288
292
  }
289
293
 
290
294
  const EN = {
291
295
  "nav": "Custom mode",
292
296
  "assistant.heading": "Assistants",
293
- "assistant.hint": "Each assistant is its own mode, with its own prompt, base mode and plugin switches. Pick one from the mode menu when you start a session.",
297
+ "assistant.hint": "Each assistant keeps its own system prompt, base mode and plugin switches.",
298
+ "posture.hint": "Applies only to the assistant you are about to create.",
299
+ "posture.develop.label": "Coding",
300
+ "posture.develop.note": "After create: Standard, tools as shipped.",
301
+ "posture.write.label": "Writing",
302
+ "posture.write.note": "After create: terminal off, file access on.",
303
+ "posture.chat.label": "Chat",
304
+ "posture.chat.note": "After create: files and terminal off.",
294
305
  "assistant.empty": "No assistants yet — create one with the field below.",
295
306
  "assistant.loadingList": "Loading assistants…",
296
307
  "assistant.newPlaceholder": "Name of the new assistant (e.g. Writing assistant)",
297
308
  "assistant.copyName": "{name} (copy)",
298
309
  "assistant.broken": "Will not start here",
299
310
  "assistant.brokenRows": "{count} row(s) unusable here",
300
- "assistant.brokenHint": "The platform cannot mount this mode, so it will not appear in the new-session picker. Click Fix for this line below to turn off just those rows.",
311
+ "assistant.brokenHint": "This mode cannot start here, so it will not appear for a new session. Fix for this line turns off only those rows.",
301
312
  "assistant.switchHint": "Switching assistants keeps each draft; the \"Unsaved\" marker in the list is exactly that.",
302
313
  "btn.create": "New assistant",
314
+ "create.heading": "New assistant",
303
315
  "btn.creating": "Creating…",
304
316
  "btn.duplicate": "Duplicate",
305
317
  "btn.moveUp": "Move up",
306
318
  "btn.moveDown": "Move down",
307
- "btn.import": "Import prompt",
308
- "btn.export": "Export prompt",
309
- "btn.reset": "Reset to factory prompt",
310
- "btn.resetHint": "Puts the shipped template back into the editor (the text a new assistant starts from). Like every other edit it only changes the draft — save to apply, or use Reload to discard.",
311
- "btn.delete": "Delete this assistant",
319
+ "btn.import": "Import",
320
+ "btn.export": "Export",
321
+ "btn.reset": "Factory prompt",
322
+ "btn.resetHint": "Restores the prompt saved when this assistant was created. The editor changes. Save writes it.",
323
+ "btn.delete": "Delete",
312
324
  "btn.cancel": "Cancel",
313
- "msg.created": "Created. You can write its system prompt now. Refresh once before the new-session mode menu will list it.",
325
+ "msg.created": "Created. Refresh once, then it appears in the mode menu for a new session.",
314
326
  "msg.duplicated": "Duplicated. The two are independent from here on.",
315
327
  "msg.deleted": "Deleted.",
316
328
  "msg.createFailed": "Could not create",
317
329
  "msg.deleteFailed": "Could not delete",
318
330
  "msg.nameRequired": "Give the new assistant a name first.",
319
331
  "msg.readOnlyHint": "Manages only the assistants this plugin created; a hand-written preset is not listed and is never rewritten.",
320
- "msg.reordered": "Order saved: the mode picker for new sessions follows it.",
332
+ "msg.reordered": "Order saved. New sessions list modes in this order.",
321
333
  "msg.reorderFailed": "Could not reorder",
322
- "msg.imported": "Imported into the editor (not saved yet) — review it, then click Save.",
334
+ "msg.imported": "Imported into the editor. Not saved yet.",
323
335
  "msg.importFailed": "Import failed",
324
336
  "api.badDirection": "Unknown reorder direction: {direction}",
325
- "api.unknownAssistant": "No assistant {id}: this page only manages the assistants it created (a directory with prompt.md whose composition injects the identity through prompt-reader.mjs).",
337
+ "api.unknownAssistant": "No assistant \"{id}\". This page only manages assistants it created.",
326
338
  "api.alreadyFirst": "{name} is already first.",
327
339
  "api.alreadyLast": "{name} is already last.",
328
- "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.",
329
- "api.unknownVariable": "{variable} is not a registered variable — rendering would fail every request in this mode. Available: {known}.",
330
- "mode.unavailable": "This DSH line exposes no readable shipped composition, so the base mode cannot be switched right now.",
340
+ "api.badVariableName": "{variable} is not a valid variable. Use a lower-case name.",
341
+ "api.unknownVariable": "{variable} cannot be used. Available: {known}.",
342
+ "mode.unavailable": "No shipped composition is available, so the base mode cannot be changed.",
331
343
  "rows.unavailable": "No shipped composition is available, so the plugin switches cannot be edited right now; the row states already on disk still apply.",
332
- "mode.pendingRows": "Base mode changed to {mode}: the row list below is recomputed from it when you save.",
344
+ "mode.pendingRows": "Base mode is now {mode}. On save, the tool base becomes this shipped preset.",
333
345
  "aria.expand": "Show details",
334
346
  "aria.collapse": "Hide details",
335
347
  "detail.id": "Row id",
@@ -346,18 +358,20 @@ try {
346
358
  "btn.repair": "Fix for this line",
347
359
  "msg.repaired": "Repaired",
348
360
  "meta.version": "Plugin version",
349
- "meta.versionHint": "Installing without a pinned version is subject to pnpm’s release cooldown (24 h by default) and can land on an older release. To change version, reinstall with the pinned command from the README and restart DSH.",
350
- "api.saved": "Saved ({name}, base mode {mode}). A new session picks it up; the current one keeps its configuration.",
351
- "api.savedWithExclusiveRows": "Saved ({name}, base mode {mode}). The two shell implementations register the same tool names and cannot both be on — turned off automatically: {rows}. A new session picks it up; the current one keeps its configuration.",
352
- "api.created": "Created {name}. You can write its system prompt now. Refresh once before the new-session mode menu will list it.",
361
+ "meta.versionHint": "Without a pinned version, an older release may be installed. To change it, reinstall with the version pin from the README, then restart.",
362
+ "api.saved": "Saved ({name}, {mode}). New sessions use it. The current session does not change.",
363
+ "api.savedWithExclusiveRows": "Saved ({name}, {mode}). Turned off rows that cannot be on together: {rows}.",
364
+ "api.created": "Created {name}. Refresh once, then it appears in the mode menu for a new session.",
353
365
  "api.savedPrompt": "System prompt saved ({name}). It applies on the next model step.",
354
366
  "api.duplicated": "Copied from {from}. The two are independent from now on.",
355
- "api.deleted": "Deleted {name}. Sessions already using it keep running; it no longer appears for new sessions.",
356
- "api.reordered": "Order saved: the new-session mode picker follows it.",
367
+ "api.deleted": "Deleted {name}. Sessions already using it are unchanged.",
368
+ "api.reordered": "Order saved. New sessions list modes in this order.",
357
369
  "api.nameRequired": "Give the new assistant a name first.",
370
+ "api.postureUnknown": "No such starter: {posture}.",
371
+ "api.posturePromptMissing": "Could not read the starter prompt for {posture}.",
358
372
  "api.nameTooLong": "That name is too long (limit {max} characters).",
359
373
  "api.descriptionTooLong": "That description is too long (limit {max} characters).",
360
- "api.promptEmpty": "Save rejected: the system prompt is empty. Emptying it would not clear the identity — the reader keeps the last good text.",
374
+ "api.promptEmpty": "An empty system prompt cannot be saved.",
361
375
  "api.badMode": "Unknown base mode: {mode}",
362
376
  "api.dirExists": "That directory already exists — pick another name: {path}",
363
377
  "api.compositionMissing": "Composition file not found: {path}",
@@ -367,68 +381,68 @@ try {
367
381
  "api.selfCheckFailed": "The generated composition failed its self-check, so nothing was written: {detail}",
368
382
  "api.internalError": "Unexpected host error: {detail}",
369
383
  "api.bodyTooLarge": "Request body too large (limit {max} bytes)",
370
- "api.repairNotNeeded": "Nothing to repair — every row of this assistant can start on this machine.",
371
- "api.repaired": "Disabled {count} row(s) this dsh line cannot start ({ids}). The mode can appear in the pickers again.",
384
+ "api.repairNotNeeded": "Nothing to repair.",
385
+ "api.repaired": "Turned off {count} row(s) that cannot start here ({ids}).",
372
386
  "api.badAssistantId": "Invalid assistant id: {id} (a-z, 0-9 and hyphens only)",
373
387
  "api.seedFailed": "Copying the mode template failed: {detail}",
374
388
  "api.metaWriteFailed": "Writing preset.yml failed: {detail}",
375
389
  "api.promptReadFailed": "Reading the prompt failed: {detail}",
376
- "api.noRemoveApi": "This DSH build has no agentPresets.remove(), so deleting is unavailable.",
390
+ "api.noRemoveApi": "This version cannot delete an assistant.",
377
391
  "api.noDirectory": "Delete failed: this assistant's directory is gone from disk, so the list on screen is stale. Refresh and try again.",
378
392
  "api.deleteFailed": "Delete failed: {detail}",
379
393
  "api.versionMissing": "That version is gone (the history is capped).",
380
394
  "api.badJson": "The request body is not valid JSON",
381
- "api.savedPromptOnly": "System prompt saved (this machine exposes no shipped composition, so the plugin switches and the base mode were left untouched).",
395
+ "api.savedPromptOnly": "System prompt saved. Base mode and plugin switches were not changed.",
382
396
  "api.baseCompositionUnavailable": "No shipped composition for base mode \"{mode}\" on this machine, so its plugin switches and base mode cannot be changed (the system prompt can still be saved on its own).",
383
- "warn.personaOffWithPrompt": "The \"Identity (system prompt)\" row is off, so prompt.md is never injected — the prompt being edited has no effect. Turn the row on, or clear the prompt.",
384
- "warn.toolOff": "The \"custom_prompt tool\" row is off: the agent cannot change the prompt from inside a session, only this page can.",
385
- "warn.noDescription": "No description: the new-session mode picker will show it as \"no description yet\".",
386
- "warn.approvalGateMissing": "The approval gate is off: this DSH build has no tools/pre-execute event, so in-session prompt rewrites do not ask for approval. See the details.",
387
- "warn.unresolvableRows": "Some rows cannot be resolved on this DSH line: the platform marks the whole mode broken and silently drops it from the new-session picker. Click Fix for this line — it only turns those rows off and leaves your other choices alone.",
388
- "warn.presetBroken": "The platform marks this mode broken, so it disappears from the new-session picker — and the \"unresolvable\" check above cannot explain why (usually a row whose service or engine never started). Click Fix for this line to turn those rows off. The host's own reason is on the line below.",
389
- "warn.badPromptVariable": "The system prompt contains an unknown or invalid variable reference, so every request in this mode fails. Fix it, then save.",
390
- "warn.duplicateRowIds": "The composition repeats a row id. A switch applies only to the first copy. Remove the duplicate.",
391
- "warn.predicateFailed": "A platform condition failed to evaluate. That row is treated as off so the mode can still mount.",
392
- "warn.exclusiveRowsActive": "Both shells are on, and they register the same tool names: the platform marks the whole mode broken and silently drops it from the new-session picker. Turn one of them off — saving from the settings page also resolves it automatically.",
393
- "warn.noName": "No name: the mode picker will show the directory id (e.g. custom).",
394
- "warn.baseCompositionUnavailable": "This DSH line exposes no shipped composition: the base mode and the plugin switches cannot be changed right now (the system prompt is unaffected and still saves). The routes that were tried are in the host log.",
397
+ "warn.personaOffWithPrompt": "Identity is off, so this prompt has no effect.",
398
+ "warn.toolOff": "custom_prompt is off. The prompt can be edited on this page only.",
399
+ "warn.noDescription": "Without a description, the mode menu shows \"no description yet\".",
400
+ "warn.approvalGateMissing": "This version has no approval event. In-session prompt edits are not confirmed.",
401
+ "warn.unresolvableRows": "Some rows cannot start here, so this mode is absent from the new-session menu. Fix for this line turns off only those rows.",
402
+ "warn.presetBroken": "This mode cannot start, so it will not appear for a new session. Use Fix for this line. The reason is on the next line.",
403
+ "warn.badPromptVariable": "The prompt uses a variable that is not available. Fix it before saving.",
404
+ "warn.duplicateRowIds": "A row id is repeated. A switch applies only to the first one.",
405
+ "warn.predicateFailed": "A platform condition could not be evaluated. That row is treated as off.",
406
+ "warn.exclusiveRowsActive": "The two terminals cannot both be on, or this mode will not start. Saving turns one of them off.",
407
+ "warn.noName": "Without a name, the mode menu shows the directory id.",
408
+ "warn.baseCompositionUnavailable": "No shipped composition is available. Base mode and plugin switches cannot be changed. The system prompt can still be saved.",
395
409
  "history.label": "Change history",
396
- "history.pick": "Pick a version to load…",
397
- "history.load": "Load this version",
398
- "history.hint": "Loading a version only edits the draft. Nothing is written until you save.",
399
- "history.by.settings": "saved from this page",
400
- "history.by.external": "changed in a session / by hand",
410
+ "history.pick": "Choose a version…",
411
+ "history.load": "Load",
412
+ "history.hint": "Loading edits the draft only. Save writes it.",
413
+ "history.by.settings": "saved here",
414
+ "history.by.external": "external edit",
401
415
  "msg.loadFailed": "Could not load that version",
402
416
  "msg.importEmpty": "That file is empty.",
403
417
  "msg.importTooLarge": "That file is too large (1 MB limit).",
404
418
  "msg.exported": "Exported to a file.",
405
419
  "msg.exportFailed": "Export failed",
406
- "delete.title": "Delete \"{name}\" permanently?",
407
- "delete.description": "This removes the mode directory from disk, system prompt included, and cannot be undone. Sessions already using it keep running; new sessions no longer offer it. ({id})",
420
+ "delete.title": "Delete \"{name}\"?",
421
+ "delete.description": "This deletes the assistant directory and its system prompt. It cannot be undone. Sessions already using it are unchanged.",
408
422
  "delete.acknowledge": "I understand this assistant's prompt will be deleted permanently",
409
- "delete.confirm": "Delete permanently",
423
+ "delete.confirm": "Delete",
410
424
  "delete.close": "Close",
411
- "delete.plainConfirm": "Permanently delete \"{name}\"? This cannot be undone.",
412
- "name.heading": "Mode name",
413
- "name.hint": "Shown in the mode menu. The internal id does not change.",
425
+ "delete.plainConfirm": "Delete \"{name}\"? This cannot be undone.",
426
+ "name.heading": "Name",
427
+ "name.hint": "Shown in the mode menu. The internal id stays the same.",
414
428
  "name.placeholder": "Custom mode",
415
- "name.descriptionPlaceholder": "Mode description (shown in the mode picker, optional)",
429
+ "name.descriptionPlaceholder": "Description, shown in the mode menu. Optional.",
416
430
  "mode.heading": "Base mode",
417
- "mode.hint": "Pick a shipped tool set as the starting point. The prompt on this page still wins.",
431
+ "mode.hint": "Pick a shipped preset as the tool base. The prompt on this page still applies.",
418
432
  "rows.heading": "Plugin switches",
419
- "rows.filterPlaceholder": "Filter rows by name or id",
420
- "cap.heading": "Tools",
421
- "cap.expand": "Show tools",
422
- "cap.collapse": "Hide tools",
423
- "cap.hint": "Leave this closed to keep the tools this assistant already has.",
424
- "rows.hint": "Turn a row off and this assistant will not use it.",
433
+ "rows.filterPlaceholder": "Filter by name or id",
434
+ "cap.heading": "Plugin switches",
435
+ "cap.expand": "Show plugin switches",
436
+ "cap.collapse": "Hide plugin switches",
437
+ "cap.hint": "A row that is off is not used by this assistant.",
438
+ "rows.hint": "A row that is off is not used by this assistant.",
425
439
  "prompt.heading": "System prompt",
426
440
  "prompt.hint": "After you save, the next model step uses this text.",
427
441
  "status.enabled": "Enabled",
428
442
  "status.disabled": "Disabled",
429
443
  "status.changed": "changed",
430
444
  "tag.essential": "Core",
431
- "tag.followPlatform": "follows platform",
445
+ "tag.followPlatform": "Platform",
432
446
  "btn.save": "Save",
433
447
  "btn.saving": "Working…",
434
448
  "btn.reload": "Reload",
@@ -437,10 +451,10 @@ try {
437
451
  "msg.unsaved": "Unsaved changes",
438
452
  "msg.readFailed": "Load failed",
439
453
  "msg.saveFailed": "Save failed",
440
- "msg.saved": "Saved. A new session picks it up; the current one keeps its configuration.",
454
+ "msg.saved": "Saved. New sessions use it. The current session does not change.",
441
455
  "status.detail": "{message}: {detail}",
442
456
  "row.persona.label": "Identity (system prompt)",
443
- "row.persona.note": "Where the prompt is injected; turning it off falls back to the deployment identity",
457
+ "row.persona.note": "Off: this prompt has no effect.",
444
458
  "row.custom-prompt-tool.label": "custom_prompt tool",
445
459
  "row.custom-prompt-tool.note": "Without it the prompt can still be edited here, but not by asking the agent",
446
460
  "row.agent-instructions.label": "Project instructions (AGENTS.md)",
@@ -452,7 +466,7 @@ try {
452
466
  "row.tool-fs-search.label": "File search (glob/grep)",
453
467
  "row.tool-jobs.label": "Background jobs",
454
468
  "row.planning.label": "Plan mode (group)",
455
- "row.planning.note": "Carries an isolate realm; turning it off removes planning entirely",
469
+ "row.planning.note": "Off: planning is removed with it.",
456
470
  "row.plan-mode.label": "Plan mode implementation",
457
471
  "row.compaction.label": "Context compaction (group)",
458
472
  "row.compaction.note": "Carries an isolate realm",
@@ -460,7 +474,7 @@ try {
460
474
  "row.command-compact.label": "/compact command",
461
475
  "row.tool-result-pruner.label": "Tool-result pruning",
462
476
  "row.delegation.label": "Delegation and workflows (group)",
463
- "row.delegation.note": "Carries an isolate realm; turning it off removes subagents and workflows",
477
+ "row.delegation.note": "Off: subagents and workflows are removed with it.",
464
478
  "row.tool-subagent.label": "Subagent (spawn)",
465
479
  "row.tool-subagent-fork.label": "Subagent (fork)",
466
480
  "row.tool-subagent-control.label": "Subagent control",
@@ -470,7 +484,7 @@ try {
470
484
  "row.tool-subagent-claude-code.label": "Claude Code subagent",
471
485
  "row.tool-subagent-claude-code.note": "Needs its Bundle installed first; the shipped state is in the details",
472
486
  "row.time-context.label": "Time context (the model can see the clock)",
473
- "row.time-context.note": "On eligible steps, appends the current time, the browser zone and the gap since the previous model-visible message; 10-minute minimum interval by default",
487
+ "row.time-context.note": "Adds the current time on some steps.",
474
488
  "row.tool-schedule.label": "Scheduled reminders tool",
475
489
  "row.tool-schedule.note": "Lets the agent create, list, edit and delete durable reminders; the host schedule service owns storage and delivery",
476
490
  "row.workflow-ptc.label": "Workflow engine",
@@ -485,7 +499,7 @@ try {
485
499
  "row.tool-cordis.label": "Cordis runtime tools",
486
500
  "row.tool-cordis.note": "Can read and modify the running harness",
487
501
  "row.tool-plugin-manager.label": "Plugin manager (install / toggle)",
488
- "row.tool-plugin-manager.note": "Lets the agent install and toggle plugins; shipped OFF in Standard and PTC (only Creator enables it) — this mode can switch it on",
502
+ "row.tool-plugin-manager.note": "Lets the model install or toggle plugins.",
489
503
  "row.tool-presentation.label": "PTC tool presentation",
490
504
  "row.present.label": "Deliverables (present)",
491
505
  "row.command-goal.label": "Goal command",
@@ -498,20 +512,16 @@ try {
498
512
  "row.persistent-bash.label": "Persistent bash",
499
513
  "row.terminal-pwsh.label": "Terminal (pwsh)",
500
514
  "row.persistent-pwsh.label": "Persistent pwsh",
501
- // ★ 与官方选择器逐字一致(实测 `dsh-client-ui-agent-preset` 的英文字典,两版 DSH 同名):
502
- // 官方英文名都带 " mode" 后缀 —— "Standard mode" / "PTC mode" / "Minimal mode" /
503
- // "Creator mode"。本仓此前写成 "Standard" / "PTC" / "Minimal" / "Cordis",
504
- // 与用户在官方模式选择器里看到的同一个模式名对不上。
505
515
  "base.standard.label": "Standard mode",
506
- "base.standard.note": "Full coding agent: shell, files, search, skills, planning, goals, subagents, workflows",
516
+ "base.standard.note": "The shipped tool set.",
507
517
  "base.ptc.label": "PTC mode",
508
- "base.ptc.note": "Standard plus PTC tool presentation (tool-presentation)",
518
+ "base.ptc.note": "Standard, plus PTC tool presentation.",
509
519
  "base.minimal.label": "Minimal mode",
510
- "base.minimal.note": "Shell and terminal only, 7 rows; no files, search, skills or subagents. The system prompt is not minimal's either (this mode replaces the persona row)",
520
+ "base.minimal.note": "Shell and terminal only. The prompt on this page still applies.",
511
521
  "base.cordis.label": "Creator mode",
512
- "base.cordis.note": "Standard plus the Cordis toolset, letting the agent modify its own harness",
522
+ "base.cordis.note": "Standard, plus Cordis runtime tools.",
513
523
  "base.all.label": "All shipped rows",
514
- "base.all.note": "Inherits no single shipped mode: standard's full row list plus the rows only PTC / Minimal / Creator declare, every one of them switchable"
524
+ "base.all.note": "Rows from all four shipped presets. Each row can be switched."
515
525
  }
516
526
 
517
527
  const TRANSLATIONS = { zh: ZH, en: EN }
@@ -721,7 +731,9 @@ try {
721
731
  * grids, the status line).
722
732
  */
723
733
  const CSS = [
724
- ".cpfe{--g:8px;display:flex;flex-direction:column;gap:24px;width:100%;max-width:900px;box-sizing:border-box;padding-bottom:16px;font:inherit;color:inherit}",
734
+ ".cpfe{--g:8px;display:flex;flex-direction:column;gap:16px;width:100%;max-width:900px;box-sizing:border-box;padding-bottom:16px;font:inherit;color:inherit}",
735
+ ".cpfe > section{padding-top:16px;border-top:1px solid var(--dsw-alias-border-l1)}",
736
+ ".cpfe > section:first-child{padding-top:0;border-top:0}",
725
737
  // 分节标题:度量抄官方设置面板里的同类文本。
726
738
  // 实测(**0.1.7-rc.2 与 0.2.0-rc.2 各量一次,结论相同**):官方 `_title`(设置行标题)
727
739
  // 与 `groupTitle`(插件页分组标题)都是 14px/22px **w400**;官方唯一的 14px w500 是插件
@@ -735,11 +747,13 @@ try {
735
747
  ".cpfe-hint-line{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}",
736
748
  ".cpfe-hint-detail{margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}",
737
749
  // 描述:多行、自适应高度(没有多行输入组件,所以用 textarea + 同一批语义变量)
738
- ".cpfe-desc{box-sizing:border-box;min-height:56px;max-height:160px;resize:vertical;padding:8px 12px;border-radius:var(--dsw-radius-md);border:.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;line-height:20px;margin-bottom:8px}",
750
+ ".cpfe-desc{box-sizing:border-box;min-height:40px;max-height:120px;resize:vertical;padding:8px 12px;border-radius:var(--dsw-radius-md);border:.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;line-height:20px;margin-bottom:0}",
739
751
  ".cpfe-base-pending{color:var(--dsw-alias-state-warn-primary)}",
740
752
  ".cpfe-note{display:block;margin:8px 0 0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}",
741
753
  ".cpfe-mono{display:block;margin:0;font-family:var(--ds-font-family-code, ui-monospace, SFMono-Regular, Menlo, monospace);font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere}",
742
754
  ".cpfe-pills{display:flex;flex-wrap:wrap;gap:6px;align-items:center}",
755
+ ".cpfe-pills > *{flex:0 0 auto;white-space:nowrap}",
756
+ ".cpfe-pills button{white-space:nowrap}",
743
757
  ".cpfe-picker{display:flex;align-items:center;gap:8px;margin:2px 0 4px}",
744
758
  // 壳的菜单浮层靠一个独立的 backing 元素上色(实测在 0.1.7-rc.2 + headless 下那块是透明的,
745
759
  // 文字会"压"在下面的输入框上)。用官方 token 显式补一层底色,真实浏览器与 headless 一致。
@@ -750,11 +764,13 @@ try {
750
764
  ".cpfe-pill-wrap{display:inline-flex;align-items:center;gap:4px}",
751
765
  ".cpfe-actions{display:flex;flex-wrap:wrap;gap:var(--g);align-items:center;margin-top:4px}",
752
766
  ".cpfe-newrow{display:flex;gap:var(--g);align-items:center;flex-wrap:wrap;margin-top:10px}",
767
+ ".cpfe-postures{display:flex;flex-direction:column;gap:6px}",
768
+ ".cpfe-create{display:flex;flex-direction:column;gap:6px;margin-top:8px}",
753
769
  ".cpfe-field{display:flex;width:100%;margin-bottom:8px}",
754
770
  ".cpfe-row-head{font-size:14px;line-height:22px;color:var(--dsw-alias-label-primary)}",
755
771
  ".cpfe-row-switch{flex:0 0 auto}",
756
772
  ".cpfe-row-line{display:flex;align-items:center;gap:8px;flex-wrap:wrap;min-width:0}",
757
- ".cpfe-row-note{min-height:18px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}",
773
+ ".cpfe-row-note{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}",
758
774
  ".cpfe-row-toggle{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;border:0;border-radius:var(--dsw-radius-sm);background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer}",
759
775
  ".cpfe-row-toggle:hover{background:var(--dsw-alias-bg-layer-2)}",
760
776
  ".cpfe-row-open{border-color:var(--dsw-alias-border-l2)}",
@@ -762,7 +778,8 @@ try {
762
778
  ".cpfe-detail-line{display:flex;gap:8px;min-width:0}",
763
779
  ".cpfe-detail-key{flex:0 0 9.5em;color:var(--dsw-alias-label-secondary)}",
764
780
  ".cpfe-detail-value{min-width:0;overflow-wrap:anywhere}",
765
- ".cpfe-sec-head{display:flex;flex-direction:column;align-items:flex-start;gap:8px}",
781
+ ".cpfe-sec-head{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:8px}",
782
+ ".cpfe-sec-head .cpfe-actions{margin-top:0}",
766
783
  ".cpfe-newinput{flex:1;min-width:200px}",
767
784
  // 单列、行高统一(对齐官方插件页的形态);之前的自适应多列网格会让行高参差不齐。
768
785
  // ★ 对齐官方设置行(实测 0.1.7-rc.2 的行:padding 16px 0、无背景、无圆角,行间只有一条 1px
@@ -772,10 +789,10 @@ try {
772
789
  ".cpfe-line:last-child{border-bottom:0}",
773
790
  ".cpfe-group{display:flex;flex-direction:column;min-width:0}",
774
791
  ".cpfe-kids{display:flex;flex-direction:column;margin-left:0;padding-left:16px}",
792
+ ".cpfe-row-meta{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}",
793
+ ".cpfe-row-badges{display:flex;align-items:center;gap:4px;flex:0 1 auto;min-width:0}",
775
794
  ".cpfe-row{display:flex;gap:12px;align-items:center;box-sizing:border-box;min-height:0;padding:16px 0;border:0;border-radius:0;background:none}",
776
- ".cpfe-row-meta{display:flex;flex-direction:column;gap:4px;min-width:0;flex:1}",
777
- ".cpfe-row-badges{display:flex;align-items:center;gap:6px;flex-wrap:wrap;height:18px}",
778
- ".cpfe-editor{box-sizing:border-box;width:100%;min-height:180px;resize:vertical;padding:12px;border-radius:var(--dsw-radius-md);border:.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary);font-family:var(--ds-font-family-code, ui-monospace, SFMono-Regular, Menlo, monospace);font-size:13px;line-height:20px}",
795
+ ".cpfe-editor{box-sizing:border-box;width:100%;min-height:120px;resize:vertical;padding:12px;border-radius:var(--dsw-radius-md);border:.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary);font-family:var(--ds-font-family-code, ui-monospace, SFMono-Regular, Menlo, monospace);font-size:13px;line-height:20px}",
779
796
  ".cpfe-bar{display:flex;align-items:center;gap:var(--g);flex-wrap:wrap;margin-top:8px}",
780
797
  ".cpfe-disclosure{appearance:none;display:inline-flex;align-items:center;gap:6px;margin:8px 0 0;padding:0;border:0;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:14px;line-height:22px;font-weight:400;cursor:pointer}",
781
798
  ".cpfe-filter{box-sizing:border-box;width:100%;height:36px;margin:0 0 8px;padding:0 12px;border-radius:var(--dsw-radius-md);border:.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px}",
@@ -1010,11 +1027,6 @@ try {
1010
1027
  react.createElement(
1011
1028
  "div",
1012
1029
  { className: "cpfe-row-badges" },
1013
- react.createElement(
1014
- A.Tag,
1015
- { tone: effective ? "success" : "neutral" },
1016
- effective ? t("status.enabled") : t("status.disabled"),
1017
- ),
1018
1030
  row.essential ? react.createElement(A.Tag, { tone: "warning" }, t("tag.essential")) : null,
1019
1031
  row.disabledExpression !== null && row.disabledExpression !== undefined
1020
1032
  ? react.createElement(A.Tag, { tone: "outline" }, t("tag.followPlatform"))
@@ -1022,9 +1034,9 @@ try {
1022
1034
  changed ? react.createElement(A.Tag, { tone: "info" }, t("status.changed")) : null,
1023
1035
  ),
1024
1036
  ),
1025
- // 折叠时只留一行说明(超出截断,悬停给全文)。**没有说明就留空**:裸露的行 id
1026
- // 是开发者信息,已经在「详情」里 —— 之前它作为副标题占了每行一行。
1027
- react.createElement("span", { className: "cpfe-row-note", title: note ?? "" }, note ?? ""),
1037
+ note
1038
+ ? react.createElement("span", { className: "cpfe-row-note", title: note }, note)
1039
+ : null,
1028
1040
  ),
1029
1041
  react.createElement(
1030
1042
  "button",
@@ -1194,6 +1206,8 @@ try {
1194
1206
  const [failed, setFailed] = react.useState(false)
1195
1207
  const [busy, setBusy] = react.useState(false)
1196
1208
  const [newName, setNewName] = react.useState("")
1209
+ const [postureChoices, setPostureChoices] = react.useState([])
1210
+ const [posture, setPosture] = react.useState("develop")
1197
1211
  const [deleteOpen, setDeleteOpen] = react.useState(false)
1198
1212
  const [acknowledged, setAcknowledged] = react.useState(false)
1199
1213
  /**
@@ -1323,8 +1337,11 @@ try {
1323
1337
  return
1324
1338
  }
1325
1339
  const assistants = Array.isArray(result.assistants) ? result.assistants : []
1340
+ const choices = Array.isArray(result.postures) ? result.postures.filter((item) => item !== null && typeof item === "object" && typeof item.id === "string") : []
1326
1341
  setListFailed(false)
1327
1342
  setList(assistants)
1343
+ setPostureChoices(choices)
1344
+ setPosture((current) => (choices.some((item) => item.id === current) ? current : choices.length > 0 ? choices[0].id : "develop"))
1328
1345
  const wanted =
1329
1346
  typeof preferId === "string" && preferId !== ""
1330
1347
  ? preferId
@@ -1455,7 +1472,10 @@ try {
1455
1472
  }
1456
1473
  setBusy(true)
1457
1474
  try {
1458
- const result = await postJson(ROUTES.create, from === undefined ? { name } : { name, from })
1475
+ const body = { name, locale: activeLanguage(localeRef) }
1476
+ if (from !== undefined) body.from = from
1477
+ else if (postureChoices.some((item) => item.id === posture)) body.posture = posture
1478
+ const result = await postJson(ROUTES.create, body)
1459
1479
  if (result !== null && result !== undefined && result.ok === true) {
1460
1480
  setNewName("")
1461
1481
  setFailed(false)
@@ -1693,6 +1713,64 @@ try {
1693
1713
  : "cpfe-status cpfe-ok"
1694
1714
  const shown = dirty && status === "" ? t("msg.unsaved") : status
1695
1715
 
1716
+ const createGroup = react.createElement(
1717
+ "section",
1718
+ { className: "cpfe-create" },
1719
+ react.createElement("h2", { className: "cpfe-h" }, t("create.heading")),
1720
+ react.createElement(SectionHint, { text: t("posture.hint") }),
1721
+ postureChoices.length === 0
1722
+ ? null
1723
+ : react.createElement(
1724
+ "div",
1725
+ null,
1726
+ react.createElement(
1727
+ "div",
1728
+ { className: "cpfe-pills", role: "radiogroup", "aria-label": t("posture.hint") },
1729
+ postureChoices.map((item) =>
1730
+ react.createElement(
1731
+ A.Pill,
1732
+ {
1733
+ key: item.id,
1734
+ active: item.id === posture,
1735
+ disabled: busy,
1736
+ "aria-pressed": item.id === posture,
1737
+ onClick: () => setPosture(item.id),
1738
+ },
1739
+ t("posture." + item.id + ".label", item.id),
1740
+ ),
1741
+ ),
1742
+ ),
1743
+ react.createElement("p", { className: "cpfe-note" }, t("posture." + posture + ".note", "")),
1744
+ ),
1745
+ react.createElement(
1746
+ "div",
1747
+ { className: "cpfe-newrow" },
1748
+ react.createElement(A.Input, {
1749
+ className: "cpfe-newinput",
1750
+ value: newName,
1751
+ placeholder: t("assistant.newPlaceholder"),
1752
+ "aria-label": t("assistant.newPlaceholder"),
1753
+ onKeyDown: (event) => {
1754
+ if (event.key === "Enter" && busy !== true) create()
1755
+ },
1756
+ onChange: (event) => {
1757
+ setNewName(event.target.value)
1758
+ setStatus("")
1759
+ },
1760
+ }),
1761
+ react.createElement(
1762
+ A.Button,
1763
+ {
1764
+ variant: "outline",
1765
+ icon: A.IconPlusOutline16 === undefined ? null : react.createElement(A.IconPlusOutline16),
1766
+ disabled: busy,
1767
+ onClick: () => create(),
1768
+ },
1769
+ busy ? t("btn.creating") : t("btn.create"),
1770
+ ),
1771
+ ),
1772
+ )
1773
+
1696
1774
  // ── assistant list ────────────────────────────────────────────────────
1697
1775
  const assistantList = react.createElement(
1698
1776
  "section",
@@ -1794,33 +1872,6 @@ try {
1794
1872
  )
1795
1873
  : null,
1796
1874
  ),
1797
- react.createElement(
1798
- "div",
1799
- { className: "cpfe-newrow" },
1800
- react.createElement(A.Input, {
1801
- className: "cpfe-newinput",
1802
- value: newName,
1803
- placeholder: t("assistant.newPlaceholder"),
1804
- "aria-label": t("assistant.newPlaceholder"),
1805
- onKeyDown: (event) => {
1806
- if (event.key === "Enter" && busy !== true) create()
1807
- },
1808
- onChange: (event) => {
1809
- setNewName(event.target.value)
1810
- setStatus("")
1811
- },
1812
- }),
1813
- react.createElement(
1814
- A.Button,
1815
- {
1816
- variant: "outline",
1817
- icon: A.IconPlusOutline16 === undefined ? null : react.createElement(A.IconPlusOutline16),
1818
- disabled: busy,
1819
- onClick: () => create(),
1820
- },
1821
- busy ? t("btn.creating") : t("btn.create"),
1822
- ),
1823
- ),
1824
1875
  editorReady
1825
1876
  ? react.createElement(
1826
1877
  "div",
@@ -1872,6 +1923,7 @@ try {
1872
1923
  ),
1873
1924
  )
1874
1925
  : null,
1926
+ createGroup,
1875
1927
  typeof (assistants.find((item) => item.id === selected) || {}).broken === "string"
1876
1928
  ? react.createElement("p", { className: "cpfe-note" }, t("assistant.brokenHint"))
1877
1929
  : null,
@@ -1953,8 +2005,6 @@ try {
1953
2005
  react.createElement(
1954
2006
  "section",
1955
2007
  { key: "rows" },
1956
- react.createElement("h2", { className: "cpfe-h" }, t("rows.heading")),
1957
- react.createElement(SectionHint, { text: t("rows.hint") }),
1958
2008
  draft.baseUnavailable !== null
1959
2009
  ? react.createElement("p", { className: "cpfe-note" }, t("rows.unavailable"))
1960
2010
  : react.createElement(
@@ -2020,7 +2070,7 @@ try {
2020
2070
  react.createElement(
2021
2071
  A.Button,
2022
2072
  {
2023
- variant: "outline",
2073
+ variant: "ghost",
2024
2074
  size: "sm",
2025
2075
  disabled:
2026
2076
  busy ||
@@ -2128,11 +2178,11 @@ try {
2128
2178
  variant: "outline",
2129
2179
  size: "sm",
2130
2180
  disabled: busy || draft.historyPick === "",
2181
+ title: t("history.hint"),
2131
2182
  onClick: loadVersion,
2132
2183
  },
2133
2184
  t("history.load"),
2134
2185
  ),
2135
- react.createElement("span", { className: "cpfe-history-hint" }, t("history.hint")),
2136
2186
  ),
2137
2187
  ),
2138
2188
  ]
@@ -2197,7 +2247,8 @@ try {
2197
2247
  ),
2198
2248
  ),
2199
2249
  ),
2200
- ...(editorReady ? editorSections.filter((section) => section.key === "name" || section.key === "prompt") : []),
2250
+ ...(editorReady ? editorSections.filter((section) => section.key === "prompt") : []),
2251
+ ...(editorReady ? editorSections.filter((section) => section.key === "name") : []),
2201
2252
  react.createElement(
2202
2253
  "div",
2203
2254
  { className: "cpfe-bar" },
@@ -2228,23 +2279,18 @@ try {
2228
2279
  dirty ? t("btn.reloadDiscard") : t("btn.reload"),
2229
2280
  ),
2230
2281
  react.createElement("span", { className: statusClass }, shown),
2231
- react.createElement(
2232
- "span",
2233
- { className: "cpfe-meta-line" },
2234
- // 让用户能自己判断装到的是哪一版:pnpm 的发布冷却期会让"不钉版本"的安装落到旧版
2235
- // (实测:干净机器上按名安装装到 1.0.1,而 latest 是 1.9.x)。
2236
- react.createElement(
2237
- "span",
2282
+ ),
2283
+ editorReady
2284
+ ? react.createElement(
2285
+ "p",
2238
2286
  {
2239
2287
  className: "cpfe-version",
2240
- title:
2241
- (editorReady ? String(payload.compositionPath ?? "") + "\n" : "") + t("meta.versionHint"),
2288
+ title: String(payload.compositionPath ?? "") + "\n" + t("meta.versionHint"),
2242
2289
  },
2243
- payload === null ? "" : "v" + String(payload.version ?? "?"),
2244
- ),
2245
- null,
2246
- ),
2247
- ),
2290
+ "v" + String(payload.version ?? "?"),
2291
+ )
2292
+ : null,
2293
+ ...(editorReady ? editorSections.filter((section) => section.key === "mode") : []),
2248
2294
  editorReady
2249
2295
  ? react.createElement(
2250
2296
  "section",
@@ -2264,10 +2310,16 @@ try {
2264
2310
  : capOpen
2265
2311
  ? "▾"
2266
2312
  : "▸",
2267
- t("cap.heading"),
2313
+ t("rows.heading"),
2268
2314
  ),
2269
- react.createElement(SectionHint, { text: t("cap.hint") }),
2270
- capOpen ? editorSections.filter((section) => section.key === "mode" || section.key === "rows") : null,
2315
+ capOpen
2316
+ ? react.createElement(
2317
+ "div",
2318
+ null,
2319
+ react.createElement(SectionHint, { text: t("cap.hint") }),
2320
+ editorSections.filter((section) => section.key === "rows"),
2321
+ )
2322
+ : null,
2271
2323
  )
2272
2324
  : null,
2273
2325
  // The confirmation is a portal: rendering it here keeps every piece of this page's