✨ feat(vscode): 代码阅读加「问 AI」,块下面问 Claude Code,问答存进 .spx 挂在块下

This commit is contained in:
cruldra committed 2026-09-26 21:12:47 +08:00
1 parent badc722640
commit fb53bb727c
34 files changed
+2726 -43

No files matched your search

+37 -1
View File
@@ -1,10 +1,13 @@
import type { CSSProperties, ReactElement } from 'react'
import type { QaState } from './qa'
import type { BlockView } from './types'
import { ChevronDown, ChevronRight, EyeOff } from 'lucide-react'
import { Fragment } from 'react'
import { AskThread } from './AskThread'
import { BlockTools } from './BlockTools'
import { CodeEditor } from './CodeEditor'
import { PropertyGrid } from './PropertyGrid'
import { asksOf, historyOf } from './qa'
import { TextPreview } from './TextPreview'
export interface BlockTreeState {
@@ -25,6 +28,18 @@ export interface BlockTreeState {
onFocused: () => void
onToggleEdit: (id: string) => void
onToggleFold: (id: string) => void
/** 问 AI;文件不在工作区文件夹里(问答没地方存)时没有 */
qa?: QaTree
}
/** 问 AI 的状态和动作,块按自己的记号(anchor)取 */
export interface QaTree {
state: QaState
onToggle: (anchor: string) => void
onClose: (anchor: string) => void
onAsk: (anchor: string, question: string) => void
onStop: (askId: string) => void
onDelete: (anchor: string, entryId: string) => void
}
/** 块下面有没有画出来的子块:子块都被筛掉、被盖住时不留空的一层、不给折叠箭头。 */
@@ -64,6 +79,7 @@ export function BlockLevel({ blocks, owner, tree }: { blocks: BlockView[], owner
{blocks.filter(block => shown(block, tree)).map(block => (
<Fragment key={block.id}>
<BlockCard block={block} tree={tree} />
{tree.qa && <BlockQa key={block.anchor} anchor={block.anchor} qa={tree.qa} />}
{hasShownChildren(block, tree) && !tree.collapsed.has(block.id) && (
<BlockLevel blocks={block.children} owner={block} tree={tree} />
)}
@@ -74,6 +90,21 @@ export function BlockLevel({ blocks, owner, tree }: { blocks: BlockView[], owner
)
}
/** 块下面的问答区,紧跟在块后面、子块前面;输入框、展开状态跟着块的记号走,文档刷新块 id 变了也不丢 */
function BlockQa({ anchor, qa }: { anchor: string, qa: QaTree }): ReactElement {
return (
<AskThread
history={historyOf(qa.state, anchor)}
asks={asksOf(qa.state, anchor)}
open={qa.state.open.includes(anchor)}
onAsk={question => qa.onAsk(anchor, question)}
onStop={qa.onStop}
onDelete={entryId => qa.onDelete(anchor, entryId)}
onClose={() => qa.onClose(anchor)}
/>
)
}
/** 预览状态的内容:函数、类、枚举、导入、常量、变量块和库规则的卡片是属性表,其余是一行译文,带说明的译文下面接着说明。 */
function BlockPreview({ block, tree }: { block: BlockView, tree: BlockTreeState }): ReactElement {
if ('properties' in block)
@@ -135,7 +166,12 @@ function BlockCard({ block, tree }: { block: BlockView, tree: BlockTreeState }):
已被筛选隐藏,临时显示
</span>
)}
<BlockTools editing={editing} onToggleEdit={() => tree.onToggleEdit(block.id)} />
<BlockTools
editing={editing}
onToggleEdit={() => tree.onToggleEdit(block.id)}
asking={tree.qa?.state.open.includes(block.anchor)}
onToggleAsk={tree.qa && (() => tree.qa?.onToggle(block.anchor))}
/>
</div>
)
}