sdocs 0.0.1

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 (129) hide show
  1. package/README.md +43 -0
  2. package/bin/sdocs.js +2 -0
  3. package/dist/Sdocs.svelte +1210 -0
  4. package/dist/Sdocs.svelte.d.ts +5 -0
  5. package/dist/cli/app-plugin.d.ts +7 -0
  6. package/dist/cli/app-plugin.js +69 -0
  7. package/dist/cli/config.d.ts +12 -0
  8. package/dist/cli/config.js +34 -0
  9. package/dist/cli/index.d.ts +1 -0
  10. package/dist/cli/index.js +72 -0
  11. package/dist/cli/server.d.ts +2 -0
  12. package/dist/cli/server.js +62 -0
  13. package/dist/docgen.d.ts +47 -0
  14. package/dist/docgen.js +463 -0
  15. package/dist/index.d.ts +2 -0
  16. package/dist/index.js +4 -0
  17. package/dist/internal/ComponentPreview.svelte +58 -0
  18. package/dist/internal/ComponentPreview.svelte.d.ts +17 -0
  19. package/dist/internal/CssPropsTable.svelte +239 -0
  20. package/dist/internal/CssPropsTable.svelte.d.ts +11 -0
  21. package/dist/internal/Home.svelte +92 -0
  22. package/dist/internal/Home.svelte.d.ts +9 -0
  23. package/dist/internal/MethodsTable.svelte +72 -0
  24. package/dist/internal/MethodsTable.svelte.d.ts +7 -0
  25. package/dist/internal/PropsTable.svelte +342 -0
  26. package/dist/internal/PropsTable.svelte.d.ts +12 -0
  27. package/dist/internal/Showcase.svelte +130 -0
  28. package/dist/internal/Showcase.svelte.d.ts +21 -0
  29. package/dist/types.d.ts +162 -0
  30. package/dist/types.js +1 -0
  31. package/dist/ui/Badge/Badge.docs.svelte +46 -0
  32. package/dist/ui/Badge/Badge.docs.svelte.d.ts +26 -0
  33. package/dist/ui/Badge/Badge.svelte +59 -0
  34. package/dist/ui/Badge/Badge.svelte.d.ts +17 -0
  35. package/dist/ui/Badge/index.d.ts +1 -0
  36. package/dist/ui/Badge/index.js +1 -0
  37. package/dist/ui/Checkbox/Checkbox.docs.svelte +51 -0
  38. package/dist/ui/Checkbox/Checkbox.docs.svelte.d.ts +27 -0
  39. package/dist/ui/Checkbox/Checkbox.svelte +169 -0
  40. package/dist/ui/Checkbox/Checkbox.svelte.d.ts +18 -0
  41. package/dist/ui/Checkbox/index.d.ts +1 -0
  42. package/dist/ui/Checkbox/index.js +1 -0
  43. package/dist/ui/CodeBlock/CodeBlock.docs.svelte +28 -0
  44. package/dist/ui/CodeBlock/CodeBlock.docs.svelte.d.ts +24 -0
  45. package/dist/ui/CodeBlock/CodeBlock.svelte +101 -0
  46. package/dist/ui/CodeBlock/CodeBlock.svelte.d.ts +7 -0
  47. package/dist/ui/CodeBlock/index.d.ts +1 -0
  48. package/dist/ui/CodeBlock/index.js +1 -0
  49. package/dist/ui/Frame/Frame.docs.svelte +140 -0
  50. package/dist/ui/Frame/Frame.docs.svelte.d.ts +26 -0
  51. package/dist/ui/Frame/Frame.svelte +88 -0
  52. package/dist/ui/Frame/Frame.svelte.d.ts +15 -0
  53. package/dist/ui/Frame/index.d.ts +1 -0
  54. package/dist/ui/Frame/index.js +1 -0
  55. package/dist/ui/InputNumber/InputNumber.docs.svelte +50 -0
  56. package/dist/ui/InputNumber/InputNumber.docs.svelte.d.ts +26 -0
  57. package/dist/ui/InputNumber/InputNumber.svelte +275 -0
  58. package/dist/ui/InputNumber/InputNumber.svelte.d.ts +26 -0
  59. package/dist/ui/InputNumber/index.d.ts +1 -0
  60. package/dist/ui/InputNumber/index.js +1 -0
  61. package/dist/ui/InputText/InputText.docs.svelte +43 -0
  62. package/dist/ui/InputText/InputText.docs.svelte.d.ts +26 -0
  63. package/dist/ui/InputText/InputText.svelte +116 -0
  64. package/dist/ui/InputText/InputText.svelte.d.ts +22 -0
  65. package/dist/ui/InputText/index.d.ts +1 -0
  66. package/dist/ui/InputText/index.js +1 -0
  67. package/dist/ui/Panel/CollapsiblePanel.docs.svelte +45 -0
  68. package/dist/ui/Panel/CollapsiblePanel.docs.svelte.d.ts +25 -0
  69. package/dist/ui/Panel/CollapsiblePanel.svelte +93 -0
  70. package/dist/ui/Panel/CollapsiblePanel.svelte.d.ts +14 -0
  71. package/dist/ui/Panel/index.d.ts +1 -0
  72. package/dist/ui/Panel/index.js +1 -0
  73. package/dist/ui/Placeholder/Placeholder.docs.svelte +49 -0
  74. package/dist/ui/Placeholder/Placeholder.docs.svelte.d.ts +26 -0
  75. package/dist/ui/Placeholder/Placeholder.svelte +99 -0
  76. package/dist/ui/Placeholder/Placeholder.svelte.d.ts +21 -0
  77. package/dist/ui/Placeholder/index.d.ts +1 -0
  78. package/dist/ui/Placeholder/index.js +1 -0
  79. package/dist/ui/Radio/Radio.docs.svelte +67 -0
  80. package/dist/ui/Radio/Radio.docs.svelte.d.ts +27 -0
  81. package/dist/ui/Radio/Radio.svelte +165 -0
  82. package/dist/ui/Radio/Radio.svelte.d.ts +22 -0
  83. package/dist/ui/Radio/RadioGroup.docs.svelte +70 -0
  84. package/dist/ui/Radio/RadioGroup.docs.svelte.d.ts +27 -0
  85. package/dist/ui/Radio/RadioGroup.svelte +98 -0
  86. package/dist/ui/Radio/RadioGroup.svelte.d.ts +27 -0
  87. package/dist/ui/Radio/index.d.ts +2 -0
  88. package/dist/ui/Radio/index.js +2 -0
  89. package/dist/ui/SegmentControl/SegmentControl.docs.svelte +54 -0
  90. package/dist/ui/SegmentControl/SegmentControl.docs.svelte.d.ts +25 -0
  91. package/dist/ui/SegmentControl/SegmentControl.svelte +120 -0
  92. package/dist/ui/SegmentControl/SegmentControl.svelte.d.ts +18 -0
  93. package/dist/ui/SegmentControl/index.d.ts +1 -0
  94. package/dist/ui/SegmentControl/index.js +1 -0
  95. package/dist/ui/Stack/Stack.docs.svelte +63 -0
  96. package/dist/ui/Stack/Stack.docs.svelte.d.ts +26 -0
  97. package/dist/ui/Stack/Stack.svelte +45 -0
  98. package/dist/ui/Stack/Stack.svelte.d.ts +19 -0
  99. package/dist/ui/Stack/index.d.ts +1 -0
  100. package/dist/ui/Stack/index.js +1 -0
  101. package/dist/ui/Table/Body.svelte +17 -0
  102. package/dist/ui/Table/Body.svelte.d.ts +11 -0
  103. package/dist/ui/Table/Caption.svelte +17 -0
  104. package/dist/ui/Table/Caption.svelte.d.ts +11 -0
  105. package/dist/ui/Table/Cell.svelte +24 -0
  106. package/dist/ui/Table/Cell.svelte.d.ts +15 -0
  107. package/dist/ui/Table/Foot.svelte +17 -0
  108. package/dist/ui/Table/Foot.svelte.d.ts +11 -0
  109. package/dist/ui/Table/Head.svelte +17 -0
  110. package/dist/ui/Table/Head.svelte.d.ts +11 -0
  111. package/dist/ui/Table/Header.svelte +27 -0
  112. package/dist/ui/Table/Header.svelte.d.ts +17 -0
  113. package/dist/ui/Table/Row.svelte +19 -0
  114. package/dist/ui/Table/Row.svelte.d.ts +13 -0
  115. package/dist/ui/Table/Table.docs.svelte +197 -0
  116. package/dist/ui/Table/Table.docs.svelte.d.ts +28 -0
  117. package/dist/ui/Table/Table.svelte +140 -0
  118. package/dist/ui/Table/Table.svelte.d.ts +27 -0
  119. package/dist/ui/Table/index.js +10 -0
  120. package/dist/ui/css/colors.css +377 -0
  121. package/dist/ui/css/global.css +10 -0
  122. package/dist/ui/index.d.ts +12 -0
  123. package/dist/ui/index.js +12 -0
  124. package/dist/virtual-sdocs.d.ts +20 -0
  125. package/dist/vite-plugin.d.ts +18 -0
  126. package/dist/vite-plugin.js +206 -0
  127. package/dist/vite.d.ts +2 -0
  128. package/dist/vite.js +2 -0
  129. package/package.json +76 -0
