@su-record/vibe 2.8.30 → 2.8.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +3 -3
  2. package/dist/cli/commands/figma.d.ts +2 -2
  3. package/dist/cli/commands/figma.d.ts.map +1 -1
  4. package/dist/cli/commands/figma.js +3 -7
  5. package/dist/cli/commands/figma.js.map +1 -1
  6. package/dist/cli/commands/skills.d.ts.map +1 -1
  7. package/dist/cli/commands/skills.js +10 -0
  8. package/dist/cli/commands/skills.js.map +1 -1
  9. package/dist/infra/lib/figma/api.d.ts +9 -0
  10. package/dist/infra/lib/figma/api.d.ts.map +1 -0
  11. package/dist/infra/lib/figma/api.js +73 -0
  12. package/dist/infra/lib/figma/api.js.map +1 -0
  13. package/dist/infra/lib/figma/extract.d.ts +12 -0
  14. package/dist/infra/lib/figma/extract.d.ts.map +1 -0
  15. package/dist/infra/lib/figma/extract.js +285 -0
  16. package/dist/infra/lib/figma/extract.js.map +1 -0
  17. package/dist/infra/lib/figma/index.d.ts +4 -0
  18. package/dist/infra/lib/figma/index.d.ts.map +1 -0
  19. package/dist/infra/lib/figma/index.js +3 -0
  20. package/dist/infra/lib/figma/index.js.map +1 -0
  21. package/dist/infra/lib/figma/types.d.ts +40 -0
  22. package/dist/infra/lib/figma/types.d.ts.map +1 -0
  23. package/dist/infra/lib/figma/types.js +5 -0
  24. package/dist/infra/lib/figma/types.js.map +1 -0
  25. package/dist/infra/lib/llm-availability.d.ts.map +1 -1
  26. package/dist/infra/lib/llm-availability.js +2 -0
  27. package/dist/infra/lib/llm-availability.js.map +1 -1
  28. package/dist/infra/lib/memory/ReflectionStore.d.ts.map +1 -1
  29. package/dist/infra/lib/memory/ReflectionStore.js +3 -8
  30. package/dist/infra/lib/memory/ReflectionStore.js.map +1 -1
  31. package/hooks/scripts/figma-extract.js +225 -0
  32. package/package.json +1 -2
  33. package/skills/vibe.figma/SKILL.md +68 -60
  34. package/skills/vibe.figma.extract/SKILL.md +76 -114
@@ -1,150 +1,112 @@
1
1
  ---
2
2
  name: vibe.figma.extract
3
- description: Figma MCP에서 이미지 다운로드 + CSS 추출
3
+ description: Figma REST API로 노드 트리, CSS, 이미지 추출
4
4
  triggers: []
5
5
  tier: standard
6
6
  ---
7
7
 
8
8
  # vibe.figma.extract — 데이터 추출
9
9
 
10
- `get_design_context` 응답에서 이미지와 CSS 값을 추출하는 절차.
10
+ Figma REST API(`src/infra/lib/figma/`)를 사용하여 노드 트리, CSS, 이미지를 추출.
11
11
 
12
12
  ---
13
13
 
14
- ## 1. 이미지 에셋 추출 + 다운로드
15
-
16
- ### URL 추출
17
-
18
- ```
19
- 참조 코드에서 모든 에셋 URL을 수집:
20
- 패턴: const {변수명} = "https://www.figma.com/api/mcp/asset/{uuid}"
21
-
22
- 예:
23
- const img21 = "https://www.figma.com/api/mcp/asset/76e951df-..."
24
- const imgTitle = "https://www.figma.com/api/mcp/asset/f97dad41-..."
25
- const imgSnowParticle12 = "https://www.figma.com/api/mcp/asset/3de2eeed-..."
26
- ```
27
-
28
- ### 파일명 결정
14
+ ## 1. 노드 트리 + CSS 추출
29
15
 
