✨ 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:
cruldra committed 2026-09-15 00:11:49 +08:00
1 parent def5f4e88c
commit 142af57d9d
24 files changed
+628 -85

No files matched your search

+19
View File
@@ -45,6 +45,19 @@ export function isValidSpxFilePath(v: unknown): v is string {
&& v !== '…'
}
/**
* prototypeFile 指向 Figma 插件的 manifest.json,规则与 isValidSpxFilePath
* 同款,只是后缀换成 .json(可以是仓库相对路径,也可以是绝对路径)。
*/
export function isValidPrototypeFilePath(v: unknown): v is string {
return typeof v === 'string'
&& v.length > 0
&& v.includes('/')
&& v.endsWith('.json')
&& v !== '...'
&& v !== '…'
}
function isValidPrDiffFilePath(v: unknown): v is string {
return typeof v === 'string'
&& /^docs\/pr-diff\/\S+\.md$/.test(v)
@@ -73,6 +86,7 @@ function parseColumnFromComments(comments: GiteaComment[]): {
prototypeProfilePath?: string
specFile?: string
planFile?: string
prototypeFile?: string
prDiffFile?: string
pr?: string
prMerged?: boolean
@@ -121,6 +135,7 @@ function parseColumnFromComments(comments: GiteaComment[]): {
prototypeProfilePath?: unknown
specFile?: unknown
planFile?: unknown
prototypeFile?: unknown
prDiffFile?: unknown
pr?: unknown
prMerged?: unknown
@@ -155,6 +170,7 @@ function parseColumnFromComments(comments: GiteaComment[]): {
: undefined
const specFile = isValidSpxFilePath(obj.specFile) ? obj.specFile : undefined
const planFile = isValidSpxFilePath(obj.planFile) ? obj.planFile : undefined
const prototypeFile = isValidPrototypeFilePath(obj.prototypeFile) ? obj.prototypeFile : undefined
const prDiffFile = isValidPrDiffFilePath(obj.prDiffFile) ? obj.prDiffFile : undefined
const pr = typeof obj.pr === 'string' && obj.pr.length > 0
? obj.pr
@@ -205,6 +221,7 @@ function parseColumnFromComments(comments: GiteaComment[]): {
prototypeProfilePath,
specFile,
planFile,
prototypeFile,
prDiffFile,
pr,
prMerged,
@@ -348,6 +365,7 @@ async function buildIssue(opts: {
prototypeProfilePath,
specFile,
planFile,
prototypeFile,
prDiffFile,
pr,
prMerged: persistedMerged,
@@ -412,6 +430,7 @@ async function buildIssue(opts: {
...(prototypeProfilePath ? { prototypeProfilePath } : {}),
...(specFile ? { specFile } : {}),
...(planFile ? { planFile } : {}),
...(prototypeFile ? { prototypeFile } : {}),
...(prDiffFile ? { prDiffFile } : {}),
...(pr ? { pr } : {}),
...(prMerged !== undefined ? { prMerged } : {}),
+1 -1
View File
@@ -19,7 +19,7 @@ import { listIssueComments, postIssueComment } from './api'
* 普通文本评论、审查评论、Gitea 自动关联评论都不含这些字段,因此从尾往前
* 扫描时会被跳过,避免它们插队后下一次 merge 从空状态开始丢历史。
*/
const KNOWN_STATE_FIELDS = ['column', 'sessionId', 'implementSessionId', 'reviewSessionId', 'testSessionId', 'prototypeSessionId', 'profilePath', 'brainstormProfilePath', 'testProfilePath', 'prototypeProfilePath', 'specFile', 'planFile', 'prDiffFile', 'pr', 'prMerged', 'prMergedAt', 'branch', 'worktreePath', 'implementStatus', 'color', 'autoReview', 'handoffAttachmentId', 'handoffFrom'] as const
const KNOWN_STATE_FIELDS = ['column', 'sessionId', 'implementSessionId', 'reviewSessionId', 'testSessionId', 'prototypeSessionId', 'profilePath', 'brainstormProfilePath', 'testProfilePath', 'prototypeProfilePath', 'specFile', 'planFile', 'prototypeFile', 'prDiffFile', 'pr', 'prMerged', 'prMergedAt', 'branch', 'worktreePath', 'implementStatus', 'color', 'autoReview', 'handoffAttachmentId', 'handoffFrom'] as const
/**
* body 能 parse 成 object 且含至少一个已知 state 字段 → 认为是 state JSON。
+5
View File
@@ -59,6 +59,11 @@ export interface Issue {
* `.claude/plans/<stem>.md`.
*/
planFile?: string
/**
* 原型会话产出的 Figma 插件 `manifest.json` 路径(仓库相对或绝对)。
* cc 画完原型后用 `spx issue marker --type prototype` 挂上来。
*/
prototypeFile?: string
/** Optional workspace-relative path to the generated PR diff summary. */
prDiffFile?: string
/** PR number associated with this issue (set after webhook fires). */