✨ 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

+36 -1
View File
@@ -1,5 +1,9 @@
import type { PathMapFile } from './pathMap'
import { promises as fsp } from 'node:fs'
import * as os from 'node:os'
import * as path from 'node:path'
import { describe, expect, it } from 'vitest'
import { changedKeys, dropEntries, entriesUnder, moveEntries, normalizeKey, placeEntries, toPathKey } from './pathMap'
import { changedKeys, detachedFiles, dropEntries, entriesUnder, moveEntries, normalizeKey, placeEntries, removeFiles, toPathKey } from './pathMap'
describe('路径的键', () => {
it('相对工作区文件夹、用 / 分隔', () => {
@@ -92,3 +96,34 @@ describe('变了哪些行', () => {
expect(changedKeys({ a: '1' }, { a: '1' })).toEqual([])
})
})
describe('条目带着的文件', () => {
const file: PathMapFile<string[]> = { path: '.spx/x.json', label: 'x', parse: () => undefined, attachments: files => files }
it('改表后没有哪条再带着的才删:删掉的条目、改掉的引用;别的条目还带着同一个文件的留着', () => {
const before = { 'a.py': ['.spx/f/1.png', '.spx/f/2.png'], 'b.py': ['.spx/f/3.png', '.spx/f/2.png'] }
expect(detachedFiles(file, before, { 'b.py': ['.spx/f/3.png', '.spx/f/2.png'] })).toEqual(['.spx/f/1.png'])
expect(detachedFiles(file, before, { 'a.py': ['.spx/f/1.png'] }).sort()).toEqual(['.spx/f/2.png', '.spx/f/3.png'])
expect(detachedFiles(file, before, before)).toEqual([])
})
it('不带文件的表什么都不删', () => {
expect(detachedFiles({ ...file, attachments: undefined }, { a: ['x'] }, {})).toEqual([])
})
it('删文件:已经没了的不算错,跑到工作区文件夹外面的路径不碰', async () => {
const parent = await fsp.mkdtemp(path.join(os.tmpdir(), 'path-map-'))
try {
const root = path.join(parent, 'work')
await fsp.mkdir(path.join(root, '.spx', 'f'), { recursive: true })
await fsp.writeFile(path.join(root, '.spx', 'f', '1.png'), '')
await fsp.writeFile(path.join(parent, 'outside.png'), '')
await removeFiles(root, ['.spx/f/1.png', '.spx/f/missing.png', '../outside.png', path.join(parent, 'outside.png')])
await expect(fsp.access(path.join(root, '.spx', 'f', '1.png'))).rejects.toThrow()
await expect(fsp.access(path.join(parent, 'outside.png'))).resolves.toBeUndefined()
}
finally {
await fsp.rm(parent, { recursive: true, force: true })
}
})
})