Files
superwork/vscode/webview-ui/src/codeReader/useRewrite.ts
T

80 lines
3.4 KiB
TypeScript

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<ChatState>, EMPTY_CHAT)
useEffect(() => {
/** 攒着没画的进展:askId → 最新的一份 */
const pending = new Map<string, { question: string, text: string, activity?: string, queued?: boolean }>()
let timer: ReturnType<typeof setTimeout> | 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])
}