import type { RewriteTree } from './BlockTree' import type { ChatState } from './qa' import { useEffect, useMemo, useReducer, useRef } from 'react' import { chatReducer, EMPTY_CHAT, newAskId, unfinished } from './qa' import { toAskImage } from './qaImages' import { ANSWER_FRAME_MS } from './useQa' import { onMessage, postMessage } from './vscode' /** * 按注释规范重写:收扩展端的重写记录、当前一轮的进展,给块树用的状态和动作。和问 AI 同一套: * 排着队的、进行中的、出错被叫停留着的那一轮记在扩展端,页面重新加载后随 qa 消息的 rewriteCurrent 接上。profile 下拉是自己的,和问 AI 的互不影响。 */ export function useRewrite(): RewriteTree { const [state, dispatch] = useReducer(chatReducer, EMPTY_CHAT) useEffect(() => { /** 攒着没画的进展:askId → 最新的一份 */ const pending = new Map() let timer: ReturnType | undefined const flush = (): void => { timer = undefined for (const [askId, progress] of pending) dispatch({ type: 'answer', askId, ...progress }) pending.clear() } const off = onMessage((msg) => { switch (msg.type) { case 'qa': dispatch({ type: 'sync', available: msg.available, threads: msg.rewrites, current: msg.rewriteCurrent }) return case 'profiles': if (msg.kind === 'rewrite') dispatch({ type: 'profiles', profiles: { options: msg.options, selected: msg.selected } }) return case 'rewriteProgress': pending.set(msg.askId, { question: msg.question, text: msg.text, ...(msg.activity !== undefined && { activity: msg.activity }), ...(msg.queued && { queued: true }) }) timer ??= setTimeout(flush, ANSWER_FRAME_MS) return case 'rewriteEnd': clearTimeout(timer) flush() dispatch({ type: 'answerEnd', end: msg }) } }) return () => { clearTimeout(timer) off() } }, []) /** 动作里读眼下的状态(这块是不是已经在写或排着队) */ const current = useRef(state) current.current = state return useMemo((): RewriteTree => ({ state, onSelectProfile: id => postMessage({ type: 'selectProfile', kind: 'rewrite', id }), onRewrite: (anchor) => { if (current.current.asks.some(ask => ask.anchor === anchor && unfinished(ask))) return const askId = newAskId() // 那句技能命令由扩展端拼好,开始后随 rewriteProgress 报上来 dispatch({ type: 'ask', askId, anchor, question: '', images: [] }) postMessage({ type: 'rewriteComment', askId, anchor, images: [] }) }, onAsk: (anchor, request, images) => { const askId = newAskId() dispatch({ type: 'ask', askId, anchor, question: request, images: images.map(image => image.url) }) postMessage({ type: 'rewriteComment', askId, anchor, request, images: images.map(toAskImage) }) }, onStop: askId => postMessage({ type: 'stopRewrite', askId }), onDelete: (anchor, entryId) => postMessage({ type: 'deleteRewrite', anchor, entryId }), onOpen: anchor => dispatch({ type: 'open', anchor }), onClose: (anchor) => { dispatch({ type: 'close', anchor }) postMessage({ type: 'forgetRound', kind: 'rewrite', anchor }) }, }), [state]) }