dsh-multiline-input 0.1.0 → 0.1.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/CHANGELOG.md CHANGED
@@ -1,12 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.1 — 2026-10-29
4
+
5
+ 修复 0.1.0 的 tab 压住输入框上方卡片的问题:[npm](https://www.npmjs.com/package/dsh-multiline-input) · [GitHub Release](https://github.com/ZilongYang/dsh-multiline-input/releases/tag/v0.1.1)
6
+
7
+ - tab 提示从 `conversation.input.overlay` 浮层改到 `conversation.input.dock` 座位里的一行(`order: 1000`,紧贴输入卡片上方),占 24px 真实布局:不再覆盖目标条(GoalBar)的文案与暂停 / 编辑 / 清除按钮,也不会被待办、队列、额度横条等卡片撞到。
8
+ - tab 行按运行时读到的 composer 栈 `row-gap` 自适应(dock 6px / hero 8px),底边始终与卡片上沿齐平,外观与 0.1.0 一致(仍是「卡片肩上的 tab」)。
9
+ - 观感变化:输入区比 0.1.0 高约 18px(消息区相应少 18px),composer 上方的 dock 卡片整体上移 24px。
10
+ - 测试:新增「tab 座位必须是 `conversation.input.dock`、且 order 排在全部内置条目之后」的断言。
11
+
3
12
  ## 0.1.0 — 2026-10-28
4
13
 
5
- 首个版本。
14
+ 首个公开发布版本:[npm](https://www.npmjs.com/package/dsh-multiline-input) · [GitHub Release](https://github.com/ZilongYang/dsh-multiline-input/releases/tag/v0.1.0) · [市场 PR #9938](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/pull/9938)(条目校验已过,等仓库满 24h 自动合并门槛)。
6
15
 
7
16
  - 对话输入框单行 / 多行双态切换,模式按设备持久化。
8
17
  - 通过 DSH 官方快捷键服务注册 `multiline-input.toggle`:桌面默认 `Cmd+E`(macOS)/ `Ctrl+E`(Windows / Linux),可在「设置 → 通用 → 快捷键」改绑、恢复默认、查看冲突。
9
- - 卡片顶部常驻提示条(与卡片同底色、底部淡分割线):实时显示当前生效键位与目标模式;点击可切换。
18
+ - 输入框上沿常驻 tab 提示(与卡片同底色、底部淡分割线):实时显示当前生效键位与目标模式;点击可切换。
10
19
  - 多行模式最小高度 4 行,内容变多自动长高(沿用 DSH 的 336px 滚动上限)。
11
20
  - 多行模式 Enter 换行、Shift+Enter 发送;`Cmd/Ctrl+Enter`、斜杠 / 引用菜单、输入法组合态保持原有语义。
12
21
  - 卸载 / 禁用后恢复原生输入框,无 DOM、样式、监听残留。
22
+ - 市场素材:`screenshots.json` + `assets/` 两张截图(1360 宽)。
package/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # dsh-multiline-input
2
2
 
3
+ > 官网 / Website: <https://dsh.zlmix.com/multiline-input/>
4
+
5
+ [npm](https://www.npmjs.com/package/dsh-multiline-input) · [GitHub](https://github.com/ZilongYang/dsh-multiline-input) · [Release v0.1.1](https://github.com/ZilongYang/dsh-multiline-input/releases/tag/v0.1.1) · [市场 PR #9938](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/pull/9938)
6
+
3
7
  把 DeepSeek Harness 的对话输入框在**单行 / 多行**之间切换的 Web 客户端插件。
4
8
 
5
9
  - 默认快捷键 **Cmd+E(macOS)/ Ctrl+E(Windows / Linux)**;输入框上沿有一个与输入框同底色的小 **tab** 显示当前快捷键与目标模式,点击即可切换。
@@ -45,6 +49,7 @@ pnpm add link:/absolute/path/to/dsh-multiline-input
45
49
 
46
50
  - 按 `Cmd+E` / `Ctrl+E`(或点击 tab)在两种模式间循环;切换不清空草稿、不抢焦点。
47
51
  - tab **常驻显示**,输入文字时也不隐藏;多行时显示 `Shift+Enter 提交 · Cmd+E 单行输入`,单行时显示 `Cmd+E 多行输入`。
52
+ - tab 是输入框上方**独立的一行**(占 24px):目标条、待办、队列、额度横条等 DSH 卡片排在它上面,不会被遮住;目标条的暂停 / 编辑 / 清除按钮也能正常点击。
48
53
 
49
54
  ## 修改快捷键
50
55
 
@@ -70,7 +75,7 @@ tab 实时跟随当前生效的组合;把绑定清空后只显示「多行输
70
75
 
71
76
  - **模式状态**:模块级 store + `localStorage['dsh-multiline-input.mode.v1']`,投影为 `<html data-dsh-ml-input="multi">`;
72
77
  - **高度**:一条 CSS 规则给 `[data-composer-card] [data-composer-input][contenteditable="true"]` 设 `min-height: calc(4 * 1lh + 8px)`,不碰 DSH 的滚动上限;
73
- - **tab 提示**:注册到 `conversation.input.overlay` 座位,绝对定位到卡片上沿右侧(`bottom: 100%`,同底色 + 底部 0.5px 细线),宽度随文字;
78
+ - **tab 提示**:注册到 `conversation.input.dock` 座位(`order: 1000`,排在 todo 0 / 额度横条 5 / 目标条 10 / 队列 20 之后),成为输入卡片正上方**独立的一行**:整行只有 24px,宽度与卡片同列、tab 右对齐到卡片右边缘;`margin-bottom` 吃掉栈的行距(dock 态 6px、hero 态 8px,运行时按实际 `row-gap` 同步),让 tab 底边与卡片上沿齐平、视觉上仍是「卡片肩上的 tab」,但因为占位而**不可能压到任何上方卡片**。旧实现走 `conversation.input.overlay` 浮层(`bottom: 100%` + `z-index: 2`),会盖住目标条右侧的文案与暂停 / 编辑 / 清除按钮;
74
79
  - **Enter / Shift+Enter**:在 document 捕获阶段拦下多行模式下的 `Enter`,向编辑器重放一个翻转了 `shiftKey` 的合成按键——仍由 DSH 自己的 composer keymap 决定「换行」还是「提交」,撤销历史、输入法保护、busy-enter(queue/steer)、菜单仲裁全部保持原逻辑;
75
80
  - **快捷键**:通过 `ctx.shortcuts.register()` 注册命令,编辑与持久化完全交给 DSH。
76
81
 
@@ -78,11 +83,12 @@ tab 实时跟随当前生效的组合;把绑定清空后只显示「多行输
78
83
 
79
84
  | 契约 | 源码文件 |
80
85
  |---|---|
81
- | 输入卡片 / 覆盖层锚点 | `packages/client/ui-conversation/src/client/skeleton/InputBar.tsx` |
86
+ | 输入卡片 / 覆盖层锚点 | `packages/client/ui-conversation/src/client/skeleton/InputBar.tsx`、`InputBar.module.css` |
87
+ | composer 卡片栈(`conversation.input.dock`) | `packages/client/ui-conversation/src/client/skeleton/ConversationContent.tsx`、`ConversationRoot.module.css` |
82
88
  | 可编辑面 / 占位符 | `packages/client/ui-conversation/src/client/input/editor/DraftEditor.tsx`、`ComposerContentEditable.tsx` |
83
89
  | Enter 语义 | `packages/client/ui-conversation/src/client/input/editor/keymap.ts` |
84
90
  | 公开输入动作面 | `packages/client/ui-conversation/src/client/contract/input.ts` |
85
- | 座位与标准 props | `packages/client/ui-conversation/src/client/contract/slots.ts` |
91
+ | 座位与标准 props | `packages/client/ui-conversation/src/client/contract/slots.ts`、`packages/extensions/cordis-client-runner/src/client/slot-catalog.ts` |
86
92
  | 快捷键命令契约 | `packages/client/shortcuts/src/client/types.ts`、`binding.ts` |
87
93
  | 客户端清单 | `packages/util/package-manifest/src/types.ts`、`packages/client/modules/README.zh.md` |
88
94
 
@@ -90,6 +96,7 @@ tab 实时跟随当前生效的组合;把绑定清空后只显示「多行输
90
96
 
91
97
  - 在 DSH **0.2.1-rc.3**(桌面版 / Web)验证;`dsh.compatibility.dsh` = `>=0.2.1-rc.3 <0.3.0-0`。
92
98
  - 依赖 composer 的 `data-*` 锚点与 `role="listbox"`,属 DSH 内部实现;升级 DSH 后需要回归(锚点缺失时插件只 warn,不会崩)。
99
+ - tab 行的宽度与右对齐复用了 DSH 布局变量 `--dsh-composer-card-max-width` / `--dsh-composer-side-clearance`(都带兜底值),若 DSH 改了输入区宽度算法,tab 会与卡片右边缘错开几像素。
93
100
  - Web + Linux 没有默认快捷键(DSH 对 Web 可选组合有白名单限制),需在设置里自行指定。
94
101
 
95
102
  ## 开发
@@ -106,7 +113,7 @@ dsh-multiline-input/
106
113
  └── docs/ # 计划、发版清单
107
114
  ```
108
115
 
109
- - 测试:`npm test`(`node --test`,当前 12 条用例)。
116
+ - 测试:`npm test`(`node --test`,当前 13 条用例)。
110
117
  - 语法:`node --check client.js && node --check index.js`。
111
118
  - 调试:HMR 默认忽略 `**/node_modules`,链接安装的 `client.js` 改动不会被热加载;改完客户端代码后在插件页关/开一次(或重启应用),再刷新页面。
112
119
 
@@ -114,12 +121,14 @@ dsh-multiline-input/
114
121
 
115
122
  - 仅作用于 Web GUI(桌面版与浏览器),TUI / CLI 不适用。
116
123
  - 多行起始高度是代码常量 4 行,暂不提供配置项。
124
+ - tab 行占输入框上方 24px:输入区整体比没有 tab 时高约 18px(24px 行高抵掉 6px 栈间距),消息区相应少 18px。
125
+ - hero(空会话)状态下栈行距是 8px,tab 行会按运行时读到的实际 `row-gap` 自适应,视觉上与 dock 态一致。
117
126
  - 若默认键位与其它插件的命令默认值冲突,插件会降级为「点击 tab 切换」并在控制台输出一次警告。
118
127
 
119
128
  ## 文档
120
129
 
121
130
  - [实施计划(2026-10-27)](docs/PLAN-2026-10-27.md)
122
- - [发版检查清单 0.1.0](docs/RELEASE-CHECKLIST-0.1.0.md)
131
+ - 发版检查清单:[0.1.1](docs/RELEASE-CHECKLIST-0.1.1.md)、[0.1.0](docs/RELEASE-CHECKLIST-0.1.0.md)
123
132
  - [路线图](ROADMAP.md)
124
133
  - [变更记录](CHANGELOG.md)
125
134
 
package/ROADMAP.md CHANGED
@@ -1,14 +1,22 @@
1
1
  # ROADMAP
2
2
 
3
- ## 0.1.0(当前)
3
+ ## 0.1.0(2026-10-28 已发布)
4
4
 
5
5
  - [x] 单行 / 多行双态切换,模式按设备持久化(`localStorage`)
6
- - [x] 官方快捷键命令(`multiline-input.toggle`,桌面默认 Ctrl+E,可在设置中改绑)
7
- - [x] 右上角提示(仅空输入时显示,实时显示当前生效键位)
6
+ - [x] 官方快捷键命令(`multiline-input.toggle`;macOS `Cmd+E`,Windows / Linux `Ctrl+E`,可在设置中改绑)
7
+ - [x] 输入框上沿常驻 tab 提示(同底色、实时显示当前生效键位与目标模式,可点击切换)
8
8
  - [x] 多行模式 4 行起高,随内容长高,沿用 DSH 336px 滚动上限
9
9
  - [x] 多行模式 Enter 换行 / Shift+Enter 发送,保留撤销、输入法、菜单、busy-enter 语义
10
10
  - [x] 与 composer 其它输入框、设置弹窗、斜杠菜单互不干扰
11
11
  - [x] 单元测试(`node --test`)与静态检查
12
+ - [x] 发布:npm 0.1.0、GitHub Release `v0.1.0`、awesome-dsh-plugin PR #9938(等仓库满 24h 自动过门槛)
13
+
14
+ ## 0.1.1(2026-10-29 已发布,布局修复)
15
+
16
+ - [x] tab 提示从 `conversation.input.overlay` 浮层改到 `conversation.input.dock` 独立一行(`order: 1000`,紧贴卡片上方):不再压住目标条文案与暂停 / 编辑 / 清除按钮,也不会被以后新增的 composer 卡片覆盖
17
+ - [x] tab 行按运行时读到的栈 `row-gap` 自适应(dock 6px / hero 8px),底边始终与卡片上沿齐平
18
+ - [x] 子龙刷新后确认观感(目标条三个图标完整可见可点、tab 仍在卡片肩上)
19
+ - [x] 发布:版本号 `0.1.1`、CHANGELOG、`npm publish`、GitHub Release `v0.1.1`,清单见 [docs/RELEASE-CHECKLIST-0.1.1.md](docs/RELEASE-CHECKLIST-0.1.1.md)
12
20
 
13
21
  ## 0.2.0 候选
14
22
 
@@ -20,4 +28,6 @@
20
28
  ## 文档
21
29
 
22
30
  - 计划:[docs/PLAN-2026-10-27.md](docs/PLAN-2026-10-27.md)
23
- - 发版清单:[docs/RELEASE-CHECKLIST-0.1.0.md](docs/RELEASE-CHECKLIST-0.1.0.md)
31
+ - 发版清单:[docs/RELEASE-CHECKLIST-0.1.1.md](docs/RELEASE-CHECKLIST-0.1.1.md)、[0.1.0](docs/RELEASE-CHECKLIST-0.1.0.md)
32
+ - npm:<https://www.npmjs.com/package/dsh-multiline-input>
33
+ - GitHub Release:<https://github.com/ZilongYang/dsh-multiline-input/releases/tag/v0.1.1>
Binary file
Binary file
package/client.js CHANGED
@@ -6,8 +6,11 @@
6
6
  * - a shortcut command (default Ctrl+E) registered through `ctx.shortcuts`,
7
7
  * so the binding appears in Settings → General → Keyboard shortcuts and
8
8
  * every edit there is validated and persisted by DSH itself;
9
- * - a top-right hint inside the composer card (`conversation.input.overlay`),
10
- * shown only while the draft is empty;
9
+ * - a hint tab riding the composer card's top shoulder, rendered as its own
10
+ * row in the composer dock stack (`conversation.input.dock`): the row is in
11
+ * flow, so a dock card above the composer (goal bar, todos, queue, cost
12
+ * strip) can never be overlapped — the old floating overlay painted over
13
+ * them and swallowed their buttons;
11
14
  * - a taller composer floor in multi-line mode, driven by
12
15
  * `<html data-dsh-multiline-input="multi">` and one CSS rule;
13
16
  * - Enter / Shift+Enter swapped in multi-line mode by re-dispatching the
@@ -21,10 +24,15 @@
21
24
  *
22
25
  * Reference contracts (source checkout, 0.2.1-rc.3):
23
26
  * - packages/client/ui-conversation/src/client/skeleton/InputBar.tsx
27
+ * - packages/client/ui-conversation/src/client/skeleton/ConversationContent.tsx
28
+ * (composer stack: `conversation.input.dock` rows above the card)
29
+ * - packages/client/ui-conversation/src/client/skeleton/InputBar.module.css
30
+ * (`.root` side clearance, `.card` card-width cap)
24
31
  * - packages/client/ui-conversation/src/client/input/editor/DraftEditor.tsx
25
32
  * - packages/client/ui-conversation/src/client/input/editor/keymap.ts
26
33
  * - packages/client/ui-conversation/src/client/contract/input.ts
27
34
  * - packages/client/ui-conversation/src/client/contract/slots.ts
35
+ * - packages/extensions/cordis-client-runner/src/client/slot-catalog.ts
28
36
  * - packages/client/shortcuts/src/client/types.ts
29
37
  * - packages/client/modules/README.zh.md
30
38
  */
@@ -45,8 +53,14 @@ window.__ModuleLoader__.load({
45
53
 
46
54
  /** Shortcut command id; dotted form so DSH can persist user overrides. */
47
55
  const COMMAND_ID = 'multiline-input.toggle'
48
- /** Composer seats: after the slash menu (order 0), above the card content. */
49
- const HINT_ORDER = 10
56
+ /**
57
+ * The hint lives in the composer dock stack — the in-flow rows directly
58
+ * above the card (goal 10, queue 20, todos 0, cost strip 5). A high order
59
+ * keeps the tab last, flush on the card's top shoulder.
60
+ */
61
+ const HINT_SLOT = 'conversation.input.dock'
62
+ const HINT_ID = 'ml-input-hint'
63
+ const HINT_ORDER = 1000
50
64
  /** Multi-line floor in text lines (the requested 3–5 range). */
51
65
  const MIN_LINES = 4
52
66
 
@@ -87,13 +101,34 @@ window.__ModuleLoader__.load({
87
101
  }
88
102
 
89
103
  const CSS = `
104
+ /* The hint is its own row in the composer dock stack — the in-flow column of
105
+ strips directly above the input card (goal bar, todos, queue, cost strip).
106
+ Occupying real layout space is what makes the tab safe: the old version
107
+ floated in \`conversation.input.overlay\` at \`bottom: 100%\`, painted over
108
+ whatever DSH docked above the card and swallowed that strip's buttons. */
109
+ .dmi-hint-row {
110
+ box-sizing: border-box;
111
+ display: flex;
112
+ justify-content: flex-end;
113
+ /* The card's own column: InputBar's root pads by the shared side clearance
114
+ and the card is capped at the shared card width, centered. The two
115
+ declarations mirror that so the tab's right edge lands on the card's. */
116
+ width: min(
117
+ calc(100% - 2 * var(--dsh-composer-side-clearance, 16px)),
118
+ var(--dsh-composer-card-max-width, 100%)
119
+ );
120
+ margin-inline: auto;
121
+ /* The stack's row gap would detach the tab from the card's top edge;
122
+ cancelling it keeps the "tab on the shoulder" seam. The live gap is read
123
+ in \`MultiLineHint\` (6px docked, 8px in the hero composer) into
124
+ \`--dmi-stack-gap\`; the fallbacks only cover the first frame. */
125
+ margin-bottom: calc(-1 * var(--dmi-stack-gap, var(--dsh-composer-stack-gap, 6px)));
126
+ padding-right: calc(var(--dsw-radius-panel, 16px) + 4px);
127
+ pointer-events: none;
128
+ }
90
129
  /* A small tab riding on the card's top shoulder: same fill as the card, so it
91
130
  reads as one piece, with the card's own hairline continuing under it. */
92
131
  .dmi-hint {
93
- position: absolute;
94
- right: calc(var(--dsw-radius-panel) + 4px);
95
- bottom: 100%;
96
- z-index: 2;
97
132
  display: flex;
98
133
  align-items: center;
99
134
  justify-content: flex-end;
@@ -354,38 +389,73 @@ html[${MODE_ATTRIBUTE}="multi"] [data-composer-card] [data-composer-input][conte
354
389
  /**
355
390
  * Composer hint on the right shoulder above the card: current shortcut plus
356
391
  * the target mode, click to toggle. Always visible, so the binding stays
357
- * discoverable while typing.
392
+ * discoverable while typing. The outer row is the dock-stack slot that
393
+ * reserves the tab's 24px, so no strip above the card can be covered.
358
394
  * @param props - injected stores, dictionary and toggle callback.
359
- * @returns the hint element.
395
+ * @returns the hint row.
360
396
  */
361
397
  function MultiLineHint({ t, modeStore, shortcutStore, toggle }) {
362
398
  const mode = React.useSyncExternalStore(modeStore.subscribe, modeStore.getSnapshot, modeStore.getSnapshot)
363
399
  const shortcut = React.useSyncExternalStore(shortcutStore.subscribe, shortcutStore.getSnapshot, shortcutStore.getSnapshot)
364
400
  const action = mode === MODE_MULTI ? t('toSingle') : t('toMulti')
365
401
  const meta = mode === MODE_MULTI ? t('submitHint') : null
402
+ const rowRef = React.useRef(null)
403
+
404
+ // Cancel exactly the row gap the composer stack is using, so the tab's
405
+ // bottom hairline lands on the card's top edge. The docked composer gaps
406
+ // its rows by 6px and the hero composer by 8px; reading the live value
407
+ // (instead of trusting one of them) keeps both flush, and the observer
408
+ // covers a hero -> docked switch that reuses the same row.
409
+ React.useLayoutEffect(() => {
410
+ const row = rowRef.current
411
+ if (row === null) return undefined
412
+ // The slot outlet's own anchor is `display: contents`; the first flex
413
+ // ancestor carrying a row gap is the composer stack.
414
+ let stack = row.parentElement
415
+ while (stack !== null && !Number.isFinite(Number.parseFloat(window.getComputedStyle(stack).rowGap))) {
416
+ stack = stack.parentElement
417
+ }
418
+ if (stack === null) return undefined
419
+ const sync = () => {
420
+ const gap = Number.parseFloat(window.getComputedStyle(stack).rowGap)
421
+ if (Number.isFinite(gap)) row.style.setProperty('--dmi-stack-gap', `${gap}px`)
422
+ }
423
+ sync()
424
+ if (typeof ResizeObserver !== 'function') return undefined
425
+ const observer = new ResizeObserver(sync)
426
+ observer.observe(stack)
427
+ return () => {
428
+ observer.disconnect()
429
+ }
430
+ }, [])
431
+
366
432
  return h(
367
433
  'div',
368
- { className: 'dmi-hint' },
434
+ { ref: rowRef, className: 'dmi-hint-row', 'data-ml-input-hint': '' },
369
435
  h(
370
- 'button',
371
- {
372
- type: 'button',
373
- className: 'dmi-hint-button',
374
- title: t('hintTitle'),
375
- 'aria-label': shortcut === null ? action : t('ariaLabel', { action, keys: shortcut }),
376
- onMouseDown: (event) => {
377
- event.preventDefault()
378
- },
379
- onClick: (event) => {
380
- event.preventDefault()
381
- event.stopPropagation()
382
- toggle()
436
+ 'div',
437
+ { className: 'dmi-hint' },
438
+ h(
439
+ 'button',
440
+ {
441
+ type: 'button',
442
+ className: 'dmi-hint-button',
443
+ title: t('hintTitle'),
444
+ 'aria-label': shortcut === null ? action : t('ariaLabel', { action, keys: shortcut }),
445
+ onMouseDown: (event) => {
446
+ event.preventDefault()
447
+ },
448
+ onClick: (event) => {
449
+ event.preventDefault()
450
+ event.stopPropagation()
451
+ toggle()
452
+ },
383
453
  },
384
- },
385
- meta !== null && h('span', { className: 'dmi-hint-meta' }, meta),
386
- meta !== null && h('span', { className: 'dmi-hint-sep', 'aria-hidden': true }, '·'),
387
- shortcut !== null && h('span', { className: 'dmi-hint-key' }, shortcut),
388
- h('span', { className: 'dmi-hint-label' }, action),
454
+ meta !== null && h('span', { className: 'dmi-hint-meta' }, meta),
455
+ meta !== null && h('span', { className: 'dmi-hint-sep', 'aria-hidden': true }, '·'),
456
+ shortcut !== null && h('span', { className: 'dmi-hint-key' }, shortcut),
457
+ h('span', { className: 'dmi-hint-label' }, action),
458
+ ),
389
459
  ),
390
460
  )
391
461
  }
@@ -534,9 +604,9 @@ html[${MODE_ATTRIBUTE}="multi"] [data-composer-card] [data-composer-input][conte
534
604
  }, 'ml-input: shortcut command')
535
605
  })
536
606
 
537
- ctx.slots.inject('conversation.input.overlay', () => ctx.slots.register({
538
- name: 'conversation.input.overlay',
539
- id: 'ml-input-hint',
607
+ ctx.slots.inject(HINT_SLOT, () => ctx.slots.register({
608
+ name: HINT_SLOT,
609
+ id: HINT_ID,
540
610
  order: HINT_ORDER,
541
611
  locale: NS,
542
612
  inject: () => ({ t, modeStore, shortcutStore, toggle }),
@@ -551,6 +621,9 @@ html[${MODE_ATTRIBUTE}="multi"] [data-composer-card] [data-composer-input][conte
551
621
  */
552
622
  __test: {
553
623
  COMMAND_ID,
624
+ HINT_SLOT,
625
+ HINT_ID,
626
+ HINT_ORDER,
554
627
  MODE_ATTRIBUTE,
555
628
  MODE_STORAGE_KEY,
556
629
  MODE_SINGLE,
@@ -200,16 +200,28 @@ document 捕获阶段 keydown(`ctx.effect` 卸载),全部满足才接管
200
200
  ---
201
201
 
202
202
  ## 实施偏差(事后补记)
203
-
204
- > 本节为实施后补记,与上方计划原文分开看。
205
-
206
203
  1. **目标 profile 判断错误**:计划里写「当前 `main`」,实际当前运行的是 **DeepSeek Harness.app + `desktop` profile**。依据:运行中的 Host 进程 cwd 与打开的 `~/.dsh/profiles/desktop/package.json`、`cordis.patch.yml`;本会话可用的 zotero / im / univer 工具只出现在 desktop 的 bundle 列表;desktop profile 的 `.plugin-manager` 日志显示最近的本地 link 安装。`main` 属于同时运行的 DSH NEXT(监听 58501,日志 `desktop-next.log`),与当前 GUI 不是同一实例。
204
+
207
205
  2. **安装方式**:`dsh plugin --profile desktop add ...` 被应用独占拒绝(`profile "desktop" is managed exclusively by the Electron application`)。改为等价的手动 link:用应用自带的 pnpm 11.28.5 在 desktop profile 执行 `pnpm add link:/Users/zilong/zWork/ai/zl-plugins/dsh-ml-input`,并把 `dsh-ml-input` 追加进 `dsh.profile.bundles`。profile 的 `patchReload: live` 让 HMR 监听 `package.json`,改动后自动 reconcile。
206
+
208
207
  3. **Host 行激活验证**:曾临时在 `index.js` 的 `apply()` 里写文件哨兵,并在 profile 补丁里临时加 `- id: ml-input` 的 disable/enable 行强制重载;哨兵于 `2026-10-27T16:30:37Z` 出现,证明 Host 已组合并激活插件行。随后已删除哨兵、恢复补丁文件并还原 `index.js`。
208
+
209
209
  4. **浏览器半验证**:曾临时在 `apply()` 写 `localStorage['dsh-ml-input.selfcheck']`,在 Electron 的 LevelDB 中读到该键,证明客户端 bundle 已加载且 `apply()` 真正执行;随后用一次性 removeItem 版本清除并恢复最终代码。
210
+
210
211
  5. 实现与计划一致:单文件手写 `client.js`、`conversation.input.overlay` 提示、`ctx.shortcuts` 命令、捕获阶段翻转 `shiftKey` 的 Enter 接管、`data-*` 锚点。(计划里“用 `useInput` 判断空草稿”在后续样式调整中已移除,提示改为常驻。)
211
- 10. **改名(2026-10-28)**:开源前把包名从 `dsh-ml-input` 改为 **`dsh-multiline-input`**(`ml` 在 LLM 插件市场易被读成 machine learning;新名与 `dsh-composer-*` 命名簇一致、可搜索)。同步改了模块 id、locale NS、快捷键命令 id(`multiline-input.toggle`)、storage key、宿主 row id、CSS 前缀(`dml-`→`dmi-`)、profile 依赖与 bundle 列表;本地目录名保持 `dsh-ml-input`(会话工作区不便改名,包名以 package.json 为准)。
212
+
212
213
  6. **开发热更新更正**:计划第 7 节写「改 client.js 刷新页面即可」,实测不成立——HMR 默认忽略 `**/node_modules`(`packages/boot/hmr/src/index.ts`),链接安装的 `client.js` 改动不会被 `rebuilt()` 拾取。实际做法:在插件页把本插件关/开一次(或重启应用)触发客户端 bundle 重组,再刷新页面;`package.json` / 补丁文件的改动仍会被 profile 监听自动 reconcile。README 已同步。
214
+
213
215
  7. **验收后的用户调整(2026-10-28)**:按子龙反馈——① 桌面默认键从字面 `Control+E` 改为 `primary+KeyE`(macOS 显示并生效为 `Cmd+E`,Windows/Linux 为 `Ctrl+E`);② 提示改为**常驻**,不再随输入隐藏。
216
+
214
217
  8. **提示样式再调整(2026-10-28)**:试过“与卡片一体的顶部窄条”(同底色、底部淡线、给卡片预留 `padding-top`),但子龙要的是**贴着输入框上沿、像给输入框加一个 tab** 的形态。
218
+
215
219
  9. **最终形态(2026-10-28)**:提示改为输入框上沿右侧的小 tab——`position:absolute; right: calc(var(--dsw-radius-panel) + 4px); bottom: 100%`(浮在卡片上方、不占输入区布局)、背景 `--dsw-specific-input-major`(同卡片底色)、宽度 `max-content`(只包文字)、顶部 `--dsw-radius-md` 圆角、底部 `0.5px solid --dsw-alias-border-l2`(与卡片描边同色,视觉上连成一体);原先的卡片 `padding-top` 预留已移除。
220
+
221
+ 10. **改名(2026-10-28)**:开源前把包名从 `dsh-ml-input` 改为 **`dsh-multiline-input`**(`ml` 在 LLM 插件市场易被读成 machine learning;新名与 `dsh-composer-*` 命名簇一致、可搜索)。同步改了模块 id、locale NS、快捷键命令 id(`multiline-input.toggle`)、storage key、宿主 row id、CSS 前缀(`dml-`→`dmi-`)、profile 依赖与 bundle 列表;本地目录名保持 `dsh-ml-input`(会话工作区不便改名,包名以 package.json 为准)。
222
+
223
+ 11. **开源与市场收录(2026-10-28)**:改名为 `dsh-multiline-input` 后建公开仓库、打 `v0.1.0` tag 与 Release、`npm publish`,并向 awesome-dsh-plugin 提交单文件 PR #9938(`data/plugins/ZilongYang__dsh-multiline-input.yml`,category `ui`);仓库与 npm 包都带 `screenshots.json` + `assets/` 截图。
224
+
225
+ 12. **市场截图与 PR 门槛(2026-10-28 补记)**:子龙指出首版市场截图过宽、单行图卡片底部被裁;按其调整后的输入框宽度重截——单行用子龙提供的 1360×330 原图,多行裁成同宽 1360×464,两张都完整保留卡片底部与状态行,并更新 `assets/` 与 commit `8036ab6`。awesome-dsh-plugin PR #9938 的 `check` 已通过;`Submission gate` 失败的唯一原因是新建仓库仅 0.0 天(规则要求满 1 天),CI 会自动重跑、约 24h 后转绿。npm 0.1.0 tarball 中的截图仍是首发旧图(市场读取仓库 `screenshots.json`,不受影响)。
226
+
227
+ 13. **提示 tab 的布局重叠修复(2026-10-29 补记,对应 plan 第 8/9 条的最终形态)**:子龙反馈「顶部红框里排版重叠」——目标条(ui-goal `GoalBar`,`conversation.input.dock` order 10)的文案与暂停 / 编辑 / 清除按钮被浮在上方的 tab 压住,tab 的按钮还会截走这部分点击。根因是 tab 走 `conversation.input.overlay` 座位、`position:absolute; bottom:100%` 浮在卡片外且 `z-index:2`,而 DSH 的输入区是「卡片上下文栈」(`ConversationContent.tsx` 里 `conversation.input.dock` 的条目按 order 排在卡片正上方),只要紧贴卡片上方那一行铺到右边缘就会撞。修复方案(子龙 2026-10-29 选定 A):tab 改注册到 `conversation.input.dock`(`order: 1000`,排在 todo 0 / 额度横条 5 / 目标条 10 / 队列 20 之后)成为**在流内的一行**,整行 24px、宽度按卡片列(`--dsh-composer-card-max-width` / `--dsh-composer-side-clearance`)右对齐,`margin-bottom: -实际 row-gap` 让 tab 底边与卡片上沿齐平,保留「卡片肩上的 tab」外观但不再与任何上方卡片重叠;`row-gap` 在运行时读取(dock 6px / hero 8px,用 `ResizeObserver` 覆盖 hero→dock 切换)。副作用:输入区高 18px、dock 卡片整体上移 24px。未选方案:B「被占时隐藏 tab」(长任务会话里反而看不到快捷键提示)、C「挪进工具行 `conversation.input.right`」(视觉变化大、与模型选择器抢空间)。子龙 2026-10-29 刷新验证通过后同日发版 **0.1.1**(版本号、CHANGELOG、`npm publish`、GitHub Release `v0.1.1`),发版清单见 `docs/RELEASE-CHECKLIST-0.1.1.md`。
@@ -1,60 +1,69 @@
1
1
  # dsh-multiline-input 0.1.0 发版 / 执行检查清单
2
2
 
3
- > 每次发版或装到本机验收时逐条勾选;未勾选的项目要写明原因。
3
+ > 状态(2026-10-28):0.1.0 已发布到 npm 与 GitHub;市场 PR #9938 的条目校验已通过,仅剩「仓库满 24h」的自动门槛。勾选为实测结果,未勾选项注明原因。
4
4
 
5
5
  ## 1. 代码与静态检查
6
6
 
7
7
  - [x] `node --check client.js`
8
8
  - [x] `node --check index.js`
9
- - [x] `npm test`(`node --test`)全绿
9
+ - [x] `npm test`(`node --test`)13 条全绿
10
10
  - [x] `locale/zh.json`、`locale/en.json`、`package.json` 可被 JSON 解析
11
- - [x] `npm --cache .npm-cache pack --dry-run` 文件清单包含 `client.js`、`cordis.patch.yml`、`locale/*`、README、docs
11
+ - [x] `npm --cache /tmp/dsh-npm-cache pack --dry-run`:17 个文件,含 `client.js`、`cordis.patch.yml`、`locale/*`、`assets/*`、`screenshots.json`、README、docs
12
12
  - [x] `package.json` 版本 = `0.1.0`,`dsh.compatibility.dsh` = `>=0.2.1-rc.3 <0.3.0-0`
13
13
 
14
- ## 2. 安装
14
+ ## 2. 发布结果(2026-10-28)
15
15
 
16
- > 本机运行中的 DeepSeek Harness 使用 `desktop` profile,由应用独占;`dsh plugin --profile desktop` 会报
17
- > `profile "desktop" is managed exclusively by the Electron application`。
16
+ - GitHub:https://github.com/ZilongYang/dsh-multiline-input
17
+ - tag `v0.1.0`;Release:https://github.com/ZilongYang/dsh-multiline-input/releases/tag/v0.1.0
18
+ - topics:`dsh-plugin` / `deepseek-harness` / `dsh` / `ui` / `composer` / `multiline`
19
+ - npm:`dsh-multiline-input@0.1.0`
20
+ - `npm publish` 成功;公开版本端点 `https://registry.npmjs.org/dsh-multiline-input/0.1.0` = 200
21
+ - `npm view dsh-multiline-input version dist.fileCount` = `0.1.0` / `17`
22
+ - 市场条目:https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/pull/9938
23
+ - 只新增 `data/plugins/ZilongYang__dsh-multiline-input.yml`(category `ui`)
24
+ - `check` 通过;`Submission gate` 失败原因仅 **仓库 0.0 天 < 1 天**,提示会自动重跑、约 24h 后转绿,无需重提
25
+ - 市场截图(1360 宽,含卡片底部与状态行):`assets/screenshot-1-single-line.png`(1360×330)、`assets/screenshot-2-multiline.png`(1360×464);`screenshots.json` 已提交
26
+ - npm tarball 内的截图仍是首发旧图(市场读取仓库,不受影响;如需同步可发 0.1.1)
27
+
28
+ ## 3. 安装
29
+
30
+ > 本机 DeepSeek Harness 使用 `desktop` profile,由应用独占;`dsh plugin --profile desktop` 会报 `profile "desktop" is managed exclusively by the Electron application`。
18
31
 
19
32
  - [x] 方式一(应用内):「插件」页 → 添加插件 → 绝对路径本目录 → 安装 → 启用
20
- - [x] 方式二(开发期手动 link,已在 2026-10-28 执行):
21
- `cd ~/.dsh/profiles/desktop && pnpm add link:<本目录>`,
22
- 再把 `dsh-multiline-input` 追加进 `package.json` 的 `dsh.profile.bundles`;
23
- profile 的 `patchReload: live` 会自动 reconcile(曾用临时文件哨兵验证 Host 行于 2026-10-27T16:30:37Z 激活)
24
- - [ ] 插件出现在已安装列表,且能启用 / 禁用
25
- - [ ] 刷新页面后插件加载(无控制台报错;若报错记录完整堆栈)
26
-
27
- ## 3. 实机验收(空会话 + 有内容会话各一次)
28
-
29
- - [ ] 输入框上方右侧常驻显示 `Cmd+E 多行输入`(macOS;Windows / Linux 为 `Ctrl+E`)
30
- - [ ] 输入文字后提示仍在(常驻,不隐藏)
31
- - [ ] `Cmd+E` / `Ctrl+E` 在编辑器内可切换;在页面空白处也可切换
32
- - [ ] 多行模式输入框最小高度 4 行;切回单行恢复原高度
33
- - [ ] 多行模式 `Enter` 换行(`Cmd/Ctrl+Z` 可撤销)
34
- - [ ] 多行模式 `Shift+Enter` 发送
35
- - [ ] 多行模式 `Cmd/Ctrl+Enter` 仍为加速提交
36
- - [ ] 输入 `/` 打开菜单后 `Enter` 仍选中菜单项,不换行
37
- - [ ] 输入 `@` 打开引用菜单后 `Enter` 仍选中菜单项
38
- - [ ] 中文输入法候选态按 `Enter` 不误触,选词正常
39
- - [ ] 在设置搜索框 / 会话重命名输入框内按 `Ctrl+E` 不切换模式
40
- - [ ] 刷新页面后模式保持;重启应用后模式保持
41
- - [ ] `设置 → 通用 → 快捷键` 中出现「切换多行输入」,可改绑、可恢复默认
42
- - [ ] 改绑后右上角提示立即更新;清空绑定后提示只显示文案且可点击
43
- - [ ] 禁用插件后:提示消失、高度恢复、`Ctrl+E` 不再切换、无残留 DOM / 控制台报错
44
-
45
- ## 4. 回滚
33
+ - [x] 方式二(开发期手动 link):
34
+ `cd ~/.dsh/profiles/desktop && pnpm add link:<本目录>`,并把 `dsh-multiline-input` 追加进 `package.json` 的 `dsh.profile.bundles`
35
+ - [x] 插件出现在已安装列表,且能启用 / 禁用(改名后重新安装并确认)
36
+ - [x] 刷新 / 重启应用后插件加载(重命名后重启验证,控制台无报错)
37
+
38
+ ## 4. 实机验收
39
+
40
+ - [x] 输入框上方右侧常驻显示 `Cmd+E 多行输入`(macOS)
41
+ - [x] 输入文字后提示仍在(常驻)
42
+ - [x] `Cmd+E` 切换单行 / 多行;页面空白处也可切换(用户实测)
43
+ - [x] 多行模式最小高度 4 行;切回单行恢复原高度(截图对比)
44
+ - [x] 多行模式 `Shift+Enter` 发送、`Enter` 换行(用户实测)
45
+ - [ ] `Cmd/Ctrl+Enter` 加速提交(未专项回归)
46
+ - [ ] `/`、`@` 菜单打开时 `Enter` 选中菜单项(未专项回归)
47
+ - [ ] 中文输入法候选态 `Enter` 不误触(未专项回归)
48
+ - [ ] 设置搜索框 / 会话重命名框内 `Ctrl+E` 不切换(未专项回归)
49
+ - [ ] 改名后刷新 / 重启模式保持(storage key 变更,需再验一次)
50
+ - [ ] `设置 → 通用 → 快捷键` 出现「切换多行输入」,可改绑 / 恢复默认(未在界面逐项复核)
51
+ - [ ] 改绑后 tab 文案立即更新;清空绑定后只显示文案且可点击(未复核)
52
+ - [ ] 禁用插件后提示消失、高度恢复、无残留 DOM / 监听(未专项复核)
53
+
54
+ ## 5. 回滚
46
55
 
47
56
  - [ ] 插件页关闭或卸载;或 `dsh plugin --profile <profile> remove dsh-multiline-input`
48
- - [ ] 回滚后与安装前一致(原输入框行为、快捷键设置里的条目消失)
57
+ - [ ] 回滚后与安装前一致(原输入框行为、快捷键条目消失)
49
58
 
50
- ## 5. 存档
59
+ ## 6. 存档
51
60
 
52
- - [ ] `docs/PLAN-2026-10-27.md` 已保存获批原文
53
- - [ ] 实施偏差已回填到计划文末
54
- - [ ] README 文档索引、ROADMAP 链接已更新
55
- - [ ] git 首次提交完成(本地)
61
+ - [x] `docs/PLAN-2026-10-27.md` 已保存获批原文
62
+ - [x] 实施偏差已回填到计划文末(含改名、发布、截图调整)
63
+ - [x] README 文档索引、ROADMAP 链接已更新
64
+ - [x] git 提交与远端推送完成
56
65
 
57
66
  ## 备注
58
67
 
59
- - 本版本不发布 npm、不做市场条目、不建远端。
60
- - 依赖 DSH `0.2.1-rc.3` 的 composer DOM 锚点;升级 DSH 后按第 3 节重新验收。
68
+ - 0.1.0 已发布(npm + GitHub);市场 PR 只等仓库满 24h 的 CI 门槛。
69
+ - 依赖 DSH `0.2.1-rc.3` 的 composer DOM 锚点;升级 DSH 后按第 4 节重新验收。
@@ -0,0 +1,58 @@
1
+ # dsh-multiline-input 0.1.1 发版 / 执行检查清单
2
+
3
+ > 状态(2026-10-29):0.1.1 为布局修复版(tab 不再压住 composer 上方卡片)。勾选为实测结果,未勾选项注明原因。
4
+
5
+ ## 1. 变更范围
6
+
7
+ - [x] `client.js`:tab 从 `conversation.input.overlay` 浮层改到 `conversation.input.dock` 一行(`order: 1000`);行宽按卡片列(`--dsh-composer-card-max-width` / `--dsh-composer-side-clearance`)右对齐;`margin-bottom: -实际 row-gap`(运行时读 `row-gap`,dock 6px / hero 8px,用 `ResizeObserver` 覆盖 hero→dock 切换)让 tab 底边与卡片上沿齐平
8
+ - [x] `client.js`:`__test` 增加 `HINT_SLOT` / `HINT_ID` / `HINT_ORDER`
9
+ - [x] `test/core.test.js`:新增「tab 座位必须是 `conversation.input.dock`、order 排在全部内置 dock 条目之后」的断言(13 条用例)
10
+ - [x] 文档:README(使用 / 实现说明 / 兼容性 / 已知限制 / 源码依据表)、ROADMAP、CHANGELOG、plan 事后补记第 13 条
11
+
12
+ ## 2. 代码与静态检查
13
+
14
+ - [x] `node --check client.js`
15
+ - [x] `node --check index.js`
16
+ - [x] `npm test`(`node --test`)14 个测试节点全绿(13 条用例 + harness)
17
+ - [x] `locale/zh.json`、`locale/en.json`、`package.json` 可被 JSON 解析
18
+ - [x] `npm pack --dry-run`:tarball 文件清单含 `client.js`、`cordis.patch.yml`、`locale/*`、`assets/*`、`screenshots.json`、README、docs
19
+ - [x] `package.json` 版本 = `0.1.1`,`dsh.compatibility.dsh` = `>=0.2.1-rc.3 <0.3.0-0`
20
+
21
+ ## 3. 布局几何验证(无 GUI,静态复刻)
22
+
23
+ 用 Chrome 无头模式复刻 DSH `0.2.1-rc.3` 的 composer 栈(`.body` / `.scrollBody` / `.composerSeat` / `.composerStack` / InputBar `.root`+`.card` 的真实几何 CSS),插件 CSS 直接从 `client.js` 抽取后注入:
24
+
25
+ - [x] `tab.bottom - card.top = 0.00px`(tab 底边与卡片上沿齐平)
26
+ - [x] `tab.right - (card.right - (radius + 4)) = 0.00px`(右边缘与 0.1.0 位置一致)
27
+ - [x] 目标条底边在 tab 上方 6px,**无重叠**;目标条三个图标按钮落在 tab 的 x 范围之外
28
+ - [x] 1600 / 1400 两种宽度、栈间距 6px 与 8px(hero)两种情形结论一致
29
+
30
+ ## 4. 实机验收(子龙,2026-10-29)
31
+
32
+ - [x] 目标条(进行中的目标)文案与暂停 / 编辑 / 清除三个图标完整可见、可点击
33
+ - [x] tab 仍在输入卡片上沿右侧、贴着卡片
34
+ - [x] 输入区比 0.1.0 高约 18px、composer 上方 dock 卡片整体上移 24px(预期变化,子龙确认可接受)
35
+ - [ ] 待办面板 / 队列面板与 tab 的同框观感(本次会话未出现这两种卡片,未专项复核)
36
+ - [ ] `Cmd+E` 切换、多行 `Shift+Enter` 发送(0.1.0 已验证,本次改动未触碰该逻辑,未重跑)
37
+
38
+ ## 5. 发布结果(2026-10-29)
39
+
40
+ - GitHub:https://github.com/ZilongYang/dsh-multiline-input
41
+ - tag `v0.1.1`;Release:https://github.com/ZilongYang/dsh-multiline-input/releases/tag/v0.1.1
42
+ - npm:`dsh-multiline-input@0.1.1`
43
+ - `npm publish` 成功;`npm view dsh-multiline-input version` = `0.1.1`
44
+ - 市场:首版 PR #9938 已收录,市场读取仓库 + npm,无需重新提 PR;`assets/` 截图沿用 0.1.0(tab 外观与位置未变,仅上方状态行上移 24px,未重截)
45
+
46
+ ## 6. 本机安装(desktop profile,link 安装)
47
+
48
+ - [x] profile 里 `dsh-multiline-input` 指向本目录(`link:`),代码改动即时生效;`package.json` 版本变化由 `patchReload: live` reconcile
49
+ - [x] 插件页关 / 开一次 + 刷新页面即可加载新 `client.js`(HMR 忽略 `node_modules`,见 plan 偏差 #6)
50
+
51
+ ## 7. 回滚
52
+
53
+ - 插件页关闭 / 卸载,或 `dsh plugin --profile <profile> remove dsh-multiline-input`
54
+ - 代码回滚:`git revert <0.1.1 commit>`;npm 侧如需回退版本,`npm deprecate dsh-multiline-input@0.1.1 "..."` 并发布修正版
55
+
56
+ ## 备注
57
+
58
+ - 依赖 DSH `0.2.1-rc.3` 的 composer 栈与布局变量;升级 DSH 后按第 3、4 节回归(tab 与卡片右边缘错位 / 出现缝隙即为变量或栈间距变了)。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-multiline-input",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "description": "Single/multi-line composer toggle for DeepSeek Harness: an editable Cmd/Ctrl+E shortcut, a tab hint on the composer, a 4-line floor for multi-line drafts, and Shift+Enter to send.",
6
6
  "license": "MIT",
@@ -65,7 +65,7 @@
65
65
  "type": "git",
66
66
  "url": "git+https://github.com/ZilongYang/dsh-multiline-input.git"
67
67
  },
68
- "homepage": "https://github.com/ZilongYang/dsh-multiline-input#readme",
68
+ "homepage": "https://dsh.zlmix.com/multiline-input/",
69
69
  "bugs": {
70
70
  "url": "https://github.com/ZilongYang/dsh-multiline-input/issues"
71
71
  }