@discourser/design-system 0.25.1 → 0.25.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/figma-codex/resolver.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,KAAK,EACV,cAAc,EAGf,MAAM,UAAU,CAAC;AAoMlB,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE,gBAAgB,GACvB,cAAc,CAyEhB"}
1
+ {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/figma-codex/resolver.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,KAAK,EACV,cAAc,EAGf,MAAM,UAAU,CAAC;AA8SlB,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE,gBAAgB,GACvB,cAAc,CAyEhB"}
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "version": "1.0.0",
3
3
  "packageName": "@discourser/design-system",
4
- "generatedAt": "2026-03-31T14:38:15.806Z",
5
- "gitHash": "e47e7ec5",
4
+ "generatedAt": "2026-03-31T15:43:15.946Z",
5
+ "gitHash": "c57712c8",
6
6
  "figmaFiles": {
7
7
  "GaHmFfmvO4loUzuZS4TgEz": {
8
8
  "fileKey": "GaHmFfmvO4loUzuZS4TgEz"
@@ -20,7 +20,13 @@
20
20
  "imports": {
21
21
  "primary": "import * as Accordion from '@discourser/design-system/Accordion'",
22
22
  "namedExports": [
23
- "Accordion.ItemBody"
23
+ "Accordion.ItemBody",
24
+ "Accordion.Root",
25
+ "Accordion.RootProvider",
26
+ "Accordion.Item",
27
+ "Accordion.ItemContent",
28
+ "Accordion.ItemIndicator",
29
+ "Accordion.ItemTrigger"
24
30
  ],
25
31
  "subpath": "@discourser/design-system/Accordion"
26
32
  },
@@ -29,6 +35,30 @@
29
35
  {
30
36
  "name": "ItemBody",
31
37
  "element": "div"
38
+ },
39
+ {
40
+ "name": "Root",
41
+ "element": "root"
42
+ },
43
+ {
44
+ "name": "RootProvider",
45
+ "element": "root"
46
+ },
47
+ {
48
+ "name": "Item",
49
+ "element": "item"
50
+ },
51
+ {
52
+ "name": "ItemContent",
53
+ "element": "itemContent"
54
+ },
55
+ {
56
+ "name": "ItemIndicator",
57
+ "element": "itemIndicator"
58
+ },
59
+ {
60
+ "name": "ItemTrigger",
61
+ "element": "itemTrigger"
32
62
  }
33
63
  ],
34
64
  "example": "<Accordion.Root collapsible>\n <Accordion.Item value=\"item-1\">\n <Accordion.ItemTrigger>\n <span>Trigger</span>\n <Accordion.ItemIndicator />\n </Accordion.ItemTrigger>\n <Accordion.ItemContent>Content</Accordion.ItemContent>\n </Accordion.Item>\n </Accordion.Root>",
@@ -716,11 +746,91 @@
716
746
  },
717
747
  "imports": {
718
748
  "primary": "import * as RadioGroup from '@discourser/design-system/RadioGroup'",
719
- "namedExports": [],
749
+ "namedExports": [
750
+ "RadioGroup.Root",
751
+ "RadioGroup.RootProvider",
752
+ "RadioGroup.Indicator",
753
+ "RadioGroup.Item",
754
+ "RadioGroup.ItemControl",
755
+ "RadioGroup.ItemText",
756
+ "RadioGroup.Label"
757
+ ],
720
758
  "subpath": "@discourser/design-system/RadioGroup"
721
759
  },
722
- "props": [],
723
- "subComponents": [],
760
+ "props": [
761
+ {
762
+ "name": "value",
763
+ "type": "string",
764
+ "required": false,
765
+ "description": "controlled selected value (string)"
766
+ },
767
+ {
768
+ "name": "defaultValue",
769
+ "type": "string",
770
+ "required": false,
771
+ "description": "uncontrolled initial value (string)"
772
+ },
773
+ {
774
+ "name": "onValueChange",
775
+ "type": "({ value }: { value: string }) => void",
776
+ "required": false,
777
+ "description": "callback ({ value }: { value: string }) => void"
778
+ },
779
+ {
780
+ "name": "disabled",
781
+ "type": "boolean",
782
+ "required": false,
783
+ "description": "boolean, disables all items"
784
+ },
785
+ {
786
+ "name": "orientation",
787
+ "type": "'horizontal' | 'vertical'",
788
+ "required": false,
789
+ "description": "'horizontal' | 'vertical' (default: 'vertical')"
790
+ },
791
+ {
792
+ "name": "value",
793
+ "type": "string",
794
+ "required": false,
795
+ "description": "required unique string for this option"
796
+ },
797
+ {
798
+ "name": "disabled",
799
+ "type": "boolean",
800
+ "required": false,
801
+ "description": "boolean, disables this item only"
802
+ }
803
+ ],
804
+ "subComponents": [
805
+ {
806
+ "name": "Root",
807
+ "element": "root"
808
+ },
809
+ {
810
+ "name": "RootProvider",
811
+ "element": "root"
812
+ },
813
+ {
814
+ "name": "Indicator",
815
+ "element": "indicator"
816
+ },
817
+ {
818
+ "name": "Item",
819
+ "element": "item"
820
+ },
821
+ {
822
+ "name": "ItemControl",
823
+ "element": "itemControl"
824
+ },
825
+ {
826
+ "name": "ItemText",
827
+ "element": "itemText"
828
+ },
829
+ {
830
+ "name": "Label",
831
+ "element": "label"
832
+ }
833
+ ],
724
834
  "example": "<RadioGroup.Root defaultValue=\"option-1\">\n <RadioGroup.Item value=\"option-1\">\n <RadioGroup.ItemControl />\n <RadioGroup.ItemText>Option 1</RadioGroup.ItemText>\n </RadioGroup.Item>\n <RadioGroup.Item value=\"option-2\">\n <RadioGroup.ItemControl />\n <RadioGroup.ItemText>Option 2</RadioGroup.ItemText>\n </RadioGroup.Item>\n <RadioGroup.Item value=\"option-3\">\n <RadioGroup.ItemControl />\n <RadioGroup.ItemText>Option 3</RadioGroup.ItemText>\n </RadioGroup.Item>\n </RadioGroup.Root>",
725
835
  "sourcePath": "src/components/RadioGroup.tsx",
726
836
  "tokens": {
@@ -931,11 +1041,128 @@
931
1041
  },
932
1042
  "imports": {
933
1043
  "primary": "import * as Slider from '@discourser/design-system/Slider'",
934
- "namedExports": [],
1044
+ "namedExports": [
1045
+ "Slider.Control",
1046
+ "Slider.Track",
1047
+ "Slider.Range",
1048
+ "Slider.Root",
1049
+ "Slider.Thumb",
1050
+ "Slider.Label",
1051
+ "Slider.ValueText",
1052
+ "Slider.Marker",
1053
+ "Slider.MarkerGroup",
1054
+ "Slider.MarkerIndicator",
1055
+ "Slider.DraggingIndicator",
1056
+ "Slider.Marks"
1057
+ ],
935
1058
  "subpath": "@discourser/design-system/Slider"
936
1059
  },
937
- "props": [],
938
- "subComponents": [],
1060
+ "props": [
1061
+ {
1062
+ "name": "defaultValue",
1063
+ "type": "string",
1064
+ "required": false,
1065
+ "description": "uncontrolled initial value array, e.g. [50]"
1066
+ },
1067
+ {
1068
+ "name": "value",
1069
+ "type": "string",
1070
+ "required": false,
1071
+ "description": "controlled value array, e.g. [50]"
1072
+ },
1073
+ {
1074
+ "name": "onValueChange",
1075
+ "type": "({ value }: { value: number[] }) => void",
1076
+ "required": false,
1077
+ "description": "callback ({ value }: { value: number[] }) => void"
1078
+ },
1079
+ {
1080
+ "name": "min",
1081
+ "type": "string",
1082
+ "required": false,
1083
+ "description": "number (default: 0)"
1084
+ },
1085
+ {
1086
+ "name": "max",
1087
+ "type": "string",
1088
+ "required": false,
1089
+ "description": "number (default: 100)"
1090
+ },
1091
+ {
1092
+ "name": "step",
1093
+ "type": "string",
1094
+ "required": false,
1095
+ "description": "number (default: 1)"
1096
+ },
1097
+ {
1098
+ "name": "orientation",
1099
+ "type": "'horizontal' | 'vertical'",
1100
+ "required": false,
1101
+ "description": "'horizontal' | 'vertical' (default: 'horizontal')"
1102
+ },
1103
+ {
1104
+ "name": "disabled",
1105
+ "type": "boolean",
1106
+ "required": false,
1107
+ "description": "boolean"
1108
+ },
1109
+ {
1110
+ "name": "colorPalette",
1111
+ "type": "'primary' | 'secondary' | 'tertiary' | 'neutral' | 'error'",
1112
+ "required": false,
1113
+ "description": "'primary' | 'secondary' | 'tertiary' | 'neutral' | 'error'"
1114
+ }
1115
+ ],
1116
+ "subComponents": [
1117
+ {
1118
+ "name": "Control",
1119
+ "element": "control"
1120
+ },
1121
+ {
1122
+ "name": "Track",
1123
+ "element": "track"
1124
+ },
1125
+ {
1126
+ "name": "Range",
1127
+ "element": "range"
1128
+ },
1129
+ {
1130
+ "name": "Root",
1131
+ "element": "div"
1132
+ },
1133
+ {
1134
+ "name": "Thumb",
1135
+ "element": "div"
1136
+ },
1137
+ {
1138
+ "name": "Label",
1139
+ "element": "label"
1140
+ },
1141
+ {
1142
+ "name": "ValueText",
1143
+ "element": "div"
1144
+ },
1145
+ {
1146
+ "name": "Marker",
1147
+ "element": "span"
1148
+ },
1149
+ {
1150
+ "name": "MarkerGroup",
1151
+ "element": "div"
1152
+ },
1153
+ {
1154
+ "name": "MarkerIndicator",
1155
+ "element": "div"
1156
+ },
1157
+ {
1158
+ "name": "DraggingIndicator",
1159
+ "element": "div"
1160
+ },
1161
+ {
1162
+ "name": "Marks",
1163
+ "element": "div"
1164
+ }
1165
+ ],
939
1166
  "example": "<Slider.Root defaultValue={[50]} orientation={orientation}>\n <Slider.Label>Volume</Slider.Label>\n <Slider.Control>\n <Slider.Track>\n <Slider.Range />\n </Slider.Track>\n <Slider.Thumbs />\n </Slider.Control>\n </Slider.Root>",
940
1167
  "sourcePath": "src/components/Slider.tsx",
941
1168
  "tokens": {
@@ -1075,11 +1302,58 @@
1075
1302
  },
1076
1303
  "imports": {
1077
1304
  "primary": "import * as Switch from '@discourser/design-system/Switch'",
1078
- "namedExports": [],
1305
+ "namedExports": [
1306
+ "Switch.Root",
1307
+ "Switch.Label",
1308
+ "Switch.Thumb",
1309
+ "Switch.Control"
1310
+ ],
1079
1311
  "subpath": "@discourser/design-system/Switch"
1080
1312
  },
1081
- "props": [],
1082
- "subComponents": [],
1313
+ "props": [
1314
+ {
1315
+ "name": "defaultChecked",
1316
+ "type": "boolean",
1317
+ "required": false,
1318
+ "description": "uncontrolled initial state (boolean)"
1319
+ },
1320
+ {
1321
+ "name": "checked",
1322
+ "type": "boolean",
1323
+ "required": false,
1324
+ "description": "controlled state (boolean)"
1325
+ },
1326
+ {
1327
+ "name": "onCheckedChange",
1328
+ "type": "({ checked }: { checked: boolean }) => void",
1329
+ "required": false,
1330
+ "description": "callback ({ checked }: { checked: boolean }) => void"
1331
+ },
1332
+ {
1333
+ "name": "disabled",
1334
+ "type": "boolean",
1335
+ "required": false,
1336
+ "description": "boolean"
1337
+ }
1338
+ ],
1339
+ "subComponents": [
1340
+ {
1341
+ "name": "Root",
1342
+ "element": "root"
1343
+ },
1344
+ {
1345
+ "name": "Label",
1346
+ "element": "label"
1347
+ },
1348
+ {
1349
+ "name": "Thumb",
1350
+ "element": "thumb"
1351
+ },
1352
+ {
1353
+ "name": "Control",
1354
+ "element": "control"
1355
+ }
1356
+ ],
1083
1357
  "example": "<Switch.Root defaultChecked={checked}>\n <Switch.Control />\n <Switch.Label>Toggle</Switch.Label>\n </Switch.Root>",
1084
1358
  "sourcePath": "src/components/Switch.tsx",
1085
1359
  "tokens": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@discourser/design-system",
3
- "version": "0.25.1",
3
+ "version": "0.25.2",
4
4
  "description": "Aesthetic-agnostic design system with Panda CSS and Ark UI",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -38,13 +38,93 @@ function resolveImplementationFile(sourceFile: string): string {
38
38
  return sourceFile;
39
39
  }
40
40
 
41
- function extractProps(sourceContent: string): PropDefinition[] {
41
+ function inferTypeFromDescription(description: string): string {
42
+ const desc = description.trim();
43
+
44
+ // Direct type keyword at start
45
+ const directType = desc.match(
46
+ /^(boolean|string|number|string\[\]|number\[\])(\s*[,.]|$)/,
47
+ );
48
+ if (directType) return directType[1];
49
+
50
+ // Parenthesized type at end: "description (boolean)"
51
+ const parenEnd = desc.match(/\(([a-zA-Z[\]]+)\)\s*$/);
52
+ if (parenEnd && /^(boolean|string|number)(\[\])?$/.test(parenEnd[1])) {
53
+ return parenEnd[1];
54
+ }
55
+
56
+ // Union of string literals: 'a' | 'b' | 'c' — possibly with trailing " (default: ...)"
57
+ if (/^'[^']+'(\s*\|\s*'[^']+')+/.test(desc)) {
58
+ const match = desc.match(/^('(?:[^']+)'\s*(?:\|\s*'(?:[^']+)'\s*)*)/);
59
+ if (match) return match[1].trim();
60
+ }
61
+
62
+ // Callback: "callback (...) => void"
63
+ if (desc.startsWith('callback')) {
64
+ const rest = desc.replace(/^callback\s+/, '');
65
+ return rest || '(...args: unknown[]) => void';
66
+ }
67
+
68
+ return 'string';
69
+ }
70
+
71
+ function extractPropsFromFigmaJsdoc(figmaFilePath: string): PropDefinition[] {
72
+ if (!existsSync(figmaFilePath)) return [];
73
+ const content = readFileSync(figmaFilePath, 'utf-8');
74
+ const props: PropDefinition[] = [];
75
+
76
+ const lines = content.split('\n');
77
+ let inPropsSection = false;
78
+
79
+ for (const line of lines) {
80
+ const trimmed = line.trim();
81
+
82
+ if (!trimmed.startsWith('//')) {
83
+ if (inPropsSection) break; // left the comment block
84
+ continue;
85
+ }
86
+
87
+ // Strip the leading `//` (and optional single space)
88
+ const commentContent = trimmed.replace(/^\/\/\s?/, '');
89
+
90
+ if (/^Key props/i.test(commentContent)) {
91
+ inPropsSection = true;
92
+ continue;
93
+ }
94
+
95
+ if (!inPropsSection) continue;
96
+
97
+ // Match " propName — description" (em-dash, en-dash, or hyphen)
98
+ const propMatch = commentContent.match(/^\s+(\w+)\s+[—–-]+\s+(.+)$/);
99
+ if (propMatch) {
100
+ const [, name, description] = propMatch;
101
+ const type = inferTypeFromDescription(description);
102
+ props.push({
103
+ name,
104
+ type,
105
+ required: false,
106
+ description: description.trim(),
107
+ });
108
+ }
109
+ }
110
+
111
+ return props;
112
+ }
113
+
114
+ function extractProps(
115
+ sourceContent: string,
116
+ figmaFilePath?: string,
117
+ ): PropDefinition[] {
42
118
  const props: PropDefinition[] = [];
43
119
 
44
120
  // Step 1: Find the Props interface and extract its full body using balanced-brace tracking
45
121
  const ifaceRe = /interface\s+\w+Props\s*\{/g;
46
122
  const startMatch = ifaceRe.exec(sourceContent);
47
- if (!startMatch) return props;
123
+ if (!startMatch) {
124
+ // Fallback: no interface found — read Key props section from the .figma.tsx JSDoc
125
+ if (figmaFilePath) return extractPropsFromFigmaJsdoc(figmaFilePath);
126
+ return props;
127
+ }
48
128
 
49
129
  // Find the opening { of the interface body
50
130
  const openBrace = sourceContent.indexOf(
@@ -157,15 +237,41 @@ function extractProps(sourceContent: string): PropDefinition[] {
157
237
  }
158
238
 
159
239
  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;
240
+ const subs = new Map<string, SubComponentEntry>();
163
241
  let m: RegExpExecArray | null;
164
- while ((m = re.exec(sourceContent)) !== null) {
165
- const [, name, element] = m;
166
- subs.push({ name, element });
242
+
243
+ // Pattern 1: withProvider/withContext(ark.tagName, ...) Park UI ark factory
244
+ const arkRe =
245
+ /export\s+const\s+(\w+)\s+=\s+with(?:Provider|Context)\(ark\.(\w+)/g;
246
+ while ((m = arkRe.exec(sourceContent)) !== null) {
247
+ subs.set(m[1], { name: m[1], element: m[2] });
248
+ }
249
+
250
+ // Pattern 2: withProvider/withContext(Namespace.Sub, 'slotName') — styleContext pattern
251
+ // Used by RadioGroup, Switch, and similar Ark UI compound components
252
+ const styleCtxRe =
253
+ /export\s+const\s+(\w+)\s+=\s+with(?:Provider|Context)\(\s*\w+\.\w+\s*,\s*['"](\w+)['"]/g;
254
+ while ((m = styleCtxRe.exec(sourceContent)) !== null) {
255
+ if (!subs.has(m[1])) subs.set(m[1], { name: m[1], element: m[2] });
167
256
  }
168
- return subs;
257
+
258
+ // Pattern 3: createStyledComponent(Namespace.Sub, 'slotName', ...) — custom styled wrapper
259
+ // Used by Slider and similar components with manual style context
260
+ const styledRe =
261
+ /export\s+const\s+(\w+)\s+=\s+createStyledComponent\(\s*\w+\.\w+\s*,\s*['"](\w+)['"]/g;
262
+ while ((m = styledRe.exec(sourceContent)) !== null) {
263
+ if (!subs.has(m[1])) subs.set(m[1], { name: m[1], element: m[2] });
264
+ }
265
+
266
+ // Pattern 4: forwardRef<HTMLXxxElement, ...> — typed forward ref exports
267
+ const forwardRefRe =
268
+ /export\s+const\s+(\w+)\s+=\s+forwardRef<HTML(\w+)Element/g;
269
+ while ((m = forwardRefRe.exec(sourceContent)) !== null) {
270
+ if (!subs.has(m[1]))
271
+ subs.set(m[1], { name: m[1], element: m[2].toLowerCase() });
272
+ }
273
+
274
+ return Array.from(subs.values());
169
275
  }
170
276
 
171
277
  function classifyComponent(
@@ -237,7 +343,7 @@ export function resolveComponent(
237
343
  }
238
344
 
239
345
  const componentType = classifyComponent(parsed, sourceContent);
240
- const props = extractProps(typesContent);
346
+ const props = extractProps(typesContent, parsed.filePath);
241
347
  const subComponents =
242
348
  componentType === 'compound'
243
349
  ? extractSubComponents(sourceContent)