dsh-client-ui-model-seat 0.0.0-stage → 0.1.2

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 ADDED
@@ -0,0 +1,51 @@
1
+ # CHANGELOG
2
+
3
+ 版本号跟 `package.json` 的 `version` 走;每条只写用户能感知的行为变化和会咬人的坑,实现细节在 [`MAINTENANCE.md`](https://github.com/HimA6652/dsh-client-ui-model-seat/blob/main/MAINTENANCE.md)。
4
+
5
+ ## [Unreleased]
6
+
7
+ ## [0.1.2] - 2026-10-28
8
+
9
+ 界面与 0.1.1 一致,这一版把发布通道和文档补齐,npm 通道从这一版开通。
10
+
11
+ ### Added
12
+
13
+ - `npm run check:secret` / `npm run check:pack`:发布前扫凭据形状与本机路径,并拿 `npm pack` 的真实产物跟 `files` 四向对账(目录项、`.bak` / `.log`、声明了却没打进去的文件都会被拦下)。
14
+ - `scripts/release.mjs`:查版本说明 → 工作区干净 → 在 main → 本地与远端标签都不存在,四道闸过后才发;tag 推送后由 Release workflow 建 GitHub Release,正文取自本文件。
15
+ - `CHANGELOG.md`(本文件)。
16
+
17
+ ### Changed
18
+
19
+ - 制品清单从目录改成逐文件枚举:0.1.1 的 `files` 写的是 `assets`,任何临时截图、`.bak` 都会跟着包走;现在只发 `files` 里的 11 条确定路径(加上 `package.json` 一共 12 个文件)。
20
+ - 文档里的本机绝对路径全部换成 `/path/to/...` 占位;README 的维护文档、更新日志链接改为绝对地址(在 npm 页面上原本是死链)。
21
+ - README 增加「适用场景」与「已知限制」两节,并写明当前只有 GitHub 安装通道。
22
+
23
+ ### Upgrade
24
+
25
+ - 装了 0.1.1 的不用重装,界面与存储键都没变(颜色仍在 `dsh-client-ui-model-seat:config:v1`)。
26
+ - 这一版起可以直接用 npm 通道:`dsh plugin --profile <profile> add dsh-client-ui-model-seat`;GitHub 通道照旧。
27
+
28
+ ## [0.1.1] - 2026-10-28
29
+
30
+ 首个公开版本:把两个只能二选一的 composer 模型控件合成一个。
31
+
32
+ ### Added
33
+
34
+ - 合并后的模型控件:Provider / 模型触发按钮 + 两列卡片,占用 `conversation.input.model` 席位并以 `priority: -2` 注册,因此原插件(`-1`)或内置控件(`0`)留在 profile 里也不会撞车。
35
+ - 思考强度触发按钮与粒子滑块;`600 ms` 长按闪电锁定 fast 模式,锁定状态跨刷新保留。
36
+ - 跨供应商搜索:输入时把所有供应商的同名模型列在一起,卡片尺寸不变。
37
+ - 外观面板(`Ctrl+Shift+Alt+G`):双主题色板、方案库、JSON 导入导出、跨主题套用。
38
+
39
+ ### Changed
40
+
41
+ - 卡片高度改为测量一次后冻结,供应商列悬停只换右侧内容、不改外框尺寸。
42
+ - 不再输出 `data-gpt-helper` / `data-dsh-pm-*` 钩子;卡片改用 `.ms-*` 与 `data-model-seat`。
43
+
44
+ ### Fixed
45
+
46
+ - 某个供应商模型过多时选择窗口反复放大缩小:卡片贴底、按内容自适应高度、又按悬停换列,构成布局与指针的回环(旧实现实测 900 ms 内 27 次高度翻转)。现在几何只由锚点与布局决定,两列各自滚动。
47
+
48
+ ### Upgrade
49
+
50
+ - 从原插件迁过来:先 `dsh plugin remove dsh-client-ui-gpt-helper` 与 `dsh plugin remove dsh-model-provider-prefix`,再装本插件;两个原插件与本插件不要同时启用。
51
+ - 颜色配置不继承旧键(不读 `dsh-client-ui-gpt-helper:config:v1`),首次启动是默认配色,之后存到 `dsh-client-ui-model-seat:config:v1`。
package/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 AprilCrystal (dsh-client-ui-gpt-helper)
4
+ Copyright (c) 2026 zszz3 (dsh-model-provider-prefix)
5
+ Copyright (c) 2026 HimA6652 (this merged plugin)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all
15
+ copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,230 @@
1
- # Temporary Holding Version
1
+ # dsh-client-ui-model-seat
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The model + thinking control of the dsh composer, merged from two plugins that
4
+ both replaced the same seat and therefore could not be installed together:
5
+
6
+ - **dsh-client-ui-gpt-helper** — the shell: token-driven colours, the particle
7
+ thinking slider, fast mode with its long-press lock, the appearance panel,
8
+ the i18n dictionaries and the per-session wiring.
9
+ - **dsh-model-provider-prefix** — the skeleton of the card: a provider column
10
+ beside a model column, hover decides which provider the model column shows,
11
+ and a failed provider turns into a retry row.
12
+
13
+ ![The card: providers on the left, the hovered provider's models on the right](https://raw.githubusercontent.com/HimA6652/dsh-client-ui-model-seat/main/assets/screenshot-1.png)
14
+ ![Search reaches every provider and the card keeps its size](https://raw.githubusercontent.com/HimA6652/dsh-client-ui-model-seat/main/assets/screenshot-2.png)
15
+ ![The thinking pane: particle slider, fast toggle, long-press lock](https://raw.githubusercontent.com/HimA6652/dsh-client-ui-model-seat/main/assets/screenshot-3.png)
16
+
17
+ Use this one **instead of** either original — not beside them:
18
+
19
+ dsh plugin remove dsh-client-ui-gpt-helper
20
+ dsh plugin remove dsh-model-provider-prefix
21
+
22
+ Maintaining this file: see [MAINTENANCE.md](https://github.com/HimA6652/dsh-client-ui-model-seat/blob/main/MAINTENANCE.md) (Chinese) for the
23
+ code map, the invariants that keep the card stable, the test matrix and the
24
+ real-app verification recipes. Changes per release: [CHANGELOG.md](https://github.com/HimA6652/dsh-client-ui-model-seat/blob/main/CHANGELOG.md).
25
+
26
+ Who it is for / 适用场景:
27
+
28
+ | You are… | This plugin gives you |
29
+ | --- | --- |
30
+ | Someone who needs the provider next to the model | One trigger showing `provider / model`, one card with both columns. |
31
+ | Someone whose provider list is long | Hover the provider column; the model column swaps and the card stays put. |
32
+ | Someone who tunes reasoning effort | The effort trigger and its particle slider, plus fast mode with a long-press lock. |
33
+ | Someone carrying both original plugins | One seat instead of two that cannot coexist. |
34
+
35
+ Requires dsh `>= 0.2.1-rc.2` (`engines.dsh`). Client-side only: no network calls,
36
+ no credential access, no host module.
37
+
38
+ ## What it does
39
+
40
+ | Surface | Behaviour |
41
+ | --- | --- |
42
+ | Model trigger | `provider / model` plus a caret. Opens the card on the provider+model columns; a second click closes it. |
43
+ | Effort trigger | The current thinking level (`Max`, `Medium`, …). Opens the card on the particle slider; rendered only when the selected model declares thinking levels, and its label becomes `Fast on` while fast mode is locked. |
44
+ | Provider column | Every provider in the session directory with its model count, a check on the current provider, and a retry row for a provider whose list failed to load. |
45
+ | Model column | Head shows the provider under the pointer; a 30 px search box sits inside the column. Hovering the provider column swaps the models **without moving the card**. |
46
+ | Search | Typing searches **every** provider at once and shows the provider name on each row. The card keeps its two-column shape while typing — a re-shaping card is exactly the resize flicker this plugin removes. |
47
+ | Thinking slider | The particle slider from the shell, over the level ladder of the selected model, with keyboard arrows/Home/End. |
48
+ | Fast toggle | A 600 ms long-press on the bolt locks fast mode; the lock survives a reload (`…:fast:v1`) and re-arms when the window regains focus. |
49
+ | Appearance panel | `Ctrl+Shift+Alt+G`. Colour tokens per side (light/dark), scheme library, JSON import/export, reset, cross-apply to the other side, live preview. |
50
+
51
+ Geometry: the card is `416 px` wide in the column layout and `300 px` in the
52
+ thinking panel, clamped to the viewport; heights clamp to `132…420 px` and the
53
+ card flips above the trigger when that gives more room.
54
+
55
+ ## The flicker this merge had to fix
56
+
57
+ The upstream card was anchored by its **bottom** edge and sized by its
58
+ **content**, while its content was chosen by the **row under the pointer**. With
59
+ one provider holding more models than the card could show, that closed a loop:
60
+
61
+ 1. the card grows to fit the model list, so its top edge moves up;
62
+ 2. the pointer, which was over a provider row, is now over a different row (or
63
+ outside the card), so the model column swaps or empties;
64
+ 3. the card shrinks, the pointer is back over the first row, and the loop runs
65
+ again — measured at **27 height flips in 900 ms** on the old bundle.
66
+
67
+ The merged card breaks the loop by making the frame a function of the anchor and
68
+ the layout only:
69
+
70
+ - `frame(anchor, layout, natural, floor)` is pure: it clamps width and height to
71
+ the viewport and flips above the trigger when that gives back more room.
72
+ - The natural height is measured **once** per (layout, status, level count),
73
+ while the card is still hidden (`data-ready="false"`), and then frozen for as
74
+ long as the card stays open on that key.
75
+ - The columns scroll internally (`scrollbar-gutter: stable`), so a long model
76
+ list never changes the card outer height.
77
+ - `box-sizing: border-box` keeps the inline height equal to what was measured.
78
+ - `floor` is the height the card already had, so any later re-measure inside the
79
+ same placement can only grow the card, never collapse it over the pointer. A
80
+ new anchor (resize, scroll) starts the floor over at zero.
81
+ - The query does not change the layout, so typing never resizes the card.
82
+
83
+ Re-measurement happens only on open, pane change, anchor move (scroll/resize) or
84
+ a status change — never on hover.
85
+
86
+ ## Configuration
87
+
88
+ Everything user-facing is edited in the appearance panel; there is no profile
89
+ config key. Two `localStorage` records hold the state:
90
+
91
+ | Key | Shape |
92
+ | --- | --- |
93
+ | `dsh-client-ui-model-seat:config:v1` | `{ version: 3, hotkey, paused, crossApply, slots: { light: { scheme }, dark: { scheme } }, schemes: { name: { name, colors } }, sides: { light, dark } }` |
94
+ | `dsh-client-ui-model-seat:fast:v1` | `{ fast, locked }` |
95
+
96
+ `paused: true` switches every custom colour off (the panel master switch);
97
+ `slots[side].scheme` names the active scheme for a side, `sides[side]` holds the
98
+ per-token tune-ups (they win over the active scheme, so a manual edit is never
99
+ swallowed), and `crossApply` makes one side colour both.
100
+ Records written by the shell plugin (`dsh-client-ui-gpt-helper:config:v1`) are
101
+ **not** read: the schema changed and a silent half-migration would be worse than
102
+ the defaults. Both records are read on load, on a `storage` event, and when the
103
+ window regains focus.
104
+
105
+ ## DOM contract
106
+
107
+ Stable hooks, for CSS and for tests:
108
+
109
+ | Element | Attribute |
110
+ | --- | --- |
111
+ | Root | `[data-model-seat="root"]`, plus `data-build` |
112
+ | Triggers | `[data-model-seat="model-trigger"]`, `[data-model-seat="effort-trigger"]` |
113
+ | Card | `[data-model-seat="menu"]` with `data-pane`, `data-layout`, `data-ready`, `data-flipped`, `data-fast`, `data-ultra`, `data-locked` |
114
+ | Columns | `.ms-col[data-col="providers"]`, `.ms-col[data-col="models"]` |
115
+ | Column head | `.ms-colHead`, separator `.ms-sep` |
116
+ | Search | `.ms-search` (inside the models column) |
117
+ | Rows | `.ms-row[data-kind="provider"\|model\|retry"]`, `data-active`, `data-current`, count `.ms-count`, note `.ms-rowNote` |
118
+ | Slider / fast toggle | `[data-model-seat="thinking-slider"]`, `[data-model-seat="fast-toggle"]` |
119
+ | Colour panel | `[data-model-seat="config-panel"]`, backdrop `[data-model-seat="config-backdrop"]` |
120
+ | Fallback | `[data-model-seat="fallback"]` — what the error boundary renders |
121
+
122
+ That is the whole contract: since 0.1.1 nothing emits `data-gpt-helper` or
123
+ `data-dsh-pm-*`. CSS written against either upstream plugin no longer matches,
124
+ which is the point — a leftover `[data-gpt-helper]` rule from the shell plugin
125
+ would otherwise restyle this card. `npm run validate` fails if either prefix
126
+ comes back.
127
+
128
+ ## Install
129
+
130
+ dsh plugin add github:HimA6652/dsh-client-ui-model-seat # from source (works today)
131
+ dsh plugin add link:/absolute/path/to/dsh-client-ui-model-seat # local checkout
132
+
133
+ The npm channel is not open yet: this package has no registry release, so
134
+ `dsh plugin add dsh-client-ui-model-seat` will fail with `404` until it does. Use
135
+ the GitHub install above. Once published, the same command resolves from the
136
+ registry without any change on your side.
137
+
138
+ The plugin bundle inserts one row into the profile (`id: model-seat`) and
139
+ registers its seat at `priority: -2`, so a leftover copy of either original
140
+ (`-1`) or the shipped control (`0`) is shadowed instead of colliding.
141
+
142
+ A `desktop` profile cannot be edited by the plain CLI — it answers
143
+ `profile "desktop" is managed exclusively by the Electron application`. Use the
144
+ desktop host CLI instead (see MAINTENANCE.md, "安装、升级、回滚").
145
+
146
+ Screenshots are generated, not hand-cropped: `npm run shots` renders the real
147
+ bundle in the harness over a demo provider set and clips each PNG at 2x.
148
+
149
+ ## Verifying on a real dsh
150
+
151
+ `npm run e2e` runs the bundle against a fixture page. To check the same bundle
152
+ inside a real dsh web app without touching your own store, give it a throwaway
153
+ `DSH_HOME`:
154
+
155
+ SEAT_HOME=C:/Users/you/AppData/Local/Temp/seat-live-home
156
+ mkdir -p "$SEAT_HOME" && cp ~/.dsh/.credentials.yaml "$SEAT_HOME/"
157
+ export DSH_HOME=$SEAT_HOME
158
+ DSH=<checkout>/node_modules/@deepseek-ai/dsh/lib/bin.js
159
+ node "$DSH" --profile seatcheck --from-default-profile web --help # creates seatcheck from the web template
160
+ node "$DSH" plugin --profile seatcheck add link:$PWD
161
+ node "$DSH" --profile seatcheck --port 6124 --host 127.0.0.1 --no-open
162
+
163
+ The credentials copy is what makes the account model directory resolve; without
164
+ it the card is empty. The banner prints the URL with its `?token=`; hand that to
165
+
166
+ npm run live -- --url "http://127.0.0.1:6124/?token=..."
167
+
168
+ `--publish` also clicks a model row and asserts the trigger relabels. That writes
169
+ the account model selection, which is why it is off by default and why the run
170
+ belongs on a throwaway `DSH_HOME`. The check reports the trigger labels, the
171
+ card layout/height/width, a 900 ms frame sample, the thinking pane, and any
172
+ console error naming the seat; it exits non-zero on a problem. Your own `~/.dsh`
173
+ store and the desktop app are never involved.
174
+
175
+ ## Development
176
+
177
+ npm run check # secrets + pack + validate + css audit + load check + tests
178
+ npm run check:secret # dev paths, tokens, private keys in every text file
179
+ npm run check:pack # `files` vs the real npm artifact, in both directions
180
+ npm run validate # manifest, patch row, entry module, bundle load, apply
181
+ npm run css # stylesheet scoping and dead-class audit
182
+ npm run loadcheck # against the installed dsh checkout (skips when absent)
183
+ npm test # node:test unit and integration tests
184
+ npm run e2e # drives the real bundle in headless Edge over CDP
185
+ npm run check:all # check + e2e
186
+ npm run live -- --url URL # the same checks inside a real dsh
187
+ npm run release 0.2.0 # gates → npm → commit → tag → push (see CHANGELOG rule)
188
+
189
+ `npm run e2e` serves `test/harness/` on a loopback port, opens it in headless
190
+ Edge over CDP and asserts, in nine sections: the two-column card, a constant
191
+ height while the pointer rests over 60 models, close/reopen determinism, the
192
+ cross-provider query, the empty-query note (one padded `.ms-empty`, zero rows),
193
+ the slider keyboard path, the fast toggle (click, long-press lock, unlock), the
194
+ head status button returning to the columns, the colour-panel hotkey, and a
195
+ clean console. `--shot DIR` writes screenshots;
196
+ `--dump` prints the card box and its children. The e2e loads React from the
197
+ committed `test/vendor/` UMD builds, so a fresh clone needs no external
198
+ directory and no network; `--vendor DIR` / `REACT_VENDOR=DIR` override it.
199
+
200
+ `lib/client.js` is **hand-written** — a 3642-line single-file bundle with no
201
+ build step, because `dsh.client` takes one `./client` entry. `tools/merge/gen-client.mjs`
202
+ produced the first draft from the shell bundle and is kept only as a record. It
203
+ now refuses to run without `--force`, and even then backs the current file up to
204
+ `lib/client.js.bak-gen-<timestamp>` first.
205
+
206
+ ## Known limitations
207
+
208
+ - **One seat, one owner.** The plugin registers at `priority: -2` for
209
+ `conversation.input.model`. Another plugin registering the same priority for the
210
+ same slot is resolved by mount order, not by this plugin.
211
+ - **No column-to-column keyboard navigation.** Arrows move inside the focused
212
+ column (the slider, the search box, the rows); Tab leaves the card. Hover and
213
+ clicks are the way to change provider.
214
+ - **The card does not animate.** Switching panes or providers relayouts in one
215
+ frame by design — an animated height is the resize loop this merge removes.
216
+ A window resize re-measures the clamp, so an open card follows the viewport.
217
+ - **Colours live in `localStorage`** (`dsh-client-ui-model-seat:config:v1`) per
218
+ browser profile. They do not follow the account across machines; the panel's
219
+ JSON import/export is the only way to move them.
220
+ - **Search covers model name and id, not provider names.** Typing `ollama` finds
221
+ `ollama/qwen` through the model, not through the provider column.
222
+ - **Web client surface only** (`dsh.client.platform: "web"`, which the desktop app
223
+ renders). The terminal surface is untouched.
224
+ - **`lib/client.js` is one hand-written file** with no build step; `dsh.client`
225
+ takes a single `./client` entry. There is no per-model override layer: the
226
+ plugin edits the seat, not the model list.
227
+
228
+ ## License
229
+
230
+ MIT, see LICENSE.
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,9 @@
1
+ # The plugin row this bundle inserts into the profile.
2
+ #
3
+ # `id` is the row id the profile shows, and it is deliberately new: the two
4
+ # plugins this one replaces shipped `gpt-helper` and `dsh-model-provider-prefix`,
5
+ # and copying either id would collide with a row already present in a profile
6
+ # that still has the original installed.
7
+ - insert:
8
+ - id: model-seat
9
+ name: dsh-client-ui-model-seat