✨ feat(vscode): profile 值编辑器改为多行+图片粘贴(图片落 .spx/profile-assets)
双击 profile 值单元格改为打开富编辑弹窗:多行 textarea + Ctrl+V 粘贴截图, ⌘/Ctrl+Enter 或点遮罩保存、Esc 取消。值仍是单个字符串,图片存磁盘 (内容寻址 sha1 命名,去重),值里只留 markdown 引用,不撑大 profiles.json。 折叠单元格显示首行 + 含图时加 🖼 徽标。key/列头仍走原内联输入。 - 抽公用 lib/imagePaste:同步探测(preventDefault) + 异步读取,新建工单改用它、行为不变 - 新增扩展侧 profileAssets handler:存图(image/* 校验)/读图(挡路径穿越) - 新增 useProfileAssets(requestId 关联并发) 与 ProfileCellEditor 弹窗 - 消息两侧同步 profile-asset/save|load|saved|loaded
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* profile 值编辑器的图片存/读桥:把 `profile-asset/save|load` 的一问一答封装成
|
||||
* Promise。用 requestId 关联并发请求——同时粘多张图时,回执按 id 各归各的。
|
||||
*/
|
||||
|
||||
import { useCallback, useEffect, useRef } from 'react'
|
||||
import { onMessage, postMessage } from '../lib/vscode'
|
||||
|
||||
interface Pending {
|
||||
resolve: (value: string) => void
|
||||
reject: (err: Error) => void
|
||||
}
|
||||
|
||||
export interface UseProfileAssetsResult {
|
||||
/** 存图,resolve 出工作区相对路径(写进 profile 值的引用)。 */
|
||||
saveAsset: (base64: string, mediaType: string) => Promise<string>
|
||||
/** 读图,resolve 出 data URL(渲染缩略图)。 */
|
||||
loadAsset: (assetPath: string) => Promise<string>
|
||||
}
|
||||
|
||||
export function useProfileAssets(): UseProfileAssetsResult {
|
||||
const pending = useRef<Map<string, Pending>>(new Map())
|
||||
|
||||
useEffect(() => {
|
||||
return onMessage((msg) => {
|
||||
if (msg.type === 'profile-asset/saved') {
|
||||
const p = pending.current.get(msg.requestId)
|
||||
if (!p)
|
||||
return
|
||||
pending.current.delete(msg.requestId)
|
||||
if (msg.error || !msg.assetPath)
|
||||
p.reject(new Error(msg.error ?? '保存失败'))
|
||||
else
|
||||
p.resolve(msg.assetPath)
|
||||
}
|
||||
else if (msg.type === 'profile-asset/loaded') {
|
||||
const p = pending.current.get(msg.requestId)
|
||||
if (!p)
|
||||
return
|
||||
pending.current.delete(msg.requestId)
|
||||
if (msg.error || !msg.dataUrl)
|
||||
p.reject(new Error(msg.error ?? '读取失败'))
|
||||
else
|
||||
p.resolve(msg.dataUrl)
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
|
||||
const newId = (): string => `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
||||
|
||||
const saveAsset = useCallback((base64: string, mediaType: string): Promise<string> => {
|
||||
const requestId = newId()
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
pending.current.set(requestId, { resolve, reject })
|
||||
postMessage({ type: 'profile-asset/save', requestId, base64, mediaType })
|
||||
})
|
||||
}, [])
|
||||
|
||||
const loadAsset = useCallback((assetPath: string): Promise<string> => {
|
||||
const requestId = newId()
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
pending.current.set(requestId, { resolve, reject })
|
||||
postMessage({ type: 'profile-asset/load', requestId, assetPath })
|
||||
})
|
||||
}, [])
|
||||
|
||||
return { saveAsset, loadAsset }
|
||||
}
|
||||
Reference in New Issue
Block a user