@discourser/design-system 0.15.0 → 0.17.0

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 (229) hide show
  1. package/dist/chunk-2P7Z5PVP.cjs +2151 -0
  2. package/dist/chunk-2P7Z5PVP.cjs.map +1 -0
  3. package/dist/{chunk-QC44JPCA.cjs → chunk-PFWU7QSM.cjs} +777 -15
  4. package/dist/chunk-PFWU7QSM.cjs.map +1 -0
  5. package/dist/chunk-QC7LGFM3.js +2099 -0
  6. package/dist/chunk-QC7LGFM3.js.map +1 -0
  7. package/dist/{chunk-M7J7WKJY.js → chunk-SNUJBT5R.js} +778 -16
  8. package/dist/chunk-SNUJBT5R.js.map +1 -0
  9. package/dist/components/Accordion.figma.d.ts +2 -0
  10. package/dist/components/Accordion.figma.d.ts.map +1 -0
  11. package/dist/components/Breadcrumb.d.ts +11 -0
  12. package/dist/components/Breadcrumb.d.ts.map +1 -0
  13. package/dist/components/Breadcrumb.figma.d.ts +2 -0
  14. package/dist/components/Breadcrumb.figma.d.ts.map +1 -0
  15. package/dist/components/Checkbox.d.ts +6 -6
  16. package/dist/components/ContentCard/ContentCard.d.ts +13 -0
  17. package/dist/components/ContentCard/ContentCard.d.ts.map +1 -0
  18. package/dist/components/ContentCard/ContentCard.figma.d.ts +2 -0
  19. package/dist/components/ContentCard/ContentCard.figma.d.ts.map +1 -0
  20. package/dist/components/ContentCard/index.d.ts +2 -0
  21. package/dist/components/ContentCard/index.d.ts.map +1 -0
  22. package/dist/components/{Heading.d.ts → Header.d.ts} +3 -3
  23. package/dist/components/Header.d.ts.map +1 -0
  24. package/dist/components/Header.figma.d.ts +2 -0
  25. package/dist/components/Header.figma.d.ts.map +1 -0
  26. package/dist/components/Icons/AccountIcon.d.ts +6 -0
  27. package/dist/components/Icons/AccountIcon.d.ts.map +1 -0
  28. package/dist/components/Icons/ChevronUpIcon.d.ts +6 -0
  29. package/dist/components/Icons/ChevronUpIcon.d.ts.map +1 -0
  30. package/dist/components/Icons/ClockIcon.d.ts +6 -0
  31. package/dist/components/Icons/ClockIcon.d.ts.map +1 -0
  32. package/dist/components/Icons/DashboardIcon.d.ts +6 -0
  33. package/dist/components/Icons/DashboardIcon.d.ts.map +1 -0
  34. package/dist/components/Icons/DiscourserLogo.d.ts +6 -0
  35. package/dist/components/Icons/DiscourserLogo.d.ts.map +1 -0
  36. package/dist/components/Icons/DiscourserLogo.figma.d.ts +2 -0
  37. package/dist/components/Icons/DiscourserLogo.figma.d.ts.map +1 -0
  38. package/dist/components/Icons/GripDotsVerticalIcon.d.ts +6 -0
  39. package/dist/components/Icons/GripDotsVerticalIcon.d.ts.map +1 -0
  40. package/dist/components/Icons/HelpIcon.d.ts +6 -0
  41. package/dist/components/Icons/HelpIcon.d.ts.map +1 -0
  42. package/dist/components/Icons/NotebookIcon.d.ts +6 -0
  43. package/dist/components/Icons/NotebookIcon.d.ts.map +1 -0
  44. package/dist/components/Icons/RightArrowIcon.d.ts +6 -0
  45. package/dist/components/Icons/RightArrowIcon.d.ts.map +1 -0
  46. package/dist/components/Icons/ScenarioIcon.d.ts +6 -0
  47. package/dist/components/Icons/ScenarioIcon.d.ts.map +1 -0
  48. package/dist/components/Icons/index.d.ts +11 -0
  49. package/dist/components/Icons/index.d.ts.map +1 -0
  50. package/dist/components/NavigationMenu/NavigationMenu.d.ts +3 -0
  51. package/dist/components/NavigationMenu/NavigationMenu.d.ts.map +1 -0
  52. package/dist/components/NavigationMenu/NavigationMenu.figma.d.ts +2 -0
  53. package/dist/components/NavigationMenu/NavigationMenu.figma.d.ts.map +1 -0
  54. package/dist/components/NavigationMenu/index.d.ts +3 -0
  55. package/dist/components/NavigationMenu/index.d.ts.map +1 -0
  56. package/dist/components/NavigationMenu/types.d.ts +25 -0
  57. package/dist/components/NavigationMenu/types.d.ts.map +1 -0
  58. package/dist/components/QuickStartPage/QuickStartPage.d.ts +21 -0
  59. package/dist/components/QuickStartPage/QuickStartPage.d.ts.map +1 -0
  60. package/dist/components/QuickStartPage/index.d.ts +3 -0
  61. package/dist/components/QuickStartPage/index.d.ts.map +1 -0
  62. package/dist/components/ScenarioQueue/AddScenarioDialog.d.ts +16 -0
  63. package/dist/components/ScenarioQueue/AddScenarioDialog.d.ts.map +1 -0
  64. package/dist/components/ScenarioQueue/ScenarioCard.d.ts +10 -0
  65. package/dist/components/ScenarioQueue/ScenarioCard.d.ts.map +1 -0
  66. package/dist/components/ScenarioQueue/ScenarioCardDraggable.d.ts +15 -0
  67. package/dist/components/ScenarioQueue/ScenarioCardDraggable.d.ts.map +1 -0
  68. package/dist/components/ScenarioQueue/ScenarioQueue.d.ts +3 -0
  69. package/dist/components/ScenarioQueue/ScenarioQueue.d.ts.map +1 -0
  70. package/dist/components/ScenarioQueue/ScenarioQueue.figma.d.ts +2 -0
  71. package/dist/components/ScenarioQueue/ScenarioQueue.figma.d.ts.map +1 -0
  72. package/dist/components/ScenarioQueue/index.d.ts +6 -0
  73. package/dist/components/ScenarioQueue/index.d.ts.map +1 -0
  74. package/dist/components/ScenarioQueue/types.d.ts +56 -0
  75. package/dist/components/ScenarioQueue/types.d.ts.map +1 -0
  76. package/dist/components/ScenarioSettings/ScenarioSettings.d.ts +3 -0
  77. package/dist/components/ScenarioSettings/ScenarioSettings.d.ts.map +1 -0
  78. package/dist/components/ScenarioSettings/ScenarioSettings.figma.d.ts +2 -0
  79. package/dist/components/ScenarioSettings/ScenarioSettings.figma.d.ts.map +1 -0
  80. package/dist/components/ScenarioSettings/index.d.ts +3 -0
  81. package/dist/components/ScenarioSettings/index.d.ts.map +1 -0
  82. package/dist/components/ScenarioSettings/types.d.ts +54 -0
  83. package/dist/components/ScenarioSettings/types.d.ts.map +1 -0
  84. package/dist/components/index.cjs +110 -34
  85. package/dist/components/index.d.ts +16 -1
  86. package/dist/components/index.d.ts.map +1 -1
  87. package/dist/components/index.js +1 -1
  88. package/dist/figma-codex/config.d.ts +8 -0
  89. package/dist/figma-codex/config.d.ts.map +1 -0
  90. package/dist/figma-codex/fixtures/CompoundComponent/CompoundComponent.d.ts +6 -0
  91. package/dist/figma-codex/fixtures/CompoundComponent/CompoundComponent.d.ts.map +1 -0
  92. package/dist/figma-codex/fixtures/CompoundComponent/index.d.ts +2 -0
  93. package/dist/figma-codex/fixtures/CompoundComponent/index.d.ts.map +1 -0
  94. package/dist/figma-codex/fixtures/CompoundComponent.figma.d.ts +2 -0
  95. package/dist/figma-codex/fixtures/CompoundComponent.figma.d.ts.map +1 -0
  96. package/dist/figma-codex/fixtures/SimpleComponent.d.ts +8 -0
  97. package/dist/figma-codex/fixtures/SimpleComponent.d.ts.map +1 -0
  98. package/dist/figma-codex/fixtures/SimpleComponent.figma.d.ts +2 -0
  99. package/dist/figma-codex/fixtures/SimpleComponent.figma.d.ts.map +1 -0
  100. package/dist/figma-codex/generate.d.ts +6 -0
  101. package/dist/figma-codex/generate.d.ts.map +1 -0
  102. package/dist/figma-codex/parser.d.ts +18 -0
  103. package/dist/figma-codex/parser.d.ts.map +1 -0
  104. package/dist/figma-codex/resolver.d.ts +5 -0
  105. package/dist/figma-codex/resolver.d.ts.map +1 -0
  106. package/dist/figma-codex/schema.d.ts +60 -0
  107. package/dist/figma-codex/schema.d.ts.map +1 -0
  108. package/dist/figma-codex/writer.d.ts +8 -0
  109. package/dist/figma-codex/writer.d.ts.map +1 -0
  110. package/dist/figma-codex.json +373 -0
  111. package/dist/index.cjs +114 -38
  112. package/dist/index.js +2 -2
  113. package/dist/preset/index.cjs +2 -2
  114. package/dist/preset/index.d.ts.map +1 -1
  115. package/dist/preset/index.js +1 -1
  116. package/dist/preset/recipes/accordion.d.ts.map +1 -1
  117. package/dist/preset/recipes/avatar.d.ts.map +1 -1
  118. package/dist/preset/recipes/breadcrumb.d.ts +2 -0
  119. package/dist/preset/recipes/breadcrumb.d.ts.map +1 -0
  120. package/dist/preset/recipes/checkbox.d.ts.map +1 -1
  121. package/dist/preset/recipes/content-card.d.ts +2 -0
  122. package/dist/preset/recipes/content-card.d.ts.map +1 -0
  123. package/dist/preset/recipes/field.d.ts.map +1 -1
  124. package/dist/preset/recipes/index.d.ts +7 -0
  125. package/dist/preset/recipes/index.d.ts.map +1 -1
  126. package/dist/preset/recipes/navigation-menu.d.ts +2 -0
  127. package/dist/preset/recipes/navigation-menu.d.ts.map +1 -0
  128. package/dist/preset/recipes/progress.d.ts.map +1 -1
  129. package/dist/preset/recipes/radio-group.d.ts.map +1 -1
  130. package/dist/preset/recipes/scenario-card.d.ts +2 -0
  131. package/dist/preset/recipes/scenario-card.d.ts.map +1 -0
  132. package/dist/preset/recipes/scenario-queue.d.ts +2 -0
  133. package/dist/preset/recipes/scenario-queue.d.ts.map +1 -0
  134. package/dist/preset/recipes/scenario-settings.d.ts +2 -0
  135. package/dist/preset/recipes/scenario-settings.d.ts.map +1 -0
  136. package/dist/preset/recipes/steps.d.ts.map +1 -1
  137. package/dist/preset/recipes/toast.d.ts.map +1 -1
  138. package/dist/preset/recipes/tooltip.d.ts.map +1 -1
  139. package/dist/preset/semantic-tokens.d.ts +12 -0
  140. package/dist/preset/semantic-tokens.d.ts.map +1 -1
  141. package/package.json +35 -2
  142. package/src/components/Accordion.figma.tsx +20 -0
  143. package/src/components/Breadcrumb.figma.tsx +18 -0
  144. package/src/components/Breadcrumb.tsx +52 -0
  145. package/src/components/ContentCard/ContentCard.figma.tsx +21 -0
  146. package/src/components/ContentCard/ContentCard.test.tsx +197 -0
  147. package/src/components/ContentCard/ContentCard.tsx +19 -0
  148. package/src/components/ContentCard/index.ts +13 -0
  149. package/src/components/Header.figma.tsx +25 -0
  150. package/src/components/{Heading.tsx → Header.tsx} +2 -2
  151. package/src/components/Icons/AccountIcon.tsx +26 -0
  152. package/src/components/Icons/ChevronUpIcon.tsx +24 -0
  153. package/src/components/Icons/ClockIcon.tsx +40 -0
  154. package/src/components/Icons/DashboardIcon.tsx +47 -0
  155. package/src/components/Icons/Discourser-Logo.svg +14 -0
  156. package/src/components/Icons/DiscourserLogo.figma.tsx +10 -0
  157. package/src/components/Icons/DiscourserLogo.tsx +72 -0
  158. package/src/components/Icons/GripDotsVerticalIcon.tsx +26 -0
  159. package/src/components/Icons/HelpIcon.tsx +26 -0
  160. package/src/components/Icons/NotebookIcon.tsx +26 -0
  161. package/src/components/Icons/RightArrowIcon.tsx +23 -0
  162. package/src/components/Icons/ScenarioIcon.tsx +26 -0
  163. package/src/components/Icons/index.ts +13 -0
  164. package/src/components/NavigationMenu/NavigationMenu.figma.tsx +26 -0
  165. package/src/components/NavigationMenu/NavigationMenu.test.tsx +524 -0
  166. package/src/components/NavigationMenu/NavigationMenu.tsx +102 -0
  167. package/src/components/NavigationMenu/index.ts +2 -0
  168. package/src/components/NavigationMenu/types.ts +27 -0
  169. package/src/components/QuickStartPage/QuickStartPage.tsx +627 -0
  170. package/src/components/QuickStartPage/index.ts +2 -0
  171. package/src/components/ScenarioQueue/AddScenarioDialog.tsx +137 -0
  172. package/src/components/ScenarioQueue/ScenarioCard.tsx +120 -0
  173. package/src/components/ScenarioQueue/ScenarioCardDraggable.tsx +41 -0
  174. package/src/components/ScenarioQueue/ScenarioQueue.figma.tsx +37 -0
  175. package/src/components/ScenarioQueue/ScenarioQueue.test.tsx +398 -0
  176. package/src/components/ScenarioQueue/ScenarioQueue.tsx +162 -0
  177. package/src/components/ScenarioQueue/index.ts +11 -0
  178. package/src/components/ScenarioQueue/types.ts +86 -0
  179. package/src/components/ScenarioSettings/ScenarioSettings.figma.tsx +12 -0
  180. package/src/components/ScenarioSettings/ScenarioSettings.test.tsx +406 -0
  181. package/src/components/ScenarioSettings/ScenarioSettings.tsx +386 -0
  182. package/src/components/ScenarioSettings/index.ts +11 -0
  183. package/src/components/ScenarioSettings/types.ts +70 -0
  184. package/src/components/__tests__/Breadcrumb.test.tsx +94 -0
  185. package/src/components/index.ts +54 -1
  186. package/src/figma-codex/README.md +186 -0
  187. package/src/figma-codex/__tests__/config.test.ts +63 -0
  188. package/src/figma-codex/__tests__/generate.test.ts +78 -0
  189. package/src/figma-codex/__tests__/parser.test.ts +138 -0
  190. package/src/figma-codex/__tests__/resolver.test.ts +196 -0
  191. package/src/figma-codex/__tests__/writer.test.ts +111 -0
  192. package/src/figma-codex/config.ts +42 -0
  193. package/src/figma-codex/fixtures/CompoundComponent/CompoundComponent.tsx +17 -0
  194. package/src/figma-codex/fixtures/CompoundComponent/index.ts +1 -0
  195. package/src/figma-codex/fixtures/CompoundComponent.figma.tsx +14 -0
  196. package/src/figma-codex/fixtures/SimpleComponent.figma.tsx +10 -0
  197. package/src/figma-codex/fixtures/SimpleComponent.tsx +10 -0
  198. package/src/figma-codex/fixtures/expected-output.json +78 -0
  199. package/src/figma-codex/generate.ts +106 -0
  200. package/src/figma-codex/parser.ts +138 -0
  201. package/src/figma-codex/resolver.ts +280 -0
  202. package/src/figma-codex/schema.ts +79 -0
  203. package/src/figma-codex/writer.ts +54 -0
  204. package/src/preset/index.ts +15 -0
  205. package/src/preset/recipes/accordion.ts +8 -5
  206. package/src/preset/recipes/avatar.ts +1 -2
  207. package/src/preset/recipes/breadcrumb.ts +109 -0
  208. package/src/preset/recipes/checkbox.ts +1 -2
  209. package/src/preset/recipes/content-card.ts +124 -0
  210. package/src/preset/recipes/field.ts +1 -2
  211. package/src/preset/recipes/index.ts +11 -0
  212. package/src/preset/recipes/navigation-menu.ts +97 -0
  213. package/src/preset/recipes/progress.ts +1 -2
  214. package/src/preset/recipes/radio-group.ts +1 -2
  215. package/src/preset/recipes/scenario-card.ts +151 -0
  216. package/src/preset/recipes/scenario-queue.ts +99 -0
  217. package/src/preset/recipes/scenario-settings.ts +182 -0
  218. package/src/preset/recipes/steps.ts +1 -2
  219. package/src/preset/recipes/toast.ts +1 -2
  220. package/src/preset/recipes/tooltip.ts +1 -2
  221. package/src/preset/semantic-tokens.ts +4 -0
  222. package/src/test/setup.ts +15 -0
  223. package/dist/chunk-F7LHARS4.js +0 -869
  224. package/dist/chunk-F7LHARS4.js.map +0 -1
  225. package/dist/chunk-M7J7WKJY.js.map +0 -1
  226. package/dist/chunk-QC44JPCA.cjs.map +0 -1
  227. package/dist/chunk-QP4EJI3G.cjs +0 -902
  228. package/dist/chunk-QP4EJI3G.cjs.map +0 -1
  229. package/dist/components/Heading.d.ts.map +0 -1
