dsh-codinput 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.
Files changed (54) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/LICENSE +21 -0
  3. package/NOTICE +18 -0
  4. package/README.en.md +167 -0
  5. package/README.md +167 -0
  6. package/TESTING.md +95 -0
  7. package/assets/en/preview-ball-detail.png +0 -0
  8. package/assets/en/preview-float.png +0 -0
  9. package/assets/en/preview-normal.png +0 -0
  10. package/assets/en/preview-side.png +0 -0
  11. package/assets/en/preview-split.png +0 -0
  12. package/assets/icon.svg +1 -0
  13. package/assets/preview-ball-detail.png +0 -0
  14. package/assets/preview-float.png +0 -0
  15. package/assets/preview-normal.png +0 -0
  16. package/assets/preview-side.png +0 -0
  17. package/assets/preview-split.png +0 -0
  18. package/cordis.patch.yml +5 -0
  19. package/lib/client.js +544 -0
  20. package/lib/index.js +5 -0
  21. package/package.json +79 -0
  22. package/src/client/attachments.ts +43 -0
  23. package/src/client/cm.ts +207 -0
  24. package/src/client/command.ts +78 -0
  25. package/src/client/components/AttachmentRail.tsx +241 -0
  26. package/src/client/components/CodinputBar.tsx +357 -0
  27. package/src/client/components/CodinputSurface.tsx +375 -0
  28. package/src/client/components/EditorPane.tsx +214 -0
  29. package/src/client/components/ExpandBall.tsx +122 -0
  30. package/src/client/components/MenuSurface.tsx +72 -0
  31. package/src/client/components/ModelMenu.tsx +234 -0
  32. package/src/client/components/PermissionMenu.tsx +257 -0
  33. package/src/client/components/PopupSelectView.tsx +140 -0
  34. package/src/client/components/SlashMenu.tsx +255 -0
  35. package/src/client/components/StatsRow.tsx +188 -0
  36. package/src/client/components/Toolbar.tsx +239 -0
  37. package/src/client/components/popup-filter.ts +13 -0
  38. package/src/client/components/useSnapshot.ts +18 -0
  39. package/src/client/diag.ts +58 -0
  40. package/src/client/host-types.ts +328 -0
  41. package/src/client/hotkey.ts +82 -0
  42. package/src/client/i18n.ts +312 -0
  43. package/src/client/icons.tsx +296 -0
  44. package/src/client/index.tsx +178 -0
  45. package/src/client/modeldir.ts +42 -0
  46. package/src/client/permission.ts +123 -0
  47. package/src/client/prefs.ts +123 -0
  48. package/src/client/services.ts +78 -0
  49. package/src/client/settings.tsx +236 -0
  50. package/src/client/sidebar.tsx +114 -0
  51. package/src/client/sideinput.ts +171 -0
  52. package/src/client/stats.ts +166 -0
  53. package/src/client/styles.ts +489 -0
  54. package/src/client/triggers.ts +84 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,30 @@
