dsh-input-traffic 0.1.0
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/LICENSE +21 -0
- package/README.en.md +202 -0
- package/README.md +202 -0
- package/cordis.patch.yml +11 -0
- package/lib/client.js +920 -0
- package/lib/client.js.map +1 -0
- package/lib/index.js +5 -0
- package/lib/types/client/freeze-button.d.ts +10 -0
- package/lib/types/client/freeze-button.d.ts.map +1 -0
- package/lib/types/client/freeze-button.js +51 -0
- package/lib/types/client/freeze-button.js.map +1 -0
- package/lib/types/client/freeze-store.d.ts +24 -0
- package/lib/types/client/freeze-store.d.ts.map +1 -0
- package/lib/types/client/freeze-store.js +24 -0
- package/lib/types/client/freeze-store.js.map +1 -0
- package/lib/types/client/hide-enter-row.d.ts +17 -0
- package/lib/types/client/hide-enter-row.d.ts.map +1 -0
- package/lib/types/client/hide-enter-row.js +5 -0
- package/lib/types/client/hide-enter-row.js.map +1 -0
- package/lib/types/client/index.d.ts +25 -0
- package/lib/types/client/index.d.ts.map +1 -0
- package/lib/types/client/index.js +80 -0
- package/lib/types/client/index.js.map +1 -0
- package/lib/types/client/locales.d.ts +54 -0
- package/lib/types/client/locales.d.ts.map +1 -0
- package/lib/types/client/locales.js +96 -0
- package/lib/types/client/locales.js.map +1 -0
- package/lib/types/client/steer-queue-dock.d.ts +59 -0
- package/lib/types/client/steer-queue-dock.d.ts.map +1 -0
- package/lib/types/client/steer-queue-dock.js +320 -0
- package/lib/types/client/steer-queue-dock.js.map +1 -0
- package/lib/types/index.d.ts +11 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/index.js +5 -0
- package/lib/types/index.js.map +1 -0
- package/lib/types/invariant.d.ts +12 -0
- package/lib/types/invariant.d.ts.map +1 -0
- package/lib/types/invariant.js +12 -0
- package/lib/types/invariant.js.map +1 -0
- package/package.json +96 -0
package/lib/client.js
ADDED
|
@@ -0,0 +1,920 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "dsh-input-traffic",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
let react = require("react");
|
|
8
|
+
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
9
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
|
+
//#region src/client/locales.ts
|
|
11
|
+
/** `steer` namespace dictionaries. */
|
|
12
|
+
/** Dictionary namespace owned by this plugin. */
|
|
13
|
+
const NS = "steer";
|
|
14
|
+
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
15
|
+
const zh = {
|
|
16
|
+
"queue.count": "{n} 条排队消息",
|
|
17
|
+
"queue.edit": "编辑排队消息",
|
|
18
|
+
"queue.edit.unsupported": "包含非文本内容,暂不支持编辑",
|
|
19
|
+
"queue.save": "保存排队消息",
|
|
20
|
+
"queue.cancelEdit": "取消编辑",
|
|
21
|
+
"queue.remove": "删除排队消息",
|
|
22
|
+
"queue.removeFailed": "删除失败:这条消息可能已经开始发送。",
|
|
23
|
+
"queue.editFailed": "编辑失败:这条消息可能已经开始发送。",
|
|
24
|
+
"queue.editFailed.pulledBack": "编辑失败,内容已退回主输入框,请确认后重新发送。",
|
|
25
|
+
"steer.inflight": "{n} 条正在插入",
|
|
26
|
+
"steer.now": "打断并输入",
|
|
27
|
+
"steer.now.aria": "红色:打断当前动作并立即输入",
|
|
28
|
+
"steer.now.unsupported": "包含非文本内容,暂不支持打断",
|
|
29
|
+
"steer.nowFailed": "打断失败,请重试。",
|
|
30
|
+
"steer.next": "插话发送",
|
|
31
|
+
"steer.next.aria": "黄色:当前动作结束后插入输入",
|
|
32
|
+
"steer.nextFailed": "插话发送失败,请重试。",
|
|
33
|
+
"steer.later": "排队到下一轮",
|
|
34
|
+
"steer.later.aria": "绿色:排队,上一轮输入的动作都结束后再处理",
|
|
35
|
+
"steer.revoke": "收回排队",
|
|
36
|
+
"steer.revoke.aria": "绿色:撤销插入,收回排队(下一轮处理)",
|
|
37
|
+
"steer.revoke.unsupported": "包含非文本内容,暂不支持收回",
|
|
38
|
+
"steer.revokeFailed": "收回排队失败,请重试。",
|
|
39
|
+
"steer.moveUp": "上移",
|
|
40
|
+
"steer.moveDown": "下移",
|
|
41
|
+
"steer.reorder.unsupported": "包含非文本内容,暂不支持排序",
|
|
42
|
+
"steer.reorderFailed": "调整顺序失败,请重试。",
|
|
43
|
+
"steer.pullBack": "打回输入框编辑",
|
|
44
|
+
"steer.pullBack.unsupported": "包含非文本内容,暂不支持打回编辑",
|
|
45
|
+
"steer.pullBack.composerBusy": "主输入框已有内容,请先发送或清空后再打回",
|
|
46
|
+
"steer.pullBackFailed": "打回输入框失败,请重试。",
|
|
47
|
+
"steer.badge.now": "打断中",
|
|
48
|
+
"steer.badge.next": "插话中",
|
|
49
|
+
"steer.badge.later": "排队",
|
|
50
|
+
"steer.unavailable.running": "仅运行中可规划",
|
|
51
|
+
"steer.clear": "取消并清空",
|
|
52
|
+
"steer.clear.aria": "取消当前执行并清空全部排队消息",
|
|
53
|
+
"steer.clearFailed": "取消并清空失败,请重试。",
|
|
54
|
+
"steer.freeze": "冻结会话",
|
|
55
|
+
"steer.resume": "恢复会话",
|
|
56
|
+
"steer.frozen": "已冻结:当前轮次完成后暂停,排队消息将在恢复后继续",
|
|
57
|
+
"steer.freezeFailed": "冻结失败,请重试。",
|
|
58
|
+
"steer.resumeFailed": "恢复失败,请重试。"
|
|
59
|
+
};
|
|
60
|
+
/** English dictionary (keys mirror zh). */
|
|
61
|
+
const en = {
|
|
62
|
+
"queue.count": "{n} queued messages",
|
|
63
|
+
"queue.edit": "Edit queued message",
|
|
64
|
+
"queue.edit.unsupported": "Contains non-text content; editing is unsupported",
|
|
65
|
+
"queue.save": "Save queued message",
|
|
66
|
+
"queue.cancelEdit": "Cancel edit",
|
|
67
|
+
"queue.remove": "Remove queued message",
|
|
68
|
+
"queue.removeFailed": "Remove failed: the message may have started sending.",
|
|
69
|
+
"queue.editFailed": "Edit failed: the message may have started sending.",
|
|
70
|
+
"queue.editFailed.pulledBack": "Edit failed; the content was moved back to the composer, please review and resend.",
|
|
71
|
+
"steer.inflight": "{n} inserting",
|
|
72
|
+
"steer.now": "Interrupt and send",
|
|
73
|
+
"steer.now.aria": "Red: interrupt the running action and send now",
|
|
74
|
+
"steer.now.unsupported": "Contains non-text content; interrupting is unsupported",
|
|
75
|
+
"steer.nowFailed": "Interrupt failed, please retry.",
|
|
76
|
+
"steer.next": "Send after action",
|
|
77
|
+
"steer.next.aria": "Yellow: insert after the current action finishes",
|
|
78
|
+
"steer.nextFailed": "Steering failed, please retry.",
|
|
79
|
+
"steer.later": "Queue for next turn",
|
|
80
|
+
"steer.later.aria": "Green: queue; processed after all previously steered actions finish",
|
|
81
|
+
"steer.revoke": "Pull back to queue",
|
|
82
|
+
"steer.revoke.aria": "Green: revoke the insertion and queue for the next turn",
|
|
83
|
+
"steer.revoke.unsupported": "Contains non-text content; revoking is unsupported",
|
|
84
|
+
"steer.revokeFailed": "Revoke failed, please retry.",
|
|
85
|
+
"steer.moveUp": "Move up",
|
|
86
|
+
"steer.moveDown": "Move down",
|
|
87
|
+
"steer.reorder.unsupported": "Contains non-text content; reordering is unsupported",
|
|
88
|
+
"steer.reorderFailed": "Reorder failed, please retry.",
|
|
89
|
+
"steer.pullBack": "Edit in composer",
|
|
90
|
+
"steer.pullBack.unsupported": "Contains non-text content; pulling back is unsupported",
|
|
91
|
+
"steer.pullBack.composerBusy": "The composer already has content; send or clear it first",
|
|
92
|
+
"steer.pullBackFailed": "Pull back failed, please retry.",
|
|
93
|
+
"steer.badge.now": "interrupting",
|
|
94
|
+
"steer.badge.next": "steering",
|
|
95
|
+
"steer.badge.later": "queued",
|
|
96
|
+
"steer.unavailable.running": "Planning requires a running session",
|
|
97
|
+
"steer.clear": "Cancel and clear",
|
|
98
|
+
"steer.clear.aria": "Cancel the current run and clear all queued messages",
|
|
99
|
+
"steer.clearFailed": "Cancel and clear failed, please retry.",
|
|
100
|
+
"steer.freeze": "Freeze session",
|
|
101
|
+
"steer.resume": "Resume session",
|
|
102
|
+
"steer.frozen": "Frozen: the current turn finishes, then the queue pauses until resumed",
|
|
103
|
+
"steer.freezeFailed": "Freeze failed, please retry.",
|
|
104
|
+
"steer.resumeFailed": "Resume failed, please retry."
|
|
105
|
+
};
|
|
106
|
+
//#endregion
|
|
107
|
+
//#region src/client/freeze-store.ts
|
|
108
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
109
|
+
let state = {
|
|
110
|
+
frozen: false,
|
|
111
|
+
pending: []
|
|
112
|
+
};
|
|
113
|
+
/** Minimal snapshot store (no runtime dependency, stable identity per mount). */
|
|
114
|
+
const freezeStore = {
|
|
115
|
+
getSnapshot() {
|
|
116
|
+
return state;
|
|
117
|
+
},
|
|
118
|
+
subscribe(listener) {
|
|
119
|
+
listeners.add(listener);
|
|
120
|
+
return () => {
|
|
121
|
+
listeners.delete(listener);
|
|
122
|
+
};
|
|
123
|
+
},
|
|
124
|
+
set(next) {
|
|
125
|
+
state = next;
|
|
126
|
+
for (const listener of listeners) listener();
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
//#endregion
|
|
130
|
+
//#region \0dsh-css:E:\test\rewrite-agently\dsh-input-traffic\src\client\steer-queue-dock.module.css.mjs
|
|
131
|
+
const css$1 = ".cev0eq_dock{width:100%}.cev0eq_panel{background:var(--ds-color-surface,#fffc);border:1px solid var(--ds-color-border,#00000014);border-radius:10px;overflow:hidden}.cev0eq_toolbar{border-bottom:1px solid var(--ds-color-border,#0000000f);align-items:center;gap:8px;padding:4px 8px;display:flex}.cev0eq_header{color:var(--ds-color-text-secondary,#6b7280);cursor:pointer;background:0 0;border:0;align-items:center;gap:6px;padding:2px 6px;font-size:12px;display:inline-flex}.cev0eq_header:disabled{cursor:default}.cev0eq_lead{color:var(--ds-color-text-tertiary,#9ca3af);display:inline-flex}.cev0eq_count{font-weight:600}.cev0eq_chevron{color:var(--ds-color-text-tertiary,#9ca3af);display:inline-flex}.cev0eq_toolbarActions{margin-left:auto}.cev0eq_clear{color:var(--ds-color-text-secondary,#6b7280);cursor:pointer;background:0 0;border:1px solid #0000;border-radius:6px;align-items:center;gap:4px;padding:3px 8px;font-size:12px;display:inline-flex}.cev0eq_freeze{border:1px solid var(--ds-color-border,#0000001f);color:var(--ds-color-text-secondary,#6b7280);cursor:pointer;background:0 0;border-radius:6px;align-items:center;gap:4px;padding:3px 8px;font-size:12px;display:inline-flex}.cev0eq_freeze[aria-pressed=true]{border-color:var(--ds-steer-green,#30a46c);color:var(--ds-steer-green,#30a46c)}.cev0eq_freeze:hover:not(:disabled){background:var(--ds-color-surface-hover,#0000000a)}.cev0eq_freeze:disabled{opacity:.5;cursor:default}.cev0eq_frozenBanner{border-bottom:1px solid var(--ds-color-border,#0000000f);background:color-mix(in srgb, var(--ds-steer-green,#30a46c) 8%, transparent);color:var(--ds-steer-green,#30a46c);padding:4px 10px;font-size:12px}.cev0eq_clear:hover:not(:disabled){border-color:var(--ds-color-border,#0000001f);background:var(--ds-color-surface-hover,#0000000a)}.cev0eq_clear:disabled{opacity:.5;cursor:default}.cev0eq_clearLabel{font-size:12px}.cev0eq_list{margin:0;padding:4px;list-style:none}.cev0eq_row{border-radius:6px;align-items:center;gap:8px;padding:4px 6px;display:flex}.cev0eq_row:hover{background:var(--ds-color-surface-hover,#00000008)}.cev0eq_row[data-editing]{align-items:flex-start}.cev0eq_preview{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--ds-color-text,#1f2937);flex:1;font-size:13px;overflow:hidden}.cev0eq_badge{white-space:nowrap;border-radius:999px;flex:none;align-items:center;gap:4px;padding:1px 6px;font-size:11px;line-height:16px;display:inline-flex}.cev0eq_badgeLabel{color:inherit}.cev0eq_badgeNow{color:var(--ds-steer-red,#e5484d);background:color-mix(in srgb, var(--ds-steer-red,#e5484d) 10%, transparent)}.cev0eq_badgeNext{color:var(--ds-steer-yellow,#e8a33d);background:color-mix(in srgb, var(--ds-steer-yellow,#e8a33d) 12%, transparent)}.cev0eq_badgeLater{color:var(--ds-steer-green,#30a46c);background:color-mix(in srgb, var(--ds-steer-green,#30a46c) 12%, transparent)}.cev0eq_badgeNow .cev0eq_dot{background:var(--ds-steer-red,#e5484d)}.cev0eq_badgeNext .cev0eq_dot{background:var(--ds-steer-yellow,#e8a33d)}.cev0eq_badgeLater .cev0eq_dot{background:var(--ds-steer-green,#30a46c)}.cev0eq_steeringList{margin:0;padding:0 4px 4px;list-style:none}.cev0eq_steeringList .cev0eq_row{opacity:.85}.cev0eq_editor{border:1px solid var(--ds-color-border,#00000026);background:var(--ds-color-surface,#fff);resize:none;border-radius:6px;flex:1;min-width:0;max-height:160px;padding:4px 6px;font-size:13px;line-height:20px;overflow-y:auto}.cev0eq_actions{align-items:center;gap:2px;display:inline-flex}.cev0eq_action{width:24px;height:24px;color:var(--ds-color-text-tertiary,#9ca3af);cursor:pointer;background:0 0;border:0;border-radius:6px;justify-content:center;align-items:center;display:inline-flex}.cev0eq_action:hover:not(:disabled){background:var(--ds-color-surface-hover,#0000000d);color:var(--ds-color-text,#1f2937)}.cev0eq_action:disabled{opacity:.4;cursor:default}.cev0eq_plan{border-left:1px solid var(--ds-color-border,#00000014);align-items:center;gap:2px;margin-left:4px;padding-left:6px;display:inline-flex}.cev0eq_tier{cursor:pointer;background:0 0;border:1px solid #0000;border-radius:6px;justify-content:center;align-items:center;width:20px;height:20px;padding:0;display:inline-flex}.cev0eq_tier:hover:not(:disabled){border-color:var(--ds-color-border,#0003)}.cev0eq_tier:disabled{cursor:default}.cev0eq_tierLater{border-color:var(--ds-steer-green,#30a46c);background:color-mix(in srgb, var(--ds-steer-green,#30a46c) 12%, transparent)}.cev0eq_dot{border-radius:50%;width:10px;height:10px}.cev0eq_tierNow .cev0eq_dot{background:var(--ds-steer-red,#e5484d)}.cev0eq_tierNext .cev0eq_dot{background:var(--ds-steer-yellow,#e8a33d)}.cev0eq_tierLater .cev0eq_dot{background:var(--ds-steer-green,#30a46c)}";
|
|
132
|
+
const tagId$1 = "dsh-input-traffic/steer-queue-dock.module.css";
|
|
133
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
134
|
+
const tag = document.createElement("style");
|
|
135
|
+
tag.dataset.plugin = "dsh-input-traffic";
|
|
136
|
+
tag.dataset.pluginCss = tagId$1;
|
|
137
|
+
tag.textContent = css$1;
|
|
138
|
+
document.head.appendChild(tag);
|
|
139
|
+
}
|
|
140
|
+
var steer_queue_dock_module_css_default = {
|
|
141
|
+
"row": "cev0eq_row",
|
|
142
|
+
"clear": "cev0eq_clear",
|
|
143
|
+
"list": "cev0eq_list",
|
|
144
|
+
"tierLater": "cev0eq_tierLater",
|
|
145
|
+
"toolbar": "cev0eq_toolbar",
|
|
146
|
+
"count": "cev0eq_count",
|
|
147
|
+
"action": "cev0eq_action",
|
|
148
|
+
"tierNext": "cev0eq_tierNext",
|
|
149
|
+
"tierNow": "cev0eq_tierNow",
|
|
150
|
+
"panel": "cev0eq_panel",
|
|
151
|
+
"clearLabel": "cev0eq_clearLabel",
|
|
152
|
+
"badgeLabel": "cev0eq_badgeLabel",
|
|
153
|
+
"badgeNow": "cev0eq_badgeNow",
|
|
154
|
+
"dock": "cev0eq_dock",
|
|
155
|
+
"badgeLater": "cev0eq_badgeLater",
|
|
156
|
+
"preview": "cev0eq_preview",
|
|
157
|
+
"frozenBanner": "cev0eq_frozenBanner",
|
|
158
|
+
"plan": "cev0eq_plan",
|
|
159
|
+
"toolbarActions": "cev0eq_toolbarActions",
|
|
160
|
+
"chevron": "cev0eq_chevron",
|
|
161
|
+
"badgeNext": "cev0eq_badgeNext",
|
|
162
|
+
"steeringList": "cev0eq_steeringList",
|
|
163
|
+
"lead": "cev0eq_lead",
|
|
164
|
+
"header": "cev0eq_header",
|
|
165
|
+
"tier": "cev0eq_tier",
|
|
166
|
+
"badge": "cev0eq_badge",
|
|
167
|
+
"editor": "cev0eq_editor",
|
|
168
|
+
"actions": "cev0eq_actions",
|
|
169
|
+
"freeze": "cev0eq_freeze",
|
|
170
|
+
"dot": "cev0eq_dot"
|
|
171
|
+
};
|
|
172
|
+
//#endregion
|
|
173
|
+
//#region src/client/steer-queue-dock.tsx
|
|
174
|
+
/**
|
|
175
|
+
* Three-tier steering queue dock: the shadowing replacement for the official
|
|
176
|
+
* `conversation.input.dock` entry (same cell id `queue`, priority -1, so the
|
|
177
|
+
* lower priority wins and the official QueueDock never renders while this
|
|
178
|
+
* plugin is mounted).
|
|
179
|
+
*
|
|
180
|
+
* Every queued row carries three planning buttons:
|
|
181
|
+
* - green (later, the default state): the row already queues for the next
|
|
182
|
+
* turn —plain Enter keeps feeding rows here; pressing green on a row that
|
|
183
|
+
* was already steered (yellow) revokes the insertion and pulls it back to
|
|
184
|
+
* later (the yellow flow is reversible);
|
|
185
|
+
* - yellow (next): strict-steer the row into the running turn, consumed at
|
|
186
|
+
* the next step boundary (after the current action finishes);
|
|
187
|
+
* - red (now): cancel the running turn, then remove the row and re-send its
|
|
188
|
+
* text as a fresh message —the re-send arms the harness wake latch (the
|
|
189
|
+
* converged driver restarts) and the interrupted input is processed as the
|
|
190
|
+
* next turn's input. A plain steer after cancel would re-insert a message
|
|
191
|
+
* that is already pending in next-turn and the inbox rejects it.
|
|
192
|
+
*
|
|
193
|
+
* The toolbar also exposes a queue-level "cancel and clear" that stops the
|
|
194
|
+
* current run and removes every queued row, plus a session-level
|
|
195
|
+
* freeze/resume toggle for the peak-hour scenario: freeze stops the current
|
|
196
|
+
* run while preserving the queue (keepInbox), resume re-arms the driver by
|
|
197
|
+
* re-sending the first queued row so the preserved work continues.
|
|
198
|
+
*/
|
|
199
|
+
/** Busy marker for a whole-queue rebuild (reorder); locks every row action. */
|
|
200
|
+
const REORDER_MARK = "__reorder__";
|
|
201
|
+
/**
|
|
202
|
+
* Project the tier badge of one inbox row from its placement. Pure so the
|
|
203
|
+
* row rendering and its tests share one truth.
|
|
204
|
+
* @param placement - the inbox projection placement.
|
|
205
|
+
* @returns the tier badge, or null for non-visible context rows.
|
|
206
|
+
*/
|
|
207
|
+
function badgeFor(placement) {
|
|
208
|
+
if (placement === "steering") return "next";
|
|
209
|
+
if (placement === "queued") return "later";
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Auto-grow one edit textarea to its content. The CSS max-height caps the
|
|
214
|
+
* growth; beyond it the textarea scrolls internally. Height is reset first so
|
|
215
|
+
* shrink (deleting lines) also tracks the content.
|
|
216
|
+
* @param el - the textarea to resize in place.
|
|
217
|
+
*/
|
|
218
|
+
function resizeEditor(el) {
|
|
219
|
+
el.style.height = "auto";
|
|
220
|
+
el.style.height = `${el.scrollHeight}px`;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Queue strip with three-tier planning: one item renders directly; multiple
|
|
224
|
+
* items default to a collapsible count header; an empty queue renders nothing.
|
|
225
|
+
*/
|
|
226
|
+
function SteerQueueDock({ useSession, input, updateQueue, cancel, send, setDraft, notify, t }) {
|
|
227
|
+
const inbox = useSession((s) => s.queue);
|
|
228
|
+
const queue = (0, react.useMemo)(() => inbox.filter((row) => row.placement === "queued"), [inbox]);
|
|
229
|
+
const steering = (0, react.useMemo)(() => inbox.filter((row) => row.placement === "steering"), [inbox]);
|
|
230
|
+
const running = useSession((s) => s.running);
|
|
231
|
+
const queueMutable = useSession((s) => s.subagent === null);
|
|
232
|
+
const [editing, setEditing] = (0, react.useState)(null);
|
|
233
|
+
const [busy, setBusy] = (0, react.useState)(null);
|
|
234
|
+
const [clearing, setClearing] = (0, react.useState)(false);
|
|
235
|
+
const [collapsed, setCollapsed] = (0, react.useState)(true);
|
|
236
|
+
const { frozen } = (0, react.useSyncExternalStore)(freezeStore.subscribe, freezeStore.getSnapshot);
|
|
237
|
+
const listId = (0, react.useId)();
|
|
238
|
+
const editorRef = (0, react.useRef)(null);
|
|
239
|
+
(0, react.useEffect)(() => {
|
|
240
|
+
if (queue.length === 0 && !collapsed) setCollapsed(true);
|
|
241
|
+
if (editing !== null && (!queueMutable || !queue.some((row) => row.id === editing.id))) setEditing(null);
|
|
242
|
+
}, [
|
|
243
|
+
collapsed,
|
|
244
|
+
editing,
|
|
245
|
+
queue,
|
|
246
|
+
queueMutable
|
|
247
|
+
]);
|
|
248
|
+
(0, react.useEffect)(() => {
|
|
249
|
+
if (editing === null) return;
|
|
250
|
+
const el = editorRef.current;
|
|
251
|
+
if (el !== null) resizeEditor(el);
|
|
252
|
+
}, [editing?.id]);
|
|
253
|
+
if (queue.length === 0 && steering.length === 0 && !running) return null;
|
|
254
|
+
const interactionActive = queueMutable && (editing !== null || busy !== null || clearing);
|
|
255
|
+
const expanded = !collapsed || interactionActive;
|
|
256
|
+
const listVisible = queue.length === 1 || expanded;
|
|
257
|
+
const planDisabled = !queueMutable || !running || frozen;
|
|
258
|
+
const nothingPending = queue.length === 0 && steering.length === 0;
|
|
259
|
+
const reorderUnsupported = queue.some((row) => row.text === null);
|
|
260
|
+
const composerEmpty = input.draft.trim() === "";
|
|
261
|
+
const applyAction = async (itemId, action, failure) => {
|
|
262
|
+
setBusy(itemId);
|
|
263
|
+
try {
|
|
264
|
+
await updateQueue(itemId, action);
|
|
265
|
+
return true;
|
|
266
|
+
} catch {
|
|
267
|
+
notify("error", failure);
|
|
268
|
+
return false;
|
|
269
|
+
} finally {
|
|
270
|
+
setBusy((current) => current === itemId ? null : current);
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
const saveEdit = async () => {
|
|
274
|
+
if (editing === null || editing.text.trim() === "") return;
|
|
275
|
+
const itemId = editing.id;
|
|
276
|
+
const text = editing.text;
|
|
277
|
+
setBusy(itemId);
|
|
278
|
+
try {
|
|
279
|
+
await updateQueue(itemId, {
|
|
280
|
+
kind: "edit",
|
|
281
|
+
content: [{
|
|
282
|
+
type: "text",
|
|
283
|
+
text
|
|
284
|
+
}]
|
|
285
|
+
});
|
|
286
|
+
setEditing(null);
|
|
287
|
+
} catch {
|
|
288
|
+
if (input.draft.trim() === "") {
|
|
289
|
+
setDraft(text);
|
|
290
|
+
setEditing(null);
|
|
291
|
+
notify("error", t("queue.editFailed.pulledBack"));
|
|
292
|
+
} else notify("error", t("queue.editFailed"));
|
|
293
|
+
} finally {
|
|
294
|
+
setBusy((current) => current === itemId ? null : current);
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
const steerRow = async (row, tier) => {
|
|
298
|
+
setBusy(row.id);
|
|
299
|
+
try {
|
|
300
|
+
if (tier === "now") {
|
|
301
|
+
await cancel();
|
|
302
|
+
await updateQueue(row.id, { kind: "remove" });
|
|
303
|
+
if (row.text !== null) await send(row.text);
|
|
304
|
+
} else await updateQueue(row.id, { kind: "steer" });
|
|
305
|
+
} catch {
|
|
306
|
+
notify("error", tier === "now" ? t("steer.nowFailed") : t("steer.nextFailed"));
|
|
307
|
+
} finally {
|
|
308
|
+
setBusy((current) => current === row.id ? null : current);
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
/**
|
|
312
|
+
* Revoke a steered (yellow) or interrupting (red) row back to later: remove
|
|
313
|
+
* the admitted row and re-send its text as a queued follow-up, so it lands
|
|
314
|
+
* in next-turn again (the yellow flow is reversible).
|
|
315
|
+
*/
|
|
316
|
+
const revokeToLater = async (row) => {
|
|
317
|
+
if (row.text === null) return;
|
|
318
|
+
setBusy(row.id);
|
|
319
|
+
try {
|
|
320
|
+
await updateQueue(row.id, { kind: "remove" });
|
|
321
|
+
await send(row.text);
|
|
322
|
+
} catch {
|
|
323
|
+
notify("error", t("steer.revokeFailed"));
|
|
324
|
+
} finally {
|
|
325
|
+
setBusy((current) => current === row.id ? null : current);
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
/**
|
|
329
|
+
* Move one queued row up/down in the FIFO order. The queue is rebuilt in
|
|
330
|
+
* the new order: every queued row is removed and re-sent sequentially
|
|
331
|
+
* (plain-text rows only), so the agent's next-turn list matches the
|
|
332
|
+
* reordered view.
|
|
333
|
+
*/
|
|
334
|
+
const reorder = async (rowId, delta) => {
|
|
335
|
+
const rows = queue;
|
|
336
|
+
const index = rows.findIndex((row) => row.id === rowId);
|
|
337
|
+
const target = index + delta;
|
|
338
|
+
if (index < 0 || target < 0 || target >= rows.length) return;
|
|
339
|
+
const next = [...rows];
|
|
340
|
+
const swapped = next[index];
|
|
341
|
+
next[index] = next[target];
|
|
342
|
+
next[target] = swapped;
|
|
343
|
+
if (next.some((row) => row.text === null)) {
|
|
344
|
+
notify("error", t("steer.reorder.unsupported"));
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
setBusy(REORDER_MARK);
|
|
348
|
+
try {
|
|
349
|
+
for (const row of rows) await updateQueue(row.id, { kind: "remove" });
|
|
350
|
+
for (const row of next) await send(row.text);
|
|
351
|
+
} catch {
|
|
352
|
+
notify("error", t("steer.reorderFailed"));
|
|
353
|
+
} finally {
|
|
354
|
+
setBusy(null);
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
/**
|
|
358
|
+
* Pull one queued row back into the composer draft for editing: the text
|
|
359
|
+
* back-fills the input box and the row leaves the queue; the user edits and
|
|
360
|
+
* resubmits it as a fresh message.
|
|
361
|
+
*/
|
|
362
|
+
const pullBackToComposer = async (row) => {
|
|
363
|
+
if (row.text === null) return;
|
|
364
|
+
setDraft(row.text);
|
|
365
|
+
setBusy(row.id);
|
|
366
|
+
try {
|
|
367
|
+
await updateQueue(row.id, { kind: "remove" });
|
|
368
|
+
} catch {
|
|
369
|
+
notify("error", t("steer.pullBackFailed"));
|
|
370
|
+
} finally {
|
|
371
|
+
setBusy((current) => current === row.id ? null : current);
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
const clearAll = async () => {
|
|
375
|
+
setClearing(true);
|
|
376
|
+
const pending = [...queue, ...steering];
|
|
377
|
+
try {
|
|
378
|
+
await cancel();
|
|
379
|
+
await Promise.all(pending.map((row) => updateQueue(row.id, { kind: "remove" }).catch(() => void 0)));
|
|
380
|
+
} catch {
|
|
381
|
+
notify("error", t("steer.clearFailed"));
|
|
382
|
+
} finally {
|
|
383
|
+
setClearing(false);
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
387
|
+
className: steer_queue_dock_module_css_default.dock,
|
|
388
|
+
"data-steer-dock": "",
|
|
389
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
390
|
+
className: steer_queue_dock_module_css_default.panel,
|
|
391
|
+
children: [
|
|
392
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
393
|
+
className: steer_queue_dock_module_css_default.toolbar,
|
|
394
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
395
|
+
type: "button",
|
|
396
|
+
className: steer_queue_dock_module_css_default.header,
|
|
397
|
+
"aria-controls": listId,
|
|
398
|
+
"aria-expanded": expanded,
|
|
399
|
+
disabled: queue.length <= 1 || interactionActive,
|
|
400
|
+
onClick: () => {
|
|
401
|
+
setCollapsed((value) => !value);
|
|
402
|
+
},
|
|
403
|
+
children: [
|
|
404
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
405
|
+
className: steer_queue_dock_module_css_default.lead,
|
|
406
|
+
"aria-hidden": true,
|
|
407
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconQueueOutline14, {})
|
|
408
|
+
}),
|
|
409
|
+
queue.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
410
|
+
className: steer_queue_dock_module_css_default.count,
|
|
411
|
+
children: t("queue.count", { n: queue.length })
|
|
412
|
+
}),
|
|
413
|
+
queue.length > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
414
|
+
className: steer_queue_dock_module_css_default.chevron,
|
|
415
|
+
"aria-hidden": true,
|
|
416
|
+
children: expanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, {})
|
|
417
|
+
})
|
|
418
|
+
]
|
|
419
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
420
|
+
className: steer_queue_dock_module_css_default.toolbarActions,
|
|
421
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
422
|
+
label: t("steer.clear"),
|
|
423
|
+
side: "top",
|
|
424
|
+
delayMs: 500,
|
|
425
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
426
|
+
type: "button",
|
|
427
|
+
className: steer_queue_dock_module_css_default.clear,
|
|
428
|
+
"aria-label": t("steer.clear"),
|
|
429
|
+
disabled: clearing || busy !== null || nothingPending,
|
|
430
|
+
onClick: () => {
|
|
431
|
+
clearAll();
|
|
432
|
+
},
|
|
433
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, { size: 14 }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
434
|
+
className: steer_queue_dock_module_css_default.clearLabel,
|
|
435
|
+
children: t("steer.clear")
|
|
436
|
+
})]
|
|
437
|
+
})
|
|
438
|
+
})
|
|
439
|
+
})]
|
|
440
|
+
}),
|
|
441
|
+
frozen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
442
|
+
className: steer_queue_dock_module_css_default.frozenBanner,
|
|
443
|
+
role: "status",
|
|
444
|
+
children: t("steer.frozen")
|
|
445
|
+
}),
|
|
446
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
447
|
+
id: listId,
|
|
448
|
+
className: steer_queue_dock_module_css_default.list,
|
|
449
|
+
hidden: !listVisible,
|
|
450
|
+
children: listVisible && queue.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
451
|
+
className: steer_queue_dock_module_css_default.row,
|
|
452
|
+
"data-tier": badgeFor("queued") ?? void 0,
|
|
453
|
+
"data-editing": editing?.id === row.id ? "" : void 0,
|
|
454
|
+
children: [
|
|
455
|
+
queue.length === 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
456
|
+
className: steer_queue_dock_module_css_default.lead,
|
|
457
|
+
"aria-hidden": true,
|
|
458
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconQueueOutline14, {})
|
|
459
|
+
}),
|
|
460
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SteerBadge, {
|
|
461
|
+
tier: badgeFor("queued"),
|
|
462
|
+
t
|
|
463
|
+
}),
|
|
464
|
+
editing?.id === row.id ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
465
|
+
ref: editorRef,
|
|
466
|
+
autoFocus: true,
|
|
467
|
+
className: steer_queue_dock_module_css_default.editor,
|
|
468
|
+
"aria-label": t("queue.edit"),
|
|
469
|
+
rows: 1,
|
|
470
|
+
value: editing.text,
|
|
471
|
+
onChange: (event) => {
|
|
472
|
+
setEditing({
|
|
473
|
+
id: row.id,
|
|
474
|
+
text: event.currentTarget.value
|
|
475
|
+
});
|
|
476
|
+
},
|
|
477
|
+
onInput: (event) => {
|
|
478
|
+
resizeEditor(event.currentTarget);
|
|
479
|
+
},
|
|
480
|
+
onKeyDown: (event) => {
|
|
481
|
+
if (event.key === "Escape") {
|
|
482
|
+
setEditing(null);
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing) {
|
|
486
|
+
event.preventDefault();
|
|
487
|
+
saveEdit();
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
491
|
+
className: steer_queue_dock_module_css_default.preview,
|
|
492
|
+
children: row.preview
|
|
493
|
+
}),
|
|
494
|
+
queueMutable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
495
|
+
className: steer_queue_dock_module_css_default.actions,
|
|
496
|
+
children: editing?.id === row.id ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
497
|
+
label: t("queue.save"),
|
|
498
|
+
side: "bottom",
|
|
499
|
+
delayMs: 500,
|
|
500
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
501
|
+
type: "button",
|
|
502
|
+
className: steer_queue_dock_module_css_default.action,
|
|
503
|
+
"aria-label": t("queue.save"),
|
|
504
|
+
disabled: busy !== null || editing.text.trim() === "",
|
|
505
|
+
onClick: () => {
|
|
506
|
+
saveEdit();
|
|
507
|
+
},
|
|
508
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCheckOutline16, { size: 14 })
|
|
509
|
+
})
|
|
510
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
511
|
+
label: t("queue.cancelEdit"),
|
|
512
|
+
side: "bottom",
|
|
513
|
+
delayMs: 500,
|
|
514
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
515
|
+
type: "button",
|
|
516
|
+
className: steer_queue_dock_module_css_default.action,
|
|
517
|
+
"aria-label": t("queue.cancelEdit"),
|
|
518
|
+
disabled: busy !== null,
|
|
519
|
+
onClick: () => {
|
|
520
|
+
setEditing(null);
|
|
521
|
+
},
|
|
522
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 14 })
|
|
523
|
+
})
|
|
524
|
+
})] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
525
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
526
|
+
label: t("steer.moveUp"),
|
|
527
|
+
side: "bottom",
|
|
528
|
+
delayMs: 500,
|
|
529
|
+
disabled: frozen || reorderUnsupported,
|
|
530
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
531
|
+
type: "button",
|
|
532
|
+
className: steer_queue_dock_module_css_default.action,
|
|
533
|
+
"aria-label": t("steer.moveUp"),
|
|
534
|
+
title: reorderUnsupported ? t("steer.reorder.unsupported") : void 0,
|
|
535
|
+
disabled: busy !== null || frozen || reorderUnsupported,
|
|
536
|
+
onClick: () => {
|
|
537
|
+
reorder(row.id, -1);
|
|
538
|
+
},
|
|
539
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, {})
|
|
540
|
+
})
|
|
541
|
+
}),
|
|
542
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
543
|
+
label: t("steer.moveDown"),
|
|
544
|
+
side: "bottom",
|
|
545
|
+
delayMs: 500,
|
|
546
|
+
disabled: frozen || reorderUnsupported,
|
|
547
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
548
|
+
type: "button",
|
|
549
|
+
className: steer_queue_dock_module_css_default.action,
|
|
550
|
+
"aria-label": t("steer.moveDown"),
|
|
551
|
+
title: reorderUnsupported ? t("steer.reorder.unsupported") : void 0,
|
|
552
|
+
disabled: busy !== null || frozen || reorderUnsupported,
|
|
553
|
+
onClick: () => {
|
|
554
|
+
reorder(row.id, 1);
|
|
555
|
+
},
|
|
556
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {})
|
|
557
|
+
})
|
|
558
|
+
}),
|
|
559
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
560
|
+
label: t("steer.pullBack"),
|
|
561
|
+
side: "bottom",
|
|
562
|
+
delayMs: 500,
|
|
563
|
+
disabled: row.text === null || !composerEmpty,
|
|
564
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
565
|
+
type: "button",
|
|
566
|
+
className: steer_queue_dock_module_css_default.action,
|
|
567
|
+
"aria-label": t("steer.pullBack"),
|
|
568
|
+
title: row.text === null ? t("steer.pullBack.unsupported") : !composerEmpty ? t("steer.pullBack.composerBusy") : void 0,
|
|
569
|
+
disabled: busy !== null || frozen || row.text === null || !composerEmpty,
|
|
570
|
+
onClick: () => {
|
|
571
|
+
pullBackToComposer(row);
|
|
572
|
+
},
|
|
573
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRightUpOutline16, {})
|
|
574
|
+
})
|
|
575
|
+
}),
|
|
576
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
577
|
+
label: t("queue.edit"),
|
|
578
|
+
side: "bottom",
|
|
579
|
+
delayMs: 500,
|
|
580
|
+
disabled: row.text === null,
|
|
581
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
582
|
+
type: "button",
|
|
583
|
+
className: steer_queue_dock_module_css_default.action,
|
|
584
|
+
"aria-label": t("queue.edit"),
|
|
585
|
+
title: row.text === null ? t("queue.edit.unsupported") : void 0,
|
|
586
|
+
disabled: busy !== null || row.text === null,
|
|
587
|
+
onClick: () => {
|
|
588
|
+
if (row.text !== null) setEditing({
|
|
589
|
+
id: row.id,
|
|
590
|
+
text: row.text
|
|
591
|
+
});
|
|
592
|
+
},
|
|
593
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEditOutline16, { size: 14 })
|
|
594
|
+
})
|
|
595
|
+
}),
|
|
596
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
597
|
+
label: t("queue.remove"),
|
|
598
|
+
side: "bottom",
|
|
599
|
+
delayMs: 500,
|
|
600
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
601
|
+
type: "button",
|
|
602
|
+
className: steer_queue_dock_module_css_default.action,
|
|
603
|
+
"aria-label": t("queue.remove"),
|
|
604
|
+
disabled: busy !== null,
|
|
605
|
+
onClick: () => {
|
|
606
|
+
applyAction(row.id, { kind: "remove" }, t("queue.removeFailed"));
|
|
607
|
+
},
|
|
608
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, { size: 14 })
|
|
609
|
+
})
|
|
610
|
+
}),
|
|
611
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
612
|
+
className: steer_queue_dock_module_css_default.plan,
|
|
613
|
+
role: "group",
|
|
614
|
+
"aria-label": t("steer.later.aria"),
|
|
615
|
+
children: [
|
|
616
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
617
|
+
label: t("steer.now"),
|
|
618
|
+
side: "bottom",
|
|
619
|
+
delayMs: 500,
|
|
620
|
+
disabled: planDisabled || row.text === null,
|
|
621
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
622
|
+
type: "button",
|
|
623
|
+
className: `${steer_queue_dock_module_css_default.tier} ${steer_queue_dock_module_css_default.tierNow}`,
|
|
624
|
+
"aria-label": t("steer.now"),
|
|
625
|
+
title: planDisabled ? t("steer.unavailable.running") : row.text === null ? t("steer.now.unsupported") : void 0,
|
|
626
|
+
disabled: busy !== null || planDisabled || row.text === null,
|
|
627
|
+
onClick: () => {
|
|
628
|
+
steerRow(row, "now");
|
|
629
|
+
},
|
|
630
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
631
|
+
className: steer_queue_dock_module_css_default.dot,
|
|
632
|
+
"aria-hidden": true
|
|
633
|
+
})
|
|
634
|
+
})
|
|
635
|
+
}),
|
|
636
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
637
|
+
label: t("steer.next"),
|
|
638
|
+
side: "bottom",
|
|
639
|
+
delayMs: 500,
|
|
640
|
+
disabled: planDisabled,
|
|
641
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
642
|
+
type: "button",
|
|
643
|
+
className: `${steer_queue_dock_module_css_default.tier} ${steer_queue_dock_module_css_default.tierNext}`,
|
|
644
|
+
"aria-label": t("steer.next"),
|
|
645
|
+
title: planDisabled ? t("steer.unavailable.running") : void 0,
|
|
646
|
+
disabled: busy !== null || planDisabled,
|
|
647
|
+
onClick: () => {
|
|
648
|
+
steerRow(row, "next");
|
|
649
|
+
},
|
|
650
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
651
|
+
className: steer_queue_dock_module_css_default.dot,
|
|
652
|
+
"aria-hidden": true
|
|
653
|
+
})
|
|
654
|
+
})
|
|
655
|
+
}),
|
|
656
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
657
|
+
label: t("steer.later"),
|
|
658
|
+
side: "bottom",
|
|
659
|
+
delayMs: 500,
|
|
660
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
661
|
+
type: "button",
|
|
662
|
+
className: `${steer_queue_dock_module_css_default.tier} ${steer_queue_dock_module_css_default.tierLater}`,
|
|
663
|
+
"aria-label": t("steer.later"),
|
|
664
|
+
"aria-pressed": true,
|
|
665
|
+
disabled: busy !== null || clearing,
|
|
666
|
+
onClick: () => {},
|
|
667
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
668
|
+
className: steer_queue_dock_module_css_default.dot,
|
|
669
|
+
"aria-hidden": true
|
|
670
|
+
})
|
|
671
|
+
})
|
|
672
|
+
})
|
|
673
|
+
]
|
|
674
|
+
})
|
|
675
|
+
] })
|
|
676
|
+
})
|
|
677
|
+
]
|
|
678
|
+
}, row.id))
|
|
679
|
+
}),
|
|
680
|
+
steering.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
681
|
+
className: steer_queue_dock_module_css_default.steeringList,
|
|
682
|
+
"data-steering-list": "",
|
|
683
|
+
children: steering.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
684
|
+
className: steer_queue_dock_module_css_default.row,
|
|
685
|
+
"data-tier": badgeFor("steering") ?? void 0,
|
|
686
|
+
children: [
|
|
687
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
688
|
+
className: steer_queue_dock_module_css_default.lead,
|
|
689
|
+
"aria-hidden": true,
|
|
690
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconQueueOutline14, {})
|
|
691
|
+
}),
|
|
692
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SteerBadge, {
|
|
693
|
+
tier: badgeFor("steering"),
|
|
694
|
+
t
|
|
695
|
+
}),
|
|
696
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
697
|
+
className: steer_queue_dock_module_css_default.preview,
|
|
698
|
+
children: row.preview
|
|
699
|
+
}),
|
|
700
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
701
|
+
className: steer_queue_dock_module_css_default.actions,
|
|
702
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
703
|
+
label: t("steer.revoke"),
|
|
704
|
+
side: "bottom",
|
|
705
|
+
delayMs: 500,
|
|
706
|
+
disabled: row.text === null,
|
|
707
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
708
|
+
type: "button",
|
|
709
|
+
className: `${steer_queue_dock_module_css_default.tier} ${steer_queue_dock_module_css_default.tierLater}`,
|
|
710
|
+
"aria-label": t("steer.revoke"),
|
|
711
|
+
title: row.text === null ? t("steer.revoke.unsupported") : void 0,
|
|
712
|
+
disabled: busy !== null || row.text === null || frozen,
|
|
713
|
+
onClick: () => {
|
|
714
|
+
revokeToLater(row);
|
|
715
|
+
},
|
|
716
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
717
|
+
className: steer_queue_dock_module_css_default.dot,
|
|
718
|
+
"aria-hidden": true
|
|
719
|
+
})
|
|
720
|
+
})
|
|
721
|
+
})
|
|
722
|
+
})
|
|
723
|
+
]
|
|
724
|
+
}, row.id))
|
|
725
|
+
})
|
|
726
|
+
]
|
|
727
|
+
})
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
/** Badge label keys per tier. */
|
|
731
|
+
const BADGE_KEYS = {
|
|
732
|
+
now: "steer.badge.now",
|
|
733
|
+
next: "steer.badge.next",
|
|
734
|
+
later: "steer.badge.later"
|
|
735
|
+
};
|
|
736
|
+
/** Badge tint classes per tier (present at bundle time; typed through the css-modules declaration). */
|
|
737
|
+
const BADGE_CLASSES = {
|
|
738
|
+
now: steer_queue_dock_module_css_default.badgeNow,
|
|
739
|
+
next: steer_queue_dock_module_css_default.badgeNext,
|
|
740
|
+
later: steer_queue_dock_module_css_default.badgeLater
|
|
741
|
+
};
|
|
742
|
+
/** One row's tier badge: colored dot + label; context rows render nothing. */
|
|
743
|
+
function SteerBadge({ tier, t }) {
|
|
744
|
+
if (tier === null) return null;
|
|
745
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
746
|
+
className: `${steer_queue_dock_module_css_default.badge} ${BADGE_CLASSES[tier]}`,
|
|
747
|
+
"data-badge": tier,
|
|
748
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
749
|
+
className: steer_queue_dock_module_css_default.dot,
|
|
750
|
+
"aria-hidden": true
|
|
751
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
752
|
+
className: steer_queue_dock_module_css_default.badgeLabel,
|
|
753
|
+
children: t(BADGE_KEYS[tier])
|
|
754
|
+
})]
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
//#endregion
|
|
758
|
+
//#region \0dsh-css:E:\test\rewrite-agently\dsh-input-traffic\src\client\freeze-button.module.css.mjs
|
|
759
|
+
const css = ".Klmgza_freeze{border:1px solid var(--ds-color-border,#0000001f);height:24px;color:var(--ds-color-text-secondary,#6b7280);cursor:pointer;white-space:nowrap;background:0 0;border-radius:6px;align-items:center;gap:4px;padding:0 8px;font-size:12px;display:inline-flex}.Klmgza_freeze:hover:not(:disabled){background:var(--ds-color-surface-hover,#0000000a)}.Klmgza_freeze[aria-pressed=true]{border-color:var(--ds-steer-green,#30a46c);color:var(--ds-steer-green,#30a46c);background:color-mix(in srgb, var(--ds-steer-green,#30a46c) 8%, transparent)}.Klmgza_label{font-size:12px}";
|
|
760
|
+
const tagId = "dsh-input-traffic/freeze-button.module.css";
|
|
761
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
762
|
+
const tag = document.createElement("style");
|
|
763
|
+
tag.dataset.plugin = "dsh-input-traffic";
|
|
764
|
+
tag.dataset.pluginCss = tagId;
|
|
765
|
+
tag.textContent = css;
|
|
766
|
+
document.head.appendChild(tag);
|
|
767
|
+
}
|
|
768
|
+
var freeze_button_module_css_default = {
|
|
769
|
+
"freeze": "Klmgza_freeze",
|
|
770
|
+
"label": "Klmgza_label"
|
|
771
|
+
};
|
|
772
|
+
//#endregion
|
|
773
|
+
//#region src/client/freeze-button.tsx
|
|
774
|
+
/**
|
|
775
|
+
* Session freeze/resume control, mounted in the composer's right tool row
|
|
776
|
+
* (`conversation.input.right`) so it is reachable before anything queues.
|
|
777
|
+
*
|
|
778
|
+
* Peak-hour pause semantics: freeze does NOT interrupt the running turn —it
|
|
779
|
+
* finishes naturally. The queued messages are detached (removed, preserved in
|
|
780
|
+
* the shared store), so the driver finds no pending work and stops after the
|
|
781
|
+
* current turn. Resume re-submits the preserved texts, waking the driver and
|
|
782
|
+
* continuing the queue.
|
|
783
|
+
*/
|
|
784
|
+
/**
|
|
785
|
+
* Freeze/resume toggle for the peak-hour scenario.
|
|
786
|
+
* @param props - slot props; the session snapshot drives the detach list.
|
|
787
|
+
*/
|
|
788
|
+
function FreezeButton({ session, updateQueue, send, notify, t }) {
|
|
789
|
+
const { frozen } = (0, react.useSyncExternalStore)(freezeStore.subscribe, freezeStore.getSnapshot);
|
|
790
|
+
const freeze = async () => {
|
|
791
|
+
const queued = session.queue.filter((row) => row.placement === "queued");
|
|
792
|
+
const pending = queued.flatMap((row) => row.text === null ? [] : [row.text]);
|
|
793
|
+
await Promise.all(queued.map((row) => updateQueue(row.id, { kind: "remove" }).catch(() => void 0)));
|
|
794
|
+
freezeStore.set({
|
|
795
|
+
frozen: true,
|
|
796
|
+
pending
|
|
797
|
+
});
|
|
798
|
+
};
|
|
799
|
+
const resume = async () => {
|
|
800
|
+
const pending = freezeStore.getSnapshot().pending;
|
|
801
|
+
freezeStore.set({
|
|
802
|
+
frozen: false,
|
|
803
|
+
pending: []
|
|
804
|
+
});
|
|
805
|
+
try {
|
|
806
|
+
for (const text of pending) await send(text);
|
|
807
|
+
} catch {
|
|
808
|
+
notify("error", t("steer.resumeFailed"));
|
|
809
|
+
}
|
|
810
|
+
};
|
|
811
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
812
|
+
type: "button",
|
|
813
|
+
className: freeze_button_module_css_default.freeze,
|
|
814
|
+
"aria-label": frozen ? t("steer.resume") : t("steer.freeze"),
|
|
815
|
+
"aria-pressed": frozen || void 0,
|
|
816
|
+
title: frozen ? t("steer.frozen") : void 0,
|
|
817
|
+
onClick: () => {
|
|
818
|
+
if (frozen) resume();
|
|
819
|
+
else freeze();
|
|
820
|
+
},
|
|
821
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
822
|
+
className: freeze_button_module_css_default.label,
|
|
823
|
+
children: frozen ? t("steer.resume") : t("steer.freeze")
|
|
824
|
+
})
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
//#endregion
|
|
828
|
+
//#region src/client/hide-enter-row.tsx
|
|
829
|
+
/** Render nothing: the official busy-Enter row is hidden while mounted. */
|
|
830
|
+
function HideEnterRow(_props) {
|
|
831
|
+
return null;
|
|
832
|
+
}
|
|
833
|
+
//#endregion
|
|
834
|
+
//#region src/client/index.ts
|
|
835
|
+
/** Durable conversation settings namespace owned by ui-conversation. */
|
|
836
|
+
const CONVERSATION_SETTINGS_NAMESPACE = "ui-conversation";
|
|
837
|
+
/** Busy-Enter field inside that namespace; the plugin pins it to queue. */
|
|
838
|
+
const BUSY_ENTER_FIELD = "busyEnter";
|
|
839
|
+
/** Services required by the browser half. */
|
|
840
|
+
const inject = [
|
|
841
|
+
"slots",
|
|
842
|
+
"locale",
|
|
843
|
+
"sessions",
|
|
844
|
+
"conversation",
|
|
845
|
+
"settingsScope"
|
|
846
|
+
];
|
|
847
|
+
/**
|
|
848
|
+
* Client plugin body: dictionaries, busy-Enter pinning, and the two slot
|
|
849
|
+
* shadowings.
|
|
850
|
+
* @param ctx - client root context.
|
|
851
|
+
*/
|
|
852
|
+
function apply(ctx) {
|
|
853
|
+
ctx.effect(() => ctx.locale.register(NS, {
|
|
854
|
+
zh,
|
|
855
|
+
en
|
|
856
|
+
}), "dsh-input-traffic: dictionaries");
|
|
857
|
+
ctx.settingsScope.bind({ namespace: CONVERSATION_SETTINGS_NAMESPACE }).set(BUSY_ENTER_FIELD, "queue");
|
|
858
|
+
ctx.slots.inject("conversation.input.dock", () => ctx.slots.register({
|
|
859
|
+
name: "conversation.input.dock",
|
|
860
|
+
id: "queue",
|
|
861
|
+
order: 20,
|
|
862
|
+
priority: -1,
|
|
863
|
+
locale: NS,
|
|
864
|
+
inject: (sessionId) => {
|
|
865
|
+
const actx = ctx.sessions.scope(sessionId);
|
|
866
|
+
if (actx === void 0) throw new Error(`steer dock: session "${sessionId}" resolved no scope`);
|
|
867
|
+
const conversation = actx.get("conversation");
|
|
868
|
+
if (conversation === void 0) throw new Error("steer dock: conversation service unavailable");
|
|
869
|
+
return {
|
|
870
|
+
updateQueue: (itemId, action) => conversation.updateQueue(itemId, action),
|
|
871
|
+
cancel: () => conversation.cancel(),
|
|
872
|
+
send: (text) => conversation.send(text),
|
|
873
|
+
setDraft: (text) => {
|
|
874
|
+
conversation.input.for(actx).actions.setDraft(text);
|
|
875
|
+
},
|
|
876
|
+
notify: (level, text) => {
|
|
877
|
+
conversation.input.for(actx).notify(level, text);
|
|
878
|
+
}
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
}, SteerQueueDock));
|
|
882
|
+
ctx.slots.inject("conversation.input.right", () => ctx.slots.register({
|
|
883
|
+
name: "conversation.input.right",
|
|
884
|
+
id: "steer-freeze",
|
|
885
|
+
order: 30,
|
|
886
|
+
locale: NS,
|
|
887
|
+
inject: (sessionId) => {
|
|
888
|
+
const actx = ctx.sessions.scope(sessionId);
|
|
889
|
+
if (actx === void 0) throw new Error(`steer freeze: session "${sessionId}" resolved no scope`);
|
|
890
|
+
const conversation = actx.get("conversation");
|
|
891
|
+
if (conversation === void 0) throw new Error("steer freeze: conversation service unavailable");
|
|
892
|
+
return {
|
|
893
|
+
updateQueue: (itemId, action) => conversation.updateQueue(itemId, action),
|
|
894
|
+
cancel: () => conversation.cancel(),
|
|
895
|
+
send: (text) => conversation.send(text),
|
|
896
|
+
setDraft: (text) => {
|
|
897
|
+
conversation.input.for(actx).actions.setDraft(text);
|
|
898
|
+
},
|
|
899
|
+
notify: (level, text) => {
|
|
900
|
+
conversation.input.for(actx).notify(level, text);
|
|
901
|
+
}
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
}, FreezeButton));
|
|
905
|
+
ctx.slots.inject("settings.general.item", () => ctx.slots.register({
|
|
906
|
+
name: "settings.general.item",
|
|
907
|
+
id: "composer-enter",
|
|
908
|
+
order: 20,
|
|
909
|
+
priority: -1,
|
|
910
|
+
locale: NS
|
|
911
|
+
}, HideEnterRow));
|
|
912
|
+
}
|
|
913
|
+
//#endregion
|
|
914
|
+
exports.apply = apply;
|
|
915
|
+
exports.inject = inject;
|
|
916
|
+
return module.exports;
|
|
917
|
+
}
|
|
918
|
+
});
|
|
919
|
+
|
|
920
|
+
//# sourceMappingURL=client.js.map
|