@ryuu-64/dsh-find-all 0.1.3 → 0.1.5
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 +130 -92
- package/LICENSE +22 -22
- package/README.fork-origin.md +48 -48
- package/README.md +30 -4
- package/docs/compatibility.md +68 -0
- package/lib/client.js +306 -94
- package/package.json +83 -76
package/CHANGELOG.md
CHANGED
|
@@ -1,92 +1,130 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## 0.1.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.5
|
|
4
|
+
|
|
5
|
+
Fix compatibility with newer DSH hosts while preserving the existing find controls and matching behavior.
|
|
6
|
+
|
|
7
|
+
- Use the official session header slot and session identity instead of the removed global current-session field. Ambiguous, unsupported or stale conversation targets fail closed; highlights, paging and listeners stay scoped and are cleaned up with the host lifecycle.
|
|
8
|
+
- Retain all previously admitted published runtimes and include the two tested 0.2 release candidates through an exact ten-version peer list. No version exemption or future-version guarantee is added.
|
|
9
|
+
- Document the legacy Desktop plugin-manager UI installation route, alongside the current bundled CLI route.
|
|
10
|
+
|
|
11
|
+
[Acceptance run 36998700597](https://github.com/Ryuu-64/dsh-find-all/actions/runs/36998700597) completed the plugin business checks on ten Web runtimes and three official Windows Desktop builds. The unavailable historical Desktop builds remain unverified. Host startup and shutdown errors also occurred without the plugin and remain recorded as an overall CI failure; this release does not claim to fix those host errors. See the README for the accepted version and installation boundaries.
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## 0.1.4
|
|
15
|
+
|
|
16
|
+
Documentation only: no code, no bundle, no behavior changed — `lib/` is untouched, so 0.1.4
|
|
17
|
+
behaves exactly like 0.1.3.
|
|
18
|
+
|
|
19
|
+
0.1.3 fixed the README but sent it with a link to `CONTRIBUTING.md`, a file that same release had
|
|
20
|
+
just added. That file has now been deleted (see below), which would have left the published README
|
|
21
|
+
pointing at nothing. npm serves the README carried by the latest release, so correcting the link
|
|
22
|
+
on the package page means publishing again — that is the only reason this version exists.
|
|
23
|
+
|
|
24
|
+
### Removed
|
|
25
|
+
|
|
26
|
+
- **`CONTRIBUTING.md` deleted, in the repository and in the published package.** It was written as
|
|
27
|
+
a chronological account of what changed rather than a document with a point of view: the
|
|
28
|
+
registration-id section worked through the 0.1.0 post-mortem before saying what to do about it,
|
|
29
|
+
and the structure, commands, and version-number sections restated the commit history. Nothing in
|
|
30
|
+
it was needed to use the plugin.
|
|
31
|
+
- One item in it was not recoverable from anywhere else and is gone with the file: that a
|
|
32
|
+
`failed to import loader entry <id> … loaded without registering` banner means the client
|
|
33
|
+
bundle registered under the wrong id, and is not a DSH version incompatibility. It stays
|
|
34
|
+
reachable in git history, and `scripts/check-client-registration.mjs` still fails the build on
|
|
35
|
+
the underlying defect.
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- `README.md` no longer links to the deleted file.
|
|
40
|
+
|
|
41
|
+
## 0.1.3
|
|
42
|
+
|
|
43
|
+
Documentation only: no code, no bundle, no behavior changed. Published so the README on the npm
|
|
44
|
+
package page is the user-facing one — npm serves the README carried by the latest release.
|
|
45
|
+
|
|
46
|
+
### Changed
|
|
47
|
+
|
|
48
|
+
- **`README.md` is now written for users.** It had been carrying maintainer material: internal
|
|
49
|
+
session-service calls, the tarball install for when publishing is blocked, the 0.1.0
|
|
50
|
+
registration post-mortem, the derived-literal warning for future forkers, and the test
|
|
51
|
+
commands. A user opening the page had to work out which parts applied to them. The README now
|
|
52
|
+
answers what the plugin does, how to install it, the keyboard reference, known limitations, and
|
|
53
|
+
how to uninstall.
|
|
54
|
+
|
|
55
|
+
### Added
|
|
56
|
+
|
|
57
|
+
- **`CONTRIBUTING.md`** takes that material rather than dropping it, plus a note on why release
|
|
58
|
+
coordinates cannot be reused (the reason 0.1.1 became 0.1.2). It is not in `package.json`'s
|
|
59
|
+
`files` whitelist, so npm users do not receive it.
|
|
60
|
+
|
|
61
|
+
### Fixed
|
|
62
|
+
|
|
63
|
+
- `scripts/install-local.mjs` told the operator to move to the registry "once 0.1.1 is published",
|
|
64
|
+
a version that can never exist. It now refers to the installed version generically.
|
|
65
|
+
|
|
66
|
+
## 0.1.2
|
|
67
|
+
|
|
68
|
+
Nothing about the code changed — this release exists only because **0.1.1 can never be
|
|
69
|
+
published**. The publish that produced `+ @ryuu-64/dsh-find-all@0.1.1` left the version in npm's
|
|
70
|
+
staging area rather than on the registry, and the registry refuses to reuse the coordinate:
|
|
71
|
+
|
|
72
|
+
```text
|
|
73
|
+
409 Cannot publish over previously staged version "0.1.1"
|
|
74
|
+
409 Cannot stage previously published version "0.1.1"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
That is expected, not a bug: registry data is immutable, and *"if you've ever published a package
|
|
78
|
+
called bob at version 1.1.0, no other package can ever be published with that name at that
|
|
79
|
+
version. This is true even if that package is unpublished"* ([npm Unpublish
|
|
80
|
+
Policy](https://docs.npmjs.com/policies/unpublish)). The 0.1.1 tarball never became reachable
|
|
81
|
+
(`/0.1.1` → 404, its tarball → 404), so the fix is republished unchanged as 0.1.2.
|
|
82
|
+
|
|
83
|
+
The 0.1.1 section below still describes this release's content: same bundle, same fix, byte for
|
|
84
|
+
byte.
|
|
85
|
+
|
|
86
|
+
## 0.1.1
|
|
87
|
+
|
|
88
|
+
### Fixed
|
|
89
|
+
|
|
90
|
+
- **The client bundle now registers under the package name.** `lib/client.js` called
|
|
91
|
+
`window.__ModuleLoader__.load({ id: "dsh-find-all", ... })` — the unscoped name — while the
|
|
92
|
+
package is `@ryuu-64/dsh-find-all`. The host composes the boot-graph row id from the package
|
|
93
|
+
name (`ClientModuleRegistry.reconcilePackage`), and `ClientModuleSystem.arrive()` rejects the
|
|
94
|
+
row when the executed bundle did not register a factory under exactly that key, so the plugin
|
|
95
|
+
loaded and then failed with:
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
failed to import loader entry <id>: client-modules: bundle /plugins/??...&rev=...
|
|
99
|
+
loaded without registering "@ryuu-64/dsh-find-all" via __ModuleLoader__.load
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The GUI surfaced this as "Failed to load plugins / 部分插件加载失败", which reads like a DSH
|
|
103
|
+
version incompatibility; it was not.
|
|
104
|
+
|
|
105
|
+
Introduced by the 0.1.0 fork, which renamed every other `dsh-find-bar` identifier to
|
|
106
|
+
`dsh-find-all` but left the registration id unscoped. `id` is now `"@ryuu-64/dsh-find-all"`.
|
|
107
|
+
|
|
108
|
+
### Added
|
|
109
|
+
|
|
110
|
+
- `scripts/check-client-registration.mjs` (wired into `npm run check`): reads the expected id from
|
|
111
|
+
`package.json` and fails with a `file:line` diagnostic when the bundle registers anything else,
|
|
112
|
+
so the drift cannot come back unnoticed. It takes an optional package root, so it can inspect a
|
|
113
|
+
registry tarball or an installed profile instead of only this checkout.
|
|
114
|
+
- `npm run verify:release`: packs the artifact `npm publish` would upload and checks the id inside
|
|
115
|
+
that tarball. This is the check that would have caught 0.1.0 before it reached the registry.
|
|
116
|
+
- `npm run deploy:profile`: installs a published release into a DSH profile and refuses to finish
|
|
117
|
+
until the installed bundle is byte-identical to the registry artifact and registers correctly.
|
|
118
|
+
Rolls the profile manifests back on any failure.
|
|
119
|
+
- Tests for the registration check itself (`test/client-registration-check.test.mjs`): they run it
|
|
120
|
+
against a mutated copy, which is how the check was caught silently falling back to the checkout
|
|
121
|
+
and reporting the defective registry 0.1.0 as clean.
|
|
122
|
+
- `test/bundle-registration.test.mjs`: the invariant as a test, with the expected value read
|
|
123
|
+
from `package.json` rather than hardcoded.
|
|
124
|
+
- README section on the "Failed to load plugins" symptom, why it is not a DSH version problem, and
|
|
125
|
+
the fork re-scope hazard behind it.
|
|
126
|
+
|
|
127
|
+
## 0.1.0
|
|
128
|
+
|
|
129
|
+
Initial release: a Ctrl+F find bar that searches the whole conversation, forked from
|
|
130
|
+
[`secyborg/dsh-find-bar`](https://github.com/secyborg/dsh-find-bar) (MIT).
|
package/LICENSE
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 secyborg
|
|
4
|
-
Copyright (c) 2026 Ryuu-64
|
|
5
|
-
|
|
6
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
-
in the Software without restriction, including without limitation the rights
|
|
9
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
-
furnished to do so, subject to the following conditions:
|
|
12
|
-
|
|
13
|
-
The above copyright notice and this permission notice shall be included in all
|
|
14
|
-
copies or substantial portions of the Software.
|
|
15
|
-
|
|
16
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 secyborg
|
|
4
|
+
Copyright (c) 2026 Ryuu-64
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
package/README.fork-origin.md
CHANGED
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
# dsh-find-bar
|
|
2
|
-
|
|
3
|
-
DSH web 插件:为桌面 App(Electron 壳)补上 **⌘F / Ctrl+F 页内查找**,
|
|
4
|
-
体验对齐浏览器原生查找条——支持全文高亮、匹配计数、回车跳转。
|
|
5
|
-
|
|
6
|
-
搜索范围:当前页面渲染的全部文本——对话内容、侧边栏、设置页。
|
|
7
|
-
|
|
8
|
-
## 原理
|
|
9
|
-
|
|
10
|
-
Electron 没有浏览器内置查找栏。本插件在客户端实现:
|
|
11
|
-
|
|
12
|
-
- 监听 ⌘F/Ctrl+F(capture 阶段)弹出自绘查找条(跟随主题变量)
|
|
13
|
-
- 用 TreeWalker 收集文本节点(含 shadow root 内),构建 DOM Range 匹配
|
|
14
|
-
- 用 **CSS Custom Highlight API**(`CSS.highlights` + `::highlight()`)高亮——
|
|
15
|
-
只绘制不修改 DOM,与 React 渲染零冲突
|
|
16
|
-
- 不支持该 API 的环境自动退回 `window.find()`
|
|
17
|
-
|
|
18
|
-
## 快捷键
|
|
19
|
-
|
|
20
|
-
| 键 | 功能 |
|
|
21
|
-
|---|---|
|
|
22
|
-
| ⌘F / Ctrl+F | 打开查找条(自动填入当前选中文本) |
|
|
23
|
-
| Enter / Shift+Enter | 下一个 / 上一个匹配 |
|
|
24
|
-
| ⌘G / F3(+Shift) | 下一个 / 上一个 |
|
|
25
|
-
| Esc | 关闭并清除高亮 |
|
|
26
|
-
|
|
27
|
-
输入即搜(防抖跟随内容流式更新),计数显示 `当前/总数`。
|
|
28
|
-
|
|
29
|
-
## 安装
|
|
30
|
-
|
|
31
|
-
```sh
|
|
32
|
-
npm pack
|
|
33
|
-
dsh plugin --profile desktop add ./dsh-find-bar-0.1.0.tgz
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
安装后重启一次应用(插件清单启动时装载)。
|
|
37
|
-
|
|
38
|
-
## 已知限制
|
|
39
|
-
|
|
40
|
-
- 只搜索**已渲染**在 DOM 里的内容;未展开的会话/极长列表若被界面折叠,需先展开
|
|
41
|
-
- 匹配上限 5000 条(超限显示 `+`)
|
|
42
|
-
- 大小写不敏感,暂无"区分大小写"开关
|
|
43
|
-
|
|
44
|
-
## 卸载
|
|
45
|
-
|
|
46
|
-
```sh
|
|
47
|
-
dsh plugin --profile desktop remove dsh-find-bar
|
|
48
|
-
```
|
|
1
|
+
# dsh-find-bar
|
|
2
|
+
|
|
3
|
+
DSH web 插件:为桌面 App(Electron 壳)补上 **⌘F / Ctrl+F 页内查找**,
|
|
4
|
+
体验对齐浏览器原生查找条——支持全文高亮、匹配计数、回车跳转。
|
|
5
|
+
|
|
6
|
+
搜索范围:当前页面渲染的全部文本——对话内容、侧边栏、设置页。
|
|
7
|
+
|
|
8
|
+
## 原理
|
|
9
|
+
|
|
10
|
+
Electron 没有浏览器内置查找栏。本插件在客户端实现:
|
|
11
|
+
|
|
12
|
+
- 监听 ⌘F/Ctrl+F(capture 阶段)弹出自绘查找条(跟随主题变量)
|
|
13
|
+
- 用 TreeWalker 收集文本节点(含 shadow root 内),构建 DOM Range 匹配
|
|
14
|
+
- 用 **CSS Custom Highlight API**(`CSS.highlights` + `::highlight()`)高亮——
|
|
15
|
+
只绘制不修改 DOM,与 React 渲染零冲突
|
|
16
|
+
- 不支持该 API 的环境自动退回 `window.find()`
|
|
17
|
+
|
|
18
|
+
## 快捷键
|
|
19
|
+
|
|
20
|
+
| 键 | 功能 |
|
|
21
|
+
|---|---|
|
|
22
|
+
| ⌘F / Ctrl+F | 打开查找条(自动填入当前选中文本) |
|
|
23
|
+
| Enter / Shift+Enter | 下一个 / 上一个匹配 |
|
|
24
|
+
| ⌘G / F3(+Shift) | 下一个 / 上一个 |
|
|
25
|
+
| Esc | 关闭并清除高亮 |
|
|
26
|
+
|
|
27
|
+
输入即搜(防抖跟随内容流式更新),计数显示 `当前/总数`。
|
|
28
|
+
|
|
29
|
+
## 安装
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
npm pack
|
|
33
|
+
dsh plugin --profile desktop add ./dsh-find-bar-0.1.0.tgz
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
安装后重启一次应用(插件清单启动时装载)。
|
|
37
|
+
|
|
38
|
+
## 已知限制
|
|
39
|
+
|
|
40
|
+
- 只搜索**已渲染**在 DOM 里的内容;未展开的会话/极长列表若被界面折叠,需先展开
|
|
41
|
+
- 匹配上限 5000 条(超限显示 `+`)
|
|
42
|
+
- 大小写不敏感,暂无"区分大小写"开关
|
|
43
|
+
|
|
44
|
+
## 卸载
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
dsh plugin --profile desktop remove dsh-find-bar
|
|
48
|
+
```
|
package/README.md
CHANGED
|
@@ -22,24 +22,30 @@ DSH 桌面窗口是 Electron 壳,本身没有浏览器那种查找栏。而会
|
|
|
22
22
|
|
|
23
23
|
## 安装
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
先按 **DSH 运行时版本**选择安装入口;它不一定等于 Desktop 产品版本。下面的包名用于安装 npm 已发布版本;验收特定候选包时,请替换为对应 `.tgz` 的绝对路径。
|
|
26
|
+
|
|
27
|
+
**运行时 0.2.0-rc.2**:用 Desktop 自带的插件命令安装:
|
|
26
28
|
|
|
27
29
|
```sh
|
|
28
30
|
dsh plugin --profile desktop add @ryuu-64/dsh-find-all
|
|
29
31
|
```
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
**运行时 0.2.0-rc.1 / 0.1.7-rc.2**:这两个官方安装包没有上述新版 CLI 入口,通用 `dsh plugin --profile desktop` 也会拒绝操作。请在 Desktop 中打开「插件 / Plugins」→「添加插件 / Add plugin」,填入 `@ryuu-64/dsh-find-all`(验收候选时填 `.tgz` 绝对路径),点击「安装 / Install」,完成后点击「立即启用 / Enable now」。不要用版本豁免绕过安装错误。
|
|
34
|
+
|
|
35
|
+
安装后**完整重启一次 DSH Desktop**,再检查插件是否正常加载。已验证的三个桌面版本支持原生禁用/重新启用;旧宿主的刷新要求见下方兼容表说明。
|
|
32
36
|
|
|
33
37
|
装完按 **⌘F / Ctrl+F**,右上角出现查找栏就算装好了。
|
|
34
38
|
|
|
35
39
|
### 和 dsh-find-bar 只能留一个
|
|
36
40
|
|
|
37
|
-
`dsh-find-bar` 和本插件**都会抢 Ctrl+F
|
|
41
|
+
`dsh-find-bar` 和本插件**都会抢 Ctrl+F**,两个同时装会互相顶掉。如果你之前装过它,先卸掉。运行时 0.2.0-rc.2 可用:
|
|
38
42
|
|
|
39
43
|
```sh
|
|
40
44
|
dsh plugin --profile desktop remove dsh-find-bar
|
|
41
45
|
```
|
|
42
46
|
|
|
47
|
+
运行时 0.2.0-rc.1 / 0.1.7-rc.2 请在「插件 / Plugins」中找到 `dsh-find-bar` 并选择「卸载 / Uninstall」。
|
|
48
|
+
|
|
43
49
|
## 使用
|
|
44
50
|
|
|
45
51
|
| 操作 | 按键 |
|
|
@@ -62,15 +68,35 @@ dsh plugin --profile desktop remove dsh-find-bar
|
|
|
62
68
|
|
|
63
69
|
## 卸载 / 回退
|
|
64
70
|
|
|
71
|
+
运行时 0.2.0-rc.2 可用:
|
|
72
|
+
|
|
65
73
|
```sh
|
|
66
74
|
dsh plugin --profile desktop remove @ryuu-64/dsh-find-all # 卸载
|
|
67
75
|
dsh plugin --profile desktop add dsh-find-bar # 想换回上游那个插件
|
|
68
76
|
```
|
|
69
77
|
|
|
78
|
+
运行时 0.2.0-rc.1 / 0.1.7-rc.2 请在「插件 / Plugins」中卸载本插件;需要回退时,再通过「添加插件 / Add plugin」安装 `dsh-find-bar`。
|
|
79
|
+
|
|
70
80
|
## 关于这个项目
|
|
71
81
|
|
|
72
82
|
本插件 fork 自 [secyborg/dsh-find-bar](https://github.com/secyborg/dsh-find-bar)(作者 secyborg,MIT),在上游的基础上加了「整段会话」搜索。上游原有的查找条界面、高亮、`n/N` 计数和键盘导航都保留了下来。
|
|
73
83
|
|
|
74
84
|
版权与许可为 MIT 双署名:`Copyright (c) 2026 secyborg` 与 `Copyright (c) 2026 Ryuu-64`。上游 README 原样保存在 `README.fork-origin.md`,改动清单与致谢见 `NOTICE.md`。
|
|
75
85
|
|
|
76
|
-
|
|
86
|
+
## 兼容性开发说明
|
|
87
|
+
|
|
88
|
+
当前候选改动由会话标题栏中的查找按钮取得明确的会话身份;首次快捷键在只有一个可见会话面板时可直接查找;多会话时先点击或聚焦目标会话。已明确拒绝或失效的目标不会自动改搜其它会话。无法识别可靠的会话正文范围时会提示先选择会话,不退回整页搜索。历史加载失败或中断时保留已显示的部分结果,并说明尚未搜完。
|
|
89
|
+
|
|
90
|
+
右侧嵌入会话暂不支持查找,会提示范围不可用,不会自动改搜主栏;明确选择主会话后仍可查找。
|
|
91
|
+
|
|
92
|
+
[2026-10-02 验收记录(代码版本 `69c477c`)](https://github.com/Ryuu-64/dsh-find-all/actions/runs/36998700597)使用同一候选包完成十版 Web 和三个官方 Windows Desktop 的插件功能验收:首次快捷键、80 条结果的分页、会话切换、结果导航及宿主支持的启停行为。当前 rc2 还验证了实际右侧嵌入会话的范围隔离。
|
|
93
|
+
|
|
94
|
+
| DSH 运行时版本 | 实际 Web | 官方 Windows Desktop |
|
|
95
|
+
| --- | --- | --- |
|
|
96
|
+
| 0.1.5-rc.2、0.1.5-rc.3、0.1.6-alpha.2 | 功能验收通过 | 已核官方路径未取得安装包,未验证 |
|
|
97
|
+
| 0.1.6-alpha.1、0.1.7-alpha.1、0.1.7-alpha.2、0.1.7-rc.1 | 功能验收通过 | 本轮未验证 |
|
|
98
|
+
| 0.1.7-rc.2、0.2.0-rc.1、0.2.0-rc.2 | 功能验收通过 | 插件功能验收通过;宿主错误仍保留 |
|
|
99
|
+
|
|
100
|
+
**该次 CI 整体仍为失败**:三个桌面版本均记录到宿主启动时的 `startupData/preloadScripts` 错误及退出时的连接错误,未装插件的基线也存在同类错误。没有忽略这些错误,也不把插件功能通过写成宿主整体无错误。Web 通过不代表对应 Desktop 已验证,未知未来版本不作承诺。详见 [兼容性验收方法与边界](docs/compatibility.md);最终交付状态以 [PR #9](https://github.com/Ryuu-64/dsh-find-all/pull/9) 的独立审核为准。
|
|
101
|
+
|
|
102
|
+
旧宿主 0.1.5-rc.2 / rc.3 / 0.1.6-alpha.1 本身不支持插件图变更的热卸载:禁用或重新启用后须刷新页面或重启应用。其余七个已验 Web 运行时和上述三个已验 Desktop 均通过了两次原生禁用/重新启用测试;未验证的 Desktop 不据此推定通过。
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Issue #1: compatibility acceptance
|
|
2
|
+
|
|
3
|
+
This implementation follows the approved [Issue #1 plan](https://github.com/Ryuu-64/dsh-find-all/issues/1#issuecomment-5933617888), with the accepted release scope and unverified Desktop limits recorded in [PR #9](https://github.com/Ryuu-64/dsh-find-all/pull/9). This does not promise compatibility with untested or future hosts. Issues #2–#8 are outside this change.
|
|
4
|
+
|
|
5
|
+
## Contract research
|
|
6
|
+
|
|
7
|
+
The adapter uses a `conversation.session.header.utilities` list entry. Its `sessionId` comes from the session standard props; it never consults the old global `sessions.list.current` field. A mounted button identifies one view instance, not just one session. Keyboard focus or a click inside a visible view selects that instance. An ambiguous multi-view selection fails closed with a prompt to select a conversation. Focus in the find bar preserves the selected instance. On the first shortcut, neutral body focus may discover one registered view only when all visible registered instances and known conversation content/scroll/flow containers identify that same single instance. Visible unsupported or embedded views also make discovery ambiguous; the main panel and its own content are deduplicated. Explicit rejection, ambiguity, or loss of an earlier selection blocks that discovery; passive checks never select a replacement.
|
|
8
|
+
|
|
9
|
+
Official release-tag sources inspected:
|
|
10
|
+
|
|
11
|
+
- [`0.1.5-rc.2` slot contract](https://github.com/deepseek-ai/deepseek-harness/blob/dsh-v0.1.5-rc.2/packages/client/ui-conversation/src/client/contract/slots.ts), [session standard props](https://github.com/deepseek-ai/deepseek-harness/blob/dsh-v0.1.5-rc.2/packages/client/ui-session/src/client/index.ts), and [header utility registration / `ctx.effect` example](https://github.com/deepseek-ai/deepseek-harness/blob/dsh-v0.1.5-rc.2/packages/session-query/session-log-export/src/client/index.ts)
|
|
12
|
+
- [`0.1.5-rc.2` root](https://github.com/deepseek-ai/deepseek-harness/blob/dsh-v0.1.5-rc.2/packages/client/ui-conversation/src/client/skeleton/ConversationRoot.tsx) and [`0.1.5-rc.3` root](https://github.com/deepseek-ai/deepseek-harness/blob/dsh-v0.1.5-rc.3/packages/client/ui-conversation/src/client/skeleton/ConversationRoot.tsx): the header and one conversation scrollport share the `data-phase` root
|
|
13
|
+
- [`0.1.6-alpha.2` main panel](https://github.com/deepseek-ai/deepseek-harness/blob/dsh-v0.1.6-alpha.2/packages/client/ui-conversation/src/client/skeleton/ConversationMainPanel.tsx) and [content](https://github.com/deepseek-ai/deepseek-harness/blob/dsh-v0.1.6-alpha.2/packages/client/ui-conversation/src/client/skeleton/ConversationContent.tsx): split components retain that root/scrollport structure
|
|
14
|
+
- [`0.1.7-rc.2` content](https://github.com/deepseek-ai/deepseek-harness/blob/dsh-v0.1.7-rc.2/packages/client/ui-conversation/src/client/skeleton/ConversationContent.tsx), [`0.2.0-rc.1` content](https://github.com/deepseek-ai/deepseek-harness/blob/dsh-v0.2.0-rc.1/packages/client/ui-conversation/src/client/skeleton/ConversationContent.tsx), and [`0.2.0-rc.2` content](https://github.com/deepseek-ai/deepseek-harness/blob/dsh-v0.2.0-rc.2/packages/client/ui-conversation/src/client/skeleton/ConversationContent.tsx): explicit content session identity is additionally checked
|
|
15
|
+
- [`0.2.0-rc.2` slot registry](https://github.com/deepseek-ai/deepseek-harness/blob/dsh-v0.2.0-rc.2/packages/client/ui-renderer/src/client/registry.ts): slot injection and registration are context-owned effects
|
|
16
|
+
|
|
17
|
+
The official [startup fixture](https://github.com/deepseek-ai/deepseek-harness/blob/dsh-v0.2.0-rc.2/apps/web/tests/startup-auto-selection.e2e.ts) explicitly distinguishes the resident conversation root from the composer's unrelated `data-phase`. Routing skips that editor attribute. Initial focus in an unbound Hero/settling composer before any visible registered session exists keeps the never-selected state; a rejected active conversation or lost selection still cannot trigger discovery of another view.
|
|
18
|
+
|
|
19
|
+
DOM attributes are implementation details, not a promised future host API. `resolveScope` is their single adapter boundary. It requires an active, connected, visible panel, exactly one registered session anchor, one scrollport, and one visible `data-chat-flow`. If explicit host session metadata exists, it must match. Search and mutation observation stay inside that flow. A missing/ambiguous/hidden/replaced flow yields no results or paging. There is no `document.body` or `window.find` fallback.
|
|
20
|
+
|
|
21
|
+
`ctx.effect` owns global key/focus/pointer listeners and teardown. React effect cleanup removes each session anchor. Close/dispose cancels debounce, paging-start, paging-settle, session polling, observation, and highlights. An in-flight host request cannot be aborted through this API; its generation token prevents stale results from being painted or another page being requested.
|
|
22
|
+
|
|
23
|
+
Paging errors/stalls and manual stop retain rendered matches and explicitly label them incomplete. Existing matching and query behavior have not been redesigned.
|
|
24
|
+
|
|
25
|
+
## Acceptance layers (do not conflate)
|
|
26
|
+
|
|
27
|
+
1. **Logic and contract fixtures**: `npm run check` runs pure logic, registration, and React 18 + jsdom synthetic session tests. These assert old/new structural adapters, multiple views (including the same session twice), ambiguity, hidden/mismatched roots, scoped results, paging failure, in-flight switching, timer/highlight teardown, and no browser-wide fallback. Geometry is mocked in jsdom; this is not a real host/browser acceptance result.
|
|
28
|
+
2. **Actual Web runtime + synthetic sessions**: installation of the same packed artifact, real module loader/slot rendering, paging, multiple view transitions and the host-supported disable/re-enable lifecycle must be recorded for each target release. A browser fixture alone cannot pass this layer.
|
|
29
|
+
3. **Target Electron Desktop smoke**: the same artifact must be checked on the target Desktop build/OS. Web or jsdom success does not pass this layer.
|
|
30
|
+
|
|
31
|
+
Candidate releases: `0.1.5-rc.2`, `0.1.5-rc.3`, `0.1.6-alpha.1`, `0.1.6-alpha.2`, `0.1.7-alpha.1`, `0.1.7-alpha.2`, `0.1.7-rc.1`, `0.1.7-rc.2`, `0.2.0-rc.1`, `0.2.0-rc.2`.
|
|
32
|
+
|
|
33
|
+
The 2026-10-02 registry audit compared every published `@deepseek-ai/dsh` version with main's three peer requirements (`>=0.1.5-rc.2 <0.1.6-0 || >=0.1.6-0 <0.2.0-0`) using the official prerequisite rule. The original six-target sample accidentally excluded four releases that the old gate admitted: `0.1.6-alpha.1`, `0.1.7-alpha.1`, `0.1.7-alpha.2`, and `0.1.7-rc.1`. The ten-target candidate includes all currently published versions admitted by that old gate plus both current 0.2 release candidates. The user's Desktop 2.0.14 report identifies its bundled runtime as `0.1.7-rc.1`; Desktop marketing/version labels are not inferred to equal DSH runtime versions.
|
|
34
|
+
|
|
35
|
+
The four added targets' official tagged slot, session props, conversation skeleton and HMR sources were inspected, and their vendor package pins are recorded in `scripts/compat/vendor-versions.json`. Links: [0.1.6-alpha.1](https://github.com/deepseek-ai/deepseek-harness/tree/dsh-v0.1.6-alpha.1/packages/client), [0.1.7-alpha.1](https://github.com/deepseek-ai/deepseek-harness/tree/dsh-v0.1.7-alpha.1/packages/client), [0.1.7-alpha.2](https://github.com/deepseek-ai/deepseek-harness/tree/dsh-v0.1.7-alpha.2/packages/client), [0.1.7-rc.1](https://github.com/deepseek-ai/deepseek-harness/tree/dsh-v0.1.7-rc.1/packages/client).
|
|
36
|
+
|
|
37
|
+
Every candidate must pass the same new tgz in real Web acceptance, including first Ctrl+F with body focus and no preparatory anchor click. Previous artifact results cannot validate changed production bytes. Desktop acceptance is separately recorded, including a Windows Web run for platform-specific harness behavior. The exact peer list enables verification without exemptions; it is not a passed support matrix. The historical `dshReleases` record is unchanged. Before release, failures must be fixed or clearly documented; removing a previously accepted target requires an explicit reviewed compatibility decision, not silent narrowing.
|
|
38
|
+
|
|
39
|
+
The official [compatibility evaluator](https://github.com/deepseek-ai/deepseek-harness/blob/dsh-v0.2.0-rc.2/packages/boot/app-boot/src/plugin-compatibility.ts) uses `semver.satisfies(..., { includePrerelease: true })`. Do not infer its results from ordinary npm semver behavior. Do not remove peer requirements or grant an exemption to make the matrix appear green.
|
|
40
|
+
|
|
41
|
+
## Reproduction
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
npm ci --ignore-scripts --legacy-peer-deps
|
|
45
|
+
npm run check
|
|
46
|
+
npm_config_cache=/tmp/find-all-npm-cache npm run verify:release
|
|
47
|
+
npm pack --ignore-scripts --pack-destination /tmp/find-all-candidate
|
|
48
|
+
# Extract this one tgz, retain its SHA-256, then test those bytes:
|
|
49
|
+
FIND_ALL_PACKAGE_ROOT=/tmp/find-all-candidate/package node --test test/session-adapter.test.mjs
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`--legacy-peer-deps` here is only for isolated development test dependencies; it is not a host installation result or exemption. Test sessions must be synthetic, with no user data or model credentials. Do not change the artifact between version runs. Unknown future host versions need fresh regression evidence before changing support metadata.
|
|
53
|
+
|
|
54
|
+
## Host lifecycle boundary
|
|
55
|
+
|
|
56
|
+
The official HMR client in [0.1.5-rc.2](https://github.com/deepseek-ai/deepseek-harness/blob/dsh-v0.1.5-rc.2/packages/client/hmr/src/client/index.ts) and [0.1.5-rc.3](https://github.com/deepseek-ai/deepseek-harness/blob/dsh-v0.1.5-rc.3/packages/client/hmr/src/client/index.ts) deliberately ignores graph changes after initial load. These hosts and [0.1.6-alpha.1](https://github.com/deepseek-ai/deepseek-harness/blob/dsh-v0.1.6-alpha.1/packages/client/hmr/src/client/index.ts) require a page refresh or application restart after enabling/disabling a plugin. We do not patch the host or claim hot unload works there. Their acceptance checks wait for the actual server graph change, then verify disabled/restored behavior after an explicit refresh.
|
|
57
|
+
|
|
58
|
+
The inspected HMR clients in [0.1.6-alpha.2](https://github.com/deepseek-ai/deepseek-harness/blob/dsh-v0.1.6-alpha.2/packages/client/hmr/src/client/index.ts), [0.1.7-rc.2](https://github.com/deepseek-ai/deepseek-harness/blob/dsh-v0.1.7-rc.2/packages/client/hmr/src/client/index.ts), [0.2.0-rc.1](https://github.com/deepseek-ai/deepseek-harness/blob/dsh-v0.2.0-rc.1/packages/client/hmr/src/client/index.ts), and [0.2.0-rc.2](https://github.com/deepseek-ai/deepseek-harness/blob/dsh-v0.2.0-rc.2/packages/client/hmr/src/client/index.ts) synchronize graph entries. The newly inspected 0.1.7-alpha.1, alpha.2 and rc.1 use the same `entries.sync` mechanism. Their acceptance checks require two real hot disable/re-enable cycles with zero page navigations. Every target must also pass actual search, history paging, and A/B/A session switching using the same candidate tgz. See the PR's linked CI evidence for run-specific results and artifact hashes; pending matrix entries are not passes. All Electron Desktop targets remain unverified until separately recorded.
|
|
59
|
+
|
|
60
|
+
## Windows validation isolation
|
|
61
|
+
|
|
62
|
+
The runner preserves its real `USERPROFILE`; DSH state and Electron `--user-data-dir` use owned temporary directories. A bounded no-inspector comparison changes only `USERPROFILE` (with separate evidence log filenames) before the UI smoke. It records crash/survival, not a UI pass. No real user profile, credentials, sandbox or security policy is changed. Windows Web validation invokes the npm `.cmd` shim through PowerShell with literal arguments and imports resolved modules through `pathToFileURL`.
|
|
63
|
+
|
|
64
|
+
## Simultaneous embedded sidebar boundary
|
|
65
|
+
|
|
66
|
+
The official rc2 [SidebarChatTab](https://github.com/deepseek-ai/deepseek-harness/blob/dsh-v0.2.0-rc.2/packages/client/ui-subagent/src/client/sidebar-chat/index.tsx) renders an embedded ConversationContent with `data-conversation-content`, `data-conversation-session` and `data-content-phase`, without the main panel's `data-phase` or a session header utility. This unsupported view must block neutral fallback to another conversation; explicit interaction with it remains rejected after it disappears. Fixtures cover visible registered Hero/settling/unknown phases, hidden utilities and headerless sidebars.
|
|
67
|
+
|
|
68
|
+
The current rc2 real-host case authors a completed one-shot child with the official persisted header/descriptor/catalog pattern from [subagent-conversation.e2e.ts](https://github.com/deepseek-ai/deepseek-harness/blob/dsh-v0.2.0-rc.2/apps/web/tests/subagent-conversation.e2e.ts), opens the host's actual right-sidebar control, and natively re-enables the plugin with both views visible. It requires neutral Ctrl+F to reject ambiguity, an explicit main choice to remain scoped, and unsupported sidebar interaction to reject main-session search. No model is called and no second DOM pane is fabricated. The CI result for each artifact determines whether this case passed.
|
package/lib/client.js
CHANGED
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
// * Highlights use the CSS Custom Highlight API (CSS.highlights + Highlight)
|
|
33
33
|
// which paints ranges WITHOUT mutating the DOM — React keeps full
|
|
34
34
|
// ownership of the tree. Chromium/Electron support this since 105.
|
|
35
|
-
// *
|
|
36
|
-
// window.find()
|
|
35
|
+
// * Without CSS Highlights, navigation still scrolls only scoped ranges.
|
|
36
|
+
// Browser-wide window.find() is deliberately never used.
|
|
37
37
|
//
|
|
38
38
|
// Keyboard:
|
|
39
39
|
// Cmd/Ctrl+F open (prefilled with the current selection)
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
|
|
44
44
|
window.__ModuleLoader__.load({
|
|
45
45
|
id: "@ryuu-64/dsh-find-all",
|
|
46
|
-
factory: () => {
|
|
46
|
+
factory: (require) => {
|
|
47
47
|
var module = { exports: {} };
|
|
48
48
|
var exports = module.exports;
|
|
49
49
|
|
|
@@ -76,7 +76,11 @@ window.__ModuleLoader__.load({
|
|
|
76
76
|
capped: "已到加载上限({n} 页),停止加载",
|
|
77
77
|
stalled: "没有更早的历史了",
|
|
78
78
|
stopTip: "点击停止加载",
|
|
79
|
-
noService: "
|
|
79
|
+
noService: "会话接口不可用,仅保留已显示的部分结果",
|
|
80
|
+
noTarget: "请先选择可见的会话,再打开查找",
|
|
81
|
+
noRoot: "当前会话的搜索范围不可用",
|
|
82
|
+
incomplete: "历史尚未搜完,保留部分结果",
|
|
83
|
+
find: "查找会话"
|
|
80
84
|
} : {
|
|
81
85
|
placeholder: "Find in conversation…",
|
|
82
86
|
prev: "Previous match",
|
|
@@ -92,7 +96,11 @@ window.__ModuleLoader__.load({
|
|
|
92
96
|
capped: "Page cap reached ({n}), stopped loading",
|
|
93
97
|
stalled: "No earlier history",
|
|
94
98
|
stopTip: "Click to stop loading",
|
|
95
|
-
noService: "Session service unavailable
|
|
99
|
+
noService: "Session service unavailable; rendered results only",
|
|
100
|
+
noTarget: "Select a visible conversation before opening find",
|
|
101
|
+
noRoot: "Search scope unavailable for this conversation",
|
|
102
|
+
incomplete: "History search incomplete; partial results retained",
|
|
103
|
+
find: "Find in conversation"
|
|
96
104
|
};
|
|
97
105
|
|
|
98
106
|
//#region pure helpers (exported for tests)
|
|
@@ -121,7 +129,7 @@ window.__ModuleLoader__.load({
|
|
|
121
129
|
if (meta && (key === "f" || key === "F")) {
|
|
122
130
|
event.preventDefault();
|
|
123
131
|
event.stopPropagation();
|
|
124
|
-
ops.open();
|
|
132
|
+
ops.open(event.target);
|
|
125
133
|
return;
|
|
126
134
|
}
|
|
127
135
|
if (!ops.isOpen()) return;
|
|
@@ -239,10 +247,10 @@ window.__ModuleLoader__.load({
|
|
|
239
247
|
return into;
|
|
240
248
|
}
|
|
241
249
|
|
|
242
|
-
function collectRanges(query) {
|
|
250
|
+
function collectRanges(query, root) {
|
|
243
251
|
var ranges = [];
|
|
244
|
-
if (!query || typeof document === "undefined"
|
|
245
|
-
var nodes = collectTextNodes(
|
|
252
|
+
if (!query || !root || typeof document === "undefined") return ranges;
|
|
253
|
+
var nodes = collectTextNodes(root, []);
|
|
246
254
|
for (var i = 0; i < nodes.length && ranges.length < MAX_MATCHES; i++) {
|
|
247
255
|
var node = nodes[i];
|
|
248
256
|
var indices = findIndices(node.data, query);
|
|
@@ -270,48 +278,195 @@ window.__ModuleLoader__.load({
|
|
|
270
278
|
}
|
|
271
279
|
}
|
|
272
280
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
281
|
+
//#region host adapter
|
|
282
|
+
// Official session-scoped header utility props supply identity. DOM selectors
|
|
283
|
+
// are intentionally isolated here: see docs/compatibility.md for tag sources.
|
|
284
|
+
// No global current-session state and no page-wide search fallback.
|
|
285
|
+
function visible(element) {
|
|
286
|
+
if (!element || !element.isConnected || !element.getClientRects().length) return false;
|
|
287
|
+
for (var node = element; node; node = node.parentElement) {
|
|
288
|
+
if (node.hidden || node.getAttribute("aria-hidden") === "true") return false;
|
|
289
|
+
var css = window.getComputedStyle(node);
|
|
290
|
+
if (css.display === "none" || css.visibility === "hidden" || css.visibility === "collapse" || css.opacity === "0") return false;
|
|
291
|
+
}
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function conversationPanel(element) {
|
|
296
|
+
// The host composer has its own unrelated data-phase. A conversation
|
|
297
|
+
// root additionally owns the scrollport (official startup fixture).
|
|
298
|
+
for (var node = element; node && node !== document.body; node = node.parentElement) {
|
|
299
|
+
if (!node.hasAttribute || !node.hasAttribute("data-phase")) continue;
|
|
300
|
+
var phase = node.getAttribute("data-phase");
|
|
301
|
+
// Preserve even a broken declared panel boundary; never walk through
|
|
302
|
+
// it to a different ancestor conversation because its scroll is absent.
|
|
303
|
+
if (phase === "active" || phase === "hero" || phase === "settling" || node.querySelector('[data-conversation-scroll]')) return node;
|
|
304
|
+
}
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function resolveScope(anchor, sessionId) {
|
|
309
|
+
if (!anchor || typeof sessionId !== "string" || !sessionId || !visible(anchor)) return null;
|
|
310
|
+
var panel = conversationPanel(anchor);
|
|
311
|
+
if (!panel || panel.getAttribute("data-phase") !== "active" || panel === document.documentElement) return null;
|
|
312
|
+
var anchors = panel.querySelectorAll('[data-find-all-session]');
|
|
313
|
+
if (anchors.length !== 1 || anchors[0] !== anchor) return null;
|
|
314
|
+
var scrolls = panel.querySelectorAll('[data-conversation-scroll]');
|
|
315
|
+
if (scrolls.length !== 1) return null;
|
|
316
|
+
var content = scrolls[0].closest('[data-conversation-content]');
|
|
317
|
+
// 0.1.6-alpha.2 has the content wrapper but no identity attribute.
|
|
318
|
+
// Identity still comes from the unique session-scoped header anchor.
|
|
319
|
+
if (content && content.hasAttribute('data-conversation-session') && content.getAttribute('data-conversation-session') !== sessionId) return null;
|
|
320
|
+
var flows = scrolls[0].querySelectorAll('[data-chat-flow]');
|
|
321
|
+
if (flows.length !== 1 || !visible(flows[0])) return null;
|
|
322
|
+
return { panel: panel, root: flows[0] };
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function createHostAdapter(ctx, changed) {
|
|
326
|
+
var entries = new Map();
|
|
327
|
+
var scopeUnavailable = false;
|
|
328
|
+
// Only a never-selected adapter may discover an initial neutral context.
|
|
329
|
+
// Explicit rejection or loss of a selection must never select a substitute.
|
|
330
|
+
var selectionState = "pristine";
|
|
331
|
+
function candidates() {
|
|
332
|
+
var out = [];
|
|
333
|
+
entries.forEach(function (id, anchor) {
|
|
334
|
+
// Keep unsupported/invisible instances in the identity roster. Dropping
|
|
335
|
+
// them before routing could silently choose a different valid session.
|
|
336
|
+
out.push({ id: id, anchor: anchor, panel: conversationPanel(anchor), scope: resolveScope(anchor, id) });
|
|
337
|
+
});
|
|
338
|
+
return out;
|
|
339
|
+
}
|
|
340
|
+
function choose(entry) {
|
|
341
|
+
if (!entry) return null;
|
|
342
|
+
if (!entry.scope) { selectionState = "rejected"; scopeUnavailable = true; return null; }
|
|
343
|
+
selectionState = "selected";
|
|
344
|
+
return { id: entry.id, anchor: entry.anchor, panel: entry.scope.panel, root: entry.scope.root };
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function visibleEntry(entry) {
|
|
348
|
+
var container = entry.panel || entry.anchor.closest('[data-phase], [data-conversation-content]');
|
|
349
|
+
return visible(entry.anchor) || visible(container);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function discoverInitial(all) {
|
|
353
|
+
var shown = all.filter(visibleEntry);
|
|
354
|
+
var nodes = Array.prototype.filter.call(document.querySelectorAll('[data-phase], [data-conversation-content], [data-conversation-scroll], [data-chat-flow]'), visible);
|
|
355
|
+
scopeUnavailable = false;
|
|
356
|
+
if (shown.length === 0) {
|
|
357
|
+
// An empty resident Hero is not a session choice. A known active
|
|
358
|
+
// or identified but unregistered view is unavailable, not pristine.
|
|
359
|
+
if (nodes.some(function (node) { return node.getAttribute('data-phase') === 'active' || node.getAttribute('data-content-phase') === 'active' || node.hasAttribute('data-conversation-session') || node.hasAttribute('data-chat-flow'); })) {
|
|
360
|
+
selectionState = "rejected"; scopeUnavailable = true;
|
|
361
|
+
}
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
if (shown.length !== 1) { selectionState = "rejected"; return null; }
|
|
365
|
+
var entry = shown[0], scope = entry.scope;
|
|
366
|
+
if (!scope) return choose(entry);
|
|
367
|
+
var content = scope.root.closest('[data-conversation-content]');
|
|
368
|
+
var scroll = scope.root.closest('[data-conversation-scroll]');
|
|
369
|
+
// One main panel and its own content/scroll/flow count as one view.
|
|
370
|
+
// Any other visible known container, including a headerless embedded
|
|
371
|
+
// sidebar, makes neutral discovery ambiguous even if unsupported.
|
|
372
|
+
var other = nodes.some(function (node) {
|
|
373
|
+
if (node.hasAttribute('data-conversation-content') && node !== content) return true;
|
|
374
|
+
if (node.hasAttribute('data-conversation-scroll') && node !== scroll) return true;
|
|
375
|
+
if (node.hasAttribute('data-chat-flow') && node !== scope.root) return true;
|
|
376
|
+
return node.hasAttribute('data-phase') && conversationPanel(node) === node && node !== scope.panel;
|
|
377
|
+
});
|
|
378
|
+
if (other) { selectionState = "rejected"; return null; }
|
|
379
|
+
return choose(entry);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function refresh(entry) {
|
|
383
|
+
if (!entry) return null;
|
|
384
|
+
// Passive checks and repeated shortcuts may only retain this exact
|
|
385
|
+
// registered instance and identity. They never discover a substitute.
|
|
386
|
+
if (entries.get(entry.anchor) !== entry.id) { selectionState = "rejected"; scopeUnavailable = true; return null; }
|
|
387
|
+
scopeUnavailable = false;
|
|
388
|
+
return choose({ id: entry.id, anchor: entry.anchor, scope: resolveScope(entry.anchor, entry.id) });
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return {
|
|
392
|
+
mount: function (anchor, id) {
|
|
393
|
+
entries.set(anchor, id);
|
|
394
|
+
changed();
|
|
395
|
+
return function () { entries.delete(anchor); changed(); };
|
|
396
|
+
},
|
|
397
|
+
refresh: refresh,
|
|
398
|
+
select: function (target, previous, retainNeutral) {
|
|
399
|
+
var all = candidates();
|
|
400
|
+
if (!target || !target.nodeType) target = document.activeElement;
|
|
401
|
+
if (target && state.bar && state.bar.contains(target)) return refresh(previous);
|
|
402
|
+
if (retainNeutral && (target === document.body || target === document.documentElement)) {
|
|
403
|
+
if (previous || selectionState !== "pristine") return refresh(previous);
|
|
404
|
+
return discoverInitial(all);
|
|
405
|
+
}
|
|
406
|
+
scopeUnavailable = false;
|
|
407
|
+
var direct = all.filter(function (entry) { return target && entry.anchor.contains(target); });
|
|
408
|
+
if (direct.length === 1) return choose(direct[0]);
|
|
409
|
+
var content = target && target.closest ? target.closest('[data-conversation-content]') : null;
|
|
410
|
+
var panel = conversationPanel(target) || content;
|
|
411
|
+
var hit = all.filter(function (entry) { return panel && entry.panel === panel; });
|
|
412
|
+
if (hit.length === 1) {
|
|
413
|
+
if (content && hit[0].scope && hit[0].scope.root.closest('[data-conversation-content]') !== content) {
|
|
414
|
+
selectionState = "rejected"; scopeUnavailable = true; return null;
|
|
415
|
+
}
|
|
416
|
+
return choose(hit[0]);
|
|
417
|
+
}
|
|
418
|
+
if (panel) {
|
|
419
|
+
// Before any session is selected, focus in the resident unbound
|
|
420
|
+
// Hero/settling composer is not a rejected conversation choice.
|
|
421
|
+
var phase = panel.getAttribute('data-phase') || panel.getAttribute('data-content-phase');
|
|
422
|
+
var unbound = !panel.hasAttribute('data-conversation-session') && !panel.querySelector('[data-conversation-session]');
|
|
423
|
+
if (selectionState === "pristine" && !previous && unbound && !all.some(visibleEntry) && (phase === "hero" || phase === "settling")) return null;
|
|
424
|
+
selectionState = "rejected"; scopeUnavailable = true; return null;
|
|
425
|
+
}
|
|
426
|
+
if (previous) selectionState = "rejected";
|
|
427
|
+
return null;
|
|
428
|
+
},
|
|
429
|
+
scopeUnavailable: function () { return scopeUnavailable; },
|
|
430
|
+
valid: function (entry) {
|
|
431
|
+
if (!entry || entries.get(entry.anchor) !== entry.id) return false;
|
|
432
|
+
var scope = resolveScope(entry.anchor, entry.id);
|
|
433
|
+
return !!scope && scope.root === entry.root;
|
|
434
|
+
},
|
|
435
|
+
face: function (entry) {
|
|
436
|
+
try {
|
|
437
|
+
if (!this.valid(entry) || !ctx.sessions || typeof ctx.sessions.binding !== "function") return null;
|
|
438
|
+
var binding = ctx.sessions.binding(entry.id);
|
|
439
|
+
var face = binding && binding.session;
|
|
440
|
+
return face && typeof face.loadOlder === "function" ? face : null;
|
|
441
|
+
} catch (error) { return null; }
|
|
442
|
+
},
|
|
443
|
+
clear: function () { entries.clear(); }
|
|
444
|
+
};
|
|
277
445
|
}
|
|
278
446
|
|
|
279
|
-
|
|
280
|
-
|
|
447
|
+
function domSignature(root) {
|
|
448
|
+
return root ? collectTextNodes(root, []).length : 0;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function waitForChange(before, root, token) {
|
|
281
452
|
return new Promise(function (resolve) {
|
|
282
453
|
var started = Date.now();
|
|
454
|
+
function finish(value) {
|
|
455
|
+
clearTimeout(state.settleTimer);
|
|
456
|
+
state.settleTimer = 0;
|
|
457
|
+
state.settleCancel = null;
|
|
458
|
+
resolve(value);
|
|
459
|
+
}
|
|
460
|
+
state.settleCancel = function () { finish(false); };
|
|
283
461
|
(function poll() {
|
|
284
|
-
if (
|
|
285
|
-
if (
|
|
286
|
-
|
|
462
|
+
if (token !== state.pageToken || !state.adapter.valid(state.target)) { finish(false); return; }
|
|
463
|
+
if (domSignature(root) !== before) { finish(true); return; }
|
|
464
|
+
if (Date.now() - started >= PAGE_SETTLE_MS) { finish(false); return; }
|
|
465
|
+
state.settleTimer = setTimeout(poll, PAGE_POLL_MS);
|
|
287
466
|
})();
|
|
288
467
|
});
|
|
289
468
|
}
|
|
290
469
|
|
|
291
|
-
//#region session bridge
|
|
292
|
-
//
|
|
293
|
-
// The `sessions` client service is provided by
|
|
294
|
-
// @deepseek-ai/dsh-api-session-controller; the per-session face it binds
|
|
295
|
-
// exposes loadOlder() plus a snapshot carrying hasMore/loadingOlder.
|
|
296
|
-
// Everything here is defensive: a host that changes shape must degrade to
|
|
297
|
-
// page-only search, never break the bar.
|
|
298
|
-
|
|
299
|
-
function resolveFace(ctx) {
|
|
300
|
-
try {
|
|
301
|
-
var sessions = ctx && ctx.sessions;
|
|
302
|
-
if (!sessions || !sessions.list || typeof sessions.list.getSnapshot !== "function") return null;
|
|
303
|
-
var id = sessions.list.getSnapshot().current;
|
|
304
|
-
if (!id) return null;
|
|
305
|
-
if (typeof sessions.binding !== "function") return null;
|
|
306
|
-
var binding = sessions.binding(id);
|
|
307
|
-
var face = binding && binding.session ? binding.session : null;
|
|
308
|
-
if (!face || typeof face.loadOlder !== "function") return null;
|
|
309
|
-
return { id: id, face: face };
|
|
310
|
-
} catch (error) {
|
|
311
|
-
return null;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
470
|
function faceHasMore(face) {
|
|
316
471
|
try {
|
|
317
472
|
if (typeof face.getSnapshot !== "function") return null;
|
|
@@ -327,6 +482,10 @@ window.__ModuleLoader__.load({
|
|
|
327
482
|
|
|
328
483
|
var state = {
|
|
329
484
|
ctx: null,
|
|
485
|
+
adapter: null,
|
|
486
|
+
target: null,
|
|
487
|
+
settleTimer: 0,
|
|
488
|
+
settleCancel: null,
|
|
330
489
|
bar: null,
|
|
331
490
|
input: null,
|
|
332
491
|
count: null,
|
|
@@ -384,32 +543,29 @@ window.__ModuleLoader__.load({
|
|
|
384
543
|
else state.scopeBtn.removeAttribute("data-whole");
|
|
385
544
|
}
|
|
386
545
|
|
|
546
|
+
function unavailableStatus() {
|
|
547
|
+
return state.target || (state.adapter && state.adapter.scopeUnavailable()) ? L.noRoot : L.noTarget;
|
|
548
|
+
}
|
|
549
|
+
|
|
387
550
|
function runSearch(keepIndex) {
|
|
388
|
-
state.
|
|
551
|
+
var valid = state.adapter && state.adapter.valid(state.target);
|
|
552
|
+
state.ranges = valid ? collectRanges(state.query, state.target.root) : [];
|
|
553
|
+
if (!valid) setStatus(unavailableStatus(), false);
|
|
389
554
|
state.capped = state.ranges.length >= MAX_MATCHES;
|
|
390
555
|
if (!keepIndex || state.index >= state.ranges.length) state.index = state.ranges.length > 0 ? 0 : -1;
|
|
391
556
|
paint();
|
|
392
557
|
updateCount();
|
|
393
|
-
|
|
558
|
+
|
|
394
559
|
}
|
|
395
560
|
|
|
396
561
|
function goTo(delta) {
|
|
562
|
+
if (!state.adapter || !state.adapter.valid(state.target)) { runSearch(false); return; }
|
|
397
563
|
var n = state.ranges.length;
|
|
398
564
|
if (n === 0) return;
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
scrollToRange(state.ranges[state.index]);
|
|
404
|
-
} else {
|
|
405
|
-
legacyFind(state.query, delta < 0);
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
function legacyFind(query, backwards) {
|
|
410
|
-
if (typeof window.find === "function") {
|
|
411
|
-
try { window.find(query, false, backwards, true); } catch (error) {}
|
|
412
|
-
}
|
|
565
|
+
state.index = ((state.index + delta) % n + n) % n;
|
|
566
|
+
paint();
|
|
567
|
+
updateCount();
|
|
568
|
+
scrollToRange(state.ranges[state.index]);
|
|
413
569
|
}
|
|
414
570
|
|
|
415
571
|
function scheduleRescan() {
|
|
@@ -418,14 +574,14 @@ window.__ModuleLoader__.load({
|
|
|
418
574
|
if (state.paging) return;
|
|
419
575
|
clearTimeout(state.debounce);
|
|
420
576
|
state.debounce = setTimeout(function () {
|
|
421
|
-
if (
|
|
577
|
+
if (isOpen()) runSearch(true);
|
|
422
578
|
}, 250);
|
|
423
579
|
}
|
|
424
580
|
|
|
425
581
|
function startObserver() {
|
|
426
|
-
if (state.observer || typeof MutationObserver === "undefined") return;
|
|
582
|
+
if (state.observer || !state.target || typeof MutationObserver === "undefined") return;
|
|
427
583
|
state.observer = new MutationObserver(scheduleRescan);
|
|
428
|
-
state.observer.observe(
|
|
584
|
+
state.observer.observe(state.target.root, { childList: true, subtree: true, characterData: true });
|
|
429
585
|
}
|
|
430
586
|
|
|
431
587
|
function stopObserver() {
|
|
@@ -438,6 +594,8 @@ window.__ModuleLoader__.load({
|
|
|
438
594
|
|
|
439
595
|
function cancelPageIn() {
|
|
440
596
|
state.pageToken += 1;
|
|
597
|
+
if (state.settleCancel) state.settleCancel();
|
|
598
|
+
clearTimeout(state.settleTimer);
|
|
441
599
|
clearTimeout(state.pageTimer);
|
|
442
600
|
state.pageTimer = 0;
|
|
443
601
|
state.paging = false;
|
|
@@ -454,13 +612,18 @@ window.__ModuleLoader__.load({
|
|
|
454
612
|
function startPageIn() {
|
|
455
613
|
if (state.scope !== "whole" || !state.query || !isOpen()) return;
|
|
456
614
|
var token = ++state.pageToken;
|
|
457
|
-
var
|
|
458
|
-
if (!
|
|
615
|
+
var target = state.target;
|
|
616
|
+
if (!state.adapter || !state.adapter.valid(target)) {
|
|
617
|
+
setStatus(unavailableStatus(), false);
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
var face = state.adapter.face(target);
|
|
621
|
+
if (!face) {
|
|
459
622
|
setStatus(L.noService, false);
|
|
460
623
|
return;
|
|
461
624
|
}
|
|
462
|
-
state.sessionId =
|
|
463
|
-
if (faceHasMore(
|
|
625
|
+
state.sessionId = target.id;
|
|
626
|
+
if (faceHasMore(face) === false) {
|
|
464
627
|
setStatus(L.allLoaded, false);
|
|
465
628
|
return;
|
|
466
629
|
}
|
|
@@ -469,11 +632,11 @@ window.__ModuleLoader__.load({
|
|
|
469
632
|
setStatus(L.loading.replace("{n}", "0"), true);
|
|
470
633
|
runPageIn({
|
|
471
634
|
maxPages: MAX_PAGES,
|
|
472
|
-
isCancelled: function () { return token !== state.pageToken || !isOpen(); },
|
|
473
|
-
hasMore: function () { return faceHasMore(
|
|
474
|
-
loadOlder: function () { return Promise.resolve(
|
|
475
|
-
signature: domSignature,
|
|
476
|
-
waitChange: waitForChange,
|
|
635
|
+
isCancelled: function () { return token !== state.pageToken || !isOpen() || !state.adapter.valid(target); },
|
|
636
|
+
hasMore: function () { return faceHasMore(face); },
|
|
637
|
+
loadOlder: function () { return Promise.resolve(face.loadOlder()); },
|
|
638
|
+
signature: function () { return domSignature(target.root); },
|
|
639
|
+
waitChange: function (before) { return waitForChange(before, target.root, token); },
|
|
477
640
|
onProgress: function (pages) {
|
|
478
641
|
if (token !== state.pageToken) return;
|
|
479
642
|
state.pages = pages;
|
|
@@ -485,25 +648,35 @@ window.__ModuleLoader__.load({
|
|
|
485
648
|
state.paging = false;
|
|
486
649
|
state.pages = result.pages;
|
|
487
650
|
if (result.reason === "capped") setStatus(L.capped.replace("{n}", String(result.pages)), false);
|
|
488
|
-
else if (result.reason
|
|
651
|
+
else if (result.reason !== "done") setStatus(L.incomplete, false);
|
|
489
652
|
else setStatus(result.pages > 0 ? L.allLoaded : "", false);
|
|
490
653
|
runSearch(true);
|
|
491
654
|
});
|
|
492
655
|
}
|
|
493
656
|
|
|
494
|
-
|
|
657
|
+
function syncTarget(target, passive, retainNeutral) {
|
|
658
|
+
if (!state.adapter) return;
|
|
659
|
+
var selected = passive ? state.adapter.refresh(state.target) : state.adapter.select(target || document.activeElement, state.target, retainNeutral);
|
|
660
|
+
if (selected && state.target && selected.id === state.target.id && selected.anchor === state.target.anchor && selected.root === state.target.root) return;
|
|
661
|
+
if (!selected && !state.target) {
|
|
662
|
+
if (isOpen()) runSearch(false);
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
cancelPageIn();
|
|
666
|
+
stopObserver();
|
|
667
|
+
state.target = selected;
|
|
668
|
+
state.sessionId = selected ? selected.id : null;
|
|
669
|
+
if (!isOpen()) return;
|
|
670
|
+
setStatus(selected ? "" : unavailableStatus(), false);
|
|
671
|
+
runSearch(false);
|
|
672
|
+
startObserver();
|
|
673
|
+
if (selected && state.scope === "whole" && state.query) schedulePageIn();
|
|
674
|
+
}
|
|
675
|
+
|
|
495
676
|
function watchSession() {
|
|
496
677
|
clearInterval(state.sessionTimer);
|
|
497
678
|
state.sessionTimer = setInterval(function () {
|
|
498
|
-
if (
|
|
499
|
-
var resolved = resolveFace(state.ctx);
|
|
500
|
-
var id = resolved ? resolved.id : null;
|
|
501
|
-
if (id === state.sessionId) return;
|
|
502
|
-
state.sessionId = id;
|
|
503
|
-
cancelPageIn();
|
|
504
|
-
setStatus("", false);
|
|
505
|
-
runSearch(false);
|
|
506
|
-
if (state.scope === "whole" && state.query) schedulePageIn();
|
|
679
|
+
if (isOpen()) syncTarget(null, true);
|
|
507
680
|
}, SESSION_POLL_MS);
|
|
508
681
|
}
|
|
509
682
|
|
|
@@ -582,7 +755,7 @@ window.__ModuleLoader__.load({
|
|
|
582
755
|
status.addEventListener("click", function () {
|
|
583
756
|
if (!state.paging) return;
|
|
584
757
|
cancelPageIn();
|
|
585
|
-
setStatus(L.
|
|
758
|
+
setStatus(L.incomplete, false);
|
|
586
759
|
});
|
|
587
760
|
|
|
588
761
|
bar.appendChild(input);
|
|
@@ -602,13 +775,14 @@ window.__ModuleLoader__.load({
|
|
|
602
775
|
document.body.appendChild(bar);
|
|
603
776
|
}
|
|
604
777
|
|
|
605
|
-
function open() {
|
|
778
|
+
function open(target) {
|
|
606
779
|
if (typeof document === "undefined" || document.body === null) return;
|
|
780
|
+
syncTarget(target || document.activeElement, false, true);
|
|
607
781
|
if (!state.bar) buildBar();
|
|
608
782
|
state.bar.style.display = "flex";
|
|
609
783
|
var selection = "";
|
|
610
784
|
try { selection = String(window.getSelection()).trim(); } catch (error) {}
|
|
611
|
-
if (selection && selection.length <= 200 && selection !== state.query) {
|
|
785
|
+
if (state.target && selection && selection.length <= 200 && selection !== state.query) {
|
|
612
786
|
state.input.value = selection;
|
|
613
787
|
state.query = selection;
|
|
614
788
|
}
|
|
@@ -625,6 +799,7 @@ window.__ModuleLoader__.load({
|
|
|
625
799
|
stopWatchingSession();
|
|
626
800
|
if (state.bar) state.bar.style.display = "none";
|
|
627
801
|
state.query = "";
|
|
802
|
+
if (state.input) state.input.value = "";
|
|
628
803
|
state.ranges = [];
|
|
629
804
|
state.index = -1;
|
|
630
805
|
setStatus("", false);
|
|
@@ -638,20 +813,57 @@ window.__ModuleLoader__.load({
|
|
|
638
813
|
}
|
|
639
814
|
|
|
640
815
|
function apply(ctx) {
|
|
816
|
+
var react = require("react");
|
|
641
817
|
state.ctx = ctx;
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
818
|
+
state.adapter = createHostAdapter(ctx, function () {
|
|
819
|
+
syncTarget(null, true);
|
|
820
|
+
});
|
|
821
|
+
function SessionFind(props) {
|
|
822
|
+
var anchor = react.useRef(null);
|
|
823
|
+
react.useEffect(function () {
|
|
824
|
+
if (!anchor.current || !props.sessionId) return;
|
|
825
|
+
return state.adapter.mount(anchor.current, props.sessionId);
|
|
826
|
+
}, [props.sessionId]);
|
|
827
|
+
return react.createElement("button", {
|
|
828
|
+
ref: anchor, type: "button", title: L.find, "aria-label": L.find,
|
|
829
|
+
"data-find-all-session": props.sessionId,
|
|
830
|
+
onClick: function (event) { open(event.currentTarget); }
|
|
831
|
+
}, "⌕");
|
|
832
|
+
}
|
|
833
|
+
ctx.slots.inject("conversation.session.header.utilities", function () {
|
|
834
|
+
return ctx.slots.register({ name: "conversation.session.header.utilities", id: "dsh-find-all" }, SessionFind);
|
|
835
|
+
});
|
|
836
|
+
ctx.effect(function () {
|
|
837
|
+
injectCss();
|
|
838
|
+
var handler = createHandlers({
|
|
839
|
+
open: open, close: close, goTo: goTo, isOpen: isOpen,
|
|
840
|
+
isBarInput: function (target) { return target === state.input; }
|
|
841
|
+
});
|
|
842
|
+
function focus(event) {
|
|
843
|
+
if (!state.bar || !state.bar.contains(event.target)) syncTarget(event.target);
|
|
844
|
+
}
|
|
845
|
+
window.addEventListener("keydown", handler, true);
|
|
846
|
+
window.addEventListener("focusin", focus, true);
|
|
847
|
+
window.addEventListener("pointerdown", focus, true);
|
|
848
|
+
return function () {
|
|
849
|
+
window.removeEventListener("keydown", handler, true);
|
|
850
|
+
window.removeEventListener("focusin", focus, true);
|
|
851
|
+
window.removeEventListener("pointerdown", focus, true);
|
|
852
|
+
close();
|
|
853
|
+
if (state.bar) state.bar.remove();
|
|
854
|
+
var style = document.querySelector('style[data-plugin-css="dsh-find-all/bar.css"]');
|
|
855
|
+
if (style) style.remove();
|
|
856
|
+
state.bar = state.input = state.count = state.status = state.scopeBtn = null;
|
|
857
|
+
state.adapter.clear();
|
|
858
|
+
state.target = state.ctx = null;
|
|
859
|
+
};
|
|
860
|
+
}, "find-all: browser lifecycle");
|
|
651
861
|
}
|
|
652
862
|
|
|
653
863
|
exports.apply = apply;
|
|
654
|
-
exports.inject = ["sessions"];
|
|
864
|
+
exports.inject = ["sessions", "slots"];
|
|
865
|
+
exports.resolveScope = resolveScope;
|
|
866
|
+
exports.createHostAdapter = createHostAdapter;
|
|
655
867
|
exports.findIndices = findIndices; // for tests
|
|
656
868
|
exports.createHandlers = createHandlers; // for tests
|
|
657
869
|
exports.runPageIn = runPageIn; // for tests
|
package/package.json
CHANGED
|
@@ -1,76 +1,83 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@ryuu-64/dsh-find-all",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Ctrl+F find bar for the DSH desktop app: it searches the whole conversation, not just the messages already on screen.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"main": "lib/index.js",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": "./lib/index.js",
|
|
10
|
-
"./client": "./lib/client.js",
|
|
11
|
-
"./package.json": "./package.json"
|
|
12
|
-
},
|
|
13
|
-
"files": [
|
|
14
|
-
"lib",
|
|
15
|
-
"cordis.patch.yml",
|
|
16
|
-
"README.md",
|
|
17
|
-
"CHANGELOG.md",
|
|
18
|
-
"NOTICE.md",
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"@deepseek-ai/dsh-
|
|
40
|
-
"@deepseek-ai/dsh-client-ui-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"
|
|
45
|
-
"check": "node
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
"dsh
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
"@deepseek-ai/dsh-
|
|
66
|
-
"@deepseek-ai/dsh-client-ui-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@ryuu-64/dsh-find-all",
|
|
3
|
+
"version": "0.1.5",
|
|
4
|
+
"description": "Ctrl+F find bar for the DSH desktop app: it searches the whole conversation, not just the messages already on screen.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "lib/index.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./lib/index.js",
|
|
10
|
+
"./client": "./lib/client.js",
|
|
11
|
+
"./package.json": "./package.json"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"lib",
|
|
15
|
+
"cordis.patch.yml",
|
|
16
|
+
"README.md",
|
|
17
|
+
"CHANGELOG.md",
|
|
18
|
+
"NOTICE.md",
|
|
19
|
+
"docs/compatibility.md",
|
|
20
|
+
"LICENSE"
|
|
21
|
+
],
|
|
22
|
+
"author": "Ryuu-64",
|
|
23
|
+
"contributors": [
|
|
24
|
+
"secyborg (upstream dsh-find-bar, MIT)"
|
|
25
|
+
],
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/Ryuu-64/dsh-find-all.git"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/Ryuu-64/dsh-find-all#readme",
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/Ryuu-64/dsh-find-all/issues"
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public",
|
|
36
|
+
"registry": "https://registry.npmjs.org/"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"@deepseek-ai/dsh-api-session-controller": "0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2",
|
|
40
|
+
"@deepseek-ai/dsh-client-ui-chat": "0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2",
|
|
41
|
+
"@deepseek-ai/dsh-client-ui-conversation": "0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"test": "node --test \"test/**/*.test.mjs\"",
|
|
45
|
+
"check:registration": "node scripts/check-client-registration.mjs",
|
|
46
|
+
"check": "node --check lib/index.js && node --check lib/client.js && npm run check:registration && node --test \"test/**/*.test.mjs\"",
|
|
47
|
+
"verify:release": "node scripts/verify-release.mjs",
|
|
48
|
+
"install:local": "node scripts/install-local.mjs",
|
|
49
|
+
"deploy:profile": "node scripts/deploy-profile.mjs"
|
|
50
|
+
},
|
|
51
|
+
"keywords": [
|
|
52
|
+
"dsh",
|
|
53
|
+
"dsh-plugin",
|
|
54
|
+
"deepseek-harness",
|
|
55
|
+
"find",
|
|
56
|
+
"search",
|
|
57
|
+
"conversation"
|
|
58
|
+
],
|
|
59
|
+
"dsh": {
|
|
60
|
+
"bundle": {
|
|
61
|
+
"patch": "./cordis.patch.yml"
|
|
62
|
+
},
|
|
63
|
+
"client": {
|
|
64
|
+
"inject": [
|
|
65
|
+
"@deepseek-ai/dsh-api-session-controller",
|
|
66
|
+
"@deepseek-ai/dsh-client-ui-conversation",
|
|
67
|
+
"@deepseek-ai/dsh-client-ui-chat"
|
|
68
|
+
],
|
|
69
|
+
"platform": "web"
|
|
70
|
+
},
|
|
71
|
+
"compatibility": {
|
|
72
|
+
"dshReleases": {
|
|
73
|
+
"0.1.5-rc.2": "compatible"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"devDependencies": {
|
|
78
|
+
"jsdom": "27.4.0",
|
|
79
|
+
"playwright": "1.56.1",
|
|
80
|
+
"react": "18.3.1",
|
|
81
|
+
"react-dom": "18.3.1"
|
|
82
|
+
}
|
|
83
|
+
}
|