@fragments-sdk/cli 0.7.11 → 0.7.12
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/package.json
CHANGED
|
@@ -270,11 +270,6 @@ export function App({ fragments }: AppProps) {
|
|
|
270
270
|
}
|
|
271
271
|
}, [copyUrl, success, uiActions]);
|
|
272
272
|
|
|
273
|
-
const focusSearchInput = useCallback(() => {
|
|
274
|
-
searchInputRef.current?.focus();
|
|
275
|
-
searchInputRef.current?.select();
|
|
276
|
-
}, []);
|
|
277
|
-
|
|
278
273
|
// Sorted fragment paths for keyboard navigation
|
|
279
274
|
const sortedFragmentPaths = useMemo(() => {
|
|
280
275
|
return [...fragments]
|
|
@@ -328,7 +323,7 @@ export function App({ fragments }: AppProps) {
|
|
|
328
323
|
toggleResponsive: () => uiActions.setMultiViewport(!uiState.showMultiViewport),
|
|
329
324
|
copyLink: handleCopyLink,
|
|
330
325
|
showHelp: uiActions.toggleShortcutsHelp,
|
|
331
|
-
openSearch:
|
|
326
|
+
openSearch: () => uiActions.setCommandPalette(true),
|
|
332
327
|
escape: () => {
|
|
333
328
|
if (document.activeElement === searchInputRef.current) {
|
|
334
329
|
if (searchQuery) {
|
|
@@ -614,7 +609,6 @@ function HeaderSearch({ value, onChange, inputRef }: HeaderSearchProps) {
|
|
|
614
609
|
placeholder="Search components"
|
|
615
610
|
aria-label="Search components"
|
|
616
611
|
size="sm"
|
|
617
|
-
shortcut="⌘K"
|
|
618
612
|
style={{ width: '240px' }}
|
|
619
613
|
/>
|
|
620
614
|
</Header.Search>
|
|
@@ -723,11 +717,12 @@ function PreviewAside({
|
|
|
723
717
|
</a>
|
|
724
718
|
{variants.map((variant, index) => {
|
|
725
719
|
const active = index === focusedVariantIndex;
|
|
720
|
+
const anchorId = getVariantSectionId(fragment.meta.name, variant.name);
|
|
726
721
|
|
|
727
722
|
return (
|
|
728
723
|
<a
|
|
729
724
|
key={variant.name}
|
|
730
|
-
href=
|
|
725
|
+
href={`#${anchorId}`}
|
|
731
726
|
style={getLinkStyle(active)}
|
|
732
727
|
onClick={(event) => {
|
|
733
728
|
event.preventDefault();
|
|
@@ -336,10 +336,16 @@ export function CommandPalette({
|
|
|
336
336
|
function fuzzyScore(text: string, query: string): number {
|
|
337
337
|
if (!query) return 1;
|
|
338
338
|
|
|
339
|
+
// Require substring match for short queries to avoid scattered single-char noise
|
|
340
|
+
if (query.length <= 3 && !text.includes(query)) {
|
|
341
|
+
return 0;
|
|
342
|
+
}
|
|
343
|
+
|
|
339
344
|
let score = 0;
|
|
340
345
|
let queryIndex = 0;
|
|
341
346
|
let consecutiveBonus = 0;
|
|
342
347
|
let lastMatchIndex = -2;
|
|
348
|
+
let maxGap = 0;
|
|
343
349
|
|
|
344
350
|
for (let i = 0; i < text.length && queryIndex < query.length; i++) {
|
|
345
351
|
if (text[i] === query[queryIndex]) {
|
|
@@ -351,6 +357,11 @@ function fuzzyScore(text: string, query: string): number {
|
|
|
351
357
|
score += consecutiveBonus;
|
|
352
358
|
} else {
|
|
353
359
|
consecutiveBonus = 0;
|
|
360
|
+
// Track max gap between matches
|
|
361
|
+
if (lastMatchIndex >= 0) {
|
|
362
|
+
const gap = i - lastMatchIndex;
|
|
363
|
+
if (gap > maxGap) maxGap = gap;
|
|
364
|
+
}
|
|
354
365
|
}
|
|
355
366
|
|
|
356
367
|
// Bonus for matching at word start
|
|
@@ -366,6 +377,11 @@ function fuzzyScore(text: string, query: string): number {
|
|
|
366
377
|
// Only return score if all query characters were found
|
|
367
378
|
if (queryIndex < query.length) return 0;
|
|
368
379
|
|
|
380
|
+
// Penalize large gaps between matched characters
|
|
381
|
+
if (maxGap > 5) {
|
|
382
|
+
score -= maxGap;
|
|
383
|
+
}
|
|
384
|
+
|
|
369
385
|
// Bonus for shorter results (more relevant)
|
|
370
386
|
score += Math.max(0, 20 - text.length);
|
|
371
387
|
|
|
@@ -375,5 +391,5 @@ function fuzzyScore(text: string, query: string): number {
|
|
|
375
391
|
// Bonus for starts with
|
|
376
392
|
if (text.startsWith(query)) score += 30;
|
|
377
393
|
|
|
378
|
-
return score;
|
|
394
|
+
return Math.max(score, 0) || 0;
|
|
379
395
|
}
|
|
@@ -162,91 +162,88 @@ export const IsolatedPreviewFrame = memo(function IsolatedPreviewFrame({
|
|
|
162
162
|
onMouseLeave={() => setIsHovered(false)}
|
|
163
163
|
>
|
|
164
164
|
{/* Skeleton loading overlay (initial load) */}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
</div>
|
|
165
|
+
{showSkeleton && (
|
|
166
|
+
<div
|
|
167
|
+
style={{
|
|
168
|
+
position: 'absolute',
|
|
169
|
+
inset: 0,
|
|
170
|
+
zIndex: 10,
|
|
171
|
+
background: 'var(--bg-primary, rgba(255, 255, 255, 0.95))',
|
|
172
|
+
}}
|
|
173
|
+
>
|
|
174
|
+
<PreviewSkeleton />
|
|
175
|
+
</div>
|
|
176
|
+
)}
|
|
178
177
|
|
|
179
178
|
{/* Spinner overlay (subsequent renders) */}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
<span>Rendering...</span>
|
|
179
|
+
{showSpinner && (
|
|
180
|
+
<div
|
|
181
|
+
style={{
|
|
182
|
+
position: 'absolute',
|
|
183
|
+
inset: 0,
|
|
184
|
+
zIndex: 10,
|
|
185
|
+
display: 'flex',
|
|
186
|
+
alignItems: 'center',
|
|
187
|
+
justifyContent: 'center',
|
|
188
|
+
background: 'color-mix(in srgb, var(--bg-primary, white) 80%, transparent)',
|
|
189
|
+
}}
|
|
190
|
+
>
|
|
191
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 8, color: 'var(--text-tertiary, #6b7280)', fontSize: 14 }}>
|
|
192
|
+
<LoadingSpinner />
|
|
193
|
+
<span>Rendering...</span>
|
|
194
|
+
</div>
|
|
197
195
|
</div>
|
|
198
|
-
|
|
196
|
+
)}
|
|
199
197
|
|
|
200
198
|
{/* Error overlay */}
|
|
201
|
-
|
|
202
|
-
style={{
|
|
203
|
-
position: 'absolute',
|
|
204
|
-
inset: 0,
|
|
205
|
-
zIndex: 10,
|
|
206
|
-
display: 'flex',
|
|
207
|
-
alignItems: 'center',
|
|
208
|
-
justifyContent: 'center',
|
|
209
|
-
padding: '16px',
|
|
210
|
-
transition: 'opacity 150ms',
|
|
211
|
-
opacity: frameError && !isLoading ? 1 : 0,
|
|
212
|
-
pointerEvents: frameError && !isLoading ? 'auto' : 'none',
|
|
213
|
-
background: 'rgba(254, 242, 242, 0.95)',
|
|
214
|
-
}}
|
|
215
|
-
>
|
|
199
|
+
{frameError && !isLoading && (
|
|
216
200
|
<div
|
|
217
201
|
style={{
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
202
|
+
position: 'absolute',
|
|
203
|
+
inset: 0,
|
|
204
|
+
zIndex: 10,
|
|
205
|
+
display: 'flex',
|
|
206
|
+
alignItems: 'center',
|
|
207
|
+
justifyContent: 'center',
|
|
208
|
+
padding: '16px',
|
|
209
|
+
background: 'rgba(254, 242, 242, 0.95)',
|
|
223
210
|
}}
|
|
224
211
|
>
|
|
225
|
-
<div
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
212
|
+
<div
|
|
213
|
+
style={{
|
|
214
|
+
background: 'white',
|
|
215
|
+
border: '1px solid #fecaca',
|
|
216
|
+
borderRadius: 8,
|
|
217
|
+
padding: 16,
|
|
218
|
+
maxWidth: 400,
|
|
219
|
+
}}
|
|
220
|
+
>
|
|
221
|
+
<div style={{ color: '#dc2626', fontWeight: 500, marginBottom: 8 }}>
|
|
222
|
+
Preview Error
|
|
223
|
+
</div>
|
|
224
|
+
<div style={{ color: '#991b1b', fontSize: 13, marginBottom: retryCount < MAX_RETRIES ? 12 : 0 }}>
|
|
225
|
+
{frameError}
|
|
226
|
+
</div>
|
|
227
|
+
{retryCount < MAX_RETRIES && (
|
|
228
|
+
<button
|
|
229
|
+
onClick={handleRetry}
|
|
230
|
+
style={{
|
|
231
|
+
padding: '6px 12px',
|
|
232
|
+
fontSize: 13,
|
|
233
|
+
fontWeight: 500,
|
|
234
|
+
color: 'white',
|
|
235
|
+
background: '#dc2626',
|
|
236
|
+
border: 'none',
|
|
237
|
+
borderRadius: 6,
|
|
238
|
+
cursor: 'pointer',
|
|
239
|
+
}}
|
|
240
|
+
>
|
|
241
|
+
Retry ({MAX_RETRIES - retryCount} remaining)
|
|
242
|
+
</button>
|
|
243
|
+
)}
|
|
230
244
|
</div>
|
|
231
|
-
{retryCount < MAX_RETRIES && (
|
|
232
|
-
<button
|
|
233
|
-
onClick={handleRetry}
|
|
234
|
-
style={{
|
|
235
|
-
padding: '6px 12px',
|
|
236
|
-
fontSize: 13,
|
|
237
|
-
fontWeight: 500,
|
|
238
|
-
color: 'white',
|
|
239
|
-
background: '#dc2626',
|
|
240
|
-
border: 'none',
|
|
241
|
-
borderRadius: 6,
|
|
242
|
-
cursor: 'pointer',
|
|
243
|
-
}}
|
|
244
|
-
>
|
|
245
|
-
Retry ({MAX_RETRIES - retryCount} remaining)
|
|
246
|
-
</button>
|
|
247
|
-
)}
|
|
248
245
|
</div>
|
|
249
|
-
|
|
246
|
+
)}
|
|
250
247
|
|
|
251
248
|
{/* The iframe */}
|
|
252
249
|
<iframe
|
|
@@ -14,16 +14,26 @@ function fuzzyMatch(text: string, pattern: string): FuzzyMatch | null {
|
|
|
14
14
|
const textLower = text.toLowerCase();
|
|
15
15
|
const patternLower = pattern.toLowerCase();
|
|
16
16
|
|
|
17
|
+
// Require substring match for short queries (<=3 chars) to avoid scattered single-char noise
|
|
18
|
+
if (patternLower.length <= 3 && !textLower.includes(patternLower)) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
17
22
|
const indices: number[] = [];
|
|
18
23
|
let patternIdx = 0;
|
|
19
24
|
let score = 0;
|
|
20
25
|
let consecutiveBonus = 0;
|
|
26
|
+
let maxGap = 0;
|
|
21
27
|
|
|
22
28
|
for (let i = 0; i < textLower.length && patternIdx < patternLower.length; i++) {
|
|
23
29
|
if (textLower[i] === patternLower[patternIdx]) {
|
|
24
30
|
indices.push(i);
|
|
25
|
-
if (indices.length > 1
|
|
26
|
-
|
|
31
|
+
if (indices.length > 1) {
|
|
32
|
+
const gap = i - indices[indices.length - 2];
|
|
33
|
+
if (gap === 1) {
|
|
34
|
+
consecutiveBonus += 5;
|
|
35
|
+
}
|
|
36
|
+
if (gap > maxGap) maxGap = gap;
|
|
27
37
|
}
|
|
28
38
|
if (i === 0 || text[i - 1] === ' ' || text[i - 1] === '-' || text[i - 1] === '_') {
|
|
29
39
|
score += 10;
|
|
@@ -36,9 +46,19 @@ function fuzzyMatch(text: string, pattern: string): FuzzyMatch | null {
|
|
|
36
46
|
return null;
|
|
37
47
|
}
|
|
38
48
|
|
|
49
|
+
// Penalize large gaps between matched characters
|
|
50
|
+
if (maxGap > 5) {
|
|
51
|
+
score -= maxGap * 2;
|
|
52
|
+
}
|
|
53
|
+
|
|
39
54
|
score += consecutiveBonus;
|
|
40
55
|
score += (patternLower.length / textLower.length) * 20;
|
|
41
56
|
|
|
57
|
+
// Reject very low scores (scattered single-char matches)
|
|
58
|
+
if (score <= 0 && patternLower.length > 1) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
42
62
|
return { score, indices };
|
|
43
63
|
}
|
|
44
64
|
|
|
@@ -364,7 +384,7 @@ export function LeftSidebar({ fragments, activeFragment, searchQuery, onSelect,
|
|
|
364
384
|
<Sidebar.Footer>
|
|
365
385
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', width: '100%' }}>
|
|
366
386
|
<Text size="xs" color="tertiary">
|
|
367
|
-
{isFilterActive ? `${flatItems.length}
|
|
387
|
+
{isFilterActive || searchResults ? `${flatItems.length} of ${fragments.length}` : fragments.length} components
|
|
368
388
|
</Text>
|
|
369
389
|
<Sidebar.CollapseToggle />
|
|
370
390
|
</div>
|
|
@@ -262,7 +262,7 @@ export const SHORTCUTS = [
|
|
|
262
262
|
{ keys: ["p"], description: "Toggle addons panel" },
|
|
263
263
|
{ keys: ["m"], description: "Matrix view" },
|
|
264
264
|
{ keys: ["v"], description: "Responsive view" },
|
|
265
|
-
{ keys: ["/", "⌘K"], description: "
|
|
265
|
+
{ keys: ["/", "⌘K"], description: "Command palette" },
|
|
266
266
|
{ keys: ["?"], description: "Show shortcuts" },
|
|
267
267
|
{ keys: ["Esc"], description: "Close / Clear" },
|
|
268
268
|
];
|