@@ -0,0 +1,280 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import type { ParsedFigmaFile } from './parser';
4
+ import type { FigmaCodexConfig } from './config';
5
+ import type {
6
+ ComponentEntry,
7
+ PropDefinition,
8
+ SubComponentEntry,
9
+ } from './schema';
10
+
11
+ function resolveSourceFile(
12
+ importSource: string,
13
+ figmaFilePath: string,
14
+ ): string | null {
15
+ const baseDir = dirname(figmaFilePath);
16
+ const candidates = [
17
+ join(baseDir, importSource + '.tsx'),
18
+ join(baseDir, importSource + '.ts'),
19
+ join(baseDir, importSource, 'index.ts'),
20
+ join(baseDir, importSource, 'index.tsx'),
21
+ join(baseDir, importSource),
22
+ ];
23
+ for (const c of candidates) {
24
+ if (existsSync(c)) return c;
25
+ }
26
+ return null;
27
+ }
28
+
29
+ function resolveImplementationFile(sourceFile: string): string {
30
+ const content = readFileSync(sourceFile, 'utf-8');
31
+ // If this is an index that re-exports from a single file, resolve to that file
32
+ // Use a simple match without end-of-string anchor to handle trailing newlines
33
+ const reExportMatch = content.match(/from\s+['"]\.\/(\w+)['"]/);
34
+ if (reExportMatch) {
35
+ const implFile = join(dirname(sourceFile), reExportMatch[1] + '.tsx');
36
+ if (existsSync(implFile)) return implFile;
37
+ }
38
+ return sourceFile;
39
+ }
40
+
41
+ function extractProps(sourceContent: string): PropDefinition[] {
42
+ const props: PropDefinition[] = [];
43
+
44
+ // Step 1: Find the Props interface and extract its full body using balanced-brace tracking
45
+ const ifaceRe = /interface\s+\w+Props\s*\{/g;
46
+ const startMatch = ifaceRe.exec(sourceContent);
47
+ if (!startMatch) return props;
48
+
49
+ // Find the opening { of the interface body
50
+ const openBrace = sourceContent.indexOf(
51
+ '{',
52
+ startMatch.index + startMatch[0].length - 1,
53
+ );
54
+ let depth = 0;
55
+ let closeBrace = openBrace;
56
+ for (let i = openBrace; i < sourceContent.length; i++) {
57
+ if (sourceContent[i] === '{') depth++;
58
+ else if (sourceContent[i] === '}') {
59
+ depth--;
60
+ if (depth === 0) {
61
+ closeBrace = i;
62
+ break;
63
+ }
64
+ }
65
+ }
66
+
67
+ const body = sourceContent.slice(openBrace + 1, closeBrace);
68
+
69
+ // Step 2: Parse each prop using a position-based scanner
70
+ let pos = 0;
71
+
72
+ while (pos < body.length) {
73
+ // Skip whitespace
74
+ while (pos < body.length && /\s/.test(body[pos])) pos++;
75
+ if (pos >= body.length) break;
76
+
77
+ // Try to match JSDoc comment /** ... */
78
+ let jsdoc: string | undefined;
79
+ if (body.startsWith('/**', pos)) {
80
+ const endDoc = body.indexOf('*/', pos);
81
+ if (endDoc !== -1) {
82
+ jsdoc = body
83
+ .slice(pos + 3, endDoc)
84
+ .trim()
85
+ .replace(/\n\s*\*\s*/g, ' ');
86
+ pos = endDoc + 2;
87
+ while (pos < body.length && /\s/.test(body[pos])) pos++;
88
+ }
89
+ }
90
+
91
+ // Match prop name followed by optional `?` and `:`
92
+ const nameMatch = /^(\w+)(\?)?:/.exec(body.slice(pos));
93
+ if (!nameMatch) {
94
+ // Not a prop — skip to next semicolon or newline
95
+ const next = body.indexOf('\n', pos);
96
+ pos = next === -1 ? body.length : next + 1;
97
+ continue;
98
+ }
99
+
100
+ const [fullMatch, name, optional] = nameMatch;
101
+ pos += fullMatch.length;
102
+
103
+ if (name === 'children') {
104
+ // Skip children — find the next semicolon at depth 0
105
+ let bracketDepth = 0;
106
+ while (pos < body.length) {
107
+ const ch = body[pos];
108
+ if (ch === '(' || ch === '{') bracketDepth++;
109
+ else if ((ch === ')' || ch === '}') && bracketDepth > 0) bracketDepth--;
110
+ else if (ch === ';' && bracketDepth === 0) {
111
+ pos++;
112
+ break;
113
+ }
114
+ pos++;
115
+ }
116
+ continue;
117
+ }
118
+
119
+ // Skip whitespace after the colon
120
+ while (pos < body.length && body[pos] === ' ') pos++;
121
+
122
+ // Read the type: track () and {} depth, stop at ; when depth === 0
123
+ const typeStart = pos;
124
+ let bracketDepth = 0;
125
+ while (pos < body.length) {
126
+ const ch = body[pos];
127
+ if (ch === '(' || ch === '{') {
128
+ bracketDepth++;
129
+ } else if ((ch === ')' || ch === '}') && bracketDepth > 0) {
130
+ bracketDepth--;
131
+ } else if (ch === ';' && bracketDepth === 0) {
132
+ break;
133
+ } else if (ch === '\n' && bracketDepth === 0) {
134
+ break;
135
+ }
136
+ pos++;
137
+ }
138
+
139
+ const typeRaw = body.slice(typeStart, pos).trim();
140
+ // Normalize multi-line types: collapse internal whitespace sequences
141
+ const typeNormalized = typeRaw.replace(/\s+/g, ' ');
142
+
143
+ // Skip the terminating ; or newline
144
+ if (pos < body.length && (body[pos] === ';' || body[pos] === '\n')) pos++;
145
+
146
+ if (!name || !typeNormalized) continue;
147
+
148
+ props.push({
149
+ name,
150
+ type: typeNormalized,
151
+ required: !optional,
152
+ description: jsdoc?.trim(),
153
+ });
154
+ }
155
+
156
+ return props;
157
+ }
158
+
159
+ function extractSubComponents(sourceContent: string): SubComponentEntry[] {
160
+ const subs: SubComponentEntry[] = [];
161
+ const re =
162
+ /export\s+const\s+(\w+)\s+=\s+with(?:Provider|Context)\(ark\.(\w+)/g;
163
+ let m: RegExpExecArray | null;
164
+ while ((m = re.exec(sourceContent)) !== null) {
165
+ const [, name, element] = m;
166
+ subs.push({ name, element });
167
+ }
168
+ return subs;
169
+ }
170
+
171
+ function classifyComponent(
172
+ parsed: ParsedFigmaFile,
173
+ sourceContent: string,
174
+ ): 'simple' | 'compound' | 'composite' {
175
+ if (parsed.importStyle === 'namespace') return 'compound';
176
+ // Composite: imports from sibling DDS components (e.g., '../Accordion')
177
+ if (/from\s+['"]\.\.\//g.test(sourceContent)) return 'composite';
178
+ return 'simple';
179
+ }
180
+
181
+ function resolveSubpath(
182
+ parsed: ParsedFigmaFile,
183
+ config: FigmaCodexConfig,
184
+ projectRoot: string,
185
+ ): string | undefined {
186
+ try {
187
+ const pkgPath = join(projectRoot, 'package.json');
188
+ if (!existsSync(pkgPath)) return undefined;
189
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
190
+ const exports = pkg.exports as Record<string, unknown>;
191
+ if (!exports) return undefined;
192
+
193
+ const name = parsed.componentName;
194
+ for (const key of Object.keys(exports)) {
195
+ if (key === `./${name}` || key.endsWith(`/${name}`)) {
196
+ return `${config.packageName}${key.slice(1)}`;
197
+ }
198
+ }
199
+ } catch {
200
+ // ignore
201
+ }
202
+ return undefined;
203
+ }
204
+
205
+ export function resolveComponent(
206
+ parsed: ParsedFigmaFile,
207
+ config: FigmaCodexConfig,
208
+ ): ComponentEntry {
209
+ // Determine project root: walk up from figma file until we find package.json
210
+ let projectRoot = dirname(parsed.filePath);
211
+ for (let i = 0; i < 8; i++) {
212
+ if (existsSync(join(projectRoot, 'package.json'))) break;
213
+ projectRoot = dirname(projectRoot);
214
+ }
215
+
216
+ const sourceFile = resolveSourceFile(parsed.importSource, parsed.filePath);
217
+
218
+ let sourceContent = '';
219
+ if (sourceFile && existsSync(sourceFile)) {
220
+ sourceContent = readFileSync(sourceFile, 'utf-8');
221
+ // For index files, look for the actual implementation
222
+ if (sourceFile.endsWith('index.ts') || sourceFile.endsWith('index.tsx')) {
223
+ const implFile = resolveImplementationFile(sourceFile);
224
+ if (implFile !== sourceFile && existsSync(implFile)) {
225
+ sourceContent = readFileSync(implFile, 'utf-8');
226
+ }
227
+ }
228
+ }
229
+
230
+ // For composite components, types may be in a separate types.ts file
231
+ let typesContent = sourceContent;
232
+ if (sourceFile) {
233
+ const typesFile = join(dirname(sourceFile), 'types.ts');
234
+ if (existsSync(typesFile)) {
235
+ typesContent = readFileSync(typesFile, 'utf-8');
236
+ }
237
+ }
238
+
239
+ const componentType = classifyComponent(parsed, sourceContent);
240
+ const props = extractProps(typesContent);
241
+ const subComponents =
242
+ componentType === 'compound'
243
+ ? extractSubComponents(sourceContent)
244
+ : undefined;
245
+
246
+ const subpath = resolveSubpath(parsed, config, projectRoot);
247
+ const isNamespace = parsed.importStyle === 'namespace';
248
+ const importName = isNamespace
249
+ ? `* as ${parsed.componentName}`
250
+ : `{ ${parsed.componentName} }`;
251
+ const importFrom = subpath ?? `${config.packageName}/${parsed.componentName}`;
252
+ const primaryImport = `import ${importName} from '${importFrom}'`;
253
+
254
+ const namedExports = isNamespace
255
+ ? (subComponents?.map((s) => `${parsed.componentName}.${s.name}`) ?? [])
256
+ : [parsed.componentName];
257
+
258
+ const sourcePath = sourceFile
259
+ ? sourceFile.replace(projectRoot + '/', '')
260
+ : parsed.importSource;
261
+
262
+ return {
263
+ name: parsed.componentName,
264
+ type: componentType,
265
+ figma: {
266
+ fileKey: parsed.figmaFileKey,
267
+ nodeId: parsed.figmaNodeId,
268
+ url: parsed.figmaUrl,
269
+ },
270
+ imports: {
271
+ primary: primaryImport,
272
+ namedExports,
273
+ subpath,
274
+ },
275
+ props,
276
+ subComponents,
277
+ example: parsed.example,
278
+ sourcePath,
279
+ };
280
+ }
@@ -0,0 +1,79 @@
1
+ // src/figma-codex/schema.ts
2
+
3
+ export interface FigmaCodex {
4
+ /** Schema version for forward compatibility */
5
+ version: '1.0.0';
6
+
7
+ /** Package name of the design system */
8
+ packageName: string;
9
+
10
+ /** ISO timestamp of generation */
11
+ generatedAt: string;
12
+
13
+ /** Git commit hash (if available) */
14
+ gitHash?: string;
15
+
16
+ /** Source Figma file(s) referenced */
17
+ figmaFiles: Record<
18
+ string,
19
+ {
20
+ fileKey: string;
21
+ fileName?: string;
22
+ }
23
+ >;
24
+
25
+ /** The component registry */
26
+ components: Record<string, ComponentEntry>;
27
+ }
28
+
29
+ export interface ComponentEntry {
30
+ /** Human-readable component name */
31
+ name: string;
32
+
33
+ /** Component type classification */
34
+ type: 'simple' | 'compound' | 'composite';
35
+
36
+ /** Figma reference */
37
+ figma: {
38
+ fileKey: string;
39
+ nodeId: string;
40
+ nodeName?: string;
41
+ url: string;
42
+ };
43
+
44
+ /** Import information */
45
+ imports: {
46
+ /** Primary import statement */
47
+ primary: string;
48
+ /** Named exports available */
49
+ namedExports: string[];
50
+ /** Package subpath, e.g. '@discourser/design-system/Breadcrumb' */
51
+ subpath?: string;
52
+ };
53
+
54
+ /** Prop interface (extracted from TypeScript source) */
55
+ props: PropDefinition[];
56
+
57
+ /** Sub-components for compound components */
58
+ subComponents?: SubComponentEntry[];
59
+
60
+ /** Example JSX from .figma.tsx */
61
+ example: string;
62
+
63
+ /** Source file path relative to project root */
64
+ sourcePath: string;
65
+ }
66
+
67
+ export interface PropDefinition {
68
+ name: string;
69
+ type: string; // TypeScript type as string
70
+ required: boolean;
71
+ description?: string; // JSDoc comment if available
72
+ defaultValue?: string;
73
+ }
74
+
75
+ export interface SubComponentEntry {
76
+ name: string; // e.g. 'Root', 'Header', 'Title'
77
+ element: string; // underlying HTML element, e.g. 'div', 'h2', 'nav'
78
+ description?: string;
79
+ }
@@ -0,0 +1,54 @@
1
+ import { writeFileSync, mkdirSync, existsSync } from 'node:fs';
2
+ import { dirname, join, isAbsolute } from 'node:path';
3
+ import { execSync } from 'node:child_process';
4
+ import type { ComponentEntry, FigmaCodex } from './schema';
5
+
6
+ interface WriteOptions {
7
+ packageName: string;
8
+ outputPath: string;
9
+ }
10
+
11
+ function getGitHash(cwd: string): string | undefined {
12
+ try {
13
+ return execSync('git rev-parse --short HEAD', {
14
+ cwd,
15
+ stdio: ['pipe', 'pipe', 'pipe'],
16
+ })
17
+ .toString()
18
+ .trim();
19
+ } catch {
20
+ return undefined;
21
+ }
22
+ }
23
+
24
+ export function writeManifest(
25
+ components: Record<string, ComponentEntry>,
26
+ options: WriteOptions,
27
+ projectRoot: string,
28
+ ): void {
29
+ const figmaFiles: FigmaCodex['figmaFiles'] = {};
30
+ for (const entry of Object.values(components)) {
31
+ const { fileKey } = entry.figma;
32
+ if (fileKey && !figmaFiles[fileKey]) {
33
+ figmaFiles[fileKey] = { fileKey };
34
+ }
35
+ }
36
+
37
+ const manifest: FigmaCodex = {
38
+ version: '1.0.0',
39
+ packageName: options.packageName,
40
+ generatedAt: new Date().toISOString(),
41
+ gitHash: getGitHash(projectRoot),
42
+ figmaFiles,
43
+ components,
44
+ };
45
+
46
+ const outPath = isAbsolute(options.outputPath)
47
+ ? options.outputPath
48
+ : join(projectRoot, options.outputPath);
49
+
50
+ const outDir = dirname(outPath);
51
+ if (!existsSync(outDir)) mkdirSync(outDir, { recursive: true });
52
+
53
+ writeFileSync(outPath, JSON.stringify(manifest, null, 2) + '\n', 'utf-8');
54
+ }
@@ -19,6 +19,10 @@ import { accordion } from './recipes/accordion';
19
19
  import { drawer } from './recipes/drawer';
20
20
  import { tabs } from './recipes/tabs';
21
21
 
22
+ // Park UI recipes - Navigation
23
+ import { breadcrumb } from './recipes/breadcrumb';
24
+ import { navigationMenu } from './recipes/navigation-menu';
25
+
22
26
  // Park UI recipes - Form Elements
23
27
  import { switchRecipe } from './recipes/switch';
24
28
  import { checkbox } from './recipes/checkbox';
@@ -44,6 +48,10 @@ import { heading } from './recipes/heading';
44
48
 
45
49
  // Custom Components
46
50
  import { stepper } from './recipes/stepper';
51
+ import { contentCard } from './recipes/content-card';
52
+ import { scenarioCard } from './recipes/scenario-card';
53
+ import { scenarioQueue } from './recipes/scenario-queue';
54
+ import { scenarioSettings } from './recipes/scenario-settings';
47
55
 
48
56
  // Park UI theme extensions
49
57
  import { layerStyles } from './layer-styles';
@@ -155,6 +163,9 @@ export const discourserPandaPreset = definePreset({
155
163
  accordion,
156
164
  drawer,
157
165
  tabs,
166
+ // Navigation
167
+ breadcrumb,
168
+ navigationMenu,
158
169
  // Form Elements
159
170
  switchComponent: switchRecipe,
160
171
  checkbox,
@@ -171,6 +182,10 @@ export const discourserPandaPreset = definePreset({
171
182
  tooltip,
172
183
  // Custom Components
173
184
  stepper,
185
+ contentCard,
186
+ scenarioCard,
187
+ scenarioQueue,
188
+ scenarioSettings,
174
189
  },
175
190
  },
176
191
  },
@@ -53,13 +53,16 @@ export const accordion = defineSlotRecipe({
53
53
  overflow: 'hidden',
54
54
  borderRadius: 'var(--accordion-radius)',
55
55
  _open: {
56
- animationName: 'expand-height, fade-in',
57
- animationDuration: 'normal',
56
+ animationName: 'slide-down',
57
+ animationDuration: '250ms',
58
+ animationTimingFunction: 'ease-out',
59
+ animationFillMode: 'forwards',
58
60
  },
59
61
  _closed: {
60
- animationName: 'collapse-height, fade-out',
61
- animationDuration: 'normal',
62
- display: 'none',
62
+ animationName: 'slide-up',
63
+ animationDuration: '200ms',
64
+ animationTimingFunction: 'ease-out',
65
+ animationFillMode: 'forwards',
63
66
  },
64
67
  },
65
68
  },
@@ -1,9 +1,8 @@
1
- import { avatarAnatomy } from '@ark-ui/react/anatomy'
2
1
  import { defineSlotRecipe } from '@pandacss/dev'
3
2
 
4
3
  export const avatar = defineSlotRecipe({
5
4
  className: 'avatar',
6
- slots: avatarAnatomy.keys(),
5
+ slots: ['root', 'image', 'fallback'],
7
6
  base: {
8
7
  root: {
9
8
  display: 'inline-flex',
@@ -0,0 +1,109 @@
1
+ import { defineSlotRecipe } from '@pandacss/dev';
2
+
3
+ export const breadcrumb = defineSlotRecipe({
4
+ className: 'breadcrumb',
5
+ slots: ['root', 'list', 'link', 'item', 'separator', 'ellipsis'],
6
+ base: {
7
+ list: {
8
+ alignItems: 'center',
9
+ display: 'flex',
10
+ listStyle: 'none',
11
+ wordBreak: 'break-word',
12
+ },
13
+ link: {
14
+ alignItems: 'center',
15
+ borderRadius: 'l1',
16
+ display: 'inline-flex',
17
+ focusRing: 'outside',
18
+ gap: '2',
19
+ outline: '0',
20
+ textDecoration: 'none',
21
+ transition: 'color',
22
+ _icon: { boxSize: '1em' },
23
+ },
24
+ item: {
25
+ display: 'inline-flex',
26
+ alignItems: 'center',
27
+ color: 'fg.muted',
28
+ _last: {
29
+ color: 'fg.default',
30
+ },
31
+ },
32
+ separator: {
33
+ color: 'fg.subtle',
34
+ _icon: { boxSize: '1em' },
35
+ _rtl: { rotate: '180deg' },
36
+ },
37
+ ellipsis: {
38
+ alignItems: 'center',
39
+ color: 'fg.muted',
40
+ display: 'inline-flex',
41
+ justifyContent: 'center',
42
+ _icon: { boxSize: '1em' },
43
+ },
44
+ },
45
+
46
+ variants: {
47
+ variant: {
48
+ underline: {
49
+ link: {
50
+ textDecoration: 'underline',
51
+ textDecorationThickness: '0.1em',
52
+ textUnderlineOffset: '0.125em',
53
+ textDecorationColor: 'fg.subtle',
54
+ _hover: { textDecorationColor: 'fg.default' },
55
+ },
56
+ },
57
+ plain: {
58
+ link: {
59
+ color: 'fg.muted',
60
+ _hover: { color: 'fg.default' },
61
+ _currentPage: { color: 'fg.default' },
62
+ },
63
+ },
64
+ discourser: {
65
+ list: {
66
+ gap: '2.5',
67
+ alignItems: 'center',
68
+ },
69
+ item: {
70
+ bg: 'transparent',
71
+ px: '1.5',
72
+ py: '1.5',
73
+ borderRadius: 'l2',
74
+ h: '9',
75
+ display: 'inline-flex',
76
+ alignItems: 'center',
77
+ _last: {
78
+ color: 'fg.default',
79
+ fontWeight: 'medium',
80
+ fontSize: 'md',
81
+ fontFeatureSettings: "'liga' 0, 'calt' 0",
82
+ },
83
+ },
84
+ link: {
85
+ color: 'fg.subtle',
86
+ fontWeight: 'medium',
87
+ fontSize: 'lg',
88
+ _hover: { color: 'fg.default' },
89
+ _currentPage: { color: 'fg.default', fontWeight: 'semibold' },
90
+ },
91
+ separator: {
92
+ color: 'primary.7',
93
+ _icon: { width: '27px', height: '27px' },
94
+ },
95
+ },
96
+ },
97
+ size: {
98
+ xs: { list: { gap: '1', textStyle: 'xs' } },
99
+ sm: { list: { gap: '1', textStyle: 'sm' } },
100
+ md: { list: { gap: '1.5', textStyle: 'md' } },
101
+ lg: { list: { gap: '2', textStyle: 'lg' } },
102
+ },
103
+ },
104
+
105
+ defaultVariants: {
106
+ variant: 'plain',
107
+ size: 'md',
108
+ },
109
+ });
@@ -1,8 +1,7 @@
1
- import { checkboxAnatomy } from '@ark-ui/react/anatomy'
2
1
  import { defineSlotRecipe } from '@pandacss/dev'
3
2
 
4
3
  export const checkbox = defineSlotRecipe({
5
- slots: checkboxAnatomy.keys(),
4
+ slots: ['root', 'label', 'control', 'indicator', 'group'],
6
5
  className: 'checkbox',
7
6
  base: {
8
7
  root: {