重构: 对话透明化 L1 + ③.2 审批拆分 + ⑥.4 @展开 + DAG 展示
修复 tool_result 压缩零效果(BUG-260628-01):单行/少行/JSON 大字符 串字段逃逸压缩的问题,基于实测数据(53/94 次迭代零节省)调参, 增加字符级保底截断,压缩有效率从 8% 提升至 ~85%+
This commit is contained in:
@@ -120,6 +120,25 @@
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<!-- ⑥.4 Phase 4: @项目展开摘要(输入区 enrichment 预览) -->
|
||||
<div v-if="hasEnrichment" class="ai-enrichment-bar">
|
||||
<button
|
||||
class="ai-enrichment-toggle"
|
||||
:class="{ 'ai-enrichment-toggle--expanded': enrichmentExpanded }"
|
||||
@click="enrichmentExpanded = !enrichmentExpanded"
|
||||
:title="enrichmentExpanded ? $t('aiChat.enrichmentCollapse') : $t('aiChat.enrichmentExpand')"
|
||||
:aria-label="enrichmentExpanded ? $t('aiChat.enrichmentCollapse') : $t('aiChat.enrichmentExpand')"
|
||||
>
|
||||
<svg class="ai-enrichment-chevron" width="10" height="10" viewBox="0 0 10 10" fill="currentColor">
|
||||
<path d="M2 3l3 4 3-4" />
|
||||
</svg>
|
||||
<span class="ai-enrichment-badge">{{ enrichmentBadgeText }}</span>
|
||||
</button>
|
||||
<div v-if="enrichmentExpanded" class="ai-enrichment-panel">
|
||||
<div class="ai-enrichment-label">{{ $t('aiChat.enrichmentLabel') }}</div>
|
||||
<pre class="ai-enrichment-body"><code>{{ enrichmentDetailText }}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ai-input-hint">{{ $t('aiChat.inputHint') }}</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -382,6 +401,90 @@ const mentionStart = ref(-1)
|
||||
// 后端据 mentionSpans resolve 投影成 Augmentation 注入;MessageList 据此精确切区间渲染 chip。
|
||||
const pendingMentionSpans = ref<MentionSpan[]>([])
|
||||
|
||||
// ── ⑥.4 Phase 4: @项目展开摘要(输入区 enrichment 预览) ──
|
||||
// 当用户 @[项目:xxx] 后,从 projectsStore 反查项目+关联任务/灵感,
|
||||
// 在输入区下方显式 badge + 可展开的上下文参考面板。
|
||||
interface ProjectEnrichment {
|
||||
id: string
|
||||
name: string
|
||||
status: string
|
||||
path: string | null
|
||||
taskCount: number
|
||||
ideaCount: number
|
||||
inProgressTasks: { title: string; status: string }[]
|
||||
pendingIdeas: { title: string; status: string }[]
|
||||
}
|
||||
|
||||
const enrichmentExpanded = ref(false)
|
||||
|
||||
/** @项目 mentions 经 stores 反查后的 enrichment 摘要列表 */
|
||||
const projectEnrichments = computed<ProjectEnrichment[]>(() => {
|
||||
const projectSpans = pendingMentionSpans.value.filter(sp => sp.kind === 'project')
|
||||
if (projectSpans.length === 0) return []
|
||||
const projects = (projectsStore.projects || []) as ProjectRecord[]
|
||||
const tasks = (projectsStore.tasks || []) as TaskRecord[]
|
||||
const ideas = (projectsStore.ideas || []) as IdeaRecord[]
|
||||
const results: ProjectEnrichment[] = []
|
||||
for (const span of projectSpans) {
|
||||
const project = projects.find(p => p.id === span.refId)
|
||||
if (!project) continue
|
||||
const relTasks = tasks.filter(t => t.project_id === project.id)
|
||||
// ideas linking to this project via promoted_to (project name matching)
|
||||
const relIdeas = ideas.filter(i => i.promoted_to === project.name || i.promoted_to === project.id)
|
||||
const running = relTasks.filter(t => t.status === 'in_progress').slice(0, 20)
|
||||
const pending = relIdeas.filter(i => i.status === 'pending_review').slice(0, 20)
|
||||
results.push({
|
||||
id: project.id,
|
||||
name: project.name,
|
||||
status: project.status as string,
|
||||
path: project.path,
|
||||
taskCount: relTasks.length,
|
||||
ideaCount: relIdeas.length,
|
||||
inProgressTasks: running.map(t => ({ title: t.title, status: t.status as string })),
|
||||
pendingIdeas: pending.map(i => ({ title: i.title, status: i.status as string })),
|
||||
})
|
||||
}
|
||||
return results
|
||||
})
|
||||
|
||||
/** 是否有 enrichment 可展示 */
|
||||
const hasEnrichment = computed(() => projectEnrichments.value.length > 0)
|
||||
|
||||
/** 概要 badge 文本(汇总所有 @项目 entrichment) */
|
||||
const enrichmentBadgeText = computed(() => {
|
||||
const list = projectEnrichments.value
|
||||
if (!list.length) return ''
|
||||
const totalTasks = list.reduce((s, p) => s + p.taskCount, 0)
|
||||
const totalIdeas = list.reduce((s, p) => s + p.ideaCount, 0)
|
||||
const names = list.map(p => p.name).join('、')
|
||||
return t('aiChat.enrichmentBadge', { project: names, tasks: totalTasks, ideas: totalIdeas })
|
||||
})
|
||||
|
||||
/** 展开后的 enrichment 正文(mimic 后端 build_augmentation_segment 格式) */
|
||||
const enrichmentDetailText = computed(() => {
|
||||
const list = projectEnrichments.value
|
||||
if (!list.length) return ''
|
||||
const lines: string[] = []
|
||||
for (const p of list) {
|
||||
lines.push(`【项目】${p.name}(状态: ${p.status})`)
|
||||
if (p.path) lines.push(`目录: ${p.path}`)
|
||||
if (p.inProgressTasks.length > 0) {
|
||||
lines.push(`进行中任务(${p.taskCount}):`)
|
||||
for (const t of p.inProgressTasks) {
|
||||
lines.push(` - ${t.title} (${t.status})`)
|
||||
}
|
||||
}
|
||||
if (p.pendingIdeas.length > 0) {
|
||||
lines.push(`待评估灵感(${p.ideaCount}):`)
|
||||
for (const i of p.pendingIdeas) {
|
||||
lines.push(` - ${i.title} (${i.status})`)
|
||||
}
|
||||
}
|
||||
lines.push('')
|
||||
}
|
||||
return lines.join('\n')
|
||||
})
|
||||
|
||||
const mentionGroupLabel = computed(() => ({
|
||||
project: t('aiChat.mentionGroupProject'),
|
||||
task: t('aiChat.mentionGroupTask'),
|
||||
@@ -972,4 +1075,70 @@ defineExpose({
|
||||
margin-top: 4px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
/* ── ⑥.4 Phase 4: @项目展开摘要(输入区 enrichment 预览) ── */
|
||||
.ai-enrichment-bar {
|
||||
margin-top: 6px;
|
||||
border: 0.5px solid color-mix(in srgb, var(--df-accent) 30%, transparent);
|
||||
border-radius: var(--df-radius);
|
||||
overflow: hidden;
|
||||
}
|
||||
.ai-enrichment-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
width: 100%;
|
||||
padding: 4px 8px;
|
||||
border: none;
|
||||
background: color-mix(in srgb, var(--df-accent) 10%, transparent);
|
||||
color: var(--df-text);
|
||||
cursor: pointer;
|
||||
font-size: 11px;
|
||||
text-align: left;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
.ai-enrichment-toggle:hover {
|
||||
background: color-mix(in srgb, var(--df-accent) 18%, transparent);
|
||||
}
|
||||
.ai-enrichment-toggle--expanded {
|
||||
border-bottom: 0.5px solid color-mix(in srgb, var(--df-accent) 20%, transparent);
|
||||
}
|
||||
.ai-enrichment-chevron {
|
||||
flex-shrink: 0;
|
||||
color: var(--df-text-dim);
|
||||
transition: transform 0.15s;
|
||||
}
|
||||
.ai-enrichment-toggle--expanded .ai-enrichment-chevron {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
.ai-enrichment-badge {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: var(--df-accent);
|
||||
font-weight: 500;
|
||||
}
|
||||
.ai-enrichment-panel {
|
||||
padding: 6px 8px;
|
||||
background: var(--df-bg);
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.ai-enrichment-label {
|
||||
font-size: 10px;
|
||||
color: var(--df-text-dim);
|
||||
margin-bottom: 4px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.ai-enrichment-body {
|
||||
margin: 0;
|
||||
font-size: 11px;
|
||||
line-height: 1.6;
|
||||
color: var(--df-text);
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
font-family: var(--df-font-mono, ui-monospace, monospace);
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user