dsh-opencode-palette 1.4.1 → 1.5.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/README.md CHANGED
@@ -5,6 +5,7 @@
5
5
  **The complete opencode palette for DeepSeek Harness — 34 official themes, one click.**
6
6
 
7
7
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](../LICENSE)
8
+ [![npm](https://img.shields.io/npm/v/dsh-opencode-palette)](https://www.npmjs.com/package/dsh-opencode-palette)
8
9
  [![opencode](https://img.shields.io/badge/themes-opencode%20v1.18.12-orange)](https://github.com/anomalyco/opencode)
9
10
  [![tests](https://img.shields.io/badge/tests-20%2F20-green)]()
10
11
 
@@ -24,15 +25,9 @@ Then install the plugin into your profile:
24
25
  dsh plugin --profile web add dsh-opencode-palette
25
26
  ```
26
27
 
27
- Register the entry (hot-reloaded, no restart): append to `~/.dsh/profiles/web/cordis.patch.yml`:
28
+ That's it — **zero configuration**: this plugin uses DSH's official bundle mechanism — it ships its own `cordis.patch.yml` (declared via `dsh.bundle.patch`), so `dsh plugin add` automatically joins the package into the profile's `dsh.profile.bundles` layer stack, where the DSH loader assembles it at startup; `dsh plugin remove` removes it automatically. No manual file editing, and no pnpm build scripts (no postinstall, so pnpm v10 never blocks it). Restart DSH (or refresh the browser page) and the plugin is on, using the official `opencode` theme (deep black with orange / blue / violet).
28
29
 
29
- ```yaml
30
- - insert:
31
- - id: opencode-palette
32
- name: 'dsh-opencode-palette'
33
- ```
34
-
35
- Refresh the browser page — the plugin is on, using the official `opencode` theme (deep black with orange / blue / violet).
30
+ > **Upgrading from 1.4.x or earlier**: old versions wrote a registration block into `~/.dsh/profiles/web/cordis.patch.yml` via postinstall. Before upgrading, delete the `opencode-palette` block from that file (a leftover would duplicate the bundle registration), then re-run `dsh plugin --profile web add dsh-opencode-palette`.
36
31
 
37
32
  ## What it does
38
33
 
@@ -0,0 +1,7 @@
1
+ # dsh-opencode-palette bundle 层:插入主题引擎插件条目。
2
+ # 由 dsh plugin --profile <name> add 自动装配(包声明 dsh.bundle.patch),
3
+ # 无需手动编辑任何文件;dsh plugin remove 时自动移除。
4
+ - insert:
5
+ - id: opencode-palette
6
+ name: 'dsh-opencode-palette'
7
+ config: {}
package/lib/client.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * dsh-opencode-palette v1.4.1 — 浏览器半(构建产物,勿手改)
2
+ * dsh-opencode-palette v1.5.1 — 浏览器半(构建产物,勿手改)
3
3
  * 数据驱动管线:opencode v1.18.12 官方主题 JSON → 颜色解析 → DSH 适配注入
4
4
  * 源:src/engine/* + runtime/client.mjs(npm run build 重新生成)
5
5
  */
@@ -8008,8 +8008,8 @@ const I18N = {
8008
8008
  enableTitle: { zh: '点击启用主题', en: 'Click to enable' },
8009
8009
  typography: { zh: '字体字号', en: 'Typography' },
8010
8010
  bodyStyle: { zh: '正文样式', en: 'Body style' },
8011
- mono: { zh: '等宽字体(终端风)', en: 'Monospace (terminal)' },
8012
- sans: { zh: '常规字体(界面风)', en: 'Regular (UI)' },
8011
+ mono: { zh: '等宽(终端风)', en: 'Monospace (terminal)' },
8012
+ sans: { zh: '常规(界面风)', en: 'Regular (UI)' },
8013
8013
  fontSize: { zh: '字号', en: 'Font size' },
8014
8014
  codeFont: { zh: '代码字体', en: 'Code font' },
8015
8015
  fontPreview: { zh: '等宽', en: 'mono' },
@@ -8146,20 +8146,23 @@ function createClient(slotTarget) {
8146
8146
  const h = react.createElement
8147
8147
  const [query, setQuery] = react.useState('')
8148
8148
  const [fontOpen, setFontOpen] = react.useState(false)
8149
+ const [sizeOpen, setSizeOpen] = react.useState(false)
8149
8150
  const fontRef = react.useRef(null)
8151
+ const sizeRef = react.useRef(null)
8150
8152
  // UI 快照:所有引擎动作后 setUi(props.getState()) 重同步,避免受控控件显示值漂移
8151
8153
  const [ui, setUi] = react.useState(props.getState())
8152
8154
  const st = ui
8153
8155
 
8154
8156
  // 字体下拉:点击外部关闭
8155
8157
  react.useEffect(function () {
8156
- if (!fontOpen) return
8158
+ if (!fontOpen && !sizeOpen) return
8157
8159
  function onDoc(e) {
8158
8160
  if (fontRef.current && !fontRef.current.contains(e.target)) setFontOpen(false)
8161
+ if (sizeRef.current && !sizeRef.current.contains(e.target)) setSizeOpen(false)
8159
8162
  }
8160
8163
  document.addEventListener('mousedown', onDoc)
8161
8164
  return function () { document.removeEventListener('mousedown', onDoc) }
8162
- }, [fontOpen])
8165
+ }, [fontOpen, sizeOpen])
8163
8166
 
8164
8167
  // 语言切换:DSH 界面语言变化时重渲染(文案跟随)
8165
8168
  react.useEffect(function () {
@@ -8197,9 +8200,26 @@ function createClient(slotTarget) {
8197
8200
  }, opt.label)
8198
8201
  }))
8199
8202
  }
8200
- const field = function (labelText, node) {
8201
- return h('div', { style: { display: 'flex', flexDirection: 'column', gap: 5 } },
8202
- h('span', { style: fieldLabel }, labelText), node)
8203
+ // 通用下拉(字号 / 代码字体):紧凑按钮 + 弹出菜单,对齐 setup-panel 样例
8204
+ const dd = function (open, setOpen, ref, labelNode, items) {
8205
+ return h('div', { ref: ref, style: { position: 'relative' } }, [
8206
+ h('button', {
8207
+ onClick: function () { setOpen(!open) },
8208
+ style: {
8209
+ display: 'flex', alignItems: 'center', gap: 8,
8210
+ background: 'var(--dsw-alias-bg-layer-2)', border: '1px solid var(--dsw-alias-border-l1)',
8211
+ borderRadius: 6, padding: '5px 10px', fontSize: 12, cursor: 'pointer',
8212
+ color: base, fontFamily: 'var(--dsw-font-family)',
8213
+ },
8214
+ }, [labelNode, h('span', { style: { color: muted } }, '▾')]),
8215
+ open ? h('div', {
8216
+ style: {
8217
+ position: 'absolute', top: 'calc(100% + 4px)', left: 0, zIndex: 20,
8218
+ background: 'var(--dsw-alias-bg-overlay)', border: '1px solid var(--dsw-alias-border-l1)',
8219
+ borderRadius: 8, minWidth: 200, padding: 4, boxShadow: '0 8px 24px rgba(0,0,0,0.5)',
8220
+ },
8221
+ }, items) : null,
8222
+ ])
8203
8223
  }
8204
8224
  const dot = function (color, size) {
8205
8225
  return h('span', { style: { width: size, height: size, borderRadius: '50%', background: color || '#555', display: 'inline-block', flex: 'none' } })
@@ -8228,43 +8248,6 @@ function createClient(slotTarget) {
8228
8248
  ])
8229
8249
  }
8230
8250
 
8231
- // 自定义字体下拉(每个选项用其字体预览)
8232
- const fontDropdown = h('div', { ref: fontRef, style: { position: 'relative' } }, [
8233
- h('button', {
8234
- onClick: function () { setFontOpen(!fontOpen) },
8235
- style: {
8236
- display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10,
8237
- background: 'var(--dsw-alias-bg-layer-2)', border: '1px solid var(--dsw-alias-border-l1)',
8238
- borderRadius: 6, padding: '6px 12px', fontSize: 12, cursor: 'pointer', minWidth: 200,
8239
- color: base, fontFamily: 'var(--dsw-font-family)',
8240
- },
8241
- }, [
8242
- h('span', { style: { fontFamily: FONTS[st.fontKey] || FONTS['JetBrains Mono'] } }, st.fontKey),
8243
- h('span', { style: { color: muted } }, '▾'),
8244
- ]),
8245
- fontOpen ? h('div', {
8246
- style: {
8247
- position: 'absolute', top: 'calc(100% + 4px)', left: 0, zIndex: 20,
8248
- background: 'var(--dsw-alias-bg-overlay)', border: '1px solid var(--dsw-alias-border-l1)',
8249
- borderRadius: 8, minWidth: 230, padding: 4, boxShadow: '0 8px 24px rgba(0,0,0,0.5)',
8250
- },
8251
- }, Object.keys(FONTS).map(function (k) {
8252
- const on = k === st.fontKey
8253
- return h('div', {
8254
- key: k,
8255
- onClick: function () {
8256
- props.refresh(st.mode, st.size, k)
8257
- setUi(props.getState())
8258
- setFontOpen(false)
8259
- },
8260
- style: {
8261
- padding: '6px 10px', fontSize: 12, borderRadius: 5, cursor: 'pointer',
8262
- background: on ? 'rgba(255,255,255,0.1)' : 'transparent',
8263
- color: on ? base : muted,
8264
- },
8265
- }, h('span', { style: { fontFamily: FONTS[k] } }, k + ' — Aa ' + tr('fontPreview')))
8266
- })) : null,
8267
- ])
8268
8251
 
8269
8252
 
8270
8253
  return h('div', { style: { display: 'flex', flexDirection: 'column', gap: 14, maxWidth: 920 } }, [
@@ -8299,14 +8282,43 @@ function createClient(slotTarget) {
8299
8282
  h('div', { style: secTitle }, [
8300
8283
  h('span', null, tr('typography')),
8301
8284
  ]),
8302
- h('div', { style: { display: 'flex', alignItems: 'flex-end', gap: 20, flexWrap: 'wrap' } }, [
8303
- field(tr('bodyStyle'), seg(st.mode, [
8285
+ h('div', { style: { display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' } }, [
8286
+ seg(st.mode, [
8304
8287
  { value: 'mono', label: tr('mono') },
8305
8288
  { value: 'tui', label: tr('sans') },
8306
- ], function (v) { props.refresh(v, st.size, st.fontKey); setUi(props.getState()) })),
8307
- field(tr('fontSize'), seg(st.size, [11, 12, 13, 14, 15, 16, 17, 18].map(function (s) { return { value: s, label: s + 'px' } }),
8308
- function (v) { props.refresh(st.mode, Number(v), st.fontKey); setUi(props.getState()) })),
8309
- field(tr('codeFont'), fontDropdown),
8289
+ ], function (v) { props.refresh(v, st.size, st.fontKey); setUi(props.getState()) }),
8290
+ dd(sizeOpen, setSizeOpen, sizeRef,
8291
+ h('span', null, tr('fontSize') + ' ' + st.size + 'px'),
8292
+ [11, 12, 13, 14, 15, 16, 17, 18].map(function (s) {
8293
+ const on = s === st.size
8294
+ return h('div', {
8295
+ key: String(s),
8296
+ onClick: function () { props.refresh(st.mode, s, st.fontKey); setUi(props.getState()); setSizeOpen(false) },
8297
+ style: {
8298
+ padding: '6px 10px', fontSize: 12, borderRadius: 5, cursor: 'pointer',
8299
+ background: on ? 'rgba(255,255,255,0.1)' : 'transparent',
8300
+ color: on ? base : muted,
8301
+ },
8302
+ }, String(s) + 'px')
8303
+ })),
8304
+ dd(fontOpen, setFontOpen, fontRef,
8305
+ h('span', { style: { fontFamily: FONTS[st.fontKey] || FONTS['JetBrains Mono'] } }, st.fontKey),
8306
+ Object.keys(FONTS).map(function (k) {
8307
+ const on = k === st.fontKey
8308
+ return h('div', {
8309
+ key: k,
8310
+ onClick: function () {
8311
+ props.refresh(st.mode, st.size, k)
8312
+ setUi(props.getState())
8313
+ setFontOpen(false)
8314
+ },
8315
+ style: {
8316
+ padding: '6px 10px', fontSize: 12, borderRadius: 5, cursor: 'pointer',
8317
+ background: on ? 'rgba(255,255,255,0.1)' : 'transparent',
8318
+ color: on ? base : muted,
8319
+ },
8320
+ }, h('span', { style: { fontFamily: FONTS[k] } }, k + ' — Aa ' + tr('fontPreview')))
8321
+ })),
8310
8322
  ]),
8311
8323
  // ── 主题选择(色系分组标签 + mini 芯片)──
8312
8324
  h('div', { style: secTitle }, [
package/lib/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * dsh-opencode-palette v1.4.1 — 宿主半(no-op)
2
+ * dsh-opencode-palette v1.5.1 — 宿主半(no-op)
3
3
  *
4
4
  * 主题的全部工作在浏览器端(./client.js)完成:
5
5
  * - 34 个 opencode 主题(33 内置 JSON + system)经数据驱动管线解析
package/package.json CHANGED
@@ -1,22 +1,19 @@
1
1
  {
2
2
  "name": "dsh-opencode-palette",
3
- "version": "1.4.1",
4
- "description": "DSH Web 多主题引擎:完整支持 opencode TUI 全部 34 个主题(33 内置 + system)。数据驱动管线:主题 JSON → 颜色解析 → DSH 适配注入;设置面板即时切换、持久化。",
3
+ "version": "1.5.1",
4
+ "description": "让 DeepSeek Harness 穿上 34 款经典皮肤——东京的霓虹夜色、德古拉的暗红月光、复古工坊的暖黄灯火、黑客帝国的数字雨、玫瑰松林间的风……一键换肤,即点即换,重启不丢。34 legendary skins for DeepSeek Harness — tokyonight's neon dusk, dracula's crimson moon, gruvbox's retro glow, the matrix's digital rain, rose-pine's rosewood calm… one click, instant, persisted.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "exports": {
8
8
  ".": "./lib/index.js",
9
9
  "./client": "./lib/client.js",
10
+ "./cordis.patch.yml": "./cordis.patch.yml",
10
11
  "./package.json": "./package.json"
11
12
  },
12
13
  "files": [
13
14
  "lib",
14
- "scripts"
15
+ "cordis.patch.yml"
15
16
  ],
16
- "scripts": {
17
- "postinstall": "node scripts/install-patch.cjs",
18
- "preuninstall": "node scripts/uninstall-patch.cjs"
19
- },
20
17
  "keywords": [
21
18
  "dsh",
22
19
  "deepseek-harness",
@@ -28,6 +25,9 @@
28
25
  "multi-theme"
29
26
  ],
30
27
  "dsh": {
28
+ "bundle": {
29
+ "patch": "./cordis.patch.yml"
30
+ },
31
31
  "client": {
32
32
  "platform": "web",
33
33
  "immediately": true,
@@ -1,58 +0,0 @@
1
- /**
2
- * dsh-opencode-palette postinstall — 自动注册到 DSH profile(npm 标准安装即完成,无需手动编辑 patch)
3
- *
4
- * 行为:
5
- * 1. 定位 $DSH_HOME/profiles/web/cordis.patch.yml(DSH_HOME 未设 → ~/.dsh/profiles/web/…)
6
- * 2. 文件不存在 → 跳过(非 DSH 环境不打扰)
7
- * 3. 已含注册行 → 跳过(幂等,重装/升级不叠加)
8
- * 4. 否则文件末尾追加注册块(- insert: id opencode-palette / name dsh-opencode-palette)
9
- * 容错:任何异常只警告,绝不令 npm install 失败(退出码恒 0)。
10
- */
11
- const fs = require('fs')
12
- const path = require('path')
13
-
14
- const PLUGIN_ID = 'opencode-palette'
15
- const PLUGIN_NAME = 'dsh-opencode-palette'
16
-
17
- function findPatchPath() {
18
- const home = process.env.DSH_HOME
19
- || (process.platform === 'win32' ? process.env.USERPROFILE : process.env.HOME)
20
- if (!home) return null
21
- return path.join(home, '.dsh', 'profiles', 'web', 'cordis.patch.yml')
22
- }
23
-
24
- const REGISTER_BLOCK =
25
- '\n# dsh-opencode-palette(npm 安装 · 自动注册)\n' +
26
- '- insert:\n' +
27
- ' - id: ' + PLUGIN_ID + '\n' +
28
- " name: '" + PLUGIN_NAME + "'\n"
29
-
30
- function main() {
31
- const patch = findPatchPath()
32
- if (!patch) {
33
- console.log('[dsh-opencode-palette] 未找到 DSH_HOME,跳过自动注册(非 DSH 环境)')
34
- return
35
- }
36
- let existed = true
37
- let text = ''
38
- try {
39
- text = fs.readFileSync(patch, 'utf8')
40
- } catch (e) {
41
- existed = false
42
- }
43
- if (existed && new RegExp("id\\s*:\\s*['\"]?" + PLUGIN_ID + "['\"]?").test(text)) {
44
- console.log('[dsh-opencode-palette] 已在 cordis.patch.yml 注册,跳过')
45
- return
46
- }
47
- try {
48
- fs.mkdirSync(path.dirname(patch), { recursive: true })
49
- fs.appendFileSync(patch, REGISTER_BLOCK, 'utf8')
50
- console.log('[dsh-opencode-palette] 已自动注册到 ' + patch + '(刷新浏览器页面即生效)')
51
- } catch (e) {
52
- console.warn('[dsh-opencode-palette] 自动注册失败(不影响本包安装):' + String((e && e.message) || e))
53
- }
54
- }
55
-
56
- try { main() } catch (e) {
57
- console.warn('[dsh-opencode-palette] postinstall 异常(已忽略):' + String((e && e.message) || e))
58
- }
@@ -1,50 +0,0 @@
1
- /**
2
- * dsh-opencode-palette preuninstall — 自动从 DSH profile 移除注册(npm remove 即完成,无需手动编辑 patch)
3
- * 幂等:不存在注册块 → 直接返回;任何异常只警告,退出码恒 0。
4
- */
5
- const fs = require('fs')
6
- const path = require('path')
7
-
8
- const PLUGIN_ID = 'opencode-palette'
9
-
10
- function findPatchPath() {
11
- const home = process.env.DSH_HOME
12
- || (process.platform === 'win32' ? process.env.USERPROFILE : process.env.HOME)
13
- if (!home) return null
14
- return path.join(home, '.dsh', 'profiles', 'web', 'cordis.patch.yml')
15
- }
16
-
17
- // 匹配注册块:可选注释行 + insert 块(容错 name 引号/缩进变化)
18
- const BLOCK_RE = new RegExp(
19
- '(?:^|\\n)\\s*# dsh-opencode-palette[^\\n]*\\n' +
20
- '\\s*- insert:\\n' +
21
- '\\s*\\s*- id:\\s*' + PLUGIN_ID + '\\s*\\n' +
22
- "\\s*\\s*name:\\s*['\"]?dsh-opencode-palette['\"]?\\s*\\n",
23
- 'g'
24
- )
25
-
26
- function main() {
27
- const patch = findPatchPath()
28
- if (!patch) return
29
- let text = ''
30
- try {
31
- text = fs.readFileSync(patch, 'utf8')
32
- } catch (e) {
33
- return // 文件不存在 → 无需清理
34
- }
35
- const next = text.replace(BLOCK_RE, '\n')
36
- if (next === text) {
37
- console.log('[dsh-opencode-palette] 未在 cordis.patch.yml 找到注册块,无需清理')
38
- return
39
- }
40
- try {
41
- fs.writeFileSync(patch, next, 'utf8')
42
- console.log('[dsh-opencode-palette] 已从 cordis.patch.yml 移除注册(卸载完成)')
43
- } catch (e) {
44
- console.warn('[dsh-opencode-palette] 移除注册失败:' + String((e && e.message) || e))
45
- }
46
- }
47
-
48
- try { main() } catch (e) {
49
- console.warn('[dsh-opencode-palette] preuninstall 异常(已忽略):' + String((e && e.message) || e))
50
- }