30
16
  ```
31
- 변수명 → kebab-case 파일명:
32
- img21 img-21.webp
33
- imgTitle title.webp
34
- imgSnowParticle12 → snow-particle-12.webp
35
- imgSnowmanItem11 snowman-item-11.webp
36
- imgImgBannerStatic → banner-static.webp
37
- ```
38
-
39
- ### 다운로드
40
-
17
+ Bash:
18
+ # [FIGMA_SCRIPT] = ~/.vibe/hooks/scripts/figma-extract.js
19
+ node "[FIGMA_SCRIPT]" tree {fileKey} {nodeId} --depth=10
20
+
21
+ 반환 (FigmaNode JSON):
22
+ {
23
+ nodeId: "641:78152",
24
+ name: "KID",
25
+ type: "INSTANCE",
26
+ size: { width: 720, height: 487 },
27
+ css: { display: "flex", flexDirection: "column", gap: "32px", ... },
28
+ text: "텍스트 내용" (TEXT 노드만),
29
+ imageRef: "abc123" (이미지 fill이 있는 노드만),
30
+ children: [...]
31
+ }
41
32
  ```
42
- for each (변수명, url) in assets:
43
- Bash: curl -sL "{url}" -o public/images/{feature}/{파일명}.webp
44
33
 
45
- 다운로드 검증:
46
- Bash: ls -la public/images/{feature}/
47
- 모든 파일 존재 + 0byte 아닌지 확인
48
- → 실패한 파일 → 재시도 1회
49
- ```
34
+ ### Figma 속성 → CSS 변환표
35
+
36
+ 도구가 자동으로 변환하는 속성:
37
+
38
+ | Figma 속성 | CSS | 스케일 적용 |
39
+ |-----------|-----|-----------|
40
+ | `fills[].color` | `background-color` | ❌ |
41
+ | `fills[].type=IMAGE` | `imageRef` (다운로드 대상) | — |
42
+ | `strokes[].color` + `strokeWeight` | `border` | ✅ (width만) |
43
+ | `effects[].DROP_SHADOW` | `box-shadow` | ✅ (px만) |
44
+ | `effects[].LAYER_BLUR` | `filter: blur()` | ✅ |
45
+ | `effects[].BACKGROUND_BLUR` | `backdrop-filter: blur()` | ✅ |
46
+ | `cornerRadius` | `border-radius` | ✅ |
47
+ | `opacity` | `opacity` | ❌ |
48
+ | `blendMode` | `mix-blend-mode` | ❌ |
49
+ | `style.fontFamily` | `font-family` | ❌ |
50
+ | `style.fontSize` | `font-size` | ✅ |
51
+ | `style.fontWeight` | `font-weight` | ❌ |
52
+ | `style.lineHeightPx` | `line-height` | ❌ (px이지만 상대값으로 취급) |
53
+ | `style.letterSpacing` | `letter-spacing` | ✅ |
54
+ | `style.textAlignHorizontal` | `text-align` | ❌ |
55
+ | `fills[].color` (TEXT) | `color` | ❌ |
56
+ | `characters` | 텍스트 내용 | — |
57
+ | `absoluteBoundingBox.width/height` | `width/height` | ✅ |
58
+ | `layoutMode=VERTICAL` | `display:flex; flex-direction:column` | ❌ |
59
+ | `layoutMode=HORIZONTAL` | `display:flex; flex-direction:row` | ❌ |
60
+ | `primaryAxisAlignItems` | `justify-content` | ❌ |
61
+ | `counterAxisAlignItems` | `align-items` | ❌ |
62
+ | `itemSpacing` | `gap` | ✅ |
63
+ | `padding*` | `padding` | ✅ |
64
+ | `clipsContent` | `overflow: hidden` | ❌ |
65
+ | `layoutPositioning=ABSOLUTE` | `position: absolute` | ❌ |
50
66
 
51
- ### 누락 에셋 처리
67
+ ---
52
68
 
53
- ```
54
- 스크린샷에 보이는 이미지가 참조 코드에 에셋 URL로 없을 때:
69
+ ## 2. 이미지 다운로드
55
70
 
56
- 1. get_metadata(섹션 nodeId)로 하위 노드 목록 확보
57
- 2. 이미지로 의심되는 하위 nodeId에 get_design_context 재호출
58
- → 에셋 URL 발견 시 다운로드
59
- 3. 그래도 없으면 → get_screenshot(해당 nodeId)으로 이미지 직접 저장
60
71
  ```
72
+ 트리에서 imageRef 수집 → Figma API로 다운로드:
61
73
 
62
- ### 이미지 매핑 테이블
63
-
64
- ```
65
- 다운로드 완료 후 매핑 생성:
74
+ Bash:
75
+ node "[FIGMA_SCRIPT]" images {fileKey} {nodeId} --out={outDir} --depth=10
66
76
 
67
- imageMap = {
68
- img21: '/images/{feature}/img-21.webp',
69
- imgTitle: '/images/{feature}/title.webp',
70
- imgSnowParticle12: '/images/{feature}/snow-particle-12.webp',
71
- ...
72
- }
77
+ 반환: { total: N, images: { "imageRef": "/path/to/file.png", ... } }
73
78
 
74
- 매핑은 코드 변환 src={변수명}을 로컬 경로로 교체하는 데 사용.
79
+ 파일명: imageRef 16자 + .png
80
+ 검증: total = refs.size (누락 0), 0byte 파일 없음
75
81
  ```
76
82
 
77
83
  ---
78
84
 