package/dist/docgen.js ADDED
@@ -0,0 +1,463 @@
1
+ /** Types that can't be serialized as control values — component uses its own defaults */
2
+ const NON_SERIALIZABLE_TYPES = ['bigint', 'symbol', 'object', 'null', 'undefined'];
3
+ // ============================================================================
4
+ // Parsing - Extract props from component source
5
+ // ============================================================================
6
+ /**
7
+ * Parse Svelte 5 component props from source code.
8
+ * Supports both interface Props {} and inline type annotations.
9
+ */
10
+ export function parseProps(source) {
11
+ const props = [];
12
+ const cssProps = [];
13
+ let description;
14
+ // Try to find Props interface with optional JSDoc comment before it
15
+ const interfaceMatch = source.match(/(?:\/\*\*\s*([\s\S]*?)\s*\*\/\s*)?interface\s+Props\s*\{([^}]+)\}/);
16
+ if (interfaceMatch) {
17
+ // Extract component description and @cssvar tags from JSDoc before interface
18
+ if (interfaceMatch[1]) {
19
+ const { description: desc, cssVars } = parseJsDocBlock(interfaceMatch[1]);
20
+ description = desc;
21
+ cssProps.push(...cssVars);
22
+ }
23
+ const interfaceBody = interfaceMatch[2];
24
+ props.push(...parseInterfaceBody(interfaceBody));
25
+ }
26
+ // Also check for inline type in $props destructuring
27
+ // let { foo = 'bar' }: { foo?: string } = $props();
28
+ const inlineMatch = source.match(/\$props\s*<\s*\{([^}]+)\}\s*>\s*\(\s*\)/s);
29
+ if (inlineMatch && props.length === 0) {
30
+ props.push(...parseInterfaceBody(inlineMatch[1]));
31
+ }
32
+ // Support JSDoc @type for JavaScript components
33
+ // /** @type {{ label?: string, children?: import('svelte').Snippet }} */
34
+ // let { label = 'default' } = $props();
35
+ if (props.length === 0) {
36
+ const jsdocTypeMatch = source.match(/\/\*\*\s*@type\s*\{\s*\{([^}]+)\}\s*\}\s*\*\/[\s\S]*?\$props\s*\(\s*\)/s);
37
+ if (jsdocTypeMatch) {
38
+ props.push(...parseJsDocTypeProps(jsdocTypeMatch[1]));
39
+ }
40
+ // For JS components, look for a separate JSDoc block with description/@cssvar
41
+ // This is the block before the @type block (or before $props)
42
+ if (!description) {
43
+ const jsDocBlocks = source.match(/\/\*\*\s*([\s\S]*?)\s*\*\//g);
44
+ if (jsDocBlocks) {
45
+ for (const block of jsDocBlocks) {
46
+ // Skip @type blocks
47
+ if (block.includes('@type'))
48
+ continue;
49
+ const content = block.replace(/^\/\*\*\s*/, '').replace(/\s*\*\/$/, '');
50
+ const { description: desc, cssVars } = parseJsDocBlock(content);
51
+ if (desc || cssVars.length > 0) {
52
+ description = desc;
53
+ cssProps.push(...cssVars);
54
+ break;
55
+ }
56
+ }
57
+ }
58
+ }
59
+ }
60
+ // Extract default values from destructuring
61
+ // let { foo = 'default', bar = 123 }: Props = $props();
62
+ const destructureMatch = source.match(/let\s*\{([\s\S]*?)\}[^=]*=\s*\$props\s*\(\s*\)/s);
63
+ if (destructureMatch) {
64
+ const destructure = destructureMatch[1];
65
+ const defaults = parseDestructureDefaults(destructure);
66
+ // Merge defaults into props
67
+ for (const prop of props) {
68
+ if (defaults[prop.name] !== undefined) {
69
+ prop.defaultValue = defaults[prop.name];
70
+ prop.required = false;
71
+ }
72
+ }
73
+ }
74
+ // Extract exported functions
75
+ const methods = parseExportedFunctions(source);
76
+ return { props, cssProps, methods, description };
77
+ }
78
+ function parseInterfaceBody(body) {
79
+ const props = [];
80
+ // Match lines like:
81
+ // propName?: type;
82
+ // propName: type;
83
+ // /** description */ propName?: type;
84
+ const propRegex = /(?:\/\*\*\s*([^*]*)\s*\*\/\s*)?(\w+)(\?)?\s*:\s*([^;]+);/g;
85
+ let match;
86
+ while ((match = propRegex.exec(body)) !== null) {
87
+ const [, description, name, optional, type] = match;
88
+ // Skip internal Svelte props only
89
+ if (name.startsWith('$$')) {
90
+ continue;
91
+ }
92
+ props.push({
93
+ name,
94
+ type: type.trim(),
95
+ required: !optional,
96
+ description: description?.trim()
97
+ });
98
+ }
99
+ return props;
100
+ }
101
+ /**
102
+ * Parse props from JSDoc @type annotation
103
+ * Format: { propName?: type, propName2: type2 }
104
+ */
105
+ function parseJsDocTypeProps(typeBody) {
106
+ const props = [];
107
+ // Match: propName?: type or propName: type
108
+ // Type can include import('svelte').Snippet or simple types
109
+ const propRegex = /(\w+)(\?)?\s*:\s*([^,}]+)/g;
110
+ let match;
111
+ while ((match = propRegex.exec(typeBody)) !== null) {
112
+ const [, name, optional, type] = match;
113
+ // Skip internal props
114
+ if (name.startsWith('$$')) {
115
+ continue;
116
+ }
117
+ props.push({
118
+ name,
119
+ type: type.trim(),
120
+ required: !optional
121
+ });
122
+ }
123
+ return props;
124
+ }
125
+ function parseDestructureDefaults(destructure) {
126
+ const defaults = {};
127
+ // Match: name = value
128
+ // Handle strings, numbers, booleans
129
+ const defaultRegex = /(\w+)\s*=\s*(['"`]?)([^,}]+?)\2(?:\s*,|\s*$)/g;
130
+ let match;
131
+ while ((match = defaultRegex.exec(destructure)) !== null) {
132
+ const [, name, quote, value] = match;
133
+ // Clean up the value
134
+ let cleanValue = value.trim();
135
+ // If it was quoted, add quotes back for string identification
136
+ if (quote) {
137
+ cleanValue = quote + cleanValue + quote;
138
+ }
139
+ defaults[name] = cleanValue;
140
+ }
141
+ return defaults;
142
+ }
143
+ /**
144
+ * Parse exported functions from component source code.
145
+ * Supports both TS and JS: export function name(params): returnType
146
+ * With optional JSDoc comment before.
147
+ */
148
+ function parseExportedFunctions(source) {
149
+ const methods = [];
150
+ // Match optional JSDoc followed by export function
151
+ const methodRegex = /(?:\/\*\*\s*([\s\S]*?)\s*\*\/\s*)?export\s+function\s+(\w+)\s*\(([^)]*)\)(?:\s*:\s*([\w<>[\]|&\s,]+))?\s*\{/g;
152
+ let match;
153
+ while ((match = methodRegex.exec(source)) !== null) {
154
+ const [, jsDoc, name, params, returnType] = match;
155
+ // Extract description from JSDoc (just the text, skip tags)
156
+ let description;
157
+ if (jsDoc) {
158
+ const lines = jsDoc.split('\n').map(line => line.replace(/^\s*\*\s?/, '').trim());
159
+ const descLines = lines.filter(l => l.length > 0 && !l.startsWith('@'));
160
+ description = descLines.length > 0 ? descLines.join(' ').trim() : undefined;
161
+ }
162
+ methods.push({
163
+ name,
164
+ params: params.trim() || undefined,
165
+ returnType: returnType?.trim() || 'void',
166
+ description
167
+ });
168
+ }
169
+ return methods;
170
+ }
171
+ /**
172
+ * Parse JSDoc block to extract description and @cssvar tags
173
+ * Format: @cssvar {type} --name - description (default: value)
174
+ */
175
+ function parseJsDocBlock(comment) {
176
+ const cssVars = [];
177
+ const descriptionLines = [];
178
+ const lines = comment.split('\n').map(line => line.replace(/^\s*\*\s?/, '').trim());
179
+ for (const line of lines) {
180
+ // Match @cssvar {type} --name - description (default: value)
181
+ // Type is optional: @cssvar --name - description
182
+ const cssvarMatch = line.match(/@cssvar\s+(?:\{(\w+)\}\s+)?(--[\w-]+)\s*(?:-\s*(.+))?/);
183
+ if (cssvarMatch) {
184
+ const [, type, name, rest] = cssvarMatch;
185
+ let description;
186
+ let defaultValue;
187
+ if (rest) {
188
+ // Extract default value from "(default: value)" at the end
189
+ const defaultMatch = rest.match(/^(.+?)\s*\(default:\s*([^)]+)\)\s*$/);
190
+ if (defaultMatch) {
191
+ description = defaultMatch[1].trim();
192
+ defaultValue = defaultMatch[2].trim();
193
+ }
194
+ else {
195
+ description = rest.trim();
196
+ }
197
+ }
198
+ cssVars.push({
199
+ name,
200
+ type: type,
201
+ description,
202
+ default: defaultValue
203
+ });
204
+ }
205
+ else if (!line.startsWith('@') && line.length > 0) {
206
+ // Regular description line (not a tag)
207
+ descriptionLines.push(line);
208
+ }
209
+ }
210
+ return {
211
+ description: descriptionLines.length > 0 ? descriptionLines.join(' ').trim() : undefined,
212
+ cssVars
213
+ };
214
+ }
215
+ // ============================================================================
216
+ // Conversion - Transform parsed props to runtime types
217
+ // ============================================================================
218
+ /**
219
+ * Convert extracted component props to argTypes for controls
220
+ */
221
+ export function propsToArgTypes(docgen) {
222
+ if (!docgen?.props)
223
+ return {};
224
+ const argTypes = {};
225
+ for (const prop of docgen.props) {
226
+ // Skip internal Svelte props
227
+ if (prop.name.startsWith('$$')) {
228
+ continue;
229
+ }
230
+ const argType = inferArgType(prop);
231
+ argTypes[prop.name] = argType;
232
+ }
233
+ return argTypes;
234
+ }
235
+ /**
236
+ * Extract default args from component docgen
237
+ */
238
+ export function propsToDefaultArgs(docgen) {
239
+ if (!docgen?.props)
240
+ return {};
241
+ const args = {};
242
+ for (const prop of docgen.props) {
243
+ if (prop.name === 'children' || prop.name === 'class' || prop.name.startsWith('$$')) {
244
+ continue;
245
+ }
246
+ // Skip types that can't be represented as control values
247
+ const typeLower = prop.type.toLowerCase();
248
+ if (NON_SERIALIZABLE_TYPES.includes(typeLower) || typeLower.includes('=>') || typeLower.includes('function')) {
249
+ continue;
250
+ }
251
+ if (prop.defaultValue !== undefined) {
252
+ args[prop.name] = parseDefaultValue(prop.defaultValue, prop.type);
253
+ }
254
+ }
255
+ return args;
256
+ }
257
+ /**
258
+ * Convert extracted CSS props to CssProps config
259
+ */
260
+ export function cssVarsToCssProps(docgen) {
261
+ if (!docgen?.cssProps)
262
+ return {};
263
+ const cssProps = {};
264
+ for (const cssProp of docgen.cssProps) {
265
+ cssProps[cssProp.name] = {
266
+ description: cssProp.description,
267
+ default: cssProp.default,
268
+ control: cssProp.type ?? 'color' // default to color picker
269
+ };
270
+ }
271
+ return cssProps;
272
+ }
273
+ /**
274
+ * Convert extracted methods to MethodType[] for the UI
275
+ */
276
+ export function docgenToMethods(docgen) {
277
+ if (!docgen?.methods)
278
+ return [];
279
+ return docgen.methods.map((m) => ({
280
+ name: m.name,
281
+ params: m.params,
282
+ returnType: m.returnType,
283
+ description: m.description
284
+ }));
285
+ }
286
+ // ============================================================================
287
+ // Helpers
288
+ // ============================================================================
289
+ function inferArgType(prop) {
290
+ const type = prop.type.toLowerCase();
291
+ const originalType = prop.type;
292
+ const defaultValue = prop.defaultValue;
293
+ // Snippet types - show in table but not controllable
294
+ // Normalize display: import('svelte').Snippet -> Snippet
295
+ if (type.includes('snippet')) {
296
+ const snippetType = originalType.includes('import(') ? 'Snippet' : originalType;
297
+ return {
298
+ control: false,
299
+ type: { name: snippetType },
300
+ description: prop.description,
301
+ required: prop.required,
302
+ defaultValue
303
+ };
304
+ }
305
+ // Function types (callbacks, event handlers) - show but not controllable
306
+ if (type.includes('=>') || type.includes('function')) {
307
+ return {
308
+ control: false,
309
+ type: { name: 'Function' },
310
+ description: prop.description,
311
+ required: prop.required,
312
+ defaultValue
313
+ };
314
+ }
315
+ // Non-serializable types - show but not controllable
316
+ if (NON_SERIALIZABLE_TYPES.includes(type)) {
317
+ return {
318
+ control: false,
319
+ type: { name: originalType },
320
+ description: prop.description,
321
+ required: prop.required,
322
+ defaultValue
323
+ };
324
+ }
325
+ // children prop - show but not controllable
326
+ if (prop.name === 'children') {
327
+ return {
328
+ control: false,
329
+ type: { name: originalType },
330
+ description: prop.description,
331
+ required: prop.required,
332
+ defaultValue
333
+ };
334
+ }
335
+ // class prop - text input
336
+ if (prop.name === 'class') {
337
+ return {
338
+ control: 'text',
339
+ type: { name: 'string' },
340
+ description: prop.description,
341
+ required: prop.required,
342
+ defaultValue
343
+ };
344
+ }
345
+ // Boolean
346
+ if (type === 'boolean' || type === 'bool') {
347
+ return { control: 'boolean', type: { name: 'boolean' }, description: prop.description, required: prop.required, defaultValue };
348
+ }
349
+ // Number
350
+ if (type === 'number' || type === 'int' || type === 'float') {
351
+ return { control: 'number', type: { name: 'number' }, description: prop.description, required: prop.required, defaultValue };
352
+ }
353
+ // Union types (string literals) -> select or radio
354
+ const unionMatch = prop.type.match(/^['"]([^'"]+)['"](?:\s*\|\s*['"]([^'"]+)['"])+$/);
355
+ if (unionMatch) {
356
+ const options = extractUnionOptions(prop.type);
357
+ if (options.length <= 4) {
358
+ return {
359
+ control: { type: 'radio', options },
360
+ type: { name: originalType },
361
+ description: prop.description,
362
+ required: prop.required,
363
+ defaultValue
364
+ };
365
+ }
366
+ return {
367
+ control: { type: 'select', options },
368
+ type: { name: originalType },
369
+ description: prop.description,
370
+ required: prop.required,
371
+ defaultValue
372
+ };
373
+ }
374
+ // Explicit union check with pipe (string literals)
375
+ if (prop.type.includes('|') && prop.type.includes("'")) {
376
+ const options = extractUnionOptions(prop.type);
377
+ if (options.length > 0) {
378
+ if (options.length <= 4) {
379
+ return {
380
+ control: { type: 'radio', options },
381
+ type: { name: originalType },
382
+ description: prop.description,
383
+ required: prop.required,
384
+ defaultValue
385
+ };
386
+ }
387
+ return {
388
+ control: { type: 'select', options },
389
+ type: { name: originalType },
390
+ description: prop.description,
391
+ required: prop.required,
392
+ defaultValue
393
+ };
394
+ }
395
+ }
396
+ // Number literal unions (e.g., 20 | 24 | 28 | 32 | 40)
397
+ if (prop.type.includes('|')) {
398
+ const numberOptions = extractNumberUnionOptions(prop.type);
399
+ if (numberOptions.length > 0) {
400
+ if (numberOptions.length <= 5) {
401
+ return {
402
+ control: { type: 'radio', options: numberOptions },
403
+ type: { name: originalType },
404
+ description: prop.description,
405
+ required: prop.required,
406
+ defaultValue
407
+ };
408
+ }
409
+ return {
410
+ control: { type: 'select', options: numberOptions },
411
+ type: { name: originalType },
412
+ description: prop.description,
413
+ required: prop.required,
414
+ defaultValue
415
+ };
416
+ }
417
+ }
418
+ // String (fallback for most text types)
419
+ if (type === 'string' || type.includes('string')) {
420
+ return { control: 'text', type: { name: 'string' }, description: prop.description, required: prop.required, defaultValue };
421
+ }
422
+ // Default to text for unknown types
423
+ return { control: 'text', type: { name: originalType }, description: prop.description, required: prop.required, defaultValue };
424
+ }
425
+ function extractUnionOptions(typeStr) {
426
+ const matches = typeStr.match(/['"]([^'"]+)['"]/g);
427
+ if (!matches)
428
+ return [];
429
+ return matches.map((m) => m.replace(/['"]/g, ''));
430
+ }
431
+ function extractNumberUnionOptions(typeStr) {
432
+ // Match number literals in unions like "20 | 24 | 28 | 32 | 40"
433
+ const parts = typeStr.split('|').map((s) => s.trim());
434
+ const numbers = [];
435
+ for (const part of parts) {
436
+ if (/^\d+$/.test(part)) {
437
+ numbers.push(part);
438
+ }
439
+ }
440
+ // Only return if all parts are numbers
441
+ return numbers.length === parts.length ? numbers : [];
442
+ }
443
+ function parseDefaultValue(value, type) {
444
+ // Handle boolean
445
+ if (value === 'true')
446
+ return true;
447
+ if (value === 'false')
448
+ return false;
449
+ // Handle number (including number literal unions like "20 | 24 | 32")
450
+ const num = Number(value);
451
+ if (!isNaN(num)) {
452
+ const isNumberType = type.toLowerCase().includes('number');
453
+ const isNumberUnion = extractNumberUnionOptions(type).length > 0;
454
+ if (isNumberType || isNumberUnion) {
455
+ return num;
456
+ }
457
+ }
458
+ // Handle string with quotes
459
+ if (value.startsWith("'") || value.startsWith('"')) {
460
+ return value.slice(1, -1);
461
+ }
462
+ return value;
463
+ }
@@ -0,0 +1,2 @@
1
+ export { default as Sdocs } from './Sdocs.svelte';
2
+ export type { ControlType, ArgType, ArgTypes, CssPropType, CssProps, DocMeta, Example, DocFile, SdocsProps } from './types.js';
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ // Main component
2
+ export { default as Sdocs } from './Sdocs.svelte';
3
+ // Note: Vite plugin is exported separately from 'sdocs/vite'
4
+ // to avoid bundling Node.js code into client builds
@@ -0,0 +1,58 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ /**
5
+ * A container for previewing components with consistent styling.
6
+ */
7
+ interface Props {
8
+ /** Padding size */
9
+ padding?: 's' | 'm' | 'l';
10
+ /** Whether to center content */
11
+ centered?: boolean;
12
+ /** CSS custom properties to apply */
13
+ cssVars?: Record<string, string>;
14
+ /** Children content */
15
+ children?: Snippet;
16
+ }
17
+
18
+ let { padding = 'm', centered = true, cssVars = {}, children }: Props = $props();
19
+
20
+ // Build style string from CSS vars
21
+ let styleString = $derived(
22
+ Object.entries(cssVars)
23
+ .filter(([_, v]) => v)
24
+ .map(([k, v]) => `${k}: ${v}`)
25
+ .join('; '),
26
+ );
27
+ </script>
28
+
29
+ <div class="ComponentPreview {padding}" class:centered style={styleString || undefined}>
30
+ {#if children}
31
+ {@render children()}
32
+ {/if}
33
+ </div>
34
+
35
+ <style>
36
+ .ComponentPreview {
37
+ background: var(--color-bg-elevated);
38
+ }
39
+
40
+ .centered {
41
+ display: flex;
42
+ /* align-items: center; */
43
+ /* justify-content: center; */
44
+ }
45
+
46
+ /* Padding sizes */
47
+ .s {
48
+ padding: 16px;
49
+ }
50
+
51
+ .m {
52
+ padding: 32px;
53
+ }
54
+
55
+ .l {
56
+ padding: 40px;
57
+ }
58
+ </style>
@@ -0,0 +1,17 @@
1
+ import type { Snippet } from 'svelte';
2
+ /**
3
+ * A container for previewing components with consistent styling.
4
+ */
5
+ interface Props {
6
+ /** Padding size */
7
+ padding?: 's' | 'm' | 'l';
8
+ /** Whether to center content */
9
+ centered?: boolean;
10
+ /** CSS custom properties to apply */
11
+ cssVars?: Record<string, string>;
12
+ /** Children content */
13
+ children?: Snippet;
14
+ }
15
+ declare const ComponentPreview: import("svelte").Component<Props, {}, "">;
16
+ type ComponentPreview = ReturnType<typeof ComponentPreview>;
17
+ export default ComponentPreview;