dsh-input-traffic 0.7.6 → 0.7.8
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 +33 -0
- 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 +94 -29
- package/lib/client.js.map +1 -1
- package/lib/types/client/locales.js +9 -9
- package/lib/types/client/locales.js.map +1 -1
- package/lib/types/client/steer-queue-dock.d.ts.map +1 -1
- package/lib/types/client/steer-queue-dock.js +113 -30
- package/lib/types/client/steer-queue-dock.js.map +1 -1
- package/package.json +17 -17
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.7 (2026-10-27)
|
|
4
|
+
|
|
5
|
+
两条外部 issue(feidky)的修复;无宿主兼容面变更,`engines.dsh` 枚举与 peers 保持 0.7.6。
|
|
6
|
+
|
|
7
|
+
### 修复:多条排队时自动折叠,与用户抢开关(issue #4)
|
|
8
|
+
|
|
9
|
+
- 多条排队**默认展开**(`listVisible` 仍保留单条直显):排队信息的价值就是看得见。
|
|
10
|
+
- 删除「队列清空 → `setCollapsed(true)`」的自动收回 effect;折叠态只由用户手动决定。
|
|
11
|
+
- 持久化键升 v2(`dsh-input-traffic:collapsed:v2`):v1 的值含自动折叠写入的结果,
|
|
12
|
+
不是用户选择,不能压过新默认值。
|
|
13
|
+
- `collapseTouched` latch:未手动点过表头前**不写存储**,避免首帧把默认值物化成决定。
|
|
14
|
+
|
|
15
|
+
### 修复:红档「打断并输入」在多条排队时投了队首行(issue #3)
|
|
16
|
+
|
|
17
|
+
- 根因(确定性,非竞态):宿主唤醒只经 `followup`,而 `followup` 永远追加到 `next-turn`
|
|
18
|
+
尾部(`agent.ts` `inbox.splice(…, Infinity, …)`),唤醒后 driver 按 FIFO 取**队首**。
|
|
19
|
+
旧实现 `cancel → remove(目标) → send(目标)` 让目标落在队尾,被消费的是队首。
|
|
20
|
+
另:`conversation.send()` 的 mode 硬编码为 `queue`,与 `busyEnter` 设置无关。
|
|
21
|
+
- 新流程:`cancel`(宿主 park 队列,收敛期不 claim)→ 摘出全部其它行 →
|
|
22
|
+
`send(目标)`(此时唯一,即队首)→ 其余行按原序重投。
|
|
23
|
+
- 语义:红档只投入当条;其余行(含黄档插入行)统一降级回绿色等待档——
|
|
24
|
+
被打断的回合不存在「下一个 step」可插入。
|
|
25
|
+
- 顺序:`cancel` 提到最前,所有摘取都发生在 park 之后,去掉「摘到一半被消费」的窗口。
|
|
26
|
+
- `send(目标)` 失败时仍恢复其余排队行(try/finally),不吞队列;
|
|
27
|
+
`queue-item-not-found` 视为已被宿主取走,该行不重投(避免重复投递)。
|
|
28
|
+
|
|
29
|
+
### 限制:附件行仍锁红档
|
|
30
|
+
|
|
31
|
+
- 重投只有 `send(text)` 一条通道:`file` 块的 `receiptId` 一次性、无反向解析;
|
|
32
|
+
图片需 `readAttachment` 回读字节(未实现)。
|
|
33
|
+
- 因此待处理行含非文本内容时**整队**红档禁用(不只那一条),文案改为队列口径(9 语)。
|
|
34
|
+
- 根治需宿主提供队列位置操作(`move` / `prepend`),届时红档与重排的附件锁可一并解除。
|
|
35
|
+
|
|
3
36
|
0.7.6: host enumeration addendum for DSH 0.2.1-rc.3 (declaration-only bump; contract plane zero diff).
|
|
4
37
|
|
|
5
38
|
## 0.7.5 (2026-10-18)
|
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` `0.2.1-rc.1` `0.2.1-rc.2` `0.2.1-rc.3` (
|
|
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` `0.2.1-rc.2` `0.2.1-rc.3` `0.2.1-rc.4` (19 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.2.1-rc.1` `0.2.1-rc.2` `0.2.1-rc.3`(0.1.0 / 0.1.1 / 0.1.2 / 0.1.5 / 0.1.7 / 0.2.0 / 0.2.1 の
|
|
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.2.1-rc.2` `0.2.1-rc.3` `0.2.1-rc.4`(0.1.0 / 0.1.1 / 0.1.2 / 0.1.5 / 0.1.7 / 0.2.0 / 0.2.1 の 19 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.2.1-rc.1` `0.2.1-rc.2` `0.2.1-rc.3` (0.1.0 / 0.1.1 / 0.1.2 / 0.1.5 / 0.1.7 / 0.2.0 / 0.2.1 라인의
|
|
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.2.1-rc.2` `0.2.1-rc.3` `0.2.1-rc.4` (0.1.0 / 0.1.1 / 0.1.2 / 0.1.5 / 0.1.7 / 0.2.0 / 0.2.1 라인의 19개 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.2.1-rc.1` `0.2.1-rc.2` `0.2.1-rc.3`(覆盖 0.1.0 / 0.1.1 / 0.1.2 / 0.1.5 / 0.1.7 / 0.2.0 / 0.2.1 共 7 条线
|
|
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.2.1-rc.2` `0.2.1-rc.3` `0.2.1-rc.4`(覆盖 0.1.0 / 0.1.1 / 0.1.2 / 0.1.5 / 0.1.7 / 0.2.0 / 0.2.1 共 7 条线 19 个 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.8",
|
|
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 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.3"
|
|
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 || 0.2.1-rc.2 || 0.2.1-rc.3 || 0.2.1-rc.4"
|
|
8
8
|
},
|
|
9
9
|
"contributes": {
|
|
10
10
|
"tools": [],
|
package/lib/client.js
CHANGED
|
@@ -29,7 +29,7 @@ window.__ModuleLoader__.load({
|
|
|
29
29
|
"steer.inflight": "{n} 条正在插入",
|
|
30
30
|
"steer.now": "打断并输入",
|
|
31
31
|
"steer.now.aria": "红色:打断当前动作并立即输入",
|
|
32
|
-
"steer.now.unsupported": "
|
|
32
|
+
"steer.now.unsupported": "队列含非文本内容,暂不支持打断",
|
|
33
33
|
"steer.nowFailed": "打断失败,请重试。",
|
|
34
34
|
"steer.next": "插话发送",
|
|
35
35
|
"steer.next.aria": "黄色:当前动作结束后插入输入",
|
|
@@ -83,7 +83,7 @@ window.__ModuleLoader__.load({
|
|
|
83
83
|
"steer.inflight": "{n} inserting",
|
|
84
84
|
"steer.now": "Interrupt and send",
|
|
85
85
|
"steer.now.aria": "Red: interrupt the running action and send now",
|
|
86
|
-
"steer.now.unsupported": "
|
|
86
|
+
"steer.now.unsupported": "The queue contains non-text content; interrupting is unsupported",
|
|
87
87
|
"steer.nowFailed": "Interrupt failed, please retry.",
|
|
88
88
|
"steer.next": "Send after action",
|
|
89
89
|
"steer.next.aria": "Yellow: insert after the current action finishes",
|
|
@@ -137,7 +137,7 @@ window.__ModuleLoader__.load({
|
|
|
137
137
|
"steer.inflight": "{n} 件挿入中",
|
|
138
138
|
"steer.now": "中断して送信",
|
|
139
139
|
"steer.now.aria": "赤:実行中のアクションを中断して即座に送信",
|
|
140
|
-
"steer.now.unsupported": "
|
|
140
|
+
"steer.now.unsupported": "キューに非テキストコンテンツが含まれるため、中断はサポートされていません",
|
|
141
141
|
"steer.nowFailed": "中断失敗。再試行してください。",
|
|
142
142
|
"steer.next": "アクション後に送信",
|
|
143
143
|
"steer.next.aria": "黄:現在のアクション完了後に挿入",
|
|
@@ -191,7 +191,7 @@ window.__ModuleLoader__.load({
|
|
|
191
191
|
"steer.inflight": "{n}개 삽입 중",
|
|
192
192
|
"steer.now": "중단 후 전송",
|
|
193
193
|
"steer.now.aria": "빨간색: 실행 중인 액션을 중단하고 즉시 전송",
|
|
194
|
-
"steer.now.unsupported": "비텍스트 콘텐츠가
|
|
194
|
+
"steer.now.unsupported": "대기열에 비텍스트 콘텐츠가 있어 중단이 지원되지 않습니다",
|
|
195
195
|
"steer.nowFailed": "중단 실패. 다시 시도하세요.",
|
|
196
196
|
"steer.next": "액션 후 전송",
|
|
197
197
|
"steer.next.aria": "노란색: 현재 액션 완료 후 삽입",
|
|
@@ -245,7 +245,7 @@ window.__ModuleLoader__.load({
|
|
|
245
245
|
"steer.inflight": "{n} en cours d'insertion",
|
|
246
246
|
"steer.now": "Interrompre et envoyer",
|
|
247
247
|
"steer.now.aria": "Rouge : interrompre l'action en cours et envoyer immédiatement",
|
|
248
|
-
"steer.now.unsupported": "
|
|
248
|
+
"steer.now.unsupported": "La file contient du contenu non textuel ; interruption non prise en charge",
|
|
249
249
|
"steer.nowFailed": "Échec de l'interruption, veuillez réessayer.",
|
|
250
250
|
"steer.next": "Envoyer après l'action",
|
|
251
251
|
"steer.next.aria": "Jaune : insérer après la fin de l'action en cours",
|
|
@@ -299,7 +299,7 @@ window.__ModuleLoader__.load({
|
|
|
299
299
|
"steer.inflight": "{n} werden eingefügt",
|
|
300
300
|
"steer.now": "Unterbrechen und senden",
|
|
301
301
|
"steer.now.aria": "Rot: Laufende Aktion unterbrechen und sofort senden",
|
|
302
|
-
"steer.now.unsupported": "
|
|
302
|
+
"steer.now.unsupported": "Warteschlange enthält Nicht-Text-Inhalt; Unterbrechen nicht unterstützt",
|
|
303
303
|
"steer.nowFailed": "Unterbrechen fehlgeschlagen, bitte erneut versuchen.",
|
|
304
304
|
"steer.next": "Nach Aktion senden",
|
|
305
305
|
"steer.next.aria": "Gelb: Nach Abschluss der aktuellen Aktion einfügen",
|
|
@@ -353,7 +353,7 @@ window.__ModuleLoader__.load({
|
|
|
353
353
|
"steer.inflight": "{n} in inserimento",
|
|
354
354
|
"steer.now": "Interrompi e invia",
|
|
355
355
|
"steer.now.aria": "Rosso: interrompi l'azione in corso e invia immediatamente",
|
|
356
|
-
"steer.now.unsupported": "
|
|
356
|
+
"steer.now.unsupported": "La coda contiene contenuto non testuale; interruzione non supportata",
|
|
357
357
|
"steer.nowFailed": "Interruzione fallita, riprovare.",
|
|
358
358
|
"steer.next": "Invia dopo l'azione",
|
|
359
359
|
"steer.next.aria": "Giallo: inserisci al termine dell'azione corrente",
|
|
@@ -407,7 +407,7 @@ window.__ModuleLoader__.load({
|
|
|
407
407
|
"steer.inflight": "{n} вставляется",
|
|
408
408
|
"steer.now": "Прервать и отправить",
|
|
409
409
|
"steer.now.aria": "Красный: прервать текущее действие и отправить немедленно",
|
|
410
|
-
"steer.now.unsupported": "
|
|
410
|
+
"steer.now.unsupported": "Очередь содержит нетекстовый контент; прерывание не поддерживается",
|
|
411
411
|
"steer.nowFailed": "Не удалось прервать, повторите.",
|
|
412
412
|
"steer.next": "Отправить после действия",
|
|
413
413
|
"steer.next.aria": "Жёлтый: вставить после завершения текущего действия",
|
|
@@ -461,7 +461,7 @@ window.__ModuleLoader__.load({
|
|
|
461
461
|
"steer.inflight": "{n} insertándose",
|
|
462
462
|
"steer.now": "Interrumpir y enviar",
|
|
463
463
|
"steer.now.aria": "Rojo: interrumpir la acción en curso y enviar inmediatamente",
|
|
464
|
-
"steer.now.unsupported": "
|
|
464
|
+
"steer.now.unsupported": "La cola contiene contenido no textual; interrupción no compatible",
|
|
465
465
|
"steer.nowFailed": "Error al interrumpir, inténtelo de nuevo.",
|
|
466
466
|
"steer.next": "Enviar después de la acción",
|
|
467
467
|
"steer.next.aria": "Amarillo: insertar después de que termine la acción actual",
|
|
@@ -652,11 +652,14 @@ window.__ModuleLoader__.load({
|
|
|
652
652
|
* later (the yellow flow is reversible);
|
|
653
653
|
* - yellow (next): strict-steer the row into the running turn, consumed at
|
|
654
654
|
* the next step boundary (after the current action finishes);
|
|
655
|
-
* - red (now):
|
|
656
|
-
*
|
|
657
|
-
*
|
|
658
|
-
*
|
|
659
|
-
*
|
|
655
|
+
* - red (now): interrupt the running turn and make that single row the next
|
|
656
|
+
* input. Every other pending row leaves the queue first and comes back
|
|
657
|
+
* afterwards in its original order as a plain queued (green) row — the
|
|
658
|
+
* re-sent target is then the only pending row, hence the one the woken
|
|
659
|
+
* driver claims. A cancelled turn has no next step left for a yellow row to
|
|
660
|
+
* steer into, so falling back to green is the correct outcome. Every touched
|
|
661
|
+
* row must be pure text: the re-send path only carries text (a durable file
|
|
662
|
+
* reference has no way back into `prompt`).
|
|
660
663
|
*
|
|
661
664
|
* The toolbar also exposes a queue-level "cancel and clear" that stops the
|
|
662
665
|
* current run and removes every queued row, plus a session-level
|
|
@@ -682,8 +685,13 @@ window.__ModuleLoader__.load({
|
|
|
682
685
|
}
|
|
683
686
|
/** Busy marker for a whole-queue rebuild (reorder); locks every row action. */
|
|
684
687
|
const REORDER_MARK = "__reorder__";
|
|
685
|
-
/**
|
|
686
|
-
|
|
688
|
+
/**
|
|
689
|
+
* localStorage key for the MANUAL collapse state. Deliberately versioned v2:
|
|
690
|
+
* v1 was also written by an auto-collapse (empty-queue) path, so a stored v1
|
|
691
|
+
* value is not necessarily a user decision and must not shadow the new
|
|
692
|
+
* expanded-by-default layout.
|
|
693
|
+
*/
|
|
694
|
+
const COLLAPSE_KEY = "dsh-input-traffic:collapsed:v2";
|
|
687
695
|
/**
|
|
688
696
|
* Project the tier badge of one inbox row from its placement. Pure so the
|
|
689
697
|
* row rendering and its tests share one truth.
|
|
@@ -743,9 +751,9 @@ window.__ModuleLoader__.load({
|
|
|
743
751
|
const [collapsed, setCollapsed] = (0, react.useState)(() => {
|
|
744
752
|
try {
|
|
745
753
|
const v = localStorage.getItem(COLLAPSE_KEY);
|
|
746
|
-
return v === null ?
|
|
754
|
+
return v === null ? false : v === "1";
|
|
747
755
|
} catch {
|
|
748
|
-
return
|
|
756
|
+
return false;
|
|
749
757
|
}
|
|
750
758
|
});
|
|
751
759
|
const { frozen, pending: frozenPending } = (0, react.useSyncExternalStore)(freezeStore.subscribe, () => freezeStore.getSnapshot(sessionId ?? ""));
|
|
@@ -753,15 +761,15 @@ window.__ModuleLoader__.load({
|
|
|
753
761
|
const listId = (0, react.useId)();
|
|
754
762
|
const editorRef = (0, react.useRef)(null);
|
|
755
763
|
(0, react.useEffect)(() => {
|
|
756
|
-
if (queue.length === 0 && !collapsed) setCollapsed(true);
|
|
757
764
|
if (editing !== null && !editing.id.startsWith("frozen:") && (!queueMutable || !queue.some((row) => row.id === editing.id))) setEditing(null);
|
|
758
765
|
}, [
|
|
759
|
-
collapsed,
|
|
760
766
|
editing,
|
|
761
767
|
queue,
|
|
762
768
|
queueMutable
|
|
763
769
|
]);
|
|
770
|
+
const collapseTouched = (0, react.useRef)(false);
|
|
764
771
|
(0, react.useEffect)(() => {
|
|
772
|
+
if (!collapseTouched.current) return;
|
|
765
773
|
try {
|
|
766
774
|
localStorage.setItem(COLLAPSE_KEY, collapsed ? "1" : "0");
|
|
767
775
|
} catch {}
|
|
@@ -782,6 +790,7 @@ window.__ModuleLoader__.load({
|
|
|
782
790
|
const planDisabled = !queueMutable || !running || frozen;
|
|
783
791
|
const nothingPending = queue.length === 0 && steering.length === 0;
|
|
784
792
|
const reorderUnsupported = queue.some((row) => row.text === null);
|
|
793
|
+
const interruptUnsupported = reorderUnsupported || steering.some((row) => row.text === null);
|
|
785
794
|
const applyAction = async (itemId, action, failure) => {
|
|
786
795
|
setBusy(itemId);
|
|
787
796
|
try {
|
|
@@ -818,16 +827,71 @@ window.__ModuleLoader__.load({
|
|
|
818
827
|
setBusy((current) => current === itemId ? null : current);
|
|
819
828
|
}
|
|
820
829
|
};
|
|
821
|
-
|
|
830
|
+
/**
|
|
831
|
+
* Interrupt the running turn and make ONE row the next input; every other
|
|
832
|
+
* pending row falls back to a plain queued (green) row in its original
|
|
833
|
+
* order.
|
|
834
|
+
*
|
|
835
|
+
* The host only ever wakes a turn through `followup`, which appends to the
|
|
836
|
+
* TAIL of next-turn, and the woken driver claims the HEAD. So "submit this
|
|
837
|
+
* row now" has to be built as: interrupt first (the aborted driver parks the
|
|
838
|
+
* queue and claims nothing while it quiesces), take every other row out,
|
|
839
|
+
* re-send this one — it is then the only pending row, hence the head — and
|
|
840
|
+
* finally put the others back in order.
|
|
841
|
+
* @param row - the row to submit, already known to carry pure text.
|
|
842
|
+
*/
|
|
843
|
+
const interruptRow = async (row) => {
|
|
822
844
|
setBusy(row.id);
|
|
823
845
|
try {
|
|
824
|
-
|
|
825
|
-
|
|
846
|
+
await cancel();
|
|
847
|
+
const displaced = [...queue.filter((candidate) => candidate.id !== row.id), ...steering];
|
|
848
|
+
const restored = [];
|
|
849
|
+
let restoreFailed = false;
|
|
850
|
+
for (const candidate of displaced) try {
|
|
851
|
+
await updateQueue(candidate.id, { kind: "remove" });
|
|
852
|
+
restored.push(candidate);
|
|
853
|
+
} catch (error) {
|
|
854
|
+
if (!(error instanceof Error) || !error.message.includes("queue-item-not-found")) restoreFailed = true;
|
|
855
|
+
}
|
|
856
|
+
try {
|
|
826
857
|
await updateQueue(row.id, { kind: "remove" });
|
|
827
|
-
|
|
828
|
-
|
|
858
|
+
} catch {}
|
|
859
|
+
try {
|
|
860
|
+
await send(row.text);
|
|
861
|
+
} finally {
|
|
862
|
+
for (const candidate of restored) {
|
|
863
|
+
if (candidate.text === null) continue;
|
|
864
|
+
try {
|
|
865
|
+
await send(candidate.text);
|
|
866
|
+
} catch {
|
|
867
|
+
restoreFailed = true;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
if (restoreFailed) notify("error", t("steer.nowFailed"));
|
|
872
|
+
} catch {
|
|
873
|
+
notify("error", t("steer.nowFailed"));
|
|
874
|
+
} finally {
|
|
875
|
+
setBusy((current) => current === row.id ? null : current);
|
|
876
|
+
}
|
|
877
|
+
};
|
|
878
|
+
const steerRow = async (row, tier) => {
|
|
879
|
+
if (tier === "now") {
|
|
880
|
+
if (row.text === null || interruptUnsupported) {
|
|
881
|
+
notify("error", t("steer.now.unsupported"));
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
await interruptRow({
|
|
885
|
+
id: row.id,
|
|
886
|
+
text: row.text
|
|
887
|
+
});
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
setBusy(row.id);
|
|
891
|
+
try {
|
|
892
|
+
await updateQueue(row.id, { kind: "steer" });
|
|
829
893
|
} catch {
|
|
830
|
-
notify("error",
|
|
894
|
+
notify("error", t("steer.nextFailed"));
|
|
831
895
|
} finally {
|
|
832
896
|
setBusy((current) => current === row.id ? null : current);
|
|
833
897
|
}
|
|
@@ -966,6 +1030,7 @@ window.__ModuleLoader__.load({
|
|
|
966
1030
|
"aria-expanded": expanded,
|
|
967
1031
|
disabled: queue.length <= 1 || interactionActive,
|
|
968
1032
|
onClick: () => {
|
|
1033
|
+
collapseTouched.current = true;
|
|
969
1034
|
setCollapsed((value) => !value);
|
|
970
1035
|
},
|
|
971
1036
|
children: [
|
|
@@ -1425,13 +1490,13 @@ window.__ModuleLoader__.load({
|
|
|
1425
1490
|
label: t("steer.now"),
|
|
1426
1491
|
side: "bottom",
|
|
1427
1492
|
delayMs: 500,
|
|
1428
|
-
disabled: planDisabled ||
|
|
1493
|
+
disabled: planDisabled || interruptUnsupported,
|
|
1429
1494
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1430
1495
|
type: "button",
|
|
1431
1496
|
className: `${steer_queue_dock_module_css_default.tier} ${steer_queue_dock_module_css_default.tierNow}`,
|
|
1432
1497
|
"aria-label": t("steer.now"),
|
|
1433
|
-
title: planDisabled ? t("steer.unavailable.running") :
|
|
1434
|
-
disabled: busy !== null || planDisabled ||
|
|
1498
|
+
title: planDisabled ? t("steer.unavailable.running") : interruptUnsupported ? t("steer.now.unsupported") : void 0,
|
|
1499
|
+
disabled: busy !== null || planDisabled || interruptUnsupported,
|
|
1435
1500
|
onClick: () => {
|
|
1436
1501
|
steerRow(row, "now");
|
|
1437
1502
|
},
|