@fragments-sdk/cli 0.7.3 → 0.7.5
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/LICENSE +1 -4
- package/README.md +2 -0
- package/dist/bin.js +39 -16
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-D34Q6A7S.js → chunk-AWYCDRPG.js} +8 -2
- package/dist/chunk-AWYCDRPG.js.map +1 -0
- package/dist/{chunk-R2YH7NLN.js → chunk-CR3XHBGM.js} +3 -3
- package/dist/{chunk-QPY4DUFB.js → chunk-EFQ7SIBX.js} +583 -108
- package/dist/chunk-EFQ7SIBX.js.map +1 -0
- package/dist/{chunk-UXLGIGSX.js → chunk-GIC3I2KZ.js} +2 -2
- package/dist/{chunk-R6IZZSE7.js → chunk-JZNATKQA.js} +9 -3
- package/dist/chunk-JZNATKQA.js.map +1 -0
- package/dist/{chunk-P33AKQJW.js → chunk-SFWZ4K7C.js} +8 -2
- package/dist/{chunk-P33AKQJW.js.map → chunk-SFWZ4K7C.js.map} +1 -1
- package/dist/{core-3NMNCLFW.js → core-T7BDYEGO.js} +3 -3
- package/dist/{discovery-AKGA6CJD.js → discovery-Z4RDDFVR.js} +2 -2
- package/dist/{generate-JAUEHKK7.js → generate-C2DKFCFJ.js} +5 -5
- package/dist/index.d.ts +28 -2
- package/dist/index.js +9 -7
- package/dist/index.js.map +1 -1
- package/dist/{init-DZQOT54X.js → init-O3FCHEPN.js} +26 -8
- package/dist/init-O3FCHEPN.js.map +1 -0
- package/dist/mcp-bin.js +3 -3
- package/dist/{scan-OJRCVKK2.js → scan-IYTZDUKG.js} +6 -6
- package/dist/{service-CFFBHW4X.js → service-VA6XKADO.js} +3 -3
- package/dist/{static-viewer-VA2JXSCX.js → static-viewer-5N42MBDR.js} +3 -3
- package/dist/{test-O7DZNKDC.js → test-OMMDWL2W.js} +4 -4
- package/dist/{tokens-N7THFD6J.js → tokens-6VJAHFIG.js} +5 -5
- package/dist/{viewer-QTR7QJMM.js → viewer-IVP5XC7U.js} +37 -17
- package/dist/viewer-IVP5XC7U.js.map +1 -0
- package/package.json +8 -2
- package/src/bin.ts +4 -0
- package/src/commands/add.ts +6 -0
- package/src/commands/init.ts +24 -4
- package/src/commands/validate.ts +24 -2
- package/src/core/config.ts +6 -0
- package/src/core/discovery.ts +7 -1
- package/src/core/index.ts +1 -0
- package/src/core/schema.ts +6 -0
- package/src/core/types.ts +21 -0
- package/src/index.ts +2 -1
- package/src/migrate/detect.ts +4 -0
- package/src/service/snippet-validation.test.ts +209 -0
- package/src/service/snippet-validation.ts +635 -0
- package/src/validators.ts +53 -5
- package/src/viewer/__tests__/viewer-integration.test.ts +8 -0
- package/src/viewer/components/App.tsx +63 -2
- package/src/viewer/components/CodePanel.naming.test.tsx +60 -0
- package/src/viewer/components/CodePanel.tsx +76 -468
- package/src/viewer/components/Layout.tsx +2 -2
- package/src/viewer/components/LeftSidebar.tsx +35 -77
- package/src/viewer/preview-frame.html +1 -1
- package/src/viewer/styles/globals.css +2 -1
- package/src/viewer/utils/a11y-fixes.ts +24 -9
- package/src/viewer/vite-plugin.ts +27 -2
- package/dist/chunk-D34Q6A7S.js.map +0 -1
- package/dist/chunk-QPY4DUFB.js.map +0 -1
- package/dist/chunk-R6IZZSE7.js.map +0 -1
- package/dist/init-DZQOT54X.js.map +0 -1
- package/dist/viewer-QTR7QJMM.js.map +0 -1
- /package/dist/{chunk-R2YH7NLN.js.map → chunk-CR3XHBGM.js.map} +0 -0
- /package/dist/{chunk-UXLGIGSX.js.map → chunk-GIC3I2KZ.js.map} +0 -0
- /package/dist/{core-3NMNCLFW.js.map → core-T7BDYEGO.js.map} +0 -0
- /package/dist/{discovery-AKGA6CJD.js.map → discovery-Z4RDDFVR.js.map} +0 -0
- /package/dist/{generate-JAUEHKK7.js.map → generate-C2DKFCFJ.js.map} +0 -0
- /package/dist/{scan-OJRCVKK2.js.map → scan-IYTZDUKG.js.map} +0 -0
- /package/dist/{service-CFFBHW4X.js.map → service-VA6XKADO.js.map} +0 -0
- /package/dist/{static-viewer-VA2JXSCX.js.map → static-viewer-5N42MBDR.js.map} +0 -0
- /package/dist/{test-O7DZNKDC.js.map → test-OMMDWL2W.js.map} +0 -0
- /package/dist/{tokens-N7THFD6J.js.map → tokens-6VJAHFIG.js.map} +0 -0
|
@@ -109,6 +109,14 @@ describe("virtual module @fragments/core import", () => {
|
|
|
109
109
|
|
|
110
110
|
expect(content).toContain('"@fragments/viewer": viewerRoot');
|
|
111
111
|
});
|
|
112
|
+
|
|
113
|
+
it("vite-plugin merges authored variant code from metadata fragments", async () => {
|
|
114
|
+
const pluginPath = resolve(viewerDir, "vite-plugin.ts");
|
|
115
|
+
const content = await readFile(pluginPath, "utf-8");
|
|
116
|
+
|
|
117
|
+
expect(content).toContain("if (metaVariant.code && !fragmentVariant.code)");
|
|
118
|
+
expect(content).toContain("fragmentVariant.code = metaVariant.code;");
|
|
119
|
+
});
|
|
112
120
|
});
|
|
113
121
|
|
|
114
122
|
describe("viewer HTML templates", () => {
|
|
@@ -28,11 +28,19 @@ import { useAllFigmaUrls } from "./FigmaEmbed.js";
|
|
|
28
28
|
import { ActionCapture } from "./ActionCapture.js";
|
|
29
29
|
|
|
30
30
|
// Fragments UI
|
|
31
|
-
import { Header, Stack, Text, Separator, Tooltip, Button, EmptyState, Box, Alert, ScrollArea, Input } from "@fragments/ui";
|
|
31
|
+
import { Header, Stack, Text, Separator, Tooltip, Button, EmptyState, Box, Alert, ScrollArea, Input, ThemeToggle } from "@fragments/ui";
|
|
32
32
|
|
|
33
33
|
// Icons
|
|
34
34
|
import { EmptyIcon, ExternalLinkIcon, FigmaIcon, CompareIcon, CheckIcon, LinkIcon, GridIcon, DevicesIcon } from "./Icons.js";
|
|
35
35
|
|
|
36
|
+
function GitHubIcon() {
|
|
37
|
+
return (
|
|
38
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
|
39
|
+
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
|
|
40
|
+
</svg>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
36
44
|
// Hooks
|
|
37
45
|
import { useAppState } from "../hooks/useAppState.js";
|
|
38
46
|
import { useViewSettings } from "../hooks/useViewSettings.js";
|
|
@@ -475,13 +483,14 @@ function HeaderSearch({ value, onChange, inputRef }: HeaderSearchProps) {
|
|
|
475
483
|
aria-label="Search components"
|
|
476
484
|
size="sm"
|
|
477
485
|
shortcut="⌘K"
|
|
478
|
-
style={{ width: '
|
|
486
|
+
style={{ width: '240px' }}
|
|
479
487
|
/>
|
|
480
488
|
</Header.Search>
|
|
481
489
|
);
|
|
482
490
|
}
|
|
483
491
|
|
|
484
492
|
function ViewerHeader({ showHealth, searchQuery, onSearchChange, searchInputRef }: ViewerHeaderProps) {
|
|
493
|
+
const { setTheme, resolvedTheme } = useTheme();
|
|
485
494
|
return (
|
|
486
495
|
<Header aria-label="Fragments viewer header">
|
|
487
496
|
<Header.Trigger />
|
|
@@ -493,6 +502,32 @@ function ViewerHeader({ showHealth, searchQuery, onSearchChange, searchInputRef
|
|
|
493
502
|
</Header.Brand>
|
|
494
503
|
<HeaderSearch value={searchQuery} onChange={onSearchChange} inputRef={searchInputRef} />
|
|
495
504
|
<Header.Spacer />
|
|
505
|
+
<Header.Actions>
|
|
506
|
+
<ThemeToggle
|
|
507
|
+
size="sm"
|
|
508
|
+
value={resolvedTheme}
|
|
509
|
+
onValueChange={(value) => setTheme(value)}
|
|
510
|
+
aria-label={`Theme: ${resolvedTheme}`}
|
|
511
|
+
/>
|
|
512
|
+
<a
|
|
513
|
+
href="https://github.com/ConanMcN/fragments"
|
|
514
|
+
target="_blank"
|
|
515
|
+
rel="noopener noreferrer"
|
|
516
|
+
style={{
|
|
517
|
+
display: 'flex',
|
|
518
|
+
alignItems: 'center',
|
|
519
|
+
justifyContent: 'center',
|
|
520
|
+
width: '32px',
|
|
521
|
+
height: '32px',
|
|
522
|
+
borderRadius: 'var(--radius-md, 6px)',
|
|
523
|
+
color: 'var(--text-secondary)',
|
|
524
|
+
transition: 'background-color 150ms ease, color 150ms ease',
|
|
525
|
+
}}
|
|
526
|
+
aria-label="View on GitHub"
|
|
527
|
+
>
|
|
528
|
+
<GitHubIcon />
|
|
529
|
+
</a>
|
|
530
|
+
</Header.Actions>
|
|
496
531
|
</Header>
|
|
497
532
|
);
|
|
498
533
|
}
|
|
@@ -510,6 +545,7 @@ function TopToolbar({
|
|
|
510
545
|
onSearchChange,
|
|
511
546
|
searchInputRef,
|
|
512
547
|
}: TopToolbarProps) {
|
|
548
|
+
const { setTheme, resolvedTheme } = useTheme();
|
|
513
549
|
return (
|
|
514
550
|
<Header aria-label="Component preview toolbar">
|
|
515
551
|
<Header.Trigger />
|
|
@@ -595,6 +631,31 @@ function TopToolbar({
|
|
|
595
631
|
</Tooltip>
|
|
596
632
|
</>
|
|
597
633
|
)}
|
|
634
|
+
<Separator orientation="vertical" style={{ height: '16px' }} />
|
|
635
|
+
<ThemeToggle
|
|
636
|
+
size="sm"
|
|
637
|
+
value={resolvedTheme}
|
|
638
|
+
onValueChange={(value) => setTheme(value)}
|
|
639
|
+
aria-label={`Theme: ${resolvedTheme}`}
|
|
640
|
+
/>
|
|
641
|
+
<a
|
|
642
|
+
href="https://github.com/ConanMcN/fragments"
|
|
643
|
+
target="_blank"
|
|
644
|
+
rel="noopener noreferrer"
|
|
645
|
+
style={{
|
|
646
|
+
display: 'flex',
|
|
647
|
+
alignItems: 'center',
|
|
648
|
+
justifyContent: 'center',
|
|
649
|
+
width: '32px',
|
|
650
|
+
height: '32px',
|
|
651
|
+
borderRadius: 'var(--radius-md, 6px)',
|
|
652
|
+
color: 'var(--text-secondary)',
|
|
653
|
+
transition: 'background-color 150ms ease, color 150ms ease',
|
|
654
|
+
}}
|
|
655
|
+
aria-label="View on GitHub"
|
|
656
|
+
>
|
|
657
|
+
<GitHubIcon />
|
|
658
|
+
</a>
|
|
598
659
|
</Header.Actions>
|
|
599
660
|
</Header>
|
|
600
661
|
);
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import type { FragmentVariant } from '../../core/index.js';
|
|
3
|
+
|
|
4
|
+
vi.mock('@fragments/ui', () => ({
|
|
5
|
+
CodeBlock: () => null,
|
|
6
|
+
}));
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
__buildFallbackSnippetForTest,
|
|
10
|
+
__resolveDisplayedCodeForTest,
|
|
11
|
+
} = await import('./CodePanel.js');
|
|
12
|
+
|
|
13
|
+
function makeVariant(partial: Partial<FragmentVariant>): FragmentVariant {
|
|
14
|
+
return {
|
|
15
|
+
name: 'Default',
|
|
16
|
+
description: 'Default variant',
|
|
17
|
+
render: () => null,
|
|
18
|
+
...partial,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe('CodePanel authored code pipeline', () => {
|
|
23
|
+
it('prefers authored variant.code as-is', () => {
|
|
24
|
+
const variant = makeVariant({
|
|
25
|
+
code: `import { Button } from '@fragments-sdk/ui';\n\n<Button>Save</Button>`,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const resolved = __resolveDisplayedCodeForTest('Button', variant);
|
|
29
|
+
|
|
30
|
+
expect(resolved).toBe(`import { Button } from '@fragments-sdk/ui';\n\n<Button>Save</Button>`);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('falls back to generated placeholder when variant.code is missing', () => {
|
|
34
|
+
const variant = makeVariant({
|
|
35
|
+
name: 'Primary',
|
|
36
|
+
args: {
|
|
37
|
+
variant: 'primary',
|
|
38
|
+
disabled: false,
|
|
39
|
+
children: 'Save Changes',
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const code = __buildFallbackSnippetForTest('Button', variant);
|
|
44
|
+
|
|
45
|
+
expect(code).toContain("import { Button } from '@/components/Button';");
|
|
46
|
+
expect(code).toContain('TODO: Add explicit `code` for variant "Primary"');
|
|
47
|
+
expect(code).toContain("<Button variant='primary' disabled={false}>Save Changes</Button>");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('does not synthesize runtime JSX output', () => {
|
|
51
|
+
const variant = makeVariant({
|
|
52
|
+
code: '<Button icon={/* @__PURE__ */ jsxDEV(GearIcon, {}, void 0)} />',
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const resolved = __resolveDisplayedCodeForTest('Button', variant);
|
|
56
|
+
|
|
57
|
+
// CodePanel now treats authored code as source-of-truth only.
|
|
58
|
+
expect(resolved).toContain('jsxDEV');
|
|
59
|
+
});
|
|
60
|
+
});
|