新增: 待发箱抽屉与对话体验前端批
- 待发箱抽屉: ChatInput ⏰ 入口 + OutboxDrawer + useAiOutbox IPC 封装
- U2 进度语义三层: 内容层(最近动作) + 静默三档降速脉冲, 永不自动判死
- 流式心跳自愈: 竞态窗口旧任务迟到完成事件误收敛的自愈判定(949 前端兜底)
- 953 截断提示: 末条气泡渲染截断标记 + 继续输出按钮, 事件契约透传
This commit is contained in:
@@ -815,6 +815,23 @@ async function onInstallObscura(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 看板 953:截断气泡「继续输出」按钮。回复被 provider max_tokens 上限腰斩时,注入续写指令
|
||||
* 走既有发送通道(复用 store.sendMessage,对齐 onInstallObscura 模式),模型从上条回复的
|
||||
* 中断处衔接输出,不再整段重发。streaming 中按钮 disabled(模板绑定);失败 toast 对齐
|
||||
* handleRetry 错误处理模式。
|
||||
*/
|
||||
async function onContinueAfterTruncation(): Promise<void> {
|
||||
try {
|
||||
await store.sendMessage(t('ai.continueOutputPrompt'))
|
||||
await nextTick()
|
||||
scrollToBottom()
|
||||
} catch (e) {
|
||||
const msg = e instanceof Error ? e.message : String(e)
|
||||
emit('toast', { msg: t('aiChat.sendFailed', { msg }), type: 'error' })
|
||||
}
|
||||
}
|
||||
|
||||
// ── F-15 阶段2: 消息分段(归档段折叠) ──
|
||||
// ChatMessage.status 渲染语义(types.ts AiMessage.status 字面量联合定义):
|
||||
// null | undefined | 'active' → 正常消息(现状)
|
||||
@@ -1835,6 +1852,20 @@ defineExpose({
|
||||
<span class="ai-cursor"></span>
|
||||
</div>
|
||||
|
||||
<!-- 看板 953:max_tokens 截断提示条(仅末条 assistant 带标记时,气泡尾部弱提示)。
|
||||
点击继续输出=注入续写指令复用 sendMessage,衔接勿重复;streaming 中禁用防叠发。 -->
|
||||
<div v-if="isLastAi(item.msg) && item.msg.truncatedByMaxTokens" class="ai-msg-truncated">
|
||||
<span class="ai-msg-truncated-text">{{ $t('ai.truncatedByMaxTokens') }}</span>
|
||||
<button
|
||||
class="ai-msg-act"
|
||||
:disabled="store.state.streaming"
|
||||
@click.stop="onContinueAfterTruncation"
|
||||
>
|
||||
<svg aria-hidden="true" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 11-2.12-9.36L23 10"/></svg>
|
||||
<span>{{ $t('ai.continueOutput') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- UX-03 / 错误气泡操作入口(重试 / 去设置) -->
|
||||
<div v-if="item.msg.isError" class="ai-msg-actions ai-msg-actions--error">
|
||||
<span v-if="!canRetry(item.msg)" class="ai-msg-not-retryable">{{ $t('ai.notRetryable') }}</span>
|
||||
@@ -2458,6 +2489,23 @@ defineExpose({
|
||||
color: var(--df-accent);
|
||||
}
|
||||
|
||||
/* 看板 953:max_tokens 截断弱提示条(末条 assistant 气泡尾部,warning 弱化不着色抢焦) */
|
||||
.ai-msg-truncated {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-top: 4px;
|
||||
padding: 3px 8px;
|
||||
border-radius: var(--df-radius-sm);
|
||||
background: color-mix(in srgb, var(--df-warning, #ffc107) 10%, transparent);
|
||||
width: fit-content;
|
||||
}
|
||||
.ai-msg-truncated-text {
|
||||
font-size: 10.5px;
|
||||
line-height: 1.4;
|
||||
color: var(--df-text-dim);
|
||||
}
|
||||
|
||||
/* -- Streaming Cursor -- */
|
||||
.ai-cursor {
|
||||
display: inline-block;
|
||||
|
||||
Reference in New Issue
Block a user