@idoall/dsh-session-colors 0.1.2 → 0.1.3
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 +48 -0
- package/README.md +32 -15
- package/README.zh.md +15 -9
- package/package.json +13 -7
- package/src/client.js +65 -22
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,54 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [0.1.3] - 2026-09-23
|
|
12
|
+
|
|
13
|
+
Verified against DeepSeek Harness `0.1.7-alpha.2`. The first fix below is what
|
|
14
|
+
made the previous build fail to load on that release; the other two are a
|
|
15
|
+
declaration correctness fix and a visible behaviour fix.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- **The plugin could not load from a `link:` install on DSH 0.1.7.** From 0.1.7,
|
|
20
|
+
only a linked plugin's **peer** dependencies resolve from the running
|
|
21
|
+
installation; a plain dependency is looked up under the plugin's own
|
|
22
|
+
`node_modules`, which a link install does not populate. `@deepseek-ai/schemastery`
|
|
23
|
+
was a plain dependency and this repository ships no `node_modules`, so the Host
|
|
24
|
+
half failed to import and the plugin never loaded. It is a peer now (kept in
|
|
25
|
+
`devDependencies` for this repository's own tests).
|
|
26
|
+
- **The declared peer range excluded the release it was running on.**
|
|
27
|
+
`>=0.1.6-0 <0.2.0` does **not** admit `0.1.7-alpha.2` under node-semver's
|
|
28
|
+
default prerelease rule, so pnpm reports the peer as unmet (the plugin market's
|
|
29
|
+
discovery check passes `includePrerelease`, which is why it did not surface as
|
|
30
|
+
an outright incompatibility). The range is now `>=0.1.7-alpha.2 <0.2.0`, with a
|
|
31
|
+
`dsh.engines.dsh` requirement and `dsh.compatibility.dshReleases` recording
|
|
32
|
+
`0.1.7-alpha.2`.
|
|
33
|
+
- **Chips no longer stayed behind while rows glided.** DSH 0.1.7 moves sidebar
|
|
34
|
+
rows with the Web Animations API (`element.animate`), which fires neither
|
|
35
|
+
`transitionrun` nor `transitionend` and mutates no DOM. The layer only opened
|
|
36
|
+
its bounded follow window on a transition event, so a collapse, expand or
|
|
37
|
+
reorder left every chip at the position the glide started from. A row-list
|
|
38
|
+
mutation and a window resize now re-measure once and enter the same 40-frame
|
|
39
|
+
follow window; an unrelated body mutation re-measures once but opens no window,
|
|
40
|
+
so a streaming transcript cannot keep a measuring loop alive.
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- The Session id is read from the row's own `data-row-key` (`session:<id>`, added
|
|
45
|
+
in DSH 0.1.7) before falling back to the React fiber, so the common case no
|
|
46
|
+
longer depends on React internals. The fiber fallback remains for older builds
|
|
47
|
+
and search-result rows.
|
|
48
|
+
- The client build marker is `host-routes+animated-rows`.
|
|
49
|
+
- The docs-consistency test now evaluates the peer range with the prerelease
|
|
50
|
+
rule instead of a substring check — the check that let the old, excluding range
|
|
51
|
+
pass.
|
|
52
|
+
|
|
53
|
+
### Notes
|
|
54
|
+
|
|
55
|
+
- `0.1.3` supports DSH `0.1.7-alpha.2` only and declares
|
|
56
|
+
`>=0.1.7-alpha.2 <0.2.0` as its Host requirement. Installations on an older DSH,
|
|
57
|
+
including `0.1.6-alpha.2`, should stay on plugin `0.1.2`.
|
|
58
|
+
|
|
11
59
|
## [0.1.2] - 2026-09-22
|
|
12
60
|
|
|
13
61
|
### Fixed
|
package/README.md
CHANGED
|
@@ -69,12 +69,15 @@ Requirements:
|
|
|
69
69
|
|
|
70
70
|
- DeepSeek Harness with a Web profile
|
|
71
71
|
- Node.js 20 or newer
|
|
72
|
-
- **Verified DSH version: `0.1.
|
|
72
|
+
- **Verified DSH version: `0.1.7-alpha.2`** (see [Compatibility](#compatibility))
|
|
73
73
|
|
|
74
74
|
```sh
|
|
75
|
-
dsh plugin --profile <profile> add @idoall/dsh-session-colors@
|
|
75
|
+
dsh plugin --profile <profile> add @idoall/dsh-session-colors@latest
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
+
The [compatibility table](#compatibility) lists the exact versions, if you need to
|
|
79
|
+
pin one to a specific DSH release.
|
|
80
|
+
|
|
78
81
|
Working on the plugin itself? Install the directory instead:
|
|
79
82
|
|
|
80
83
|
```sh
|
|
@@ -114,12 +117,17 @@ stored as HSVA, so alpha round-trips exactly.
|
|
|
114
117
|
|
|
115
118
|
## Compatibility
|
|
116
119
|
|
|
117
|
-
Current release: plugin **`0.1.
|
|
118
|
-
**`0.1.
|
|
120
|
+
Current release: plugin **`0.1.3`**, verified against DeepSeek Harness
|
|
121
|
+
**`0.1.7-alpha.2`**.
|
|
122
|
+
|
|
123
|
+
**`0.1.3` supports DSH `0.1.7-alpha.2` only.** On an older DSH — including
|
|
124
|
+
`0.1.6-alpha.2` — keep plugin **`0.1.2`**: `0.1.3` declares its Host requirement
|
|
125
|
+
as `>=0.1.7-alpha.2 <0.2.0` and is not verified below it.
|
|
119
126
|
|
|
120
127
|
| Plugin version | Verified DeepSeek Harness | npm status | What it is |
|
|
121
128
|
| --- | --- | --- | --- |
|
|
122
|
-
| **`0.1.
|
|
129
|
+
| **`0.1.3`** | `0.1.7-alpha.2` | `latest` | Adapts to DSH 0.1.7: the Session id now comes from the row's own `data-row-key` (fiber fallback kept), and chips follow the Web-Animations row gliding `0.1.7` introduced. The Host dependency is declared the way `0.1.7` resolves a linked plugin, and the peer range admits the release. |
|
|
130
|
+
| `0.1.2` | `0.1.6-alpha.2` | published | Fixes chips staying at their old coordinates for ~20s when a workspace is collapsed or expanded: a second `MutationObserver.observe()` on the same target had silently replaced the `childList` watch. |
|
|
123
131
|
| `0.1.1` | `0.1.6-alpha.2` | published | Documentation only: the packaged README no longer says "not published", and the market screenshots are declared. |
|
|
124
132
|
| `0.1.0` | `0.1.6-alpha.2` | published | First release: Session colour marks, Host-side storage, visible inside a phone's sidebar drawer |
|
|
125
133
|
|
|
@@ -128,9 +136,17 @@ Current release: plugin **`0.1.2`**, verified against DeepSeek Harness
|
|
|
128
136
|
[`package.json`](package.json) — and a test keeps both READMEs' compatibility
|
|
129
137
|
sections word-for-word with it and inside the range `peerDependencies`
|
|
130
138
|
declares. A DSH version that is not listed is **never claimed as compatible**.
|
|
131
|
-
- `peerDependencies` declares `>=0.1.
|
|
132
|
-
`dsh-client-ui-conversation
|
|
133
|
-
|
|
139
|
+
- `peerDependencies` declares `>=0.1.7-alpha.2 <0.2.0` for `dsh-client-ui-layout`
|
|
140
|
+
and `dsh-client-ui-conversation`, and `dsh.engines.dsh` declares the same range
|
|
141
|
+
as the Host requirement: that is the range allowed to **load**, which is not the
|
|
142
|
+
same as verified. The lower bound names the alpha on purpose — under
|
|
143
|
+
node-semver's default prerelease rule a range like `>=0.1.6-0 <0.2.0` does
|
|
144
|
+
**not** admit `0.1.7-alpha.2`, so pnpm reports the peer as unmet even though the
|
|
145
|
+
plugin runs.
|
|
146
|
+
- `@deepseek-ai/schemastery` is a **peer**, not a plain dependency: DSH 0.1.7
|
|
147
|
+
resolves only a linked plugin's peer dependencies from the running
|
|
148
|
+
installation, so a `link:` install of this directory would otherwise fail to
|
|
149
|
+
import the Host half.
|
|
134
150
|
- DSH moving faster than this plugin does not break it: chips depend on DSH's row
|
|
135
151
|
DOM (see [Limitations](#limitations)), so a large change to the row structure
|
|
136
152
|
can at worst stop chips from appearing — it cannot block clicking.
|
|
@@ -184,7 +200,7 @@ document.querySelector('.dsh-sc-layer').dataset
|
|
|
184
200
|
// { dshScBuild, dshScStatus, dshScWritable, dshScMarks, dshScError }
|
|
185
201
|
```
|
|
186
202
|
|
|
187
|
-
If `dshScBuild` is not `host-routes+
|
|
203
|
+
If `dshScBuild` is not `host-routes+animated-rows`, the device is running a
|
|
188
204
|
**cached client**: refresh the page (the browser half reloads on a refresh, no DSH
|
|
189
205
|
restart needed). If `dshScStatus` is not `ready`, the store cannot be read — see
|
|
190
206
|
the next item.
|
|
@@ -201,9 +217,10 @@ pages, because storage uses its own route. If you see the old "settings
|
|
|
201
217
|
unavailable" behaviour, check which version is installed.
|
|
202
218
|
|
|
203
219
|
**Chips are in the wrong place, or gone.**
|
|
204
|
-
The layer finds rows by ARIA role and reads the Session id from the
|
|
205
|
-
A large change to DSH's sidebar
|
|
206
|
-
disappear, but **clicking is
|
|
220
|
+
The layer finds rows by ARIA role and reads the Session id from the row's
|
|
221
|
+
`data-row-key` (falling back to React internals). A large change to DSH's sidebar
|
|
222
|
+
structure can make it find nothing; chips then disappear, but **clicking is
|
|
223
|
+
unaffected** because the layer is click-through.
|
|
207
224
|
|
|
208
225
|
## Security boundaries
|
|
209
226
|
|
|
@@ -221,8 +238,8 @@ disappear, but **clicking is unaffected** because the layer is click-through.
|
|
|
221
238
|
## Limitations
|
|
222
239
|
|
|
223
240
|
- **The chip depends on DSH's row DOM.** It reads rows by ARIA role and the
|
|
224
|
-
Session id from
|
|
225
|
-
appearing; it cannot break clicking.
|
|
241
|
+
Session id from the row's `data-row-key`, falling back to React internals. A
|
|
242
|
+
future DSH release can stop chips from appearing; it cannot break clicking.
|
|
226
243
|
- **Marks are per DSH instance.** They live in one profile's data directory, so
|
|
227
244
|
two independent DSH servers do not share them.
|
|
228
245
|
- **No permission model.** Anyone who can reach the Host's authenticated routes
|
|
@@ -241,7 +258,7 @@ is not deleted for you; remove it yourself if you do not want it.
|
|
|
241
258
|
## Development
|
|
242
259
|
|
|
243
260
|
```sh
|
|
244
|
-
npm test #
|
|
261
|
+
npm test # 38 tests: bundle shape, store, route, cross-device, geometry, docs
|
|
245
262
|
```
|
|
246
263
|
|
|
247
264
|
The browser half is a hand-authored `__ModuleLoader__` bundle, which is the only
|
package/README.zh.md
CHANGED
|
@@ -51,12 +51,14 @@
|
|
|
51
51
|
|
|
52
52
|
- 带 Web profile 的 DeepSeek Harness
|
|
53
53
|
- Node.js 20 或更新版本
|
|
54
|
-
- **已验证的 DSH 版本:`0.1.
|
|
54
|
+
- **已验证的 DSH 版本:`0.1.7-alpha.2`**(见[兼容性](#兼容性))
|
|
55
55
|
|
|
56
56
|
```sh
|
|
57
|
-
dsh plugin --profile <profile> add @idoall/dsh-session-colors@
|
|
57
|
+
dsh plugin --profile <profile> add @idoall/dsh-session-colors@latest
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
+
要按 DSH 版本精确对应,用[兼容性](#兼容性)表里的具体版本号。
|
|
61
|
+
|
|
60
62
|
要改插件本身就用目录安装:
|
|
61
63
|
|
|
62
64
|
```sh
|
|
@@ -88,16 +90,20 @@ dsh plugin --profile <profile> add link:/path/to/dsh-session-colors
|
|
|
88
90
|
|
|
89
91
|
## 兼容性
|
|
90
92
|
|
|
91
|
-
当前版本:插件 **`0.1.
|
|
93
|
+
当前版本:插件 **`0.1.3`** 已针对 DeepSeek Harness **`0.1.7-alpha.2`** 验证。
|
|
94
|
+
|
|
95
|
+
**`0.1.3` 只支持 DSH `0.1.7-alpha.2`。** 更旧的 DSH(包括 `0.1.6-alpha.2`)请继续用插件 **`0.1.2`**:`0.1.3` 声明的宿主要求是 `>=0.1.7-alpha.2 <0.2.0`,低于它的版本未经本轮验证。
|
|
92
96
|
|
|
93
97
|
| 插件版本 | 已验证的 DeepSeek Harness | npm 发布状态 | 该版本是什么 |
|
|
94
98
|
| --- | --- | --- | --- |
|
|
95
|
-
| **`0.1.
|
|
99
|
+
| **`0.1.3`** | `0.1.7-alpha.2` | `latest` | 适配 DSH 0.1.7:会话 id 改从行自身的 `data-row-key` 读取(保留 fiber 回退),色块跟随 0.1.7 引入的 Web Animations 行滑动;宿主依赖按 0.1.7 对 link 插件的解析方式声明,peer 范围也真正覆盖该版本。 |
|
|
100
|
+
| `0.1.2` | `0.1.6-alpha.2` | 已发布 | 修掉折叠/展开工作区后色块停在旧位置约 20 秒:同一个 target 上第二次 `MutationObserver.observe()` 把 `childList` 监听顶掉了。 |
|
|
96
101
|
| `0.1.1` | `0.1.6-alpha.2` | 已发布 | 仅文档:打包进 npm 的 README 不再写"尚未发布",并声明了市场截图。 |
|
|
97
102
|
| `0.1.0` | `0.1.6-alpha.2` | 已发布 | 首个版本:会话颜色标记、宿主侧存储、手机抽屉内可见 |
|
|
98
103
|
|
|
99
104
|
- **已验证的 DeepSeek Harness** 是这个插件**实际跑过**的确切 DSH 版本。这份清单只有一个存放处——[`package.json`](package.json) 的 `dsh.compatibility.dshReleases`——并且有测试保证两个 README 的兼容性段落与它逐字一致、且落在 `peerDependencies` 声明的范围内。未列出的 DSH 版本**不会被宣称为兼容**。
|
|
100
|
-
- `peerDependencies` 声明的范围是 `>=0.1.
|
|
105
|
+
- `peerDependencies` 声明的范围是 `>=0.1.7-alpha.2 <0.2.0`(`dsh-client-ui-layout` 与 `dsh-client-ui-conversation`),`dsh.engines.dsh` 把同一范围声明为宿主要求:这是**允许加载**的范围,不等于已验证。下界特意写成 alpha 版本——按 node-semver 的默认预发布规则,`>=0.1.6-0 <0.2.0` 这样的范围**并不包含** `0.1.7-alpha.2`,pnpm 会把 peer 报成 unmet,即使插件实际跑得起来。
|
|
106
|
+
- `@deepseek-ai/schemastery` 声明为 **peer**,不是普通依赖:DSH 0.1.7 只把 link 插件的 **peer** 依赖解析到运行实例,所以用 `link:` 装入本目录时,普通依赖会让宿主半边 import 失败。
|
|
101
107
|
- DSH 升级快于插件时,插件本身不会因此报错:色块依赖 DSH 的行 DOM(见[限制](#限制)),所以 DSH 大改行结构时最坏的结果是**色块不再显示**,不会挡住点击。
|
|
102
108
|
|
|
103
109
|
## 跨设备与局域网
|
|
@@ -141,7 +147,7 @@ document.querySelector('.dsh-sc-layer').dataset
|
|
|
141
147
|
// { dshScBuild, dshScStatus, dshScWritable, dshScMarks, dshScError }
|
|
142
148
|
```
|
|
143
149
|
|
|
144
|
-
`dshScBuild` 不是 `host-routes+
|
|
150
|
+
`dshScBuild` 不是 `host-routes+animated-rows` 就是**客户端缓存**,刷新页面即可(客户端半边刷新即热重载,不需要重启 DSH)。`dshScStatus` 不是 `ready` 则是存储读不到,见下一条。
|
|
145
151
|
|
|
146
152
|
**头部控制显示 ⚠。**
|
|
147
153
|
表示读不到存储:`dshScError` 里有原因,`dshScWritable` 说明能不能写。宿主路由未注册(插件没被 profile 加载)或数据目录不可写时会这样。注意**这不是**"这个会话没有颜色"。
|
|
@@ -150,7 +156,7 @@ document.querySelector('.dsh-sc-layer').dataset
|
|
|
150
156
|
从 `0.1.0` 起本插件在局域网/隧道页面与回环页面行为一致(存储走自有路由)。如果你看到的是旧版那种"设置读不到"的表现,先确认装的是哪个版本。
|
|
151
157
|
|
|
152
158
|
**色块位置不对或消失。**
|
|
153
|
-
色块浮层靠 ARIA role
|
|
159
|
+
色块浮层靠 ARIA role 找会话行、靠行自身的 `data-row-key` 取会话 ID(取不到时回退到 React fiber)。DSH 大改侧栏结构后可能找不到行;此时色块会消失,但**不会影响点击**,因为浮层是点击穿透的。
|
|
154
160
|
|
|
155
161
|
## 安全边界
|
|
156
162
|
|
|
@@ -162,7 +168,7 @@ document.querySelector('.dsh-sc-layer').dataset
|
|
|
162
168
|
|
|
163
169
|
## 限制
|
|
164
170
|
|
|
165
|
-
- **色块依赖 DSH 的行 DOM。** 它靠 ARIA role
|
|
171
|
+
- **色块依赖 DSH 的行 DOM。** 它靠 ARIA role 找行、靠行自身的 `data-row-key` 取会话 ID(回退到 React 内部结构)。DSH 未来改版可能让色块不再显示;但**不会影响点击**。
|
|
166
172
|
- **标记属于单个 DSH 实例。** 它存在某个 profile 的数据目录里,两个独立的 DSH 服务之间不共享。
|
|
167
173
|
- **没有权限模型。** 凡是能访问该 profile 已鉴权路由的人,都能读写标记。
|
|
168
174
|
- **只有颜色。** 没有文字标签、图标或 emoji。
|
|
@@ -178,7 +184,7 @@ dsh plugin --profile <profile> remove @idoall/dsh-session-colors
|
|
|
178
184
|
## 开发
|
|
179
185
|
|
|
180
186
|
```sh
|
|
181
|
-
npm test #
|
|
187
|
+
npm test # 38 项:bundle 结构、存储、路由、跨设备、几何、文档一致性
|
|
182
188
|
```
|
|
183
189
|
|
|
184
190
|
浏览器半边是**手写的** `__ModuleLoader__` bundle——这是 DSH 客户端加载器接受的唯一格式。手写是刻意的:**没有构建步骤**,就不会有产物与目标 DSH 版本漂移的问题。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@idoall/dsh-session-colors",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "DSH web plugin: give any Session a colour mark, shown as a chip in the sidebar and picked from the conversation header. Marks are stored on the Host, so a colour chosen on one device is visible on every other one — including over LAN and tunnels.",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -20,9 +20,12 @@
|
|
|
20
20
|
],
|
|
21
21
|
"dsh": {
|
|
22
22
|
"manifestVersion": 1,
|
|
23
|
+
"engines": {
|
|
24
|
+
"dsh": ">=0.1.7-alpha.2 <0.2.0"
|
|
25
|
+
},
|
|
23
26
|
"compatibility": {
|
|
24
27
|
"dshReleases": {
|
|
25
|
-
"0.1.
|
|
28
|
+
"0.1.7-alpha.2": "compatible"
|
|
26
29
|
}
|
|
27
30
|
},
|
|
28
31
|
"bundle": {
|
|
@@ -42,12 +45,10 @@
|
|
|
42
45
|
"publishConfig": {
|
|
43
46
|
"access": "public"
|
|
44
47
|
},
|
|
45
|
-
"dependencies": {
|
|
46
|
-
"@deepseek-ai/schemastery": "^3.18.2"
|
|
47
|
-
},
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
"@deepseek-ai/dsh-client-ui-conversation": ">=0.1.
|
|
50
|
-
"@deepseek-ai/dsh-client-ui-layout": ">=0.1.
|
|
49
|
+
"@deepseek-ai/dsh-client-ui-conversation": ">=0.1.7-alpha.2 <0.2.0",
|
|
50
|
+
"@deepseek-ai/dsh-client-ui-layout": ">=0.1.7-alpha.2 <0.2.0",
|
|
51
|
+
"@deepseek-ai/schemastery": "^3.18.2",
|
|
51
52
|
"react": "^18.2.0"
|
|
52
53
|
},
|
|
53
54
|
"peerDependenciesMeta": {
|
|
@@ -58,6 +59,11 @@
|
|
|
58
59
|
"optional": true
|
|
59
60
|
}
|
|
60
61
|
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@deepseek-ai/cordis": "^4.0.4",
|
|
64
|
+
"@deepseek-ai/schemastery": "^3.18.2",
|
|
65
|
+
"react": "^18.3.1"
|
|
66
|
+
},
|
|
61
67
|
"license": "MIT",
|
|
62
68
|
"engines": {
|
|
63
69
|
"node": ">=20"
|
package/src/client.js
CHANGED
|
@@ -15,12 +15,13 @@
|
|
|
15
15
|
* `ChipsLayer` and `layerZIndexOf`.
|
|
16
16
|
*
|
|
17
17
|
* There is no Session-row slot, so a chip cannot be rendered inside a row. The
|
|
18
|
-
* layer finds rows by ARIA role and reads each row's Session id from the
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
18
|
+
* layer finds rows by ARIA role and reads each row's Session id from the
|
|
19
|
+
* `data-row-key` attribute DSH 0.1.7 puts on every row (`session:<id>`),
|
|
20
|
+
* falling back to the React fiber the renderer attaches — the only source on a
|
|
21
|
+
* build without that attribute, and still the source for search-result rows.
|
|
22
|
+
* That coupling is deliberate and is kept harmless: every step is guarded, an
|
|
23
|
+
* unreadable row is skipped, and the layer never accepts pointer events, so a
|
|
24
|
+
* future DSH change can at worst stop the chips from appearing.
|
|
24
25
|
*
|
|
25
26
|
* Written as a hand-authored `__ModuleLoader__` bundle (the only format the
|
|
26
27
|
* client loader accepts for a package client half), so it needs no build step.
|
|
@@ -46,13 +47,13 @@ window.__ModuleLoader__.load({
|
|
|
46
47
|
|
|
47
48
|
const NS = 'sessionColor'
|
|
48
49
|
/**
|
|
49
|
-
* Build marker. Stored marks are Host-side as of
|
|
50
|
-
* layer paints just above the sidebar as of
|
|
51
|
-
*
|
|
52
|
-
* that still shows an older marker is running a cached
|
|
53
|
-
* hard-refreshed.
|
|
50
|
+
* Build marker. Stored marks are Host-side as of the `host-routes` build,
|
|
51
|
+
* the chip layer paints just above the sidebar as of `drawer-aware`, and as
|
|
52
|
+
* of `animated-rows` it follows the Web-Animations row gliding DSH 0.1.7
|
|
53
|
+
* introduced. A device that still shows an older marker is running a cached
|
|
54
|
+
* client and must be hard-refreshed.
|
|
54
55
|
*/
|
|
55
|
-
const BUILD = 'host-routes+
|
|
56
|
+
const BUILD = 'host-routes+animated-rows'
|
|
56
57
|
|
|
57
58
|
/**
|
|
58
59
|
* This plugin's own Host route. Kept in sync with the Host half's
|
|
@@ -429,13 +430,28 @@ window.__ModuleLoader__.load({
|
|
|
429
430
|
|
|
430
431
|
//#region row discovery
|
|
431
432
|
/**
|
|
432
|
-
*
|
|
433
|
-
*
|
|
434
|
-
*
|
|
435
|
-
|
|
433
|
+
* Prefix DSH 0.1.7 uses for a Session row's `data-row-key`. It is the
|
|
434
|
+
* exact, stable DOM source of a row's Session id; older builds and
|
|
435
|
+
* search-result rows carry no such attribute.
|
|
436
|
+
*/
|
|
437
|
+
const SESSION_ROW_KEY = 'session:'
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Session id of a rendered row. The `data-row-key` attribute is read first
|
|
441
|
+
* because DSH 0.1.7 marks every row with it; the React fiber the renderer
|
|
442
|
+
* already attached remains the fallback, and the walk is bounded so an
|
|
443
|
+
* unexpected shape only costs that row its chip.
|
|
436
444
|
* @param node - a Session row element.
|
|
437
445
|
*/
|
|
438
446
|
function sessionIdOf(node) {
|
|
447
|
+
try {
|
|
448
|
+
const key = node === null || node === undefined || typeof node.getAttribute !== 'function'
|
|
449
|
+
? undefined
|
|
450
|
+
: node.getAttribute('data-row-key')
|
|
451
|
+
if (typeof key === 'string' && key.startsWith(SESSION_ROW_KEY)) return key.slice(SESSION_ROW_KEY.length)
|
|
452
|
+
} catch {
|
|
453
|
+
// An unreadable attribute only sends this row to the fiber fallback.
|
|
454
|
+
}
|
|
439
455
|
try {
|
|
440
456
|
const key = Object.keys(node).find((name) => name.startsWith('__reactFiber$') || name.startsWith('__reactInternalInstance$'))
|
|
441
457
|
if (key === undefined) return undefined
|
|
@@ -603,10 +619,23 @@ window.__ModuleLoader__.load({
|
|
|
603
619
|
setLayer({ items, clip, zIndex: rows.length === 0 ? 1 : layerZIndexOf(rows[0]) })
|
|
604
620
|
}
|
|
605
621
|
sync()
|
|
622
|
+
/**
|
|
623
|
+
* Re-measure now, then keep re-measuring for a bounded window.
|
|
624
|
+
*
|
|
625
|
+
* DSH 0.1.7 glides rows with the Web Animations API
|
|
626
|
+
* (`element.animate`): that fires neither `transitionrun` nor
|
|
627
|
+
* `transitionend` and mutates no DOM, so the one MutationObserver
|
|
628
|
+
* callback for a collapse lands on the animation's first frame — every
|
|
629
|
+
* row still painted at its old coordinates — and nothing would ever
|
|
630
|
+
* report the movement again. Following each mutation for the same
|
|
631
|
+
* bounded window as a transition keeps the chips on their rows.
|
|
632
|
+
*/
|
|
633
|
+
const kick = () => { sync(); follow() }
|
|
606
634
|
// Rows move with their scroll container and re-render on every list
|
|
607
|
-
// change, so follow both.
|
|
635
|
+
// change, so follow both. A scroll reports its own frames continuously,
|
|
636
|
+
// so it needs no follow window of its own.
|
|
608
637
|
document.addEventListener('scroll', sync, true)
|
|
609
|
-
window.addEventListener('resize',
|
|
638
|
+
window.addEventListener('resize', kick)
|
|
610
639
|
// A CSS transition moves every row without any scroll, resize or DOM
|
|
611
640
|
// mutation: the phone adapter slides the whole sidebar in with a
|
|
612
641
|
// transform on the drawer while the rows themselves never change. Only
|
|
@@ -629,19 +658,33 @@ window.__ModuleLoader__.load({
|
|
|
629
658
|
// the peer poll happened to re-render the layer ~20s later.
|
|
630
659
|
const observers = []
|
|
631
660
|
try {
|
|
632
|
-
|
|
661
|
+
/**
|
|
662
|
+
* Whether a mutation could have changed the row list, and so started a
|
|
663
|
+
* row glide. The childList watch is body-wide because rows live deep
|
|
664
|
+
* inside the sidebar, so a mutation unrelated to the list — a
|
|
665
|
+
* streaming transcript, a tooltip — re-measures once but must not open
|
|
666
|
+
* a follow window for every frame it produces.
|
|
667
|
+
*/
|
|
668
|
+
const rowsChanged = (records) => records.some((record) => {
|
|
669
|
+
const target = record.target
|
|
670
|
+
return target !== null && target !== undefined
|
|
671
|
+
&& typeof target.closest === 'function'
|
|
672
|
+
&& target.closest('[role="tree"]') !== null
|
|
673
|
+
})
|
|
674
|
+
const onRowsMutation = (records) => { if (rowsChanged(records)) kick(); else sync() }
|
|
675
|
+
const rowsObserver = new MutationObserver(onRowsMutation)
|
|
633
676
|
rowsObserver.observe(document.body, { childList: true, subtree: true })
|
|
634
677
|
observers.push(rowsObserver)
|
|
635
678
|
// The phone adapter opens its drawer by toggling a class on `body`.
|
|
636
679
|
// With transitions disabled the geometry still changes, just without a
|
|
637
680
|
// transition event to follow.
|
|
638
|
-
const drawerObserver = new MutationObserver(
|
|
681
|
+
const drawerObserver = new MutationObserver(kick)
|
|
639
682
|
drawerObserver.observe(document.body, { attributes: true, attributeFilter: ['class'] })
|
|
640
683
|
observers.push(drawerObserver)
|
|
641
684
|
// A workspace collapse that hides rows instead of removing them
|
|
642
685
|
// produces no childList mutation, so follow the ARIA state and
|
|
643
686
|
// `hidden` as well.
|
|
644
|
-
const stateObserver = new MutationObserver(
|
|
687
|
+
const stateObserver = new MutationObserver(kick)
|
|
645
688
|
stateObserver.observe(document.body, { attributes: true, attributeFilter: ['aria-expanded', 'hidden'], subtree: true })
|
|
646
689
|
observers.push(stateObserver)
|
|
647
690
|
} catch {
|
|
@@ -650,7 +693,7 @@ window.__ModuleLoader__.load({
|
|
|
650
693
|
return () => {
|
|
651
694
|
cancelFrame(frame)
|
|
652
695
|
document.removeEventListener('scroll', sync, true)
|
|
653
|
-
window.removeEventListener('resize',
|
|
696
|
+
window.removeEventListener('resize', kick)
|
|
654
697
|
document.removeEventListener('transitionrun', onTransition, true)
|
|
655
698
|
document.removeEventListener('transitionend', onTransition, true)
|
|
656
699
|
for (const watcher of observers) watcher.disconnect()
|