✨ 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

+20 -1
View File
@@ -16,6 +16,8 @@ export interface PathMapFile<T> {
label: string
/** 认一条的值:不合法的返回 undefined,这条就跳过;合法的可以顺手规范成统一写法 */
parse: (value: unknown) => T | undefined
/** 一条值带着的文件(相对工作区文件夹,`/` 分隔,例如问答的图片):改表后不再有哪条带着的,跟着删掉 */
attachments?: (value: T) => string[]
}
/** 规范成表的键:去掉 `./`、重复和结尾的 `/`;工作区文件夹本身、文件夹外面的路径没有键。 */
@@ -62,7 +64,7 @@ export function changedKeys<T>(before: PathMap<T>, after: PathMap<T>): string[]
return [...keys].filter(key => JSON.stringify(before[key]) !== JSON.stringify(after[key]))
}
function fileIn(workspaceRoot: string, file: PathMapFile<unknown>): string {
function fileIn<T>(workspaceRoot: string, file: PathMapFile<T>): string {
return path.join(workspaceRoot, ...file.path.split('/'))
}
@@ -100,3 +102,20 @@ export async function writePathMap<T>(workspaceRoot: string, file: PathMapFile<T
const sorted = Object.fromEntries(Object.entries(map).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0))
await fsp.writeFile(target, `${JSON.stringify(sorted, null, 2)}\n`, 'utf8')
}
/** 改表前带着、改表后没有哪条再带着的文件(见 PathMapFile.attachments)。 */
export function detachedFiles<T>(file: PathMapFile<T>, before: PathMap<T>, after: PathMap<T>): string[] {
const { attachments } = file
if (!attachments)
return []
const kept = new Set(Object.values(after).flatMap(attachments))
return [...new Set(Object.values(before).flatMap(attachments))].filter(attachment => !kept.has(attachment))
}
/** 删掉工作区文件夹里的这些文件(相对路径);已经没了的不算错,跑到文件夹外面的路径不碰。 */
export async function removeFiles(workspaceRoot: string, relativePaths: readonly string[]): Promise<void> {
await Promise.all(relativePaths.flatMap((relativePath) => {
const key = normalizeKey(relativePath)
return key === undefined ? [] : [fsp.rm(path.join(workspaceRoot, ...key.split('/')), { force: true })]
}))
}