@discourser/design-system 0.15.1 → 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.
- package/dist/{chunk-UNWXE6UB.cjs → chunk-2P7Z5PVP.cjs} +817 -16
- package/dist/chunk-2P7Z5PVP.cjs.map +1 -0
- package/dist/{chunk-ABC7N32K.cjs → chunk-PFWU7QSM.cjs} +464 -8
- package/dist/chunk-PFWU7QSM.cjs.map +1 -0
- package/dist/{chunk-GD6Q2FUE.js → chunk-QC7LGFM3.js} +808 -18
- package/dist/chunk-QC7LGFM3.js.map +1 -0
- package/dist/{chunk-SBKRSXSZ.js → chunk-SNUJBT5R.js} +464 -8
- package/dist/chunk-SNUJBT5R.js.map +1 -0
- package/dist/components/Accordion.figma.d.ts +2 -0
- package/dist/components/Accordion.figma.d.ts.map +1 -0
- package/dist/components/Breadcrumb.d.ts +2 -0
- package/dist/components/Breadcrumb.d.ts.map +1 -1
- package/dist/components/Breadcrumb.figma.d.ts +2 -0
- package/dist/components/Breadcrumb.figma.d.ts.map +1 -0
- package/dist/components/ContentCard/ContentCard.d.ts +13 -0
- package/dist/components/ContentCard/ContentCard.d.ts.map +1 -0
- package/dist/components/ContentCard/ContentCard.figma.d.ts +2 -0
- package/dist/components/ContentCard/ContentCard.figma.d.ts.map +1 -0
- package/dist/components/ContentCard/index.d.ts +2 -0
- package/dist/components/ContentCard/index.d.ts.map +1 -0
- package/dist/components/{Heading.d.ts → Header.d.ts} +3 -3
- package/dist/components/Header.d.ts.map +1 -0
- package/dist/components/Header.figma.d.ts +2 -0
- package/dist/components/Header.figma.d.ts.map +1 -0
- package/dist/components/Icons/AccountIcon.d.ts +6 -0
- package/dist/components/Icons/AccountIcon.d.ts.map +1 -0
- package/dist/components/Icons/ChevronUpIcon.d.ts +6 -0
- package/dist/components/Icons/ChevronUpIcon.d.ts.map +1 -0
- package/dist/components/Icons/ClockIcon.d.ts.map +1 -1
- package/dist/components/Icons/DashboardIcon.d.ts +6 -0
- package/dist/components/Icons/DashboardIcon.d.ts.map +1 -0
- package/dist/components/Icons/DiscourserLogo.d.ts +6 -0
- package/dist/components/Icons/DiscourserLogo.d.ts.map +1 -0
- package/dist/components/Icons/DiscourserLogo.figma.d.ts +2 -0
- package/dist/components/Icons/DiscourserLogo.figma.d.ts.map +1 -0
- package/dist/components/Icons/GripDotsVerticalIcon.d.ts.map +1 -1
- package/dist/components/Icons/HelpIcon.d.ts +6 -0
- package/dist/components/Icons/HelpIcon.d.ts.map +1 -0
- package/dist/components/Icons/NotebookIcon.d.ts +6 -0
- package/dist/components/Icons/NotebookIcon.d.ts.map +1 -0
- package/dist/components/Icons/RightArrowIcon.d.ts +6 -0
- package/dist/components/Icons/RightArrowIcon.d.ts.map +1 -0
- package/dist/components/Icons/ScenarioIcon.d.ts +6 -0
- package/dist/components/Icons/ScenarioIcon.d.ts.map +1 -0
- package/dist/components/Icons/index.d.ts +9 -1
- package/dist/components/Icons/index.d.ts.map +1 -1
- package/dist/components/NavigationMenu/NavigationMenu.d.ts +3 -0
- package/dist/components/NavigationMenu/NavigationMenu.d.ts.map +1 -0
- package/dist/components/NavigationMenu/NavigationMenu.figma.d.ts +2 -0
- package/dist/components/NavigationMenu/NavigationMenu.figma.d.ts.map +1 -0
- package/dist/components/NavigationMenu/index.d.ts +3 -0
- package/dist/components/NavigationMenu/index.d.ts.map +1 -0
- package/dist/components/NavigationMenu/types.d.ts +25 -0
- package/dist/components/NavigationMenu/types.d.ts.map +1 -0
- package/dist/components/QuickStartPage/QuickStartPage.d.ts +21 -0
- package/dist/components/QuickStartPage/QuickStartPage.d.ts.map +1 -0
- package/dist/components/QuickStartPage/index.d.ts +3 -0
- package/dist/components/QuickStartPage/index.d.ts.map +1 -0
- package/dist/components/ScenarioQueue/ScenarioQueue.figma.d.ts +2 -0
- package/dist/components/ScenarioQueue/ScenarioQueue.figma.d.ts.map +1 -0
- package/dist/components/ScenarioSettings/ScenarioSettings.d.ts +3 -0
- package/dist/components/ScenarioSettings/ScenarioSettings.d.ts.map +1 -0
- package/dist/components/ScenarioSettings/ScenarioSettings.figma.d.ts +2 -0
- package/dist/components/ScenarioSettings/ScenarioSettings.figma.d.ts.map +1 -0
- package/dist/components/ScenarioSettings/index.d.ts +3 -0
- package/dist/components/ScenarioSettings/index.d.ts.map +1 -0
- package/dist/components/ScenarioSettings/types.d.ts +54 -0
- package/dist/components/ScenarioSettings/types.d.ts.map +1 -0
- package/dist/components/index.cjs +86 -42
- package/dist/components/index.d.ts +14 -3
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +1 -1
- package/dist/figma-codex/config.d.ts +8 -0
- package/dist/figma-codex/config.d.ts.map +1 -0
- package/dist/figma-codex/fixtures/CompoundComponent/CompoundComponent.d.ts +6 -0
- package/dist/figma-codex/fixtures/CompoundComponent/CompoundComponent.d.ts.map +1 -0
- package/dist/figma-codex/fixtures/CompoundComponent/index.d.ts +2 -0
- package/dist/figma-codex/fixtures/CompoundComponent/index.d.ts.map +1 -0
- package/dist/figma-codex/fixtures/CompoundComponent.figma.d.ts +2 -0
- package/dist/figma-codex/fixtures/CompoundComponent.figma.d.ts.map +1 -0
- package/dist/figma-codex/fixtures/SimpleComponent.d.ts +8 -0
- package/dist/figma-codex/fixtures/SimpleComponent.d.ts.map +1 -0
- package/dist/figma-codex/fixtures/SimpleComponent.figma.d.ts +2 -0
- package/dist/figma-codex/fixtures/SimpleComponent.figma.d.ts.map +1 -0
- package/dist/figma-codex/generate.d.ts +6 -0
- package/dist/figma-codex/generate.d.ts.map +1 -0
- package/dist/figma-codex/parser.d.ts +18 -0
- package/dist/figma-codex/parser.d.ts.map +1 -0
- package/dist/figma-codex/resolver.d.ts +5 -0
- package/dist/figma-codex/resolver.d.ts.map +1 -0
- package/dist/figma-codex/schema.d.ts +60 -0
- package/dist/figma-codex/schema.d.ts.map +1 -0
- package/dist/figma-codex/writer.d.ts +8 -0
- package/dist/figma-codex/writer.d.ts.map +1 -0
- package/dist/figma-codex.json +373 -0
- package/dist/index.cjs +90 -46
- package/dist/index.js +2 -2
- package/dist/preset/index.cjs +2 -2
- package/dist/preset/index.d.ts.map +1 -1
- package/dist/preset/index.js +1 -1
- package/dist/preset/recipes/accordion.d.ts.map +1 -1
- package/dist/preset/recipes/breadcrumb.d.ts.map +1 -1
- package/dist/preset/recipes/content-card.d.ts +2 -0
- package/dist/preset/recipes/content-card.d.ts.map +1 -0
- package/dist/preset/recipes/index.d.ts +4 -0
- package/dist/preset/recipes/index.d.ts.map +1 -1
- package/dist/preset/recipes/navigation-menu.d.ts +2 -0
- package/dist/preset/recipes/navigation-menu.d.ts.map +1 -0
- package/dist/preset/recipes/scenario-settings.d.ts +2 -0
- package/dist/preset/recipes/scenario-settings.d.ts.map +1 -0
- package/package.json +26 -2
- package/src/components/Accordion.figma.tsx +20 -0
- package/src/components/Breadcrumb.figma.tsx +18 -0
- package/src/components/Breadcrumb.tsx +33 -15
- package/src/components/ContentCard/ContentCard.figma.tsx +21 -0
- package/src/components/ContentCard/ContentCard.test.tsx +197 -0
- package/src/components/ContentCard/ContentCard.tsx +19 -0
- package/src/components/ContentCard/index.ts +13 -0
- package/src/components/Header.figma.tsx +25 -0
- package/src/components/{Heading.tsx → Header.tsx} +2 -2
- package/src/components/Icons/AccountIcon.tsx +26 -0
- package/src/components/Icons/ChevronUpIcon.tsx +24 -0
- package/src/components/Icons/ClockIcon.tsx +6 -6
- package/src/components/Icons/DashboardIcon.tsx +47 -0
- package/src/components/Icons/Discourser-Logo.svg +14 -0
- package/src/components/Icons/DiscourserLogo.figma.tsx +10 -0
- package/src/components/Icons/DiscourserLogo.tsx +72 -0
- package/src/components/Icons/GripDotsVerticalIcon.tsx +6 -6
- package/src/components/Icons/HelpIcon.tsx +26 -0
- package/src/components/Icons/NotebookIcon.tsx +26 -0
- package/src/components/Icons/RightArrowIcon.tsx +23 -0
- package/src/components/Icons/ScenarioIcon.tsx +26 -0
- package/src/components/Icons/index.ts +13 -2
- package/src/components/NavigationMenu/NavigationMenu.figma.tsx +26 -0
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +524 -0
- package/src/components/NavigationMenu/NavigationMenu.tsx +102 -0
- package/src/components/NavigationMenu/index.ts +2 -0
- package/src/components/NavigationMenu/types.ts +27 -0
- package/src/components/QuickStartPage/QuickStartPage.tsx +627 -0
- package/src/components/QuickStartPage/index.ts +2 -0
- package/src/components/ScenarioQueue/ScenarioQueue.figma.tsx +37 -0
- package/src/components/ScenarioSettings/ScenarioSettings.figma.tsx +12 -0
- package/src/components/ScenarioSettings/ScenarioSettings.test.tsx +406 -0
- package/src/components/ScenarioSettings/ScenarioSettings.tsx +386 -0
- package/src/components/ScenarioSettings/index.ts +11 -0
- package/src/components/ScenarioSettings/types.ts +70 -0
- package/src/components/__tests__/Breadcrumb.test.tsx +94 -0
- package/src/components/index.ts +38 -4
- package/src/figma-codex/README.md +186 -0
- package/src/figma-codex/__tests__/config.test.ts +63 -0
- package/src/figma-codex/__tests__/generate.test.ts +78 -0
- package/src/figma-codex/__tests__/parser.test.ts +138 -0
- package/src/figma-codex/__tests__/resolver.test.ts +196 -0
- package/src/figma-codex/__tests__/writer.test.ts +111 -0
- package/src/figma-codex/config.ts +42 -0
- package/src/figma-codex/fixtures/CompoundComponent/CompoundComponent.tsx +17 -0
- package/src/figma-codex/fixtures/CompoundComponent/index.ts +1 -0
- package/src/figma-codex/fixtures/CompoundComponent.figma.tsx +14 -0
- package/src/figma-codex/fixtures/SimpleComponent.figma.tsx +10 -0
- package/src/figma-codex/fixtures/SimpleComponent.tsx +10 -0
- package/src/figma-codex/fixtures/expected-output.json +78 -0
- package/src/figma-codex/generate.ts +106 -0
- package/src/figma-codex/parser.ts +138 -0
- package/src/figma-codex/resolver.ts +280 -0
- package/src/figma-codex/schema.ts +79 -0
- package/src/figma-codex/writer.ts +54 -0
- package/src/preset/index.ts +6 -0
- package/src/preset/recipes/accordion.ts +8 -5
- package/src/preset/recipes/breadcrumb.ts +34 -2
- package/src/preset/recipes/content-card.ts +124 -0
- package/src/preset/recipes/index.ts +4 -0
- package/src/preset/recipes/navigation-menu.ts +97 -0
- package/src/preset/recipes/scenario-settings.ts +182 -0
- package/src/test/setup.ts +12 -9
- package/dist/chunk-ABC7N32K.cjs.map +0 -1
- package/dist/chunk-GD6Q2FUE.js.map +0 -1
- package/dist/chunk-SBKRSXSZ.js.map +0 -1
- package/dist/chunk-UNWXE6UB.cjs.map +0 -1
- 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
|
+
}
|
package/src/preset/index.ts
CHANGED
|
@@ -21,6 +21,7 @@ import { tabs } from './recipes/tabs';
|
|
|
21
21
|
|
|
22
22
|
// Park UI recipes - Navigation
|
|
23
23
|
import { breadcrumb } from './recipes/breadcrumb';
|
|
24
|
+
import { navigationMenu } from './recipes/navigation-menu';
|
|
24
25
|
|
|
25
26
|
// Park UI recipes - Form Elements
|
|
26
27
|
import { switchRecipe } from './recipes/switch';
|
|
@@ -47,8 +48,10 @@ import { heading } from './recipes/heading';
|
|
|
47
48
|
|
|
48
49
|
// Custom Components
|
|
49
50
|
import { stepper } from './recipes/stepper';
|
|
51
|
+
import { contentCard } from './recipes/content-card';
|
|
50
52
|
import { scenarioCard } from './recipes/scenario-card';
|
|
51
53
|
import { scenarioQueue } from './recipes/scenario-queue';
|
|
54
|
+
import { scenarioSettings } from './recipes/scenario-settings';
|
|
52
55
|
|
|
53
56
|
// Park UI theme extensions
|
|
54
57
|
import { layerStyles } from './layer-styles';
|
|
@@ -162,6 +165,7 @@ export const discourserPandaPreset = definePreset({
|
|
|
162
165
|
tabs,
|
|
163
166
|
// Navigation
|
|
164
167
|
breadcrumb,
|
|
168
|
+
navigationMenu,
|
|
165
169
|
// Form Elements
|
|
166
170
|
switchComponent: switchRecipe,
|
|
167
171
|
checkbox,
|
|
@@ -178,8 +182,10 @@ export const discourserPandaPreset = definePreset({
|
|
|
178
182
|
tooltip,
|
|
179
183
|
// Custom Components
|
|
180
184
|
stepper,
|
|
185
|
+
contentCard,
|
|
181
186
|
scenarioCard,
|
|
182
187
|
scenarioQueue,
|
|
188
|
+
scenarioSettings,
|
|
183
189
|
},
|
|
184
190
|
},
|
|
185
191
|
},
|
|
@@ -53,13 +53,16 @@ export const accordion = defineSlotRecipe({
|
|
|
53
53
|
overflow: 'hidden',
|
|
54
54
|
borderRadius: 'var(--accordion-radius)',
|
|
55
55
|
_open: {
|
|
56
|
-
animationName: '
|
|
57
|
-
animationDuration: '
|
|
56
|
+
animationName: 'slide-down',
|
|
57
|
+
animationDuration: '250ms',
|
|
58
|
+
animationTimingFunction: 'ease-out',
|
|
59
|
+
animationFillMode: 'forwards',
|
|
58
60
|
},
|
|
59
61
|
_closed: {
|
|
60
|
-
animationName: '
|
|
61
|
-
animationDuration: '
|
|
62
|
-
|
|
62
|
+
animationName: 'slide-up',
|
|
63
|
+
animationDuration: '200ms',
|
|
64
|
+
animationTimingFunction: 'ease-out',
|
|
65
|
+
animationFillMode: 'forwards',
|
|
63
66
|
},
|
|
64
67
|
},
|
|
65
68
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineSlotRecipe } from '@pandacss/dev'
|
|
1
|
+
import { defineSlotRecipe } from '@pandacss/dev';
|
|
2
2
|
|
|
3
3
|
export const breadcrumb = defineSlotRecipe({
|
|
4
4
|
className: 'breadcrumb',
|
|
@@ -61,6 +61,38 @@ export const breadcrumb = defineSlotRecipe({
|
|
|
61
61
|
_currentPage: { color: 'fg.default' },
|
|
62
62
|
},
|
|
63
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
|
+
},
|
|
64
96
|
},
|
|
65
97
|
size: {
|
|
66
98
|
xs: { list: { gap: '1', textStyle: 'xs' } },
|
|
@@ -74,4 +106,4 @@ export const breadcrumb = defineSlotRecipe({
|
|
|
74
106
|
variant: 'plain',
|
|
75
107
|
size: 'md',
|
|
76
108
|
},
|
|
77
|
-
})
|
|
109
|
+
});
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { defineSlotRecipe } from '@pandacss/dev';
|
|
2
|
+
|
|
3
|
+
export const contentCard = defineSlotRecipe({
|
|
4
|
+
className: 'content-card',
|
|
5
|
+
slots: [
|
|
6
|
+
'root',
|
|
7
|
+
'header',
|
|
8
|
+
'title',
|
|
9
|
+
'badgeBar',
|
|
10
|
+
'body',
|
|
11
|
+
'section',
|
|
12
|
+
'sectionTitle',
|
|
13
|
+
'separator',
|
|
14
|
+
'list',
|
|
15
|
+
'listItem',
|
|
16
|
+
],
|
|
17
|
+
base: {
|
|
18
|
+
root: {
|
|
19
|
+
bg: 'neutral.1',
|
|
20
|
+
borderRadius: 'xl',
|
|
21
|
+
borderWidth: '2px',
|
|
22
|
+
borderColor: 'border.default',
|
|
23
|
+
display: 'flex',
|
|
24
|
+
flexDirection: 'column',
|
|
25
|
+
w: 'full',
|
|
26
|
+
},
|
|
27
|
+
header: {
|
|
28
|
+
display: 'flex',
|
|
29
|
+
flexDirection: 'column',
|
|
30
|
+
gap: '4',
|
|
31
|
+
},
|
|
32
|
+
title: {
|
|
33
|
+
fontFamily: 'heading',
|
|
34
|
+
fontWeight: 'semibold',
|
|
35
|
+
fontSize: '2xl',
|
|
36
|
+
color: 'fg.default',
|
|
37
|
+
fontVariationSettings: "'SOFT' 0, 'WONK' 1",
|
|
38
|
+
},
|
|
39
|
+
badgeBar: {
|
|
40
|
+
display: 'flex',
|
|
41
|
+
flexWrap: 'wrap',
|
|
42
|
+
gap: '2.5',
|
|
43
|
+
px: '2.5',
|
|
44
|
+
py: '1',
|
|
45
|
+
},
|
|
46
|
+
body: {
|
|
47
|
+
display: 'flex',
|
|
48
|
+
flexDirection: 'column',
|
|
49
|
+
gap: '9',
|
|
50
|
+
fontSize: 'xl',
|
|
51
|
+
lineHeight: '1.4',
|
|
52
|
+
color: 'fg.default',
|
|
53
|
+
},
|
|
54
|
+
section: {
|
|
55
|
+
display: 'flex',
|
|
56
|
+
flexDirection: 'column',
|
|
57
|
+
gap: '5',
|
|
58
|
+
},
|
|
59
|
+
sectionTitle: {
|
|
60
|
+
fontFamily: 'heading',
|
|
61
|
+
fontWeight: 'semibold',
|
|
62
|
+
fontSize: '2xl',
|
|
63
|
+
color: 'fg.default',
|
|
64
|
+
fontVariationSettings: "'SOFT' 0, 'WONK' 1",
|
|
65
|
+
px: '2.5',
|
|
66
|
+
py: '2.5',
|
|
67
|
+
},
|
|
68
|
+
separator: {
|
|
69
|
+
borderTopWidth: '1px',
|
|
70
|
+
borderColor: 'border.default',
|
|
71
|
+
w: 'full',
|
|
72
|
+
my: '4',
|
|
73
|
+
},
|
|
74
|
+
list: {
|
|
75
|
+
display: 'flex',
|
|
76
|
+
flexDirection: 'column',
|
|
77
|
+
gap: '5',
|
|
78
|
+
listStyleType: 'disc',
|
|
79
|
+
ps: '8',
|
|
80
|
+
},
|
|
81
|
+
listItem: {
|
|
82
|
+
fontSize: 'xl',
|
|
83
|
+
lineHeight: '1.4',
|
|
84
|
+
color: 'fg.default',
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
variants: {
|
|
88
|
+
size: {
|
|
89
|
+
sm: {
|
|
90
|
+
root: { maxW: 'xl', pt: '6', px: '6', pb: '6' },
|
|
91
|
+
title: { fontSize: 'xl' },
|
|
92
|
+
body: { fontSize: 'lg' },
|
|
93
|
+
listItem: { fontSize: 'lg' },
|
|
94
|
+
},
|
|
95
|
+
md: {
|
|
96
|
+
root: { maxW: '2xl', pt: '10', px: '10', pb: '10', gap: '6' },
|
|
97
|
+
title: { fontSize: '2xl' },
|
|
98
|
+
body: { fontSize: 'xl' },
|
|
99
|
+
listItem: { fontSize: 'xl' },
|
|
100
|
+
},
|
|
101
|
+
lg: {
|
|
102
|
+
root: { maxW: '4xl', pt: '12', px: '12', pb: '12', gap: '8' },
|
|
103
|
+
title: { fontSize: '3xl' },
|
|
104
|
+
body: { fontSize: 'xl' },
|
|
105
|
+
listItem: { fontSize: 'xl' },
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
variant: {
|
|
109
|
+
elevated: {
|
|
110
|
+
root: { boxShadow: 'lg', borderWidth: '0' },
|
|
111
|
+
},
|
|
112
|
+
outline: {
|
|
113
|
+
root: { borderWidth: '2px', borderColor: 'border.default' },
|
|
114
|
+
},
|
|
115
|
+
flat: {
|
|
116
|
+
root: { borderWidth: '0', bg: 'transparent' },
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
defaultVariants: {
|
|
121
|
+
size: 'md',
|
|
122
|
+
variant: 'outline',
|
|
123
|
+
},
|
|
124
|
+
});
|
|
@@ -13,6 +13,7 @@ export * from './tabs';
|
|
|
13
13
|
|
|
14
14
|
// Navigation
|
|
15
15
|
export * from './breadcrumb';
|
|
16
|
+
export * from './navigation-menu';
|
|
16
17
|
|
|
17
18
|
// Form elements
|
|
18
19
|
export * from './switch';
|
|
@@ -43,5 +44,8 @@ export * from './absolute-center';
|
|
|
43
44
|
export * from './group';
|
|
44
45
|
|
|
45
46
|
// Custom Components
|
|
47
|
+
export * from './stepper';
|
|
48
|
+
export * from './content-card';
|
|
46
49
|
export * from './scenario-card';
|
|
47
50
|
export * from './scenario-queue';
|
|
51
|
+
export * from './scenario-settings';
|