@max-null/dsh-node-appearance 0.1.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/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # dsh-node-appearance
2
+
3
+ 会话节点外观插件:按节点类型 / 工具名给 DeepSeek Harness Web GUI 的会话面板节点着色(可配置配色),并提供"显示思考过程"开关。纯前端渲染增强,不改 DSH 源码,`cordis.yml` 一行挂载。
4
+
5
+ ## 功能
6
+
7
+ - **节点着色**:工具调用、联网搜索、智能体调用、代码 / 指令执行、文件操作、任务 / 目标、指令节点(`/command`)、思考行(Think)——每种类别一个可配置颜色,左侧 3px 色条 + 淡色底,深 / 浅主题均可读。
8
+ - **工具级颜色覆盖**:任意工具名(如 `web_search`、`subagent`、`run_code`)可单独指定颜色,优先级高于类别色。
9
+ - **思考过程显示开关**:关闭后 Think 思考行前端隐藏(`display: none`),配置项与配色在同一张设置卡片里。
10
+ - **即时生效**:设置改动立即重绘会话,并持久化到 DSH 用户设置文档(`$DSH_HOME/settings.yaml`)。
11
+
12
+ ## 安装
13
+
14
+ ```sh
15
+ # 在 dsh profile 目录(如 ~/.dsh/profiles/web)
16
+ pnpm add @max-null/dsh-node-appearance
17
+ ```
18
+
19
+ 在 profile 的 `package.json`(`dsh.profile.bundles`)加入 `@max-null/dsh-node-appearance`,或在 profile 的 `cordis.patch.yml` 插入:
20
+
21
+ ```yaml
22
+ - insert:
23
+ - id: node-appearance
24
+ name: '@max-null/dsh-node-appearance'
25
+ ```
26
+
27
+ 重启 `dsh web` 后生效。设置入口:设置 → 插件配置 → **节点外观**。
28
+
29
+ ## 配置
30
+
31
+ `cordis.yml` / settings 文档均可覆盖(以下为初始化配色):
32
+
33
+ ```yaml
34
+ node-appearance:
35
+ showThinking: true
36
+ colors:
37
+ search: '#3b82f6' # 联网搜索
38
+ agent: '#a855f7' # 智能体调用
39
+ execute: '#f59e0b' # 代码 / 指令执行
40
+ file: '#22c55e' # 文件操作
41
+ task: '#ec4899' # 任务 / 目标
42
+ command: '#f97316' # 指令节点
43
+ thinking: '#c4b5fd' # 思考过程
44
+ context: '#8a9bb5' # 上下文注入
45
+ other: '#64748b' # 其他工具
46
+ toolColors: {} # 工具名 → 颜色覆盖
47
+ ```
48
+
49
+ ## 工作方式
50
+
51
+ 双面插件(Host + browser half,`dsh.client` bundle 由 DSH client 模块系统自动加载):
52
+
53
+ - Host half 通过 `installSettingsSection` 注册 `node-appearance` settings namespace,插件配置作为 base 层。
54
+ - Browser half 绑定 `ctx.settingsScope`,把快照交给纯函数 `buildCss()` 生成 CSS,注入一个 `<style data-plugin-css="node-appearance/rules">` 标签;快照变化即重绘。
55
+ - 着色目标全部使用 DSH 会话 DOM 的稳定 data 属性(`data-chat-flow-kind` / `data-tool` / `data-variant`),不依赖任何 CSS Modules 哈希类名。
56
+
57
+ ## 已知限制
58
+
59
+ - v0.1 不做运行态动画与节点折叠。
60
+ - 命令节点只有类别色(`command`),暂无命令名级配色。
61
+ - `toolColors` 按工具名精确匹配;DSH 工具名变更时旧条目静默失效(可在设置面板删除)。
62
+
63
+ ## 开发
64
+
65
+ ```sh
66
+ npm install
67
+ npm run typecheck # tsc
68
+ npm test # vitest(CSS 规则生成 + Config schema)
69
+ npm run build # tsc 类型 + tsdown(lib/index.js + lib/client.js)
70
+ ```
71
+
72
+ ## 文档
73
+
74
+ - [决策记录](docs/决策/2026-08-17-节点外观插件-独立插件决策.md)
75
+ - [设计方案](docs/设计/DSH节点外观插件-设计方案.md)
@@ -0,0 +1,6 @@
1
+ # dsh-node-appearance profile patch layer — insert one loader entry for the
2
+ # plugin package. The browser half is picked up by dsh-client-modules through
3
+ # the package's dsh.client declaration, exactly like the shipped ui-* packages.
4
+ - insert:
5
+ - id: node-appearance
6
+ name: '@max-null/dsh-node-appearance'
package/lib/client.js ADDED
@@ -0,0 +1,438 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@max-null/dsh-node-appearance",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react = require("react");
8
+ let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
9
+ let react_jsx_runtime = require("react/jsx-runtime");
10
+ //#region src/client/palette.ts
11
+ /**
12
+ * Node appearance palette: category/tool mapping, default colors, and the pure
13
+ * CSS-rule generator that paints the chat flow. Browser-half owned; the Host
14
+ * half mirrors the same defaults through its schemastery schema (the two
15
+ * cannot share a value import — the browser bundle purity gate forbids
16
+ * cross-package value imports).
17
+ */
18
+ /** The settings namespace this plugin owns (spelled identically in both halves). */
19
+ const NODE_APPEARANCE_NS = "node-appearance";
20
+ /** The plugin's style tag identity (removed/replaced on every re-paint). */
21
+ const STYLE_TAG_ID = `${NODE_APPEARANCE_NS}/rules`;
22
+ /** Initial palette: mid-luminance hues readable on both light and dark themes. */
23
+ const DEFAULT_COLORS = {
24
+ search: "#3b82f6",
25
+ agent: "#a855f7",
26
+ execute: "#f59e0b",
27
+ file: "#22c55e",
28
+ task: "#ec4899",
29
+ command: "#f97316",
30
+ thinking: "#c4b5fd",
31
+ context: "#8a9bb5",
32
+ other: "#64748b"
33
+ };
34
+ /** Wire tool name → category for the shipped mapping. */
35
+ const TOOL_CATEGORIES = {
36
+ search: ["web_search", "web_fetch"],
37
+ agent: [
38
+ "subagent",
39
+ "subagent_fork",
40
+ "send_message",
41
+ "interrupt_agent",
42
+ "list_agents",
43
+ "report",
44
+ "workflow"
45
+ ],
46
+ execute: [
47
+ "bash",
48
+ "pwsh",
49
+ "run_code",
50
+ "terminal_open",
51
+ "terminal_close",
52
+ "terminal_list",
53
+ "terminal_read",
54
+ "terminal_send",
55
+ "terminal_signal",
56
+ "str_replace_editor"
57
+ ],
58
+ file: [
59
+ "read",
60
+ "write",
61
+ "edit",
62
+ "read_image",
63
+ "glob",
64
+ "grep"
65
+ ],
66
+ task: [
67
+ "todo_write",
68
+ "create_goal",
69
+ "get_goal",
70
+ "update_goal",
71
+ "job_kill",
72
+ "job_list",
73
+ "job_output",
74
+ "schedule_create",
75
+ "schedule_delete",
76
+ "schedule_list",
77
+ "exit_plan_mode"
78
+ ],
79
+ other: []
80
+ };
81
+ /** Selector matching every tool row inside a tool-call node. */
82
+ const TOOL_ROW = "[data-chat-flow-kind=\"tool-call\"] [data-tool]";
83
+ /** The ToolRow root itself (carries data-variant; the wrapper callRow does not). */
84
+ const TOOL_ROW_ROOT = "[data-chat-flow-kind=\"tool-call\"] [data-tool][data-variant]";
85
+ /** Selector of the command node wrapper. */
86
+ const COMMAND_ROW = "[data-chat-flow-kind=\"command\"]";
87
+ /** Selector of the Think reasoning row. */
88
+ const THINK_ROW = "[data-variant=\"think\"]";
89
+ /** Selector of the injected-context row (the flow-item wrapper). */
90
+ const CONTEXT_ROW = "[data-chat-flow-kind=\"context\"]";
91
+ /**
92
+ * The rows that carry the accent paint: the ToolRow root (not its wrapper
93
+ * callRow — both carry data-tool, only the inner row carries data-variant),
94
+ * command nodes, Think rows, and injected-context rows. The 3px inset rail is
95
+ * compensated with a matching padding-left so the rail never covers the
96
+ * row's leading icon.
97
+ */
98
+ const ACCENTED_ROWS = [
99
+ TOOL_ROW_ROOT,
100
+ COMMAND_ROW,
101
+ THINK_ROW,
102
+ CONTEXT_ROW
103
+ ].join(",\n");
104
+ /**
105
+ * Accept a configured CSS color (hex, color functions) or reject it so an
106
+ * invalid value falls back to the category default instead of poisoning CSS.
107
+ * @param value - configured color string.
108
+ * @returns whether the value is a plausible CSS color literal.
109
+ */
110
+ function isCssColor(value) {
111
+ const trimmed = value.trim();
112
+ if (trimmed === "") return false;
113
+ if (/^#[0-9a-f]{3,8}$/i.test(trimmed)) return true;
114
+ return /^(rgb|rgba|hsl|hsla|hwb|lab|lch|oklab|oklch|color|device-cmyk)\(/i.test(trimmed);
115
+ }
116
+ /** Resolve the effective palette from user settings merged over defaults. */
117
+ function resolveColors(settings) {
118
+ const colors = { ...DEFAULT_COLORS };
119
+ const configured = settings.colors;
120
+ if (configured !== void 0 && typeof configured === "object" && configured !== null) for (const key of Object.keys(DEFAULT_COLORS)) {
121
+ const value = configured[key];
122
+ if (typeof value === "string" && isCssColor(value)) colors[key] = value;
123
+ }
124
+ return colors;
125
+ }
126
+ /** Escape a tool name into a CSS attribute selector string literal. */
127
+ function attributeLiteral(value) {
128
+ return value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
129
+ }
130
+ /**
131
+ * Build the complete stylesheet text for one settings snapshot.
132
+ * @param settings - the namespace scope value (may be partial or undefined).
133
+ * @returns CSS text painting accent rows and (optionally) hiding Think rows.
134
+ */
135
+ function buildCss(settings) {
136
+ const colors = resolveColors(settings ?? {});
137
+ const toolOverrides = settings?.toolColors;
138
+ const lines = [];
139
+ for (const [category, tools] of Object.entries(TOOL_CATEGORIES)) {
140
+ const key = category;
141
+ for (const tool of tools) {
142
+ const override = toolOverrides?.[tool];
143
+ const color = typeof override === "string" && isCssColor(override) ? override : colors[key];
144
+ lines.push(`[data-chat-flow-kind="tool-call"] [data-tool="${attributeLiteral(tool)}"] { --ncolor-accent: ${color}; }`);
145
+ }
146
+ }
147
+ lines.push(`${TOOL_ROW} { --ncolor-accent: ${colors.other}; }`);
148
+ lines.push(`${COMMAND_ROW} { --ncolor-accent: ${colors.command}; }`);
149
+ lines.push(`${THINK_ROW} { --ncolor-accent: ${colors.thinking}; }`);
150
+ lines.push(`${CONTEXT_ROW} { --ncolor-accent: ${colors.context}; }`);
151
+ lines.push(`${ACCENTED_ROWS} {
152
+ box-shadow: inset 3px 0 0 var(--ncolor-accent);
153
+ background-color: color-mix(in srgb, var(--ncolor-accent) 8%, transparent);
154
+ padding-left: 3px;
155
+ }`);
156
+ if (settings?.showThinking === false) lines.push(`${THINK_ROW} { display: none !important; }`);
157
+ return lines.join("\n");
158
+ }
159
+ //#endregion
160
+ //#region \0dsh-css:H:\MaxNull\WorkStation\dsh-node-appearance\src\client\card.module.css.mjs
161
+ const css = ".AWuyDW_group{border-bottom:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:10px;padding:16px 0;display:flex}.AWuyDW_header{appearance:none;width:100%;font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:6px;align-items:center;gap:8px;padding:0;display:flex}.AWuyDW_title{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:14px;font-weight:400;line-height:22px}.AWuyDW_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.AWuyDW_chevronOpen{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s;transform:rotate(180deg)}.AWuyDW_body{flex-direction:column;display:flex}.AWuyDW_row{align-items:center;gap:10px;padding:6px 0;font-size:13px;display:flex}.AWuyDW_rowLabel{color:var(--dsw-alias-label-primary);flex:0 0 132px;font-size:13px;font-weight:500;line-height:1.5}.AWuyDW_rowHint{color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:12px;line-height:1.5;overflow:hidden}.AWuyDW_checkbox{accent-color:var(--dsw-alias-brand-primary);cursor:pointer;width:15px;height:15px}.AWuyDW_colorInput{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);cursor:pointer;border-radius:6px;width:44px;height:26px;padding:2px}.AWuyDW_colorInput:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.AWuyDW_colorValue{color:var(--dsw-alias-label-secondary);font-family:ui-monospace,monospace;font-size:12px}.AWuyDW_sectionTitle{color:var(--dsw-alias-label-primary);border-top:1px solid var(--dsw-alias-border-l1);margin-top:10px;padding:10px 0 4px;font-size:13px;font-weight:600;line-height:1.5}.AWuyDW_toolRow{align-items:center;gap:10px;padding:6px 0;font-size:13px;display:flex}.AWuyDW_toolName{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-secondary);flex:1;font-family:ui-monospace,monospace;font-size:12px;line-height:1.5;overflow:hidden}.AWuyDW_removeButton,.AWuyDW_addButton{appearance:none;font:inherit;cursor:pointer;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.AWuyDW_removeButton:hover:not(:disabled),.AWuyDW_addButton:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.AWuyDW_removeButton:disabled,.AWuyDW_addButton:disabled{opacity:.4;cursor:default}.AWuyDW_removeButton:focus-visible,.AWuyDW_addButton:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.AWuyDW_addRow{align-items:center;gap:10px;padding:10px 0 4px;display:flex}.AWuyDW_toolInput{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);min-width:0;height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;flex:1;padding:0 12px;font-size:13px;line-height:1.5}.AWuyDW_toolInput:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.AWuyDW_statusLine{color:var(--dsw-alias-label-tertiary);margin:0 0 8px;font-size:12px;line-height:1.5}.AWuyDW_disabled{opacity:.5;pointer-events:none}";
162
+ const tagId = "@max-null/dsh-node-appearance/card.module.css";
163
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
164
+ const tag = document.createElement("style");
165
+ tag.dataset.plugin = "@max-null/dsh-node-appearance";
166
+ tag.dataset.pluginCss = tagId;
167
+ tag.textContent = css;
168
+ document.head.appendChild(tag);
169
+ }
170
+ var card_module_css_default = {
171
+ "disabled": "AWuyDW_disabled",
172
+ "rowLabel": "AWuyDW_rowLabel",
173
+ "chevron": "AWuyDW_chevron",
174
+ "checkbox": "AWuyDW_checkbox",
175
+ "title": "AWuyDW_title",
176
+ "sectionTitle": "AWuyDW_sectionTitle",
177
+ "rowHint": "AWuyDW_rowHint",
178
+ "chevronOpen": "AWuyDW_chevronOpen",
179
+ "removeButton": "AWuyDW_removeButton",
180
+ "addRow": "AWuyDW_addRow",
181
+ "toolRow": "AWuyDW_toolRow",
182
+ "row": "AWuyDW_row",
183
+ "body": "AWuyDW_body",
184
+ "colorInput": "AWuyDW_colorInput",
185
+ "toolName": "AWuyDW_toolName",
186
+ "group": "AWuyDW_group",
187
+ "header": "AWuyDW_header",
188
+ "colorValue": "AWuyDW_colorValue",
189
+ "addButton": "AWuyDW_addButton",
190
+ "toolInput": "AWuyDW_toolInput",
191
+ "statusLine": "AWuyDW_statusLine"
192
+ };
193
+ //#endregion
194
+ //#region src/client/settings-card.tsx
195
+ /**
196
+ * The plugin's General-settings row: a "节点外观" title with the standard
197
+ * chevron, expanding to palette rows, tool overrides, and the Think
198
+ * visibility switch. Reads the namespace scope through the injected hooks
199
+ * face and writes through the injected actions; every change applies
200
+ * immediately. Drawn by hand (no value import from ui-settings-plugins — the
201
+ * client bundle purity gate forbids cross-plugin value imports), styled with
202
+ * the DSH design tokens like the shipped Appearance row.
203
+ */
204
+ /** Chinese labels per category (product copy is Chinese). */
205
+ const CATEGORY_LABELS = {
206
+ search: "联网搜索",
207
+ agent: "智能体调用",
208
+ execute: "代码 / 指令执行",
209
+ file: "文件操作",
210
+ task: "任务 / 目标",
211
+ command: "指令节点",
212
+ thinking: "思考过程",
213
+ context: "上下文注入",
214
+ other: "其他工具"
215
+ };
216
+ /** Tools shown in the add-override autocomplete-free text input placeholder. */
217
+ const TOOL_SUGGESTIONS = Object.values(TOOL_CATEGORIES).flat().join("、");
218
+ /**
219
+ * Render the node-appearance General row.
220
+ * @param props - the injected scope snapshot hook and write actions.
221
+ * @returns the row, always visible once its namespace is served.
222
+ */
223
+ function NodeAppearanceRow({ useNodeAppearance, setShowThinking, setCategoryColor, setToolColor, removeToolColor }) {
224
+ const [open, setOpen] = (0, react.useState)(false);
225
+ const [toolName, setToolName] = (0, react.useState)("");
226
+ const [toolColor, setToolColorDraft] = (0, react.useState)("#64748b");
227
+ const snapshot = useNodeAppearance((snapshot) => snapshot);
228
+ const colors = resolveColors(snapshot.value ?? {});
229
+ const toolColors = snapshot.value?.toolColors ?? {};
230
+ const showThinking = snapshot.value?.showThinking ?? true;
231
+ const disabled = snapshot.writable === false;
232
+ const addTool = (event) => {
233
+ event.preventDefault();
234
+ const name = toolName.trim();
235
+ if (name === "" || !isCssColor(toolColor)) return;
236
+ setToolColor(name, toolColor);
237
+ setToolName("");
238
+ };
239
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
240
+ className: card_module_css_default.group,
241
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
242
+ type: "button",
243
+ className: card_module_css_default.header,
244
+ "aria-expanded": open,
245
+ onClick: () => {
246
+ setOpen(!open);
247
+ },
248
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
249
+ className: card_module_css_default.title,
250
+ children: "节点外观"
251
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: open ? card_module_css_default.chevronOpen : card_module_css_default.chevron })]
252
+ }), open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
253
+ className: card_module_css_default.body,
254
+ children: [snapshot.status !== "ready" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
255
+ className: card_module_css_default.statusLine,
256
+ children: snapshot.status === "unavailable" ? "配置暂不可用:插件主机侧未挂载或连接非本机。" : "配置加载中…"
257
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
258
+ className: `${disabled ? card_module_css_default.disabled : ""}`,
259
+ children: [
260
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
261
+ className: card_module_css_default.row,
262
+ children: [
263
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
264
+ className: card_module_css_default.rowLabel,
265
+ htmlFor: "node-appearance-show-thinking",
266
+ children: "显示思考过程"
267
+ }),
268
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
269
+ id: "node-appearance-show-thinking",
270
+ className: card_module_css_default.checkbox,
271
+ type: "checkbox",
272
+ checked: showThinking,
273
+ onChange: (event) => {
274
+ setShowThinking(event.target.checked);
275
+ }
276
+ }),
277
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
278
+ className: card_module_css_default.rowHint,
279
+ children: "关闭后会话中的 Think 思考行隐藏"
280
+ })
281
+ ]
282
+ }),
283
+ Object.keys(CATEGORY_LABELS).map((category) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
284
+ className: card_module_css_default.row,
285
+ children: [
286
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
287
+ className: card_module_css_default.rowLabel,
288
+ htmlFor: `node-appearance-color-${category}`,
289
+ children: CATEGORY_LABELS[category]
290
+ }),
291
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
292
+ id: `node-appearance-color-${category}`,
293
+ className: card_module_css_default.colorInput,
294
+ type: "color",
295
+ value: colors[category],
296
+ onChange: (event) => {
297
+ setCategoryColor(category, event.target.value);
298
+ }
299
+ }),
300
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
301
+ className: card_module_css_default.colorValue,
302
+ children: colors[category]
303
+ })
304
+ ]
305
+ }, category)),
306
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
307
+ className: card_module_css_default.sectionTitle,
308
+ children: "工具颜色覆盖(可选)"
309
+ }),
310
+ Object.entries(toolColors).map(([tool, color]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
311
+ className: card_module_css_default.toolRow,
312
+ children: [
313
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
314
+ className: card_module_css_default.toolName,
315
+ title: tool,
316
+ children: tool
317
+ }),
318
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
319
+ className: card_module_css_default.colorInput,
320
+ type: "color",
321
+ value: isCssColor(color) ? color : DEFAULT_COLORS.other,
322
+ onChange: (event) => {
323
+ setToolColor(tool, event.target.value);
324
+ }
325
+ }),
326
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
327
+ type: "button",
328
+ className: card_module_css_default.removeButton,
329
+ onClick: () => {
330
+ removeToolColor(tool);
331
+ },
332
+ children: "删除"
333
+ })
334
+ ]
335
+ }, tool)),
336
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
337
+ className: card_module_css_default.addRow,
338
+ onSubmit: addTool,
339
+ children: [
340
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
341
+ className: card_module_css_default.toolInput,
342
+ type: "text",
343
+ placeholder: `工具名,如 ${TOOL_SUGGESTIONS}`,
344
+ value: toolName,
345
+ onChange: (event) => {
346
+ setToolName(event.target.value);
347
+ }
348
+ }),
349
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
350
+ className: card_module_css_default.colorInput,
351
+ type: "color",
352
+ value: toolColor,
353
+ onChange: (event) => {
354
+ setToolColorDraft(event.target.value);
355
+ }
356
+ }),
357
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
358
+ type: "submit",
359
+ className: card_module_css_default.addButton,
360
+ disabled: toolName.trim() === "",
361
+ children: "添加"
362
+ })
363
+ ]
364
+ })
365
+ ]
366
+ })]
367
+ })]
368
+ });
369
+ }
370
+ //#endregion
371
+ //#region src/client/index.ts
372
+ const inject = [
373
+ "slots",
374
+ "connection",
375
+ "remote",
376
+ "settingsScope"
377
+ ];
378
+ /** Upsert the plugin's paint stylesheet with the current snapshot's CSS. */
379
+ function paint(scope) {
380
+ let tag = document.querySelector(`style[data-plugin-css="${STYLE_TAG_ID}"]`);
381
+ if (tag === null) {
382
+ tag = document.createElement("style");
383
+ tag.dataset.plugin = NODE_APPEARANCE_NS;
384
+ tag.dataset.pluginCss = STYLE_TAG_ID;
385
+ document.head.appendChild(tag);
386
+ }
387
+ tag.textContent = buildCss(scope.getSnapshot().value);
388
+ }
389
+ /**
390
+ * Mount the node-appearance plugin on the page.
391
+ * @param ctx - client root context.
392
+ */
393
+ function apply(ctx) {
394
+ const scope = ctx.settingsScope.bind({ namespace: NODE_APPEARANCE_NS });
395
+ paint(scope);
396
+ ctx.effect(() => scope.subscribe(() => {
397
+ paint(scope);
398
+ }), "dsh-node-appearance: repaint on settings change");
399
+ const face = {
400
+ hooks: { nodeAppearance: scope },
401
+ setShowThinking: (show) => {
402
+ scope.set("showThinking", show);
403
+ },
404
+ setCategoryColor: (category, color) => {
405
+ const value = scope.getSnapshot().value;
406
+ scope.set("colors", {
407
+ ...value?.colors,
408
+ [category]: color
409
+ });
410
+ },
411
+ setToolColor: (tool, color) => {
412
+ const value = scope.getSnapshot().value;
413
+ scope.set("toolColors", {
414
+ ...value?.toolColors,
415
+ [tool]: color
416
+ });
417
+ },
418
+ removeToolColor: (tool) => {
419
+ const toolColors = { ...scope.getSnapshot().value?.toolColors };
420
+ delete toolColors[tool];
421
+ scope.set("toolColors", toolColors);
422
+ }
423
+ };
424
+ ctx.slots.inject("settings.general.item", () => ctx.slots.register({
425
+ name: "settings.general.item",
426
+ id: NODE_APPEARANCE_NS,
427
+ order: 30,
428
+ inject: () => face
429
+ }, NodeAppearanceRow));
430
+ }
431
+ //#endregion
432
+ exports.apply = apply;
433
+ exports.inject = inject;
434
+ return module.exports;
435
+ }
436
+ });
437
+
438
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","names":["useState","css","IconChevronDownOutline14"],"sources":["../src/client/palette.ts","../src/client/settings-card.tsx","../src/client/index.ts"],"sourcesContent":["/**\n * Node appearance palette: category/tool mapping, default colors, and the pure\n * CSS-rule generator that paints the chat flow. Browser-half owned; the Host\n * half mirrors the same defaults through its schemastery schema (the two\n * cannot share a value import — the browser bundle purity gate forbids\n * cross-package value imports).\n */\n\n/** The settings namespace this plugin owns (spelled identically in both halves). */\nexport const NODE_APPEARANCE_NS = 'node-appearance'\n\n/** The plugin's style tag identity (removed/replaced on every re-paint). */\nexport const STYLE_TAG_ID = `${NODE_APPEARANCE_NS}/rules`\n\n/** One paintable node category. `command`, `thinking`, and `context` are non-tool rows. */\nexport type NodeCategory =\n | 'search' | 'agent' | 'execute' | 'file' | 'task' | 'command' | 'thinking' | 'context' | 'other'\n\n/** Accent color per category (CSS colors). */\nexport type NodeAppearanceColors = Record<NodeCategory, string>\n\n/** The settings value this plugin reads from its namespace scope. */\nexport interface NodeAppearanceSettings {\n /** Show assistant reasoning blocks as Think rows; false hides them. */\n showThinking?: boolean | undefined\n /** Per-category accent colors; missing keys fall back to defaults. */\n colors?: Partial<NodeAppearanceColors> | undefined\n /** Per-tool accent overrides keyed by wire tool name. */\n toolColors?: Record<string, string> | undefined\n}\n\n/** Initial palette: mid-luminance hues readable on both light and dark themes. */\nexport const DEFAULT_COLORS: NodeAppearanceColors = {\n search: '#3b82f6', // blue — web_search / web_fetch\n agent: '#a855f7', // purple — subagent / workflow / send_message …\n execute: '#f59e0b', // amber — bash / pwsh / run_code / terminal_*\n file: '#22c55e', // green — read / write / edit / glob / grep\n task: '#ec4899', // pink — todo_write / goal / job_* / schedule_*\n command: '#f97316', // orange — /command nodes\n thinking: '#c4b5fd', // light purple — Think rows\n context: '#8a9bb5', // slate blue — injected context rows (informational)\n other: '#64748b', // slate — every unlisted tool\n}\n\n/** Wire tool name → category for the shipped mapping. */\nexport const TOOL_CATEGORIES: Record<Exclude<NodeCategory, 'command' | 'thinking' | 'context'>, readonly string[]> = {\n search: ['web_search', 'web_fetch'],\n agent: ['subagent', 'subagent_fork', 'send_message', 'interrupt_agent', 'list_agents', 'report', 'workflow'],\n execute: ['bash', 'pwsh', 'run_code', 'terminal_open', 'terminal_close', 'terminal_list', 'terminal_read', 'terminal_send', 'terminal_signal', 'str_replace_editor'],\n file: ['read', 'write', 'edit', 'read_image', 'glob', 'grep'],\n task: ['todo_write', 'create_goal', 'get_goal', 'update_goal', 'job_kill', 'job_list', 'job_output', 'schedule_create', 'schedule_delete', 'schedule_list', 'exit_plan_mode'],\n other: [],\n}\n\n/** Selector matching every tool row inside a tool-call node. */\nconst TOOL_ROW = '[data-chat-flow-kind=\"tool-call\"] [data-tool]'\n/** The ToolRow root itself (carries data-variant; the wrapper callRow does not). */\nconst TOOL_ROW_ROOT = '[data-chat-flow-kind=\"tool-call\"] [data-tool][data-variant]'\n/** Selector of the command node wrapper. */\nconst COMMAND_ROW = '[data-chat-flow-kind=\"command\"]'\n/** Selector of the Think reasoning row. */\nconst THINK_ROW = '[data-variant=\"think\"]'\n/** Selector of the injected-context row (the flow-item wrapper). */\nconst CONTEXT_ROW = '[data-chat-flow-kind=\"context\"]'\n\n/**\n * The rows that carry the accent paint: the ToolRow root (not its wrapper\n * callRow — both carry data-tool, only the inner row carries data-variant),\n * command nodes, Think rows, and injected-context rows. The 3px inset rail is\n * compensated with a matching padding-left so the rail never covers the\n * row's leading icon.\n */\nconst ACCENTED_ROWS = [TOOL_ROW_ROOT, COMMAND_ROW, THINK_ROW, CONTEXT_ROW].join(',\\n')\n\n/**\n * Accept a configured CSS color (hex, color functions) or reject it so an\n * invalid value falls back to the category default instead of poisoning CSS.\n * @param value - configured color string.\n * @returns whether the value is a plausible CSS color literal.\n */\nexport function isCssColor(value: string): boolean {\n const trimmed = value.trim()\n if (trimmed === '') return false\n if (/^#[0-9a-f]{3,8}$/i.test(trimmed)) return true\n return /^(rgb|rgba|hsl|hsla|hwb|lab|lch|oklab|oklch|color|device-cmyk)\\(/i.test(trimmed)\n}\n\n/** Resolve the effective palette from user settings merged over defaults. */\nexport function resolveColors(settings: NodeAppearanceSettings): NodeAppearanceColors {\n const colors: NodeAppearanceColors = { ...DEFAULT_COLORS }\n const configured = settings.colors\n if (configured !== undefined && typeof configured === 'object' && configured !== null) {\n for (const key of Object.keys(DEFAULT_COLORS) as NodeCategory[]) {\n const value = configured[key]\n if (typeof value === 'string' && isCssColor(value)) colors[key] = value\n }\n }\n return colors\n}\n\n/** Escape a tool name into a CSS attribute selector string literal. */\nfunction attributeLiteral(value: string): string {\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"')\n}\n\n/**\n * Build the complete stylesheet text for one settings snapshot.\n * @param settings - the namespace scope value (may be partial or undefined).\n * @returns CSS text painting accent rows and (optionally) hiding Think rows.\n */\nexport function buildCss(settings: NodeAppearanceSettings | undefined): string {\n const colors = resolveColors(settings ?? {})\n const toolOverrides = settings?.toolColors\n const lines: string[] = []\n\n // Per-tool accent assignments (explicit overrides win over category color).\n for (const [category, tools] of Object.entries(TOOL_CATEGORIES)) {\n const key = category as Exclude<NodeCategory, 'command' | 'thinking'>\n for (const tool of tools) {\n const override = toolOverrides?.[tool]\n const color = typeof override === 'string' && isCssColor(override)\n ? override\n : colors[key]\n lines.push(`[data-chat-flow-kind=\"tool-call\"] [data-tool=\"${attributeLiteral(tool)}\"] { --ncolor-accent: ${color}; }`)\n }\n }\n // Unlisted tools keep the neutral fallback painted by the base rule.\n lines.push(`${TOOL_ROW} { --ncolor-accent: ${colors.other}; }`)\n\n // Non-tool accent rows.\n lines.push(`${COMMAND_ROW} { --ncolor-accent: ${colors.command}; }`)\n lines.push(`${THINK_ROW} { --ncolor-accent: ${colors.thinking}; }`)\n lines.push(`${CONTEXT_ROW} { --ncolor-accent: ${colors.context}; }`)\n\n // One shared paint rule: 3px inset left rail (layout-free) + 8% wash. The\n // matching padding-left moves the row content off the rail so the leading\n // icon is never covered.\n lines.push(`${ACCENTED_ROWS} {\n box-shadow: inset 3px 0 0 var(--ncolor-accent);\n background-color: color-mix(in srgb, var(--ncolor-accent) 8%, transparent);\n padding-left: 3px;\n}`)\n\n // Visibility switch: hide Think rows entirely on the frontend. `!important`\n // outranks the module stylesheet's `.root { display: flex }` (same\n // specificity, but the plugin style tag is injected before the module's).\n if (settings?.showThinking === false) {\n lines.push(`${THINK_ROW} { display: none !important; }`)\n }\n\n return lines.join('\\n')\n}\n","/**\n * The plugin's General-settings row: a \"节点外观\" title with the standard\n * chevron, expanding to palette rows, tool overrides, and the Think\n * visibility switch. Reads the namespace scope through the injected hooks\n * face and writes through the injected actions; every change applies\n * immediately. Drawn by hand (no value import from ui-settings-plugins — the\n * client bundle purity gate forbids cross-plugin value imports), styled with\n * the DSH design tokens like the shipped Appearance row.\n */\n\nimport { useState, type ChangeEvent, type FormEvent } from 'react'\nimport type { SettingsScopeSnapshot } from '@deepseek-ai/dsh-client-runtime/client'\nimport { IconChevronDownOutline14 } from '@deepseek-ai/dsh-client-ui-primitives'\nimport type { InjectFace, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport {\n DEFAULT_COLORS, isCssColor, resolveColors, TOOL_CATEGORIES,\n type NodeAppearanceSettings, type NodeCategory,\n} from './palette.ts'\nimport css from './card.module.css'\n\n/** The row's inject face: the read-only scope snapshot plus write actions. */\nexport interface NodeAppearanceRowFace {\n hooks: {\n /** The bound settings-scope snapshot, rendered as useNodeAppearance. */\n nodeAppearance: {\n getSnapshot(): SettingsScopeSnapshot<NodeAppearanceSettings>\n subscribe(listener: () => void): () => void\n }\n }\n /** Write the Think visibility switch. */\n setShowThinking(show: boolean): void\n /** Merge one category color into the palette section. */\n setCategoryColor(category: NodeCategory, color: string): void\n /** Merge one tool override into the toolColors section. */\n setToolColor(tool: string, color: string): void\n /** Remove one tool override. */\n removeToolColor(tool: string): void\n}\n\n/** Props the renderer binds for this General row. */\nexport type NodeAppearanceRowProps =\n PropsRuntime<'settings.general.item'>\n & InjectFace<NodeAppearanceRowFace>\n\n/** Chinese labels per category (product copy is Chinese). */\nconst CATEGORY_LABELS: Record<NodeCategory, string> = {\n search: '联网搜索',\n agent: '智能体调用',\n execute: '代码 / 指令执行',\n file: '文件操作',\n task: '任务 / 目标',\n command: '指令节点',\n thinking: '思考过程',\n context: '上下文注入',\n other: '其他工具',\n}\n\n/** Tools shown in the add-override autocomplete-free text input placeholder. */\nconst TOOL_SUGGESTIONS = Object.values(TOOL_CATEGORIES).flat().join('、')\n\n/**\n * Render the node-appearance General row.\n * @param props - the injected scope snapshot hook and write actions.\n * @returns the row, always visible once its namespace is served.\n */\nexport function NodeAppearanceRow({ useNodeAppearance, setShowThinking, setCategoryColor, setToolColor, removeToolColor }: NodeAppearanceRowProps) {\n const [open, setOpen] = useState(false)\n const [toolName, setToolName] = useState('')\n const [toolColor, setToolColorDraft] = useState('#64748b')\n const snapshot = useNodeAppearance(snapshot => snapshot)\n const colors = resolveColors(snapshot.value ?? {})\n const toolColors = snapshot.value?.toolColors ?? {}\n const showThinking = snapshot.value?.showThinking ?? true\n const disabled = snapshot.writable === false\n\n const addTool = (event: FormEvent) => {\n event.preventDefault()\n const name = toolName.trim()\n if (name === '' || !isCssColor(toolColor)) return\n setToolColor(name, toolColor)\n setToolName('')\n }\n\n return (\n <div className={css.group}>\n <button\n type=\"button\"\n className={css.header}\n aria-expanded={open}\n onClick={() => { setOpen(!open) }}\n >\n <span className={css.title}>节点外观</span>\n <IconChevronDownOutline14 className={open ? css.chevronOpen : css.chevron} />\n </button>\n {open && (\n <div className={css.body}>\n {snapshot.status !== 'ready' && (\n <p className={css.statusLine}>\n {snapshot.status === 'unavailable'\n ? '配置暂不可用:插件主机侧未挂载或连接非本机。'\n : '配置加载中…'}\n </p>\n )}\n <div className={`${disabled ? css.disabled : ''}`}>\n <div className={css.row}>\n <label className={css.rowLabel} htmlFor=\"node-appearance-show-thinking\">显示思考过程</label>\n <input\n id=\"node-appearance-show-thinking\"\n className={css.checkbox}\n type=\"checkbox\"\n checked={showThinking}\n onChange={(event: ChangeEvent<HTMLInputElement>) => { setShowThinking(event.target.checked) }}\n />\n <span className={css.rowHint}>关闭后会话中的 Think 思考行隐藏</span>\n </div>\n {(Object.keys(CATEGORY_LABELS) as NodeCategory[]).map(category => (\n <div className={css.row} key={category}>\n <label className={css.rowLabel} htmlFor={`node-appearance-color-${category}`}>\n {CATEGORY_LABELS[category]}\n </label>\n <input\n id={`node-appearance-color-${category}`}\n className={css.colorInput}\n type=\"color\"\n value={colors[category]}\n onChange={(event: ChangeEvent<HTMLInputElement>) => { setCategoryColor(category, event.target.value) }}\n />\n <span className={css.colorValue}>{colors[category]}</span>\n </div>\n ))}\n <div className={css.sectionTitle}>工具颜色覆盖(可选)</div>\n {Object.entries(toolColors).map(([tool, color]) => (\n <div className={css.toolRow} key={tool}>\n <span className={css.toolName} title={tool}>{tool}</span>\n <input\n className={css.colorInput}\n type=\"color\"\n value={isCssColor(color) ? color : DEFAULT_COLORS.other}\n onChange={(event: ChangeEvent<HTMLInputElement>) => { setToolColor(tool, event.target.value) }}\n />\n <button type=\"button\" className={css.removeButton} onClick={() => { removeToolColor(tool) }}>删除</button>\n </div>\n ))}\n <form className={css.addRow} onSubmit={addTool}>\n <input\n className={css.toolInput}\n type=\"text\"\n placeholder={`工具名,如 ${TOOL_SUGGESTIONS}`}\n value={toolName}\n onChange={(event: ChangeEvent<HTMLInputElement>) => { setToolName(event.target.value) }}\n />\n <input\n className={css.colorInput}\n type=\"color\"\n value={toolColor}\n onChange={(event: ChangeEvent<HTMLInputElement>) => { setToolColorDraft(event.target.value) }}\n />\n <button type=\"submit\" className={css.addButton} disabled={toolName.trim() === ''}>添加</button>\n </form>\n </div>\n </div>\n )}\n </div>\n )\n}\n","/**\n * Browser half: binds the `node-appearance` settings scope, paints the chat\n * flow from it (a single <style data-plugin-css> tag rebuilt on every\n * snapshot change), and registers the settings card.\n */\n\nimport type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\nimport type { SettingsScope } from '@deepseek-ai/dsh-client-runtime/client'\n// Type-only: the ctx.settingsScope Context merge and the slot's declaration.\n// Cross-plugin collaboration goes through cordis services; a value import\n// would fail the client bundle-purity gate.\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings-plugins/client'\nimport { buildCss, NODE_APPEARANCE_NS, STYLE_TAG_ID, type NodeAppearanceSettings } from './palette.ts'\nimport { NodeAppearanceRow, type NodeAppearanceRowFace } from './settings-card.tsx'\n\nexport const inject = ['slots', 'connection', 'remote', 'settingsScope']\n\n/** Upsert the plugin's paint stylesheet with the current snapshot's CSS. */\nfunction paint(scope: SettingsScope<NodeAppearanceSettings>): void {\n let tag = document.querySelector<HTMLStyleElement>(`style[data-plugin-css=\"${STYLE_TAG_ID}\"]`)\n if (tag === null) {\n tag = document.createElement('style')\n tag.dataset.plugin = NODE_APPEARANCE_NS\n tag.dataset.pluginCss = STYLE_TAG_ID\n document.head.appendChild(tag)\n }\n tag.textContent = buildCss(scope.getSnapshot().value)\n}\n\n/**\n * Mount the node-appearance plugin on the page.\n * @param ctx - client root context.\n */\nexport function apply(ctx: ClientContext): void {\n const scope = ctx.settingsScope.bind<NodeAppearanceSettings>({ namespace: NODE_APPEARANCE_NS })\n // Paint once from the current snapshot (defaults before the first Host read).\n paint(scope)\n ctx.effect(\n () => scope.subscribe(() => { paint(scope) }),\n 'dsh-node-appearance: repaint on settings change',\n )\n\n const face: NodeAppearanceRowFace = {\n hooks: { nodeAppearance: scope },\n setShowThinking: (show) => { void scope.set('showThinking', show) },\n setCategoryColor: (category, color) => {\n const value = scope.getSnapshot().value\n void scope.set('colors', { ...value?.colors, [category]: color })\n },\n setToolColor: (tool, color) => {\n const value = scope.getSnapshot().value\n void scope.set('toolColors', { ...value?.toolColors, [tool]: color })\n },\n removeToolColor: (tool) => {\n const value = scope.getSnapshot().value\n const toolColors = { ...value?.toolColors }\n delete toolColors[tool]\n void scope.set('toolColors', toolColors)\n },\n }\n\n // The General settings section stacks preference rows (locale → Language,\n // ui-theme → Appearance, dsh-skin → Skins). Node appearance is the same\n // kind of visual preference, so it registers here beside them.\n ctx.slots.inject('settings.general.item', () => ctx.slots.register({\n name: 'settings.general.item',\n id: NODE_APPEARANCE_NS,\n order: 30,\n inject: () => face,\n }, NodeAppearanceRow))\n}\n"],"mappings":"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"}
package/lib/index.js ADDED
@@ -0,0 +1,46 @@
1
+ import { installSettingsSection, settingsNamespace } from "@deepseek-ai/dsh-settings";
2
+ import z from "@deepseek-ai/schemastery";
3
+ //#region src/index.ts
4
+ const name = "dsh-node-appearance";
5
+ /** The settings namespace this plugin owns (spelled identically in both halves). */
6
+ const NODE_APPEARANCE_NS = settingsNamespace("node-appearance");
7
+ /** Initial palette: mid-luminance hues readable on both light and dark themes. */
8
+ const DEFAULT_COLORS = {
9
+ search: "#3b82f6",
10
+ agent: "#a855f7",
11
+ execute: "#f59e0b",
12
+ file: "#22c55e",
13
+ task: "#ec4899",
14
+ command: "#f97316",
15
+ thinking: "#c4b5fd",
16
+ context: "#8a9bb5",
17
+ other: "#64748b"
18
+ };
19
+ const Config = z.object({
20
+ showThinking: z.boolean().default(true),
21
+ colors: z.object({
22
+ search: z.string().default(DEFAULT_COLORS.search),
23
+ agent: z.string().default(DEFAULT_COLORS.agent),
24
+ execute: z.string().default(DEFAULT_COLORS.execute),
25
+ file: z.string().default(DEFAULT_COLORS.file),
26
+ task: z.string().default(DEFAULT_COLORS.task),
27
+ command: z.string().default(DEFAULT_COLORS.command),
28
+ thinking: z.string().default(DEFAULT_COLORS.thinking),
29
+ context: z.string().default(DEFAULT_COLORS.context),
30
+ other: z.string().default(DEFAULT_COLORS.other)
31
+ }).default(DEFAULT_COLORS),
32
+ toolColors: z.dict(z.string()).default({})
33
+ });
34
+ /**
35
+ * Install the plugin's settings section; the browser half consumes it.
36
+ * @param ctx - Host context.
37
+ * @param config - the composed entry config (becomes the section's base layer).
38
+ */
39
+ function apply(ctx, config) {
40
+ installSettingsSection(ctx, NODE_APPEARANCE_NS, Config, config, {
41
+ setSource: () => {},
42
+ onChange: () => {}
43
+ });
44
+ }
45
+ //#endregion
46
+ export { Config, DEFAULT_COLORS, NODE_APPEARANCE_NS, apply, name };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Browser half: binds the `node-appearance` settings scope, paints the chat
3
+ * flow from it (a single <style data-plugin-css> tag rebuilt on every
4
+ * snapshot change), and registers the settings card.
5
+ */
6
+ import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
7
+ export declare const inject: string[];
8
+ /**
9
+ * Mount the node-appearance plugin on the page.
10
+ * @param ctx - client root context.
11
+ */
12
+ export declare function apply(ctx: ClientContext): void;
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAU3E,eAAO,MAAM,MAAM,UAAqD,CAAA;AAcxE;;;GAGG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAqC9C"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Node appearance palette: category/tool mapping, default colors, and the pure
3
+ * CSS-rule generator that paints the chat flow. Browser-half owned; the Host
4
+ * half mirrors the same defaults through its schemastery schema (the two
5
+ * cannot share a value import — the browser bundle purity gate forbids
6
+ * cross-package value imports).
7
+ */
8
+ /** The settings namespace this plugin owns (spelled identically in both halves). */
9
+ export declare const NODE_APPEARANCE_NS = "node-appearance";
10
+ /** The plugin's style tag identity (removed/replaced on every re-paint). */
11
+ export declare const STYLE_TAG_ID = "node-appearance/rules";
12
+ /** One paintable node category. `command`, `thinking`, and `context` are non-tool rows. */
13
+ export type NodeCategory = 'search' | 'agent' | 'execute' | 'file' | 'task' | 'command' | 'thinking' | 'context' | 'other';
14
+ /** Accent color per category (CSS colors). */
15
+ export type NodeAppearanceColors = Record<NodeCategory, string>;
16
+ /** The settings value this plugin reads from its namespace scope. */
17
+ export interface NodeAppearanceSettings {
18
+ /** Show assistant reasoning blocks as Think rows; false hides them. */
19
+ showThinking?: boolean | undefined;
20
+ /** Per-category accent colors; missing keys fall back to defaults. */
21
+ colors?: Partial<NodeAppearanceColors> | undefined;
22
+ /** Per-tool accent overrides keyed by wire tool name. */
23
+ toolColors?: Record<string, string> | undefined;
24
+ }
25
+ /** Initial palette: mid-luminance hues readable on both light and dark themes. */
26
+ export declare const DEFAULT_COLORS: NodeAppearanceColors;
27
+ /** Wire tool name → category for the shipped mapping. */
28
+ export declare const TOOL_CATEGORIES: Record<Exclude<NodeCategory, 'command' | 'thinking' | 'context'>, readonly string[]>;
29
+ /**
30
+ * Accept a configured CSS color (hex, color functions) or reject it so an
31
+ * invalid value falls back to the category default instead of poisoning CSS.
32
+ * @param value - configured color string.
33
+ * @returns whether the value is a plausible CSS color literal.
34
+ */
35
+ export declare function isCssColor(value: string): boolean;
36
+ /** Resolve the effective palette from user settings merged over defaults. */
37
+ export declare function resolveColors(settings: NodeAppearanceSettings): NodeAppearanceColors;
38
+ /**
39
+ * Build the complete stylesheet text for one settings snapshot.
40
+ * @param settings - the namespace scope value (may be partial or undefined).
41
+ * @returns CSS text painting accent rows and (optionally) hiding Think rows.
42
+ */
43
+ export declare function buildCss(settings: NodeAppearanceSettings | undefined): string;
44
+ //# sourceMappingURL=palette.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"palette.d.ts","sourceRoot":"","sources":["../../../src/client/palette.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,oFAAoF;AACpF,eAAO,MAAM,kBAAkB,oBAAoB,CAAA;AAEnD,4EAA4E;AAC5E,eAAO,MAAM,YAAY,0BAAgC,CAAA;AAEzD,2FAA2F;AAC3F,MAAM,MAAM,YAAY,GACpB,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,CAAA;AAEnG,8CAA8C;AAC9C,MAAM,MAAM,oBAAoB,GAAG,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAAA;AAE/D,qEAAqE;AACrE,MAAM,WAAW,sBAAsB;IACrC,uEAAuE;IACvE,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAClC,sEAAsE;IACtE,MAAM,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG,SAAS,CAAA;IAClD,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAA;CAChD;AAED,kFAAkF;AAClF,eAAO,MAAM,cAAc,EAAE,oBAU5B,CAAA;AAED,yDAAyD;AACzD,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,OAAO,CAAC,YAAY,EAAE,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAOhH,CAAA;AAsBD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAKjD;AAED,6EAA6E;AAC7E,wBAAgB,aAAa,CAAC,QAAQ,EAAE,sBAAsB,GAAG,oBAAoB,CAUpF;AAOD;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,sBAAsB,GAAG,SAAS,GAAG,MAAM,CAyC7E"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The plugin's General-settings row: a "节点外观" title with the standard
3
+ * chevron, expanding to palette rows, tool overrides, and the Think
4
+ * visibility switch. Reads the namespace scope through the injected hooks
5
+ * face and writes through the injected actions; every change applies
6
+ * immediately. Drawn by hand (no value import from ui-settings-plugins — the
7
+ * client bundle purity gate forbids cross-plugin value imports), styled with
8
+ * the DSH design tokens like the shipped Appearance row.
9
+ */
10
+ import type { SettingsScopeSnapshot } from '@deepseek-ai/dsh-client-runtime/client';
11
+ import type { InjectFace, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
12
+ import { type NodeAppearanceSettings, type NodeCategory } from './palette.ts';
13
+ /** The row's inject face: the read-only scope snapshot plus write actions. */
14
+ export interface NodeAppearanceRowFace {
15
+ hooks: {
16
+ /** The bound settings-scope snapshot, rendered as useNodeAppearance. */
17
+ nodeAppearance: {
18
+ getSnapshot(): SettingsScopeSnapshot<NodeAppearanceSettings>;
19
+ subscribe(listener: () => void): () => void;
20
+ };
21
+ };
22
+ /** Write the Think visibility switch. */
23
+ setShowThinking(show: boolean): void;
24
+ /** Merge one category color into the palette section. */
25
+ setCategoryColor(category: NodeCategory, color: string): void;
26
+ /** Merge one tool override into the toolColors section. */
27
+ setToolColor(tool: string, color: string): void;
28
+ /** Remove one tool override. */
29
+ removeToolColor(tool: string): void;
30
+ }
31
+ /** Props the renderer binds for this General row. */
32
+ export type NodeAppearanceRowProps = PropsRuntime<'settings.general.item'> & InjectFace<NodeAppearanceRowFace>;
33
+ /**
34
+ * Render the node-appearance General row.
35
+ * @param props - the injected scope snapshot hook and write actions.
36
+ * @returns the row, always visible once its namespace is served.
37
+ */
38
+ export declare function NodeAppearanceRow({ useNodeAppearance, setShowThinking, setCategoryColor, setToolColor, removeToolColor }: NodeAppearanceRowProps): import("react").JSX.Element;
39
+ //# sourceMappingURL=settings-card.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings-card.d.ts","sourceRoot":"","sources":["../../../src/client/settings-card.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAA;AAEnF,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AAEhF,OAAO,EAEL,KAAK,sBAAsB,EAAE,KAAK,YAAY,EAC/C,MAAM,cAAc,CAAA;AAGrB,8EAA8E;AAC9E,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE;QACL,wEAAwE;QACxE,cAAc,EAAE;YACd,WAAW,IAAI,qBAAqB,CAAC,sBAAsB,CAAC,CAAA;YAC5D,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;SAC5C,CAAA;KACF,CAAA;IACD,yCAAyC;IACzC,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAA;IACpC,yDAAyD;IACzD,gBAAgB,CAAC,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7D,2DAA2D;IAC3D,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/C,gCAAgC;IAChC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CACpC;AAED,qDAAqD;AACrD,MAAM,MAAM,sBAAsB,GAChC,YAAY,CAAC,uBAAuB,CAAC,GACnC,UAAU,CAAC,qBAAqB,CAAC,CAAA;AAkBrC;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,iBAAiB,EAAE,eAAe,EAAE,gBAAgB,EAAE,YAAY,EAAE,eAAe,EAAE,EAAE,sBAAsB,+BAmGhJ"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Host half of dsh-node-appearance: registers the `node-appearance` settings
3
+ * namespace carrying the palette and the Think-visibility switch. The browser
4
+ * half reads the same namespace through `ctx.settingsScope` and paints the
5
+ * chat flow from it — see `src/client/`.
6
+ */
7
+ import type { Context } from '@deepseek-ai/cordis';
8
+ import z from '@deepseek-ai/schemastery';
9
+ export declare const name = "dsh-node-appearance";
10
+ /** The settings namespace this plugin owns (spelled identically in both halves). */
11
+ export declare const NODE_APPEARANCE_NS: import("@deepseek-ai/dsh-settings").SettingsNamespace;
12
+ /** Accent color per category (CSS colors). */
13
+ export interface NodeAppearanceColors {
14
+ search: string;
15
+ agent: string;
16
+ execute: string;
17
+ file: string;
18
+ task: string;
19
+ command: string;
20
+ thinking: string;
21
+ context: string;
22
+ other: string;
23
+ }
24
+ /** The plugin configuration: palette plus the Think-visibility switch. */
25
+ export interface Config {
26
+ /** Show assistant reasoning blocks as Think rows; false hides them on the frontend. */
27
+ showThinking: boolean;
28
+ /** Per-category accent colors. */
29
+ colors: NodeAppearanceColors;
30
+ /** Per-tool accent overrides keyed by wire tool name. */
31
+ toolColors: Record<string, string>;
32
+ }
33
+ /** Initial palette: mid-luminance hues readable on both light and dark themes. */
34
+ export declare const DEFAULT_COLORS: NodeAppearanceColors;
35
+ export declare const Config: z<Config>;
36
+ /**
37
+ * Install the plugin's settings section; the browser half consumes it.
38
+ * @param ctx - Host context.
39
+ * @param config - the composed entry config (becomes the section's base layer).
40
+ */
41
+ export declare function apply(ctx: Context, config: Config): void;
42
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAElD,OAAO,CAAC,MAAM,0BAA0B,CAAA;AAExC,eAAO,MAAM,IAAI,wBAAwB,CAAA;AAEzC,oFAAoF;AACpF,eAAO,MAAM,kBAAkB,uDAAuC,CAAA;AAEtE,8CAA8C;AAC9C,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;CACd;AAED,0EAA0E;AAC1E,MAAM,WAAW,MAAM;IACrB,uFAAuF;IACvF,YAAY,EAAE,OAAO,CAAA;IACrB,kCAAkC;IAClC,MAAM,EAAE,oBAAoB,CAAA;IAC5B,yDAAyD;IACzD,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACnC;AAED,kFAAkF;AAClF,eAAO,MAAM,cAAc,EAAE,oBAU5B,CAAA;AAED,eAAO,MAAM,MAAM,EAAE,CAAC,CAAC,MAAM,CAc3B,CAAA;AAEF;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAKxD"}
package/package.json ADDED
@@ -0,0 +1,92 @@
1
+ {
2
+ "name": "@max-null/dsh-node-appearance",
3
+ "version": "0.1.0",
4
+ "description": "会话节点外观插件:按节点类型/工具名着色的可配置配色,思考过程显示开关(DeepSeek Harness Web GUI)",
5
+ "license": "MIT",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "type": "module",
10
+ "main": "lib/index.js",
11
+ "types": "lib/types/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./lib/types/index.d.ts",
15
+ "default": "./lib/index.js"
16
+ },
17
+ "./client": {
18
+ "types": "./lib/types/client/index.d.ts",
19
+ "default": "./lib/client.js"
20
+ },
21
+ "./src/*": "./src/*",
22
+ "./package.json": "./package.json"
23
+ },
24
+ "files": [
25
+ "lib",
26
+ "cordis.patch.yml",
27
+ "README.md"
28
+ ],
29
+ "dsh": {
30
+ "bundle": {
31
+ "patch": "./cordis.patch.yml"
32
+ },
33
+ "client": {
34
+ "inject": [
35
+ "@deepseek-ai/dsh-client-runtime",
36
+ "@deepseek-ai/dsh-client-ui-settings",
37
+ "@deepseek-ai/dsh-client-ui-settings-plugins"
38
+ ],
39
+ "platform": "web",
40
+ "immediately": true
41
+ }
42
+ },
43
+ "keywords": [
44
+ "deepseek",
45
+ "harness",
46
+ "dsh",
47
+ "dsh-plugin",
48
+ "deepseek-harness",
49
+ "web-ui",
50
+ "chat",
51
+ "theme",
52
+ "color",
53
+ "plugin",
54
+ "cordis"
55
+ ],
56
+ "repository": {
57
+ "type": "git",
58
+ "url": "git+https://github.com/Max-Null/dsh-node-appearance.git"
59
+ },
60
+ "scripts": {
61
+ "build": "tsc -p tsconfig.build.json && tsdown",
62
+ "watch": "tsdown --watch",
63
+ "typecheck": "tsc --noEmit -p tsconfig.json",
64
+ "test": "vitest run",
65
+ "prepublishOnly": "npm run build && npm test"
66
+ },
67
+ "dependencies": {
68
+ "@deepseek-ai/schemastery": ">=0.1.0-rc.1"
69
+ },
70
+ "peerDependencies": {
71
+ "@deepseek-ai/cordis": "^4.0.1",
72
+ "@deepseek-ai/dsh-client-runtime": ">=0.1.0-rc.7",
73
+ "@deepseek-ai/dsh-client-ui-settings": ">=0.1.0-rc.7",
74
+ "@deepseek-ai/dsh-client-ui-settings-plugins": ">=0.1.0-rc.7",
75
+ "@deepseek-ai/dsh-settings": ">=0.1.0-rc.7",
76
+ "react": "^18.2.0"
77
+ },
78
+ "devDependencies": {
79
+ "@deepseek-ai/cordis": "^4.0.1",
80
+ "@deepseek-ai/dsh-client-runtime": ">=0.1.0-rc.7",
81
+ "@deepseek-ai/dsh-client-ui-settings": ">=0.1.0-rc.7",
82
+ "@deepseek-ai/dsh-client-ui-settings-plugins": ">=0.1.0-rc.7",
83
+ "@deepseek-ai/dsh-settings": ">=0.1.0-rc.7",
84
+ "@types/node": "^26.0.0",
85
+ "@types/react": "~18.3.1",
86
+ "lightningcss": "^1.32.0",
87
+ "react": "^18.2.0",
88
+ "tsdown": "^0.22.2",
89
+ "typescript": "^6.0.3",
90
+ "vitest": "^4.1.8"
91
+ }
92
+ }