dsh-input-traffic 0.2.10-beta.2 → 0.2.10-beta.3
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 +11 -0
- package/INSTALL.md +152 -152
- package/INSTALL.zh.md +151 -151
- package/README.md +10 -7
- package/lib/client.js +1433 -1433
- package/lib/types/client/auto-continue-core.d.ts +102 -102
- package/lib/types/client/auto-continue-core.js +130 -130
- package/lib/types/client/auto-continue-store.d.ts +40 -40
- package/lib/types/client/auto-continue-store.js +92 -92
- package/lib/types/client/index.d.ts +47 -47
- package/package.json +2 -2
package/lib/client.js
CHANGED
|
@@ -1,1434 +1,1434 @@
|
|
|
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` client dictionaries (zh / en / ja / ko). */
|
|
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.reorderStale": "队列已变化,本次排序已取消,请重试。",
|
|
44
|
-
"steer.dragReorder": "拖动调整顺序",
|
|
45
|
-
"steer.pullBack": "打回输入框编辑",
|
|
46
|
-
"steer.pullBack.unsupported": "包含非文本内容,暂不支持打回编辑",
|
|
47
|
-
"steer.pullBack.composerBusy": "主输入框已有内容,请先发送或清空后再打回",
|
|
48
|
-
"steer.pullBackFailed": "打回输入框失败,请重试。",
|
|
49
|
-
"steer.badge.now": "打断中",
|
|
50
|
-
"steer.badge.next": "插话中",
|
|
51
|
-
"steer.badge.later": "排队",
|
|
52
|
-
"steer.unavailable.running": "仅运行中可规划",
|
|
53
|
-
"steer.clear": "取消并清空",
|
|
54
|
-
"steer.clear.confirm": "确认清空?",
|
|
55
|
-
"steer.clear.cancel": "取消清空",
|
|
56
|
-
"steer.clear.aria": "取消当前执行并清空全部排队消息",
|
|
57
|
-
"steer.clearFailed": "取消并清空失败,请重试。",
|
|
58
|
-
"steer.freeze": "冻结追加",
|
|
59
|
-
"steer.resume": "恢复追加",
|
|
60
|
-
"steer.frozen": "已冻结:当前轮次完成后暂停,排队消息将在恢复后继续",
|
|
61
|
-
"steer.frozenInput": "已冻结:输入已暂停,恢复后继续",
|
|
62
|
-
"steer.frozenBadge": "已冻结",
|
|
63
|
-
"steer.freezeFailed": "冻结失败,请重试。",
|
|
64
|
-
"steer.resumeFailed": "恢复失败,请重试。"
|
|
65
|
-
};
|
|
66
|
-
/** English dictionary (keys mirror zh). */
|
|
67
|
-
const en = {
|
|
68
|
-
"queue.count": "{n} queued messages",
|
|
69
|
-
"queue.edit": "Edit queued message",
|
|
70
|
-
"queue.edit.unsupported": "Contains non-text content; editing is unsupported",
|
|
71
|
-
"queue.save": "Save queued message",
|
|
72
|
-
"queue.cancelEdit": "Cancel edit",
|
|
73
|
-
"queue.remove": "Remove queued message",
|
|
74
|
-
"queue.removeFailed": "Remove failed: the message may have started sending.",
|
|
75
|
-
"queue.editFailed": "Edit failed: the message may have started sending.",
|
|
76
|
-
"queue.editFailed.pulledBack": "Edit failed; the content was moved back to the composer, please review and resend.",
|
|
77
|
-
"steer.inflight": "{n} inserting",
|
|
78
|
-
"steer.now": "Interrupt and send",
|
|
79
|
-
"steer.now.aria": "Red: interrupt the running action and send now",
|
|
80
|
-
"steer.now.unsupported": "Contains non-text content; interrupting is unsupported",
|
|
81
|
-
"steer.nowFailed": "Interrupt failed, please retry.",
|
|
82
|
-
"steer.next": "Send after action",
|
|
83
|
-
"steer.next.aria": "Yellow: insert after the current action finishes",
|
|
84
|
-
"steer.nextFailed": "Steering failed, please retry.",
|
|
85
|
-
"steer.later": "Queue for next turn",
|
|
86
|
-
"steer.later.aria": "Green: queue; processed after all previously steered actions finish",
|
|
87
|
-
"steer.revoke": "Pull back to queue",
|
|
88
|
-
"steer.revoke.aria": "Green: revoke the insertion and queue for the next turn",
|
|
89
|
-
"steer.revoke.unsupported": "Contains non-text content; revoking is unsupported",
|
|
90
|
-
"steer.revokeFailed": "Revoke failed, please retry.",
|
|
91
|
-
"steer.moveUp": "Move up",
|
|
92
|
-
"steer.moveDown": "Move down",
|
|
93
|
-
"steer.reorder.unsupported": "Contains non-text content; reordering is unsupported",
|
|
94
|
-
"steer.reorderFailed": "Reorder failed, please retry.",
|
|
95
|
-
"steer.reorderStale": "The queue changed; this reorder was cancelled, please retry.",
|
|
96
|
-
"steer.dragReorder": "Drag to reorder",
|
|
97
|
-
"steer.pullBack": "Edit in composer",
|
|
98
|
-
"steer.pullBack.unsupported": "Contains non-text content; pulling back is unsupported",
|
|
99
|
-
"steer.pullBack.composerBusy": "The composer already has content; send or clear it first",
|
|
100
|
-
"steer.pullBackFailed": "Pull back failed, please retry.",
|
|
101
|
-
"steer.badge.now": "interrupting",
|
|
102
|
-
"steer.badge.next": "steering",
|
|
103
|
-
"steer.badge.later": "queued",
|
|
104
|
-
"steer.unavailable.running": "Planning requires a running session",
|
|
105
|
-
"steer.clear": "Cancel and clear",
|
|
106
|
-
"steer.clear.confirm": "Confirm clear?",
|
|
107
|
-
"steer.clear.cancel": "Cancel clearing",
|
|
108
|
-
"steer.clear.aria": "Cancel the current run and clear all queued messages",
|
|
109
|
-
"steer.clearFailed": "Cancel and clear failed, please retry.",
|
|
110
|
-
"steer.freeze": "Freeze & append",
|
|
111
|
-
"steer.resume": "Resume & append",
|
|
112
|
-
"steer.frozen": "Frozen: the current turn finishes, then the queue pauses until resumed",
|
|
113
|
-
"steer.frozenInput": "Frozen: input paused, resume to continue",
|
|
114
|
-
"steer.frozenBadge": "frozen",
|
|
115
|
-
"steer.freezeFailed": "Freeze failed, please retry.",
|
|
116
|
-
"steer.resumeFailed": "Resume failed, please retry."
|
|
117
|
-
};
|
|
118
|
-
//#endregion
|
|
119
|
-
//#region src/client/freeze-store.ts
|
|
120
|
-
/** Session id → per-session freeze state. */
|
|
121
|
-
const states = /* @__PURE__ */ new Map();
|
|
122
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
123
|
-
/** Stable empty snapshot: `useSyncExternalStore` needs a reference-stable
|
|
124
|
-
* value for unset sessions so unchanged consumers never re-render. */
|
|
125
|
-
const EMPTY = {
|
|
126
|
-
frozen: false,
|
|
127
|
-
pending: []
|
|
128
|
-
};
|
|
129
|
-
/** Minimal snapshot store (no runtime dependency, stable identity per mount). */
|
|
130
|
-
const freezeStore = {
|
|
131
|
-
/** Snapshot for one session; reference-stable until that session changes. */
|
|
132
|
-
getSnapshot(sessionId) {
|
|
133
|
-
return states.get(sessionId) ?? EMPTY;
|
|
134
|
-
},
|
|
135
|
-
subscribe(listener) {
|
|
136
|
-
listeners.add(listener);
|
|
137
|
-
return () => {
|
|
138
|
-
listeners.delete(listener);
|
|
139
|
-
};
|
|
140
|
-
},
|
|
141
|
-
/** Replace one session's state; unset sessions fall back to EMPTY. */
|
|
142
|
-
set(sessionId, next) {
|
|
143
|
-
if (next.frozen === false && next.pending.length === 0) states.delete(sessionId);
|
|
144
|
-
else states.set(sessionId, next);
|
|
145
|
-
emit();
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
/** Edit one detached queued message's text in place. */
|
|
149
|
-
function updatePendingAt(sessionId, index, text) {
|
|
150
|
-
const pending = states.get(sessionId)?.pending;
|
|
151
|
-
if (pending === void 0) return;
|
|
152
|
-
const entry = pending[index];
|
|
153
|
-
if (entry === void 0) return;
|
|
154
|
-
const next = [...pending];
|
|
155
|
-
next[index] = {
|
|
156
|
-
...entry,
|
|
157
|
-
text
|
|
158
|
-
};
|
|
159
|
-
freezeStore.set(sessionId, {
|
|
160
|
-
frozen: true,
|
|
161
|
-
pending: next
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
/** Change one detached queued message's planned insertion tier. */
|
|
165
|
-
function setTierAt(sessionId, index, tier) {
|
|
166
|
-
const pending = states.get(sessionId)?.pending;
|
|
167
|
-
if (pending === void 0) return;
|
|
168
|
-
const entry = pending[index];
|
|
169
|
-
if (entry === void 0) return;
|
|
170
|
-
const next = [...pending];
|
|
171
|
-
next[index] = {
|
|
172
|
-
...entry,
|
|
173
|
-
tier
|
|
174
|
-
};
|
|
175
|
-
freezeStore.set(sessionId, {
|
|
176
|
-
frozen: true,
|
|
177
|
-
pending: next
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
/** Remove one detached queued message. */
|
|
181
|
-
function removePendingAt(sessionId, index) {
|
|
182
|
-
const pending = states.get(sessionId)?.pending;
|
|
183
|
-
if (pending === void 0) return;
|
|
184
|
-
freezeStore.set(sessionId, {
|
|
185
|
-
frozen: true,
|
|
186
|
-
pending: pending.filter((_, i) => i !== index)
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
/** Move one detached queued message to a new position (reorder while frozen). */
|
|
190
|
-
function movePending(sessionId, from, to) {
|
|
191
|
-
if (from === to) return;
|
|
192
|
-
const pending = states.get(sessionId)?.pending;
|
|
193
|
-
if (pending === void 0) return;
|
|
194
|
-
const next = [...pending];
|
|
195
|
-
const [moved] = next.splice(from, 1);
|
|
196
|
-
if (moved === void 0) return;
|
|
197
|
-
next.splice(to, 0, moved);
|
|
198
|
-
freezeStore.set(sessionId, {
|
|
199
|
-
frozen: true,
|
|
200
|
-
pending: next
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
function emit() {
|
|
204
|
-
for (const listener of listeners) listener();
|
|
205
|
-
}
|
|
206
|
-
//#endregion
|
|
207
|
-
//#region \0dsh-css:E:\test\rewrite-agently\mine-dsh-plugins\dsh-input-traffic\src\client\steer-queue-dock.module.css.mjs
|
|
208
|
-
const css$1 = ".FNESVa_dock{width:100%}.FNESVa_panel{background:var(--dsw-alias-bg-layer-1,#fffc);border:1px solid var(--dsw-alias-border-l2,#00000014);border-radius:10px;overflow:hidden}.FNESVa_toolbar{border-bottom:1px solid var(--dsw-alias-border-l2,#0000000f);align-items:center;gap:8px;padding:4px 8px;display:flex}.FNESVa_header{color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;background:0 0;border:0;align-items:center;gap:6px;padding:2px 6px;font-size:12px;display:inline-flex}.FNESVa_header:disabled{cursor:default}.FNESVa_lead{color:var(--dsw-alias-label-tertiary,#9ca3af);display:inline-flex}.FNESVa_count{font-weight:600}.FNESVa_chevron{color:var(--dsw-alias-label-tertiary,#9ca3af);display:inline-flex}.FNESVa_toolbarActions{margin-left:auto}.FNESVa_clear{color:var(--dsw-alias-label-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}.FNESVa_freeze{border:1px solid var(--dsw-alias-border-l3,#0000001f);color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;background:0 0;border-radius:6px;align-items:center;gap:4px;padding:3px 8px;font-size:12px;display:inline-flex}.FNESVa_freeze[aria-pressed=true]{border-color:var(--dsw-alias-state-success-primary,#30a46c);color:var(--dsw-alias-state-success-primary,#30a46c)}.FNESVa_freeze:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#0000000a)}.FNESVa_freeze:disabled{opacity:.5;cursor:default}.FNESVa_frozenBanner{border-bottom:1px solid var(--dsw-alias-border-l2,#0000000f);background:color-mix(in srgb, var(--dsw-alias-state-success-primary,#30a46c) 8%, transparent);color:var(--dsw-alias-state-success-primary,#30a46c);padding:4px 10px;font-size:12px}.FNESVa_frozenList{margin:0;padding:4px;list-style:none}.FNESVa_frozenRow{border-radius:6px;align-items:center;gap:8px;padding:4px 6px;display:flex}.FNESVa_frozenMark{color:var(--dsw-alias-label-tertiary,#9ca3af);flex:none;margin-left:auto;font-size:11px}.FNESVa_clear:hover:not(:disabled){border-color:var(--dsw-alias-border-l3,#0000001f);background:var(--dsw-alias-interactive-bg-hover,#0000000a)}.FNESVa_clear:disabled{opacity:.5;cursor:default}.FNESVa_clearConfirm{border-color:var(--dsw-alias-state-error-primary,#e5484d);color:var(--dsw-alias-state-error-primary,#e5484d)}.FNESVa_clearConfirm:hover:not(:disabled){border-color:var(--dsw-alias-state-error-primary,#e5484d);background:color-mix(in srgb, var(--dsw-alias-state-error-primary,#e5484d) 8%, transparent)}.FNESVa_clearCancel{color:var(--dsw-alias-label-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}.FNESVa_clearCancel:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#0000000a)}.FNESVa_clearLabel{font-size:12px}.FNESVa_list{margin:0;padding:4px;list-style:none}.FNESVa_row{border-radius:6px;align-items:center;gap:8px;padding:4px 6px;display:flex}.FNESVa_row[draggable=true]{cursor:grab}.FNESVa_row[draggable=true]:active{cursor:grabbing}.FNESVa_rowDragOver{outline:2px dashed var(--dsw-alias-border-l4,#00000040);outline-offset:-2px;background:var(--dsw-alias-interactive-bg-hover,#0000000d)}.FNESVa_row:hover{background:var(--dsw-alias-interactive-bg-hover,#00000008)}.FNESVa_row[data-editing]{align-items:flex-start}.FNESVa_preview{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary,#1f2937);flex:1;font-size:13px;overflow:hidden}.FNESVa_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}.FNESVa_badgeLabel{color:inherit}.FNESVa_badgeNow{color:var(--dsw-alias-state-error-primary,#e5484d);background:color-mix(in srgb, var(--dsw-alias-state-error-primary,#e5484d) 10%, transparent)}.FNESVa_badgeNext{color:var(--dsw-alias-state-warn-primary,#e8a33d);background:color-mix(in srgb, var(--dsw-alias-state-warn-primary,#e8a33d) 12%, transparent)}.FNESVa_badgeLater{color:var(--dsw-alias-state-success-primary,#30a46c);background:color-mix(in srgb, var(--dsw-alias-state-success-primary,#30a46c) 12%, transparent)}.FNESVa_badgeNow .FNESVa_dot{background:var(--dsw-alias-state-error-primary,#e5484d)}.FNESVa_badgeNext .FNESVa_dot{background:var(--dsw-alias-state-warn-primary,#e8a33d)}.FNESVa_badgeLater .FNESVa_dot{background:var(--dsw-alias-state-success-primary,#30a46c)}.FNESVa_steeringList{margin:0;padding:0 4px 4px;list-style:none}.FNESVa_steeringList .FNESVa_row{opacity:.85}.FNESVa_editor{border:1px solid var(--dsw-alias-border-l3,#00000026);background:var(--dsw-alias-bg-layer-2,#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}.FNESVa_actions{align-items:center;gap:2px;display:inline-flex}.FNESVa_action{width:24px;height:24px;color:var(--dsw-alias-label-tertiary,#9ca3af);cursor:pointer;background:0 0;border:0;border-radius:6px;justify-content:center;align-items:center;display:inline-flex}.FNESVa_action:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#0000000d);color:var(--dsw-alias-label-primary,#1f2937)}.FNESVa_action:disabled{opacity:.4;cursor:default}.FNESVa_plan{border-left:1px solid var(--dsw-alias-border-l2,#00000014);align-items:center;gap:2px;margin-left:4px;padding-left:6px;display:inline-flex}.FNESVa_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}.FNESVa_tier:hover:not(:disabled){border-color:var(--dsw-alias-border-l4,#0003)}.FNESVa_tier:disabled{cursor:default}.FNESVa_tierLater{border-color:var(--dsw-alias-state-success-primary,#30a46c);background:color-mix(in srgb, var(--dsw-alias-state-success-primary,#30a46c) 12%, transparent)}.FNESVa_dot{border-radius:50%;width:10px;height:10px}.FNESVa_tierNow .FNESVa_dot{background:var(--dsw-alias-state-error-primary,#e5484d)}.FNESVa_tierNext .FNESVa_dot{background:var(--dsw-alias-state-warn-primary,#e8a33d)}.FNESVa_tierLater .FNESVa_dot{background:var(--dsw-alias-state-success-primary,#30a46c)}";
|
|
209
|
-
const tagId$1 = "dsh-input-traffic/steer-queue-dock.module.css";
|
|
210
|
-
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
211
|
-
const tag = document.createElement("style");
|
|
212
|
-
tag.dataset.plugin = "dsh-input-traffic";
|
|
213
|
-
tag.dataset.pluginCss = tagId$1;
|
|
214
|
-
tag.textContent = css$1;
|
|
215
|
-
document.head.appendChild(tag);
|
|
216
|
-
}
|
|
217
|
-
var steer_queue_dock_module_css_default = {
|
|
218
|
-
"editor": "FNESVa_editor",
|
|
219
|
-
"rowDragOver": "FNESVa_rowDragOver",
|
|
220
|
-
"lead": "FNESVa_lead",
|
|
221
|
-
"count": "FNESVa_count",
|
|
222
|
-
"clear": "FNESVa_clear",
|
|
223
|
-
"list": "FNESVa_list",
|
|
224
|
-
"steeringList": "FNESVa_steeringList",
|
|
225
|
-
"badgeNow": "FNESVa_badgeNow",
|
|
226
|
-
"tierNow": "FNESVa_tierNow",
|
|
227
|
-
"tier": "FNESVa_tier",
|
|
228
|
-
"frozenList": "FNESVa_frozenList",
|
|
229
|
-
"badgeLabel": "FNESVa_badgeLabel",
|
|
230
|
-
"clearCancel": "FNESVa_clearCancel",
|
|
231
|
-
"row": "FNESVa_row",
|
|
232
|
-
"dock": "FNESVa_dock",
|
|
233
|
-
"header": "FNESVa_header",
|
|
234
|
-
"actions": "FNESVa_actions",
|
|
235
|
-
"badgeLater": "FNESVa_badgeLater",
|
|
236
|
-
"chevron": "FNESVa_chevron",
|
|
237
|
-
"tierLater": "FNESVa_tierLater",
|
|
238
|
-
"badgeNext": "FNESVa_badgeNext",
|
|
239
|
-
"panel": "FNESVa_panel",
|
|
240
|
-
"preview": "FNESVa_preview",
|
|
241
|
-
"frozenRow": "FNESVa_frozenRow",
|
|
242
|
-
"tierNext": "FNESVa_tierNext",
|
|
243
|
-
"clearConfirm": "FNESVa_clearConfirm",
|
|
244
|
-
"plan": "FNESVa_plan",
|
|
245
|
-
"toolbarActions": "FNESVa_toolbarActions",
|
|
246
|
-
"frozenMark": "FNESVa_frozenMark",
|
|
247
|
-
"freeze": "FNESVa_freeze",
|
|
248
|
-
"badge": "FNESVa_badge",
|
|
249
|
-
"frozenBanner": "FNESVa_frozenBanner",
|
|
250
|
-
"dot": "FNESVa_dot",
|
|
251
|
-
"clearLabel": "FNESVa_clearLabel",
|
|
252
|
-
"action": "FNESVa_action",
|
|
253
|
-
"toolbar": "FNESVa_toolbar"
|
|
254
|
-
};
|
|
255
|
-
//#endregion
|
|
256
|
-
//#region src/client/steer-queue-dock.tsx
|
|
257
|
-
/**
|
|
258
|
-
* Three-tier steering queue dock: the shadowing replacement for the official
|
|
259
|
-
* `conversation.input.dock` entry (same cell id `queue`, priority -1, so the
|
|
260
|
-
* lower priority wins and the official QueueDock never renders while this
|
|
261
|
-
* plugin is mounted).
|
|
262
|
-
*
|
|
263
|
-
* Every queued row carries three planning buttons:
|
|
264
|
-
* - green (later, the default state): the row already queues for the next
|
|
265
|
-
* turn —plain Enter keeps feeding rows here; pressing green on a row that
|
|
266
|
-
* was already steered (yellow) revokes the insertion and pulls it back to
|
|
267
|
-
* later (the yellow flow is reversible);
|
|
268
|
-
* - yellow (next): strict-steer the row into the running turn, consumed at
|
|
269
|
-
* the next step boundary (after the current action finishes);
|
|
270
|
-
* - red (now): cancel the running turn, then remove the row and re-send its
|
|
271
|
-
* text as a fresh message —the re-send arms the harness wake latch (the
|
|
272
|
-
* converged driver restarts) and the interrupted input is processed as the
|
|
273
|
-
* next turn's input. A plain steer after cancel would re-insert a message
|
|
274
|
-
* that is already pending in next-turn and the inbox rejects it.
|
|
275
|
-
*
|
|
276
|
-
* The toolbar also exposes a queue-level "cancel and clear" that stops the
|
|
277
|
-
* current run and removes every queued row, plus a session-level
|
|
278
|
-
* freeze/resume toggle for the peak-hour scenario: freeze stops the current
|
|
279
|
-
* run while preserving the queue (keepInbox), resume re-arms the driver by
|
|
280
|
-
* re-sending the first queued row so the preserved work continues.
|
|
281
|
-
*/
|
|
282
|
-
/** Busy marker for a whole-queue rebuild (reorder); locks every row action. */
|
|
283
|
-
const REORDER_MARK = "__reorder__";
|
|
284
|
-
/** localStorage key for the manual collapse state (dsh-queue-plus parity). */
|
|
285
|
-
const COLLAPSE_KEY = "dsh-input-traffic:collapsed";
|
|
286
|
-
/**
|
|
287
|
-
* Project the tier badge of one inbox row from its placement. Pure so the
|
|
288
|
-
* row rendering and its tests share one truth.
|
|
289
|
-
* @param placement - the inbox projection placement.
|
|
290
|
-
* @returns the tier badge, or null for non-visible context rows.
|
|
291
|
-
*/
|
|
292
|
-
function badgeFor(placement) {
|
|
293
|
-
if (placement === "steering") return "next";
|
|
294
|
-
if (placement === "queued") return "later";
|
|
295
|
-
return null;
|
|
296
|
-
}
|
|
297
|
-
/**
|
|
298
|
-
* Auto-grow one edit textarea to its content. The CSS max-height caps the
|
|
299
|
-
* growth; beyond it the textarea scrolls internally. Height is reset first so
|
|
300
|
-
* shrink (deleting lines) also tracks the content.
|
|
301
|
-
* @param el - the textarea to resize in place.
|
|
302
|
-
*/
|
|
303
|
-
function resizeEditor(el) {
|
|
304
|
-
el.style.height = "auto";
|
|
305
|
-
el.style.height = `${el.scrollHeight}px`;
|
|
306
|
-
}
|
|
307
|
-
/**
|
|
308
|
-
* Queue strip with three-tier planning: one item renders directly; multiple
|
|
309
|
-
* items default to a collapsible count header; an empty queue renders nothing.
|
|
310
|
-
*/
|
|
311
|
-
function SteerQueueDock({ sessionId, useSession, input, updateQueue, cancel, send, setDraft, notify, t }) {
|
|
312
|
-
const inbox = useSession((s) => s.queue);
|
|
313
|
-
const queue = (0, react.useMemo)(() => inbox.filter((row) => row.placement === "queued"), [inbox]);
|
|
314
|
-
const steering = (0, react.useMemo)(() => inbox.filter((row) => row.placement === "steering"), [inbox]);
|
|
315
|
-
const running = useSession((s) => s.running);
|
|
316
|
-
const queueMutable = useSession((s) => s.subagent === null);
|
|
317
|
-
const [editing, setEditing] = (0, react.useState)(null);
|
|
318
|
-
const [busy, setBusy] = (0, react.useState)(null);
|
|
319
|
-
const [clearing, setClearing] = (0, react.useState)(false);
|
|
320
|
-
const [confirmClear, setConfirmClear] = (0, react.useState)(false);
|
|
321
|
-
const confirmTimer = (0, react.useRef)(null);
|
|
322
|
-
const dragIndex = (0, react.useRef)(null);
|
|
323
|
-
const [dragOver, setDragOver] = (0, react.useState)(null);
|
|
324
|
-
const [collapsed, setCollapsed] = (0, react.useState)(() => {
|
|
325
|
-
try {
|
|
326
|
-
const v = localStorage.getItem(COLLAPSE_KEY);
|
|
327
|
-
return v === null ? true : v === "1";
|
|
328
|
-
} catch {
|
|
329
|
-
return true;
|
|
330
|
-
}
|
|
331
|
-
});
|
|
332
|
-
const { frozen, pending: frozenPending } = (0, react.useSyncExternalStore)(freezeStore.subscribe, () => freezeStore.getSnapshot(sessionId ?? ""));
|
|
333
|
-
const sid = sessionId ?? "";
|
|
334
|
-
const listId = (0, react.useId)();
|
|
335
|
-
const editorRef = (0, react.useRef)(null);
|
|
336
|
-
(0, react.useEffect)(() => {
|
|
337
|
-
if (queue.length === 0 && !collapsed) setCollapsed(true);
|
|
338
|
-
if (editing !== null && !editing.id.startsWith("frozen:") && (!queueMutable || !queue.some((row) => row.id === editing.id))) setEditing(null);
|
|
339
|
-
}, [
|
|
340
|
-
collapsed,
|
|
341
|
-
editing,
|
|
342
|
-
queue,
|
|
343
|
-
queueMutable
|
|
344
|
-
]);
|
|
345
|
-
(0, react.useEffect)(() => {
|
|
346
|
-
try {
|
|
347
|
-
localStorage.setItem(COLLAPSE_KEY, collapsed ? "1" : "0");
|
|
348
|
-
} catch {}
|
|
349
|
-
}, [collapsed]);
|
|
350
|
-
(0, react.useEffect)(() => () => {
|
|
351
|
-
if (confirmTimer.current !== null) clearTimeout(confirmTimer.current);
|
|
352
|
-
}, []);
|
|
353
|
-
const editingId = editing?.id;
|
|
354
|
-
(0, react.useEffect)(() => {
|
|
355
|
-
if (editingId === void 0) return;
|
|
356
|
-
const el = editorRef.current;
|
|
357
|
-
if (el !== null) resizeEditor(el);
|
|
358
|
-
}, [editingId]);
|
|
359
|
-
if (queue.length === 0 && steering.length === 0 && !running && !frozen) return null;
|
|
360
|
-
const interactionActive = queueMutable && (editing !== null || busy !== null || clearing);
|
|
361
|
-
const expanded = !collapsed || interactionActive;
|
|
362
|
-
const listVisible = queue.length === 1 || expanded;
|
|
363
|
-
const planDisabled = !queueMutable || !running || frozen;
|
|
364
|
-
const nothingPending = queue.length === 0 && steering.length === 0;
|
|
365
|
-
const reorderUnsupported = queue.some((row) => row.text === null);
|
|
366
|
-
const composerEmpty = input.draft.trim() === "";
|
|
367
|
-
const applyAction = async (itemId, action, failure) => {
|
|
368
|
-
setBusy(itemId);
|
|
369
|
-
try {
|
|
370
|
-
await updateQueue(itemId, action);
|
|
371
|
-
return true;
|
|
372
|
-
} catch {
|
|
373
|
-
notify("error", failure);
|
|
374
|
-
return false;
|
|
375
|
-
} finally {
|
|
376
|
-
setBusy((current) => current === itemId ? null : current);
|
|
377
|
-
}
|
|
378
|
-
};
|
|
379
|
-
const saveEdit = async () => {
|
|
380
|
-
if (editing === null || editing.text.trim() === "") return;
|
|
381
|
-
const itemId = editing.id;
|
|
382
|
-
const text = editing.text;
|
|
383
|
-
setBusy(itemId);
|
|
384
|
-
try {
|
|
385
|
-
await updateQueue(itemId, {
|
|
386
|
-
kind: "edit",
|
|
387
|
-
content: [{
|
|
388
|
-
type: "text",
|
|
389
|
-
text
|
|
390
|
-
}]
|
|
391
|
-
});
|
|
392
|
-
setEditing(null);
|
|
393
|
-
} catch {
|
|
394
|
-
if (input.draft.trim() === "") {
|
|
395
|
-
setDraft(text);
|
|
396
|
-
setEditing(null);
|
|
397
|
-
notify("error", t("queue.editFailed.pulledBack"));
|
|
398
|
-
} else notify("error", t("queue.editFailed"));
|
|
399
|
-
} finally {
|
|
400
|
-
setBusy((current) => current === itemId ? null : current);
|
|
401
|
-
}
|
|
402
|
-
};
|
|
403
|
-
const steerRow = async (row, tier) => {
|
|
404
|
-
setBusy(row.id);
|
|
405
|
-
try {
|
|
406
|
-
if (tier === "now") {
|
|
407
|
-
await cancel();
|
|
408
|
-
await updateQueue(row.id, { kind: "remove" });
|
|
409
|
-
if (row.text !== null) await send(row.text);
|
|
410
|
-
} else await updateQueue(row.id, { kind: "steer" });
|
|
411
|
-
} catch {
|
|
412
|
-
notify("error", tier === "now" ? t("steer.nowFailed") : t("steer.nextFailed"));
|
|
413
|
-
} finally {
|
|
414
|
-
setBusy((current) => current === row.id ? null : current);
|
|
415
|
-
}
|
|
416
|
-
};
|
|
417
|
-
/**
|
|
418
|
-
* Revoke a steered (yellow) or interrupting (red) row back to later: remove
|
|
419
|
-
* the admitted row and re-send its text as a queued follow-up, so it lands
|
|
420
|
-
* in next-turn again (the yellow flow is reversible).
|
|
421
|
-
*/
|
|
422
|
-
const revokeToLater = async (row) => {
|
|
423
|
-
if (row.text === null) return;
|
|
424
|
-
setBusy(row.id);
|
|
425
|
-
try {
|
|
426
|
-
await updateQueue(row.id, { kind: "remove" });
|
|
427
|
-
await send(row.text);
|
|
428
|
-
} catch {
|
|
429
|
-
notify("error", t("steer.revokeFailed"));
|
|
430
|
-
} finally {
|
|
431
|
-
setBusy((current) => current === row.id ? null : current);
|
|
432
|
-
}
|
|
433
|
-
};
|
|
434
|
-
/**
|
|
435
|
-
* Rebuild the queue in a new order: remove every queued row, then re-send
|
|
436
|
-
* the texts sequentially so the agent's next-turn list matches the new
|
|
437
|
-
* order. Concurrency protection (dsh-queue-plus parity): if a remove fails
|
|
438
|
-
* with queue-item-not-found, the agent already claimed that row — the
|
|
439
|
-
* rebuild stops immediately and nothing is re-sent, so the changed queue
|
|
440
|
-
* is never scrambled.
|
|
441
|
-
*/
|
|
442
|
-
const rebuildQueue = async (rows, next) => {
|
|
443
|
-
setBusy(REORDER_MARK);
|
|
444
|
-
try {
|
|
445
|
-
for (const row of rows) try {
|
|
446
|
-
await updateQueue(row.id, { kind: "remove" });
|
|
447
|
-
} catch (error) {
|
|
448
|
-
if (error instanceof Error && error.message.includes("queue-item-not-found")) {
|
|
449
|
-
notify("error", t("steer.reorderStale"));
|
|
450
|
-
return;
|
|
451
|
-
}
|
|
452
|
-
throw error;
|
|
453
|
-
}
|
|
454
|
-
for (const row of next) if (row.text !== null) await send(row.text);
|
|
455
|
-
} catch {
|
|
456
|
-
notify("error", t("steer.reorderFailed"));
|
|
457
|
-
} finally {
|
|
458
|
-
setBusy(null);
|
|
459
|
-
}
|
|
460
|
-
};
|
|
461
|
-
/**
|
|
462
|
-
* Move one queued row up/down in the FIFO order (arrow buttons).
|
|
463
|
-
*/
|
|
464
|
-
const reorder = async (rowId, delta) => {
|
|
465
|
-
const rows = queue;
|
|
466
|
-
const index = rows.findIndex((row) => row.id === rowId);
|
|
467
|
-
const target = index + delta;
|
|
468
|
-
if (index < 0 || target < 0 || target >= rows.length) return;
|
|
469
|
-
const next = [...rows];
|
|
470
|
-
const swapped = next[index];
|
|
471
|
-
next[index] = next[target];
|
|
472
|
-
next[target] = swapped;
|
|
473
|
-
if (next.some((row) => row.text === null)) {
|
|
474
|
-
notify("error", t("steer.reorder.unsupported"));
|
|
475
|
-
return;
|
|
476
|
-
}
|
|
477
|
-
await rebuildQueue(rows, next);
|
|
478
|
-
};
|
|
479
|
-
/**
|
|
480
|
-
* Move one row to an absolute position (drag-and-drop drop handler).
|
|
481
|
-
*/
|
|
482
|
-
const reorderTo = async (fromIndex, toIndex) => {
|
|
483
|
-
if (fromIndex === toIndex) return;
|
|
484
|
-
const rows = queue;
|
|
485
|
-
if (rows[fromIndex] === void 0 || rows[toIndex] === void 0) return;
|
|
486
|
-
const next = [...rows];
|
|
487
|
-
const [moved] = next.splice(fromIndex, 1);
|
|
488
|
-
if (moved === void 0) return;
|
|
489
|
-
next.splice(toIndex, 0, moved);
|
|
490
|
-
if (next.some((row) => row.text === null)) {
|
|
491
|
-
notify("error", t("steer.reorder.unsupported"));
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
494
|
-
await rebuildQueue(rows, next);
|
|
495
|
-
};
|
|
496
|
-
/** Arm or execute the two-step clear confirmation. */
|
|
497
|
-
const armClear = () => {
|
|
498
|
-
if (confirmClear) {
|
|
499
|
-
if (confirmTimer.current !== null) clearTimeout(confirmTimer.current);
|
|
500
|
-
setConfirmClear(false);
|
|
501
|
-
clearAll();
|
|
502
|
-
return;
|
|
503
|
-
}
|
|
504
|
-
setConfirmClear(true);
|
|
505
|
-
if (confirmTimer.current !== null) clearTimeout(confirmTimer.current);
|
|
506
|
-
confirmTimer.current = setTimeout(() => setConfirmClear(false), 3e3);
|
|
507
|
-
};
|
|
508
|
-
/** Abort the armed clear confirmation (the explicit cancel button). */
|
|
509
|
-
const cancelClear = () => {
|
|
510
|
-
if (confirmTimer.current !== null) clearTimeout(confirmTimer.current);
|
|
511
|
-
setConfirmClear(false);
|
|
512
|
-
};
|
|
513
|
-
/** Save an in-place edit of one detached (frozen) queued message. */
|
|
514
|
-
const saveFrozenEdit = async (index) => {
|
|
515
|
-
if (editing === null) return;
|
|
516
|
-
const text = editing.text.trim();
|
|
517
|
-
if (text === "") {
|
|
518
|
-
setEditing(null);
|
|
519
|
-
return;
|
|
520
|
-
}
|
|
521
|
-
updatePendingAt(sid, index, text);
|
|
522
|
-
setEditing(null);
|
|
523
|
-
};
|
|
524
|
-
/**
|
|
525
|
-
* Pull one queued row back into the composer draft for editing: the text
|
|
526
|
-
* back-fills the input box and the row leaves the queue; the user edits and
|
|
527
|
-
* resubmits it as a fresh message.
|
|
528
|
-
*/
|
|
529
|
-
const pullBackToComposer = async (row) => {
|
|
530
|
-
if (row.text === null) return;
|
|
531
|
-
setDraft(row.text);
|
|
532
|
-
setBusy(row.id);
|
|
533
|
-
try {
|
|
534
|
-
await updateQueue(row.id, { kind: "remove" });
|
|
535
|
-
} catch {
|
|
536
|
-
notify("error", t("steer.pullBackFailed"));
|
|
537
|
-
} finally {
|
|
538
|
-
setBusy((current) => current === row.id ? null : current);
|
|
539
|
-
}
|
|
540
|
-
};
|
|
541
|
-
const clearAll = async () => {
|
|
542
|
-
setClearing(true);
|
|
543
|
-
const pending = [...queue, ...steering];
|
|
544
|
-
try {
|
|
545
|
-
await cancel();
|
|
546
|
-
await Promise.all(pending.map((row) => updateQueue(row.id, { kind: "remove" }).catch(() => void 0)));
|
|
547
|
-
} catch {
|
|
548
|
-
notify("error", t("steer.clearFailed"));
|
|
549
|
-
} finally {
|
|
550
|
-
setClearing(false);
|
|
551
|
-
}
|
|
552
|
-
};
|
|
553
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
554
|
-
className: steer_queue_dock_module_css_default.dock,
|
|
555
|
-
"data-steer-dock": "",
|
|
556
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
557
|
-
className: steer_queue_dock_module_css_default.panel,
|
|
558
|
-
children: [
|
|
559
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
560
|
-
className: steer_queue_dock_module_css_default.toolbar,
|
|
561
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
562
|
-
type: "button",
|
|
563
|
-
className: steer_queue_dock_module_css_default.header,
|
|
564
|
-
"aria-controls": listId,
|
|
565
|
-
"aria-expanded": expanded,
|
|
566
|
-
disabled: queue.length <= 1 || interactionActive,
|
|
567
|
-
onClick: () => {
|
|
568
|
-
setCollapsed((value) => !value);
|
|
569
|
-
},
|
|
570
|
-
children: [
|
|
571
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
572
|
-
className: steer_queue_dock_module_css_default.lead,
|
|
573
|
-
"aria-hidden": true,
|
|
574
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconQueueOutline14, {})
|
|
575
|
-
}),
|
|
576
|
-
queue.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
577
|
-
className: steer_queue_dock_module_css_default.count,
|
|
578
|
-
children: t("queue.count", { n: queue.length })
|
|
579
|
-
}),
|
|
580
|
-
queue.length > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
581
|
-
className: steer_queue_dock_module_css_default.chevron,
|
|
582
|
-
"aria-hidden": true,
|
|
583
|
-
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, {})
|
|
584
|
-
})
|
|
585
|
-
]
|
|
586
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
587
|
-
className: steer_queue_dock_module_css_default.toolbarActions,
|
|
588
|
-
children: [confirmClear && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
589
|
-
type: "button",
|
|
590
|
-
className: steer_queue_dock_module_css_default.clearCancel,
|
|
591
|
-
"aria-label": t("steer.clear.cancel"),
|
|
592
|
-
disabled: clearing || busy !== null || nothingPending,
|
|
593
|
-
onClick: cancelClear,
|
|
594
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
595
|
-
className: steer_queue_dock_module_css_default.clearLabel,
|
|
596
|
-
children: t("steer.clear.cancel")
|
|
597
|
-
})
|
|
598
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
599
|
-
label: confirmClear ? t("steer.clear.confirm") : t("steer.clear"),
|
|
600
|
-
side: "top",
|
|
601
|
-
delayMs: 500,
|
|
602
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
603
|
-
type: "button",
|
|
604
|
-
className: `${steer_queue_dock_module_css_default.clear} ${confirmClear ? steer_queue_dock_module_css_default.clearConfirm : ""}`,
|
|
605
|
-
"aria-label": confirmClear ? t("steer.clear.confirm") : t("steer.clear"),
|
|
606
|
-
disabled: clearing || busy !== null || nothingPending,
|
|
607
|
-
onClick: armClear,
|
|
608
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, { size: 14 }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
609
|
-
className: steer_queue_dock_module_css_default.clearLabel,
|
|
610
|
-
children: confirmClear ? t("steer.clear.confirm") : t("steer.clear")
|
|
611
|
-
})]
|
|
612
|
-
})
|
|
613
|
-
})]
|
|
614
|
-
})]
|
|
615
|
-
}),
|
|
616
|
-
frozen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
617
|
-
className: steer_queue_dock_module_css_default.frozenBanner,
|
|
618
|
-
role: "status",
|
|
619
|
-
children: t("steer.frozen")
|
|
620
|
-
}),
|
|
621
|
-
frozen && frozenPending.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
622
|
-
className: steer_queue_dock_module_css_default.frozenList,
|
|
623
|
-
"data-testid": "frozen-list",
|
|
624
|
-
children: frozenPending.map((entry, i) => {
|
|
625
|
-
const editingFrozen = editing?.id === `frozen:${i}`;
|
|
626
|
-
const text = entry.text;
|
|
627
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
628
|
-
className: `${steer_queue_dock_module_css_default.frozenRow} ${dragOver === i && frozen ? steer_queue_dock_module_css_default.rowDragOver : ""}`,
|
|
629
|
-
"data-tier": entry.tier === "force" ? "now" : entry.tier === "safe_point" ? "next" : "later",
|
|
630
|
-
"data-editing": editingFrozen ? "" : void 0,
|
|
631
|
-
draggable: !editingFrozen,
|
|
632
|
-
title: !editingFrozen ? t("steer.dragReorder") : void 0,
|
|
633
|
-
onDragStart: (event) => {
|
|
634
|
-
if (editingFrozen) return;
|
|
635
|
-
dragIndex.current = i;
|
|
636
|
-
event.dataTransfer.effectAllowed = "move";
|
|
637
|
-
event.dataTransfer.setData("text/plain", String(i));
|
|
638
|
-
},
|
|
639
|
-
onDragOver: (event) => {
|
|
640
|
-
if (dragIndex.current === null) return;
|
|
641
|
-
event.preventDefault();
|
|
642
|
-
setDragOver(i);
|
|
643
|
-
},
|
|
644
|
-
onDrop: (event) => {
|
|
645
|
-
event.preventDefault();
|
|
646
|
-
const from = dragIndex.current;
|
|
647
|
-
dragIndex.current = null;
|
|
648
|
-
setDragOver(null);
|
|
649
|
-
if (from !== null) movePending(sid, from, i);
|
|
650
|
-
},
|
|
651
|
-
onDragEnd: () => {
|
|
652
|
-
dragIndex.current = null;
|
|
653
|
-
setDragOver(null);
|
|
654
|
-
},
|
|
655
|
-
children: [
|
|
656
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
657
|
-
className: steer_queue_dock_module_css_default.lead,
|
|
658
|
-
"aria-hidden": true,
|
|
659
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconQueueOutline14, {})
|
|
660
|
-
}),
|
|
661
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SteerBadge, {
|
|
662
|
-
tier: entry.tier === "force" ? "now" : entry.tier === "safe_point" ? "next" : "later",
|
|
663
|
-
t
|
|
664
|
-
}),
|
|
665
|
-
editingFrozen ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
666
|
-
ref: editorRef,
|
|
667
|
-
autoFocus: true,
|
|
668
|
-
className: steer_queue_dock_module_css_default.editor,
|
|
669
|
-
"aria-label": t("queue.edit"),
|
|
670
|
-
rows: 1,
|
|
671
|
-
value: editing?.text ?? text,
|
|
672
|
-
onChange: (event) => {
|
|
673
|
-
setEditing({
|
|
674
|
-
id: `frozen:${i}`,
|
|
675
|
-
text: event.currentTarget.value
|
|
676
|
-
});
|
|
677
|
-
},
|
|
678
|
-
onInput: (event) => {
|
|
679
|
-
resizeEditor(event.currentTarget);
|
|
680
|
-
},
|
|
681
|
-
onKeyDown: (event) => {
|
|
682
|
-
if (event.key === "Escape") {
|
|
683
|
-
setEditing(null);
|
|
684
|
-
return;
|
|
685
|
-
}
|
|
686
|
-
if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing) {
|
|
687
|
-
event.preventDefault();
|
|
688
|
-
saveFrozenEdit(i);
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
692
|
-
className: steer_queue_dock_module_css_default.preview,
|
|
693
|
-
children: text
|
|
694
|
-
}),
|
|
695
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
696
|
-
className: steer_queue_dock_module_css_default.actions,
|
|
697
|
-
children: editingFrozen ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
698
|
-
label: t("queue.save"),
|
|
699
|
-
side: "bottom",
|
|
700
|
-
delayMs: 500,
|
|
701
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
702
|
-
type: "button",
|
|
703
|
-
className: steer_queue_dock_module_css_default.action,
|
|
704
|
-
"aria-label": t("queue.save"),
|
|
705
|
-
disabled: editing === null || editing.text.trim() === "",
|
|
706
|
-
onClick: () => {
|
|
707
|
-
saveFrozenEdit(i);
|
|
708
|
-
},
|
|
709
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCheckOutline16, { size: 14 })
|
|
710
|
-
})
|
|
711
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
712
|
-
label: t("queue.cancelEdit"),
|
|
713
|
-
side: "bottom",
|
|
714
|
-
delayMs: 500,
|
|
715
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
716
|
-
type: "button",
|
|
717
|
-
className: steer_queue_dock_module_css_default.action,
|
|
718
|
-
"aria-label": t("queue.cancelEdit"),
|
|
719
|
-
onClick: () => {
|
|
720
|
-
setEditing(null);
|
|
721
|
-
},
|
|
722
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 14 })
|
|
723
|
-
})
|
|
724
|
-
})] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
725
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
726
|
-
label: t("steer.moveUp"),
|
|
727
|
-
side: "bottom",
|
|
728
|
-
delayMs: 500,
|
|
729
|
-
disabled: i === 0,
|
|
730
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
731
|
-
type: "button",
|
|
732
|
-
className: steer_queue_dock_module_css_default.action,
|
|
733
|
-
"aria-label": t("steer.moveUp"),
|
|
734
|
-
disabled: i === 0,
|
|
735
|
-
onClick: () => movePending(sid, i, i - 1),
|
|
736
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, {})
|
|
737
|
-
})
|
|
738
|
-
}),
|
|
739
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
740
|
-
label: t("steer.moveDown"),
|
|
741
|
-
side: "bottom",
|
|
742
|
-
delayMs: 500,
|
|
743
|
-
disabled: i === frozenPending.length - 1,
|
|
744
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
745
|
-
type: "button",
|
|
746
|
-
className: steer_queue_dock_module_css_default.action,
|
|
747
|
-
"aria-label": t("steer.moveDown"),
|
|
748
|
-
disabled: i === frozenPending.length - 1,
|
|
749
|
-
onClick: () => movePending(sid, i, i + 1),
|
|
750
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {})
|
|
751
|
-
})
|
|
752
|
-
}),
|
|
753
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
754
|
-
label: t("queue.edit"),
|
|
755
|
-
side: "bottom",
|
|
756
|
-
delayMs: 500,
|
|
757
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
758
|
-
type: "button",
|
|
759
|
-
className: steer_queue_dock_module_css_default.action,
|
|
760
|
-
"aria-label": t("queue.edit"),
|
|
761
|
-
onClick: () => {
|
|
762
|
-
setEditing({
|
|
763
|
-
id: `frozen:${i}`,
|
|
764
|
-
text
|
|
765
|
-
});
|
|
766
|
-
},
|
|
767
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEditOutline16, { size: 14 })
|
|
768
|
-
})
|
|
769
|
-
}),
|
|
770
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
771
|
-
label: t("queue.remove"),
|
|
772
|
-
side: "bottom",
|
|
773
|
-
delayMs: 500,
|
|
774
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
775
|
-
type: "button",
|
|
776
|
-
className: steer_queue_dock_module_css_default.action,
|
|
777
|
-
"aria-label": t("queue.remove"),
|
|
778
|
-
onClick: () => removePendingAt(sid, i),
|
|
779
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, { size: 14 })
|
|
780
|
-
})
|
|
781
|
-
}),
|
|
782
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
783
|
-
className: steer_queue_dock_module_css_default.plan,
|
|
784
|
-
role: "group",
|
|
785
|
-
"aria-label": t("steer.later.aria"),
|
|
786
|
-
children: [
|
|
787
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
788
|
-
label: t("steer.now"),
|
|
789
|
-
side: "bottom",
|
|
790
|
-
delayMs: 500,
|
|
791
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
792
|
-
type: "button",
|
|
793
|
-
className: `${steer_queue_dock_module_css_default.tier} ${steer_queue_dock_module_css_default.tierNow}`,
|
|
794
|
-
"aria-label": t("steer.now"),
|
|
795
|
-
"aria-pressed": entry.tier === "force" || void 0,
|
|
796
|
-
onClick: () => setTierAt(sid, i, "force"),
|
|
797
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
798
|
-
className: steer_queue_dock_module_css_default.dot,
|
|
799
|
-
"aria-hidden": true
|
|
800
|
-
})
|
|
801
|
-
})
|
|
802
|
-
}),
|
|
803
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
804
|
-
label: t("steer.next"),
|
|
805
|
-
side: "bottom",
|
|
806
|
-
delayMs: 500,
|
|
807
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
808
|
-
type: "button",
|
|
809
|
-
className: `${steer_queue_dock_module_css_default.tier} ${steer_queue_dock_module_css_default.tierNext}`,
|
|
810
|
-
"aria-label": t("steer.next"),
|
|
811
|
-
"aria-pressed": entry.tier === "safe_point" || void 0,
|
|
812
|
-
onClick: () => setTierAt(sid, i, "safe_point"),
|
|
813
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
814
|
-
className: steer_queue_dock_module_css_default.dot,
|
|
815
|
-
"aria-hidden": true
|
|
816
|
-
})
|
|
817
|
-
})
|
|
818
|
-
}),
|
|
819
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
820
|
-
label: t("steer.later"),
|
|
821
|
-
side: "bottom",
|
|
822
|
-
delayMs: 500,
|
|
823
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
824
|
-
type: "button",
|
|
825
|
-
className: `${steer_queue_dock_module_css_default.tier} ${steer_queue_dock_module_css_default.tierLater}`,
|
|
826
|
-
"aria-label": t("steer.later"),
|
|
827
|
-
"aria-pressed": entry.tier === "queue" || void 0,
|
|
828
|
-
onClick: () => setTierAt(sid, i, "queue"),
|
|
829
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
830
|
-
className: steer_queue_dock_module_css_default.dot,
|
|
831
|
-
"aria-hidden": true
|
|
832
|
-
})
|
|
833
|
-
})
|
|
834
|
-
})
|
|
835
|
-
]
|
|
836
|
-
})
|
|
837
|
-
] })
|
|
838
|
-
}),
|
|
839
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
840
|
-
className: steer_queue_dock_module_css_default.frozenMark,
|
|
841
|
-
children: t("steer.frozenBadge")
|
|
842
|
-
})
|
|
843
|
-
]
|
|
844
|
-
}, i);
|
|
845
|
-
})
|
|
846
|
-
}),
|
|
847
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
848
|
-
id: listId,
|
|
849
|
-
className: steer_queue_dock_module_css_default.list,
|
|
850
|
-
hidden: !listVisible,
|
|
851
|
-
children: listVisible && queue.map((row, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
852
|
-
className: `${steer_queue_dock_module_css_default.row} ${dragOver === index ? steer_queue_dock_module_css_default.rowDragOver : ""}`,
|
|
853
|
-
"data-tier": badgeFor("queued") ?? void 0,
|
|
854
|
-
"data-editing": editing?.id === row.id ? "" : void 0,
|
|
855
|
-
draggable: queueMutable && !reorderUnsupported && busy === null && !frozen,
|
|
856
|
-
title: queueMutable && !reorderUnsupported && busy === null && !frozen ? t("steer.dragReorder") : void 0,
|
|
857
|
-
onDragStart: (event) => {
|
|
858
|
-
dragIndex.current = index;
|
|
859
|
-
event.dataTransfer.effectAllowed = "move";
|
|
860
|
-
event.dataTransfer.setData("text/plain", String(index));
|
|
861
|
-
},
|
|
862
|
-
onDragOver: (event) => {
|
|
863
|
-
if (dragIndex.current === null) return;
|
|
864
|
-
event.preventDefault();
|
|
865
|
-
setDragOver(index);
|
|
866
|
-
},
|
|
867
|
-
onDrop: (event) => {
|
|
868
|
-
event.preventDefault();
|
|
869
|
-
const from = dragIndex.current;
|
|
870
|
-
dragIndex.current = null;
|
|
871
|
-
setDragOver(null);
|
|
872
|
-
if (from !== null) reorderTo(from, index);
|
|
873
|
-
},
|
|
874
|
-
onDragEnd: () => {
|
|
875
|
-
dragIndex.current = null;
|
|
876
|
-
setDragOver(null);
|
|
877
|
-
},
|
|
878
|
-
children: [
|
|
879
|
-
queue.length === 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
880
|
-
className: steer_queue_dock_module_css_default.lead,
|
|
881
|
-
"aria-hidden": true,
|
|
882
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconQueueOutline14, {})
|
|
883
|
-
}),
|
|
884
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SteerBadge, {
|
|
885
|
-
tier: badgeFor("queued"),
|
|
886
|
-
t
|
|
887
|
-
}),
|
|
888
|
-
editing?.id === row.id ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
889
|
-
ref: editorRef,
|
|
890
|
-
autoFocus: true,
|
|
891
|
-
className: steer_queue_dock_module_css_default.editor,
|
|
892
|
-
"aria-label": t("queue.edit"),
|
|
893
|
-
rows: 1,
|
|
894
|
-
value: editing.text,
|
|
895
|
-
onChange: (event) => {
|
|
896
|
-
setEditing({
|
|
897
|
-
id: row.id,
|
|
898
|
-
text: event.currentTarget.value
|
|
899
|
-
});
|
|
900
|
-
},
|
|
901
|
-
onInput: (event) => {
|
|
902
|
-
resizeEditor(event.currentTarget);
|
|
903
|
-
},
|
|
904
|
-
onKeyDown: (event) => {
|
|
905
|
-
if (event.key === "Escape") {
|
|
906
|
-
setEditing(null);
|
|
907
|
-
return;
|
|
908
|
-
}
|
|
909
|
-
if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing) {
|
|
910
|
-
event.preventDefault();
|
|
911
|
-
saveEdit();
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
915
|
-
className: steer_queue_dock_module_css_default.preview,
|
|
916
|
-
children: row.preview
|
|
917
|
-
}),
|
|
918
|
-
queueMutable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
919
|
-
className: steer_queue_dock_module_css_default.actions,
|
|
920
|
-
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, {
|
|
921
|
-
label: t("queue.save"),
|
|
922
|
-
side: "bottom",
|
|
923
|
-
delayMs: 500,
|
|
924
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
925
|
-
type: "button",
|
|
926
|
-
className: steer_queue_dock_module_css_default.action,
|
|
927
|
-
"aria-label": t("queue.save"),
|
|
928
|
-
disabled: busy !== null || editing.text.trim() === "",
|
|
929
|
-
onClick: () => {
|
|
930
|
-
saveEdit();
|
|
931
|
-
},
|
|
932
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCheckOutline16, { size: 14 })
|
|
933
|
-
})
|
|
934
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
935
|
-
label: t("queue.cancelEdit"),
|
|
936
|
-
side: "bottom",
|
|
937
|
-
delayMs: 500,
|
|
938
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
939
|
-
type: "button",
|
|
940
|
-
className: steer_queue_dock_module_css_default.action,
|
|
941
|
-
"aria-label": t("queue.cancelEdit"),
|
|
942
|
-
disabled: busy !== null,
|
|
943
|
-
onClick: () => {
|
|
944
|
-
setEditing(null);
|
|
945
|
-
},
|
|
946
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 14 })
|
|
947
|
-
})
|
|
948
|
-
})] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
949
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
950
|
-
label: t("steer.moveUp"),
|
|
951
|
-
side: "bottom",
|
|
952
|
-
delayMs: 500,
|
|
953
|
-
disabled: frozen || reorderUnsupported,
|
|
954
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
955
|
-
type: "button",
|
|
956
|
-
className: steer_queue_dock_module_css_default.action,
|
|
957
|
-
"aria-label": t("steer.moveUp"),
|
|
958
|
-
title: reorderUnsupported ? t("steer.reorder.unsupported") : void 0,
|
|
959
|
-
disabled: busy !== null || frozen || reorderUnsupported,
|
|
960
|
-
onClick: () => {
|
|
961
|
-
reorder(row.id, -1);
|
|
962
|
-
},
|
|
963
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, {})
|
|
964
|
-
})
|
|
965
|
-
}),
|
|
966
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
967
|
-
label: t("steer.moveDown"),
|
|
968
|
-
side: "bottom",
|
|
969
|
-
delayMs: 500,
|
|
970
|
-
disabled: frozen || reorderUnsupported,
|
|
971
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
972
|
-
type: "button",
|
|
973
|
-
className: steer_queue_dock_module_css_default.action,
|
|
974
|
-
"aria-label": t("steer.moveDown"),
|
|
975
|
-
title: reorderUnsupported ? t("steer.reorder.unsupported") : void 0,
|
|
976
|
-
disabled: busy !== null || frozen || reorderUnsupported,
|
|
977
|
-
onClick: () => {
|
|
978
|
-
reorder(row.id, 1);
|
|
979
|
-
},
|
|
980
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {})
|
|
981
|
-
})
|
|
982
|
-
}),
|
|
983
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
984
|
-
label: t("steer.pullBack"),
|
|
985
|
-
side: "bottom",
|
|
986
|
-
delayMs: 500,
|
|
987
|
-
disabled: row.text === null || !composerEmpty,
|
|
988
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
989
|
-
type: "button",
|
|
990
|
-
className: steer_queue_dock_module_css_default.action,
|
|
991
|
-
"aria-label": t("steer.pullBack"),
|
|
992
|
-
title: row.text === null ? t("steer.pullBack.unsupported") : !composerEmpty ? t("steer.pullBack.composerBusy") : void 0,
|
|
993
|
-
disabled: busy !== null || frozen || row.text === null || !composerEmpty,
|
|
994
|
-
onClick: () => {
|
|
995
|
-
pullBackToComposer(row);
|
|
996
|
-
},
|
|
997
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRightUpOutline16, {})
|
|
998
|
-
})
|
|
999
|
-
}),
|
|
1000
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
1001
|
-
label: t("queue.edit"),
|
|
1002
|
-
side: "bottom",
|
|
1003
|
-
delayMs: 500,
|
|
1004
|
-
disabled: row.text === null,
|
|
1005
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1006
|
-
type: "button",
|
|
1007
|
-
className: steer_queue_dock_module_css_default.action,
|
|
1008
|
-
"aria-label": t("queue.edit"),
|
|
1009
|
-
title: row.text === null ? t("queue.edit.unsupported") : void 0,
|
|
1010
|
-
disabled: busy !== null || row.text === null,
|
|
1011
|
-
onClick: () => {
|
|
1012
|
-
if (row.text !== null) setEditing({
|
|
1013
|
-
id: row.id,
|
|
1014
|
-
text: row.text
|
|
1015
|
-
});
|
|
1016
|
-
},
|
|
1017
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEditOutline16, { size: 14 })
|
|
1018
|
-
})
|
|
1019
|
-
}),
|
|
1020
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
1021
|
-
label: t("queue.remove"),
|
|
1022
|
-
side: "bottom",
|
|
1023
|
-
delayMs: 500,
|
|
1024
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1025
|
-
type: "button",
|
|
1026
|
-
className: steer_queue_dock_module_css_default.action,
|
|
1027
|
-
"aria-label": t("queue.remove"),
|
|
1028
|
-
disabled: busy !== null,
|
|
1029
|
-
onClick: () => {
|
|
1030
|
-
applyAction(row.id, { kind: "remove" }, t("queue.removeFailed"));
|
|
1031
|
-
},
|
|
1032
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, { size: 14 })
|
|
1033
|
-
})
|
|
1034
|
-
}),
|
|
1035
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1036
|
-
className: steer_queue_dock_module_css_default.plan,
|
|
1037
|
-
role: "group",
|
|
1038
|
-
"aria-label": t("steer.later.aria"),
|
|
1039
|
-
children: [
|
|
1040
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
1041
|
-
label: t("steer.now"),
|
|
1042
|
-
side: "bottom",
|
|
1043
|
-
delayMs: 500,
|
|
1044
|
-
disabled: planDisabled || row.text === null,
|
|
1045
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1046
|
-
type: "button",
|
|
1047
|
-
className: `${steer_queue_dock_module_css_default.tier} ${steer_queue_dock_module_css_default.tierNow}`,
|
|
1048
|
-
"aria-label": t("steer.now"),
|
|
1049
|
-
title: planDisabled ? t("steer.unavailable.running") : row.text === null ? t("steer.now.unsupported") : void 0,
|
|
1050
|
-
disabled: busy !== null || planDisabled || row.text === null,
|
|
1051
|
-
onClick: () => {
|
|
1052
|
-
steerRow(row, "now");
|
|
1053
|
-
},
|
|
1054
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1055
|
-
className: steer_queue_dock_module_css_default.dot,
|
|
1056
|
-
"aria-hidden": true
|
|
1057
|
-
})
|
|
1058
|
-
})
|
|
1059
|
-
}),
|
|
1060
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
1061
|
-
label: t("steer.next"),
|
|
1062
|
-
side: "bottom",
|
|
1063
|
-
delayMs: 500,
|
|
1064
|
-
disabled: planDisabled,
|
|
1065
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1066
|
-
type: "button",
|
|
1067
|
-
className: `${steer_queue_dock_module_css_default.tier} ${steer_queue_dock_module_css_default.tierNext}`,
|
|
1068
|
-
"aria-label": t("steer.next"),
|
|
1069
|
-
title: planDisabled ? t("steer.unavailable.running") : void 0,
|
|
1070
|
-
disabled: busy !== null || planDisabled,
|
|
1071
|
-
onClick: () => {
|
|
1072
|
-
steerRow(row, "next");
|
|
1073
|
-
},
|
|
1074
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1075
|
-
className: steer_queue_dock_module_css_default.dot,
|
|
1076
|
-
"aria-hidden": true
|
|
1077
|
-
})
|
|
1078
|
-
})
|
|
1079
|
-
}),
|
|
1080
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
1081
|
-
label: t("steer.later"),
|
|
1082
|
-
side: "bottom",
|
|
1083
|
-
delayMs: 500,
|
|
1084
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1085
|
-
type: "button",
|
|
1086
|
-
className: `${steer_queue_dock_module_css_default.tier} ${steer_queue_dock_module_css_default.tierLater}`,
|
|
1087
|
-
"aria-label": t("steer.later"),
|
|
1088
|
-
"aria-pressed": true,
|
|
1089
|
-
disabled: busy !== null || clearing,
|
|
1090
|
-
onClick: () => {},
|
|
1091
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1092
|
-
className: steer_queue_dock_module_css_default.dot,
|
|
1093
|
-
"aria-hidden": true
|
|
1094
|
-
})
|
|
1095
|
-
})
|
|
1096
|
-
})
|
|
1097
|
-
]
|
|
1098
|
-
})
|
|
1099
|
-
] })
|
|
1100
|
-
})
|
|
1101
|
-
]
|
|
1102
|
-
}, row.id))
|
|
1103
|
-
}),
|
|
1104
|
-
steering.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
1105
|
-
className: steer_queue_dock_module_css_default.steeringList,
|
|
1106
|
-
"data-steering-list": "",
|
|
1107
|
-
children: steering.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
1108
|
-
className: steer_queue_dock_module_css_default.row,
|
|
1109
|
-
"data-tier": badgeFor("steering") ?? void 0,
|
|
1110
|
-
children: [
|
|
1111
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1112
|
-
className: steer_queue_dock_module_css_default.lead,
|
|
1113
|
-
"aria-hidden": true,
|
|
1114
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconQueueOutline14, {})
|
|
1115
|
-
}),
|
|
1116
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SteerBadge, {
|
|
1117
|
-
tier: badgeFor("steering"),
|
|
1118
|
-
t
|
|
1119
|
-
}),
|
|
1120
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1121
|
-
className: steer_queue_dock_module_css_default.preview,
|
|
1122
|
-
children: row.preview
|
|
1123
|
-
}),
|
|
1124
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1125
|
-
className: steer_queue_dock_module_css_default.actions,
|
|
1126
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
1127
|
-
label: t("steer.revoke"),
|
|
1128
|
-
side: "bottom",
|
|
1129
|
-
delayMs: 500,
|
|
1130
|
-
disabled: row.text === null,
|
|
1131
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1132
|
-
type: "button",
|
|
1133
|
-
className: `${steer_queue_dock_module_css_default.tier} ${steer_queue_dock_module_css_default.tierLater}`,
|
|
1134
|
-
"aria-label": t("steer.revoke"),
|
|
1135
|
-
title: row.text === null ? t("steer.revoke.unsupported") : void 0,
|
|
1136
|
-
disabled: busy !== null || row.text === null || frozen,
|
|
1137
|
-
onClick: () => {
|
|
1138
|
-
revokeToLater(row);
|
|
1139
|
-
},
|
|
1140
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1141
|
-
className: steer_queue_dock_module_css_default.dot,
|
|
1142
|
-
"aria-hidden": true
|
|
1143
|
-
})
|
|
1144
|
-
})
|
|
1145
|
-
})
|
|
1146
|
-
})
|
|
1147
|
-
]
|
|
1148
|
-
}, row.id))
|
|
1149
|
-
})
|
|
1150
|
-
]
|
|
1151
|
-
})
|
|
1152
|
-
});
|
|
1153
|
-
}
|
|
1154
|
-
/** Badge label keys per tier. */
|
|
1155
|
-
const BADGE_KEYS = {
|
|
1156
|
-
now: "steer.badge.now",
|
|
1157
|
-
next: "steer.badge.next",
|
|
1158
|
-
later: "steer.badge.later"
|
|
1159
|
-
};
|
|
1160
|
-
/** Badge tint classes per tier (present at bundle time; typed through the css-modules declaration). */
|
|
1161
|
-
const BADGE_CLASSES = {
|
|
1162
|
-
now: steer_queue_dock_module_css_default.badgeNow,
|
|
1163
|
-
next: steer_queue_dock_module_css_default.badgeNext,
|
|
1164
|
-
later: steer_queue_dock_module_css_default.badgeLater
|
|
1165
|
-
};
|
|
1166
|
-
/** One row's tier badge: colored dot + label; context rows render nothing. */
|
|
1167
|
-
function SteerBadge({ tier, t }) {
|
|
1168
|
-
if (tier === null) return null;
|
|
1169
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1170
|
-
className: `${steer_queue_dock_module_css_default.badge} ${BADGE_CLASSES[tier]}`,
|
|
1171
|
-
"data-badge": tier,
|
|
1172
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1173
|
-
className: steer_queue_dock_module_css_default.dot,
|
|
1174
|
-
"aria-hidden": true
|
|
1175
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1176
|
-
className: steer_queue_dock_module_css_default.badgeLabel,
|
|
1177
|
-
children: t(BADGE_KEYS[tier])
|
|
1178
|
-
})]
|
|
1179
|
-
});
|
|
1180
|
-
}
|
|
1181
|
-
//#endregion
|
|
1182
|
-
//#region src/client/session-guard-bridge.ts
|
|
1183
|
-
/**
|
|
1184
|
-
* input-traffic ↔ dsh-session-guard 透传桥(D8 fail-open)。
|
|
1185
|
-
*
|
|
1186
|
-
* input-traffic **只做冻结增强**(队列冻结/解冻),服务端会话门(暂停/恢复会话)
|
|
1187
|
-
* 归 dsh-session-guard 插件。冻结/解冻按钮触发时,尽力调用
|
|
1188
|
-
* `sessionGuard.stopNextTurn` / `resume`;**插件未装**(路由 404 / 网络失败 /
|
|
1189
|
-
* 返回错误)→ 静默跳过,**绝不报错**,前端冻结仍正常生效。
|
|
1190
|
-
*
|
|
1191
|
-
* 不吸收任何 auto-continue / 重试逻辑(重试归后端 dsh-session-guard,D9)。
|
|
1192
|
-
*/
|
|
1193
|
-
/** 尽力调用 sessionGuard.stopNextTurn(停掉 session 下一回合)。失败静默。 */
|
|
1194
|
-
async function sessionGuardStopNextTurn(sessionId) {
|
|
1195
|
-
return callGuard(sessionId, "stopNextTurn");
|
|
1196
|
-
}
|
|
1197
|
-
/** 尽力调用 sessionGuard.resume。失败静默。 */
|
|
1198
|
-
async function sessionGuardResume(sessionId) {
|
|
1199
|
-
return callGuard(sessionId, "resume");
|
|
1200
|
-
}
|
|
1201
|
-
async function callGuard(sessionId, action) {
|
|
1202
|
-
try {
|
|
1203
|
-
const res = await fetch("/session-guard/rpc", {
|
|
1204
|
-
method: "POST",
|
|
1205
|
-
headers: { "content-type": "application/json" },
|
|
1206
|
-
body: JSON.stringify({
|
|
1207
|
-
sessionId,
|
|
1208
|
-
action
|
|
1209
|
-
})
|
|
1210
|
-
});
|
|
1211
|
-
if (!res.ok) return false;
|
|
1212
|
-
return (await res.json().catch(() => null))?.ok === true;
|
|
1213
|
-
} catch {
|
|
1214
|
-
return false;
|
|
1215
|
-
}
|
|
1216
|
-
}
|
|
1217
|
-
try {
|
|
1218
|
-
globalThis.__DSH_SESSION_GUARD_BRIDGE__ = true;
|
|
1219
|
-
} catch {}
|
|
1220
|
-
//#endregion
|
|
1221
|
-
//#region \0dsh-css:E:\test\rewrite-agently\mine-dsh-plugins\dsh-input-traffic\src\client\freeze-button.module.css.mjs
|
|
1222
|
-
const css = ".BUSMDW_freeze{border:1px solid var(--dsw-alias-border-l3,#0000001f);height:24px;color:var(--dsw-alias-label-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}.BUSMDW_freeze:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#0000000a)}.BUSMDW_freeze[aria-pressed=true]{border-color:var(--dsw-alias-state-success-primary,#30a46c);color:var(--dsw-alias-state-success-primary,#30a46c);background:color-mix(in srgb, var(--dsw-alias-state-success-primary,#30a46c) 8%, transparent)}.BUSMDW_label{font-size:12px}";
|
|
1223
|
-
const tagId = "dsh-input-traffic/freeze-button.module.css";
|
|
1224
|
-
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
1225
|
-
const tag = document.createElement("style");
|
|
1226
|
-
tag.dataset.plugin = "dsh-input-traffic";
|
|
1227
|
-
tag.dataset.pluginCss = tagId;
|
|
1228
|
-
tag.textContent = css;
|
|
1229
|
-
document.head.appendChild(tag);
|
|
1230
|
-
}
|
|
1231
|
-
var freeze_button_module_css_default = {
|
|
1232
|
-
"label": "BUSMDW_label",
|
|
1233
|
-
"freeze": "BUSMDW_freeze"
|
|
1234
|
-
};
|
|
1235
|
-
//#endregion
|
|
1236
|
-
//#region src/client/freeze-button.tsx
|
|
1237
|
-
/**
|
|
1238
|
-
* Session freeze/resume control, mounted in the composer's right tool row
|
|
1239
|
-
* (`conversation.input.right`) so it is reachable before anything queues.
|
|
1240
|
-
*
|
|
1241
|
-
* Peak-hour pause semantics: freeze does NOT interrupt the running turn —it
|
|
1242
|
-
* finishes naturally. The queued messages are detached (removed, preserved in
|
|
1243
|
-
* the shared store), so the driver finds no pending work and stops after the
|
|
1244
|
-
* current turn. Resume re-submits the preserved texts, waking the driver and
|
|
1245
|
-
* continuing the queue.
|
|
1246
|
-
*
|
|
1247
|
-
* dsh-session-guard 协作(D8 fail-open):
|
|
1248
|
-
* - 冻结 = 前端摘队列(本插件)+ composer block(阻止新输入漏进对话) + 尽力调服务端
|
|
1249
|
-
* sessionGuard.stopNextTurn(停掉 session 下一回合;session-guard 未装时静默跳过)。
|
|
1250
|
-
* - 解冻 = 清 composer block + **先** await sessionGuard.resume(让被打断回合的自然
|
|
1251
|
-
* 下一步先发生),**再**重投队列(later 级条目因此排在自然 next turn 之后,不再插队)。
|
|
1252
|
-
* - 本插件只做冻结增强,不承担重试/暂停决策(归后端,D9)。
|
|
1253
|
-
*/
|
|
1254
|
-
/**
|
|
1255
|
-
* Freeze/resume toggle for the peak-hour scenario.
|
|
1256
|
-
* @param props - slot props; the session snapshot drives the detach list.
|
|
1257
|
-
*/
|
|
1258
|
-
function FreezeButton({ useSession, updateQueue, cancel, send, sendSteer, sessionId, setComposerBlock, notify, t }) {
|
|
1259
|
-
const sid = sessionId ?? "";
|
|
1260
|
-
const { frozen } = (0, react.useSyncExternalStore)(freezeStore.subscribe, () => freezeStore.getSnapshot(sid));
|
|
1261
|
-
const queue = useSession((s) => s.queue);
|
|
1262
|
-
const freeze = async () => {
|
|
1263
|
-
const rows = queue.filter((row) => row.placement === "queued" || row.placement === "steering");
|
|
1264
|
-
const pending = rows.flatMap((row) => row.text === null ? [] : [{
|
|
1265
|
-
text: row.text,
|
|
1266
|
-
tier: row.placement === "steering" ? "safe_point" : "queue"
|
|
1267
|
-
}]);
|
|
1268
|
-
await Promise.all(rows.map((row) => updateQueue(row.id, { kind: "remove" }).catch(() => void 0)));
|
|
1269
|
-
freezeStore.set(sid, {
|
|
1270
|
-
frozen: true,
|
|
1271
|
-
pending
|
|
1272
|
-
});
|
|
1273
|
-
setComposerBlock?.(t("steer.frozenInput"));
|
|
1274
|
-
if (sessionId !== void 0) sessionGuardStopNextTurn(sessionId);
|
|
1275
|
-
};
|
|
1276
|
-
const resume = async () => {
|
|
1277
|
-
const pending = freezeStore.getSnapshot(sid).pending;
|
|
1278
|
-
freezeStore.set(sid, {
|
|
1279
|
-
frozen: false,
|
|
1280
|
-
pending: []
|
|
1281
|
-
});
|
|
1282
|
-
setComposerBlock?.(void 0);
|
|
1283
|
-
try {
|
|
1284
|
-
if (sessionId !== void 0) await sessionGuardResume(sessionId);
|
|
1285
|
-
for (const entry of pending) {
|
|
1286
|
-
if (entry.tier === "force") await cancel();
|
|
1287
|
-
if (entry.tier === "safe_point" && sendSteer !== void 0) await sendSteer(entry.text);
|
|
1288
|
-
else await send(entry.text);
|
|
1289
|
-
}
|
|
1290
|
-
} catch {
|
|
1291
|
-
notify("error", t("steer.resumeFailed"));
|
|
1292
|
-
}
|
|
1293
|
-
};
|
|
1294
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1295
|
-
type: "button",
|
|
1296
|
-
className: freeze_button_module_css_default.freeze,
|
|
1297
|
-
"aria-label": frozen ? t("steer.resume") : t("steer.freeze"),
|
|
1298
|
-
"aria-pressed": frozen || void 0,
|
|
1299
|
-
title: frozen ? t("steer.frozen") : void 0,
|
|
1300
|
-
onClick: () => {
|
|
1301
|
-
if (frozen) resume();
|
|
1302
|
-
else freeze();
|
|
1303
|
-
},
|
|
1304
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1305
|
-
className: freeze_button_module_css_default.label,
|
|
1306
|
-
children: frozen ? t("steer.resume") : t("steer.freeze")
|
|
1307
|
-
})
|
|
1308
|
-
});
|
|
1309
|
-
}
|
|
1310
|
-
//#endregion
|
|
1311
|
-
//#region src/client/hide-enter-row.tsx
|
|
1312
|
-
/** Render nothing: the official busy-Enter row is hidden while mounted. */
|
|
1313
|
-
function HideEnterRow(_props) {
|
|
1314
|
-
return null;
|
|
1315
|
-
}
|
|
1316
|
-
//#endregion
|
|
1317
|
-
//#region src/client/index.ts
|
|
1318
|
-
/** Durable conversation settings namespace owned by ui-conversation. */
|
|
1319
|
-
const CONVERSATION_SETTINGS_NAMESPACE = "ui-conversation";
|
|
1320
|
-
/** Busy-Enter field inside that namespace; the plugin pins it to queue. */
|
|
1321
|
-
const BUSY_ENTER_FIELD = "busyEnter";
|
|
1322
|
-
/**
|
|
1323
|
-
* Display order of the queue strip inside `conversation.input.dock`.
|
|
1324
|
-
*
|
|
1325
|
-
* List rows render sorted by `order` ascending, so a value above every other
|
|
1326
|
-
* contributor keeps the strip as the bottom-most entry of the band — directly
|
|
1327
|
-
* on top of the composer card. Known contributors in both 0.1.1-rc.2 and
|
|
1328
|
-
* 0.1.2-rc.1: `todo` 0, `goal` 10, official `queue` 20, `dsh-perm-gate.notice`
|
|
1329
|
-
* 30. DSH has no "last" slot semantics, so this is a convention, not a
|
|
1330
|
-
* structural guarantee: a third party registering a larger value could still
|
|
1331
|
-
* land below us.
|
|
1332
|
-
*/
|
|
1333
|
-
const QUEUE_DOCK_ORDER = 1e3;
|
|
1334
|
-
/**
|
|
1335
|
-
* Deliver one plain-text message into the session's next step. The exposed
|
|
1336
|
-
* conversation `send` verb only queues into the next turn, so resume steers
|
|
1337
|
-
* through the session face's steer-mode prompt instead (no harness change).
|
|
1338
|
-
* @param ctx - root context (resolves the session face behind the scope).
|
|
1339
|
-
* @param actx - agent-scoped context of the owning session.
|
|
1340
|
-
* @param text - message text to deliver.
|
|
1341
|
-
*/
|
|
1342
|
-
function steerPrompt(actx, text) {
|
|
1343
|
-
const conversation = actx.get("conversation");
|
|
1344
|
-
if (conversation === void 0) return Promise.reject(/* @__PURE__ */ new Error("steer resume: conversation service unavailable"));
|
|
1345
|
-
return conversation.send(text);
|
|
1346
|
-
}
|
|
1347
|
-
/** Services required by the browser half. */
|
|
1348
|
-
const inject = [
|
|
1349
|
-
"slots",
|
|
1350
|
-
"locale",
|
|
1351
|
-
"sessions",
|
|
1352
|
-
"conversation",
|
|
1353
|
-
"settingsScope"
|
|
1354
|
-
];
|
|
1355
|
-
/**
|
|
1356
|
-
* Client plugin body: dictionaries, busy-Enter pinning, and the two slot
|
|
1357
|
-
* shadowings.
|
|
1358
|
-
* @param ctx - client root context.
|
|
1359
|
-
*/
|
|
1360
|
-
function apply(ctx) {
|
|
1361
|
-
ctx.effect(() => ctx.locale.register(NS, {
|
|
1362
|
-
zh,
|
|
1363
|
-
en
|
|
1364
|
-
}), "dsh-input-traffic: dictionaries");
|
|
1365
|
-
ctx.settingsScope.bind({ namespace: CONVERSATION_SETTINGS_NAMESPACE }).set(BUSY_ENTER_FIELD, "queue");
|
|
1366
|
-
ctx.slots.inject("conversation.input.dock", () => ctx.slots.register({
|
|
1367
|
-
name: "conversation.input.dock",
|
|
1368
|
-
id: "queue",
|
|
1369
|
-
order: QUEUE_DOCK_ORDER,
|
|
1370
|
-
priority: -1,
|
|
1371
|
-
locale: NS,
|
|
1372
|
-
inject: (sessionId) => {
|
|
1373
|
-
const actx = ctx.sessions.scope(sessionId);
|
|
1374
|
-
if (actx === void 0) throw new Error(`steer dock: session "${sessionId}" resolved no scope`);
|
|
1375
|
-
const conversation = actx.get("conversation");
|
|
1376
|
-
if (conversation === void 0) throw new Error("steer dock: conversation service unavailable");
|
|
1377
|
-
return {
|
|
1378
|
-
updateQueue: (itemId, action) => conversation.updateQueue(itemId, action),
|
|
1379
|
-
cancel: () => conversation.cancel(),
|
|
1380
|
-
send: (text) => conversation.send(text),
|
|
1381
|
-
setDraft: (text) => {
|
|
1382
|
-
conversation.input.for(actx).actions.setDraft(text);
|
|
1383
|
-
},
|
|
1384
|
-
notify: (level, text) => {
|
|
1385
|
-
conversation.input.for(actx).notify(level, text);
|
|
1386
|
-
}
|
|
1387
|
-
};
|
|
1388
|
-
}
|
|
1389
|
-
}, SteerQueueDock));
|
|
1390
|
-
ctx.slots.inject("conversation.input.right", () => ctx.slots.register({
|
|
1391
|
-
name: "conversation.input.right",
|
|
1392
|
-
id: "steer-freeze",
|
|
1393
|
-
order: 30,
|
|
1394
|
-
locale: NS,
|
|
1395
|
-
inject: (sessionId) => {
|
|
1396
|
-
const actx = ctx.sessions.scope(sessionId);
|
|
1397
|
-
if (actx === void 0) throw new Error(`steer freeze: session "${sessionId}" resolved no scope`);
|
|
1398
|
-
const conversation = actx.get("conversation");
|
|
1399
|
-
if (conversation === void 0) throw new Error("steer freeze: conversation service unavailable");
|
|
1400
|
-
return {
|
|
1401
|
-
updateQueue: (itemId, action) => conversation.updateQueue(itemId, action),
|
|
1402
|
-
cancel: () => conversation.cancel(),
|
|
1403
|
-
send: (text) => conversation.send(text),
|
|
1404
|
-
sendSteer: (text) => steerPrompt(actx, text),
|
|
1405
|
-
sessionId: String(sessionId),
|
|
1406
|
-
setDraft: (text) => {
|
|
1407
|
-
conversation.input.for(actx).actions.setDraft(text);
|
|
1408
|
-
},
|
|
1409
|
-
notify: (level, text) => {
|
|
1410
|
-
conversation.input.for(actx).notify(level, text);
|
|
1411
|
-
},
|
|
1412
|
-
setComposerBlock: (reason) => {
|
|
1413
|
-
conversation.blocks.set(sessionId, reason === void 0 ? void 0 : { reason });
|
|
1414
|
-
}
|
|
1415
|
-
};
|
|
1416
|
-
}
|
|
1417
|
-
}, FreezeButton));
|
|
1418
|
-
ctx.slots.inject("settings.general.item", () => ctx.slots.register({
|
|
1419
|
-
name: "settings.general.item",
|
|
1420
|
-
id: "composer-enter",
|
|
1421
|
-
order: 20,
|
|
1422
|
-
priority: -1,
|
|
1423
|
-
locale: NS
|
|
1424
|
-
}, HideEnterRow));
|
|
1425
|
-
}
|
|
1426
|
-
//#endregion
|
|
1427
|
-
exports.QUEUE_DOCK_ORDER = QUEUE_DOCK_ORDER;
|
|
1428
|
-
exports.apply = apply;
|
|
1429
|
-
exports.inject = inject;
|
|
1430
|
-
return module.exports;
|
|
1431
|
-
}
|
|
1432
|
-
});
|
|
1433
|
-
|
|
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` client dictionaries (zh / en / ja / ko). */
|
|
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.reorderStale": "队列已变化,本次排序已取消,请重试。",
|
|
44
|
+
"steer.dragReorder": "拖动调整顺序",
|
|
45
|
+
"steer.pullBack": "打回输入框编辑",
|
|
46
|
+
"steer.pullBack.unsupported": "包含非文本内容,暂不支持打回编辑",
|
|
47
|
+
"steer.pullBack.composerBusy": "主输入框已有内容,请先发送或清空后再打回",
|
|
48
|
+
"steer.pullBackFailed": "打回输入框失败,请重试。",
|
|
49
|
+
"steer.badge.now": "打断中",
|
|
50
|
+
"steer.badge.next": "插话中",
|
|
51
|
+
"steer.badge.later": "排队",
|
|
52
|
+
"steer.unavailable.running": "仅运行中可规划",
|
|
53
|
+
"steer.clear": "取消并清空",
|
|
54
|
+
"steer.clear.confirm": "确认清空?",
|
|
55
|
+
"steer.clear.cancel": "取消清空",
|
|
56
|
+
"steer.clear.aria": "取消当前执行并清空全部排队消息",
|
|
57
|
+
"steer.clearFailed": "取消并清空失败,请重试。",
|
|
58
|
+
"steer.freeze": "冻结追加",
|
|
59
|
+
"steer.resume": "恢复追加",
|
|
60
|
+
"steer.frozen": "已冻结:当前轮次完成后暂停,排队消息将在恢复后继续",
|
|
61
|
+
"steer.frozenInput": "已冻结:输入已暂停,恢复后继续",
|
|
62
|
+
"steer.frozenBadge": "已冻结",
|
|
63
|
+
"steer.freezeFailed": "冻结失败,请重试。",
|
|
64
|
+
"steer.resumeFailed": "恢复失败,请重试。"
|
|
65
|
+
};
|
|
66
|
+
/** English dictionary (keys mirror zh). */
|
|
67
|
+
const en = {
|
|
68
|
+
"queue.count": "{n} queued messages",
|
|
69
|
+
"queue.edit": "Edit queued message",
|
|
70
|
+
"queue.edit.unsupported": "Contains non-text content; editing is unsupported",
|
|
71
|
+
"queue.save": "Save queued message",
|
|
72
|
+
"queue.cancelEdit": "Cancel edit",
|
|
73
|
+
"queue.remove": "Remove queued message",
|
|
74
|
+
"queue.removeFailed": "Remove failed: the message may have started sending.",
|
|
75
|
+
"queue.editFailed": "Edit failed: the message may have started sending.",
|
|
76
|
+
"queue.editFailed.pulledBack": "Edit failed; the content was moved back to the composer, please review and resend.",
|
|
77
|
+
"steer.inflight": "{n} inserting",
|
|
78
|
+
"steer.now": "Interrupt and send",
|
|
79
|
+
"steer.now.aria": "Red: interrupt the running action and send now",
|
|
80
|
+
"steer.now.unsupported": "Contains non-text content; interrupting is unsupported",
|
|
81
|
+
"steer.nowFailed": "Interrupt failed, please retry.",
|
|
82
|
+
"steer.next": "Send after action",
|
|
83
|
+
"steer.next.aria": "Yellow: insert after the current action finishes",
|
|
84
|
+
"steer.nextFailed": "Steering failed, please retry.",
|
|
85
|
+
"steer.later": "Queue for next turn",
|
|
86
|
+
"steer.later.aria": "Green: queue; processed after all previously steered actions finish",
|
|
87
|
+
"steer.revoke": "Pull back to queue",
|
|
88
|
+
"steer.revoke.aria": "Green: revoke the insertion and queue for the next turn",
|
|
89
|
+
"steer.revoke.unsupported": "Contains non-text content; revoking is unsupported",
|
|
90
|
+
"steer.revokeFailed": "Revoke failed, please retry.",
|
|
91
|
+
"steer.moveUp": "Move up",
|
|
92
|
+
"steer.moveDown": "Move down",
|
|
93
|
+
"steer.reorder.unsupported": "Contains non-text content; reordering is unsupported",
|
|
94
|
+
"steer.reorderFailed": "Reorder failed, please retry.",
|
|
95
|
+
"steer.reorderStale": "The queue changed; this reorder was cancelled, please retry.",
|
|
96
|
+
"steer.dragReorder": "Drag to reorder",
|
|
97
|
+
"steer.pullBack": "Edit in composer",
|
|
98
|
+
"steer.pullBack.unsupported": "Contains non-text content; pulling back is unsupported",
|
|
99
|
+
"steer.pullBack.composerBusy": "The composer already has content; send or clear it first",
|
|
100
|
+
"steer.pullBackFailed": "Pull back failed, please retry.",
|
|
101
|
+
"steer.badge.now": "interrupting",
|
|
102
|
+
"steer.badge.next": "steering",
|
|
103
|
+
"steer.badge.later": "queued",
|
|
104
|
+
"steer.unavailable.running": "Planning requires a running session",
|
|
105
|
+
"steer.clear": "Cancel and clear",
|
|
106
|
+
"steer.clear.confirm": "Confirm clear?",
|
|
107
|
+
"steer.clear.cancel": "Cancel clearing",
|
|
108
|
+
"steer.clear.aria": "Cancel the current run and clear all queued messages",
|
|
109
|
+
"steer.clearFailed": "Cancel and clear failed, please retry.",
|
|
110
|
+
"steer.freeze": "Freeze & append",
|
|
111
|
+
"steer.resume": "Resume & append",
|
|
112
|
+
"steer.frozen": "Frozen: the current turn finishes, then the queue pauses until resumed",
|
|
113
|
+
"steer.frozenInput": "Frozen: input paused, resume to continue",
|
|
114
|
+
"steer.frozenBadge": "frozen",
|
|
115
|
+
"steer.freezeFailed": "Freeze failed, please retry.",
|
|
116
|
+
"steer.resumeFailed": "Resume failed, please retry."
|
|
117
|
+
};
|
|
118
|
+
//#endregion
|
|
119
|
+
//#region src/client/freeze-store.ts
|
|
120
|
+
/** Session id → per-session freeze state. */
|
|
121
|
+
const states = /* @__PURE__ */ new Map();
|
|
122
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
123
|
+
/** Stable empty snapshot: `useSyncExternalStore` needs a reference-stable
|
|
124
|
+
* value for unset sessions so unchanged consumers never re-render. */
|
|
125
|
+
const EMPTY = {
|
|
126
|
+
frozen: false,
|
|
127
|
+
pending: []
|
|
128
|
+
};
|
|
129
|
+
/** Minimal snapshot store (no runtime dependency, stable identity per mount). */
|
|
130
|
+
const freezeStore = {
|
|
131
|
+
/** Snapshot for one session; reference-stable until that session changes. */
|
|
132
|
+
getSnapshot(sessionId) {
|
|
133
|
+
return states.get(sessionId) ?? EMPTY;
|
|
134
|
+
},
|
|
135
|
+
subscribe(listener) {
|
|
136
|
+
listeners.add(listener);
|
|
137
|
+
return () => {
|
|
138
|
+
listeners.delete(listener);
|
|
139
|
+
};
|
|
140
|
+
},
|
|
141
|
+
/** Replace one session's state; unset sessions fall back to EMPTY. */
|
|
142
|
+
set(sessionId, next) {
|
|
143
|
+
if (next.frozen === false && next.pending.length === 0) states.delete(sessionId);
|
|
144
|
+
else states.set(sessionId, next);
|
|
145
|
+
emit();
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
/** Edit one detached queued message's text in place. */
|
|
149
|
+
function updatePendingAt(sessionId, index, text) {
|
|
150
|
+
const pending = states.get(sessionId)?.pending;
|
|
151
|
+
if (pending === void 0) return;
|
|
152
|
+
const entry = pending[index];
|
|
153
|
+
if (entry === void 0) return;
|
|
154
|
+
const next = [...pending];
|
|
155
|
+
next[index] = {
|
|
156
|
+
...entry,
|
|
157
|
+
text
|
|
158
|
+
};
|
|
159
|
+
freezeStore.set(sessionId, {
|
|
160
|
+
frozen: true,
|
|
161
|
+
pending: next
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
/** Change one detached queued message's planned insertion tier. */
|
|
165
|
+
function setTierAt(sessionId, index, tier) {
|
|
166
|
+
const pending = states.get(sessionId)?.pending;
|
|
167
|
+
if (pending === void 0) return;
|
|
168
|
+
const entry = pending[index];
|
|
169
|
+
if (entry === void 0) return;
|
|
170
|
+
const next = [...pending];
|
|
171
|
+
next[index] = {
|
|
172
|
+
...entry,
|
|
173
|
+
tier
|
|
174
|
+
};
|
|
175
|
+
freezeStore.set(sessionId, {
|
|
176
|
+
frozen: true,
|
|
177
|
+
pending: next
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
/** Remove one detached queued message. */
|
|
181
|
+
function removePendingAt(sessionId, index) {
|
|
182
|
+
const pending = states.get(sessionId)?.pending;
|
|
183
|
+
if (pending === void 0) return;
|
|
184
|
+
freezeStore.set(sessionId, {
|
|
185
|
+
frozen: true,
|
|
186
|
+
pending: pending.filter((_, i) => i !== index)
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
/** Move one detached queued message to a new position (reorder while frozen). */
|
|
190
|
+
function movePending(sessionId, from, to) {
|
|
191
|
+
if (from === to) return;
|
|
192
|
+
const pending = states.get(sessionId)?.pending;
|
|
193
|
+
if (pending === void 0) return;
|
|
194
|
+
const next = [...pending];
|
|
195
|
+
const [moved] = next.splice(from, 1);
|
|
196
|
+
if (moved === void 0) return;
|
|
197
|
+
next.splice(to, 0, moved);
|
|
198
|
+
freezeStore.set(sessionId, {
|
|
199
|
+
frozen: true,
|
|
200
|
+
pending: next
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
function emit() {
|
|
204
|
+
for (const listener of listeners) listener();
|
|
205
|
+
}
|
|
206
|
+
//#endregion
|
|
207
|
+
//#region \0dsh-css:E:\test\rewrite-agently\mine-dsh-plugins\dsh-input-traffic\src\client\steer-queue-dock.module.css.mjs
|
|
208
|
+
const css$1 = ".FNESVa_dock{width:100%}.FNESVa_panel{background:var(--dsw-alias-bg-layer-1,#fffc);border:1px solid var(--dsw-alias-border-l2,#00000014);border-radius:10px;overflow:hidden}.FNESVa_toolbar{border-bottom:1px solid var(--dsw-alias-border-l2,#0000000f);align-items:center;gap:8px;padding:4px 8px;display:flex}.FNESVa_header{color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;background:0 0;border:0;align-items:center;gap:6px;padding:2px 6px;font-size:12px;display:inline-flex}.FNESVa_header:disabled{cursor:default}.FNESVa_lead{color:var(--dsw-alias-label-tertiary,#9ca3af);display:inline-flex}.FNESVa_count{font-weight:600}.FNESVa_chevron{color:var(--dsw-alias-label-tertiary,#9ca3af);display:inline-flex}.FNESVa_toolbarActions{margin-left:auto}.FNESVa_clear{color:var(--dsw-alias-label-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}.FNESVa_freeze{border:1px solid var(--dsw-alias-border-l3,#0000001f);color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;background:0 0;border-radius:6px;align-items:center;gap:4px;padding:3px 8px;font-size:12px;display:inline-flex}.FNESVa_freeze[aria-pressed=true]{border-color:var(--dsw-alias-state-success-primary,#30a46c);color:var(--dsw-alias-state-success-primary,#30a46c)}.FNESVa_freeze:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#0000000a)}.FNESVa_freeze:disabled{opacity:.5;cursor:default}.FNESVa_frozenBanner{border-bottom:1px solid var(--dsw-alias-border-l2,#0000000f);background:color-mix(in srgb, var(--dsw-alias-state-success-primary,#30a46c) 8%, transparent);color:var(--dsw-alias-state-success-primary,#30a46c);padding:4px 10px;font-size:12px}.FNESVa_frozenList{margin:0;padding:4px;list-style:none}.FNESVa_frozenRow{border-radius:6px;align-items:center;gap:8px;padding:4px 6px;display:flex}.FNESVa_frozenMark{color:var(--dsw-alias-label-tertiary,#9ca3af);flex:none;margin-left:auto;font-size:11px}.FNESVa_clear:hover:not(:disabled){border-color:var(--dsw-alias-border-l3,#0000001f);background:var(--dsw-alias-interactive-bg-hover,#0000000a)}.FNESVa_clear:disabled{opacity:.5;cursor:default}.FNESVa_clearConfirm{border-color:var(--dsw-alias-state-error-primary,#e5484d);color:var(--dsw-alias-state-error-primary,#e5484d)}.FNESVa_clearConfirm:hover:not(:disabled){border-color:var(--dsw-alias-state-error-primary,#e5484d);background:color-mix(in srgb, var(--dsw-alias-state-error-primary,#e5484d) 8%, transparent)}.FNESVa_clearCancel{color:var(--dsw-alias-label-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}.FNESVa_clearCancel:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#0000000a)}.FNESVa_clearLabel{font-size:12px}.FNESVa_list{margin:0;padding:4px;list-style:none}.FNESVa_row{border-radius:6px;align-items:center;gap:8px;padding:4px 6px;display:flex}.FNESVa_row[draggable=true]{cursor:grab}.FNESVa_row[draggable=true]:active{cursor:grabbing}.FNESVa_rowDragOver{outline:2px dashed var(--dsw-alias-border-l4,#00000040);outline-offset:-2px;background:var(--dsw-alias-interactive-bg-hover,#0000000d)}.FNESVa_row:hover{background:var(--dsw-alias-interactive-bg-hover,#00000008)}.FNESVa_row[data-editing]{align-items:flex-start}.FNESVa_preview{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary,#1f2937);flex:1;font-size:13px;overflow:hidden}.FNESVa_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}.FNESVa_badgeLabel{color:inherit}.FNESVa_badgeNow{color:var(--dsw-alias-state-error-primary,#e5484d);background:color-mix(in srgb, var(--dsw-alias-state-error-primary,#e5484d) 10%, transparent)}.FNESVa_badgeNext{color:var(--dsw-alias-state-warn-primary,#e8a33d);background:color-mix(in srgb, var(--dsw-alias-state-warn-primary,#e8a33d) 12%, transparent)}.FNESVa_badgeLater{color:var(--dsw-alias-state-success-primary,#30a46c);background:color-mix(in srgb, var(--dsw-alias-state-success-primary,#30a46c) 12%, transparent)}.FNESVa_badgeNow .FNESVa_dot{background:var(--dsw-alias-state-error-primary,#e5484d)}.FNESVa_badgeNext .FNESVa_dot{background:var(--dsw-alias-state-warn-primary,#e8a33d)}.FNESVa_badgeLater .FNESVa_dot{background:var(--dsw-alias-state-success-primary,#30a46c)}.FNESVa_steeringList{margin:0;padding:0 4px 4px;list-style:none}.FNESVa_steeringList .FNESVa_row{opacity:.85}.FNESVa_editor{border:1px solid var(--dsw-alias-border-l3,#00000026);background:var(--dsw-alias-bg-layer-2,#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}.FNESVa_actions{align-items:center;gap:2px;display:inline-flex}.FNESVa_action{width:24px;height:24px;color:var(--dsw-alias-label-tertiary,#9ca3af);cursor:pointer;background:0 0;border:0;border-radius:6px;justify-content:center;align-items:center;display:inline-flex}.FNESVa_action:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#0000000d);color:var(--dsw-alias-label-primary,#1f2937)}.FNESVa_action:disabled{opacity:.4;cursor:default}.FNESVa_plan{border-left:1px solid var(--dsw-alias-border-l2,#00000014);align-items:center;gap:2px;margin-left:4px;padding-left:6px;display:inline-flex}.FNESVa_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}.FNESVa_tier:hover:not(:disabled){border-color:var(--dsw-alias-border-l4,#0003)}.FNESVa_tier:disabled{cursor:default}.FNESVa_tierLater{border-color:var(--dsw-alias-state-success-primary,#30a46c);background:color-mix(in srgb, var(--dsw-alias-state-success-primary,#30a46c) 12%, transparent)}.FNESVa_dot{border-radius:50%;width:10px;height:10px}.FNESVa_tierNow .FNESVa_dot{background:var(--dsw-alias-state-error-primary,#e5484d)}.FNESVa_tierNext .FNESVa_dot{background:var(--dsw-alias-state-warn-primary,#e8a33d)}.FNESVa_tierLater .FNESVa_dot{background:var(--dsw-alias-state-success-primary,#30a46c)}";
|
|
209
|
+
const tagId$1 = "dsh-input-traffic/steer-queue-dock.module.css";
|
|
210
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
211
|
+
const tag = document.createElement("style");
|
|
212
|
+
tag.dataset.plugin = "dsh-input-traffic";
|
|
213
|
+
tag.dataset.pluginCss = tagId$1;
|
|
214
|
+
tag.textContent = css$1;
|
|
215
|
+
document.head.appendChild(tag);
|
|
216
|
+
}
|
|
217
|
+
var steer_queue_dock_module_css_default = {
|
|
218
|
+
"editor": "FNESVa_editor",
|
|
219
|
+
"rowDragOver": "FNESVa_rowDragOver",
|
|
220
|
+
"lead": "FNESVa_lead",
|
|
221
|
+
"count": "FNESVa_count",
|
|
222
|
+
"clear": "FNESVa_clear",
|
|
223
|
+
"list": "FNESVa_list",
|
|
224
|
+
"steeringList": "FNESVa_steeringList",
|
|
225
|
+
"badgeNow": "FNESVa_badgeNow",
|
|
226
|
+
"tierNow": "FNESVa_tierNow",
|
|
227
|
+
"tier": "FNESVa_tier",
|
|
228
|
+
"frozenList": "FNESVa_frozenList",
|
|
229
|
+
"badgeLabel": "FNESVa_badgeLabel",
|
|
230
|
+
"clearCancel": "FNESVa_clearCancel",
|
|
231
|
+
"row": "FNESVa_row",
|
|
232
|
+
"dock": "FNESVa_dock",
|
|
233
|
+
"header": "FNESVa_header",
|
|
234
|
+
"actions": "FNESVa_actions",
|
|
235
|
+
"badgeLater": "FNESVa_badgeLater",
|
|
236
|
+
"chevron": "FNESVa_chevron",
|
|
237
|
+
"tierLater": "FNESVa_tierLater",
|
|
238
|
+
"badgeNext": "FNESVa_badgeNext",
|
|
239
|
+
"panel": "FNESVa_panel",
|
|
240
|
+
"preview": "FNESVa_preview",
|
|
241
|
+
"frozenRow": "FNESVa_frozenRow",
|
|
242
|
+
"tierNext": "FNESVa_tierNext",
|
|
243
|
+
"clearConfirm": "FNESVa_clearConfirm",
|
|
244
|
+
"plan": "FNESVa_plan",
|
|
245
|
+
"toolbarActions": "FNESVa_toolbarActions",
|
|
246
|
+
"frozenMark": "FNESVa_frozenMark",
|
|
247
|
+
"freeze": "FNESVa_freeze",
|
|
248
|
+
"badge": "FNESVa_badge",
|
|
249
|
+
"frozenBanner": "FNESVa_frozenBanner",
|
|
250
|
+
"dot": "FNESVa_dot",
|
|
251
|
+
"clearLabel": "FNESVa_clearLabel",
|
|
252
|
+
"action": "FNESVa_action",
|
|
253
|
+
"toolbar": "FNESVa_toolbar"
|
|
254
|
+
};
|
|
255
|
+
//#endregion
|
|
256
|
+
//#region src/client/steer-queue-dock.tsx
|
|
257
|
+
/**
|
|
258
|
+
* Three-tier steering queue dock: the shadowing replacement for the official
|
|
259
|
+
* `conversation.input.dock` entry (same cell id `queue`, priority -1, so the
|
|
260
|
+
* lower priority wins and the official QueueDock never renders while this
|
|
261
|
+
* plugin is mounted).
|
|
262
|
+
*
|
|
263
|
+
* Every queued row carries three planning buttons:
|
|
264
|
+
* - green (later, the default state): the row already queues for the next
|
|
265
|
+
* turn —plain Enter keeps feeding rows here; pressing green on a row that
|
|
266
|
+
* was already steered (yellow) revokes the insertion and pulls it back to
|
|
267
|
+
* later (the yellow flow is reversible);
|
|
268
|
+
* - yellow (next): strict-steer the row into the running turn, consumed at
|
|
269
|
+
* the next step boundary (after the current action finishes);
|
|
270
|
+
* - red (now): cancel the running turn, then remove the row and re-send its
|
|
271
|
+
* text as a fresh message —the re-send arms the harness wake latch (the
|
|
272
|
+
* converged driver restarts) and the interrupted input is processed as the
|
|
273
|
+
* next turn's input. A plain steer after cancel would re-insert a message
|
|
274
|
+
* that is already pending in next-turn and the inbox rejects it.
|
|
275
|
+
*
|
|
276
|
+
* The toolbar also exposes a queue-level "cancel and clear" that stops the
|
|
277
|
+
* current run and removes every queued row, plus a session-level
|
|
278
|
+
* freeze/resume toggle for the peak-hour scenario: freeze stops the current
|
|
279
|
+
* run while preserving the queue (keepInbox), resume re-arms the driver by
|
|
280
|
+
* re-sending the first queued row so the preserved work continues.
|
|
281
|
+
*/
|
|
282
|
+
/** Busy marker for a whole-queue rebuild (reorder); locks every row action. */
|
|
283
|
+
const REORDER_MARK = "__reorder__";
|
|
284
|
+
/** localStorage key for the manual collapse state (dsh-queue-plus parity). */
|
|
285
|
+
const COLLAPSE_KEY = "dsh-input-traffic:collapsed";
|
|
286
|
+
/**
|
|
287
|
+
* Project the tier badge of one inbox row from its placement. Pure so the
|
|
288
|
+
* row rendering and its tests share one truth.
|
|
289
|
+
* @param placement - the inbox projection placement.
|
|
290
|
+
* @returns the tier badge, or null for non-visible context rows.
|
|
291
|
+
*/
|
|
292
|
+
function badgeFor(placement) {
|
|
293
|
+
if (placement === "steering") return "next";
|
|
294
|
+
if (placement === "queued") return "later";
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Auto-grow one edit textarea to its content. The CSS max-height caps the
|
|
299
|
+
* growth; beyond it the textarea scrolls internally. Height is reset first so
|
|
300
|
+
* shrink (deleting lines) also tracks the content.
|
|
301
|
+
* @param el - the textarea to resize in place.
|
|
302
|
+
*/
|
|
303
|
+
function resizeEditor(el) {
|
|
304
|
+
el.style.height = "auto";
|
|
305
|
+
el.style.height = `${el.scrollHeight}px`;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Queue strip with three-tier planning: one item renders directly; multiple
|
|
309
|
+
* items default to a collapsible count header; an empty queue renders nothing.
|
|
310
|
+
*/
|
|
311
|
+
function SteerQueueDock({ sessionId, useSession, input, updateQueue, cancel, send, setDraft, notify, t }) {
|
|
312
|
+
const inbox = useSession((s) => s.queue);
|
|
313
|
+
const queue = (0, react.useMemo)(() => inbox.filter((row) => row.placement === "queued"), [inbox]);
|
|
314
|
+
const steering = (0, react.useMemo)(() => inbox.filter((row) => row.placement === "steering"), [inbox]);
|
|
315
|
+
const running = useSession((s) => s.running);
|
|
316
|
+
const queueMutable = useSession((s) => s.subagent === null);
|
|
317
|
+
const [editing, setEditing] = (0, react.useState)(null);
|
|
318
|
+
const [busy, setBusy] = (0, react.useState)(null);
|
|
319
|
+
const [clearing, setClearing] = (0, react.useState)(false);
|
|
320
|
+
const [confirmClear, setConfirmClear] = (0, react.useState)(false);
|
|
321
|
+
const confirmTimer = (0, react.useRef)(null);
|
|
322
|
+
const dragIndex = (0, react.useRef)(null);
|
|
323
|
+
const [dragOver, setDragOver] = (0, react.useState)(null);
|
|
324
|
+
const [collapsed, setCollapsed] = (0, react.useState)(() => {
|
|
325
|
+
try {
|
|
326
|
+
const v = localStorage.getItem(COLLAPSE_KEY);
|
|
327
|
+
return v === null ? true : v === "1";
|
|
328
|
+
} catch {
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
const { frozen, pending: frozenPending } = (0, react.useSyncExternalStore)(freezeStore.subscribe, () => freezeStore.getSnapshot(sessionId ?? ""));
|
|
333
|
+
const sid = sessionId ?? "";
|
|
334
|
+
const listId = (0, react.useId)();
|
|
335
|
+
const editorRef = (0, react.useRef)(null);
|
|
336
|
+
(0, react.useEffect)(() => {
|
|
337
|
+
if (queue.length === 0 && !collapsed) setCollapsed(true);
|
|
338
|
+
if (editing !== null && !editing.id.startsWith("frozen:") && (!queueMutable || !queue.some((row) => row.id === editing.id))) setEditing(null);
|
|
339
|
+
}, [
|
|
340
|
+
collapsed,
|
|
341
|
+
editing,
|
|
342
|
+
queue,
|
|
343
|
+
queueMutable
|
|
344
|
+
]);
|
|
345
|
+
(0, react.useEffect)(() => {
|
|
346
|
+
try {
|
|
347
|
+
localStorage.setItem(COLLAPSE_KEY, collapsed ? "1" : "0");
|
|
348
|
+
} catch {}
|
|
349
|
+
}, [collapsed]);
|
|
350
|
+
(0, react.useEffect)(() => () => {
|
|
351
|
+
if (confirmTimer.current !== null) clearTimeout(confirmTimer.current);
|
|
352
|
+
}, []);
|
|
353
|
+
const editingId = editing?.id;
|
|
354
|
+
(0, react.useEffect)(() => {
|
|
355
|
+
if (editingId === void 0) return;
|
|
356
|
+
const el = editorRef.current;
|
|
357
|
+
if (el !== null) resizeEditor(el);
|
|
358
|
+
}, [editingId]);
|
|
359
|
+
if (queue.length === 0 && steering.length === 0 && !running && !frozen) return null;
|
|
360
|
+
const interactionActive = queueMutable && (editing !== null || busy !== null || clearing);
|
|
361
|
+
const expanded = !collapsed || interactionActive;
|
|
362
|
+
const listVisible = queue.length === 1 || expanded;
|
|
363
|
+
const planDisabled = !queueMutable || !running || frozen;
|
|
364
|
+
const nothingPending = queue.length === 0 && steering.length === 0;
|
|
365
|
+
const reorderUnsupported = queue.some((row) => row.text === null);
|
|
366
|
+
const composerEmpty = input.draft.trim() === "";
|
|
367
|
+
const applyAction = async (itemId, action, failure) => {
|
|
368
|
+
setBusy(itemId);
|
|
369
|
+
try {
|
|
370
|
+
await updateQueue(itemId, action);
|
|
371
|
+
return true;
|
|
372
|
+
} catch {
|
|
373
|
+
notify("error", failure);
|
|
374
|
+
return false;
|
|
375
|
+
} finally {
|
|
376
|
+
setBusy((current) => current === itemId ? null : current);
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
const saveEdit = async () => {
|
|
380
|
+
if (editing === null || editing.text.trim() === "") return;
|
|
381
|
+
const itemId = editing.id;
|
|
382
|
+
const text = editing.text;
|
|
383
|
+
setBusy(itemId);
|
|
384
|
+
try {
|
|
385
|
+
await updateQueue(itemId, {
|
|
386
|
+
kind: "edit",
|
|
387
|
+
content: [{
|
|
388
|
+
type: "text",
|
|
389
|
+
text
|
|
390
|
+
}]
|
|
391
|
+
});
|
|
392
|
+
setEditing(null);
|
|
393
|
+
} catch {
|
|
394
|
+
if (input.draft.trim() === "") {
|
|
395
|
+
setDraft(text);
|
|
396
|
+
setEditing(null);
|
|
397
|
+
notify("error", t("queue.editFailed.pulledBack"));
|
|
398
|
+
} else notify("error", t("queue.editFailed"));
|
|
399
|
+
} finally {
|
|
400
|
+
setBusy((current) => current === itemId ? null : current);
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
const steerRow = async (row, tier) => {
|
|
404
|
+
setBusy(row.id);
|
|
405
|
+
try {
|
|
406
|
+
if (tier === "now") {
|
|
407
|
+
await cancel();
|
|
408
|
+
await updateQueue(row.id, { kind: "remove" });
|
|
409
|
+
if (row.text !== null) await send(row.text);
|
|
410
|
+
} else await updateQueue(row.id, { kind: "steer" });
|
|
411
|
+
} catch {
|
|
412
|
+
notify("error", tier === "now" ? t("steer.nowFailed") : t("steer.nextFailed"));
|
|
413
|
+
} finally {
|
|
414
|
+
setBusy((current) => current === row.id ? null : current);
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
/**
|
|
418
|
+
* Revoke a steered (yellow) or interrupting (red) row back to later: remove
|
|
419
|
+
* the admitted row and re-send its text as a queued follow-up, so it lands
|
|
420
|
+
* in next-turn again (the yellow flow is reversible).
|
|
421
|
+
*/
|
|
422
|
+
const revokeToLater = async (row) => {
|
|
423
|
+
if (row.text === null) return;
|
|
424
|
+
setBusy(row.id);
|
|
425
|
+
try {
|
|
426
|
+
await updateQueue(row.id, { kind: "remove" });
|
|
427
|
+
await send(row.text);
|
|
428
|
+
} catch {
|
|
429
|
+
notify("error", t("steer.revokeFailed"));
|
|
430
|
+
} finally {
|
|
431
|
+
setBusy((current) => current === row.id ? null : current);
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
/**
|
|
435
|
+
* Rebuild the queue in a new order: remove every queued row, then re-send
|
|
436
|
+
* the texts sequentially so the agent's next-turn list matches the new
|
|
437
|
+
* order. Concurrency protection (dsh-queue-plus parity): if a remove fails
|
|
438
|
+
* with queue-item-not-found, the agent already claimed that row — the
|
|
439
|
+
* rebuild stops immediately and nothing is re-sent, so the changed queue
|
|
440
|
+
* is never scrambled.
|
|
441
|
+
*/
|
|
442
|
+
const rebuildQueue = async (rows, next) => {
|
|
443
|
+
setBusy(REORDER_MARK);
|
|
444
|
+
try {
|
|
445
|
+
for (const row of rows) try {
|
|
446
|
+
await updateQueue(row.id, { kind: "remove" });
|
|
447
|
+
} catch (error) {
|
|
448
|
+
if (error instanceof Error && error.message.includes("queue-item-not-found")) {
|
|
449
|
+
notify("error", t("steer.reorderStale"));
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
throw error;
|
|
453
|
+
}
|
|
454
|
+
for (const row of next) if (row.text !== null) await send(row.text);
|
|
455
|
+
} catch {
|
|
456
|
+
notify("error", t("steer.reorderFailed"));
|
|
457
|
+
} finally {
|
|
458
|
+
setBusy(null);
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
/**
|
|
462
|
+
* Move one queued row up/down in the FIFO order (arrow buttons).
|
|
463
|
+
*/
|
|
464
|
+
const reorder = async (rowId, delta) => {
|
|
465
|
+
const rows = queue;
|
|
466
|
+
const index = rows.findIndex((row) => row.id === rowId);
|
|
467
|
+
const target = index + delta;
|
|
468
|
+
if (index < 0 || target < 0 || target >= rows.length) return;
|
|
469
|
+
const next = [...rows];
|
|
470
|
+
const swapped = next[index];
|
|
471
|
+
next[index] = next[target];
|
|
472
|
+
next[target] = swapped;
|
|
473
|
+
if (next.some((row) => row.text === null)) {
|
|
474
|
+
notify("error", t("steer.reorder.unsupported"));
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
await rebuildQueue(rows, next);
|
|
478
|
+
};
|
|
479
|
+
/**
|
|
480
|
+
* Move one row to an absolute position (drag-and-drop drop handler).
|
|
481
|
+
*/
|
|
482
|
+
const reorderTo = async (fromIndex, toIndex) => {
|
|
483
|
+
if (fromIndex === toIndex) return;
|
|
484
|
+
const rows = queue;
|
|
485
|
+
if (rows[fromIndex] === void 0 || rows[toIndex] === void 0) return;
|
|
486
|
+
const next = [...rows];
|
|
487
|
+
const [moved] = next.splice(fromIndex, 1);
|
|
488
|
+
if (moved === void 0) return;
|
|
489
|
+
next.splice(toIndex, 0, moved);
|
|
490
|
+
if (next.some((row) => row.text === null)) {
|
|
491
|
+
notify("error", t("steer.reorder.unsupported"));
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
await rebuildQueue(rows, next);
|
|
495
|
+
};
|
|
496
|
+
/** Arm or execute the two-step clear confirmation. */
|
|
497
|
+
const armClear = () => {
|
|
498
|
+
if (confirmClear) {
|
|
499
|
+
if (confirmTimer.current !== null) clearTimeout(confirmTimer.current);
|
|
500
|
+
setConfirmClear(false);
|
|
501
|
+
clearAll();
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
setConfirmClear(true);
|
|
505
|
+
if (confirmTimer.current !== null) clearTimeout(confirmTimer.current);
|
|
506
|
+
confirmTimer.current = setTimeout(() => setConfirmClear(false), 3e3);
|
|
507
|
+
};
|
|
508
|
+
/** Abort the armed clear confirmation (the explicit cancel button). */
|
|
509
|
+
const cancelClear = () => {
|
|
510
|
+
if (confirmTimer.current !== null) clearTimeout(confirmTimer.current);
|
|
511
|
+
setConfirmClear(false);
|
|
512
|
+
};
|
|
513
|
+
/** Save an in-place edit of one detached (frozen) queued message. */
|
|
514
|
+
const saveFrozenEdit = async (index) => {
|
|
515
|
+
if (editing === null) return;
|
|
516
|
+
const text = editing.text.trim();
|
|
517
|
+
if (text === "") {
|
|
518
|
+
setEditing(null);
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
updatePendingAt(sid, index, text);
|
|
522
|
+
setEditing(null);
|
|
523
|
+
};
|
|
524
|
+
/**
|
|
525
|
+
* Pull one queued row back into the composer draft for editing: the text
|
|
526
|
+
* back-fills the input box and the row leaves the queue; the user edits and
|
|
527
|
+
* resubmits it as a fresh message.
|
|
528
|
+
*/
|
|
529
|
+
const pullBackToComposer = async (row) => {
|
|
530
|
+
if (row.text === null) return;
|
|
531
|
+
setDraft(row.text);
|
|
532
|
+
setBusy(row.id);
|
|
533
|
+
try {
|
|
534
|
+
await updateQueue(row.id, { kind: "remove" });
|
|
535
|
+
} catch {
|
|
536
|
+
notify("error", t("steer.pullBackFailed"));
|
|
537
|
+
} finally {
|
|
538
|
+
setBusy((current) => current === row.id ? null : current);
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
const clearAll = async () => {
|
|
542
|
+
setClearing(true);
|
|
543
|
+
const pending = [...queue, ...steering];
|
|
544
|
+
try {
|
|
545
|
+
await cancel();
|
|
546
|
+
await Promise.all(pending.map((row) => updateQueue(row.id, { kind: "remove" }).catch(() => void 0)));
|
|
547
|
+
} catch {
|
|
548
|
+
notify("error", t("steer.clearFailed"));
|
|
549
|
+
} finally {
|
|
550
|
+
setClearing(false);
|
|
551
|
+
}
|
|
552
|
+
};
|
|
553
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
554
|
+
className: steer_queue_dock_module_css_default.dock,
|
|
555
|
+
"data-steer-dock": "",
|
|
556
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
557
|
+
className: steer_queue_dock_module_css_default.panel,
|
|
558
|
+
children: [
|
|
559
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
560
|
+
className: steer_queue_dock_module_css_default.toolbar,
|
|
561
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
562
|
+
type: "button",
|
|
563
|
+
className: steer_queue_dock_module_css_default.header,
|
|
564
|
+
"aria-controls": listId,
|
|
565
|
+
"aria-expanded": expanded,
|
|
566
|
+
disabled: queue.length <= 1 || interactionActive,
|
|
567
|
+
onClick: () => {
|
|
568
|
+
setCollapsed((value) => !value);
|
|
569
|
+
},
|
|
570
|
+
children: [
|
|
571
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
572
|
+
className: steer_queue_dock_module_css_default.lead,
|
|
573
|
+
"aria-hidden": true,
|
|
574
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconQueueOutline14, {})
|
|
575
|
+
}),
|
|
576
|
+
queue.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
577
|
+
className: steer_queue_dock_module_css_default.count,
|
|
578
|
+
children: t("queue.count", { n: queue.length })
|
|
579
|
+
}),
|
|
580
|
+
queue.length > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
581
|
+
className: steer_queue_dock_module_css_default.chevron,
|
|
582
|
+
"aria-hidden": true,
|
|
583
|
+
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, {})
|
|
584
|
+
})
|
|
585
|
+
]
|
|
586
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
587
|
+
className: steer_queue_dock_module_css_default.toolbarActions,
|
|
588
|
+
children: [confirmClear && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
589
|
+
type: "button",
|
|
590
|
+
className: steer_queue_dock_module_css_default.clearCancel,
|
|
591
|
+
"aria-label": t("steer.clear.cancel"),
|
|
592
|
+
disabled: clearing || busy !== null || nothingPending,
|
|
593
|
+
onClick: cancelClear,
|
|
594
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
595
|
+
className: steer_queue_dock_module_css_default.clearLabel,
|
|
596
|
+
children: t("steer.clear.cancel")
|
|
597
|
+
})
|
|
598
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
599
|
+
label: confirmClear ? t("steer.clear.confirm") : t("steer.clear"),
|
|
600
|
+
side: "top",
|
|
601
|
+
delayMs: 500,
|
|
602
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
603
|
+
type: "button",
|
|
604
|
+
className: `${steer_queue_dock_module_css_default.clear} ${confirmClear ? steer_queue_dock_module_css_default.clearConfirm : ""}`,
|
|
605
|
+
"aria-label": confirmClear ? t("steer.clear.confirm") : t("steer.clear"),
|
|
606
|
+
disabled: clearing || busy !== null || nothingPending,
|
|
607
|
+
onClick: armClear,
|
|
608
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, { size: 14 }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
609
|
+
className: steer_queue_dock_module_css_default.clearLabel,
|
|
610
|
+
children: confirmClear ? t("steer.clear.confirm") : t("steer.clear")
|
|
611
|
+
})]
|
|
612
|
+
})
|
|
613
|
+
})]
|
|
614
|
+
})]
|
|
615
|
+
}),
|
|
616
|
+
frozen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
617
|
+
className: steer_queue_dock_module_css_default.frozenBanner,
|
|
618
|
+
role: "status",
|
|
619
|
+
children: t("steer.frozen")
|
|
620
|
+
}),
|
|
621
|
+
frozen && frozenPending.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
622
|
+
className: steer_queue_dock_module_css_default.frozenList,
|
|
623
|
+
"data-testid": "frozen-list",
|
|
624
|
+
children: frozenPending.map((entry, i) => {
|
|
625
|
+
const editingFrozen = editing?.id === `frozen:${i}`;
|
|
626
|
+
const text = entry.text;
|
|
627
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
628
|
+
className: `${steer_queue_dock_module_css_default.frozenRow} ${dragOver === i && frozen ? steer_queue_dock_module_css_default.rowDragOver : ""}`,
|
|
629
|
+
"data-tier": entry.tier === "force" ? "now" : entry.tier === "safe_point" ? "next" : "later",
|
|
630
|
+
"data-editing": editingFrozen ? "" : void 0,
|
|
631
|
+
draggable: !editingFrozen,
|
|
632
|
+
title: !editingFrozen ? t("steer.dragReorder") : void 0,
|
|
633
|
+
onDragStart: (event) => {
|
|
634
|
+
if (editingFrozen) return;
|
|
635
|
+
dragIndex.current = i;
|
|
636
|
+
event.dataTransfer.effectAllowed = "move";
|
|
637
|
+
event.dataTransfer.setData("text/plain", String(i));
|
|
638
|
+
},
|
|
639
|
+
onDragOver: (event) => {
|
|
640
|
+
if (dragIndex.current === null) return;
|
|
641
|
+
event.preventDefault();
|
|
642
|
+
setDragOver(i);
|
|
643
|
+
},
|
|
644
|
+
onDrop: (event) => {
|
|
645
|
+
event.preventDefault();
|
|
646
|
+
const from = dragIndex.current;
|
|
647
|
+
dragIndex.current = null;
|
|
648
|
+
setDragOver(null);
|
|
649
|
+
if (from !== null) movePending(sid, from, i);
|
|
650
|
+
},
|
|
651
|
+
onDragEnd: () => {
|
|
652
|
+
dragIndex.current = null;
|
|
653
|
+
setDragOver(null);
|
|
654
|
+
},
|
|
655
|
+
children: [
|
|
656
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
657
|
+
className: steer_queue_dock_module_css_default.lead,
|
|
658
|
+
"aria-hidden": true,
|
|
659
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconQueueOutline14, {})
|
|
660
|
+
}),
|
|
661
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SteerBadge, {
|
|
662
|
+
tier: entry.tier === "force" ? "now" : entry.tier === "safe_point" ? "next" : "later",
|
|
663
|
+
t
|
|
664
|
+
}),
|
|
665
|
+
editingFrozen ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
666
|
+
ref: editorRef,
|
|
667
|
+
autoFocus: true,
|
|
668
|
+
className: steer_queue_dock_module_css_default.editor,
|
|
669
|
+
"aria-label": t("queue.edit"),
|
|
670
|
+
rows: 1,
|
|
671
|
+
value: editing?.text ?? text,
|
|
672
|
+
onChange: (event) => {
|
|
673
|
+
setEditing({
|
|
674
|
+
id: `frozen:${i}`,
|
|
675
|
+
text: event.currentTarget.value
|
|
676
|
+
});
|
|
677
|
+
},
|
|
678
|
+
onInput: (event) => {
|
|
679
|
+
resizeEditor(event.currentTarget);
|
|
680
|
+
},
|
|
681
|
+
onKeyDown: (event) => {
|
|
682
|
+
if (event.key === "Escape") {
|
|
683
|
+
setEditing(null);
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing) {
|
|
687
|
+
event.preventDefault();
|
|
688
|
+
saveFrozenEdit(i);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
692
|
+
className: steer_queue_dock_module_css_default.preview,
|
|
693
|
+
children: text
|
|
694
|
+
}),
|
|
695
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
696
|
+
className: steer_queue_dock_module_css_default.actions,
|
|
697
|
+
children: editingFrozen ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
698
|
+
label: t("queue.save"),
|
|
699
|
+
side: "bottom",
|
|
700
|
+
delayMs: 500,
|
|
701
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
702
|
+
type: "button",
|
|
703
|
+
className: steer_queue_dock_module_css_default.action,
|
|
704
|
+
"aria-label": t("queue.save"),
|
|
705
|
+
disabled: editing === null || editing.text.trim() === "",
|
|
706
|
+
onClick: () => {
|
|
707
|
+
saveFrozenEdit(i);
|
|
708
|
+
},
|
|
709
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCheckOutline16, { size: 14 })
|
|
710
|
+
})
|
|
711
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
712
|
+
label: t("queue.cancelEdit"),
|
|
713
|
+
side: "bottom",
|
|
714
|
+
delayMs: 500,
|
|
715
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
716
|
+
type: "button",
|
|
717
|
+
className: steer_queue_dock_module_css_default.action,
|
|
718
|
+
"aria-label": t("queue.cancelEdit"),
|
|
719
|
+
onClick: () => {
|
|
720
|
+
setEditing(null);
|
|
721
|
+
},
|
|
722
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 14 })
|
|
723
|
+
})
|
|
724
|
+
})] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
725
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
726
|
+
label: t("steer.moveUp"),
|
|
727
|
+
side: "bottom",
|
|
728
|
+
delayMs: 500,
|
|
729
|
+
disabled: i === 0,
|
|
730
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
731
|
+
type: "button",
|
|
732
|
+
className: steer_queue_dock_module_css_default.action,
|
|
733
|
+
"aria-label": t("steer.moveUp"),
|
|
734
|
+
disabled: i === 0,
|
|
735
|
+
onClick: () => movePending(sid, i, i - 1),
|
|
736
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, {})
|
|
737
|
+
})
|
|
738
|
+
}),
|
|
739
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
740
|
+
label: t("steer.moveDown"),
|
|
741
|
+
side: "bottom",
|
|
742
|
+
delayMs: 500,
|
|
743
|
+
disabled: i === frozenPending.length - 1,
|
|
744
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
745
|
+
type: "button",
|
|
746
|
+
className: steer_queue_dock_module_css_default.action,
|
|
747
|
+
"aria-label": t("steer.moveDown"),
|
|
748
|
+
disabled: i === frozenPending.length - 1,
|
|
749
|
+
onClick: () => movePending(sid, i, i + 1),
|
|
750
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {})
|
|
751
|
+
})
|
|
752
|
+
}),
|
|
753
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
754
|
+
label: t("queue.edit"),
|
|
755
|
+
side: "bottom",
|
|
756
|
+
delayMs: 500,
|
|
757
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
758
|
+
type: "button",
|
|
759
|
+
className: steer_queue_dock_module_css_default.action,
|
|
760
|
+
"aria-label": t("queue.edit"),
|
|
761
|
+
onClick: () => {
|
|
762
|
+
setEditing({
|
|
763
|
+
id: `frozen:${i}`,
|
|
764
|
+
text
|
|
765
|
+
});
|
|
766
|
+
},
|
|
767
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEditOutline16, { size: 14 })
|
|
768
|
+
})
|
|
769
|
+
}),
|
|
770
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
771
|
+
label: t("queue.remove"),
|
|
772
|
+
side: "bottom",
|
|
773
|
+
delayMs: 500,
|
|
774
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
775
|
+
type: "button",
|
|
776
|
+
className: steer_queue_dock_module_css_default.action,
|
|
777
|
+
"aria-label": t("queue.remove"),
|
|
778
|
+
onClick: () => removePendingAt(sid, i),
|
|
779
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, { size: 14 })
|
|
780
|
+
})
|
|
781
|
+
}),
|
|
782
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
783
|
+
className: steer_queue_dock_module_css_default.plan,
|
|
784
|
+
role: "group",
|
|
785
|
+
"aria-label": t("steer.later.aria"),
|
|
786
|
+
children: [
|
|
787
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
788
|
+
label: t("steer.now"),
|
|
789
|
+
side: "bottom",
|
|
790
|
+
delayMs: 500,
|
|
791
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
792
|
+
type: "button",
|
|
793
|
+
className: `${steer_queue_dock_module_css_default.tier} ${steer_queue_dock_module_css_default.tierNow}`,
|
|
794
|
+
"aria-label": t("steer.now"),
|
|
795
|
+
"aria-pressed": entry.tier === "force" || void 0,
|
|
796
|
+
onClick: () => setTierAt(sid, i, "force"),
|
|
797
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
798
|
+
className: steer_queue_dock_module_css_default.dot,
|
|
799
|
+
"aria-hidden": true
|
|
800
|
+
})
|
|
801
|
+
})
|
|
802
|
+
}),
|
|
803
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
804
|
+
label: t("steer.next"),
|
|
805
|
+
side: "bottom",
|
|
806
|
+
delayMs: 500,
|
|
807
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
808
|
+
type: "button",
|
|
809
|
+
className: `${steer_queue_dock_module_css_default.tier} ${steer_queue_dock_module_css_default.tierNext}`,
|
|
810
|
+
"aria-label": t("steer.next"),
|
|
811
|
+
"aria-pressed": entry.tier === "safe_point" || void 0,
|
|
812
|
+
onClick: () => setTierAt(sid, i, "safe_point"),
|
|
813
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
814
|
+
className: steer_queue_dock_module_css_default.dot,
|
|
815
|
+
"aria-hidden": true
|
|
816
|
+
})
|
|
817
|
+
})
|
|
818
|
+
}),
|
|
819
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
820
|
+
label: t("steer.later"),
|
|
821
|
+
side: "bottom",
|
|
822
|
+
delayMs: 500,
|
|
823
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
824
|
+
type: "button",
|
|
825
|
+
className: `${steer_queue_dock_module_css_default.tier} ${steer_queue_dock_module_css_default.tierLater}`,
|
|
826
|
+
"aria-label": t("steer.later"),
|
|
827
|
+
"aria-pressed": entry.tier === "queue" || void 0,
|
|
828
|
+
onClick: () => setTierAt(sid, i, "queue"),
|
|
829
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
830
|
+
className: steer_queue_dock_module_css_default.dot,
|
|
831
|
+
"aria-hidden": true
|
|
832
|
+
})
|
|
833
|
+
})
|
|
834
|
+
})
|
|
835
|
+
]
|
|
836
|
+
})
|
|
837
|
+
] })
|
|
838
|
+
}),
|
|
839
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
840
|
+
className: steer_queue_dock_module_css_default.frozenMark,
|
|
841
|
+
children: t("steer.frozenBadge")
|
|
842
|
+
})
|
|
843
|
+
]
|
|
844
|
+
}, i);
|
|
845
|
+
})
|
|
846
|
+
}),
|
|
847
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
848
|
+
id: listId,
|
|
849
|
+
className: steer_queue_dock_module_css_default.list,
|
|
850
|
+
hidden: !listVisible,
|
|
851
|
+
children: listVisible && queue.map((row, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
852
|
+
className: `${steer_queue_dock_module_css_default.row} ${dragOver === index ? steer_queue_dock_module_css_default.rowDragOver : ""}`,
|
|
853
|
+
"data-tier": badgeFor("queued") ?? void 0,
|
|
854
|
+
"data-editing": editing?.id === row.id ? "" : void 0,
|
|
855
|
+
draggable: queueMutable && !reorderUnsupported && busy === null && !frozen,
|
|
856
|
+
title: queueMutable && !reorderUnsupported && busy === null && !frozen ? t("steer.dragReorder") : void 0,
|
|
857
|
+
onDragStart: (event) => {
|
|
858
|
+
dragIndex.current = index;
|
|
859
|
+
event.dataTransfer.effectAllowed = "move";
|
|
860
|
+
event.dataTransfer.setData("text/plain", String(index));
|
|
861
|
+
},
|
|
862
|
+
onDragOver: (event) => {
|
|
863
|
+
if (dragIndex.current === null) return;
|
|
864
|
+
event.preventDefault();
|
|
865
|
+
setDragOver(index);
|
|
866
|
+
},
|
|
867
|
+
onDrop: (event) => {
|
|
868
|
+
event.preventDefault();
|
|
869
|
+
const from = dragIndex.current;
|
|
870
|
+
dragIndex.current = null;
|
|
871
|
+
setDragOver(null);
|
|
872
|
+
if (from !== null) reorderTo(from, index);
|
|
873
|
+
},
|
|
874
|
+
onDragEnd: () => {
|
|
875
|
+
dragIndex.current = null;
|
|
876
|
+
setDragOver(null);
|
|
877
|
+
},
|
|
878
|
+
children: [
|
|
879
|
+
queue.length === 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
880
|
+
className: steer_queue_dock_module_css_default.lead,
|
|
881
|
+
"aria-hidden": true,
|
|
882
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconQueueOutline14, {})
|
|
883
|
+
}),
|
|
884
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SteerBadge, {
|
|
885
|
+
tier: badgeFor("queued"),
|
|
886
|
+
t
|
|
887
|
+
}),
|
|
888
|
+
editing?.id === row.id ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
889
|
+
ref: editorRef,
|
|
890
|
+
autoFocus: true,
|
|
891
|
+
className: steer_queue_dock_module_css_default.editor,
|
|
892
|
+
"aria-label": t("queue.edit"),
|
|
893
|
+
rows: 1,
|
|
894
|
+
value: editing.text,
|
|
895
|
+
onChange: (event) => {
|
|
896
|
+
setEditing({
|
|
897
|
+
id: row.id,
|
|
898
|
+
text: event.currentTarget.value
|
|
899
|
+
});
|
|
900
|
+
},
|
|
901
|
+
onInput: (event) => {
|
|
902
|
+
resizeEditor(event.currentTarget);
|
|
903
|
+
},
|
|
904
|
+
onKeyDown: (event) => {
|
|
905
|
+
if (event.key === "Escape") {
|
|
906
|
+
setEditing(null);
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing) {
|
|
910
|
+
event.preventDefault();
|
|
911
|
+
saveEdit();
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
915
|
+
className: steer_queue_dock_module_css_default.preview,
|
|
916
|
+
children: row.preview
|
|
917
|
+
}),
|
|
918
|
+
queueMutable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
919
|
+
className: steer_queue_dock_module_css_default.actions,
|
|
920
|
+
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, {
|
|
921
|
+
label: t("queue.save"),
|
|
922
|
+
side: "bottom",
|
|
923
|
+
delayMs: 500,
|
|
924
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
925
|
+
type: "button",
|
|
926
|
+
className: steer_queue_dock_module_css_default.action,
|
|
927
|
+
"aria-label": t("queue.save"),
|
|
928
|
+
disabled: busy !== null || editing.text.trim() === "",
|
|
929
|
+
onClick: () => {
|
|
930
|
+
saveEdit();
|
|
931
|
+
},
|
|
932
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCheckOutline16, { size: 14 })
|
|
933
|
+
})
|
|
934
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
935
|
+
label: t("queue.cancelEdit"),
|
|
936
|
+
side: "bottom",
|
|
937
|
+
delayMs: 500,
|
|
938
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
939
|
+
type: "button",
|
|
940
|
+
className: steer_queue_dock_module_css_default.action,
|
|
941
|
+
"aria-label": t("queue.cancelEdit"),
|
|
942
|
+
disabled: busy !== null,
|
|
943
|
+
onClick: () => {
|
|
944
|
+
setEditing(null);
|
|
945
|
+
},
|
|
946
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 14 })
|
|
947
|
+
})
|
|
948
|
+
})] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
949
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
950
|
+
label: t("steer.moveUp"),
|
|
951
|
+
side: "bottom",
|
|
952
|
+
delayMs: 500,
|
|
953
|
+
disabled: frozen || reorderUnsupported,
|
|
954
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
955
|
+
type: "button",
|
|
956
|
+
className: steer_queue_dock_module_css_default.action,
|
|
957
|
+
"aria-label": t("steer.moveUp"),
|
|
958
|
+
title: reorderUnsupported ? t("steer.reorder.unsupported") : void 0,
|
|
959
|
+
disabled: busy !== null || frozen || reorderUnsupported,
|
|
960
|
+
onClick: () => {
|
|
961
|
+
reorder(row.id, -1);
|
|
962
|
+
},
|
|
963
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronUpOutline14, {})
|
|
964
|
+
})
|
|
965
|
+
}),
|
|
966
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
967
|
+
label: t("steer.moveDown"),
|
|
968
|
+
side: "bottom",
|
|
969
|
+
delayMs: 500,
|
|
970
|
+
disabled: frozen || reorderUnsupported,
|
|
971
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
972
|
+
type: "button",
|
|
973
|
+
className: steer_queue_dock_module_css_default.action,
|
|
974
|
+
"aria-label": t("steer.moveDown"),
|
|
975
|
+
title: reorderUnsupported ? t("steer.reorder.unsupported") : void 0,
|
|
976
|
+
disabled: busy !== null || frozen || reorderUnsupported,
|
|
977
|
+
onClick: () => {
|
|
978
|
+
reorder(row.id, 1);
|
|
979
|
+
},
|
|
980
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, {})
|
|
981
|
+
})
|
|
982
|
+
}),
|
|
983
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
984
|
+
label: t("steer.pullBack"),
|
|
985
|
+
side: "bottom",
|
|
986
|
+
delayMs: 500,
|
|
987
|
+
disabled: row.text === null || !composerEmpty,
|
|
988
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
989
|
+
type: "button",
|
|
990
|
+
className: steer_queue_dock_module_css_default.action,
|
|
991
|
+
"aria-label": t("steer.pullBack"),
|
|
992
|
+
title: row.text === null ? t("steer.pullBack.unsupported") : !composerEmpty ? t("steer.pullBack.composerBusy") : void 0,
|
|
993
|
+
disabled: busy !== null || frozen || row.text === null || !composerEmpty,
|
|
994
|
+
onClick: () => {
|
|
995
|
+
pullBackToComposer(row);
|
|
996
|
+
},
|
|
997
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRightUpOutline16, {})
|
|
998
|
+
})
|
|
999
|
+
}),
|
|
1000
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
1001
|
+
label: t("queue.edit"),
|
|
1002
|
+
side: "bottom",
|
|
1003
|
+
delayMs: 500,
|
|
1004
|
+
disabled: row.text === null,
|
|
1005
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1006
|
+
type: "button",
|
|
1007
|
+
className: steer_queue_dock_module_css_default.action,
|
|
1008
|
+
"aria-label": t("queue.edit"),
|
|
1009
|
+
title: row.text === null ? t("queue.edit.unsupported") : void 0,
|
|
1010
|
+
disabled: busy !== null || row.text === null,
|
|
1011
|
+
onClick: () => {
|
|
1012
|
+
if (row.text !== null) setEditing({
|
|
1013
|
+
id: row.id,
|
|
1014
|
+
text: row.text
|
|
1015
|
+
});
|
|
1016
|
+
},
|
|
1017
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEditOutline16, { size: 14 })
|
|
1018
|
+
})
|
|
1019
|
+
}),
|
|
1020
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
1021
|
+
label: t("queue.remove"),
|
|
1022
|
+
side: "bottom",
|
|
1023
|
+
delayMs: 500,
|
|
1024
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1025
|
+
type: "button",
|
|
1026
|
+
className: steer_queue_dock_module_css_default.action,
|
|
1027
|
+
"aria-label": t("queue.remove"),
|
|
1028
|
+
disabled: busy !== null,
|
|
1029
|
+
onClick: () => {
|
|
1030
|
+
applyAction(row.id, { kind: "remove" }, t("queue.removeFailed"));
|
|
1031
|
+
},
|
|
1032
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, { size: 14 })
|
|
1033
|
+
})
|
|
1034
|
+
}),
|
|
1035
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1036
|
+
className: steer_queue_dock_module_css_default.plan,
|
|
1037
|
+
role: "group",
|
|
1038
|
+
"aria-label": t("steer.later.aria"),
|
|
1039
|
+
children: [
|
|
1040
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
1041
|
+
label: t("steer.now"),
|
|
1042
|
+
side: "bottom",
|
|
1043
|
+
delayMs: 500,
|
|
1044
|
+
disabled: planDisabled || row.text === null,
|
|
1045
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1046
|
+
type: "button",
|
|
1047
|
+
className: `${steer_queue_dock_module_css_default.tier} ${steer_queue_dock_module_css_default.tierNow}`,
|
|
1048
|
+
"aria-label": t("steer.now"),
|
|
1049
|
+
title: planDisabled ? t("steer.unavailable.running") : row.text === null ? t("steer.now.unsupported") : void 0,
|
|
1050
|
+
disabled: busy !== null || planDisabled || row.text === null,
|
|
1051
|
+
onClick: () => {
|
|
1052
|
+
steerRow(row, "now");
|
|
1053
|
+
},
|
|
1054
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1055
|
+
className: steer_queue_dock_module_css_default.dot,
|
|
1056
|
+
"aria-hidden": true
|
|
1057
|
+
})
|
|
1058
|
+
})
|
|
1059
|
+
}),
|
|
1060
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
1061
|
+
label: t("steer.next"),
|
|
1062
|
+
side: "bottom",
|
|
1063
|
+
delayMs: 500,
|
|
1064
|
+
disabled: planDisabled,
|
|
1065
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1066
|
+
type: "button",
|
|
1067
|
+
className: `${steer_queue_dock_module_css_default.tier} ${steer_queue_dock_module_css_default.tierNext}`,
|
|
1068
|
+
"aria-label": t("steer.next"),
|
|
1069
|
+
title: planDisabled ? t("steer.unavailable.running") : void 0,
|
|
1070
|
+
disabled: busy !== null || planDisabled,
|
|
1071
|
+
onClick: () => {
|
|
1072
|
+
steerRow(row, "next");
|
|
1073
|
+
},
|
|
1074
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1075
|
+
className: steer_queue_dock_module_css_default.dot,
|
|
1076
|
+
"aria-hidden": true
|
|
1077
|
+
})
|
|
1078
|
+
})
|
|
1079
|
+
}),
|
|
1080
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
1081
|
+
label: t("steer.later"),
|
|
1082
|
+
side: "bottom",
|
|
1083
|
+
delayMs: 500,
|
|
1084
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1085
|
+
type: "button",
|
|
1086
|
+
className: `${steer_queue_dock_module_css_default.tier} ${steer_queue_dock_module_css_default.tierLater}`,
|
|
1087
|
+
"aria-label": t("steer.later"),
|
|
1088
|
+
"aria-pressed": true,
|
|
1089
|
+
disabled: busy !== null || clearing,
|
|
1090
|
+
onClick: () => {},
|
|
1091
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1092
|
+
className: steer_queue_dock_module_css_default.dot,
|
|
1093
|
+
"aria-hidden": true
|
|
1094
|
+
})
|
|
1095
|
+
})
|
|
1096
|
+
})
|
|
1097
|
+
]
|
|
1098
|
+
})
|
|
1099
|
+
] })
|
|
1100
|
+
})
|
|
1101
|
+
]
|
|
1102
|
+
}, row.id))
|
|
1103
|
+
}),
|
|
1104
|
+
steering.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
1105
|
+
className: steer_queue_dock_module_css_default.steeringList,
|
|
1106
|
+
"data-steering-list": "",
|
|
1107
|
+
children: steering.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
1108
|
+
className: steer_queue_dock_module_css_default.row,
|
|
1109
|
+
"data-tier": badgeFor("steering") ?? void 0,
|
|
1110
|
+
children: [
|
|
1111
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1112
|
+
className: steer_queue_dock_module_css_default.lead,
|
|
1113
|
+
"aria-hidden": true,
|
|
1114
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconQueueOutline14, {})
|
|
1115
|
+
}),
|
|
1116
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SteerBadge, {
|
|
1117
|
+
tier: badgeFor("steering"),
|
|
1118
|
+
t
|
|
1119
|
+
}),
|
|
1120
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1121
|
+
className: steer_queue_dock_module_css_default.preview,
|
|
1122
|
+
children: row.preview
|
|
1123
|
+
}),
|
|
1124
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1125
|
+
className: steer_queue_dock_module_css_default.actions,
|
|
1126
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
1127
|
+
label: t("steer.revoke"),
|
|
1128
|
+
side: "bottom",
|
|
1129
|
+
delayMs: 500,
|
|
1130
|
+
disabled: row.text === null,
|
|
1131
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1132
|
+
type: "button",
|
|
1133
|
+
className: `${steer_queue_dock_module_css_default.tier} ${steer_queue_dock_module_css_default.tierLater}`,
|
|
1134
|
+
"aria-label": t("steer.revoke"),
|
|
1135
|
+
title: row.text === null ? t("steer.revoke.unsupported") : void 0,
|
|
1136
|
+
disabled: busy !== null || row.text === null || frozen,
|
|
1137
|
+
onClick: () => {
|
|
1138
|
+
revokeToLater(row);
|
|
1139
|
+
},
|
|
1140
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1141
|
+
className: steer_queue_dock_module_css_default.dot,
|
|
1142
|
+
"aria-hidden": true
|
|
1143
|
+
})
|
|
1144
|
+
})
|
|
1145
|
+
})
|
|
1146
|
+
})
|
|
1147
|
+
]
|
|
1148
|
+
}, row.id))
|
|
1149
|
+
})
|
|
1150
|
+
]
|
|
1151
|
+
})
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1154
|
+
/** Badge label keys per tier. */
|
|
1155
|
+
const BADGE_KEYS = {
|
|
1156
|
+
now: "steer.badge.now",
|
|
1157
|
+
next: "steer.badge.next",
|
|
1158
|
+
later: "steer.badge.later"
|
|
1159
|
+
};
|
|
1160
|
+
/** Badge tint classes per tier (present at bundle time; typed through the css-modules declaration). */
|
|
1161
|
+
const BADGE_CLASSES = {
|
|
1162
|
+
now: steer_queue_dock_module_css_default.badgeNow,
|
|
1163
|
+
next: steer_queue_dock_module_css_default.badgeNext,
|
|
1164
|
+
later: steer_queue_dock_module_css_default.badgeLater
|
|
1165
|
+
};
|
|
1166
|
+
/** One row's tier badge: colored dot + label; context rows render nothing. */
|
|
1167
|
+
function SteerBadge({ tier, t }) {
|
|
1168
|
+
if (tier === null) return null;
|
|
1169
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1170
|
+
className: `${steer_queue_dock_module_css_default.badge} ${BADGE_CLASSES[tier]}`,
|
|
1171
|
+
"data-badge": tier,
|
|
1172
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1173
|
+
className: steer_queue_dock_module_css_default.dot,
|
|
1174
|
+
"aria-hidden": true
|
|
1175
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1176
|
+
className: steer_queue_dock_module_css_default.badgeLabel,
|
|
1177
|
+
children: t(BADGE_KEYS[tier])
|
|
1178
|
+
})]
|
|
1179
|
+
});
|
|
1180
|
+
}
|
|
1181
|
+
//#endregion
|
|
1182
|
+
//#region src/client/session-guard-bridge.ts
|
|
1183
|
+
/**
|
|
1184
|
+
* input-traffic ↔ dsh-session-guard 透传桥(D8 fail-open)。
|
|
1185
|
+
*
|
|
1186
|
+
* input-traffic **只做冻结增强**(队列冻结/解冻),服务端会话门(暂停/恢复会话)
|
|
1187
|
+
* 归 dsh-session-guard 插件。冻结/解冻按钮触发时,尽力调用
|
|
1188
|
+
* `sessionGuard.stopNextTurn` / `resume`;**插件未装**(路由 404 / 网络失败 /
|
|
1189
|
+
* 返回错误)→ 静默跳过,**绝不报错**,前端冻结仍正常生效。
|
|
1190
|
+
*
|
|
1191
|
+
* 不吸收任何 auto-continue / 重试逻辑(重试归后端 dsh-session-guard,D9)。
|
|
1192
|
+
*/
|
|
1193
|
+
/** 尽力调用 sessionGuard.stopNextTurn(停掉 session 下一回合)。失败静默。 */
|
|
1194
|
+
async function sessionGuardStopNextTurn(sessionId) {
|
|
1195
|
+
return callGuard(sessionId, "stopNextTurn");
|
|
1196
|
+
}
|
|
1197
|
+
/** 尽力调用 sessionGuard.resume。失败静默。 */
|
|
1198
|
+
async function sessionGuardResume(sessionId) {
|
|
1199
|
+
return callGuard(sessionId, "resume");
|
|
1200
|
+
}
|
|
1201
|
+
async function callGuard(sessionId, action) {
|
|
1202
|
+
try {
|
|
1203
|
+
const res = await fetch("/session-guard/rpc", {
|
|
1204
|
+
method: "POST",
|
|
1205
|
+
headers: { "content-type": "application/json" },
|
|
1206
|
+
body: JSON.stringify({
|
|
1207
|
+
sessionId,
|
|
1208
|
+
action
|
|
1209
|
+
})
|
|
1210
|
+
});
|
|
1211
|
+
if (!res.ok) return false;
|
|
1212
|
+
return (await res.json().catch(() => null))?.ok === true;
|
|
1213
|
+
} catch {
|
|
1214
|
+
return false;
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
try {
|
|
1218
|
+
globalThis.__DSH_SESSION_GUARD_BRIDGE__ = true;
|
|
1219
|
+
} catch {}
|
|
1220
|
+
//#endregion
|
|
1221
|
+
//#region \0dsh-css:E:\test\rewrite-agently\mine-dsh-plugins\dsh-input-traffic\src\client\freeze-button.module.css.mjs
|
|
1222
|
+
const css = ".BUSMDW_freeze{border:1px solid var(--dsw-alias-border-l3,#0000001f);height:24px;color:var(--dsw-alias-label-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}.BUSMDW_freeze:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#0000000a)}.BUSMDW_freeze[aria-pressed=true]{border-color:var(--dsw-alias-state-success-primary,#30a46c);color:var(--dsw-alias-state-success-primary,#30a46c);background:color-mix(in srgb, var(--dsw-alias-state-success-primary,#30a46c) 8%, transparent)}.BUSMDW_label{font-size:12px}";
|
|
1223
|
+
const tagId = "dsh-input-traffic/freeze-button.module.css";
|
|
1224
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
1225
|
+
const tag = document.createElement("style");
|
|
1226
|
+
tag.dataset.plugin = "dsh-input-traffic";
|
|
1227
|
+
tag.dataset.pluginCss = tagId;
|
|
1228
|
+
tag.textContent = css;
|
|
1229
|
+
document.head.appendChild(tag);
|
|
1230
|
+
}
|
|
1231
|
+
var freeze_button_module_css_default = {
|
|
1232
|
+
"label": "BUSMDW_label",
|
|
1233
|
+
"freeze": "BUSMDW_freeze"
|
|
1234
|
+
};
|
|
1235
|
+
//#endregion
|
|
1236
|
+
//#region src/client/freeze-button.tsx
|
|
1237
|
+
/**
|
|
1238
|
+
* Session freeze/resume control, mounted in the composer's right tool row
|
|
1239
|
+
* (`conversation.input.right`) so it is reachable before anything queues.
|
|
1240
|
+
*
|
|
1241
|
+
* Peak-hour pause semantics: freeze does NOT interrupt the running turn —it
|
|
1242
|
+
* finishes naturally. The queued messages are detached (removed, preserved in
|
|
1243
|
+
* the shared store), so the driver finds no pending work and stops after the
|
|
1244
|
+
* current turn. Resume re-submits the preserved texts, waking the driver and
|
|
1245
|
+
* continuing the queue.
|
|
1246
|
+
*
|
|
1247
|
+
* dsh-session-guard 协作(D8 fail-open):
|
|
1248
|
+
* - 冻结 = 前端摘队列(本插件)+ composer block(阻止新输入漏进对话) + 尽力调服务端
|
|
1249
|
+
* sessionGuard.stopNextTurn(停掉 session 下一回合;session-guard 未装时静默跳过)。
|
|
1250
|
+
* - 解冻 = 清 composer block + **先** await sessionGuard.resume(让被打断回合的自然
|
|
1251
|
+
* 下一步先发生),**再**重投队列(later 级条目因此排在自然 next turn 之后,不再插队)。
|
|
1252
|
+
* - 本插件只做冻结增强,不承担重试/暂停决策(归后端,D9)。
|
|
1253
|
+
*/
|
|
1254
|
+
/**
|
|
1255
|
+
* Freeze/resume toggle for the peak-hour scenario.
|
|
1256
|
+
* @param props - slot props; the session snapshot drives the detach list.
|
|
1257
|
+
*/
|
|
1258
|
+
function FreezeButton({ useSession, updateQueue, cancel, send, sendSteer, sessionId, setComposerBlock, notify, t }) {
|
|
1259
|
+
const sid = sessionId ?? "";
|
|
1260
|
+
const { frozen } = (0, react.useSyncExternalStore)(freezeStore.subscribe, () => freezeStore.getSnapshot(sid));
|
|
1261
|
+
const queue = useSession((s) => s.queue);
|
|
1262
|
+
const freeze = async () => {
|
|
1263
|
+
const rows = queue.filter((row) => row.placement === "queued" || row.placement === "steering");
|
|
1264
|
+
const pending = rows.flatMap((row) => row.text === null ? [] : [{
|
|
1265
|
+
text: row.text,
|
|
1266
|
+
tier: row.placement === "steering" ? "safe_point" : "queue"
|
|
1267
|
+
}]);
|
|
1268
|
+
await Promise.all(rows.map((row) => updateQueue(row.id, { kind: "remove" }).catch(() => void 0)));
|
|
1269
|
+
freezeStore.set(sid, {
|
|
1270
|
+
frozen: true,
|
|
1271
|
+
pending
|
|
1272
|
+
});
|
|
1273
|
+
setComposerBlock?.(t("steer.frozenInput"));
|
|
1274
|
+
if (sessionId !== void 0) sessionGuardStopNextTurn(sessionId);
|
|
1275
|
+
};
|
|
1276
|
+
const resume = async () => {
|
|
1277
|
+
const pending = freezeStore.getSnapshot(sid).pending;
|
|
1278
|
+
freezeStore.set(sid, {
|
|
1279
|
+
frozen: false,
|
|
1280
|
+
pending: []
|
|
1281
|
+
});
|
|
1282
|
+
setComposerBlock?.(void 0);
|
|
1283
|
+
try {
|
|
1284
|
+
if (sessionId !== void 0) await sessionGuardResume(sessionId);
|
|
1285
|
+
for (const entry of pending) {
|
|
1286
|
+
if (entry.tier === "force") await cancel();
|
|
1287
|
+
if (entry.tier === "safe_point" && sendSteer !== void 0) await sendSteer(entry.text);
|
|
1288
|
+
else await send(entry.text);
|
|
1289
|
+
}
|
|
1290
|
+
} catch {
|
|
1291
|
+
notify("error", t("steer.resumeFailed"));
|
|
1292
|
+
}
|
|
1293
|
+
};
|
|
1294
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1295
|
+
type: "button",
|
|
1296
|
+
className: freeze_button_module_css_default.freeze,
|
|
1297
|
+
"aria-label": frozen ? t("steer.resume") : t("steer.freeze"),
|
|
1298
|
+
"aria-pressed": frozen || void 0,
|
|
1299
|
+
title: frozen ? t("steer.frozen") : void 0,
|
|
1300
|
+
onClick: () => {
|
|
1301
|
+
if (frozen) resume();
|
|
1302
|
+
else freeze();
|
|
1303
|
+
},
|
|
1304
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1305
|
+
className: freeze_button_module_css_default.label,
|
|
1306
|
+
children: frozen ? t("steer.resume") : t("steer.freeze")
|
|
1307
|
+
})
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1310
|
+
//#endregion
|
|
1311
|
+
//#region src/client/hide-enter-row.tsx
|
|
1312
|
+
/** Render nothing: the official busy-Enter row is hidden while mounted. */
|
|
1313
|
+
function HideEnterRow(_props) {
|
|
1314
|
+
return null;
|
|
1315
|
+
}
|
|
1316
|
+
//#endregion
|
|
1317
|
+
//#region src/client/index.ts
|
|
1318
|
+
/** Durable conversation settings namespace owned by ui-conversation. */
|
|
1319
|
+
const CONVERSATION_SETTINGS_NAMESPACE = "ui-conversation";
|
|
1320
|
+
/** Busy-Enter field inside that namespace; the plugin pins it to queue. */
|
|
1321
|
+
const BUSY_ENTER_FIELD = "busyEnter";
|
|
1322
|
+
/**
|
|
1323
|
+
* Display order of the queue strip inside `conversation.input.dock`.
|
|
1324
|
+
*
|
|
1325
|
+
* List rows render sorted by `order` ascending, so a value above every other
|
|
1326
|
+
* contributor keeps the strip as the bottom-most entry of the band — directly
|
|
1327
|
+
* on top of the composer card. Known contributors in both 0.1.1-rc.2 and
|
|
1328
|
+
* 0.1.2-rc.1: `todo` 0, `goal` 10, official `queue` 20, `dsh-perm-gate.notice`
|
|
1329
|
+
* 30. DSH has no "last" slot semantics, so this is a convention, not a
|
|
1330
|
+
* structural guarantee: a third party registering a larger value could still
|
|
1331
|
+
* land below us.
|
|
1332
|
+
*/
|
|
1333
|
+
const QUEUE_DOCK_ORDER = 1e3;
|
|
1334
|
+
/**
|
|
1335
|
+
* Deliver one plain-text message into the session's next step. The exposed
|
|
1336
|
+
* conversation `send` verb only queues into the next turn, so resume steers
|
|
1337
|
+
* through the session face's steer-mode prompt instead (no harness change).
|
|
1338
|
+
* @param ctx - root context (resolves the session face behind the scope).
|
|
1339
|
+
* @param actx - agent-scoped context of the owning session.
|
|
1340
|
+
* @param text - message text to deliver.
|
|
1341
|
+
*/
|
|
1342
|
+
function steerPrompt(actx, text) {
|
|
1343
|
+
const conversation = actx.get("conversation");
|
|
1344
|
+
if (conversation === void 0) return Promise.reject(/* @__PURE__ */ new Error("steer resume: conversation service unavailable"));
|
|
1345
|
+
return conversation.send(text);
|
|
1346
|
+
}
|
|
1347
|
+
/** Services required by the browser half. */
|
|
1348
|
+
const inject = [
|
|
1349
|
+
"slots",
|
|
1350
|
+
"locale",
|
|
1351
|
+
"sessions",
|
|
1352
|
+
"conversation",
|
|
1353
|
+
"settingsScope"
|
|
1354
|
+
];
|
|
1355
|
+
/**
|
|
1356
|
+
* Client plugin body: dictionaries, busy-Enter pinning, and the two slot
|
|
1357
|
+
* shadowings.
|
|
1358
|
+
* @param ctx - client root context.
|
|
1359
|
+
*/
|
|
1360
|
+
function apply(ctx) {
|
|
1361
|
+
ctx.effect(() => ctx.locale.register(NS, {
|
|
1362
|
+
zh,
|
|
1363
|
+
en
|
|
1364
|
+
}), "dsh-input-traffic: dictionaries");
|
|
1365
|
+
ctx.settingsScope.bind({ namespace: CONVERSATION_SETTINGS_NAMESPACE }).set(BUSY_ENTER_FIELD, "queue");
|
|
1366
|
+
ctx.slots.inject("conversation.input.dock", () => ctx.slots.register({
|
|
1367
|
+
name: "conversation.input.dock",
|
|
1368
|
+
id: "queue",
|
|
1369
|
+
order: QUEUE_DOCK_ORDER,
|
|
1370
|
+
priority: -1,
|
|
1371
|
+
locale: NS,
|
|
1372
|
+
inject: (sessionId) => {
|
|
1373
|
+
const actx = ctx.sessions.scope(sessionId);
|
|
1374
|
+
if (actx === void 0) throw new Error(`steer dock: session "${sessionId}" resolved no scope`);
|
|
1375
|
+
const conversation = actx.get("conversation");
|
|
1376
|
+
if (conversation === void 0) throw new Error("steer dock: conversation service unavailable");
|
|
1377
|
+
return {
|
|
1378
|
+
updateQueue: (itemId, action) => conversation.updateQueue(itemId, action),
|
|
1379
|
+
cancel: () => conversation.cancel(),
|
|
1380
|
+
send: (text) => conversation.send(text),
|
|
1381
|
+
setDraft: (text) => {
|
|
1382
|
+
conversation.input.for(actx).actions.setDraft(text);
|
|
1383
|
+
},
|
|
1384
|
+
notify: (level, text) => {
|
|
1385
|
+
conversation.input.for(actx).notify(level, text);
|
|
1386
|
+
}
|
|
1387
|
+
};
|
|
1388
|
+
}
|
|
1389
|
+
}, SteerQueueDock));
|
|
1390
|
+
ctx.slots.inject("conversation.input.right", () => ctx.slots.register({
|
|
1391
|
+
name: "conversation.input.right",
|
|
1392
|
+
id: "steer-freeze",
|
|
1393
|
+
order: 30,
|
|
1394
|
+
locale: NS,
|
|
1395
|
+
inject: (sessionId) => {
|
|
1396
|
+
const actx = ctx.sessions.scope(sessionId);
|
|
1397
|
+
if (actx === void 0) throw new Error(`steer freeze: session "${sessionId}" resolved no scope`);
|
|
1398
|
+
const conversation = actx.get("conversation");
|
|
1399
|
+
if (conversation === void 0) throw new Error("steer freeze: conversation service unavailable");
|
|
1400
|
+
return {
|
|
1401
|
+
updateQueue: (itemId, action) => conversation.updateQueue(itemId, action),
|
|
1402
|
+
cancel: () => conversation.cancel(),
|
|
1403
|
+
send: (text) => conversation.send(text),
|
|
1404
|
+
sendSteer: (text) => steerPrompt(actx, text),
|
|
1405
|
+
sessionId: String(sessionId),
|
|
1406
|
+
setDraft: (text) => {
|
|
1407
|
+
conversation.input.for(actx).actions.setDraft(text);
|
|
1408
|
+
},
|
|
1409
|
+
notify: (level, text) => {
|
|
1410
|
+
conversation.input.for(actx).notify(level, text);
|
|
1411
|
+
},
|
|
1412
|
+
setComposerBlock: (reason) => {
|
|
1413
|
+
conversation.blocks.set(sessionId, reason === void 0 ? void 0 : { reason });
|
|
1414
|
+
}
|
|
1415
|
+
};
|
|
1416
|
+
}
|
|
1417
|
+
}, FreezeButton));
|
|
1418
|
+
ctx.slots.inject("settings.general.item", () => ctx.slots.register({
|
|
1419
|
+
name: "settings.general.item",
|
|
1420
|
+
id: "composer-enter",
|
|
1421
|
+
order: 20,
|
|
1422
|
+
priority: -1,
|
|
1423
|
+
locale: NS
|
|
1424
|
+
}, HideEnterRow));
|
|
1425
|
+
}
|
|
1426
|
+
//#endregion
|
|
1427
|
+
exports.QUEUE_DOCK_ORDER = QUEUE_DOCK_ORDER;
|
|
1428
|
+
exports.apply = apply;
|
|
1429
|
+
exports.inject = inject;
|
|
1430
|
+
return module.exports;
|
|
1431
|
+
}
|
|
1432
|
+
});
|
|
1433
|
+
|
|
1434
1434
|
//# sourceMappingURL=client.js.map
|