✨ feat(vscode): 重写注释留会话记录、能接着说,允许画原型图

每块的重写记录存进 .spx/comment-rewrites.json(和问答分开,改名、删文件跟着走),块下面同问答区:灰字历史、当前这一轮、输入框;输入框里的话接着这块最近一次重写的会话说,交回新正文照样写回,写回失败也记下原因。
重写跑在 claude --restricted 下:只放开读搜、OpenPencil 的 op、建 prototypes 目录、改草稿目录,写注释的插件单独加载;超时 15 分钟。
进行中的、出错被叫停留着的那一轮记在扩展端,切走标签页再切回来照样接上(问 AI 同一套)。
This commit is contained in:
cruldra committed 2026-10-12 13:45:37 +11:00
1 parent ae4e2160ef
commit c9f60f5b30
32 files changed
+1654 -860

No files matched your search

@@ -1,4 +1,5 @@
import type { BlockTreeState } from './BlockTree'
import type { BlockTreeState, RewriteTree } from './BlockTree'
import type { ChatState } from './qa'
import type { BlockView, KindBadge } from './types'
import { renderToStaticMarkup } from 'react-dom/server'
import { describe, expect, it, vi } from 'vitest'
@@ -11,6 +12,7 @@ import { pythonZhTranslator } from '../../../src/codeReader/translators/pythonZh
import { BlockLevel } from './BlockTree'
import { applyFilter } from './filter'
import { FilterBar } from './FilterBar'
import { EMPTY_CHAT } from './qa'
import { collapsedIds } from './viewState'
// 高亮的上下文所在的模块、编辑框都连着和 VS Code 收发消息的那一套,渲染成 HTML 用不着
@@ -58,7 +60,12 @@ const CARD: BlockView = {
],
}
function state(collapsed: ReadonlySet<string>): BlockTreeState {
/** 重写注释的状态和动作:动作什么都不做 */
function rewriteTree(state: ChatState): RewriteTree {
return { state, profiles: { options: [], selected: '' }, onRewrite: () => {}, onAsk: () => {}, onStop: () => {}, onDelete: () => {}, onOpen: () => {}, onClose: () => {}, onSelectProfile: () => {} }
}
function state(collapsed: ReadonlySet<string>, rewrite: ChatState = { ...EMPTY_CHAT, available: true }): BlockTreeState {
return {
edit: {
version: 1,
@@ -66,10 +73,8 @@ function state(collapsed: ReadonlySet<string>): BlockTreeState {
focusId: null,
onFocused: () => {},
onToggleEdit: () => {},
rewrites: new Map(),
onRewriteComment: () => {},
onStopRewrite: () => {},
},
rewrite: rewriteTree(rewrite),
collapsed,
hidden: new Set(),
temporary: new Set(),
@@ -197,6 +202,19 @@ describe('「注释」一行和「重写注释」按钮', () => {
inToolbar(markup, '按注释规范重写')
})
it('重写过的块:下面一行「重写过 N 次」带灰字历史;记录收着时按钮先展开它,展开着时再点才重写', () => {
const block = view('5', 'statement', 'save()', { commentTarget: { kind: 'replace', startLine: 0, endLine: 0 } })
const entry = { id: 'r1', question: '/ecc:writing-code-comments a.py:1-1', answer: '保存。', createdAt: '2026-09-25T10:00:00.000Z' }
const closed = { ...EMPTY_CHAT, available: true, threads: [{ anchor: block.anchor, entries: [entry] }] }
const collapsedMarkup = renderToStaticMarkup(<BlockLevel blocks={[block]} tree={state(new Set(), closed)} />)
expect(collapsedMarkup.replace(/<[^>]*>/g, '')).toContain('重写过 1 次')
inToolbar(collapsedMarkup, '展开重写记录')
const opened = renderToStaticMarkup(<BlockLevel blocks={[block]} tree={state(new Set(), { ...closed, open: [block.anchor] })} />)
expect(opened).toMatch(/role="group" aria-label="块操作"><button[^>]*aria-label="按注释规范重写"/)
expect(opened).toContain('<section class="cr-qa" aria-label="按注释规范重写">')
expect(opened).toContain('aria-label="对这段注释的修改意见"')
})
it('还没有注释的块:按钮同样在右上角那排,叫「按注释规范写注释」;不能写注释的块没有按钮', () => {
inToolbar(html(view('3', 'statement', 'save()', { commentTarget: { kind: 'insert', line: 0 } })), '按注释规范写注释')
expect(html(view('4', 'statement', 'save()'))).not.toContain('按注释规范')