feat(quickdock): 子智能体时间线支持压缩提示与状态图标右置
上下文压缩发生时在时间线对应位置显示提示条(layers 图标 + 轮次);工具栏去掉右侧完成/进行中文字,状态图标(转圈/对勾/叉)移至行尾。 Co-authored-by: Astrion powered by DeepSeek-V4-Flash <astrion-agent@users.noreply.github.com>
This commit is contained in:
parent
10a2763f00
commit
af01030424
@ -30,15 +30,23 @@
|
||||
v-for="item in timelineItems"
|
||||
:key="item.key"
|
||||
class="qd-feed-row"
|
||||
:class="[item.kind === 'tool' ? 'feed-tool' : 'feed-text', { 'is-new': animatedKeys.has(item.key) }]"
|
||||
:class="[item.kind === 'tool' ? 'feed-tool' : item.kind === 'compression' ? 'feed-compressed' : 'feed-text', { 'is-new': animatedKeys.has(item.key) }]"
|
||||
>
|
||||
<template v-if="item.kind === 'tool'">
|
||||
<span v-if="item.state === 'running' || item.state === 'calling'" class="qd-tool-spinner"></span>
|
||||
<span v-else class="qd-tool-done">{{ item.state === 'failed' ? '✕' : '✓' }}</span>
|
||||
<span class="tool-name">{{ item.toolName }}</span>
|
||||
<span class="tool-param" :title="item.text">{{ item.text }}</span>
|
||||
<span class="tool-result" :class="{ 'is-error': item.state === 'failed' }">
|
||||
{{ item.stateLabel }}
|
||||
<span class="tool-status" :class="{ 'is-error': item.state === 'failed' }">
|
||||
<span v-if="item.state === 'running' || item.state === 'calling'" class="qd-tool-spinner"></span>
|
||||
<span v-else class="qd-tool-done">{{ item.state === 'failed' ? '✕' : '✓' }}</span>
|
||||
</span>
|
||||
</template>
|
||||
<template v-else-if="item.kind === 'compression'">
|
||||
<span class="feed-compressed__label">
|
||||
<svg class="feed-compressed__icon" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||||
<path d="M8 1.5 13.5 4v4.5c0 3-2.2 5-5.5 6-3.3-1-5.5-3-5.5-6V4L8 1.5Z" stroke="currentColor" stroke-width="1.3" stroke-linejoin="round"/>
|
||||
<path d="M5.5 8.2 7.2 9.9 11 5.8" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
{{ t('quickdock.contextCompressed', { round: item.round }) }}
|
||||
</span>
|
||||
</template>
|
||||
<template v-else>{{ item.content }}</template>
|
||||
@ -283,14 +291,6 @@ function isEntryTerminal(status?: string) {
|
||||
].includes(normalized);
|
||||
}
|
||||
|
||||
/** 工具条目状态标签:映射表存 key、使用处解析(规范 §3.3,模块顶层禁止调 t()) */
|
||||
const TOOL_STATE_LABEL_KEYS: Record<string, string> = {
|
||||
completed: 'quickdock.toolStateDone',
|
||||
failed: 'quickdock.toolStateFailed',
|
||||
calling: 'quickdock.toolStateCalling',
|
||||
running: 'quickdock.toolStateProgress'
|
||||
};
|
||||
|
||||
function buildText(entry: any): string {
|
||||
const tool = entry.tool || '';
|
||||
const args = entry.args || {};
|
||||
@ -309,7 +309,6 @@ interface ToolTimelineItem {
|
||||
kind: 'tool';
|
||||
key: string;
|
||||
state: string;
|
||||
stateLabel: string;
|
||||
toolName: string;
|
||||
text: string;
|
||||
}
|
||||
@ -320,10 +319,16 @@ interface OutputTimelineItem {
|
||||
content: string;
|
||||
}
|
||||
|
||||
const timelineItems = computed<(ToolTimelineItem | OutputTimelineItem)[]>(() => {
|
||||
interface CompressionTimelineItem {
|
||||
kind: 'compression';
|
||||
key: string;
|
||||
round: number;
|
||||
}
|
||||
|
||||
const timelineItems = computed<(ToolTimelineItem | OutputTimelineItem | CompressionTimelineItem)[]>(() => {
|
||||
void currentLocale.value;
|
||||
const entries = activityEntries.value || [];
|
||||
const rawItems: ({ kind: 'tool'; key: string; entry: any } | OutputTimelineItem)[] = [];
|
||||
const rawItems: ({ kind: 'tool'; key: string; entry: any } | OutputTimelineItem | CompressionTimelineItem)[] = [];
|
||||
let currentToolGroup: { kind: 'tool'; key: string; entry: any } | null = null;
|
||||
|
||||
const flushToolGroup = () => {
|
||||
@ -361,6 +366,17 @@ const timelineItems = computed<(ToolTimelineItem | OutputTimelineItem)[]>(() =>
|
||||
return;
|
||||
}
|
||||
|
||||
// 上下文压缩提示:在压缩发生的位置插入一条提示条目
|
||||
if (entry?.type === 'progress' && entry?.subtype === 'compression') {
|
||||
flushToolGroup();
|
||||
rawItems.push({
|
||||
kind: 'compression',
|
||||
key: `compression-${entry.ts || index}`,
|
||||
round: Number(entry.round) || 0,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!entry || entry.type !== 'progress' || !entry.tool) {
|
||||
return;
|
||||
}
|
||||
@ -401,7 +417,7 @@ const timelineItems = computed<(ToolTimelineItem | OutputTimelineItem)[]>(() =>
|
||||
flushToolGroup();
|
||||
|
||||
return rawItems.map((item) => {
|
||||
if (item.kind === 'output') {
|
||||
if (item.kind === 'output' || item.kind === 'compression') {
|
||||
return item;
|
||||
}
|
||||
const state = normalizeStatus(item.entry.status);
|
||||
@ -409,7 +425,6 @@ const timelineItems = computed<(ToolTimelineItem | OutputTimelineItem)[]>(() =>
|
||||
kind: 'tool' as const,
|
||||
key: item.key,
|
||||
state,
|
||||
stateLabel: t(TOOL_STATE_LABEL_KEYS[state] || 'quickdock.toolStateProgress'),
|
||||
toolName: item.entry.tool || t('common.tool'),
|
||||
text: buildText(item.entry)
|
||||
};
|
||||
|
||||
@ -697,15 +697,16 @@
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.qd-feed-row.feed-tool .tool-result {
|
||||
.qd-feed-row.feed-tool .tool-status {
|
||||
flex: none;
|
||||
margin-left: auto;
|
||||
font-size: 11.5px;
|
||||
color: var(--text-tertiary);
|
||||
font-variant-numeric: tabular-nums;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 14px;
|
||||
}
|
||||
|
||||
.qd-feed-row.feed-tool .tool-result.is-error {
|
||||
.qd-feed-row.feed-tool .tool-status.is-error .qd-tool-done {
|
||||
color: var(--state-danger);
|
||||
}
|
||||
|
||||
@ -733,6 +734,25 @@
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.qd-feed-row.feed-compressed {
|
||||
justify-content: center;
|
||||
padding: 2px 14px;
|
||||
}
|
||||
|
||||
.qd-feed-row.feed-compressed .feed-compressed__label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
font-size: 11.5px;
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
|
||||
.qd-feed-row.feed-compressed .feed-compressed__icon {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.qd-feed-row.feed-term {
|
||||
font-family: ui-monospace, 'SF Mono', Menlo, monospace;
|
||||
font-size: 12px;
|
||||
|
||||
@ -39,10 +39,9 @@ export default {
|
||||
toolRunCommand: 'Run command {command}',
|
||||
toolEditFile: 'Edit {path}',
|
||||
toolReadMedia: 'Read media file {path}',
|
||||
toolStateDone: 'Done',
|
||||
toolStateFailed: 'Failed',
|
||||
toolStateCalling: 'Calling...',
|
||||
toolStateProgress: 'In progress',
|
||||
|
||||
// —— Sub-agent detail panel: context compression notice ——
|
||||
contextCompressed: 'Context compressed (round {round})',
|
||||
|
||||
// —— File preview panel ——
|
||||
resizeWidthHint: 'Drag to resize width',
|
||||
|
||||
@ -46,10 +46,9 @@ export default {
|
||||
toolRunCommand: '运行命令 {command}',
|
||||
toolEditFile: '编辑 {path}',
|
||||
toolReadMedia: '读取媒体文件 {path}',
|
||||
toolStateDone: '完成',
|
||||
toolStateFailed: '失败',
|
||||
toolStateCalling: '调用中',
|
||||
toolStateProgress: '进行中',
|
||||
|
||||
// —— 子智能体详情面板:上下文压缩提示 ——
|
||||
contextCompressed: '上下文已压缩(第 {round} 轮)',
|
||||
|
||||
// —— 文件预览面板(FilePreviewPanel) ——
|
||||
resizeWidthHint: '拖拽调整宽度',
|
||||
|
||||
Loading…
Reference in New Issue
Block a user