79
- ## 2. CSS 값 추출
80
-
81
- ### Tailwind → CSS 변환표
82
-
83
- 참조 코드의 Tailwind 클래스에서 CSS 속성 + 값을 추출:
84
-
85
- | Tailwind | CSS | 스케일 적용 |
86
- |----------|-----|-----------|
87
- | `text-[48px]` | `font-size: 48px` | ✅ × scaleFactor |
88
- | `text-[#1B3A1D]` | `color: #1B3A1D` | ❌ |
89
- | `font-black` | `font-weight: 900` | ❌ |
90
- | `font-bold` | `font-weight: 700` | ❌ |
91
- | `font-semibold` | `font-weight: 600` | ❌ |
92
- | `font-medium` | `font-weight: 500` | ❌ |
93
- | `leading-[1.4]` | `line-height: 1.4` | ❌ (단위 없음) |
94
- | `tracking-[-0.36px]` | `letter-spacing: -0.36px` | ✅ |
95
- | `bg-[#0A1628]` | `background-color: #0A1628` | ❌ |
96
- | `bg-[rgba(13,40,61,0.5)]` | `background: rgba(13,40,61,0.5)` | ❌ |
97
- | `pt-[120px]` | `padding-top: 120px` | ✅ |
98
- | `gap-[24px]` | `gap: 24px` | ✅ |
99
- | `rounded-[12px]` | `border-radius: 12px` | ✅ |
100
- | `shadow-[...]` | `box-shadow: ...` | 부분 (px만) |
101
- | `w-[720px]` | `width: 720px` | ✅ |
102
- | `h-[1280px]` | `height: 1280px` | ✅ |
103
- | `opacity-40` | `opacity: 0.4` | ❌ |
104
- | `blur-[3.5px]` | `filter: blur(3.5px)` | ✅ |
105
- | `mix-blend-lighten` | `mix-blend-mode: lighten` | ❌ |
106
- | `mix-blend-multiply` | `mix-blend-mode: multiply` | ❌ |
107
- | `overflow-clip` | `overflow: clip` | ❌ |
108
- | `absolute` | `position: absolute` | ❌ |
109
- | `relative` | `position: relative` | ❌ |
110
- | `inset-0` | `inset: 0` | ❌ |
111
- | `flex` | `display: flex` | ❌ |
112
- | `flex-col` | `flex-direction: column` | ❌ |
113
- | `items-center` | `align-items: center` | ❌ |
114
- | `justify-center` | `justify-content: center` | ❌ |
115
- | `object-cover` | `object-fit: cover` | ❌ |
116
- | `object-contain` | `object-fit: contain` | ❌ |
117
- | `whitespace-nowrap` | `white-space: nowrap` | ❌ |
118
- | `text-center` | `text-align: center` | ❌ |
119
- | `text-white` | `color: #FFFFFF` | ❌ |
120
- | `size-full` | `width: 100%; height: 100%` | ❌ |
121
- | `max-w-none` | `max-width: none` | ❌ |
122
- | `pointer-events-none` | `pointer-events: none` | ❌ |
123
-
124
- ### CSS 변수 패턴
85
+ ## 3. 스크린샷
125
86
 
126
87
  ```
127
- 참조 코드에 Figma 디자인 토큰이 CSS 변수로 포함될 수 있음:
128
- font-[family-name:var(--font/family/pretendard,...)]
129
- text-[length:var(--font/size/heading/24,24px)]
130
- text-[color:var(--color/grayscale/950,#171716)]
88
+ Bash:
89
+ node "[FIGMA_SCRIPT]" screenshot {fileKey} {nodeId} --out={path}
131
90
 
132
- var() 안의 fallback 값(24px, #171716)을 사용.
133
- → CSS 변수명은 프로젝트 토큰 네이밍에 참고.
91
+ 시각 검증용. 노드를 PNG로 렌더링하여 저장.
134
92
  ```
135
93
 
136
94
  ---
137
95
 
138
- ## 3. HTML 구조 추출
96
+ ## 4. 노드 구조 참조
139
97
 
140
98
  ```
141
- 참조 코드의 JSX 구조가 HTML 구조.
142
- data-name 속성으로 레이어 용도 파악:
143
- data-name="BG" → 배경 레이어
144
- data-name="Title" → 제목 영역
145
- data-name="Period" → 기간 정보 영역
146
- data-name="Light" → 장식 조명
147
- data-name="BTN_Share" → 공유 버튼
148
-
149
- 구조 변환은 vibe.figma.convert에서 처리.
99
+ 트리의 name 속성으로 레이어 용도 파악:
100
+ name="BG" 배경 레이어 (position:absolute, imageRef 포함)
101
+ name="Contents" → 콘텐츠 영역
102
+ name="Title" → 제목
103
+ name="Step1", "Step2" 하위 섹션
104
+ name="Btn_Login" → 버튼
105
+
106
+ type으로 노드 종류 구분:
107
+ FRAME 컨테이너 (div)
108
+ TEXT → 텍스트 (p/span)
109
+ INSTANCE → 컴포넌트 인스턴스 (자식 있음)
110
+ RECTANGLE/VECTOR with imageRef → 이미지 (img)
111
+ GROUP → 그룹 (div)
150
112
  ```