@fragments-sdk/cli 0.7.9 → 0.7.11
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/bin.js +13 -13
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-CWKQQR6C.js → chunk-57OW43NL.js} +3 -3
- package/dist/chunk-57OW43NL.js.map +1 -0
- package/dist/{chunk-AA6CAHCZ.js → chunk-7CRC46HV.js} +2 -2
- package/dist/chunk-7CRC46HV.js.map +1 -0
- package/dist/{chunk-3JPJTU25.js → chunk-CRTN6BIW.js} +5 -5
- package/dist/chunk-CRTN6BIW.js.map +1 -0
- package/dist/{chunk-LHIIBI6F.js → chunk-M42XIHPV.js} +2 -2
- package/dist/{chunk-2EFVPE5Q.js → chunk-TQOGBAOZ.js} +2 -2
- package/dist/chunk-TQOGBAOZ.js.map +1 -0
- package/dist/core/index.d.ts +1944 -0
- package/dist/{core-YAPWXDZW.js → core/index.js} +5 -5
- package/dist/defineFragment-C6PFzZyo.d.ts +656 -0
- package/dist/{generate-LEBVZCCH.js → generate-ZPERYZLF.js} +4 -4
- package/dist/index.d.ts +4 -159
- package/dist/index.js +9 -4
- package/dist/index.js.map +1 -1
- package/dist/{init-4VXL3Q6N.js → init-GID2DXB3.js} +69 -7
- package/dist/init-GID2DXB3.js.map +1 -0
- package/dist/mcp-bin.js +3 -3
- package/dist/{scan-3NYSRF6G.js → scan-BSMLGBX4.js} +5 -5
- package/dist/{service-HL6TMP3B.js → service-QACVPR37.js} +3 -3
- package/dist/{static-viewer-KLD24I4R.js → static-viewer-2RQD5QLR.js} +3 -3
- package/dist/{test-Y7YZOJLE.js → test-36UELXTE.js} +3 -3
- package/dist/{tokens-M4FCJKBK.js → tokens-A3BZIQPB.js} +4 -4
- package/dist/{viewer-ZWQQ74FV.js → viewer-CNLZQUFO.js} +156 -32
- package/dist/viewer-CNLZQUFO.js.map +1 -0
- package/package.json +8 -2
- package/src/commands/add.ts +1 -1
- package/src/commands/init.ts +84 -4
- package/src/core/defineFragment.ts +1 -1
- package/src/core/figma.ts +1 -1
- package/src/core/index.ts +2 -2
- package/src/core/loader.ts +3 -3
- package/src/core/schema.ts +1 -1
- package/src/index.ts +6 -0
- package/src/migrate/converter.ts +1 -1
- package/src/service/snippet-validation.test.ts +5 -5
- package/src/service/snippet-validation.ts +0 -1
- package/src/viewer/__tests__/viewer-integration.test.ts +16 -23
- package/src/viewer/components/AccessibilityPanel.tsx +1 -1
- package/src/viewer/components/ActionsPanel.tsx +1 -1
- package/src/viewer/components/App.tsx +563 -166
- package/src/viewer/components/BottomPanel.tsx +1 -1
- package/src/viewer/components/CodePanel.naming.test.tsx +1 -2
- package/src/viewer/components/CodePanel.tsx +1 -2
- package/src/viewer/components/CommandPalette.tsx +1 -1
- package/src/viewer/components/ComponentGraph.tsx +1 -1
- package/src/viewer/components/ComponentHeader.tsx +1 -1
- package/src/viewer/components/ContractPanel.tsx +1 -1
- package/src/viewer/components/ErrorBoundary.tsx +1 -1
- package/src/viewer/components/HealthDashboard.tsx +1 -1
- package/src/viewer/components/HmrStatusIndicator.tsx +1 -1
- package/src/viewer/components/InteractionsPanel.tsx +1 -1
- package/src/viewer/components/IsolatedRender.tsx +1 -1
- package/src/viewer/components/KeyboardShortcutsHelp.tsx +1 -1
- package/src/viewer/components/LandingPage.tsx +1 -1
- package/src/viewer/components/Layout.tsx +16 -13
- package/src/viewer/components/LeftSidebar.tsx +105 -18
- package/src/viewer/components/MultiViewportPreview.tsx +1 -1
- package/src/viewer/components/PreviewArea.tsx +22 -13
- package/src/viewer/components/PreviewFrameHost.tsx +0 -4
- package/src/viewer/components/PreviewToolbar.tsx +1 -1
- package/src/viewer/components/PropsEditor.tsx +1 -1
- package/src/viewer/components/PropsTable.tsx +1 -1
- package/src/viewer/components/RightSidebar.tsx +1 -1
- package/src/viewer/components/ScreenshotButton.tsx +1 -1
- package/src/viewer/components/SkeletonLoader.tsx +1 -1
- package/src/viewer/components/Toast.tsx +2 -2
- package/src/viewer/components/TokenStylePanel.tsx +1 -1
- package/src/viewer/components/VariantMatrix.tsx +1 -1
- package/src/viewer/components/VariantTabs.tsx +1 -1
- package/src/viewer/components/ViewportSelector.tsx +1 -1
- package/src/viewer/constants/ui.ts +14 -0
- package/src/viewer/entry.tsx +3 -4
- package/src/viewer/hooks/useKeyboardShortcuts.ts +65 -17
- package/src/viewer/hooks/useViewSettings.ts +1 -2
- package/src/viewer/index.ts +1 -1
- package/src/viewer/preview-frame.html +6 -9
- package/src/viewer/server.ts +106 -9
- package/src/viewer/styles/globals.css +12 -51
- package/src/viewer/vendor/shared/src/DocsHeaderBar.tsx +110 -0
- package/src/viewer/vendor/shared/src/DocsPageAsideHost.tsx +89 -0
- package/src/viewer/vendor/shared/src/DocsPageShell.tsx +119 -0
- package/src/viewer/vendor/shared/src/DocsSearchCommand.tsx +134 -0
- package/src/viewer/vendor/shared/src/DocsSidebarNav.tsx +66 -0
- package/src/viewer/vendor/shared/src/docs-layout.scss +28 -0
- package/src/viewer/vendor/shared/src/docs-layout.scss.d.ts +2 -0
- package/src/viewer/vendor/shared/src/index.ts +26 -0
- package/src/viewer/vendor/shared/src/types.ts +41 -0
- package/src/viewer/vite-plugin.ts +70 -9
- package/dist/chunk-2EFVPE5Q.js.map +0 -1
- package/dist/chunk-3JPJTU25.js.map +0 -1
- package/dist/chunk-AA6CAHCZ.js.map +0 -1
- package/dist/chunk-CWKQQR6C.js.map +0 -1
- package/dist/init-4VXL3Q6N.js.map +0 -1
- package/dist/viewer-ZWQQ74FV.js.map +0 -1
- /package/dist/{chunk-LHIIBI6F.js.map → chunk-M42XIHPV.js.map} +0 -0
- /package/dist/{core-YAPWXDZW.js.map → core/index.js.map} +0 -0
- /package/dist/{generate-LEBVZCCH.js.map → generate-ZPERYZLF.js.map} +0 -0
- /package/dist/{scan-3NYSRF6G.js.map → scan-BSMLGBX4.js.map} +0 -0
- /package/dist/{service-HL6TMP3B.js.map → service-QACVPR37.js.map} +0 -0
- /package/dist/{static-viewer-KLD24I4R.js.map → static-viewer-2RQD5QLR.js.map} +0 -0
- /package/dist/{test-Y7YZOJLE.js.map → test-36UELXTE.js.map} +0 -0
- /package/dist/{tokens-M4FCJKBK.js.map → tokens-A3BZIQPB.js.map} +0 -0
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { memo, useCallback } from 'react';
|
|
7
7
|
import type { FragmentDefinition, FragmentVariant } from '../../core/index.js';
|
|
8
|
-
import { Tabs, Badge } from '@fragments/ui';
|
|
8
|
+
import { Tabs, Badge } from '@fragments-sdk/ui';
|
|
9
9
|
import { ResizablePanel } from './ResizablePanel.js';
|
|
10
10
|
import { CodePanel } from './CodePanel.js';
|
|
11
11
|
import { TokenStylePanel } from './TokenStylePanel.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from 'vitest';
|
|
2
2
|
import type { FragmentVariant } from '../../core/index.js';
|
|
3
3
|
|
|
4
|
-
vi.mock('@fragments/ui', () => ({
|
|
4
|
+
vi.mock('@fragments-sdk/ui', () => ({
|
|
5
5
|
CodeBlock: () => null,
|
|
6
6
|
}));
|
|
7
7
|
|
|
@@ -43,7 +43,6 @@ describe('CodePanel authored code pipeline', () => {
|
|
|
43
43
|
const code = __buildFallbackSnippetForTest('Button', variant);
|
|
44
44
|
|
|
45
45
|
expect(code).toContain("import { Button } from '@/components/Button';");
|
|
46
|
-
expect(code).toContain('TODO: Add explicit `code` for variant "Primary"');
|
|
47
46
|
expect(code).toContain("<Button variant='primary' disabled={false}>Save Changes</Button>");
|
|
48
47
|
});
|
|
49
48
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
|
-
import { CodeBlock } from '@fragments/ui';
|
|
2
|
+
import { CodeBlock } from '@fragments-sdk/ui';
|
|
3
3
|
import type { FragmentVariant, PropDefinition } from '../../core/index.js';
|
|
4
4
|
|
|
5
5
|
interface CodePanelProps {
|
|
@@ -76,7 +76,6 @@ function buildFallbackSnippet(componentName: string, variant: FragmentVariant):
|
|
|
76
76
|
|
|
77
77
|
return `import { ${componentName} } from '@/components/${componentName}';
|
|
78
78
|
|
|
79
|
-
// TODO: Add explicit \`code\` for variant "${variant.name}" in this fragment file.
|
|
80
79
|
${usage}`;
|
|
81
80
|
}
|
|
82
81
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { useState, useEffect, useRef, useMemo, useCallback } from "react";
|
|
13
|
-
import { Dialog, Stack, Text, Badge, Separator, Input } from '@fragments/ui';
|
|
13
|
+
import { Dialog, Stack, Text, Badge, Separator, Input } from '@fragments-sdk/ui';
|
|
14
14
|
import type { FragmentDefinition } from "../../core/index.js";
|
|
15
15
|
import { SearchIcon, ChevronRightIcon } from "./Icons.js";
|
|
16
16
|
|
|
@@ -12,7 +12,7 @@ import { useMemo, useState } from "react";
|
|
|
12
12
|
import type { FragmentDefinition, ComponentRelation, RelationshipType } from "../../core/index.js";
|
|
13
13
|
import { ChevronRightIcon, EmptyIcon, WandIcon } from "./Icons.js";
|
|
14
14
|
import { detectAllRelationships, mergeRelationships } from "../utils/detectRelationships.js";
|
|
15
|
-
import { Stack, Text, Badge, Button, EmptyState, CodeBlock, Grid, Separator } from "@fragments/ui";
|
|
15
|
+
import { Stack, Text, Badge, Button, EmptyState, CodeBlock, Grid, Separator } from "@fragments-sdk/ui";
|
|
16
16
|
|
|
17
17
|
interface ComponentGraphProps {
|
|
18
18
|
/** Current fragment definition */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { FragmentMeta } from '../../core/index.js';
|
|
2
|
-
import { Badge, Alert, Text, Stack, Separator } from '@fragments/ui';
|
|
2
|
+
import { Badge, Alert, Text, Stack, Separator } from '@fragments-sdk/ui';
|
|
3
3
|
import { WarningIcon, BeakerIcon } from './Icons.js';
|
|
4
4
|
|
|
5
5
|
interface ComponentHeaderProps {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { memo } from 'react';
|
|
7
7
|
import type { FragmentContract } from '../../core/index.js';
|
|
8
|
-
import { Stack, Text, Card, Chip, Alert, EmptyState, CodeBlock, Badge } from '@fragments/ui';
|
|
8
|
+
import { Stack, Text, Card, Chip, Alert, EmptyState, CodeBlock, Badge } from '@fragments-sdk/ui';
|
|
9
9
|
|
|
10
10
|
interface ContractPanelProps {
|
|
11
11
|
contract?: FragmentContract;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Component, type ReactNode, type ErrorInfo } from 'react';
|
|
2
|
-
import { Button, Alert, CodeBlock, Collapsible, Stack, Text } from '@fragments/ui';
|
|
2
|
+
import { Button, Alert, CodeBlock, Collapsible, Stack, Text } from '@fragments-sdk/ui';
|
|
3
3
|
import { ErrorIcon, RefreshIcon } from './Icons.js';
|
|
4
4
|
|
|
5
5
|
interface ErrorBoundaryProps {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { useMemo, useState, useCallback, useEffect } from 'react';
|
|
8
8
|
import type { FragmentDefinition } from '../../core/index.js';
|
|
9
9
|
import type { ImpactValue } from 'axe-core';
|
|
10
|
-
import { Badge, Progress, Stack, Text, Card, EmptyState, Table } from '@fragments/ui';
|
|
10
|
+
import { Badge, Progress, Stack, Text, Card, EmptyState, Table } from '@fragments-sdk/ui';
|
|
11
11
|
import {
|
|
12
12
|
getAllA11yData,
|
|
13
13
|
getA11ySummary,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Badge, Stack, Text } from '@fragments/ui';
|
|
1
|
+
import { Badge, Stack, Text } from '@fragments-sdk/ui';
|
|
2
2
|
import { HMR_STATUS } from '../constants/ui.js';
|
|
3
3
|
import { useHmrStatus } from '../hooks/useHmrStatus.js';
|
|
4
4
|
import { WifiIcon, WifiOffIcon } from './Icons.js';
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { useState, useCallback, useRef, useEffect } from "react";
|
|
13
|
-
import { Button, Stack, Text, Badge, EmptyState, CodeBlock, Alert } from "@fragments/ui";
|
|
13
|
+
import { Button, Stack, Text, Badge, EmptyState, CodeBlock, Alert } from "@fragments-sdk/ui";
|
|
14
14
|
import type { PlayFunction, PlayFunctionContext, FragmentVariant } from "../../core/index.js";
|
|
15
15
|
import {
|
|
16
16
|
PlayIcon,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useMemo, useEffect, useState } from "react";
|
|
2
2
|
import type { FragmentDefinition } from "../../core/index.js";
|
|
3
3
|
import { VariantRenderer } from "./VariantRenderer.js";
|
|
4
|
-
import { getBackgroundStyle, type BackgroundOption, type ZoomLevel } from "
|
|
4
|
+
import { getBackgroundStyle, type BackgroundOption, type ZoomLevel } from "../constants/ui.js";
|
|
5
5
|
|
|
6
6
|
interface IsolatedRenderProps {
|
|
7
7
|
fragments: Array<{ path: string; fragment: FragmentDefinition }>;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Uses Fragments UI Dialog compound component.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { Dialog, Stack, Text, Badge } from '@fragments/ui';
|
|
8
|
+
import { Dialog, Stack, Text, Badge } from '@fragments-sdk/ui';
|
|
9
9
|
import { SHORTCUTS } from "../hooks/useKeyboardShortcuts.js";
|
|
10
10
|
|
|
11
11
|
interface KeyboardShortcutsHelpProps {
|
|
@@ -3,7 +3,7 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { useState, useRef, useEffect } from 'react';
|
|
5
5
|
import { HexColorPicker } from 'react-colorful';
|
|
6
|
-
import { Button, Input, Toggle, Stack, Text, Card, Field, Separator, Select } from '@fragments/ui';
|
|
6
|
+
import { Button, Input, Toggle, Stack, Text, Card, Field, Separator, Select } from '@fragments-sdk/ui';
|
|
7
7
|
import { generateColorScheme, type ColorSchemeType } from '../utils/colorSchemes.js';
|
|
8
8
|
import { ChevronDownIcon } from './Icons.js';
|
|
9
9
|
|
|
@@ -1,24 +1,27 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { DocsPageShell } from '@fragments-sdk/shared';
|
|
3
3
|
|
|
4
4
|
interface LayoutProps {
|
|
5
5
|
leftSidebar: ReactNode;
|
|
6
6
|
header: ReactNode;
|
|
7
7
|
children: ReactNode;
|
|
8
|
+
aside?: ReactNode;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
export function Layout({ leftSidebar, header, children }: LayoutProps) {
|
|
11
|
+
export function Layout({ leftSidebar, header, children, aside }: LayoutProps) {
|
|
11
12
|
return (
|
|
12
|
-
<
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
13
|
+
<DocsPageShell
|
|
14
|
+
header={header}
|
|
15
|
+
sidebar={leftSidebar}
|
|
16
|
+
sidebarWidth="260px"
|
|
17
|
+
sidebarCollapsible="icon"
|
|
18
|
+
sidebarAriaLabel="Preview sidebar"
|
|
19
|
+
mainAriaLabel="Preview content"
|
|
20
|
+
mainPadding="none"
|
|
21
|
+
aside={aside}
|
|
22
|
+
asideWidth="240px"
|
|
23
|
+
>
|
|
24
|
+
{children}
|
|
25
|
+
</DocsPageShell>
|
|
23
26
|
);
|
|
24
27
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState, useMemo, useRef, useEffect, useCallback } from 'react';
|
|
2
2
|
import type { FragmentDefinition } from '../../core/index.js';
|
|
3
|
-
import { Sidebar, useSidebar, Text } from '@fragments/ui';
|
|
3
|
+
import { Sidebar, useSidebar, Text, Menu } from '@fragments-sdk/ui';
|
|
4
4
|
|
|
5
5
|
// Fuzzy matching utility
|
|
6
6
|
interface FuzzyMatch {
|
|
@@ -125,11 +125,40 @@ interface LeftSidebarProps {
|
|
|
125
125
|
onHealthClick?: () => void;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
const FilterIcon = ({ active }: { active?: boolean }) => (
|
|
129
|
+
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
130
|
+
<path
|
|
131
|
+
d="M2 3h12l-4.5 5.5V13l-3-1.5V8.5L2 3z"
|
|
132
|
+
stroke={active ? 'var(--color-brand, #3b82f6)' : 'currentColor'}
|
|
133
|
+
strokeWidth="1.5"
|
|
134
|
+
strokeLinejoin="round"
|
|
135
|
+
fill={active ? 'var(--color-brand, #3b82f6)' : 'none'}
|
|
136
|
+
fillOpacity={active ? 0.15 : 0}
|
|
137
|
+
/>
|
|
138
|
+
</svg>
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const isInstalled = (path: string) => path.startsWith('@');
|
|
142
|
+
|
|
128
143
|
export function LeftSidebar({ fragments, activeFragment, searchQuery, onSelect, showHealth, onHealthClick }: LeftSidebarProps) {
|
|
129
144
|
const [focusedIndex, setFocusedIndex] = useState(-1);
|
|
145
|
+
const [showCustom, setShowCustom] = useState(true);
|
|
146
|
+
const [showLibrary, setShowLibrary] = useState(true);
|
|
130
147
|
const { isMobile, setOpen } = useSidebar();
|
|
131
148
|
const navRef = useRef<HTMLDivElement>(null);
|
|
132
149
|
|
|
150
|
+
// Determine if both sources exist (only show filter when useful)
|
|
151
|
+
const hasBothSources = useMemo(() => {
|
|
152
|
+
let hasCustom = false;
|
|
153
|
+
let hasLibrary = false;
|
|
154
|
+
for (const item of fragments) {
|
|
155
|
+
if (isInstalled(item.path)) hasLibrary = true;
|
|
156
|
+
else hasCustom = true;
|
|
157
|
+
if (hasCustom && hasLibrary) return true;
|
|
158
|
+
}
|
|
159
|
+
return false;
|
|
160
|
+
}, [fragments]);
|
|
161
|
+
|
|
133
162
|
const debouncedSearch = useDebounce(searchQuery, 150);
|
|
134
163
|
|
|
135
164
|
const searchResults = useMemo(() => {
|
|
@@ -155,19 +184,30 @@ export function LeftSidebar({ fragments, activeFragment, searchQuery, onSelect,
|
|
|
155
184
|
return map;
|
|
156
185
|
}, [searchResults]);
|
|
157
186
|
|
|
187
|
+
const isFilterActive = showCustom !== showLibrary;
|
|
188
|
+
|
|
189
|
+
// Apply source filter
|
|
190
|
+
const sourceFiltered = useMemo(() => {
|
|
191
|
+
if (!isFilterActive) return fragments;
|
|
192
|
+
return fragments.filter(item =>
|
|
193
|
+
showLibrary ? isInstalled(item.path) : !isInstalled(item.path)
|
|
194
|
+
);
|
|
195
|
+
}, [fragments, isFilterActive, showLibrary]);
|
|
196
|
+
|
|
158
197
|
// Flat alphabetical list (search results sorted by relevance, otherwise alphabetical)
|
|
159
198
|
const flatItems = useMemo(() => {
|
|
160
199
|
if (searchResults) {
|
|
161
200
|
return searchResults
|
|
162
201
|
.map(r => r.item)
|
|
163
|
-
.filter(item => item.fragment?.meta?.name)
|
|
202
|
+
.filter(item => item.fragment?.meta?.name)
|
|
203
|
+
.filter(item => !isFilterActive || (showLibrary ? isInstalled(item.path) : !isInstalled(item.path)));
|
|
164
204
|
}
|
|
165
|
-
return [...
|
|
205
|
+
return [...sourceFiltered]
|
|
166
206
|
.filter(item => item.fragment?.meta?.name)
|
|
167
207
|
.sort((a, b) =>
|
|
168
208
|
a.fragment.meta.name.toLowerCase().localeCompare(b.fragment.meta.name.toLowerCase())
|
|
169
209
|
);
|
|
170
|
-
}, [
|
|
210
|
+
}, [sourceFiltered, searchResults, isFilterActive, showLibrary]);
|
|
171
211
|
|
|
172
212
|
const keyboardItems = useMemo(() => {
|
|
173
213
|
const componentItems = flatItems.map((item) => ({ type: 'component' as const, path: item.path }));
|
|
@@ -252,19 +292,64 @@ export function LeftSidebar({ fragments, activeFragment, searchQuery, onSelect,
|
|
|
252
292
|
</Sidebar.Section>
|
|
253
293
|
)}
|
|
254
294
|
|
|
255
|
-
<Sidebar.Section
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
295
|
+
<Sidebar.Section
|
|
296
|
+
label="Components"
|
|
297
|
+
action={hasBothSources ? (
|
|
298
|
+
<Menu modal={false}>
|
|
299
|
+
<Menu.Trigger asChild>
|
|
300
|
+
<button
|
|
301
|
+
type="button"
|
|
302
|
+
aria-label="Filter components by source"
|
|
303
|
+
style={{
|
|
304
|
+
background: 'none',
|
|
305
|
+
border: 'none',
|
|
306
|
+
cursor: 'pointer',
|
|
307
|
+
padding: '2px',
|
|
308
|
+
display: 'flex',
|
|
309
|
+
alignItems: 'center',
|
|
310
|
+
color: 'var(--text-tertiary)',
|
|
311
|
+
borderRadius: '4px',
|
|
312
|
+
}}
|
|
313
|
+
>
|
|
314
|
+
<FilterIcon active={isFilterActive} />
|
|
315
|
+
</button>
|
|
316
|
+
</Menu.Trigger>
|
|
317
|
+
<Menu.Content>
|
|
318
|
+
<Menu.CheckboxItem checked={showCustom} onCheckedChange={setShowCustom}>
|
|
319
|
+
Custom
|
|
320
|
+
</Menu.CheckboxItem>
|
|
321
|
+
<Menu.CheckboxItem checked={showLibrary} onCheckedChange={setShowLibrary}>
|
|
322
|
+
Fragments UI
|
|
323
|
+
</Menu.CheckboxItem>
|
|
324
|
+
</Menu.Content>
|
|
325
|
+
</Menu>
|
|
326
|
+
) : undefined}
|
|
327
|
+
>
|
|
328
|
+
{flatItems.map((item) => {
|
|
329
|
+
const hasError = !!(item.fragment as any)?._loadError;
|
|
330
|
+
return (
|
|
331
|
+
<Sidebar.Item
|
|
332
|
+
key={item.path}
|
|
333
|
+
active={activeFragment === item.path}
|
|
334
|
+
onClick={() => handleSelect(item.path)}
|
|
335
|
+
>
|
|
336
|
+
<span style={{ display: 'flex', alignItems: 'center', gap: '6px', width: '100%' }}>
|
|
337
|
+
<HighlightedText
|
|
338
|
+
text={item.fragment.meta.name}
|
|
339
|
+
indices={highlightMap.get(item.path) || []}
|
|
340
|
+
/>
|
|
341
|
+
{hasError && (
|
|
342
|
+
<span
|
|
343
|
+
title="Missing dependencies"
|
|
344
|
+
style={{ color: 'var(--color-warning, #f59e0b)', fontSize: '14px', lineHeight: 1, flexShrink: 0 }}
|
|
345
|
+
>
|
|
346
|
+
⚠
|
|
347
|
+
</span>
|
|
348
|
+
)}
|
|
349
|
+
</span>
|
|
350
|
+
</Sidebar.Item>
|
|
351
|
+
);
|
|
352
|
+
})}
|
|
268
353
|
</Sidebar.Section>
|
|
269
354
|
|
|
270
355
|
{flatItems.length === 0 && (
|
|
@@ -278,7 +363,9 @@ export function LeftSidebar({ fragments, activeFragment, searchQuery, onSelect,
|
|
|
278
363
|
{/* Footer */}
|
|
279
364
|
<Sidebar.Footer>
|
|
280
365
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', width: '100%' }}>
|
|
281
|
-
<Text size="xs" color="tertiary">
|
|
366
|
+
<Text size="xs" color="tertiary">
|
|
367
|
+
{isFilterActive ? `${flatItems.length} / ${fragments.length}` : fragments.length} components
|
|
368
|
+
</Text>
|
|
282
369
|
<Sidebar.CollapseToggle />
|
|
283
370
|
</div>
|
|
284
371
|
</Sidebar.Footer>
|
|
@@ -10,7 +10,7 @@ import { useState, type ReactNode } from "react";
|
|
|
10
10
|
import { ErrorBoundary } from "./ErrorBoundary.js";
|
|
11
11
|
import { IsolatedPreviewFrame } from "./IsolatedPreviewFrame.js";
|
|
12
12
|
import { ChevronDownIcon } from "./Icons.js";
|
|
13
|
-
import { getBackgroundStyle, type BackgroundOption } from "
|
|
13
|
+
import { getBackgroundStyle, type BackgroundOption } from "../constants/ui.js";
|
|
14
14
|
|
|
15
15
|
interface ViewportConfig {
|
|
16
16
|
name: string;
|
|
@@ -13,9 +13,8 @@ import { FigmaEmbed } from './FigmaEmbed.js';
|
|
|
13
13
|
import { VariantMatrix } from './VariantMatrix.js';
|
|
14
14
|
import { MultiViewportPreview } from './MultiViewportPreview.js';
|
|
15
15
|
import { IsolatedPreviewFrame } from './IsolatedPreviewFrame.js';
|
|
16
|
-
import { getBackgroundStyle, type ZoomLevel, type BackgroundOption } from '
|
|
16
|
+
import { getBackgroundStyle, getViewportWidth, type ZoomLevel, type BackgroundOption, type ViewportPreset, type ViewportSize } from '../constants/ui.js';
|
|
17
17
|
import type { PreviewTheme } from '../hooks/useViewSettings.js';
|
|
18
|
-
import { getViewportWidth, type ViewportPreset, type ViewportSize } from './ViewportSelector.js';
|
|
19
18
|
|
|
20
19
|
interface PreviewAreaProps {
|
|
21
20
|
// Component data
|
|
@@ -398,11 +397,12 @@ export function PreviewArea({
|
|
|
398
397
|
if (useIframeIsolation && variant) {
|
|
399
398
|
// When no specific viewport width, fill the container
|
|
400
399
|
const isFullWidth = !viewportWidth;
|
|
400
|
+
const scaleFactor = zoom / 100;
|
|
401
401
|
|
|
402
402
|
return (
|
|
403
403
|
<div style={isFullWidth
|
|
404
|
-
? { height: '100%', display: 'flex', flexDirection: 'column' }
|
|
405
|
-
: { minHeight: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '24px' }
|
|
404
|
+
? { height: '100%', display: 'flex', flexDirection: 'column', overflow: 'auto' }
|
|
405
|
+
: { minHeight: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '24px', overflow: 'auto' }
|
|
406
406
|
}>
|
|
407
407
|
<div
|
|
408
408
|
style={{
|
|
@@ -419,15 +419,24 @@ export function PreviewArea({
|
|
|
419
419
|
}),
|
|
420
420
|
}}
|
|
421
421
|
>
|
|
422
|
-
<
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
422
|
+
<div
|
|
423
|
+
style={{
|
|
424
|
+
transform: `scale(${scaleFactor})`,
|
|
425
|
+
transformOrigin: 'top left',
|
|
426
|
+
width: zoom !== 100 ? `${100 / scaleFactor}%` : '100%',
|
|
427
|
+
height: zoom !== 100 ? `${100 / scaleFactor}%` : '100%',
|
|
428
|
+
}}
|
|
429
|
+
>
|
|
430
|
+
<IsolatedPreviewFrame
|
|
431
|
+
fragmentPath={fragmentPath}
|
|
432
|
+
variantName={variant.name}
|
|
433
|
+
theme={previewTheme}
|
|
434
|
+
width="100%"
|
|
435
|
+
height="100%"
|
|
436
|
+
minHeight={viewportHeight || 200}
|
|
437
|
+
previewKey={previewKey}
|
|
438
|
+
/>
|
|
439
|
+
</div>
|
|
431
440
|
</div>
|
|
432
441
|
</div>
|
|
433
442
|
);
|
|
@@ -89,10 +89,6 @@ function resolvePreviewMode(fragment: FragmentDefinition): PreviewMode {
|
|
|
89
89
|
const name = fragment.meta.name.toLowerCase();
|
|
90
90
|
const category = (fragment.meta.category || '').toLowerCase();
|
|
91
91
|
|
|
92
|
-
if (category === 'layout' || category === 'navigation') {
|
|
93
|
-
return 'full-bleed';
|
|
94
|
-
}
|
|
95
|
-
|
|
96
92
|
if (name.includes('appshell') || name.includes('sidebar') || name.includes('header') || name.includes('layout')) {
|
|
97
93
|
return 'full-bleed';
|
|
98
94
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
2
|
import type { PropDefinition } from "../../core/index.js";
|
|
3
|
-
import { Input, Select, Toggle } from "@fragments/ui";
|
|
3
|
+
import { Input, Select, Toggle } from "@fragments-sdk/ui";
|
|
4
4
|
import { ControlsIcon, ChevronDownIcon, RefreshIcon } from "./Icons.js";
|
|
5
5
|
|
|
6
6
|
interface PropsEditorProps {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
2
|
import type { PropDefinition } from '../../core/index.js';
|
|
3
|
-
import { Table, createColumns, Badge, Text, Stack } from '@fragments/ui';
|
|
3
|
+
import { Table, createColumns, Badge, Text, Stack } from '@fragments-sdk/ui';
|
|
4
4
|
import { WarningIcon } from './Icons.js';
|
|
5
5
|
|
|
6
6
|
interface PropsTableProps {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { FragmentDefinition } from '../../core/index.js';
|
|
2
2
|
import { useScrollSpy } from '../hooks/useScrollSpy.js';
|
|
3
|
-
import { Sidebar } from '@fragments/ui';
|
|
3
|
+
import { Sidebar } from '@fragments-sdk/ui';
|
|
4
4
|
|
|
5
5
|
interface RightSidebarProps {
|
|
6
6
|
fragment: FragmentDefinition;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { useState, memo } from 'react';
|
|
7
7
|
import html2canvas from 'html2canvas';
|
|
8
|
-
import { Button, Tooltip } from '@fragments/ui';
|
|
8
|
+
import { Button, Tooltip } from '@fragments-sdk/ui';
|
|
9
9
|
import { CameraIcon } from './Icons.js';
|
|
10
10
|
|
|
11
11
|
interface ScreenshotButtonProps {
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// Re-export Fragments UI Toast for use in the viewer
|
|
2
|
-
export { ToastProvider, useToast } from '@fragments/ui';
|
|
3
|
-
export type { ToastData as ToastMessage } from '@fragments/ui';
|
|
2
|
+
export { ToastProvider, useToast } from '@fragments-sdk/ui';
|
|
3
|
+
export type { ToastData as ToastMessage } from '@fragments-sdk/ui';
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { useState, useEffect, useCallback } from "react";
|
|
12
|
-
import { Badge } from "@fragments/ui";
|
|
12
|
+
import { Badge } from "@fragments-sdk/ui";
|
|
13
13
|
import type { DesignToken, EnhancedStyleDiffItem, TokenUsageSummary } from "../../core/index.js";
|
|
14
14
|
import { CheckIcon, XIcon, LoadingIcon, FigmaIcon, WandIcon } from "./Icons.js";
|
|
15
15
|
|
|
@@ -17,7 +17,7 @@ import { ErrorBoundary } from "./ErrorBoundary.js";
|
|
|
17
17
|
import { StoryRenderer, LoaderIndicator } from "./StoryRenderer.js";
|
|
18
18
|
import { IsolatedPreviewFrame } from "./IsolatedPreviewFrame.js";
|
|
19
19
|
import { ChevronDownIcon } from "./Icons.js";
|
|
20
|
-
import { getBackgroundStyle, type BackgroundOption } from "
|
|
20
|
+
import { getBackgroundStyle, type BackgroundOption } from "../constants/ui.js";
|
|
21
21
|
|
|
22
22
|
interface VariantMatrixProps {
|
|
23
23
|
/** All variants to display */
|
|
@@ -126,6 +126,20 @@ export const VIEWPORT_PRESETS = {
|
|
|
126
126
|
|
|
127
127
|
export type ViewportPreset = keyof typeof VIEWPORT_PRESETS | 'custom';
|
|
128
128
|
|
|
129
|
+
export interface ViewportSize {
|
|
130
|
+
width: number | null;
|
|
131
|
+
height: number | null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Get viewport width from preset.
|
|
136
|
+
*/
|
|
137
|
+
export function getViewportWidth(viewport: ViewportPreset, customSize: ViewportSize): number | null {
|
|
138
|
+
if (viewport === 'custom') return customSize.width;
|
|
139
|
+
if (viewport === 'responsive') return null;
|
|
140
|
+
return VIEWPORT_PRESETS[viewport]?.width ?? null;
|
|
141
|
+
}
|
|
142
|
+
|
|
129
143
|
/**
|
|
130
144
|
* Get CSS background style for preview pane.
|
|
131
145
|
*/
|
package/src/viewer/entry.tsx
CHANGED
|
@@ -4,11 +4,10 @@ import { App } from "./components/App.js";
|
|
|
4
4
|
import { ThemeProvider } from "./components/ThemeProvider.js";
|
|
5
5
|
import { ToastProvider } from "./components/Toast.js";
|
|
6
6
|
import { AppSkeleton } from "./components/SkeletonLoader.js";
|
|
7
|
-
//
|
|
8
|
-
|
|
7
|
+
// Fragments UI globals - base resets (box-sizing, body, scrollbars) + --fui-* CSS variables
|
|
8
|
+
import "@fragments-sdk/ui";
|
|
9
|
+
// Viewer-specific token aliases and utility classes
|
|
9
10
|
import "./styles/globals.css";
|
|
10
|
-
// Fragments UI globals - adds --fui-* CSS variables for dogfooding UI components
|
|
11
|
-
import "@fragments/ui";
|
|
12
11
|
|
|
13
12
|
// App-level error boundary that catches any unhandled errors
|
|
14
13
|
class AppErrorBoundary extends Component<{ children: ReactNode }, { hasError: boolean; error: Error | null; errorInfo: ErrorInfo | null }> {
|