@fragments-sdk/cli 0.8.1 → 0.9.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.
- package/dist/bin.js +517 -77
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-WI6SLMSO.js → chunk-5GT62FCB.js} +2 -2
- package/dist/{chunk-CJEGT3WD.js → chunk-BW3ZATBW.js} +20 -3
- package/dist/chunk-BW3ZATBW.js.map +1 -0
- package/dist/{chunk-2JIKCJX3.js → chunk-D7372LQX.js} +13 -6
- package/dist/chunk-D7372LQX.js.map +1 -0
- package/dist/chunk-EZYXYWNF.js +131 -0
- package/dist/chunk-EZYXYWNF.js.map +1 -0
- package/dist/{chunk-NGIMCIK2.js → chunk-GF6OVPIN.js} +2 -2
- package/dist/{chunk-GOVI6COW.js → chunk-NVSPGSKB.js} +12 -4
- package/dist/chunk-NVSPGSKB.js.map +1 -0
- package/dist/core/index.d.ts +105 -3
- package/dist/core/index.js +12 -2
- package/dist/{defineFragment-D0UTve-I.d.ts → defineFragment-CBMS7Bab.d.ts} +21 -1
- package/dist/generate-LQA2R7FN.js +461 -0
- package/dist/generate-LQA2R7FN.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/{init-KFYN37ZY.js → init-2GEGVIUQ.js} +14 -76
- package/dist/init-2GEGVIUQ.js.map +1 -0
- package/dist/mcp-bin.js +4 -3
- package/dist/mcp-bin.js.map +1 -1
- package/dist/{scan-65RH3QMM.js → scan-JGS65S7P.js} +6 -5
- package/dist/{service-A5GIGGGK.js → service-XP2EAJXD.js} +4 -3
- package/dist/{static-viewer-NSODM5VX.js → static-viewer-XCS7UJTO.js} +4 -3
- package/dist/storyFilters-3LUYAFZF.js +15 -0
- package/dist/storyFilters-3LUYAFZF.js.map +1 -0
- package/dist/{test-RPWZAYSJ.js → test-TD6TJNVY.js} +3 -3
- package/dist/{tokens-NIXSZRX7.js → tokens-2EXPCVP3.js} +5 -4
- package/dist/{tokens-NIXSZRX7.js.map → tokens-2EXPCVP3.js.map} +1 -1
- package/dist/{viewer-HZK4BSDK.js → viewer-RFA2KVBG.js} +249 -22
- package/dist/viewer-RFA2KVBG.js.map +1 -0
- package/package.json +2 -2
- package/src/bin.ts +26 -0
- package/src/build.ts +12 -2
- package/src/commands/build.ts +16 -2
- package/src/commands/doctor.ts +498 -0
- package/src/commands/generate.ts +383 -68
- package/src/commands/init-framework.ts +1 -1
- package/src/commands/init.ts +9 -51
- package/src/core/config.ts +15 -2
- package/src/core/generators/typescript-extractor.ts +10 -0
- package/src/core/index.ts +15 -0
- package/src/core/schema.ts +10 -2
- package/src/core/storyFilters.test.ts +350 -0
- package/src/core/storyFilters.ts +253 -0
- package/src/core/types.ts +22 -0
- package/src/migrate/converter.ts +9 -1
- package/src/migrate/parser.ts +2 -0
- package/src/migrate/types.ts +2 -0
- package/src/setup.ts +69 -24
- package/src/viewer/__tests__/viewer-integration.test.ts +1 -1
- package/src/viewer/components/AccessibilityPanel.tsx +305 -312
- package/src/viewer/components/ActionsPanel.tsx +31 -29
- package/src/viewer/components/AllVariantsPreview.tsx +78 -0
- package/src/viewer/components/App.tsx +187 -740
- package/src/viewer/components/BottomPanel.tsx +228 -132
- package/src/viewer/components/CodePanel.tsx +1 -1
- package/src/viewer/components/CommandPalette.tsx +7 -10
- package/src/viewer/components/ComponentDocView.tsx +164 -0
- package/src/viewer/components/ComponentGraph.tsx +111 -142
- package/src/viewer/components/ContractPanel.tsx +6 -6
- package/src/viewer/components/EmptyVariantMessage.tsx +54 -0
- package/src/viewer/components/FigmaEmbed.tsx +20 -18
- package/src/viewer/components/FragmentEditor.tsx +92 -115
- package/src/viewer/components/HeaderSearch.tsx +24 -0
- package/src/viewer/components/HealthDashboard.tsx +16 -2
- package/src/viewer/components/Icons.tsx +9 -0
- package/src/viewer/components/InteractionsPanel.tsx +101 -117
- package/src/viewer/components/IsolatedPreviewFrame.tsx +1 -0
- package/src/viewer/components/LandingPage.tsx +3 -3
- package/src/viewer/components/LeftSidebar.tsx +141 -63
- package/src/viewer/components/LoadErrorMessage.tsx +102 -0
- package/src/viewer/components/MultiViewportPreview.tsx +61 -142
- package/src/viewer/components/NoVariantsMessage.tsx +59 -0
- package/src/viewer/components/PanelShell.tsx +161 -0
- package/src/viewer/components/PerformancePanel.tsx +31 -28
- package/src/viewer/components/PreviewArea.tsx +1 -1
- package/src/viewer/components/PreviewAside.tsx +168 -0
- package/src/viewer/components/PreviewFrameHost.tsx +3 -3
- package/src/viewer/components/PropsEditor.tsx +70 -156
- package/src/viewer/components/ResizablePanel.tsx +103 -263
- package/src/viewer/components/RightSidebar.tsx +3 -9
- package/src/viewer/components/SkeletonLoader.tsx +13 -13
- package/src/viewer/components/TokenStylePanel.tsx +182 -209
- package/src/viewer/components/TopToolbar.tsx +159 -0
- package/src/viewer/components/VariantMatrix.tsx +42 -86
- package/src/viewer/components/VariantTabs.tsx +3 -3
- package/src/viewer/components/ViewerHeader.tsx +69 -0
- package/src/viewer/components/WebMCPDevTools.tsx +17 -23
- package/src/viewer/components/viewer-utils.ts +16 -0
- package/src/viewer/entry.tsx +5 -0
- package/src/viewer/hooks/useAppState.ts +27 -4
- package/src/viewer/hooks/usePreviewBridge.ts +2 -2
- package/src/viewer/preview-frame.html +6 -12
- package/src/viewer/server.ts +184 -6
- package/src/viewer/vendor/shared/src/ComponentDocContent.module.scss +10 -0
- package/src/viewer/vendor/shared/src/ComponentDocContent.module.scss.d.ts +2 -0
- package/src/viewer/vendor/shared/src/ComponentDocContent.tsx +274 -0
- package/src/viewer/vendor/shared/src/DocsPageShell.tsx +5 -0
- package/src/viewer/vendor/shared/src/PropsTable.module.scss +68 -0
- package/src/viewer/vendor/shared/src/PropsTable.module.scss.d.ts +2 -0
- package/src/viewer/vendor/shared/src/PropsTable.tsx +76 -0
- package/src/viewer/vendor/shared/src/VariantPreviewCard.module.scss +122 -0
- package/src/viewer/vendor/shared/src/VariantPreviewCard.module.scss.d.ts +2 -0
- package/src/viewer/vendor/shared/src/VariantPreviewCard.tsx +134 -0
- package/src/viewer/vendor/shared/src/docs-data/index.ts +32 -0
- package/src/viewer/vendor/shared/src/docs-data/mcp-configs.ts +72 -0
- package/src/viewer/vendor/shared/src/docs-data/palettes.ts +75 -0
- package/src/viewer/vendor/shared/src/docs-data/setup-examples.ts +55 -0
- package/src/viewer/vendor/shared/src/index.ts +8 -0
- package/src/viewer/vendor/shared/src/types.ts +12 -0
- package/src/viewer/vite-plugin.ts +109 -4
- package/dist/chunk-2JIKCJX3.js.map +0 -1
- package/dist/chunk-CJEGT3WD.js.map +0 -1
- package/dist/chunk-GOVI6COW.js.map +0 -1
- package/dist/generate-35OIMW4Y.js +0 -252
- package/dist/generate-35OIMW4Y.js.map +0 -1
- package/dist/init-KFYN37ZY.js.map +0 -1
- package/dist/viewer-HZK4BSDK.js.map +0 -1
- /package/dist/{chunk-WI6SLMSO.js.map → chunk-5GT62FCB.js.map} +0 -0
- /package/dist/{chunk-NGIMCIK2.js.map → chunk-GF6OVPIN.js.map} +0 -0
- /package/dist/{scan-65RH3QMM.js.map → scan-JGS65S7P.js.map} +0 -0
- /package/dist/{service-A5GIGGGK.js.map → service-XP2EAJXD.js.map} +0 -0
- /package/dist/{static-viewer-NSODM5VX.js.map → static-viewer-XCS7UJTO.js.map} +0 -0
- /package/dist/{test-RPWZAYSJ.js.map → test-TD6TJNVY.js.map} +0 -0
|
@@ -1,38 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ResizablePanel - A panel that can be resized by dragging
|
|
2
|
+
* ResizablePanel - A bottom-docked panel that can be resized by dragging
|
|
3
3
|
*
|
|
4
4
|
* Features:
|
|
5
|
-
* - Drag-to-resize from the edge
|
|
6
|
-
* -
|
|
7
|
-
* -
|
|
8
|
-
* - Persists size and dock position to localStorage
|
|
5
|
+
* - Drag-to-resize from the top edge
|
|
6
|
+
* - Collapsible via chevron toggle
|
|
7
|
+
* - Persists size and open state to localStorage
|
|
9
8
|
*/
|
|
10
9
|
|
|
11
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
useState,
|
|
12
|
+
useEffect,
|
|
13
|
+
useCallback,
|
|
14
|
+
useRef,
|
|
15
|
+
type ReactNode,
|
|
16
|
+
type CSSProperties,
|
|
17
|
+
} from "react";
|
|
12
18
|
import { BRAND } from "../../core/index.js";
|
|
13
19
|
import { ChevronDownIcon } from "./Icons.js";
|
|
20
|
+
import { Box, Stack, Button } from "@fragments-sdk/ui";
|
|
14
21
|
|
|
15
22
|
// Storage key for persisting panel state
|
|
16
23
|
const STORAGE_KEY = `${BRAND.storagePrefix}panel-state`;
|
|
17
24
|
|
|
18
25
|
interface PanelState {
|
|
19
26
|
height: number;
|
|
20
|
-
width: number;
|
|
21
|
-
dock: "bottom" | "right";
|
|
22
27
|
isOpen: boolean;
|
|
23
28
|
}
|
|
24
29
|
|
|
25
30
|
const DEFAULT_STATE: PanelState = {
|
|
26
|
-
height: 180,
|
|
27
|
-
width: 400,
|
|
28
|
-
dock: "bottom",
|
|
31
|
+
height: 180,
|
|
29
32
|
isOpen: true,
|
|
30
33
|
};
|
|
31
34
|
|
|
32
35
|
const MIN_HEIGHT = 120;
|
|
33
36
|
const MAX_HEIGHT = 600;
|
|
34
|
-
const MIN_WIDTH = 280;
|
|
35
|
-
const MAX_WIDTH = 800;
|
|
36
37
|
|
|
37
38
|
function loadPanelState(): PanelState {
|
|
38
39
|
try {
|
|
@@ -55,7 +56,7 @@ function savePanelState(state: PanelState): void {
|
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
interface ResizablePanelProps {
|
|
59
|
+
interface ResizablePanelProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
59
60
|
/** Panel header content (tabs, title, etc.) */
|
|
60
61
|
header: ReactNode;
|
|
61
62
|
/** Panel body content */
|
|
@@ -71,12 +72,13 @@ export function ResizablePanel({
|
|
|
71
72
|
children,
|
|
72
73
|
visible = true,
|
|
73
74
|
className,
|
|
75
|
+
...props
|
|
74
76
|
}: ResizablePanelProps) {
|
|
75
77
|
const [state, setState] = useState<PanelState>(loadPanelState);
|
|
76
78
|
const [isResizing, setIsResizing] = useState(false);
|
|
77
79
|
const panelRef = useRef<HTMLDivElement>(null);
|
|
78
|
-
const
|
|
79
|
-
const
|
|
80
|
+
const startYRef = useRef(0);
|
|
81
|
+
const startHeightRef = useRef(0);
|
|
80
82
|
|
|
81
83
|
// Save state changes to localStorage
|
|
82
84
|
useEffect(() => {
|
|
@@ -87,35 +89,21 @@ export function ResizablePanel({
|
|
|
87
89
|
(e: React.MouseEvent) => {
|
|
88
90
|
e.preventDefault();
|
|
89
91
|
setIsResizing(true);
|
|
90
|
-
|
|
91
|
-
|
|
92
|
+
startYRef.current = e.clientY;
|
|
93
|
+
startHeightRef.current = state.height;
|
|
92
94
|
},
|
|
93
|
-
[state.
|
|
95
|
+
[state.height]
|
|
94
96
|
);
|
|
95
97
|
|
|
96
98
|
const handleMouseMove = useCallback(
|
|
97
99
|
(e: MouseEvent) => {
|
|
98
100
|
if (!isResizing) return;
|
|
99
101
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const newHeight = Math.max(
|
|
104
|
-
MIN_HEIGHT,
|
|
105
|
-
Math.min(MAX_HEIGHT, startSizeRef.current.height + deltaY)
|
|
106
|
-
);
|
|
107
|
-
setState((s) => ({ ...s, height: newHeight }));
|
|
108
|
-
} else {
|
|
109
|
-
// Resize width (drag left = increase, drag right = decrease)
|
|
110
|
-
const deltaX = startPosRef.current.x - e.clientX;
|
|
111
|
-
const newWidth = Math.max(
|
|
112
|
-
MIN_WIDTH,
|
|
113
|
-
Math.min(MAX_WIDTH, startSizeRef.current.width + deltaX)
|
|
114
|
-
);
|
|
115
|
-
setState((s) => ({ ...s, width: newWidth }));
|
|
116
|
-
}
|
|
102
|
+
const deltaY = startYRef.current - e.clientY;
|
|
103
|
+
const newHeight = Math.max(MIN_HEIGHT, Math.min(MAX_HEIGHT, startHeightRef.current + deltaY));
|
|
104
|
+
setState((s) => ({ ...s, height: newHeight }));
|
|
117
105
|
},
|
|
118
|
-
[isResizing
|
|
106
|
+
[isResizing]
|
|
119
107
|
);
|
|
120
108
|
|
|
121
109
|
const handleMouseUp = useCallback(() => {
|
|
@@ -127,7 +115,7 @@ export function ResizablePanel({
|
|
|
127
115
|
if (isResizing) {
|
|
128
116
|
document.addEventListener("mousemove", handleMouseMove);
|
|
129
117
|
document.addEventListener("mouseup", handleMouseUp);
|
|
130
|
-
document.body.style.cursor =
|
|
118
|
+
document.body.style.cursor = "ns-resize";
|
|
131
119
|
document.body.style.userSelect = "none";
|
|
132
120
|
|
|
133
121
|
return () => {
|
|
@@ -137,295 +125,147 @@ export function ResizablePanel({
|
|
|
137
125
|
document.body.style.userSelect = "";
|
|
138
126
|
};
|
|
139
127
|
}
|
|
140
|
-
}, [isResizing, handleMouseMove, handleMouseUp
|
|
128
|
+
}, [isResizing, handleMouseMove, handleMouseUp]);
|
|
141
129
|
|
|
142
130
|
const toggleOpen = useCallback(() => {
|
|
143
131
|
setState((s) => ({ ...s, isOpen: !s.isOpen }));
|
|
144
132
|
}, []);
|
|
145
133
|
|
|
146
|
-
const toggleDock = useCallback(() => {
|
|
147
|
-
setState((s) => ({
|
|
148
|
-
...s,
|
|
149
|
-
dock: s.dock === "bottom" ? "right" : "bottom",
|
|
150
|
-
}));
|
|
151
|
-
}, []);
|
|
152
|
-
|
|
153
134
|
if (!visible) return null;
|
|
154
135
|
|
|
155
|
-
const isBottom = state.dock === "bottom";
|
|
156
136
|
const isOpen = state.isOpen;
|
|
157
137
|
|
|
158
|
-
//
|
|
138
|
+
// Heights for layout calculation
|
|
159
139
|
const headerHeight = 40; // h-10 = 2.5rem = 40px
|
|
140
|
+
const grabHandleHeight = 12; // 6px top padding + 4px pill + 2px bottom padding
|
|
160
141
|
|
|
161
|
-
// Compute container styles
|
|
142
|
+
// Compute container styles — drawer appearance
|
|
162
143
|
const containerStyle: CSSProperties = {
|
|
163
144
|
flexShrink: 0,
|
|
164
|
-
backgroundColor:
|
|
165
|
-
position: 'relative',
|
|
166
|
-
...(isBottom
|
|
167
|
-
? { borderTop: '1px solid var(--border)' }
|
|
168
|
-
: { borderLeft: '1px solid var(--border)' }),
|
|
145
|
+
backgroundColor: "var(--bg-secondary)",
|
|
169
146
|
// Only transition when NOT resizing (for smooth open/close animations)
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
...(isBottom
|
|
173
|
-
? { height: isOpen ? state.height : headerHeight }
|
|
174
|
-
: { width: isOpen ? state.width : headerHeight }),
|
|
147
|
+
transition: isResizing ? "none" : "height 300ms ease",
|
|
148
|
+
height: isOpen ? state.height : headerHeight,
|
|
175
149
|
// Prevent content from being selected during resize
|
|
176
150
|
...(isResizing && { pointerEvents: "none" as const }),
|
|
151
|
+
position: "sticky",
|
|
152
|
+
bottom: 0,
|
|
153
|
+
// Drawer visual treatment
|
|
154
|
+
borderTopLeftRadius: "12px",
|
|
155
|
+
borderTopRightRadius: "12px",
|
|
156
|
+
boxShadow: "0 -4px 16px rgba(0, 0, 0, 0.08), 0 -1px 4px rgba(0, 0, 0, 0.04)",
|
|
157
|
+
borderTop: "1px solid var(--border)",
|
|
177
158
|
};
|
|
178
159
|
|
|
179
|
-
// Resize handle styles
|
|
180
|
-
const resizeHandleStyle: CSSProperties =
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
: {
|
|
192
|
-
position: 'absolute',
|
|
193
|
-
zIndex: 20,
|
|
194
|
-
top: 0,
|
|
195
|
-
left: '-8px',
|
|
196
|
-
bottom: 0,
|
|
197
|
-
width: '16px',
|
|
198
|
-
cursor: 'ew-resize',
|
|
199
|
-
...(isResizing && { backgroundColor: 'rgba(var(--color-accent-rgb, 59, 130, 246), 0.2)' }),
|
|
200
|
-
};
|
|
160
|
+
// Resize handle styles — extends above panel for easier grabbing
|
|
161
|
+
const resizeHandleStyle: CSSProperties = {
|
|
162
|
+
position: "absolute",
|
|
163
|
+
zIndex: 20,
|
|
164
|
+
top: "-8px",
|
|
165
|
+
left: 0,
|
|
166
|
+
right: 0,
|
|
167
|
+
height: "16px",
|
|
168
|
+
cursor: "ns-resize",
|
|
169
|
+
...(isResizing && { backgroundColor: "rgba(var(--color-accent-rgb, 59, 130, 246), 0.2)" }),
|
|
170
|
+
};
|
|
201
171
|
|
|
202
|
-
//
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
:
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
opacity: 0,
|
|
220
|
-
top: '50%',
|
|
221
|
-
left: '50%',
|
|
222
|
-
transform: 'translate(-50%, -50%)',
|
|
223
|
-
width: '4px',
|
|
224
|
-
height: '48px',
|
|
225
|
-
borderRadius: '9999px',
|
|
226
|
-
transition: 'opacity 150ms ease',
|
|
227
|
-
};
|
|
172
|
+
// Grab handle pill — always visible in drawer style
|
|
173
|
+
const grabHandleStyle: CSSProperties = {
|
|
174
|
+
width: "100%",
|
|
175
|
+
display: "flex",
|
|
176
|
+
justifyContent: "center",
|
|
177
|
+
padding: "6px 0 2px",
|
|
178
|
+
cursor: "ns-resize",
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const grabPillStyle: CSSProperties = {
|
|
182
|
+
width: "36px",
|
|
183
|
+
height: "4px",
|
|
184
|
+
borderRadius: "9999px",
|
|
185
|
+
backgroundColor: "var(--text-muted)",
|
|
186
|
+
opacity: isResizing ? 0.8 : 0.3,
|
|
187
|
+
transition: "opacity 150ms ease",
|
|
188
|
+
};
|
|
228
189
|
|
|
229
190
|
// Chevron icon rotation
|
|
230
191
|
const chevronStyle: CSSProperties = {
|
|
231
|
-
width:
|
|
232
|
-
height:
|
|
233
|
-
transition:
|
|
234
|
-
...(!isOpen &&
|
|
235
|
-
...(!isOpen && !isBottom && { transform: 'rotate(-90deg)' }),
|
|
236
|
-
...(isOpen && !isBottom && { transform: 'rotate(90deg)' }),
|
|
192
|
+
width: "16px",
|
|
193
|
+
height: "16px",
|
|
194
|
+
transition: "transform 150ms ease",
|
|
195
|
+
...(!isOpen && { transform: "rotate(180deg)" }),
|
|
237
196
|
};
|
|
238
197
|
|
|
239
198
|
return (
|
|
240
|
-
<div
|
|
241
|
-
ref={panelRef}
|
|
242
|
-
className={className}
|
|
243
|
-
style={containerStyle}
|
|
244
|
-
>
|
|
199
|
+
<div ref={panelRef} className={className} style={containerStyle} {...props}>
|
|
245
200
|
{/* Full-viewport overlay during resize - prevents iframes from capturing events */}
|
|
246
201
|
{isResizing && (
|
|
247
202
|
<div
|
|
248
203
|
style={{
|
|
249
|
-
position:
|
|
204
|
+
position: "fixed",
|
|
250
205
|
inset: 0,
|
|
251
206
|
zIndex: 50,
|
|
252
|
-
cursor:
|
|
207
|
+
cursor: "ns-resize",
|
|
253
208
|
// Must explicitly enable pointer events since parent has pointerEvents: none
|
|
254
209
|
pointerEvents: "auto",
|
|
255
210
|
}}
|
|
256
211
|
/>
|
|
257
212
|
)}
|
|
258
213
|
|
|
259
|
-
{/* Resize Handle - extends above
|
|
214
|
+
{/* Resize Handle - extends above panel for easier grabbing */}
|
|
260
215
|
{isOpen && (
|
|
261
216
|
<div
|
|
262
217
|
style={resizeHandleStyle}
|
|
263
218
|
onMouseDown={handleMouseDown}
|
|
219
|
+
/>
|
|
220
|
+
)}
|
|
221
|
+
|
|
222
|
+
{/* Grab handle pill — always visible drawer indicator */}
|
|
223
|
+
{isOpen && (
|
|
224
|
+
<div
|
|
225
|
+
style={grabHandleStyle}
|
|
226
|
+
onMouseDown={handleMouseDown}
|
|
264
227
|
onMouseEnter={(e) => {
|
|
265
|
-
const
|
|
266
|
-
if (
|
|
228
|
+
const pill = e.currentTarget.querySelector("[data-grab-pill]") as HTMLElement;
|
|
229
|
+
if (pill) pill.style.opacity = "0.6";
|
|
267
230
|
}}
|
|
268
231
|
onMouseLeave={(e) => {
|
|
269
|
-
const
|
|
270
|
-
if (
|
|
232
|
+
const pill = e.currentTarget.querySelector("[data-grab-pill]") as HTMLElement;
|
|
233
|
+
if (pill) pill.style.opacity = isResizing ? "0.8" : "0.3";
|
|
271
234
|
}}
|
|
272
235
|
>
|
|
273
|
-
|
|
274
|
-
<div data-resize-indicator style={indicatorStyle} />
|
|
236
|
+
<div data-grab-pill style={grabPillStyle} />
|
|
275
237
|
</div>
|
|
276
238
|
)}
|
|
277
239
|
|
|
278
240
|
{/* Panel Header */}
|
|
279
|
-
<
|
|
280
|
-
style={{
|
|
281
|
-
display: 'flex',
|
|
282
|
-
alignItems: 'center',
|
|
283
|
-
justifyContent: 'space-between',
|
|
284
|
-
padding: '0 16px',
|
|
285
|
-
height: '40px',
|
|
286
|
-
borderBottom: '1px solid var(--border-subtle)',
|
|
287
|
-
}}
|
|
288
|
-
>
|
|
289
|
-
<div style={{
|
|
290
|
-
display: 'flex',
|
|
291
|
-
alignItems: 'center',
|
|
292
|
-
gap: '4px',
|
|
293
|
-
flex: 1,
|
|
294
|
-
overflow: 'hidden',
|
|
295
|
-
}}>
|
|
241
|
+
<Stack direction="row" align="center" justify="between" style={{ padding: '0 16px', height: '40px', borderBottom: '1px solid var(--border-subtle)' }}>
|
|
242
|
+
<Stack direction="row" align="center" gap="xs" style={{ flex: 1, overflow: 'hidden' }}>
|
|
296
243
|
{header}
|
|
297
|
-
</
|
|
298
|
-
<
|
|
299
|
-
{/* Dock toggle button */}
|
|
300
|
-
<button
|
|
301
|
-
onClick={toggleDock}
|
|
302
|
-
style={{
|
|
303
|
-
padding: '4px',
|
|
304
|
-
color: 'var(--text-tertiary)',
|
|
305
|
-
backgroundColor: 'transparent',
|
|
306
|
-
border: 'none',
|
|
307
|
-
borderRadius: '4px',
|
|
308
|
-
cursor: 'pointer',
|
|
309
|
-
display: 'inline-flex',
|
|
310
|
-
transition: 'color 150ms ease, background-color 150ms ease',
|
|
311
|
-
}}
|
|
312
|
-
onMouseEnter={(e) => {
|
|
313
|
-
e.currentTarget.style.color = 'var(--text-primary)';
|
|
314
|
-
e.currentTarget.style.backgroundColor = 'var(--bg-hover)';
|
|
315
|
-
}}
|
|
316
|
-
onMouseLeave={(e) => {
|
|
317
|
-
e.currentTarget.style.color = 'var(--text-tertiary)';
|
|
318
|
-
e.currentTarget.style.backgroundColor = 'transparent';
|
|
319
|
-
}}
|
|
320
|
-
title={isBottom ? "Dock to right" : "Dock to bottom"}
|
|
321
|
-
>
|
|
322
|
-
<span style={{ display: 'inline-flex', width: '16px', height: '16px' }}>
|
|
323
|
-
<DockIcon dock={state.dock} />
|
|
324
|
-
</span>
|
|
325
|
-
</button>
|
|
244
|
+
</Stack>
|
|
245
|
+
<Stack direction="row" align="center" gap="xs">
|
|
326
246
|
{/* Collapse toggle button */}
|
|
327
|
-
<
|
|
247
|
+
<Button
|
|
248
|
+
variant="ghost"
|
|
249
|
+
size="sm"
|
|
250
|
+
icon
|
|
328
251
|
onClick={toggleOpen}
|
|
329
|
-
|
|
330
|
-
padding: '4px',
|
|
331
|
-
color: 'var(--text-tertiary)',
|
|
332
|
-
backgroundColor: 'transparent',
|
|
333
|
-
border: 'none',
|
|
334
|
-
borderRadius: '4px',
|
|
335
|
-
cursor: 'pointer',
|
|
336
|
-
display: 'inline-flex',
|
|
337
|
-
transition: 'color 150ms ease, background-color 150ms ease',
|
|
338
|
-
}}
|
|
339
|
-
onMouseEnter={(e) => {
|
|
340
|
-
e.currentTarget.style.color = 'var(--text-primary)';
|
|
341
|
-
e.currentTarget.style.backgroundColor = 'var(--bg-hover)';
|
|
342
|
-
}}
|
|
343
|
-
onMouseLeave={(e) => {
|
|
344
|
-
e.currentTarget.style.color = 'var(--text-tertiary)';
|
|
345
|
-
e.currentTarget.style.backgroundColor = 'transparent';
|
|
346
|
-
}}
|
|
347
|
-
title={isOpen ? "Collapse panel" : "Expand panel"}
|
|
252
|
+
aria-label={isOpen ? "Collapse panel" : "Expand panel"}
|
|
348
253
|
>
|
|
349
254
|
<span style={chevronStyle}>
|
|
350
255
|
<ChevronDownIcon />
|
|
351
256
|
</span>
|
|
352
|
-
</
|
|
353
|
-
</
|
|
354
|
-
</
|
|
257
|
+
</Button>
|
|
258
|
+
</Stack>
|
|
259
|
+
</Stack>
|
|
355
260
|
|
|
356
261
|
{/* Panel Content */}
|
|
357
262
|
{isOpen && (
|
|
358
|
-
<
|
|
359
|
-
style={{
|
|
360
|
-
overflow: 'auto',
|
|
361
|
-
height: isBottom ? `calc(100% - ${headerHeight}px)` : "100%",
|
|
362
|
-
width: isBottom ? "100%" : `calc(100% - ${headerHeight}px)`,
|
|
363
|
-
}}
|
|
364
|
-
>
|
|
263
|
+
<Box overflow="auto" style={{ height: `calc(100% - ${headerHeight}px)`, width: '100%' }}>
|
|
365
264
|
{children}
|
|
366
|
-
</
|
|
265
|
+
</Box>
|
|
367
266
|
)}
|
|
368
267
|
</div>
|
|
369
268
|
);
|
|
370
269
|
}
|
|
371
270
|
|
|
372
|
-
/**
|
|
373
|
-
* Hook to get panel dock position for layout purposes
|
|
374
|
-
*/
|
|
375
|
-
export function usePanelDock(): "bottom" | "right" {
|
|
376
|
-
const [dock, setDock] = useState<"bottom" | "right">(() => {
|
|
377
|
-
const state = loadPanelState();
|
|
378
|
-
return state.dock;
|
|
379
|
-
});
|
|
380
|
-
|
|
381
|
-
useEffect(() => {
|
|
382
|
-
const handleStorage = () => {
|
|
383
|
-
const state = loadPanelState();
|
|
384
|
-
setDock(state.dock);
|
|
385
|
-
};
|
|
386
|
-
|
|
387
|
-
// Listen for storage changes (from other tabs)
|
|
388
|
-
window.addEventListener("storage", handleStorage);
|
|
389
|
-
|
|
390
|
-
// Also poll for changes since localStorage events don't fire in same tab
|
|
391
|
-
const interval = setInterval(handleStorage, 500);
|
|
392
|
-
|
|
393
|
-
return () => {
|
|
394
|
-
window.removeEventListener("storage", handleStorage);
|
|
395
|
-
clearInterval(interval);
|
|
396
|
-
};
|
|
397
|
-
}, []);
|
|
398
|
-
|
|
399
|
-
return dock;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
// Icon for dock position toggle
|
|
403
|
-
function DockIcon({ dock }: { dock: "bottom" | "right" }) {
|
|
404
|
-
const svgStyle: CSSProperties = { width: '100%', height: '100%' };
|
|
405
|
-
|
|
406
|
-
if (dock === "bottom") {
|
|
407
|
-
// Show icon indicating "dock to right" action
|
|
408
|
-
return (
|
|
409
|
-
<svg style={svgStyle} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5">
|
|
410
|
-
{/* Outer frame */}
|
|
411
|
-
<rect x="1" y="2" width="14" height="12" rx="1" />
|
|
412
|
-
{/* Right panel indicator */}
|
|
413
|
-
<line x1="10" y1="2" x2="10" y2="14" />
|
|
414
|
-
<line x1="10" y1="8" x2="15" y2="8" strokeDasharray="2 1" />
|
|
415
|
-
</svg>
|
|
416
|
-
);
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
// Show icon indicating "dock to bottom" action
|
|
420
|
-
return (
|
|
421
|
-
<svg style={svgStyle} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5">
|
|
422
|
-
{/* Outer frame */}
|
|
423
|
-
<rect x="1" y="2" width="14" height="12" rx="1" />
|
|
424
|
-
{/* Bottom panel indicator */}
|
|
425
|
-
<line x1="1" y1="10" x2="15" y2="10" />
|
|
426
|
-
<line x1="8" y1="10" x2="8" y2="14" strokeDasharray="2 1" />
|
|
427
|
-
</svg>
|
|
428
|
-
);
|
|
429
|
-
}
|
|
430
|
-
|
|
431
271
|
export default ResizablePanel;
|
|
@@ -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-sdk/ui';
|
|
3
|
+
import { Sidebar, Text } from '@fragments-sdk/ui';
|
|
4
4
|
|
|
5
5
|
interface RightSidebarProps {
|
|
6
6
|
fragment: FragmentDefinition;
|
|
@@ -60,15 +60,9 @@ export function RightSidebar({ fragment }: RightSidebarProps) {
|
|
|
60
60
|
return (
|
|
61
61
|
<Sidebar position="right" collapsible="none" style={{ padding: '8px 0' }}>
|
|
62
62
|
<Sidebar.Header>
|
|
63
|
-
<
|
|
64
|
-
fontSize: '11px',
|
|
65
|
-
fontWeight: 500,
|
|
66
|
-
color: 'var(--text-tertiary)',
|
|
67
|
-
textTransform: 'uppercase',
|
|
68
|
-
letterSpacing: '0.05em',
|
|
69
|
-
}}>
|
|
63
|
+
<Text size="2xs" weight="medium" color="tertiary" style={{ textTransform: 'uppercase', letterSpacing: '0.05em' }}>
|
|
70
64
|
On this page
|
|
71
|
-
</
|
|
65
|
+
</Text>
|
|
72
66
|
</Sidebar.Header>
|
|
73
67
|
<Sidebar.Nav aria-label="On this page">
|
|
74
68
|
{tocItems.map((item) => {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Shows animated placeholders while the app is loading.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { Skeleton, Loading } from '@fragments-sdk/ui';
|
|
7
|
+
import { Skeleton, Loading, Stack, Box } from '@fragments-sdk/ui';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Full app skeleton shown during initial load
|
|
@@ -34,18 +34,18 @@ export function AppSkeleton() {
|
|
|
34
34
|
backgroundColor: 'var(--bg-primary)',
|
|
35
35
|
}}
|
|
36
36
|
>
|
|
37
|
-
<
|
|
37
|
+
<Stack direction="row" align="center" gap="sm">
|
|
38
38
|
<Skeleton.Circle size={20} />
|
|
39
39
|
<Skeleton variant="text" width={96} />
|
|
40
40
|
<Skeleton variant="text" width={64} />
|
|
41
|
-
</
|
|
41
|
+
</Stack>
|
|
42
42
|
<Skeleton variant="rect" width={240} height={32} radius="md" />
|
|
43
|
-
<
|
|
43
|
+
<Stack direction="row" align="center" gap="sm">
|
|
44
44
|
<Skeleton variant="rect" width={64} height={28} radius="md" />
|
|
45
45
|
<Skeleton.Circle size={20} />
|
|
46
46
|
<Skeleton.Circle size={20} />
|
|
47
47
|
<Skeleton.Circle size={20} />
|
|
48
|
-
</
|
|
48
|
+
</Stack>
|
|
49
49
|
</div>
|
|
50
50
|
|
|
51
51
|
<aside
|
|
@@ -58,10 +58,10 @@ export function AppSkeleton() {
|
|
|
58
58
|
minHeight: 0,
|
|
59
59
|
}}
|
|
60
60
|
>
|
|
61
|
-
<
|
|
61
|
+
<Box paddingX="md" paddingY="sm">
|
|
62
62
|
<Skeleton variant="rect" height={32} radius="md" />
|
|
63
|
-
</
|
|
64
|
-
<
|
|
63
|
+
</Box>
|
|
64
|
+
<Stack gap="sm" style={{ padding: '0 12px', overflow: 'hidden' }}>
|
|
65
65
|
<Skeleton variant="text" width={68} />
|
|
66
66
|
<Skeleton variant="rect" height={30} radius="md" />
|
|
67
67
|
<Skeleton variant="rect" height={30} radius="md" />
|
|
@@ -70,7 +70,7 @@ export function AppSkeleton() {
|
|
|
70
70
|
<Skeleton variant="rect" height={30} radius="md" />
|
|
71
71
|
<Skeleton variant="rect" height={30} radius="md" />
|
|
72
72
|
<Skeleton variant="rect" width="78%" height={30} radius="md" />
|
|
73
|
-
</
|
|
73
|
+
</Stack>
|
|
74
74
|
<div style={{ marginTop: 'auto', padding: '12px 16px', borderTop: '1px solid var(--border-subtle)' }}>
|
|
75
75
|
<Skeleton variant="text" width={92} />
|
|
76
76
|
</div>
|
|
@@ -131,7 +131,7 @@ export function AppSkeleton() {
|
|
|
131
131
|
padding: '16px 14px',
|
|
132
132
|
}}
|
|
133
133
|
>
|
|
134
|
-
<
|
|
134
|
+
<Stack gap="sm">
|
|
135
135
|
<Skeleton variant="text" width={92} />
|
|
136
136
|
<Skeleton variant="text" width={64} />
|
|
137
137
|
<Skeleton variant="text" width={48} />
|
|
@@ -141,7 +141,7 @@ export function AppSkeleton() {
|
|
|
141
141
|
<Skeleton variant="rect" width="100%" height={26} radius="md" />
|
|
142
142
|
<Skeleton variant="rect" width="84%" height={26} radius="md" />
|
|
143
143
|
<Skeleton variant="rect" width="70%" height={26} radius="md" />
|
|
144
|
-
</
|
|
144
|
+
</Stack>
|
|
145
145
|
</aside>
|
|
146
146
|
</div>
|
|
147
147
|
);
|
|
@@ -152,9 +152,9 @@ export function AppSkeleton() {
|
|
|
152
152
|
*/
|
|
153
153
|
export function PreviewSkeleton() {
|
|
154
154
|
return (
|
|
155
|
-
<
|
|
155
|
+
<Stack direction="row" align="center" justify="center" style={{ padding: '32px' }}>
|
|
156
156
|
<Loading size="md" />
|
|
157
|
-
</
|
|
157
|
+
</Stack>
|
|
158
158
|
);
|
|
159
159
|
}
|
|
160
160
|
|