dsh-vibegap 0.1.0 → 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.
Files changed (3) hide show
  1. package/README.md +24 -5
  2. package/lib/client.js +29 -7
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -3,18 +3,36 @@
3
3
  VibeGap 的 DeepSeek Harness 原生插件。Agent 持续运行 18 秒后,dsh web
4
4
  右下角会出现拼写单词卡;会话完成或等待确认时,完成当前单词后自动收起或继续。
5
5
  插件可独立运行,不要求安装 VibeGap Python 桌面端。
6
- 如果本机 VibeGap daemon 正在运行,插件会自动改用桌面端的当前词书和进度游标;
7
- 连接失败或运行中断线时,会回退到浏览器内的独立进度。
6
+ 如果本机 VibeGap Core 正在运行,插件会自动改用桌面端的当前词书和进度游标;
7
+ Core 在页面打开后才由另一个 Agent 拉起也能自动接入。连接失败或运行中断线时,
8
+ 会回退到浏览器内的独立进度,远端游标会同步回本地作为断线续用位置。
8
9
 
9
10
  ## 安装
10
11
 
11
- 本地开发安装:
12
+ 从 npm 安装:
13
+
14
+ ```bash
15
+ dsh plugin --profile web add dsh-vibegap
16
+ dsh web
17
+ ```
18
+
19
+ 也可以从发布镜像仓库
20
+ [`dsh-vibegap`](https://github.com/ktao732084-arch/dsh-vibegap) 安装:
21
+
22
+ ```bash
23
+ dsh plugin --profile web add "github:ktao732084-arch/dsh-vibegap#main"
24
+ dsh web
25
+ ```
26
+
27
+ 本地开发使用 link 安装:
12
28
 
13
29
  ```bash
14
30
  dsh plugin --profile web add link:<仓库绝对路径>/vibegap/adapters/dsh/plugin
15
31
  dsh web
16
32
  ```
17
33
 
34
+ > 本目录是开发主场;发布走 dsh-vibegap 镜像仓库(改动合入后同步拷贝过去打 tag)。
35
+
18
36
  卸载:
19
37
 
20
38
  ```bash
@@ -25,8 +43,9 @@ dsh plugin --profile web remove dsh-vibegap
25
43
  自动发音偏好通过 dsh 官方 snapshot store 保存在当前浏览器中。无痕模式或浏览器
26
44
  拒绝持久化时,卡片仍可使用,但刷新后进度可能丢失。
27
45
 
28
- 本机 daemon 可用时,单词和进度改由 `http://127.0.0.1:8765/panel/*` 提供,
29
- 仅允许来自 `localhost` 或 `127.0.0.1` 的 HTTP(S) 浏览器 Origin。
46
+ 本机 Core 可用时,单词和进度改由 `http://127.0.0.1:8765/panel/*` 提供,
47
+ 插件每 5 秒低频探测一次,且仅允许来自 `localhost` 或 `127.0.0.1` 的
48
+ HTTP(S) 浏览器 Origin。DSH 单独使用时不会因此启动 Python/Core 进程。
30
49
 
31
50
  ## 使用
32
51
 
package/lib/client.js CHANGED
@@ -18,6 +18,7 @@ window.__ModuleLoader__.load({
18
18
  var TRANSLATION_MAX_CHARS = 100;
19
19
  var DAEMON_URL = "http://127.0.0.1:8765/panel";
20
20
  var DAEMON_TIMEOUT_MS = 1000;
21
+ var DAEMON_PROBE_MS = 5000;
21
22
  var DICT_URL = "https://raw.githubusercontent.com/RealKai42/qwerty-learner/master/public/dicts/CET6_T.json";
22
23
  var STYLE_ID = "vg-card-styles";
23
24
  var DONE_NOTICE = "会话已完成 · 拼完当前词后收起";
@@ -180,19 +181,39 @@ window.__ModuleLoader__.load({
180
181
  if (payload.error) throw new Error(payload.error);
181
182
  return payload;
182
183
  }
184
+ function syncLocalProgress(remoteProgress) {
185
+ if (!remoteProgress || !Number.isFinite(Number(remoteProgress.cursor))) return;
186
+ progressStore.update(function (draft) {
187
+ if (!Array.isArray(draft.words) || draft.words.length === 0) return;
188
+ if (Number(remoteProgress.total) !== draft.words.length) return;
189
+ draft.cursor = Math.min(Math.max(Number(remoteProgress.cursor), 0), draft.words.length - 1);
190
+ });
191
+ }
183
192
  function useDaemonBackend() {
184
193
  var backend = React.useState({ mode: "probing", word: null });
194
+ var current = useCell({ mode: "probing", word: null });
195
+ var setBackend = function (next) { current.current = next; backend[1](next); };
185
196
  React.useEffect(function () {
186
197
  var active = true;
187
- (async function () {
198
+ var timer = null;
199
+ async function probe() {
188
200
  try {
189
201
  var state = await panelRequest("/state");
190
202
  if (!state.ready) throw new Error("daemon has no wordbook");
191
- var word = await panelRequest("/next-word");
192
- if (active) backend[1]({ mode: "daemon", word: word });
193
- } catch (_) { if (active) backend[1]({ mode: "local", word: null }); }
194
- })();
195
- return function () { active = false; };
203
+ syncLocalProgress(state.progress);
204
+ var cursor = state.progress && Number(state.progress.cursor);
205
+ var known = current.current.word && Number(current.current.word.position);
206
+ if (current.current.mode !== "daemon" || cursor !== known) {
207
+ var word = await panelRequest("/next-word");
208
+ if (active) setBackend({ mode: "daemon", word: word });
209
+ }
210
+ } catch (_) {
211
+ if (active && current.current.mode !== "local") setBackend({ mode: "local", word: null });
212
+ }
213
+ if (active) timer = setTimeout(probe, DAEMON_PROBE_MS);
214
+ }
215
+ probe();
216
+ return function () { active = false; if (timer) clearTimeout(timer); };
196
217
  }, []);
197
218
  return backend;
198
219
  }
@@ -398,13 +419,14 @@ window.__ModuleLoader__.load({
398
419
  advanceProgress(model.selection.ordered.length); return;
399
420
  }
400
421
  try {
401
- await panelRequest("/commit", {
422
+ var result = await panelRequest("/commit", {
402
423
  method: "POST", headers: { "Content-Type": "application/json" },
403
424
  body: JSON.stringify({
404
425
  result: model.states.peeked[0] ? "fail" : "pass",
405
426
  typo_count: model.states.typos[0],
406
427
  }),
407
428
  });
429
+ syncLocalProgress(result);
408
430
  var word = await panelRequest("/next-word");
409
431
  model.backend[1]({ mode: "daemon", word: word });
410
432
  } catch (_) { model.backend[1]({ mode: "local", word: null }); }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-vibegap",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Self-contained vocabulary flashcards for DeepSeek Harness web sessions",
5
5
  "main": "lib/index.js",
6
6
  "exports": {