Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 44 additions & 2 deletions app/shared/src/chatview/i18n/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,6 @@ export const chatviewResources = {
'toast.sentWithoutDispatch': '消息已发送;未 @ Agent,不会触发任务。@ 提及 Agent 后再发送即可派单。',
'toast.dispatchRetryExhausted': '派单重试 {max} 次仍被拒绝,已放弃自动重试,请稍后手动重新触发该 Agent',
'toast.dispatchRetryFailed': '派单重试失败,请手动重新触发该 Agent',
'toast.pendingDispatchBadge': '待发送 {count} 条',
'toast.actionRecorded': '操作已记录',
'toast.multiCopy': '已复制 {count} 项',
'toast.multiDelete': '已删除 {count} 项',
Expand Down Expand Up @@ -476,6 +475,29 @@ export const chatviewResources = {
'messageSearch.footerJump': '跳转',
'composer.agentPicker': 'Agent',
'composer.targetPicker': '执行目标',
// ── #1965 visible dispatch queue (queue/steer semantics, UX F7) ──
// Copy contract: the queue only schedules the NEXT dispatch after the
// active run ends; undo cancels dispatch, never the persisted message.
'composer.queue.title': '待派发队列',
'composer.queue.summaryRunning': '{{count}} 条排队中,当前任务结束后按序派发',
'composer.queue.summaryIdle': '{{count}} 条排队中,即将按序派发',
'composer.queue.clearAll': '清空队列',
'composer.queue.notice': '队列只安排下一轮派单的顺序与目标,不会打断正在运行的任务;撤销只取消派单,已发送的消息保留在聊天记录中。',
'composer.queue.item': '第 {{index}} 条:{{text}}',
'composer.queue.retarget': '更改派单目标:{{text}}',
'composer.queue.statusQueued': '排队中',
'composer.queue.statusDispatching': '派发中',
'composer.queue.statusRetrying': '第 {{attempt}} 次重试中',
'composer.queue.statusRetryExhausted': '重试 {{attempt}} 次仍被拒绝',
'composer.queue.statusFailed': '派发失败',
'composer.queue.retry': '重试',
'composer.queue.moveToFront': '置顶:{{text}}',
'composer.queue.moveUp': '上移:{{text}}',
'composer.queue.moveDown': '下移:{{text}}',
'composer.queue.undo': '撤销派单:{{text}}',
'composer.queue.undoAction': '撤销',
'composer.queue.undoConfirmed': '已撤销派单;消息仍保留在聊天记录中',
'composer.queue.clearConfirmed': '已清空待派单队列;消息仍保留在聊天记录中',
'profile.agentConfig': 'Agent 配置',
'profile.role': '职责',
'profile.engine': '引擎',
Expand Down Expand Up @@ -1053,7 +1075,6 @@ export const chatviewResources = {
'toast.sentWithoutDispatch': 'Message sent; no agent was @mentioned, so no task was triggered. @ an agent to dispatch.',
'toast.dispatchRetryExhausted': 'Dispatch retried {max} times and was still rejected; auto-retry stopped. Re-trigger the agent manually later.',
'toast.dispatchRetryFailed': 'Dispatch retry failed; re-trigger the agent manually.',
'toast.pendingDispatchBadge': '{count} pending',
'toast.actionRecorded': 'Action recorded',
'toast.multiCopy': '{count} item(s) copied',
'toast.multiDelete': '{count} item(s) deleted',
Expand Down Expand Up @@ -1236,6 +1257,27 @@ export const chatviewResources = {
'messageSearch.footerJump': 'jump',
'composer.agentPicker': 'Agent',
'composer.targetPicker': 'Execution target',
// ── #1965 visible dispatch queue (queue/steer semantics, UX F7) ──
'composer.queue.title': 'Dispatch queue',
'composer.queue.summaryRunning': '{{count}} queued; dispatched in order once the current run ends',
'composer.queue.summaryIdle': '{{count}} queued; dispatching in order',
'composer.queue.clearAll': 'Clear queue',
'composer.queue.notice': 'The queue only sets the order and target of the next dispatch; it never interrupts the running task. Undo cancels dispatch only — the sent message stays in the transcript.',
'composer.queue.item': 'Item {{index}}: {{text}}',
'composer.queue.retarget': 'Change dispatch target: {{text}}',
'composer.queue.statusQueued': 'Queued',
'composer.queue.statusDispatching': 'Dispatching',
'composer.queue.statusRetrying': 'Retry {{attempt}} in progress',
'composer.queue.statusRetryExhausted': 'Rejected after {{attempt}} retries',
'composer.queue.statusFailed': 'Dispatch failed',
'composer.queue.retry': 'Retry',
'composer.queue.moveToFront': 'Move to front: {{text}}',
'composer.queue.moveUp': 'Move up: {{text}}',
'composer.queue.moveDown': 'Move down: {{text}}',
'composer.queue.undo': 'Cancel dispatch: {{text}}',
'composer.queue.undoAction': 'Undo',
'composer.queue.undoConfirmed': 'Dispatch cancelled; the message stays in the transcript',
'composer.queue.clearConfirmed': 'Dispatch queue cleared; messages stay in the transcript',
'profile.agentConfig': 'Agent configuration',
'profile.role': 'Role',
'profile.engine': 'Engine',
Expand Down
238 changes: 227 additions & 11 deletions app/workbench/src/AgentHubWorkbench.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -1300,19 +1300,235 @@
pointer-events: none;
}

/* ── Pending dispatch queue badge (CF22) ── */
.pendingIntentBadge {
align-self: center;
margin-top: var(--sp-xxs);
padding: 2px 12px;
/* ── Visible dispatch queue (#1965, UX F7) ──
Rows are dispatch intents for already-persisted Hub messages. Controls
only affect the NEXT dispatch (order/target/retry), never the run that
is currently executing. */
.composerQueue {
position: relative;
z-index: var(--z-base);
width: min(100%, 820px);
margin: var(--sp-xxs) auto 0;
padding: var(--sp-10) var(--sp-14);
border: 1px solid var(--glass-border-strong, var(--td-line));
border-radius: var(--r-xl);
background: var(--glass-panel, color-mix(in srgb, var(--td-surface) 86%, var(--td-canvas)));
backdrop-filter: var(--glass-backdrop-filter);
-webkit-backdrop-filter: var(--glass-backdrop-filter);
box-shadow: var(--glass-elev-1);
pointer-events: auto;
}

.composerQueueHeader {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--sp-10);
}

.composerQueueHeading {
display: flex;
flex-wrap: wrap;
align-items: baseline;
gap: var(--sp-xxs) var(--sp-10);
min-width: 0;
}

.composerQueueTitle {
color: var(--td-ink);
font: 600 12px/20px var(--td-font);
}

.composerQueueSummary {
color: var(--td-ink-muted);
font: var(--caption);
}

.composerQueueClear {
flex: none;
padding: 2px 10px;
border: 1px solid var(--glass-border, var(--td-line));
border-radius: 999px;
background: transparent;
color: var(--td-ink-muted);
font: 600 11px/18px var(--td-font);
cursor: pointer;
transition:
background var(--td-dur-normal) var(--td-ease),
color var(--td-dur-normal) var(--td-ease);
}

.composerQueueClear:disabled {
opacity: 0.5;
cursor: default;
}

.composerQueueClear:hover:not(:disabled) {
background: var(--glass-bg-light, var(--td-surface-2));
color: var(--text-secondary, var(--fg-muted));
font-size: 12px;
line-height: 20px;
pointer-events: none;
user-select: none;
color: var(--td-ink);
}

.composerQueueNotice {
margin: 2px 0 var(--sp-xxs);
color: var(--td-ink-subtle);
font: var(--caption);
}

.composerQueueList {
display: flex;
flex-direction: column;
gap: var(--sp-xxs);
margin: 0;
padding: 0;
list-style: none;
}

.composerQueueItem {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--sp-xxs) var(--sp-10);
padding: var(--sp-xxs) var(--sp-10);
border: 1px solid var(--glass-border, var(--td-line));
border-radius: var(--r-md);
background: var(--glass-bg-light, var(--td-surface-2));
}

.composerQueueItem[data-status='dispatching'] {
border-color: color-mix(in srgb, var(--td-accent) 45%, var(--td-line));
}

.composerQueueItem[data-status='retrying'] {
border-color: color-mix(in srgb, var(--td-warning) 45%, var(--td-line));
}

.composerQueueItem[data-status='failed'] {
border-color: color-mix(in srgb, var(--td-danger) 45%, var(--td-line));
}

.composerQueueIndex {
display: inline-flex;
flex: none;
align-items: center;
justify-content: center;
width: 18px;
height: 18px;
border-radius: 999px;
background: var(--td-surface-3);
color: var(--td-ink-muted);
font: 600 11px/1 var(--td-font);
}

.composerQueueBody {
display: flex;
flex: 1 1 240px;
flex-direction: column;
min-width: 0;
}

.composerQueuePreview {
color: var(--td-ink);
font: var(--caption);
overflow-wrap: anywhere;
}

.composerQueueMeta {
display: flex;
flex-wrap: wrap;
gap: 0 var(--sp-10);
color: var(--td-ink-subtle);
font: 12px/18px var(--td-font);
}

.composerQueueAgent {
color: var(--td-ink-muted);
font-weight: 600;
}

.composerQueueState {
color: var(--td-ink-subtle);
}

.composerQueueItem[data-status='failed'] .composerQueueState {
color: var(--td-danger);
}

.composerQueueItem[data-status='retrying'] .composerQueueState {
color: var(--td-warning);
}

.composerQueueRetarget {
flex: none;
max-width: 160px;
padding: 2px 8px;
border: 1px solid var(--glass-border, var(--td-line));
border-radius: var(--r-md);
background: var(--td-surface);
color: var(--td-ink-muted);
font: 12px/18px var(--td-font);
}

.composerQueueRetarget:disabled {
opacity: 0.5;
}

.composerQueueActions {
display: flex;
flex: none;
align-items: center;
gap: var(--sp-xxs);
}

.composerQueueActionButton {
display: inline-flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
padding: 0;
border: 1px solid var(--glass-border, var(--td-line));
border-radius: var(--r-md);
background: transparent;
color: var(--td-ink-muted);
font: 600 12px/1 var(--td-font);
cursor: pointer;
transition:
background var(--td-dur-normal) var(--td-ease),
color var(--td-dur-normal) var(--td-ease);
}

.composerQueueActionButton:disabled {
opacity: 0.4;
cursor: default;
}

.composerQueueActionButton:hover:not(:disabled) {
background: var(--glass-bg-light, var(--td-surface-2));
color: var(--td-ink);
}

.composerQueueTextButton {
flex: none;
padding: 2px 10px;
border: 1px solid var(--glass-border, var(--td-line));
border-radius: 999px;
background: transparent;
color: var(--td-ink-muted);
font: 600 11px/18px var(--td-font);
cursor: pointer;
transition:
background var(--td-dur-normal) var(--td-ease),
color var(--td-dur-normal) var(--td-ease);
}

.composerQueueTextButton:disabled {
opacity: 0.5;
cursor: default;
}

.composerQueueTextButton:hover:not(:disabled) {
background: var(--glass-bg-light, var(--td-surface-2));
color: var(--td-ink);
}

/* ── #1819 pending-approval reminder strip (badge/count + jump) ── */
Expand Down Expand Up @@ -3503,7 +3719,7 @@
.workspaceHeader,
.mainchainStrip,
.composer,
.pendingIntentBadge,
.composerQueue,
.pendingApprovalStrip,
.replyToBar,
.quoteBar,
Expand Down
Loading