1
+ # Changelog
2
+
3
+ 本项目遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/) 与 [语义化版本](https://semver.org/lang/zh-CN/)。
4
+
5
+ ## [0.1.0] — 2026-09-23
6
+
7
+ 首个公开版本。
8
+
9
+ ### 新增
10
+
11
+ - **三态单实例输入入口**:普通 / 悬浮 / 侧栏标签三形态,任一时刻恰好一个,且绝不与官方输入框共存。
12
+ - **侧栏收起不让位 + 右上角小球**:侧栏收起后输入入口仍是侧栏那一个(不唤回普通模式,也不唤回官方输入框),标题栏下方出现圆形品牌小球,点击展开侧栏并聚焦 Codinput。小球 portal 定位、不占用官方槽位。
13
+ - **悬浮窗口式缩放**:四条边 + 四个角落 1:1 跟手,对边为锚点、不越出视口、不小于 360×140,位置与尺寸双记忆。
14
+ - **附件三入口**:点选文件、直接粘贴图片、把文件拖进卡片(拖入时描边提示)。
15
+ - **附件展示复刻官方**:图片 64×64 圆角缩略图、非图片文件卡(类型 glyph + 名称 + 「扩展名 体积」)、上传中 spinner 与进度条、失败红框整卡重试、轨道溢出时两端翻页箭头——取值逐条对齐官方 `dsh-client-ui-attachment`。
16
+ - **撤销历史跨形态保留**:编辑器状态按会话暂存,形态切换/会话切换后 `Ctrl+Z` 依然有效。
17
+ - **上下文小圈与数据行**:官方 composer stats 与上下文占用小圈的逐值复刻(含详情面板)。
18
+ - **界面多语言(中文 / English)**:全部文案走宿主 locale 服务(`ctx.locale.register` + `bind`),跟随宿主语言设置实时切换;宿主 locale 缺席时退回内置词典。README 提供[中文](./README.md)与 [English](./README.en.md) 两份。
19
+
20
+ ### 说明
21
+ - 进入侧栏形态只经侧栏标签本身;早期内部版本的「拖入右侧栏 = 侧栏模式」手势已按产品决策舍弃。
22
+ - 偏好键为 `dsh-codinput.prefs.v2` 的 `mode: 'normal' | 'float'`(侧栏态由承载派生、不落盘);早期内部版本的两态模型 `floating: true` 会自动迁移为 `mode: 'float'`。
23
+ - 发布产物为压缩 + 无内联 sourcemap(约 460 KiB);`npm run build:dev` 保留未压缩 + 内联 sourcemap 的调试产物。
24
+ - 官方侧草稿灌入(pick / claim / 提交清空)不进撤销栈。
25
+
26
+ ### 修复
27
+
28
+ - **侧栏标签里打字不落草稿**:编辑器回调走的是模块级单槽(跨形态复用 `EditorState` 必需),但卸载时无条件清空——侧栏 body 先挂载、主输入面晚一拍才卸载,于是晚卸载的旧实例把新实例的 hooks 摘掉。症状是在侧栏 Codinput 里打字只留在本地面板:不进草稿(刷新/关标签即丢)、行列指示不动、发送/换行手势失效。改为按实例身份清空(`clearActiveEditorHooks`)。
29
+
30
+ [0.1.0]: https://github.com/Witherwithwinter/DSH-Codinput/releases/tag/v0.1.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Witherwithwinter
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/NOTICE ADDED
@@ -0,0 +1,18 @@
1
+ DSH-Codinput — 第三方归属 / Third-party attribution
2
+ =====================================================
3
+
4
+ 本软件的部分视觉素材与样式取值复刻自 DeepSeek Harness(`@deepseek-ai/dsh`,
5
+ MIT License,Copyright (c) 2026 DeepSeek),包括:
6
+
7
+ - 若干 SVG 图标路径:权限盾牌系列 glyph(read-only / workspace-write /
8
+ danger-full-access)、plus、paperclip、arrow-up、发送箭头与停止方块、
9
+ 通用文件图形(file body / fold);
10
+ - 输入框卡片与附件轨的 CSS 取值:尺寸、圆角、间距、颜色变量的用法。
11
+
12
+ 上述内容依 MIT 许可使用。其原始版权与许可声明(MIT License, Copyright (c)
13
+ 2026 DeepSeek)与本仓库的 LICENSE 一并保留;详见 README「第三方归属」一节。
14
+
15
+ Portions of this project replicate DeepSeek Harness (`@deepseek-ai/dsh`, MIT
16
+ License, Copyright (c) 2026 DeepSeek), as detailed above. They are used under
17
+ the MIT license, and the original copyright and permission notice are retained
18
+ alongside this repository's LICENSE file.
package/README.en.md ADDED
@@ -0,0 +1,167 @@
1
+ # DSH-Codinput
2
+
3
+ [中文](./README.md) | English
4
+
5
+ [![npm Version](https://img.shields.io/npm/v/dsh-codinput)](https://www.npmjs.com/package/dsh-codinput)
6
+ [![DSH Version](https://img.shields.io/badge/DSH-0.1.5--rc.2%2B-blue)](https://github.com/deepseek-ai/deepseek-harness)
7
+ [![Downloads](https://img.shields.io/npm/dm/dsh-codinput)](https://www.npmjs.com/package/dsh-codinput)
8
+ [![GitHub Stars](https://img.shields.io/github/stars/Witherwithwinter/DSH-Codinput)](https://github.com/Witherwithwinter/DSH-Codinput/stargazers)
9
+ [![License](https://img.shields.io/github/license/Witherwithwinter/DSH-Codinput)](./LICENSE)
10
+
11
+ > Replaces the DeepSeek Harness WebUI composer with a **code-editor style** input panel: CodeMirror 6 takes over `conversation.composer.bar`, while the official `/` `@` trigger pipeline, model and permission controls, stats projections, and draft persistence are all preserved.
12
+
13
+ <p>
14
+ <img src="./assets/icon.svg" width="22" alt="" align="top" />
15
+ &nbsp;Icon: lucide <code>file-code-corner</code> (also used as the recall ball when the sidebar is collapsed)
16
+ </p>
17
+
18
+ ![Default mode](./assets/en/preview-normal.png)
19
+
20
+ <table>
21
+ <tr>
22
+ <td width="50%"><img src="./assets/en/preview-split.png" alt="Edit + preview split" /><br /><sub>Edit + preview split (exactly half and half)</sub></td>
23
+ <td width="50%"><img src="./assets/en/preview-float.png" alt="Floating mode" /><br /><sub>Floating mode (draggable, resizable from all 8 edges/corners)</sub></td>
24
+ </tr>
25
+ <tr>
26
+ <td width="50%"><img src="./assets/en/preview-side.png" alt="Sidebar tab mode" /><br /><sub>Sidebar tab mode (full-height, VS Code panel style)</sub></td>
27
+ <td width="50%"><img src="./assets/en/preview-ball-detail.png" alt="Recall ball" /><br /><sub>The recall ball shown when the sidebar is collapsed</sub></td>
28
+ </tr>
29
+ </table>
30
+
31
+ <p align="center">
32
+ <sub>Every string goes through the host locale service, so the UI follows the host language setting (中文 / English) — the screenshots above are the English UI, the Chinese ones live in the <a href="./README.md">中文 README</a></sub>
33
+ </p>
34
+
35
+ ## Features
36
+
37
+ **Three modes, one instance** — the input surface exists in exactly one of three states at any moment, and never coexists with the official composer:
38
+
39
+ | Mode | How to enter | Behaviour |
40
+ | --- | --- | --- |
41
+ | Normal `normal` | default | The takeover card sits in the composer's own place |
42
+ | Floating `float` | drag the blank area of the card's top row | Pops out into a floating window: draggable, resizable from all four edges and four corners (position and size are remembered) |
43
+ | Sidebar tab `side` | open the “Codinput” tab in the right sidebar | The input lives in the sidebar panel; **collapsing the sidebar does not give the composer back** — the round ball under the title bar brings it back |
44
+
45
+ - **Editor**: line numbers (optional), current-line and gutter highlighting, soft wrap, Tab indentation, a line/column indicator, and **undo history that survives mode switches** (drag into floating mode, come back, and `Ctrl+Z` still works).
46
+ - **`/` `@`**: entirely the official trigger pipeline (track → official menu store → a faithful MenuView replica): candidate menu, drill-down, crumbs and skeletons match the official UI; the command list is sorted by name for empty queries.
47
+ - **Model & reasoning effort**: read from the official shared directory (the same state source as `/model`). **Permissions**: the three official presets plus the RiskConfirmation modal for full access.
48
+ - **Attachments**: pick files with the paperclip, **paste images directly**, or **drop files onto the card** (the rail is a faithful replica of the official one: 64×64 image thumbnails, file cards with type glyph/name/size, upload spinner, progress bar, retry on failure).
49
+ - **Stats row**: replicated official composer stats (turns/steps · tok/s, total tok · cache hit) plus the context-usage dial, both with detail panels; available in all three modes.
50
+ - **Settings**: Settings → Codinput (takeover on/off, line numbers, font, **fully custom send / line-break shortcuts** via a recorder, default view).
51
+ - **Draft**: the official input machine is the single source of truth (one write entry point); drafts survive mode switches, plugin toggles, and reloads.
52
+
53
+ ## Installation
54
+
55
+ Requires the host `@deepseek-ai/dsh` (see compatibility below).
56
+
57
+ ### Method A: npm package
58
+
59
+ ```bash
60
+ dsh plugin --profile web add dsh-codinput
61
+ ```
62
+
63
+ That single command is enough: `dsh plugin` goes through the official profile package manager, which reads this package's `dsh.bundle` metadata, validates the `cordis.patch.yml` it ships (`dsh.bundle.patch`), and appends the package name to the profile's `bundles` — no hand-written patch entry.
64
+
65
+ Then start it with `dsh web` (if it is already running, just hard-reload the page — profiles default to `patchReload: live`).
66
+
67
+ ### Method B: GitHub source
68
+
69
+ ```bash
70
+ dsh plugin --profile web add github:Witherwithwinter/DSH-Codinput
71
+ ```
72
+
73
+ The repository ships the built `lib/`, so a source install **does not need a local build** either. If pnpm asks for build authorisation (for example if you install your own fork with `lib/` removed), write the key it prints into the profile's `pnpm-workspace.yaml` and re-run:
74
+
75
+ ```yaml
76
+ allowBuilds:
77
+ esbuild@0.25.12: true
78
+ ```
79
+
80
+ ### Method C: local source (development)
81
+
82
+ ```bash
83
+ git clone https://github.com/Witherwithwinter/DSH-Codinput.git
84
+ dsh plugin --profile web add link:/abs/path/to/DSH-Codinput
85
+ ```
86
+
87
+ A `link:` install copies nothing, so `npm run build` plus a page reload is enough.
88
+
89
+ ### Method D: the equivalent manual config
90
+
91
+ To pin a version or maintain the profile by hand, the manual equivalent of **Method A** is (`~/.dsh/profiles/web/package.json`):
92
+
93
+ ```jsonc
94
+ {
95
+ "dependencies": {
96
+ "dsh-codinput": "^0.1.0" // for local development: "link:/abs/path/to/DSH-Codinput"
97
+ },
98
+ "dsh": {
99
+ "profile": {
100
+ "bundles": [
101
+ "@deepseek-ai/dsh-base",
102
+ "@deepseek-ai/dsh-web-app",
103
+ "dsh-codinput" // ← append last
104
+ ],
105
+ "patchReload": "live" // after rebuilding lib/client.js, just reload the page
106
+ }
107
+ }
108
+ }
109
+ ```
110
+
111
+ After editing the profile by hand, run `pnpm install` inside the profile directory (or `dsh plugin --profile web install`), then `dsh web`.
112
+
113
+ ## Compatibility
114
+
115
+ The host publishes **rc** builds on npm's `latest` tag (`next` is the following rc, `alpha` is the further-ahead channel), so a plain install gives you an rc.
116
+
117
+ | Host version | Status |
118
+ | --- | --- |
119
+ | `0.1.5-rc.2` | ✅ Verified on a real instance (this is what a plain install gives you) |
120
+ | `0.1.6-alpha.2` | ✅ Current development baseline; every feature verified end to end |
121
+ | others | untested |
122
+
123
+ On top of the real-instance run, `0.1.5-rc.2` was also checked statically: the client packages for that version were pulled from npm and every contract this plugin relies on was confirmed present — the three slots it uses (`conversation.composer.bar`, `sidebar.right.pane.tab`, `settings.section`), the `sidebarRight` / `sidebarRightTabs` / `locale` services and `openTabIn`, the sidebar expansion planner (dockkit `planSetExpanded`), and the official primitives the replicas are copied from (`fileSizeText` / `fileExtension` and the attachment glyph paths, byte for byte). Relative to the development baseline, rc is only missing a few slots the plugin never uses (`conversation.content`, `conversation.input.permission`, `conversation.session.header.leading`, `sidebar.right.tab.guide.entry`). For how to re-run this on another channel, see the “Verifying on another channel” section of [TESTING.md](./TESTING.md).
124
+
125
+ The plugin leans on host internals (slot shadowing, `useTabInfo`, `sidebarRight`, `--dsw-*` tokens). Every call site guards with `typeof` checks and degrades gracefully, but a major host upgrade can still break it. **After upgrading the host, walk through [TESTING.md](./TESTING.md).**
126
+
127
+ ## Known limitations
128
+
129
+ - `/` `@` candidates come from host directories (skills / commands / files / sessions); when the host provides none, the menu is empty — the plugin cannot fill that in.
130
+ - **No syntax highlighting** and **no Esc behaviour** — deliberate product decisions (this is an input panel, not a code reader).
131
+ - While the sidebar tab is visible the composer is hidden entirely (single input surface rule). The official stats dock and context dial live inside the official composer, so they disappear with it and the replicated stats row takes over.
132
+ - Duplicating the “Codinput” tab into a second split pane shows a notice instead of a second editor (the input surface stays unique).
133
+ - The floating gesture only keeps “drag back to the composer area = normal”; “drag into the right sidebar = sidebar mode” was dropped by product decision.
134
+
135
+ ## Development
136
+
137
+ ```bash
138
+ npm install
139
+ npm run build # release bundle → lib/client.js (minified)
140
+ npm run build:dev # unminified + inline sourcemap (readable TypeScript in the browser)
141
+ npm run typecheck
142
+ ```
143
+
144
+ - `src/client/` holds the whole client side; host APIs are described by local structural types (zero runtime dependency on the host — only `react`/`react-dom` are treated as platform externals).
145
+ - `lib/index.js` is the host half (a bare mounting carrier); `lib/client.js` comes from the build and is **committed alongside the source** (that is what lets a source install skip building).
146
+ - **CI is a static gate only**: `npm ci` + `tsc --noEmit` + the release build + artifact checks (bundle non-empty, contains `__ModuleLoader__.load`, no inline sourcemap). It never loads a page or connects to the host, so it gives **zero coverage of runtime behaviour** — that lives in the manual checklist in [TESTING.md](./TESTING.md).
147
+ - Diagnostics (also present in production): `window.__dshCodinputDebug / __dshCodinputErrs / __dshCodinputSessionId / __dshCodinputTriggers / __dshCodinputKeyboard / __dshCodinputShell / __dshCodinputCtx`.
148
+ - Dev helpers: `node scripts/extract-official-css.mjs <host client.js> [out.css]` extracts an official CSS module verbatim (used to replicate official styling value by value).
149
+
150
+ ### Host facts worth knowing before changing code
151
+
152
+ - **Takeover point**: `conversation.composer.bar` (a `single` slot; `priority:-1` shadows the official entry). Disposing the entry restores the official composer untouched.
153
+ - **Single input surface**: a sidebar tab body **counts as carrying the input the moment it mounts** (not based on visibility — collapsing the sidebar is CSS hiding plus a translate, the body stays mounted). The main entry renders `null` accordingly, and the mode is derived as `carrying ? 'side' : prefs.mode`, so coexistence is impossible by construction.
154
+ - **Undo history**: switching modes remounts the editor, so `EditorState` is cached per session (`take/putEditorSnapshot`); extension callbacks go through a module-level hooks slot so a reused state never calls into an unmounted instance's props.
155
+ - **Official-side writes** (pick / claim / submit-clears) carry `externalAnnotation` + `Transaction.addToHistory.of(false)`, so undo only ever reverts what you typed.
156
+ - **Drawing a circle needs `corner-shape: round`**: the host root sets `corner-shape: superellipse(1.5)` globally, so a plain `border-radius:50%` renders as a rounded square.
157
+ - **The ball occupies no slot**: `conversation.session.header.corner` is the official sidebar expand button's seat (a `single` slot — taking it would remove the official button), so the ball is portalled to `body` and positioned from the measured header rect.
158
+
159
+ ## Third-party attribution
160
+
161
+ Parts of this project **replicate DeepSeek Harness** (`@deepseek-ai/dsh`, MIT License, Copyright (c) 2026 DeepSeek): several SVG icon paths (the permission shield set, plus, paperclip, arrow-up, the generic file glyph, …) and CSS values for the composer card and attachment rail. They are used under the MIT license; the original copyright and permission notice are retained in [NOTICE](./NOTICE) alongside [LICENSE](./LICENSE).
162
+
163
+ Bundled third-party dependencies: CodeMirror 6 (MIT), marked (MIT), DOMPurify (Apache-2.0 / MPL-2.0 dual licensed).
164
+
165
+ ## License
166
+
167
+ [MIT](./LICENSE)
package/README.md ADDED
@@ -0,0 +1,167 @@
1
+ # DSH-Codinput
2
+
3
+ 中文 | [English](./README.en.md)
4
+
5
+ [![npm Version](https://img.shields.io/npm/v/dsh-codinput)](https://www.npmjs.com/package/dsh-codinput)
6
+ [![DSH Version](https://img.shields.io/badge/DSH-0.1.5--rc.2%2B-blue)](https://github.com/deepseek-ai/deepseek-harness)
7
+ [![Downloads](https://img.shields.io/npm/dm/dsh-codinput)](https://www.npmjs.com/package/dsh-codinput)
8
+ [![GitHub Stars](https://img.shields.io/github/stars/Witherwithwinter/DSH-Codinput)](https://github.com/Witherwithwinter/DSH-Codinput/stargazers)
9
+ [![License](https://img.shields.io/github/license/Witherwithwinter/DSH-Codinput)](./LICENSE)
10
+
11
+ > 把 DeepSeek Harness WebUI 的聊天输入框换成**代码编辑器风格**的输入面板:CodeMirror 6 接管 `conversation.composer.bar`,保留官方的 `/` `@` 触发管线、模型与权限控件、统计投影与草稿持久化。
12
+
13
+ <p>
14
+ <img src="./assets/icon.svg" width="22" alt="" align="top" />
15
+ &nbsp;图标:lucide <code>file-code-corner</code>(同时用作侧栏收起后的唤出小球)
16
+ </p>
17
+
18
+ ![普通模式](./assets/preview-normal.png)
19
+
20
+ <table>
21
+ <tr>
22
+ <td width="50%"><img src="./assets/preview-split.png" alt="编辑 + 预览分屏" /><br /><sub>编辑 + 预览分屏(严格对半)</sub></td>
23
+ <td width="50%"><img src="./assets/preview-float.png" alt="悬浮模式" /><br /><sub>悬浮模式(可拖动位置、八向缩放)</sub></td>
24
+ </tr>
25
+ <tr>
26
+ <td width="50%"><img src="./assets/preview-side.png" alt="侧栏标签模式" /><br /><sub>侧栏标签模式(VS Code 面板式全高)</sub></td>
27
+ <td width="50%"><img src="./assets/preview-ball-detail.png" alt="右上角小球" /><br /><sub>侧栏收起时的唤出小球</sub></td>
28
+ </tr>
29
+ </table>
30
+
31
+ <p align="center">
32
+ <sub>界面语言跟随宿主设置(中文 / English)—— 文案全部走宿主 locale 服务,上图为中文界面;英文界面见 <a href="./README.en.md">English README</a></sub>
33
+ </p>
34
+
35
+ ## 特性
36
+
37
+ **三态单实例**——输入入口只有三种形态,任一时刻恰好一个,绝不与官方输入框共存:
38
+
39
+ | 形态 | 进入方式 | 说明 |
40
+ | --- | --- | --- |
41
+ | 普通 `normal` | 默认 | 接管卡片在原输入位 |
42
+ | 悬浮 `float` | 按住卡片顶部行空白区拖动 | 脱出为悬浮窗:可拖动位置、可拖边界/角落缩放(位置与尺寸记忆) |
43
+ | 侧栏标签 `side` | 打开右侧栏的「Codinput」标签 | 输入在侧栏面板内;**侧栏收起不让位**,由标题栏下方的圆形小球唤回 |
44
+
45
+ - **编辑器**:行号(可关)、当前行与行号高亮、软换行、Tab 缩进、行列指示、**撤销历史跨形态保留**(拖成悬浮/切到侧栏再回来,`Ctrl+Z` 依旧有效)。
46
+ - **`/` `@`**:完全走官方触发管线(track → 官方 menu store → 复刻的 MenuView),候选菜单、钻取、crumbs、骨架屏逐项对齐官方;命令菜单顺序在空查询下按名称排序。
47
+ - **模型与思考强度**:读官方共享目录(与 `/model` 同一状态源);**权限**:官方三预设 + 完全权限的 RiskConfirmation 模态。
48
+ - **附件**:点 `paperclip` 选文件、**直接粘贴图片**、把文件**拖进卡片**三条路径(失败释放草稿附件,带上传状态芯片)。
49
+ - **数据行**:官方 composer stats 复刻(轮次/步数 · tok/s、累计 tok · 缓存命中)+ 上下文占用小圈,均可点开详情面板;主输入/悬浮/侧栏三形态都有。
50
+ - **设置**:设置 → Codinput(启用接管、行号、字体、发送/换行快捷键**任意组合录制**、默认视图)。
51
+ - **草稿**:官方输入机是唯一事实源(单写入口),形态切换、插件禁用、重启都不丢。
52
+
53
+ ## 安装
54
+
55
+ 要求:宿主 `@deepseek-ai/dsh`(见下方[兼容性](#兼容性))。
56
+
57
+ ### 方法 A:npm 包
58
+
59
+ ```bash
60
+ dsh plugin --profile web add dsh-codinput
61
+ ```
62
+
63
+ 一条命令就够:`dsh plugin` 走官方 profile 包管理,装完会读这个包的 `dsh.bundle` 元数据、校验它自带的 `cordis.patch.yml`(`dsh.bundle.patch`),并把包名追加进 profile 的 `bundles`——不需要手写任何 patch 行。
64
+
65
+ 然后 `dsh web` 启动(已在跑的话强刷页面即可,profile 默认 `patchReload: live`)。
66
+
67
+ ### 方法 B:GitHub 源码
68
+
69
+ ```bash
70
+ dsh plugin --profile web add github:Witherwithwinter/DSH-Codinput
71
+ ```
72
+
73
+ 仓库里带着构建产物 `lib/`,所以装源码同样**不需要本地构建**。如果 pnpm 提示需要构建授权(例如你换成自己的 fork 且删掉了 `lib/`),按它打印的键名写进 profile 的 `pnpm-workspace.yaml` 后重跑:
74
+
75
+ ```yaml
76
+ allowBuilds:
77
+ esbuild@0.25.12: true
78
+ ```
79
+
80
+ ### 方法 C:本地源码(开发)
81
+
82
+ ```bash
83
+ git clone https://github.com/Witherwithwinter/DSH-Codinput.git
84
+ dsh plugin --profile web add link:/abs/path/to/DSH-Codinput
85
+ ```
86
+
87
+ `link:` 安装不会复制文件,改完 `npm run build` 刷新页面即生效。
88
+
89
+ ### 方法 D:等价的手写配置
90
+
91
+ 想在 profile 里锁版本、或要手工维护时,等价于**方法 A** 的是(`~/.dsh/profiles/web/package.json`):
92
+
93
+ ```jsonc
94
+ {
95
+ "dependencies": {
96
+ "dsh-codinput": "^0.1.0" // 本地开发用 "link:/abs/path/to/DSH-Codinput"
97
+ },
98
+ "dsh": {
99
+ "profile": {
100
+ "bundles": [
101
+ "@deepseek-ai/dsh-base",
102
+ "@deepseek-ai/dsh-web-app",
103
+ "dsh-codinput" // ← 追加在最后
104
+ ],
105
+ "patchReload": "live" // 改完 lib/client.js 强刷页面即生效
106
+ }
107
+ }
108
+ }
109
+ ```
110
+
111
+ 改完手工配置后,在 profile 目录里 `pnpm install`(或 `dsh plugin --profile web install`),再 `dsh web`。
112
+
113
+ ## 兼容性
114
+
115
+ 宿主通过 npm 的 `latest` 通道发的是 **rc** 版本(`next` 是下个 rc,`alpha` 是更超前的通道),所以普通用户装到的就是 rc。
116
+
117
+ | 宿主版本 | 状态 |
118
+ | --- | --- |
119
+ | `0.1.5-rc.2` | ✅ 实机实测通过(普通安装拿到的就是这一版) |
120
+ | `0.1.6-alpha.2` | ✅ 开发基线,全部功能端到端实测通过 |
121
+ | 其它 | 未验证 |
122
+
123
+ rc 这一版除了实机跑过,还做过一次静态契约核对:从 npm 取该版本的客户端包,逐项确认本插件依赖的契约仍在——用到的 3 个槽位(`conversation.composer.bar`、`sidebar.right.pane.tab`、`settings.section`)、`sidebarRight` / `sidebarRightTabs` / `locale` 服务与 `openTabIn`、侧栏展开规划(dockkit `planSetExpanded`)、以及复刻所依据的官方原语(`fileSizeText` / `fileExtension` 与附件 glyph 路径,逐字一致)。rc 相对开发基线只少了几个插槽(`conversation.content`、`conversation.input.permission`、`conversation.session.header.leading`、`sidebar.right.tab.guide.entry`),本插件均未使用。换通道复验的步骤见 [TESTING.md](./TESTING.md) 的「换通道验证」一节。
124
+
125
+ 插件对宿主内部结构的依赖较多(槽位遮蔽、`useTabInfo`、`sidebarRight`、`--dsw-*` 令牌),代码里处处用 `typeof` 守卫 + `try/catch` 降级,但宿主大版本升级仍可能失效。**升级宿主后请按 [TESTING.md](./TESTING.md) 走一遍冒烟清单。**
126
+
127
+ ## 已知限制
128
+
129
+ - `/` `@` 的候选内容来自宿主目录(技能 / 命令 / 文件 / 会话);宿主不提供时为空,插件侧无法补。
130
+ - **没有语法高亮**、**没有 Esc 功能**——产品决策(输入面板不是代码阅读器)。
131
+ - 侧栏标签可见期间主输入整体隐藏(唯一输入入口约定);官方 stats dock 与上下文小圈都渲染在官方输入框内部,因此随之消失、由本插件的复刻数据行接管。
132
+ - 同一 pane 复制出第二个「Codinput」标签时,第二个面板只出提示、不渲染第二个编辑面(输入入口唯一)。
133
+ - 悬浮模式的手势只保留「拖回底部输入区 = 普通」;「拖入右侧栏 = 侧栏模式」已按产品决策舍弃。
134
+
135
+ ## 开发
136
+
137
+ ```bash
138
+ npm install
139
+ npm run build # 压缩产物 → lib/client.js(发布用)
140
+ npm run build:dev # 未压缩 + 内联 sourcemap(浏览器里直接看 TS 源码)
141
+ npm run typecheck
142
+ ```
143
+
144
+ - `src/client/` 是全部客户端源码;宿主 API 用本地结构类型描述(运行时零宿主依赖,只把 `react`/`react-dom` 当平台外部模块)。
145
+ - `lib/index.js` 是宿主半边(纯挂载载体);`lib/client.js` 由构建产出,并**入库随源码一起发**(源码安装才能免构建)。
146
+ - **CI 只做静态把关**:`npm ci` + `tsc --noEmit` + 发布构建 + 产物自检(bundle 非空、含 `__ModuleLoader__.load`、无内联 sourcemap)。它不加载页面、不连宿主,**运行时行为零覆盖**——行为验证走 [TESTING.md](./TESTING.md) 的手动清单。
147
+ - 诊断口(调试用,生产也在):`window.__dshCodinputDebug / __dshCodinputErrs / __dshCodinputSessionId / __dshCodinputTriggers / __dshCodinputKeyboard / __dshCodinputShell / __dshCodinputCtx`。
148
+ - 开发辅助脚本:`node scripts/extract-official-css.mjs <宿主某包 client.js> [out.css]` 抽取官方 CSS module 原文,用于逐值复刻。
149
+
150
+ ### 改代码前值得先知道的宿主事实
151
+
152
+ - **接管点**:`conversation.composer.bar`(single 槽位,`priority:-1` 遮蔽官方条目)。卸载条目即官方输入框原样恢复。
153
+ - **唯一输入入口**:侧栏标签 body **挂载即承载**(不是按可见性——收起侧栏是 CSS 隐藏 + translate,body 仍挂载),主条目据此渲染 `null`;形态由 `承载 ? 'side' : prefs.mode` 派生,构造上不可能并存。
154
+ - **撤销历史**:三态切换会重挂载编辑器,故按会话暂存 `EditorState`(`take/putEditorSnapshot`);扩展回调走模块级 hooks 槽,避免复用旧 state 时调到已卸载实例的 props。
155
+ - **官方侧灌入**(pick/claim/提交清空)用 `externalAnnotation` + `Transaction.addToHistory.of(false)`:不进撤销栈,撤销只记用户自己敲的内容。
156
+ - **画圆要写 `corner-shape: round`**:宿主根节点设了 `corner-shape: superellipse(1.5)`(全局超椭圆),只写 `border-radius:50%` 会渲染成圆角方。
157
+ - **小球不占槽位**:`conversation.session.header.corner` 是官方侧栏展开按钮的座位(single,占它会把官方按钮顶掉),故小球 portal 到 body、量标题栏矩形定位到标题栏下方。
158
+
159
+ ## 第三方归属
160
+
161
+ 本项目的部分视觉素材与样式取值**复刻自 DeepSeek Harness**(`@deepseek-ai/dsh`,MIT License,Copyright (c) 2026 DeepSeek),包括若干 SVG 图标路径(权限盾牌系列、plus / paperclip / arrow-up 等)与输入框卡片的 CSS 取值。依 MIT 许可使用,原始版权与许可声明见仓库根目录 [NOTICE](./NOTICE) 与 [LICENSE](./LICENSE)。
162
+
163
+ 内置第三方依赖:CodeMirror 6(MIT)、marked(MIT)、DOMPurify(Apache-2.0 / MPL-2.0 双许可)。
164
+
165
+ ## 许可
166
+
167
+ [MIT](./LICENSE)
package/TESTING.md ADDED
@@ -0,0 +1,95 @@
1
+ # 冒烟清单
2
+
3
+ **本项目没有自动化测试**:这是 UI 插件,行为依赖宿主 alpha 的槽位遮蔽、输入机与侧栏内部结构,跑不起 DOM 级别的用例,成本收益不划算。
4
+
5
+ `.github/workflows/ci.yml` **不是测试套件,只做静态把关**——`npm ci` 装依赖、`tsc --noEmit` 类型检查、发布构建(压缩、无内联 sourcemap)、产物自检(bundle 非空、含 `__ModuleLoader__.load`、无内联 sourcemap)。它不加载页面、不连宿主、不跑任何交互,所以**运行时行为它是零覆盖**:CI 全绿只说明「类型对、构建得出、发出去的不是调试产物」。
6
+
7
+ 凡是「功能到底对不对」的问题,都靠下面这份清单手动过。改完代码、升级宿主、发布前,在内置浏览器/浏览器里走一遍。
8
+
9
+ 准备工作:`npm run build`(发布产物)或 `npm run build:dev`(调试用,源码可读),强刷页面(宿主 `patchReload: live` 下改 `lib/client.js` 只需刷新)。
10
+
11
+ > 推 GitHub / 发 npm 之前确保 `lib/client.js` 是**当前源码**构建出来的:仓库带构建产物,走源码安装的人是直接用这份的。CI 只重建、不比对,`git status lib/` 有改动就说明提交里的产物落后于源码(构建可复现,同源码两次构建哈希一致)。
12
+
13
+ ## 0. 载入与健康
14
+
15
+ - [ ] 页面刷新后无报错:`window.__dshCodinputErrs.length === 0`
16
+ - [ ] `window.__dshCodinputShell` 含 `apply: 'done'`、`bar: 'registered'`、`settings: 'registered'`、`sidebar.body: 'registered'`
17
+ - [ ] `__dshCodinputShell` 里没有 `failed` 项(槽位废黜会记 `slot.entry` 错误)
18
+
19
+ ## 1. 三态单实例(核心不变式)
20
+
21
+ 任何一步之后都断言:`document.querySelectorAll('.dci-surface').length === 1`,且官方输入框不存在(`[data-composer-seat]` 里除 `.dci-card` 外没有可编辑元素)。
22
+
23
+ - [ ] **普通**:接管卡片在原输入位,`.dci-surface[data-side]` / `[data-float]` 都不存在
24
+ - [ ] **悬浮**:按住卡片顶部行空白区拖动 → 脱出为 `.dci-float`,跟手;松手在底部输入区之外 → 保持悬浮
25
+ - [ ] **悬浮 → 普通**:拖回底部输入区松手 → 回到原位(位置/尺寸记忆保留)
26
+ - [ ] **悬浮缩放**:拖四个边与四个角落 → 1:1 跟手、对边不动、不越出视口、不小于 360×140;松手后刷新页面尺寸保持
27
+ - [ ] **侧栏**:展开右侧栏 → 从指南页点「Codinput」→ 输入面出现在侧栏,**主输入位为空**
28
+ - [ ] **侧栏收起不让位**:收起右侧栏 → 主输入**不**回来,右上角出现圆形小球(标题栏下方、右对齐、与官方侧栏按钮不重叠)
29
+ - [ ] **小球唤回**:点小球 → 侧栏展开且 Codinput 可见,小球消失
30
+ - [ ] **退出侧栏**:关闭 Codinput 标签 → 主输入按 `prefs.mode` 原样恢复(普通卡片或悬浮)
31
+ - [ ] **复制标签**:右侧栏「分栏」后,两栏各开一个 Codinput 标签 → 第二个面板只出提示,全局仍只有 1 个 `.cm-editor`
32
+
33
+ ## 2. 编辑与草稿
34
+
35
+ - [ ] 输入中文(输入法)→ 选词回车**不发送**、正常上屏
36
+ - [ ] 行号/当前行高亮/软换行/行列指示正常;Tab 插入缩进不夺焦点
37
+ - [ ] **撤销历史跨形态**:打字 → 拖成悬浮 → `Ctrl+Z` 能撤销刚才的字(不是撤销成空)
38
+ - [ ] **侧栏键入回写**:在侧栏标签里打字 → 关掉标签,主输入位应带着刚打的内容;侧栏内移动光标,「行, 列」指示实时更新(回归点:编辑器 hooks 是模块级单槽,若卸载时无条件清空,晚卸载的旧实例会把新实例的 hooks 摘掉——症状就是侧栏里打字不落草稿、行列指示不动、发送手势失效)
39
+ - [ ] **官方侧灌入不进撤销栈**:发送一条消息后按 `Ctrl+Z`,不应把已发送内容拉回编辑器
40
+ - [ ] 草稿在 形态切换 / 刷新页面 / 切换会话 后都不丢
41
+
42
+ ## 3. 触发管线与菜单
43
+
44
+ - [ ] 输入 `/` → 官方候选菜单在卡片上方弹出(侧栏/悬浮态改为头部行下方向下弹出),组标题本地化
45
+ - [ ] 输入 `@` → 文件/会话候选、section 小节标题、钻取(Tab/chevron)、crumbs 正常
46
+ - [ ] 菜单打开时 ↑↓/Enter/Tab 由菜单消费;**菜单关闭时 Enter 是换行**(不被吞)
47
+ - [ ] 鼠标点击候选能补全;卡片内按下不关闭菜单
48
+ - [ ] 工具行 `plus` 按钮能程序化唤起 `/` 菜单
49
+
50
+ ## 4. 工具行控件
51
+
52
+ - [ ] 模型下拉:显示当前模型与推理等级,切换后宿主 `/model` 一致
53
+ - [ ] 权限下拉:三预设 + glyph;选「完全权限」弹确认模态(勾选后才能启用)
54
+ - [ ] 附件:点 paperclip 选文件 / **粘贴图片** / **拖文件进卡片**(拖入时有描边提示)三条路径都能入草稿并出现芯片;移除附件正常
55
+ - [ ] 发送按钮:草稿与附件皆空时禁用;空草稿手势转队列 Steer
56
+ - [ ] 上下文小圈:百分比与分段面板数据合理
57
+
58
+ ## 5. 设置
59
+
60
+ - [ ] 设置 → Codinput:启用接管(关闭后官方输入框原样恢复、草稿不丢)、行号、字体、发送/换行快捷键录制(任意组合、冲突拒绝)
61
+ - [ ] 偏好刷新后保持(`localStorage['dsh-codinput.prefs.v2']`)
62
+
63
+ ## 6. 回归提示
64
+
65
+ - 宿主升级后,重点复验第 1 节的**三态与侧栏承载**、第 3 节**菜单**(宿主席位/令牌变化最先断在这里)。
66
+ - 槽位条目渲染崩溃会被「废黜」(本注册周期内永久改用下一条目):症状是形态卡住/输入面消失,先看 `__dshCodinputErrs` 里的 `slot.entry`。
67
+
68
+ ## 7. 换通道验证(rc / alpha)
69
+
70
+ 宿主在 npm 上有三个通道,**普通用户拿到的不是 alpha**:
71
+
72
+ ```bash
73
+ npm view @deepseek-ai/dsh dist-tags # latest = 最新 rc;next = 下一个 rc;alpha = 更超前
74
+ dsh --version # 你本机实际跑的那个
75
+ ```
76
+
77
+ `latest` 通道发的是 rc,所以「装了 dsh 插件却跑不起来」的报障大概率发生在 rc 上。开发机可以待在 alpha,但**对外声称的兼容范围应当以 rc 为准**,至少在 rc 上跑一遍第 1、3 节。
78
+
79
+ > **已验记录**:`0.1.5-rc.2` 于 2026-09-23 在另一台机器上实机跑通(README 兼容表据此标为实测通过)。换宿主版本后请重新走一遍下面这节。
80
+
81
+ 不动现有安装的验证方式(独立 prefix + 独立 `DSH_HOME`,不碰你在用的 profile 与会话):
82
+
83
+ ```bash
84
+ # 1) 把另一个通道装到临时 prefix,不覆盖全局安装
85
+ npm i --prefix /tmp/dsh-rc @deepseek-ai/dsh@0.1.5-rc.2
86
+
87
+ # 2) 复制一份独立 home(含带 codinput 的 profile)
88
+ cp -r ~/.dsh /tmp/dsh-rc-home
89
+
90
+ # 3) 起在另一个端口,避免和正在跑的实例撞车
91
+ DSH_HOME=/tmp/dsh-rc-home /tmp/dsh-rc/node_modules/.bin/dsh web --port 3081 --no-open
92
+ ```
93
+
94
+ 重点复验:第 1 节**三态与侧栏承载**(`conversation.composer.bar` 遮蔽、`sidebar.right.pane.tab` 承载判定、收起后的唤回小球)与第 3 节**触发管线与菜单**——宿主契约变化最先断在这两处,其余功能是自绘的,跨通道风险低。
95
+
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#8b949e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12.15V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2h-3.35"/><path d="M14 2v5a1 1 0 0 0 1 1h5"/><path d="m5 16-3 3 3 3"/><path d="m9 22 3-3-3-3"/></svg>
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,5 @@
1
+ # dsh-codinput bundle 层:插入 Codinput 输入接管插件行。
2
+ - insert:
3
+ - id: dsh-codinput
4
+ name: dsh-codinput
5
+ config: {}