✨ feat(vscode): 工单挂原型插件,详情面板一键导入 Figma
- prototypeFile 走通 state → Issue → webview 全链路(gitea/youtrack 两个
loader、两份 KNOWN_STATE_FIELDS、issue/patch、webview types),按 planFile
同款当共享字段;路径校验换成 .json 版,允许绝对路径
- 详情面板「计划文件」下加「原型插件路径」行:双击开 manifest.json,
行尾按钮「在 Figma 中导入并打开」;没挂插件时只显示空态
- src/figma/:把 manifest 写进 Figma 桌面版的 localFileExtensions 再拉起
Figma。坑是 Figma 退出会用内存状态覆盖 settings.json,所以写之前先
osascript 优雅退出并轮询等它真没了(10s 等不到就让用户手动关,不强杀);
写前备份 settings.json.bak-<时间戳>;已导入过就跳过写入直接打开;
非 macOS 只把路径复制到剪贴板
- webhook 里 spec/plan 的 marker 同步改成表驱动,顺带认第三种 prototype
- 提示词:prototype.md 要求画完挂 marker;brainstorm-continue.md 新增
{prototypeRef} 占位符,有原型插件时让续聊先读它别从零重画,没有时
连同整段抹掉
Claude-Session: https://claude.ai/code/session_01Kgmb1LvrPmFX3NoUDB4zZC
This commit is contained in:
1 parent
def5f4e88c
commit
142af57d9d
24 files changed
+628
-85
No files matched your search
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* Figma 桌面版「已导入的开发插件」清单的纯逻辑:算新 id、查重、造条目。
|
||||
*
|
||||
* 清单落在 `~/Library/Application Support/Figma/settings.json` 的
|
||||
* `localFileExtensions` 数组里,一条 manifest 条目长这样:
|
||||
*
|
||||
* ```json
|
||||
* { "id": 9, "manifestPath": "/abs/.../manifest.json",
|
||||
* "lastKnownName": "Xxx Design Lab", "lastKnownPluginId": "",
|
||||
* "fileMetadata": { "type": "manifest", "codeFileId": 10, "uiFileIds": [] } }
|
||||
* ```
|
||||
*
|
||||
* 坑:id 是**整个数组共享**的递增计数器,manifest 条目占一个 id,Figma 之后
|
||||
* 给它的 code.js 建条目时占 `fileMetadata.codeFileId` 那个 id。所以算下一个
|
||||
* id 时不能只看已有条目的 id,还得把被 codeFileId / manifestFileId 预定掉的
|
||||
* 号算进去,否则新插件会和 Figma 迟早要建的 code 条目撞号。
|
||||
*
|
||||
* 这里不碰文件系统,好单测;读写与关 Figma 在 importPlugin.ts。
|
||||
*/
|
||||
|
||||
export interface FigmaFileMetadata {
|
||||
type: 'manifest' | 'code' | string
|
||||
/** manifest 条目专有:预留给同插件 code.js 条目的 id。 */
|
||||
codeFileId?: number
|
||||
/** code 条目专有:反指所属 manifest 条目的 id。 */
|
||||
manifestFileId?: number
|
||||
uiFileIds?: number[]
|
||||
}
|
||||
|
||||
export interface FigmaLocalFileExtension {
|
||||
id: number
|
||||
manifestPath: string
|
||||
lastKnownName?: string
|
||||
lastKnownPluginId?: string
|
||||
fileMetadata?: FigmaFileMetadata
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
/** 数组里混着 Figma 写的杂项,只把「有数字 id + 字符串 manifestPath」的当条目。 */
|
||||
export function asExtensionEntries(value: unknown): FigmaLocalFileExtension[] {
|
||||
if (!Array.isArray(value))
|
||||
return []
|
||||
return value.filter((e): e is FigmaLocalFileExtension =>
|
||||
!!e
|
||||
&& typeof e === 'object'
|
||||
&& typeof (e as FigmaLocalFileExtension).id === 'number'
|
||||
&& typeof (e as FigmaLocalFileExtension).manifestPath === 'string',
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 下一个可用 id:已有条目 id 与被 codeFileId / manifestFileId 预定的 id 取最大再 +1。
|
||||
* 空清单从 1 开始(Figma 自己也是从 1 起)。
|
||||
*/
|
||||
export function nextExtensionId(entries: FigmaLocalFileExtension[]): number {
|
||||
let max = 0
|
||||
for (const e of entries) {
|
||||
for (const n of [e.id, e.fileMetadata?.codeFileId, e.fileMetadata?.manifestFileId]) {
|
||||
if (typeof n === 'number' && Number.isFinite(n) && n > max)
|
||||
max = n
|
||||
}
|
||||
}
|
||||
return max + 1
|
||||
}
|
||||
|
||||
/** 按 manifestPath 精确查重;已导入过就返回那条,用来跳过写入。 */
|
||||
export function findManifestEntry(
|
||||
entries: FigmaLocalFileExtension[],
|
||||
manifestPath: string,
|
||||
): FigmaLocalFileExtension | undefined {
|
||||
return entries.find(e => e.manifestPath === manifestPath)
|
||||
}
|
||||
|
||||
/** 造一条 manifest 条目;code 条目留给 Figma 自己建,这里只把 id 号占住。 */
|
||||
export function buildManifestEntry(opts: {
|
||||
id: number
|
||||
manifestPath: string
|
||||
name: string
|
||||
}): FigmaLocalFileExtension {
|
||||
return {
|
||||
id: opts.id,
|
||||
manifestPath: opts.manifestPath,
|
||||
lastKnownName: opts.name,
|
||||
lastKnownPluginId: '',
|
||||
fileMetadata: {
|
||||
type: 'manifest',
|
||||
codeFileId: opts.id + 1,
|
||||
uiFileIds: [],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export interface AppendResult {
|
||||
/** 追加后的完整条目数组;已存在时原样返回。 */
|
||||
entries: FigmaLocalFileExtension[]
|
||||
/** false = 已导入过,调用方跳过写文件直接拉起 Figma。 */
|
||||
added: boolean
|
||||
/** 这条插件在 Figma 里的显示名(新建用传入名,已存在用清单里记的名)。 */
|
||||
name: string
|
||||
}
|
||||
|
||||
/** 查重 → 算 id → 追加,一次算完,写文件的那端只管落盘。 */
|
||||
export function appendManifestEntry(
|
||||
entries: FigmaLocalFileExtension[],
|
||||
opts: { manifestPath: string, name: string },
|
||||
): AppendResult {
|
||||
const existing = findManifestEntry(entries, opts.manifestPath)
|
||||
if (existing)
|
||||
return { entries, added: false, name: existing.lastKnownName || opts.name }
|
||||
const entry = buildManifestEntry({
|
||||
id: nextExtensionId(entries),
|
||||
manifestPath: opts.manifestPath,
|
||||
name: opts.name,
|
||||
})
|
||||
return { entries: [...entries, entry], added: true, name: opts.name }
|
||||
}
|
||||
Reference in new issue
Block a user