✨ feat(vscode): 问 AI 输入框能粘贴、拖进图片随问题发给 Claude Code,缩略图能点开看大图,图片存进 .spx/code-qa 跟着问答删

This commit is contained in:
cruldra committed 2026-09-26 23:15:02 +08:00
1 parent d9a5c3673e
commit 36d842e18e
28 files changed
+825 -121

No files matched your search

@@ -1,4 +1,5 @@
import type { CustomTextEditorProvider, Disposable, TextDocument, TextDocumentShowOptions, Uri, WebviewPanel } from 'vscode'
import type { ClaudeImage } from '../../cc/spawnClaude'
import type { BlockView } from '../blockView'
import type { RuleLocation, RuleReference } from '../contract'
import type { CodeQa } from '../qa/codeQa'
@@ -45,7 +46,9 @@ export class PreviewEditorProvider implements CustomTextEditorProvider {
) {}
resolveCustomTextEditor(document: TextDocument, panel: WebviewPanel): void {
panel.webview.options = { enableScripts: true, localResourceRoots: [webviewUiRoot(this.extensionUri)] }
// 问答带的图片(工作区文件夹的 .spx/code-qa/)也要能读,历史里才画得出缩略图
const imageRoot = this.qa.imageRoot(document.uri)
panel.webview.options = { enableScripts: true, localResourceRoots: [webviewUiRoot(this.extensionUri), ...(imageRoot ? [imageRoot] : [])] }
panel.webview.html = webviewHtml(panel.webview, this.extensionUri, 'code-reader.html')
const key = document.uri.toString()
const preview = new Preview(document, panel, this, this.registry, this.runtime, this.filter, this.reveals.get(key))
@@ -219,7 +222,7 @@ class Preview {
private postQa(): void {
if (this.ready)
this.post(this.owner.qa.view(this.document.uri))
this.post(this.owner.qa.view(this.document.uri, file => this.panel.webview.asWebviewUri(file).toString()))
}
/** profile 列表每次现读(同看板),读完页面还在才发。 */
@@ -233,14 +236,14 @@ class Preview {
* 问 anchor 那块代码:按最近一次发出去的块树找到它,把完整问题和追问的话都交给 CodeQa,接不接着上次的会话由它定。
* 找不到这块(代码刚改过、还没重新发)或问不了时,直接回一条失败。
*/
private async ask(askId: string, anchor: string, question: string): Promise<void> {
private async ask(askId: string, anchor: string, question: string, images: ClaudeImage[]): Promise<void> {
const at = locate(this.document.uri)
const subject = at && this.sent && { path: at.key, languageId: this.document.languageId, ...this.sent, anchor }
const prompt = subject && askPrompt(subject, question)
const followUp = subject && followUpPrompt(subject, question)
const refused = prompt === undefined || followUp === undefined
? '这块代码刚改过,找不到了。等预览刷新后重新点「问 AI」再问。'
: await this.owner.qa.ask(this.document.uri, { askId, anchor, question, prompt, followUp })
: await this.owner.qa.ask(this.document.uri, { askId, anchor, question, prompt, followUp, images })
if (refused !== undefined) {
this.post({ type: 'answerEnd', askId, status: 'failed', error: refused })
return
@@ -264,7 +267,7 @@ class Preview {
await this.push()
return
case 'ask':
await this.ask(msg.askId, msg.anchor, msg.question)
await this.ask(msg.askId, msg.anchor, msg.question, msg.images)
return
case 'selectAskProfile':
await this.owner.qa.profiles.select(msg.id)