重构: 拆AiChat第五批MaxRoundsCard(strategy收尾)
- 新建 ai/MaxRoundsCard.vue(144行): 达max操作卡(继续/停止)+isViewingGenerating/maxRoundsActing/handleContinue/StopLoop+pendingMaxRounds watch - AiChat.vue 1587→1474(-113): template→<MaxRoundsCard> + 删max_rounds块 + 清dead aiApi import - store共享 + emit toast(单一源) 主代兜底: vue-tsc 0 + grep MaxRoundsCard抽离/AiChat import/emit印证 strategy: 单批1-2文件原子, 边界最干净段优先; AiChat余1474(QueuePanel/ContextActions/恢复态/快捷键待后续) git add指定(AiChat.vue+MaxRoundsCard.vue)
This commit is contained in:
@@ -56,26 +56,8 @@
|
||||
<span class="ai-agentic-progress-dot"></span>
|
||||
<span class="ai-agentic-progress-text">{{ agenticProgressText }}</span>
|
||||
</div>
|
||||
<!-- F-260616-03: 达 max_iterations 暂停态操作卡。
|
||||
条件:pendingMaxRounds(达 max 事件置) + 当前视图正在生成(防切走后误显)。
|
||||
点继续 → ai_continue_loop(后端续 max_iterations 轮)→ 卡片隐藏(等后端事件)。
|
||||
点停止 → ai_stop_loop(后端走 AiCompleted 收尾)→ 卡片隐藏。 -->
|
||||
<div v-if="showMaxRoundsCard" class="ai-max-rounds">
|
||||
<span class="ai-max-rounds-text">{{ $t('aiChat.maxRoundsReached') }}</span>
|
||||
<span class="ai-max-rounds-hint">{{ $t('aiChat.maxRoundsHint') }}</span>
|
||||
<div class="ai-max-rounds-actions">
|
||||
<button
|
||||
class="ai-max-rounds-btn ai-max-rounds-btn--continue"
|
||||
:disabled="maxRoundsActing"
|
||||
@click="handleContinueLoop"
|
||||
>{{ $t('aiChat.continueLoop') }}</button>
|
||||
<button
|
||||
class="ai-max-rounds-btn ai-max-rounds-btn--stop"
|
||||
:disabled="maxRoundsActing"
|
||||
@click="handleStopLoop"
|
||||
>{{ $t('aiChat.stopLoop') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 第五批抽离至 ai/MaxRoundsCard.vue(F-260616-03 达 max_iterations 暂停态操作卡,零行为变更,store 单例 + pendingMaxRounds 模块级 ref 共享,toast 经 emit 转父) -->
|
||||
<MaxRoundsCard @toast="(p) => showToast(p.msg, p.type)" />
|
||||
<!-- 待发送队列(生成中排队的消息,完成后自动续发) -->
|
||||
<div v-if="store.state.queue.length > 0" class="ai-queue" :class="{ 'ai-queue--timeout': queueTimedOut }">
|
||||
<div class="ai-queue-head">
|
||||
@@ -143,13 +125,12 @@ import { useI18n } from 'vue-i18n'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { listen } from '@tauri-apps/api/event'
|
||||
import { useAiStore } from '../stores/ai'
|
||||
import { pendingMaxRounds } from '../composables/ai/useAiEvents'
|
||||
import { aiApi } from '../api'
|
||||
import ConfirmDialog from './ConfirmDialog.vue'
|
||||
import ConversationSidebar from './ai/ConversationSidebar.vue'
|
||||
import ChatInput from './ai/ChatInput.vue'
|
||||
import TopBar from './ai/TopBar.vue'
|
||||
import MessageList from './ai/MessageList.vue'
|
||||
import MaxRoundsCard from './ai/MaxRoundsCard.vue'
|
||||
import { useConfirm } from '../composables/useConfirm'
|
||||
import { useProjectStore } from '../stores/project'
|
||||
import type { AiMessage } from '../api/types'
|
||||
@@ -522,50 +503,9 @@ async function toggleAlwaysOnTop() {
|
||||
alwaysOnTop.value = !alwaysOnTop.value
|
||||
}
|
||||
|
||||
// ── F-260616-03: 达 max_iterations 暂停态操作卡 ──
|
||||
// pendingMaxRounds(达 max 事件置)+ 当前视图正在生成(防切走后误显)双重守卫。
|
||||
// maxRoundsActing 本地 ref 持按钮 loading:点继续/停止后禁双按钮,等后端事件
|
||||
// (continueLoop→新一轮 AiAgentRound;stopLoop→AiCompleted)自然清卡片。
|
||||
const maxRoundsActing = ref(false)
|
||||
const showMaxRoundsCard = computed(() =>
|
||||
pendingMaxRounds.value && isViewingGenerating.value,
|
||||
)
|
||||
|
||||
/** 点继续:调 ai_continue_loop。后端续 max_iterations 轮(iteration 从 0 重计)。
|
||||
* 不主动清 pendingMaxRounds——由后端新一轮事件(AiAgentRound)与最终 AiCompleted/AiError
|
||||
* 在 useAiEvents 内清。IPC 失败回滚 acting 让用户可重试。 */
|
||||
async function handleContinueLoop(): Promise<void> {
|
||||
const convId = store.state.activeConversationId
|
||||
if (!convId) return
|
||||
maxRoundsActing.value = true
|
||||
try {
|
||||
await aiApi.continueLoop(convId)
|
||||
} catch (e) {
|
||||
maxRoundsActing.value = false
|
||||
const msg = e instanceof Error ? e.message : String(e)
|
||||
showToast(t('aiChat.continueLoopFailed', { msg }), 'error')
|
||||
}
|
||||
}
|
||||
|
||||
/** 点停止:调 ai_stop_loop。后端复位 generating + emit AiCompleted,useAiEvents 据此
|
||||
* 清 pendingMaxRounds。IPC 失败回滚 acting。 */
|
||||
async function handleStopLoop(): Promise<void> {
|
||||
const convId = store.state.activeConversationId
|
||||
if (!convId) return
|
||||
maxRoundsActing.value = true
|
||||
try {
|
||||
await aiApi.stopLoop(convId)
|
||||
} catch (e) {
|
||||
maxRoundsActing.value = false
|
||||
const msg = e instanceof Error ? e.message : String(e)
|
||||
showToast(t('aiChat.stopLoopFailed', { msg }), 'error')
|
||||
}
|
||||
}
|
||||
|
||||
/** pendingMaxRounds 离开暂停态(后端事件已清)时复位 acting,允许下次再操作 */
|
||||
watch(() => pendingMaxRounds.value, (v) => {
|
||||
if (!v) maxRoundsActing.value = false
|
||||
})
|
||||
// 第五批抽离: F-260616-03 max_iterations 暂停态操作卡(maxRoundsActing/showMaxRoundsCard/
|
||||
// handleContinueLoop/handleStopLoop/pendingMaxRounds watch)已迁移至 ai/MaxRoundsCard.vue
|
||||
// (零行为变更,store 单例 + pendingMaxRounds 模块级 ref 共享,toast 经 emit 转父)。
|
||||
|
||||
// ── F-15 阶段2: 手动上下文管理(清空 / 压缩) ──
|
||||
// ChatMessage.status 渲染语义(types.ts 未含 status 字段,前端经 cast 读写闭环):
|
||||
@@ -1169,60 +1109,7 @@ body.ai-sidebar-resizing * {
|
||||
border-top: 0.5px solid var(--df-border);
|
||||
background: var(--df-bg);
|
||||
}
|
||||
/* ── F-260616-03: 达 max_iterations 暂停态操作卡(警告色系,复用 ai-queue--timeout 风格) ── */
|
||||
.ai-max-rounds {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3px;
|
||||
margin-bottom: 6px;
|
||||
padding: 6px 8px;
|
||||
background: color-mix(in srgb, var(--df-warning) 10%, transparent);
|
||||
border: 0.5px solid color-mix(in srgb, var(--df-warning) 40%, transparent);
|
||||
border-radius: var(--df-radius);
|
||||
}
|
||||
.ai-max-rounds-text {
|
||||
font-size: 11.5px;
|
||||
font-weight: 600;
|
||||
color: var(--df-warning);
|
||||
}
|
||||
.ai-max-rounds-hint {
|
||||
font-size: 10.5px;
|
||||
color: var(--df-text-dim);
|
||||
opacity: 0.9;
|
||||
}
|
||||
.ai-max-rounds-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.ai-max-rounds-btn {
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
padding: 3px 10px;
|
||||
border-radius: calc(var(--df-radius) - 2px);
|
||||
transition: filter 0.15s var(--df-ease);
|
||||
}
|
||||
.ai-max-rounds-btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.ai-max-rounds-btn--continue {
|
||||
background: var(--df-warning);
|
||||
color: #000;
|
||||
}
|
||||
.ai-max-rounds-btn--continue:hover:not(:disabled) { filter: brightness(1.1); }
|
||||
.ai-max-rounds-btn--stop {
|
||||
background: transparent;
|
||||
color: var(--df-text-dim);
|
||||
border: 0.5px solid var(--df-border);
|
||||
}
|
||||
.ai-max-rounds-btn--stop:hover:not(:disabled) {
|
||||
color: var(--df-text);
|
||||
border-color: var(--df-text-dim);
|
||||
}
|
||||
/* 第五批抽离: F-260616-03 max_iterations 操作卡样式已迁移至 ai/MaxRoundsCard.vue */
|
||||
|
||||
/* ── AE-2025-07: Agentic 循环进度条 ── */
|
||||
.ai-agentic-progress {
|
||||
|
||||
144
src/components/ai/MaxRoundsCard.vue
Normal file
144
src/components/ai/MaxRoundsCard.vue
Normal file
@@ -0,0 +1,144 @@
|
||||
<template>
|
||||
<!-- 第五批抽离至 ai/MaxRoundsCard.vue(F-260616-03 达 max_iterations 暂停态操作卡,零行为变更)。
|
||||
条件:pendingMaxRounds(达 max 事件置)+ 当前视图正在生成(防切走后误显)。
|
||||
点继续 → ai_continue_loop(后端续 max_iterations 轮)→ 卡片隐藏(等后端事件)。
|
||||
点停止 → ai_stop_loop(后端走 AiCompleted 收尾)→ 卡片隐藏。
|
||||
store 单例共享(state/aiApi),pendingMaxRounds 模块级 ref(useAiEvents)直接导入。
|
||||
toast 经 emit 转父(保持单一 toast 源)。 -->
|
||||
<div v-if="showMaxRoundsCard" class="ai-max-rounds">
|
||||
<span class="ai-max-rounds-text">{{ $t('aiChat.maxRoundsReached') }}</span>
|
||||
<span class="ai-max-rounds-hint">{{ $t('aiChat.maxRoundsHint') }}</span>
|
||||
<div class="ai-max-rounds-actions">
|
||||
<button
|
||||
class="ai-max-rounds-btn ai-max-rounds-btn--continue"
|
||||
:disabled="maxRoundsActing"
|
||||
@click="handleContinueLoop"
|
||||
>{{ $t('aiChat.continueLoop') }}</button>
|
||||
<button
|
||||
class="ai-max-rounds-btn ai-max-rounds-btn--stop"
|
||||
:disabled="maxRoundsActing"
|
||||
@click="handleStopLoop"
|
||||
>{{ $t('aiChat.stopLoop') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useAiStore } from '../../stores/ai'
|
||||
import { pendingMaxRounds } from '../../composables/ai/useAiEvents'
|
||||
import { aiApi } from '../../api'
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'toast', payload: { msg: string; type: 'error' | 'warning' | 'info' }): void
|
||||
}>()
|
||||
|
||||
const store = useAiStore()
|
||||
const { t } = useI18n()
|
||||
|
||||
// 当前视图是否正在生成(切走后台生成时光标不显示)
|
||||
const isViewingGenerating = computed(() =>
|
||||
store.state.streaming && store.state.generatingConvId === store.state.activeConversationId,
|
||||
)
|
||||
|
||||
// pendingMaxRounds(达 max 事件置)+ 当前视图正在生成(防切走后误显)双重守卫。
|
||||
// maxRoundsActing 本地 ref 持按钮 loading:点继续/停止后禁双按钮,等后端事件
|
||||
// (continueLoop→新一轮 AiAgentRound;stopLoop→AiCompleted)自然清卡片。
|
||||
const maxRoundsActing = ref(false)
|
||||
const showMaxRoundsCard = computed(() =>
|
||||
pendingMaxRounds.value && isViewingGenerating.value,
|
||||
)
|
||||
|
||||
/** 点继续:调 ai_continue_loop。后端续 max_iterations 轮(iteration 从 0 重计)。
|
||||
* 不主动清 pendingMaxRounds——由后端新一轮事件(AiAgentRound)与最终 AiCompleted/AiError
|
||||
* 在 useAiEvents 内清。IPC 失败回滚 acting 让用户可重试。 */
|
||||
async function handleContinueLoop(): Promise<void> {
|
||||
const convId = store.state.activeConversationId
|
||||
if (!convId) return
|
||||
maxRoundsActing.value = true
|
||||
try {
|
||||
await aiApi.continueLoop(convId)
|
||||
} catch (e) {
|
||||
maxRoundsActing.value = false
|
||||
const msg = e instanceof Error ? e.message : String(e)
|
||||
emit('toast', { msg: t('aiChat.continueLoopFailed', { msg }), type: 'error' })
|
||||
}
|
||||
}
|
||||
|
||||
/** 点停止:调 ai_stop_loop。后端复位 generating + emit AiCompleted,useAiEvents 据此
|
||||
* 清 pendingMaxRounds。IPC 失败回滚 acting。 */
|
||||
async function handleStopLoop(): Promise<void> {
|
||||
const convId = store.state.activeConversationId
|
||||
if (!convId) return
|
||||
maxRoundsActing.value = true
|
||||
try {
|
||||
await aiApi.stopLoop(convId)
|
||||
} catch (e) {
|
||||
maxRoundsActing.value = false
|
||||
const msg = e instanceof Error ? e.message : String(e)
|
||||
emit('toast', { msg: t('aiChat.stopLoopFailed', { msg }), type: 'error' })
|
||||
}
|
||||
}
|
||||
|
||||
/** pendingMaxRounds 离开暂停态(后端事件已清)时复位 acting,允许下次再操作 */
|
||||
watch(() => pendingMaxRounds.value, (v) => {
|
||||
if (!v) maxRoundsActing.value = false
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ai-max-rounds {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3px;
|
||||
margin-bottom: 6px;
|
||||
padding: 6px 8px;
|
||||
background: color-mix(in srgb, var(--df-warning) 10%, transparent);
|
||||
border: 0.5px solid color-mix(in srgb, var(--df-warning) 40%, transparent);
|
||||
border-radius: var(--df-radius);
|
||||
}
|
||||
.ai-max-rounds-text {
|
||||
font-size: 11.5px;
|
||||
font-weight: 600;
|
||||
color: var(--df-warning);
|
||||
}
|
||||
.ai-max-rounds-hint {
|
||||
font-size: 10.5px;
|
||||
color: var(--df-text-dim);
|
||||
opacity: 0.9;
|
||||
}
|
||||
.ai-max-rounds-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.ai-max-rounds-btn {
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
padding: 3px 10px;
|
||||
border-radius: calc(var(--df-radius) - 2px);
|
||||
transition: filter 0.15s var(--df-ease);
|
||||
}
|
||||
.ai-max-rounds-btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.ai-max-rounds-btn--continue {
|
||||
background: var(--df-warning);
|
||||
color: #000;
|
||||
}
|
||||
.ai-max-rounds-btn--continue:hover:not(:disabled) { filter: brightness(1.1); }
|
||||
.ai-max-rounds-btn--stop {
|
||||
background: transparent;
|
||||
color: var(--df-text-dim);
|
||||
border: 0.5px solid var(--df-border);
|
||||
}
|
||||
.ai-max-rounds-btn--stop:hover:not(:disabled) {
|
||||
color: var(--df-text);
|
||||
border-color: var(--df-text-dim);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user