dsh-input-traffic 0.7.0 → 0.7.4
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 +31 -10
- package/README.en.md +1 -1
- package/README.ja.md +1 -1
- package/README.ko.md +1 -1
- package/README.md +1 -1
- package/dsh.plugin.json +2 -2
- package/lib/client.js +315 -43
- package/lib/client.js.map +1 -1
- package/lib/index.js +195 -3
- package/lib/types/bridge.d.ts +45 -0
- package/lib/types/bridge.d.ts.map +1 -0
- package/lib/types/bridge.js +130 -0
- package/lib/types/bridge.js.map +1 -0
- package/lib/types/client/bridge-scope.d.ts +38 -0
- package/lib/types/client/bridge-scope.d.ts.map +1 -0
- package/lib/types/client/bridge-scope.js +81 -0
- package/lib/types/client/bridge-scope.js.map +1 -0
- package/lib/types/client/compat.d.ts.map +1 -1
- package/lib/types/client/compat.js +18 -8
- package/lib/types/client/compat.js.map +1 -1
- package/lib/types/client/family-card.d.ts +2 -1
- package/lib/types/client/family-card.d.ts.map +1 -1
- package/lib/types/client/family-card.js +12 -2
- package/lib/types/client/family-card.js.map +1 -1
- package/lib/types/client/family-host.d.ts +51 -0
- package/lib/types/client/family-host.d.ts.map +1 -0
- package/lib/types/client/family-host.js +147 -0
- package/lib/types/client/family-host.js.map +1 -0
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +50 -11
- package/lib/types/client/index.js.map +1 -1
- package/lib/types/index.d.ts +16 -5
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +25 -2
- package/lib/types/index.js.map +1 -1
- package/package.json +20 -18
package/CHANGELOG.md
CHANGED
|
@@ -2,16 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
所有重要变更与 bug 修复记录于此。版本遵循语义化版本(`dsh plugin --profile web add dsh-input-traffic` 安装)。
|
|
4
4
|
|
|
5
|
-
## 0.
|
|
6
|
-
|
|
7
|
-
###
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
|
|
5
|
+
## 0.7.3 — 2026-10-08
|
|
6
|
+
|
|
7
|
+
### 新增:settings 数据桥(T20-b,perm-gate 5260fe6 泛化形移植)
|
|
8
|
+
|
|
9
|
+
- 老线(≤0.1.5)上 client settings 句柄永不 resolve(settingsScope 死路),家族卡
|
|
10
|
+
降级「设置服务不可用」、busyEnter 钉值静默失效。node 半区从 no-op 转正:loader
|
|
11
|
+
`inject=['webServer']` + `settings` 子注入挂活引用,暴露
|
|
12
|
+
`/api/ui-conversation/settings/{describe,mutate}`;client 半区 BridgeDocHandle
|
|
13
|
+
双轨(原生句柄优先、桥兜底),钉值在原生缺席时由桥首拉 ready 后补发一次。
|
|
14
|
+
前提实证:0.1.0 宿主 settings 服务全款具备 describe/mutate/redactSecrets 面,
|
|
15
|
+
最老线无需兜底。五格验收:四条桥线卡片真数据、0.1.7 原生无回归、桥写回落盘。
|
|
16
|
+
- 顺修 family-card 条件 hook(useSyncExternalStore 无条件调用;降级快照走模块级
|
|
17
|
+
常量)。
|
|
18
|
+
|
|
19
|
+
### 0.7.1 / 0.7.2 未单独发版(tgz 直铺五格),随本版一并进 npm
|
|
20
|
+
|
|
21
|
+
- 0.7.0:15rc 枚举单版本线(六 peer × 15 rc 全存在),latest + 四线 tag 收敛(已发)。
|
|
22
|
+
- 0.7.1:`dsh-family.tab` 注册提顶层 + scopeRef 活引用(≤0.1.5 家族节贡献卡缺席修复)。
|
|
23
|
+
- 0.7.2:settings waist 回调按 cordis 契约从 scope 对象按名读服务(裸服务调用 =
|
|
24
|
+
每代 TypeError、纤维暗死,busyEnter 钉值因此从未生效)。
|
|
25
|
+
|
|
26
|
+
## 0.5.5 — 2026-09-30
|
|
27
|
+
|
|
28
|
+
### 修复:队列行改读 inbox 投影(真实宿主渲染修复,实测 0.1.7-rc.2)
|
|
29
|
+
|
|
30
|
+
- 真实宿主的排队行不在会话快照上:组件改从 useProjection('inbox') 读取(next-turn=排队/绿档,next-step=插话/黄档),与官方 QueueDock 同源。此前读取的 s.queue 为类型镜像自造字段,行渲染必崩并回退官方队列。
|
|
31
|
+
- queueMutable 与官方判定对齐:continuable 子代理上下文同样可操作(此前整块操作按钮会在子代理会话中消失)。
|
|
32
|
+
- 移除真机上失效的「打回输入框」按钮(setDraft 为未文档化面);黄色档位是唯一插话入口。
|
|
33
|
+
- 行文本改由 content 块派生(textOf,对齐官方 textOf/previewOf),修复行预览空白。
|
|
34
|
+
- 三色档位圆点固定字面量配色:自定义皮肤(如 ui-skin-maid-atelier)重定义 warn 别名 token 会把黄点洗成不可见。
|
|
35
|
+
- CSS 压制官方 QueueDock 容器(仅崩溃回退态的可见性兜底);修复 verify-assembly 陈旧断言(settingsScope → configForms)。
|
|
15
36
|
## 0.5.4 — 2026-09-29
|
|
16
37
|
|
|
17
38
|
### 修复:0.1.7 线 devDeps 基线对齐 + dock 图标渲染崩溃
|
package/README.en.md
CHANGED
|
@@ -52,7 +52,7 @@ A cordis client plugin assembled via the `dsh plugin` command and a bundle patch
|
|
|
52
52
|
> **Suggested pairing for now**: use it together with a **reminder** plugin (e.g. [dsh-notify](https://github.com/zhengjy01/dsh-notify), desktop notifications when it is time to freeze/resume) and a **billing/usage** plugin (e.g. [dsh-deepseek-usage](https://github.com/yyb16yyb-hub/dsh-deepseek-usage), [dsh-cost-tracker](https://github.com/yflmq001/dsh-cost-tracker), [dsh-billing-balance](https://github.com/YZz-S/dsh-billing-balance), to verify actual spend around a freeze) — a "remind → freeze → resume off-peak → verify" saving loop.
|
|
53
53
|
|
|
54
54
|
<!-- host-compat:begin — generated by scripts/sync-hosts.mjs from scripts/hosts.mjs; DO NOT EDIT -->
|
|
55
|
-
- **Supported DSH hosts:** `0.1.0-rc.2` `0.1.0-rc.3` `0.1.0-rc.6` `0.1.0-rc.7` `0.1.0-rc.8` `0.1.1-rc.1` `0.1.1-rc.2` `0.1.2-rc.1` `0.1.5-rc.1` `0.1.5-rc.2` `0.1.5-rc.3` `0.1.7-rc.1` `0.1.7-rc.2` `0.2.0-rc.1` `0.2.0-rc.2` (
|
|
55
|
+
- **Supported DSH hosts:** `0.1.0-rc.2` `0.1.0-rc.3` `0.1.0-rc.6` `0.1.0-rc.7` `0.1.0-rc.8` `0.1.1-rc.1` `0.1.1-rc.2` `0.1.2-rc.1` `0.1.5-rc.1` `0.1.5-rc.2` `0.1.5-rc.3` `0.1.7-rc.1` `0.1.7-rc.2` `0.2.0-rc.1` `0.2.0-rc.2` `0.2.1-rc.1` (16 rc releases across lines 0.1.0 / 0.1.1 / 0.1.2 / 0.1.5 / 0.1.7 / 0.2.0 / 0.2.1)
|
|
56
56
|
- **Runtime-verified:** `0.1.0-rc.2`, `0.1.0-rc.3`, `0.1.0-rc.6`, `0.1.0-rc.7`, `0.1.0-rc.8`, `0.1.1-rc.1`, `0.1.1-rc.2`, `0.1.2-rc.1`, `0.1.5-rc.1`, `0.1.5-rc.2`, `0.1.5-rc.3`, `0.1.7-rc.1`, `0.1.7-rc.2`, `0.2.0-rc.1`, `0.2.0-rc.2` — evidence in `.compat-results/`
|
|
57
57
|
<!-- host-compat:end -->
|
|
58
58
|
|
package/README.ja.md
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
> **推奨セットアップ**:**リマインダー**プラグイン([dsh-notify](https://github.com/zhengjy01/dsh-notify) など、凍結/再開のタイミングをデスクトップ通知)と**課金統計**プラグイン([dsh-deepseek-usage](https://github.com/yyb16yyb-hub/dsh-deepseek-usage)、[dsh-cost-tracker](https://github.com/yflmq001/dsh-cost-tracker)、[dsh-billing-balance](https://github.com/YZz-S/dsh-billing-balance) など、凍結前後の実際の支出を確認)を組み合わせ、「リマインド → 凍結 → オフピーク再開 → 精算」の節約ループを実現します。
|
|
36
36
|
|
|
37
37
|
<!-- host-compat:begin — generated by scripts/sync-hosts.mjs from scripts/hosts.mjs; DO NOT EDIT -->
|
|
38
|
-
- **対応する DSH ホスト:** `0.1.0-rc.2` `0.1.0-rc.3` `0.1.0-rc.6` `0.1.0-rc.7` `0.1.0-rc.8` `0.1.1-rc.1` `0.1.1-rc.2` `0.1.2-rc.1` `0.1.5-rc.1` `0.1.5-rc.2` `0.1.5-rc.3` `0.1.7-rc.1` `0.1.7-rc.2` `0.2.0-rc.1` `0.2.0-rc.2`(0.1.0 / 0.1.1 / 0.1.2 / 0.1.5 / 0.1.7 / 0.2.0 の
|
|
38
|
+
- **対応する DSH ホスト:** `0.1.0-rc.2` `0.1.0-rc.3` `0.1.0-rc.6` `0.1.0-rc.7` `0.1.0-rc.8` `0.1.1-rc.1` `0.1.1-rc.2` `0.1.2-rc.1` `0.1.5-rc.1` `0.1.5-rc.2` `0.1.5-rc.3` `0.1.7-rc.1` `0.1.7-rc.2` `0.2.0-rc.1` `0.2.0-rc.2` `0.2.1-rc.1`(0.1.0 / 0.1.1 / 0.1.2 / 0.1.5 / 0.1.7 / 0.2.0 / 0.2.1 の 16 rc)
|
|
39
39
|
- **ランタイム検証済み:** `0.1.0-rc.2`, `0.1.0-rc.3`, `0.1.0-rc.6`, `0.1.0-rc.7`, `0.1.0-rc.8`, `0.1.1-rc.1`, `0.1.1-rc.2`, `0.1.2-rc.1`, `0.1.5-rc.1`, `0.1.5-rc.2`, `0.1.5-rc.3`, `0.1.7-rc.1`, `0.1.7-rc.2`, `0.2.0-rc.1`, `0.2.0-rc.2`(証跡は `.compat-results/`)
|
|
40
40
|
<!-- host-compat:end -->
|
|
41
41
|
|
package/README.ko.md
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
> **권장 구성**: **알림** 플러그인([dsh-notify](https://github.com/zhengjy01/dsh-notify), 동결/재개 시점 데스크톱 알림)과 **과금 통계** 플러그인([dsh-deepseek-usage](https://github.com/yyb16yyb-hub/dsh-deepseek-usage), [dsh-cost-tracker](https://github.com/yflmq001/dsh-cost-tracker), [dsh-billing-balance](https://github.com/YZz-S/dsh-billing-balance), 동결 전후 실제 지출 확인)을 함께 사용하여 "알림 → 동결 → 오피크 재개 → 정산" 절약 루ープ를 실현합니다.
|
|
36
36
|
|
|
37
37
|
<!-- host-compat:begin — generated by scripts/sync-hosts.mjs from scripts/hosts.mjs; DO NOT EDIT -->
|
|
38
|
-
- **지원하는 DSH 호스트:** `0.1.0-rc.2` `0.1.0-rc.3` `0.1.0-rc.6` `0.1.0-rc.7` `0.1.0-rc.8` `0.1.1-rc.1` `0.1.1-rc.2` `0.1.2-rc.1` `0.1.5-rc.1` `0.1.5-rc.2` `0.1.5-rc.3` `0.1.7-rc.1` `0.1.7-rc.2` `0.2.0-rc.1` `0.2.0-rc.2` (0.1.0 / 0.1.1 / 0.1.2 / 0.1.5 / 0.1.7 / 0.2.0 라인의
|
|
38
|
+
- **지원하는 DSH 호스트:** `0.1.0-rc.2` `0.1.0-rc.3` `0.1.0-rc.6` `0.1.0-rc.7` `0.1.0-rc.8` `0.1.1-rc.1` `0.1.1-rc.2` `0.1.2-rc.1` `0.1.5-rc.1` `0.1.5-rc.2` `0.1.5-rc.3` `0.1.7-rc.1` `0.1.7-rc.2` `0.2.0-rc.1` `0.2.0-rc.2` `0.2.1-rc.1` (0.1.0 / 0.1.1 / 0.1.2 / 0.1.5 / 0.1.7 / 0.2.0 / 0.2.1 라인의 16개 rc)
|
|
39
39
|
- **런타임 검증됨:** `0.1.0-rc.2`, `0.1.0-rc.3`, `0.1.0-rc.6`, `0.1.0-rc.7`, `0.1.0-rc.8`, `0.1.1-rc.1`, `0.1.1-rc.2`, `0.1.2-rc.1`, `0.1.5-rc.1`, `0.1.5-rc.2`, `0.1.5-rc.3`, `0.1.7-rc.1`, `0.1.7-rc.2`, `0.2.0-rc.1`, `0.2.0-rc.2` (증적은 `.compat-results/`)
|
|
40
40
|
<!-- host-compat:end -->
|
|
41
41
|
|
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
> **目前建议搭配**:配合**一般提醒**插件(如 [dsh-notify](https://github.com/zhengjy01/dsh-notify),到点桌面提醒「该冻结/该恢复」)与**计费统计**插件(如 [dsh-deepseek-usage](https://github.com/yyb16yyb-hub/dsh-deepseek-usage)、[dsh-cost-tracker](https://github.com/yflmq001/dsh-cost-tracker)、[dsh-billing-balance](https://github.com/YZz-S/dsh-billing-balance),核对冻结前后的实际花费),形成「提醒 → 冻结 → 错峰恢复 → 对账」的省钱闭环。
|
|
58
58
|
|
|
59
59
|
<!-- host-compat:begin — generated by scripts/sync-hosts.mjs from scripts/hosts.mjs; DO NOT EDIT -->
|
|
60
|
-
- **支持的 DSH 宿主:** `0.1.0-rc.2` `0.1.0-rc.3` `0.1.0-rc.6` `0.1.0-rc.7` `0.1.0-rc.8` `0.1.1-rc.1` `0.1.1-rc.2` `0.1.2-rc.1` `0.1.5-rc.1` `0.1.5-rc.2` `0.1.5-rc.3` `0.1.7-rc.1` `0.1.7-rc.2` `0.2.0-rc.1` `0.2.0-rc.2`(覆盖 0.1.0 / 0.1.1 / 0.1.2 / 0.1.5 / 0.1.7 / 0.2.0
|
|
60
|
+
- **支持的 DSH 宿主:** `0.1.0-rc.2` `0.1.0-rc.3` `0.1.0-rc.6` `0.1.0-rc.7` `0.1.0-rc.8` `0.1.1-rc.1` `0.1.1-rc.2` `0.1.2-rc.1` `0.1.5-rc.1` `0.1.5-rc.2` `0.1.5-rc.3` `0.1.7-rc.1` `0.1.7-rc.2` `0.2.0-rc.1` `0.2.0-rc.2` `0.2.1-rc.1`(覆盖 0.1.0 / 0.1.1 / 0.1.2 / 0.1.5 / 0.1.7 / 0.2.0 / 0.2.1 共 7 条线 16 个 rc)
|
|
61
61
|
- **运行时已验证:** `0.1.0-rc.2`, `0.1.0-rc.3`, `0.1.0-rc.6`, `0.1.0-rc.7`, `0.1.0-rc.8`, `0.1.1-rc.1`, `0.1.1-rc.2`, `0.1.2-rc.1`, `0.1.5-rc.1`, `0.1.5-rc.2`, `0.1.5-rc.3`, `0.1.7-rc.1`, `0.1.7-rc.2`, `0.2.0-rc.1`, `0.2.0-rc.2`(证据见 `.compat-results/`)
|
|
62
62
|
<!-- host-compat:end -->
|
|
63
63
|
|
package/dsh.plugin.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-external/dsh-input-traffic",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.4",
|
|
4
4
|
"main": "./lib/index.js",
|
|
5
5
|
"description": "Busy-time input queue: three-tier planning, drag-to-reorder, interject/interrupt, batch clear, session freeze.",
|
|
6
6
|
"engines": {
|
|
7
|
-
"dsh": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2"
|
|
7
|
+
"dsh": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1"
|
|
8
8
|
},
|
|
9
9
|
"contributes": {
|
|
10
10
|
"tools": [],
|
package/lib/client.js
CHANGED
|
@@ -600,42 +600,42 @@ window.__ModuleLoader__.load({
|
|
|
600
600
|
document.head.appendChild(tag);
|
|
601
601
|
}
|
|
602
602
|
var steer_queue_dock_module_css_default = {
|
|
603
|
-
"
|
|
604
|
-
"list": "FNESVa_list",
|
|
605
|
-
"toolbar": "FNESVa_toolbar",
|
|
603
|
+
"action": "FNESVa_action",
|
|
606
604
|
"actions": "FNESVa_actions",
|
|
605
|
+
"badge": "FNESVa_badge",
|
|
607
606
|
"badgeLabel": "FNESVa_badgeLabel",
|
|
608
|
-
"
|
|
607
|
+
"badgeLater": "FNESVa_badgeLater",
|
|
608
|
+
"badgeNext": "FNESVa_badgeNext",
|
|
609
|
+
"badgeNow": "FNESVa_badgeNow",
|
|
610
|
+
"chevron": "FNESVa_chevron",
|
|
611
|
+
"clear": "FNESVa_clear",
|
|
612
|
+
"clearCancel": "FNESVa_clearCancel",
|
|
613
|
+
"clearConfirm": "FNESVa_clearConfirm",
|
|
614
|
+
"clearLabel": "FNESVa_clearLabel",
|
|
609
615
|
"count": "FNESVa_count",
|
|
610
|
-
"
|
|
616
|
+
"dock": "FNESVa_dock",
|
|
617
|
+
"dot": "FNESVa_dot",
|
|
618
|
+
"editor": "FNESVa_editor",
|
|
619
|
+
"freeze": "FNESVa_freeze",
|
|
611
620
|
"frozenBanner": "FNESVa_frozenBanner",
|
|
612
|
-
"
|
|
613
|
-
"rowDragOver": "FNESVa_rowDragOver",
|
|
614
|
-
"steeringList": "FNESVa_steeringList",
|
|
615
|
-
"clear": "FNESVa_clear",
|
|
616
|
-
"action": "FNESVa_action",
|
|
617
|
-
"frozenRow": "FNESVa_frozenRow",
|
|
621
|
+
"frozenList": "FNESVa_frozenList",
|
|
618
622
|
"frozenMark": "FNESVa_frozenMark",
|
|
619
|
-
"
|
|
620
|
-
"
|
|
621
|
-
"freeze": "FNESVa_freeze",
|
|
623
|
+
"frozenRow": "FNESVa_frozenRow",
|
|
624
|
+
"header": "FNESVa_header",
|
|
622
625
|
"lead": "FNESVa_lead",
|
|
626
|
+
"list": "FNESVa_list",
|
|
627
|
+
"panel": "FNESVa_panel",
|
|
628
|
+
"plan": "FNESVa_plan",
|
|
623
629
|
"preview": "FNESVa_preview",
|
|
624
|
-
"badgeLater": "FNESVa_badgeLater",
|
|
625
|
-
"tierNow": "FNESVa_tierNow",
|
|
626
630
|
"row": "FNESVa_row",
|
|
627
|
-
"
|
|
628
|
-
"
|
|
629
|
-
"dot": "FNESVa_dot",
|
|
630
|
-
"tierLater": "FNESVa_tierLater",
|
|
631
|
-
"badgeNext": "FNESVa_badgeNext",
|
|
631
|
+
"rowDragOver": "FNESVa_rowDragOver",
|
|
632
|
+
"steeringList": "FNESVa_steeringList",
|
|
632
633
|
"tier": "FNESVa_tier",
|
|
634
|
+
"tierLater": "FNESVa_tierLater",
|
|
633
635
|
"tierNext": "FNESVa_tierNext",
|
|
634
|
-
"
|
|
635
|
-
"
|
|
636
|
-
"
|
|
637
|
-
"clearLabel": "FNESVa_clearLabel",
|
|
638
|
-
"clearConfirm": "FNESVa_clearConfirm"
|
|
636
|
+
"tierNow": "FNESVa_tierNow",
|
|
637
|
+
"toolbar": "FNESVa_toolbar",
|
|
638
|
+
"toolbarActions": "FNESVa_toolbarActions"
|
|
639
639
|
};
|
|
640
640
|
//#endregion
|
|
641
641
|
//#region src/client/steer-queue-dock.tsx
|
|
@@ -1722,9 +1722,23 @@ window.__ModuleLoader__.load({
|
|
|
1722
1722
|
fontSize: 12,
|
|
1723
1723
|
lineHeight: 1.5
|
|
1724
1724
|
};
|
|
1725
|
+
/** 无 scope 时的降级快照(模块级常量:useSyncExternalStore 要求稳定引用)。 */
|
|
1726
|
+
const DEGRADED_SNAPSHOT = {
|
|
1727
|
+
status: "unavailable",
|
|
1728
|
+
value: void 0,
|
|
1729
|
+
writable: false
|
|
1730
|
+
};
|
|
1725
1731
|
function InputTrafficFamilyCard({ scope, t }) {
|
|
1726
|
-
const
|
|
1732
|
+
const snapshot = (0, react.useSyncExternalStore)((listener) => scope?.subscribe(listener) ?? (() => {}), () => scope?.getSnapshot() ?? DEGRADED_SNAPSHOT);
|
|
1727
1733
|
const tr = (key) => t ? t(key) : FALLBACK_ZH[key] ?? key;
|
|
1734
|
+
if (!scope) return (0, react.createElement)("div", { style: {
|
|
1735
|
+
display: "grid",
|
|
1736
|
+
gap: 6
|
|
1737
|
+
} }, (0, react.createElement)("p", { style: {
|
|
1738
|
+
...dim,
|
|
1739
|
+
margin: 0
|
|
1740
|
+
} }, `${tr("family.desc")}(当前宿主设置服务不可用 — 只读)`));
|
|
1741
|
+
const pinned = snapshot.value?.busyEnter ?? "queue";
|
|
1728
1742
|
return (0, react.createElement)("div", { style: {
|
|
1729
1743
|
display: "grid",
|
|
1730
1744
|
gap: 6
|
|
@@ -1745,6 +1759,107 @@ window.__ModuleLoader__.load({
|
|
|
1745
1759
|
return null;
|
|
1746
1760
|
}
|
|
1747
1761
|
//#endregion
|
|
1762
|
+
//#region src/client/bridge-scope.ts
|
|
1763
|
+
/** Must match src/bridge.ts bridgeRoutesFor character for character. */
|
|
1764
|
+
function bridgeRoutesFor(ns) {
|
|
1765
|
+
return {
|
|
1766
|
+
describe: `/api/${ns}/settings/describe`,
|
|
1767
|
+
mutate: `/api/${ns}/settings/mutate`
|
|
1768
|
+
};
|
|
1769
|
+
}
|
|
1770
|
+
var BridgeDocHandle = class {
|
|
1771
|
+
snapshot = {
|
|
1772
|
+
status: "pending",
|
|
1773
|
+
value: void 0,
|
|
1774
|
+
writable: true
|
|
1775
|
+
};
|
|
1776
|
+
listeners = /* @__PURE__ */ new Set();
|
|
1777
|
+
revision;
|
|
1778
|
+
inflight;
|
|
1779
|
+
routes;
|
|
1780
|
+
constructor(ns) {
|
|
1781
|
+
this.routes = bridgeRoutesFor(ns);
|
|
1782
|
+
this.refresh();
|
|
1783
|
+
}
|
|
1784
|
+
/** The bridge transport is shape-agnostic; the ns payload narrows at this boundary. */
|
|
1785
|
+
getSnapshot() {
|
|
1786
|
+
return this.snapshot;
|
|
1787
|
+
}
|
|
1788
|
+
subscribe(listener) {
|
|
1789
|
+
this.listeners.add(listener);
|
|
1790
|
+
return () => {
|
|
1791
|
+
this.listeners.delete(listener);
|
|
1792
|
+
};
|
|
1793
|
+
}
|
|
1794
|
+
async set(field, value) {
|
|
1795
|
+
await this.mutate([{
|
|
1796
|
+
op: "set",
|
|
1797
|
+
path: [field],
|
|
1798
|
+
value
|
|
1799
|
+
}]);
|
|
1800
|
+
}
|
|
1801
|
+
notify() {
|
|
1802
|
+
for (const l of [...this.listeners]) l();
|
|
1803
|
+
}
|
|
1804
|
+
async request(path, body) {
|
|
1805
|
+
const response = await fetch(path, {
|
|
1806
|
+
method: "POST",
|
|
1807
|
+
headers: { "content-type": "application/json" },
|
|
1808
|
+
body: JSON.stringify(body ?? {})
|
|
1809
|
+
});
|
|
1810
|
+
if (!response.ok) throw new Error(`bridge ${path} HTTP ${response.status}`);
|
|
1811
|
+
return await response.json();
|
|
1812
|
+
}
|
|
1813
|
+
async refresh() {
|
|
1814
|
+
if (this.inflight !== void 0) return this.inflight;
|
|
1815
|
+
this.inflight = (async () => {
|
|
1816
|
+
try {
|
|
1817
|
+
const body = await this.request(this.routes.describe);
|
|
1818
|
+
if (body.ok && body.value?.descriptor !== void 0) {
|
|
1819
|
+
const d = body.value.descriptor;
|
|
1820
|
+
this.revision = typeof d.revision === "number" ? d.revision : void 0;
|
|
1821
|
+
this.snapshot = {
|
|
1822
|
+
status: "ready",
|
|
1823
|
+
value: d.value,
|
|
1824
|
+
writable: body.value.writable !== false
|
|
1825
|
+
};
|
|
1826
|
+
} else this.snapshot = {
|
|
1827
|
+
status: "error",
|
|
1828
|
+
value: void 0,
|
|
1829
|
+
writable: false
|
|
1830
|
+
};
|
|
1831
|
+
} catch {
|
|
1832
|
+
this.snapshot = {
|
|
1833
|
+
status: "error",
|
|
1834
|
+
value: void 0,
|
|
1835
|
+
writable: false
|
|
1836
|
+
};
|
|
1837
|
+
} finally {
|
|
1838
|
+
this.inflight = void 0;
|
|
1839
|
+
}
|
|
1840
|
+
this.notify();
|
|
1841
|
+
})();
|
|
1842
|
+
return this.inflight;
|
|
1843
|
+
}
|
|
1844
|
+
async mutate(ops) {
|
|
1845
|
+
const body = await this.request(this.routes.mutate, {
|
|
1846
|
+
ops,
|
|
1847
|
+
expectedRevision: this.revision
|
|
1848
|
+
});
|
|
1849
|
+
if (body.ok && body.value?.descriptor !== void 0) {
|
|
1850
|
+
const d = body.value.descriptor;
|
|
1851
|
+
this.revision = typeof d.revision === "number" ? d.revision : void 0;
|
|
1852
|
+
this.snapshot = {
|
|
1853
|
+
status: "ready",
|
|
1854
|
+
value: d.value,
|
|
1855
|
+
writable: this.snapshot.writable
|
|
1856
|
+
};
|
|
1857
|
+
} else if (body.code === "settings-conflict") await this.refresh();
|
|
1858
|
+
else throw new Error(`bridge mutate failed: ${body.code ?? "unknown"} ${body.message ?? ""}`);
|
|
1859
|
+
this.notify();
|
|
1860
|
+
}
|
|
1861
|
+
};
|
|
1862
|
+
//#endregion
|
|
1748
1863
|
//#region src/client/compat.ts
|
|
1749
1864
|
/**
|
|
1750
1865
|
* Pin the busy-Enter field through whichever durable-settings service this
|
|
@@ -1755,15 +1870,20 @@ window.__ModuleLoader__.load({
|
|
|
1755
1870
|
* as the family tab's slot-inject when the family holder is absent).
|
|
1756
1871
|
*/
|
|
1757
1872
|
function registerSettingsFaces(ctx, deps) {
|
|
1758
|
-
ctx.inject
|
|
1759
|
-
|
|
1760
|
-
scope.
|
|
1761
|
-
|
|
1873
|
+
const scopeInject = ctx.inject;
|
|
1874
|
+
scopeInject(["configForms"], (scope) => {
|
|
1875
|
+
const forms = scope.configForms;
|
|
1876
|
+
if (forms === void 0) return;
|
|
1877
|
+
const handle = forms.get(deps.namespace);
|
|
1878
|
+
handle.set(deps.field, deps.value);
|
|
1879
|
+
deps.onScope(handle);
|
|
1762
1880
|
});
|
|
1763
|
-
|
|
1764
|
-
const
|
|
1765
|
-
|
|
1766
|
-
deps.
|
|
1881
|
+
scopeInject(["settingsScope"], (scope) => {
|
|
1882
|
+
const settingsScope = scope.settingsScope;
|
|
1883
|
+
if (settingsScope === void 0) return;
|
|
1884
|
+
const handle = settingsScope.bind({ namespace: deps.namespace });
|
|
1885
|
+
handle.set(deps.field, deps.value);
|
|
1886
|
+
deps.onScope(handle);
|
|
1767
1887
|
});
|
|
1768
1888
|
}
|
|
1769
1889
|
/**
|
|
@@ -1779,6 +1899,139 @@ window.__ModuleLoader__.load({
|
|
|
1779
1899
|
};
|
|
1780
1900
|
}
|
|
1781
1901
|
//#endregion
|
|
1902
|
+
//#region src/client/family-host.ts
|
|
1903
|
+
/**
|
|
1904
|
+
* 家族容器兜底接管(family host backstop)。
|
|
1905
|
+
*
|
|
1906
|
+
* 为什么需要:本插件把设置卡挂在 `dsh-family.tab` 上,而那个子席位由**别的插件**
|
|
1907
|
+
* (thinking-levels / session-guard / session-steward / dsh-mcp-registry)声明。
|
|
1908
|
+
* 宿主的规则是「注册未声明的槽直接抛」,且声明全局唯一、先到先得——所以只要那几家
|
|
1909
|
+
* 全部让位或声明失败,本插件的设置面就整轮启动不可见,而且**不报任何错**
|
|
1910
|
+
* (inject 工厂在槽未声明时压根不跑)。
|
|
1911
|
+
*
|
|
1912
|
+
* 本模块把"看不见"变成"自己上位":延迟到分级时刻检查**自己的 tab 工厂有没有被跑过**
|
|
1913
|
+
* ——那是插件侧唯一合法的「有人声明了吗」探针(宿主只在声明成立后才执行工厂)。
|
|
1914
|
+
* 没跑过 → 自己注册一个通用容器,声明子席位,按账本渲染所有贡献卡(含自己的)。
|
|
1915
|
+
*
|
|
1916
|
+
* declare-or-withdraw:声明子席位失败(别人已持有)时**绝不**退回不带 children 的
|
|
1917
|
+
* 裸注册——残废容器会占住导航行、让后来者按「已有 dsh-family」集体退场,
|
|
1918
|
+
* 这正是 2026-10-14 家族面板丢失的成因。
|
|
1919
|
+
*/
|
|
1920
|
+
/** 家族节固定 id 与子席位 key(与 thinking-levels 的声明严格一致)。 */
|
|
1921
|
+
const FAMILY_SECTION_ID = "dsh-family";
|
|
1922
|
+
const FAMILY_CHILD_KEY = "dsh-family.tab";
|
|
1923
|
+
/** 派生而非硬编码:改 rank 表就自动改时刻。 */
|
|
1924
|
+
const FAMILY_CLAIM_DELAY_MS = 1100;
|
|
1925
|
+
/** 接管行的遮蔽优先级:必须大于任何正牌 owner(TL 无=0、guard=10),
|
|
1926
|
+
* 这样 owner 一旦上位就自然遮蔽本兜底行(宿主规则:同 cell 最低者渲染)。 */
|
|
1927
|
+
const FAMILY_HOST_PRIORITY = 50;
|
|
1928
|
+
const NO_TABS = [];
|
|
1929
|
+
/** 通用容器渲染:按 order 依次画出账本里每张贡献卡,不含任何"本插件特例"。 */
|
|
1930
|
+
function FamilyHostSection(props) {
|
|
1931
|
+
const raw = props.hooks?.tabs;
|
|
1932
|
+
const [polled, setPolled] = (0, react.useState)(() => raw?.getSnapshot() ?? NO_TABS);
|
|
1933
|
+
(0, react.useEffect)(() => {
|
|
1934
|
+
if (props.useTabs !== void 0 || raw === void 0) return;
|
|
1935
|
+
return raw.subscribe(() => setPolled(raw.getSnapshot()));
|
|
1936
|
+
});
|
|
1937
|
+
const tabs = props.useTabs !== void 0 ? props.useTabs((value) => value) : polled;
|
|
1938
|
+
return (0, react.createElement)("div", { style: {
|
|
1939
|
+
display: "grid",
|
|
1940
|
+
gap: "12px"
|
|
1941
|
+
} }, tabs.map((row) => (0, react.createElement)("div", { key: row.id }, props.renderSlot ? props.renderSlot(FAMILY_CHILD_KEY, {}, {
|
|
1942
|
+
only: row.id,
|
|
1943
|
+
fallback: null
|
|
1944
|
+
}) : props.renderContributor?.(row.id) ?? null)));
|
|
1945
|
+
}
|
|
1946
|
+
/** 取条目上的具名面(宿主条目是结构化对象,这里只做安全读取)。 */
|
|
1947
|
+
function face(entry, key) {
|
|
1948
|
+
if (entry === null || typeof entry !== "object") return void 0;
|
|
1949
|
+
return entry[key];
|
|
1950
|
+
}
|
|
1951
|
+
/** 条目的 options 收窄(宿主侧是结构化对象,缺失时当空对象读)。 */
|
|
1952
|
+
function optionsOf(entry) {
|
|
1953
|
+
const raw = face(entry, "options");
|
|
1954
|
+
return raw !== null && typeof raw === "object" ? raw : {};
|
|
1955
|
+
}
|
|
1956
|
+
/** 把账本条目投影成标题行:label 可能是函数,抛错退化成 id,绝不带崩整节。 */
|
|
1957
|
+
function projectLedger(slots) {
|
|
1958
|
+
return slots.entries(FAMILY_CHILD_KEY).map((rawEntry) => {
|
|
1959
|
+
const options = rawEntry.options ?? {};
|
|
1960
|
+
const id = String(options["id"] ?? "");
|
|
1961
|
+
const rawLabel = options["label"];
|
|
1962
|
+
let label = id;
|
|
1963
|
+
if (typeof rawLabel === "function") try {
|
|
1964
|
+
label = String(rawLabel() ?? id);
|
|
1965
|
+
} catch {
|
|
1966
|
+
label = id;
|
|
1967
|
+
}
|
|
1968
|
+
else if (typeof rawLabel === "string") label = rawLabel;
|
|
1969
|
+
return {
|
|
1970
|
+
id,
|
|
1971
|
+
order: Number(options["order"] ?? 0),
|
|
1972
|
+
label
|
|
1973
|
+
};
|
|
1974
|
+
}).sort((a, b) => a.order - b.order);
|
|
1975
|
+
}
|
|
1976
|
+
/**
|
|
1977
|
+
* 若到点仍无人声明子席位,则自己接管容器。
|
|
1978
|
+
* @param slots - 客户端槽门面。
|
|
1979
|
+
* @param isHeld - 探针:本插件的 tab 工厂是否已被执行(= 有人声明了子席位)。
|
|
1980
|
+
* @param label - 导航标题;家族共享节的标题,不是本插件名。
|
|
1981
|
+
* @param log - console 前缀(带插件名)。
|
|
1982
|
+
* @returns 取消定时器与撤销接管注册的 disposer。
|
|
1983
|
+
*/
|
|
1984
|
+
function claimFamilyHostIfOrphaned(slots, isHeld, label, log) {
|
|
1985
|
+
let disposeClaim;
|
|
1986
|
+
let claimed = false;
|
|
1987
|
+
const timer = setTimeout(() => {
|
|
1988
|
+
if (isHeld()) return;
|
|
1989
|
+
const ledger = {
|
|
1990
|
+
getSnapshot: () => projectLedger(slots),
|
|
1991
|
+
subscribe: (listener) => slots.subscribe ? slots.subscribe(FAMILY_CHILD_KEY, listener) : () => void 0
|
|
1992
|
+
};
|
|
1993
|
+
/** ≤0.1.5 壳没有 renderSlot:从账本条目现挂组件,失败一律 null。 */
|
|
1994
|
+
const renderContributor = (id) => {
|
|
1995
|
+
try {
|
|
1996
|
+
const entry = slots.entries(FAMILY_CHILD_KEY).find((candidate) => String(optionsOf(candidate)["id"] ?? "") === id);
|
|
1997
|
+
const component = entry ? face(entry, "component") : void 0;
|
|
1998
|
+
const inject = entry ? face(entry, "inject") : void 0;
|
|
1999
|
+
if (typeof component !== "function") return null;
|
|
2000
|
+
const injected = typeof inject === "function" ? inject() : {};
|
|
2001
|
+
return (0, react.createElement)(component, injected ?? {});
|
|
2002
|
+
} catch {
|
|
2003
|
+
return null;
|
|
2004
|
+
}
|
|
2005
|
+
};
|
|
2006
|
+
try {
|
|
2007
|
+
const dispose = slots.register({
|
|
2008
|
+
name: "settings.section",
|
|
2009
|
+
id: FAMILY_SECTION_ID,
|
|
2010
|
+
order: 40,
|
|
2011
|
+
priority: FAMILY_HOST_PRIORITY,
|
|
2012
|
+
label,
|
|
2013
|
+
inject: () => ({
|
|
2014
|
+
hooks: { tabs: ledger },
|
|
2015
|
+
renderContributor
|
|
2016
|
+
}),
|
|
2017
|
+
children: { [FAMILY_CHILD_KEY]: {
|
|
2018
|
+
kind: "list",
|
|
2019
|
+
scope: "root"
|
|
2020
|
+
} }
|
|
2021
|
+
}, FamilyHostSection);
|
|
2022
|
+
if (typeof dispose === "function") disposeClaim = dispose;
|
|
2023
|
+
claimed = true;
|
|
2024
|
+
console.warn(`${log} family container unclaimed — took it over (children declared)`);
|
|
2025
|
+
} catch (error) {
|
|
2026
|
+
console.warn(`${log} family claim lost — withdrawing (${error instanceof Error ? error.message : String(error)})`);
|
|
2027
|
+
}
|
|
2028
|
+
}, FAMILY_CLAIM_DELAY_MS);
|
|
2029
|
+
return () => {
|
|
2030
|
+
clearTimeout(timer);
|
|
2031
|
+
if (claimed) disposeClaim?.();
|
|
2032
|
+
};
|
|
2033
|
+
}
|
|
2034
|
+
//#endregion
|
|
1782
2035
|
//#region src/client/index.ts
|
|
1783
2036
|
/** Durable conversation settings namespace owned by ui-conversation. */
|
|
1784
2037
|
const CONVERSATION_SETTINGS_NAMESPACE = "ui-conversation";
|
|
@@ -1831,19 +2084,38 @@ window.__ModuleLoader__.load({
|
|
|
1831
2084
|
function apply(ctx) {
|
|
1832
2085
|
ctx.effect(() => ctx.locale.register(NS, dictionaries), "dsh-input-traffic: dictionaries");
|
|
1833
2086
|
const tFamily = ctx.locale.bind(NS);
|
|
2087
|
+
const familyScopeRef = {};
|
|
2088
|
+
const bridgeScope = new BridgeDocHandle(CONVERSATION_SETTINGS_NAMESPACE);
|
|
2089
|
+
let nativeScopeResolved = false;
|
|
2090
|
+
let bridgePinned = false;
|
|
2091
|
+
bridgeScope.subscribe(() => {
|
|
2092
|
+
if (bridgePinned || nativeScopeResolved) return;
|
|
2093
|
+
if (bridgeScope.getSnapshot().status !== "ready") return;
|
|
2094
|
+
bridgePinned = true;
|
|
2095
|
+
bridgeScope.set(BUSY_ENTER_FIELD, "queue").catch((e) => {
|
|
2096
|
+
console.warn("[dsh-input-traffic] bridge busyEnter pin failed:", e);
|
|
2097
|
+
});
|
|
2098
|
+
});
|
|
2099
|
+
let familyTabHeld = false;
|
|
2100
|
+
ctx.slots.inject("dsh-family.tab", () => {
|
|
2101
|
+
familyTabHeld = true;
|
|
2102
|
+
return ctx.slots.register({
|
|
2103
|
+
name: "dsh-family.tab",
|
|
2104
|
+
id: "input-traffic",
|
|
2105
|
+
order: 50,
|
|
2106
|
+
label: () => tFamily("family.title"),
|
|
2107
|
+
locale: NS,
|
|
2108
|
+
inject: () => ({ scope: familyScopeRef.scope ?? bridgeScope })
|
|
2109
|
+
}, InputTrafficFamilyCard);
|
|
2110
|
+
});
|
|
2111
|
+
ctx.effect(() => claimFamilyHostIfOrphaned(ctx.slots, () => familyTabHeld, () => "起子插件设置", "[dsh-input-traffic]"), "dsh-input-traffic: family host backstop");
|
|
1834
2112
|
registerSettingsFaces(ctx, {
|
|
1835
2113
|
namespace: CONVERSATION_SETTINGS_NAMESPACE,
|
|
1836
2114
|
field: BUSY_ENTER_FIELD,
|
|
1837
2115
|
value: "queue",
|
|
1838
2116
|
onScope: (scope) => {
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
id: "input-traffic",
|
|
1842
|
-
order: 50,
|
|
1843
|
-
label: () => tFamily("family.title"),
|
|
1844
|
-
locale: NS,
|
|
1845
|
-
inject: () => ({ scope })
|
|
1846
|
-
}, InputTrafficFamilyCard));
|
|
2117
|
+
familyScopeRef.scope = scope;
|
|
2118
|
+
nativeScopeResolved = true;
|
|
1847
2119
|
}
|
|
1848
2120
|
});
|
|
1849
2121
|
ctx.slots.inject("conversation.input.dock", () => ctx.slots.register({
|