@fragments-sdk/cli 0.5.2 → 0.7.0
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 +996 -79
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-ICAIQ57V.js → chunk-6JBGU74P.js} +5 -3
- package/dist/chunk-6JBGU74P.js.map +1 -0
- package/dist/chunk-7OPWMLOE.js +1625 -0
- package/dist/chunk-7OPWMLOE.js.map +1 -0
- package/dist/{chunk-2H2JAA3U.js → chunk-CVXKXVOY.js} +3 -3
- package/dist/{chunk-2H2JAA3U.js.map → chunk-CVXKXVOY.js.map} +1 -1
- package/dist/{chunk-IOJE35DZ.js → chunk-NWQ4CJOQ.js} +3 -3
- package/dist/{chunk-2DJH4F4P.js → chunk-RVRTRESS.js} +3 -3
- package/dist/{chunk-V7YLRR4C.js → chunk-TJ34N7C7.js} +41 -4
- package/dist/{chunk-V7YLRR4C.js.map → chunk-TJ34N7C7.js.map} +1 -1
- package/dist/{chunk-XNWDI6UT.js → chunk-XHUDJNN3.js} +5 -5
- package/dist/{core-DKHB7FYV.js → core-W2HYIQW6.js} +4 -4
- package/dist/{generate-KL24VZVD.js → generate-LMTISDIJ.js} +5 -5
- package/dist/index.d.ts +1 -0
- package/dist/index.js +15 -7
- package/dist/index.js.map +1 -1
- package/dist/{init-NION5S3M.js → init-7CHRKQ7P.js} +5 -5
- package/dist/mcp-bin.js +8 -220
- package/dist/mcp-bin.js.map +1 -1
- package/dist/scan-WY23TJCP.js +12 -0
- package/dist/{service-RWUMZ3EW.js → service-T2L7VLTE.js} +5 -5
- package/dist/static-viewer-GBR7YNF3.js +12 -0
- package/dist/{test-ECPEXFDN.js → test-OJRXNDO2.js} +4 -4
- package/dist/{tokens-ITADYVPF.js → tokens-3BWDESVM.js} +6 -6
- package/dist/viewer-SUFOISZM.js +1822 -0
- package/dist/viewer-SUFOISZM.js.map +1 -0
- package/package.json +6 -5
- package/src/bin.ts +31 -0
- package/src/build.ts +147 -13
- package/src/cli-commands.ts +18 -0
- package/src/commands/__tests__/a11y-scoring.test.ts +278 -0
- package/src/commands/a11y-report.ts +625 -0
- package/src/commands/a11y.ts +168 -14
- package/src/commands/build.ts +16 -0
- package/src/commands/graph.ts +274 -0
- package/src/core/auto-props.ts +464 -0
- package/src/core/composition.ts +64 -1
- package/src/core/graph-extractor.test.ts +542 -0
- package/src/core/graph-extractor.ts +601 -0
- package/src/core/importAnalyzer.ts +5 -0
- package/src/core/schema.ts +2 -0
- package/src/core/types.ts +3 -1
- package/src/index.ts +4 -0
- package/src/mcp/server.ts +13 -220
- package/src/theme/__tests__/component-contrast.test.ts +338 -0
- package/src/theme/__tests__/contrast-validation.test.ts +326 -0
- package/src/theme/contrast.test.ts +331 -0
- package/src/theme/contrast.ts +246 -0
- package/src/theme/generator.ts +213 -1
- package/src/theme/index.ts +16 -0
- package/src/theme/types.ts +51 -0
- package/src/viewer/__tests__/a11y-fixes.test.ts +358 -0
- package/src/viewer/__tests__/viewer-integration.test.ts +2 -7
- package/src/viewer/components/AccessibilityPanel.tsx +493 -433
- package/src/viewer/components/ActionCapture.tsx +1 -1
- package/src/viewer/components/ActionsPanel.tsx +142 -183
- package/src/viewer/components/App.tsx +276 -183
- package/src/viewer/components/BottomPanel.tsx +40 -80
- package/src/viewer/components/CodePanel.tsx +9 -87
- package/src/viewer/components/CommandPalette.tsx +117 -74
- package/src/viewer/components/ComponentGraph.tsx +143 -126
- package/src/viewer/components/ComponentHeader.tsx +46 -43
- package/src/viewer/components/ContractPanel.tsx +124 -117
- package/src/viewer/components/ErrorBoundary.tsx +47 -35
- package/src/viewer/components/FigmaEmbed.tsx +18 -13
- package/src/viewer/components/FragmentEditor.tsx +126 -63
- package/src/viewer/components/HealthDashboard.tsx +146 -171
- package/src/viewer/components/HmrStatusIndicator.tsx +31 -41
- package/src/viewer/components/Icons.tsx +151 -98
- package/src/viewer/components/InteractionsPanel.tsx +317 -264
- package/src/viewer/components/IsolatedPreviewFrame.tsx +52 -27
- package/src/viewer/components/IsolatedRender.tsx +12 -6
- package/src/viewer/components/KeyboardShortcutsHelp.tsx +34 -70
- package/src/viewer/components/LandingPage.tsx +285 -305
- package/src/viewer/components/Layout.tsx +12 -10
- package/src/viewer/components/LeftSidebar.tsx +103 -155
- package/src/viewer/components/MultiViewportPreview.tsx +254 -63
- package/src/viewer/components/PreviewArea.tsx +113 -44
- package/src/viewer/components/PreviewFrameHost.tsx +36 -6
- package/src/viewer/components/PreviewPane.tsx +2 -3
- package/src/viewer/components/PreviewToolbar.tsx +109 -105
- package/src/viewer/components/PropsEditor.tsx +154 -74
- package/src/viewer/components/PropsTable.tsx +95 -82
- package/src/viewer/components/RelationsSection.tsx +71 -40
- package/src/viewer/components/ResizablePanel.tsx +158 -55
- package/src/viewer/components/RightSidebar.tsx +46 -56
- package/src/viewer/components/ScreenshotButton.tsx +12 -12
- package/src/viewer/components/SkeletonLoader.tsx +99 -83
- package/src/viewer/components/StoryRenderer.tsx +4 -11
- package/src/viewer/components/Toast.tsx +3 -67
- package/src/viewer/components/TokenStylePanel.tsx +136 -118
- package/src/viewer/components/UsageSection.tsx +26 -26
- package/src/viewer/components/VariantMatrix.tsx +140 -47
- package/src/viewer/components/VariantTabs.tsx +24 -68
- package/src/viewer/components/ViewportSelector.tsx +121 -114
- package/src/viewer/constants/ui.ts +23 -22
- package/src/viewer/entry.tsx +8 -3
- package/src/viewer/index.ts +3 -6
- package/src/viewer/preview-frame.html +43 -18
- package/src/viewer/server.ts +7 -16
- package/src/viewer/styles/globals.css +46 -85
- package/src/viewer/utils/a11y-fixes.ts +53 -30
- package/dist/chunk-ICAIQ57V.js.map +0 -1
- package/dist/chunk-U4GQ2JTD.js +0 -832
- package/dist/chunk-U4GQ2JTD.js.map +0 -1
- package/dist/scan-ESEXV7LF.js +0 -12
- package/dist/static-viewer-O37MJ5B6.js +0 -12
- package/dist/viewer-YDGFDTK5.js +0 -11104
- package/dist/viewer-YDGFDTK5.js.map +0 -1
- package/src/viewer/postcss.config.js +0 -6
- package/src/viewer/tailwind.config.js +0 -37
- /package/dist/{chunk-IOJE35DZ.js.map → chunk-NWQ4CJOQ.js.map} +0 -0
- /package/dist/{chunk-2DJH4F4P.js.map → chunk-RVRTRESS.js.map} +0 -0
- /package/dist/{chunk-XNWDI6UT.js.map → chunk-XHUDJNN3.js.map} +0 -0
- /package/dist/{core-DKHB7FYV.js.map → core-W2HYIQW6.js.map} +0 -0
- /package/dist/{generate-KL24VZVD.js.map → generate-LMTISDIJ.js.map} +0 -0
- /package/dist/{init-NION5S3M.js.map → init-7CHRKQ7P.js.map} +0 -0
- /package/dist/{scan-ESEXV7LF.js.map → scan-WY23TJCP.js.map} +0 -0
- /package/dist/{service-RWUMZ3EW.js.map → service-T2L7VLTE.js.map} +0 -0
- /package/dist/{static-viewer-O37MJ5B6.js.map → static-viewer-GBR7YNF3.js.map} +0 -0
- /package/dist/{test-ECPEXFDN.js.map → test-OJRXNDO2.js.map} +0 -0
- /package/dist/{tokens-ITADYVPF.js.map → tokens-3BWDESVM.js.map} +0 -0
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { useState, useCallback, useRef, useEffect } from "react";
|
|
13
|
-
import
|
|
13
|
+
import { Button, Stack, Text, Badge, EmptyState, CodeBlock, Alert } from "@fragments/ui";
|
|
14
14
|
import type { PlayFunction, PlayFunctionContext, SegmentVariant } from "../../core/index.js";
|
|
15
15
|
import {
|
|
16
16
|
PlayIcon,
|
|
@@ -63,6 +63,44 @@ interface InteractionsPanelProps {
|
|
|
63
63
|
currentArgs?: Record<string, unknown>;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
// Status color helpers
|
|
67
|
+
const STATUS_COLORS: Record<string, { bg: string; border: string; text: string; icon: string }> = {
|
|
68
|
+
running: {
|
|
69
|
+
bg: 'color-mix(in srgb, #3b82f6 8%, transparent)',
|
|
70
|
+
border: '1px solid color-mix(in srgb, #3b82f6 25%, transparent)',
|
|
71
|
+
text: '#1d4ed8',
|
|
72
|
+
icon: '#2563eb',
|
|
73
|
+
},
|
|
74
|
+
paused: {
|
|
75
|
+
bg: 'color-mix(in srgb, #f97316 8%, transparent)',
|
|
76
|
+
border: '1px solid color-mix(in srgb, #f97316 25%, transparent)',
|
|
77
|
+
text: '#c2410c',
|
|
78
|
+
icon: '#ea580c',
|
|
79
|
+
},
|
|
80
|
+
passed: {
|
|
81
|
+
bg: 'color-mix(in srgb, #22c55e 8%, transparent)',
|
|
82
|
+
border: '1px solid color-mix(in srgb, #22c55e 25%, transparent)',
|
|
83
|
+
text: '#15803d',
|
|
84
|
+
icon: '#16a34a',
|
|
85
|
+
},
|
|
86
|
+
failed: {
|
|
87
|
+
bg: 'color-mix(in srgb, #ef4444 8%, transparent)',
|
|
88
|
+
border: '1px solid color-mix(in srgb, #ef4444 25%, transparent)',
|
|
89
|
+
text: '#b91c1c',
|
|
90
|
+
icon: '#dc2626',
|
|
91
|
+
},
|
|
92
|
+
pending: {
|
|
93
|
+
bg: 'var(--bg-secondary)',
|
|
94
|
+
border: '1px solid var(--border)',
|
|
95
|
+
text: 'var(--text-muted)',
|
|
96
|
+
icon: 'var(--text-muted)',
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
function getStatusColors(status: string) {
|
|
101
|
+
return STATUS_COLORS[status] || STATUS_COLORS.pending;
|
|
102
|
+
}
|
|
103
|
+
|
|
66
104
|
export function InteractionsPanel({
|
|
67
105
|
variant,
|
|
68
106
|
previewSelector = '[data-preview-container="true"]',
|
|
@@ -80,6 +118,7 @@ export function InteractionsPanel({
|
|
|
80
118
|
currentStepIndex: -1,
|
|
81
119
|
breakpoints: new Set(),
|
|
82
120
|
});
|
|
121
|
+
const [hoveredBreakpoint, setHoveredBreakpoint] = useState<number | null>(null);
|
|
83
122
|
const abortControllerRef = useRef<AbortController | null>(null);
|
|
84
123
|
const resumeResolverRef = useRef<(() => void) | null>(null);
|
|
85
124
|
|
|
@@ -388,27 +427,24 @@ export function InteractionsPanel({
|
|
|
388
427
|
// No play function available
|
|
389
428
|
if (!hasPlayFunction) {
|
|
390
429
|
return (
|
|
391
|
-
<div
|
|
392
|
-
<div
|
|
393
|
-
<
|
|
394
|
-
<PlayIcon
|
|
395
|
-
Interactions
|
|
396
|
-
</
|
|
430
|
+
<div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
|
|
431
|
+
<div style={{ padding: '16px', borderBottom: '1px solid var(--border)' }}>
|
|
432
|
+
<Stack direction="row" align="center" gap="sm">
|
|
433
|
+
<PlayIcon style={{ width: 16, height: 16 }} />
|
|
434
|
+
<Text weight="medium">Interactions</Text>
|
|
435
|
+
</Stack>
|
|
397
436
|
</div>
|
|
398
|
-
<div
|
|
399
|
-
<
|
|
400
|
-
<
|
|
401
|
-
<PlayIcon
|
|
402
|
-
</
|
|
403
|
-
<
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
<div className="mt-4 p-3 bg-gray-50 dark:bg-gray-800/50 rounded-lg text-left">
|
|
410
|
-
<pre className="text-xs text-gray-600 dark:text-gray-400 overflow-x-auto">
|
|
411
|
-
{`export const Default = {
|
|
437
|
+
<div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '32px' }}>
|
|
438
|
+
<EmptyState>
|
|
439
|
+
<EmptyState.Icon>
|
|
440
|
+
<PlayIcon style={{ width: 24, height: 24 }} />
|
|
441
|
+
</EmptyState.Icon>
|
|
442
|
+
<EmptyState.Title>No interactions defined</EmptyState.Title>
|
|
443
|
+
<EmptyState.Description>
|
|
444
|
+
This variant doesn't have a play function. Add a <code style={{ padding: '2px 4px', background: 'var(--bg-secondary)', borderRadius: '4px', fontSize: '12px' }}>play</code> function to your Storybook story to enable interaction testing.
|
|
445
|
+
</EmptyState.Description>
|
|
446
|
+
<div style={{ marginTop: '16px', width: '100%' }}>
|
|
447
|
+
<CodeBlock language="typescript">{`export const Default = {
|
|
412
448
|
play: async ({ canvasElement, step }) => {
|
|
413
449
|
const canvas = within(canvasElement);
|
|
414
450
|
|
|
@@ -422,297 +458,314 @@ export function InteractionsPanel({
|
|
|
422
458
|
canvas.getByText('Clicked!')
|
|
423
459
|
).toBeInTheDocument();
|
|
424
460
|
}
|
|
425
|
-
};`}
|
|
426
|
-
</pre>
|
|
461
|
+
};`}</CodeBlock>
|
|
427
462
|
</div>
|
|
428
|
-
</
|
|
463
|
+
</EmptyState>
|
|
429
464
|
</div>
|
|
430
465
|
</div>
|
|
431
466
|
);
|
|
432
467
|
}
|
|
433
468
|
|
|
434
469
|
return (
|
|
435
|
-
<div
|
|
470
|
+
<div style={{ height: '100%', display: 'flex', flexDirection: 'column' }} data-interactions-panel>
|
|
436
471
|
{/* Header */}
|
|
437
|
-
<div
|
|
438
|
-
<
|
|
439
|
-
<
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
{formatDuration(result.duration)}
|
|
446
|
-
</span>
|
|
447
|
-
)}
|
|
448
|
-
|
|
449
|
-
{/* Debug mode toggle */}
|
|
450
|
-
<button
|
|
451
|
-
onClick={toggleDebugMode}
|
|
452
|
-
className={clsx(
|
|
453
|
-
"p-1.5 rounded-md transition-colors",
|
|
454
|
-
debugState.mode === 'debug'
|
|
455
|
-
? "bg-orange-100 dark:bg-orange-900/30 text-orange-600 dark:text-orange-400"
|
|
456
|
-
: "text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800"
|
|
472
|
+
<div style={{ padding: '16px', borderBottom: '1px solid var(--border)' }}>
|
|
473
|
+
<Stack direction="row" align="center" justify="between">
|
|
474
|
+
<Stack direction="row" align="center" gap="sm">
|
|
475
|
+
<PlayIcon style={{ width: 16, height: 16 }} />
|
|
476
|
+
<Text weight="medium">Interactions</Text>
|
|
477
|
+
</Stack>
|
|
478
|
+
<Stack direction="row" align="center" gap="sm">
|
|
479
|
+
{result.duration !== undefined && (
|
|
480
|
+
<Text size="xs" color="tertiary">{formatDuration(result.duration)}</Text>
|
|
457
481
|
)}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
<
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
className="p-1.5 text-blue-600 dark:text-blue-400 hover:bg-blue-100 dark:hover:bg-blue-900/30 rounded-md transition-colors"
|
|
476
|
-
title="Step over (F10)"
|
|
477
|
-
>
|
|
478
|
-
<StepOverIcon className="w-4 h-4" />
|
|
479
|
-
</button>
|
|
480
|
-
</>
|
|
481
|
-
)}
|
|
482
|
-
|
|
483
|
-
<button
|
|
484
|
-
onClick={() => runInteractions(debugState.mode === 'debug')}
|
|
485
|
-
disabled={result.status === "running"}
|
|
486
|
-
className={clsx(
|
|
487
|
-
"flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-colors",
|
|
488
|
-
result.status === "running" || result.status === "paused"
|
|
489
|
-
? "bg-gray-100 dark:bg-gray-800 text-gray-400 cursor-not-allowed"
|
|
490
|
-
: "bg-blue-600 hover:bg-blue-700 text-white"
|
|
491
|
-
)}
|
|
492
|
-
>
|
|
493
|
-
{result.status === "running" ? (
|
|
494
|
-
<>
|
|
495
|
-
<LoadingIcon className="w-4 h-4 animate-spin" />
|
|
496
|
-
Running...
|
|
497
|
-
</>
|
|
498
|
-
) : result.status === "paused" ? (
|
|
499
|
-
<>
|
|
500
|
-
<PauseIcon className="w-4 h-4" />
|
|
501
|
-
Paused
|
|
502
|
-
</>
|
|
503
|
-
) : result.status === "idle" ? (
|
|
504
|
-
<>
|
|
505
|
-
<PlayIcon className="w-4 h-4" />
|
|
506
|
-
{debugState.mode === 'debug' ? 'Debug' : 'Run'}
|
|
507
|
-
</>
|
|
508
|
-
) : (
|
|
482
|
+
|
|
483
|
+
{/* Debug mode toggle */}
|
|
484
|
+
<Button
|
|
485
|
+
onClick={toggleDebugMode}
|
|
486
|
+
variant="ghost"
|
|
487
|
+
size="sm"
|
|
488
|
+
title={debugState.mode === 'debug' ? "Exit debug mode" : "Enable debug mode (F5 to run with debugger)"}
|
|
489
|
+
style={{
|
|
490
|
+
color: debugState.mode === 'debug' ? '#ea580c' : undefined,
|
|
491
|
+
background: debugState.mode === 'debug' ? 'color-mix(in srgb, #f97316 10%, transparent)' : undefined,
|
|
492
|
+
}}
|
|
493
|
+
>
|
|
494
|
+
<BugIcon style={{ width: 16, height: 16 }} />
|
|
495
|
+
</Button>
|
|
496
|
+
|
|
497
|
+
{/* Debug controls when paused */}
|
|
498
|
+
{debugState.isPaused && (
|
|
509
499
|
<>
|
|
510
|
-
<
|
|
511
|
-
|
|
500
|
+
<Button
|
|
501
|
+
onClick={handleResume}
|
|
502
|
+
variant="ghost"
|
|
503
|
+
size="sm"
|
|
504
|
+
title="Continue (F8)"
|
|
505
|
+
style={{ color: '#16a34a' }}
|
|
506
|
+
>
|
|
507
|
+
<ContinueIcon style={{ width: 16, height: 16 }} />
|
|
508
|
+
</Button>
|
|
509
|
+
<Button
|
|
510
|
+
onClick={handleStepOver}
|
|
511
|
+
variant="ghost"
|
|
512
|
+
size="sm"
|
|
513
|
+
title="Step over (F10)"
|
|
514
|
+
style={{ color: '#2563eb' }}
|
|
515
|
+
>
|
|
516
|
+
<StepOverIcon style={{ width: 16, height: 16 }} />
|
|
517
|
+
</Button>
|
|
512
518
|
</>
|
|
513
519
|
)}
|
|
514
|
-
|
|
515
|
-
|
|
520
|
+
|
|
521
|
+
<Button
|
|
522
|
+
onClick={() => runInteractions(debugState.mode === 'debug')}
|
|
523
|
+
disabled={result.status === "running"}
|
|
524
|
+
variant={result.status === "running" || result.status === "paused" ? "outline" : "solid"}
|
|
525
|
+
size="sm"
|
|
526
|
+
style={
|
|
527
|
+
result.status === "running" || result.status === "paused"
|
|
528
|
+
? { background: 'var(--bg-secondary)', color: 'var(--text-muted)', cursor: 'not-allowed' }
|
|
529
|
+
: { background: 'var(--color-accent)', color: '#fff' }
|
|
530
|
+
}
|
|
531
|
+
>
|
|
532
|
+
{result.status === "running" ? (
|
|
533
|
+
<>
|
|
534
|
+
<LoadingIcon style={{ width: 16, height: 16, animation: 'spin 1s linear infinite' }} />
|
|
535
|
+
Running...
|
|
536
|
+
</>
|
|
537
|
+
) : result.status === "paused" ? (
|
|
538
|
+
<>
|
|
539
|
+
<PauseIcon style={{ width: 16, height: 16 }} />
|
|
540
|
+
Paused
|
|
541
|
+
</>
|
|
542
|
+
) : result.status === "idle" ? (
|
|
543
|
+
<>
|
|
544
|
+
<PlayIcon style={{ width: 16, height: 16 }} />
|
|
545
|
+
{debugState.mode === 'debug' ? 'Debug' : 'Run'}
|
|
546
|
+
</>
|
|
547
|
+
) : (
|
|
548
|
+
<>
|
|
549
|
+
<RefreshIcon style={{ width: 16, height: 16 }} />
|
|
550
|
+
Rerun
|
|
551
|
+
</>
|
|
552
|
+
)}
|
|
553
|
+
</Button>
|
|
554
|
+
</Stack>
|
|
555
|
+
</Stack>
|
|
516
556
|
</div>
|
|
517
557
|
|
|
518
558
|
{/* Results */}
|
|
519
|
-
<div
|
|
559
|
+
<div style={{ flex: 1, overflowY: 'auto' }}>
|
|
520
560
|
{result.status === "idle" ? (
|
|
521
|
-
<div
|
|
522
|
-
<
|
|
561
|
+
<div style={{ padding: '32px', textAlign: 'center' }}>
|
|
562
|
+
<Text size="sm" color="tertiary">
|
|
523
563
|
Click "Run" to execute the interaction tests
|
|
524
|
-
</
|
|
564
|
+
</Text>
|
|
525
565
|
</div>
|
|
526
566
|
) : (
|
|
527
|
-
<
|
|
567
|
+
<Stack direction="column" gap="sm" style={{ padding: '16px' }}>
|
|
528
568
|
{/* Overall status */}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
result.status ===
|
|
533
|
-
result.status ===
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
</
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
{result.steps.filter((s) => s.status === "passed").length}/{result.steps.length} steps
|
|
568
|
-
</span>
|
|
569
|
-
</div>
|
|
569
|
+
{(() => {
|
|
570
|
+
const colors = getStatusColors(result.status);
|
|
571
|
+
const alertVariant = result.status === 'passed' ? 'success'
|
|
572
|
+
: result.status === 'failed' ? 'error'
|
|
573
|
+
: result.status === 'paused' ? 'warning'
|
|
574
|
+
: 'info';
|
|
575
|
+
return (
|
|
576
|
+
<Alert variant={alertVariant}>
|
|
577
|
+
<Alert.Body>
|
|
578
|
+
<Stack direction="row" align="center" justify="between" style={{ width: '100%' }}>
|
|
579
|
+
<Stack direction="row" align="center" gap="sm">
|
|
580
|
+
{result.status === "running" && (
|
|
581
|
+
<LoadingIcon style={{ width: 20, height: 20, animation: 'spin 1s linear infinite', color: colors.icon }} />
|
|
582
|
+
)}
|
|
583
|
+
{result.status === "paused" && (
|
|
584
|
+
<PauseIcon style={{ width: 20, height: 20, color: colors.icon }} />
|
|
585
|
+
)}
|
|
586
|
+
{result.status === "passed" && (
|
|
587
|
+
<CheckIcon style={{ width: 20, height: 20, color: colors.icon }} />
|
|
588
|
+
)}
|
|
589
|
+
{result.status === "failed" && (
|
|
590
|
+
<XIcon style={{ width: 20, height: 20, color: colors.icon }} />
|
|
591
|
+
)}
|
|
592
|
+
<Text weight="medium" style={{ color: colors.text }}>
|
|
593
|
+
{result.status === "running" && "Running interactions..."}
|
|
594
|
+
{result.status === "paused" && "Paused at breakpoint"}
|
|
595
|
+
{result.status === "passed" && "All interactions passed"}
|
|
596
|
+
{result.status === "failed" && "Interactions failed"}
|
|
597
|
+
</Text>
|
|
598
|
+
</Stack>
|
|
599
|
+
<Text size="xs" color="tertiary">
|
|
600
|
+
{result.steps.filter((s) => s.status === "passed").length}/{result.steps.length} steps
|
|
601
|
+
</Text>
|
|
602
|
+
</Stack>
|
|
603
|
+
</Alert.Body>
|
|
604
|
+
</Alert>
|
|
605
|
+
);
|
|
606
|
+
})()}
|
|
570
607
|
|
|
571
608
|
{/* Steps */}
|
|
572
609
|
{result.steps.length > 0 && (
|
|
573
|
-
<
|
|
574
|
-
<
|
|
575
|
-
<
|
|
610
|
+
<Stack direction="column" gap="xs" style={{ marginTop: '16px' }}>
|
|
611
|
+
<Stack direction="row" align="center" justify="between" style={{ marginBottom: '8px' }}>
|
|
612
|
+
<Text size="xs" weight="medium" color="tertiary" style={{ textTransform: 'uppercase', letterSpacing: '0.05em' }}>
|
|
576
613
|
Steps
|
|
577
|
-
</
|
|
614
|
+
</Text>
|
|
578
615
|
{debugState.mode === 'debug' && (
|
|
579
|
-
<
|
|
616
|
+
<Text size="xs" style={{ color: '#ea580c' }}>
|
|
580
617
|
Debug mode - click gutter to set breakpoints
|
|
581
|
-
</
|
|
618
|
+
</Text>
|
|
582
619
|
)}
|
|
583
|
-
</
|
|
584
|
-
{result.steps.map((step, index) =>
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
620
|
+
</Stack>
|
|
621
|
+
{result.steps.map((step, index) => {
|
|
622
|
+
const stepColors = getStatusColors(step.status);
|
|
623
|
+
return (
|
|
624
|
+
<div
|
|
625
|
+
key={index}
|
|
626
|
+
style={{
|
|
627
|
+
borderRadius: '8px',
|
|
628
|
+
border: step.status === 'passed' ? '1px solid var(--border)' : stepColors.border,
|
|
629
|
+
transition: 'background 0.15s',
|
|
630
|
+
display: 'flex',
|
|
631
|
+
background: step.status === 'passed' ? 'var(--bg-primary)' : stepColors.bg,
|
|
632
|
+
outline: debugState.currentStepIndex === index ? '2px solid #f97316' : undefined,
|
|
633
|
+
outlineOffset: debugState.currentStepIndex === index ? '0px' : undefined,
|
|
634
|
+
}}
|
|
635
|
+
>
|
|
636
|
+
{/* Breakpoint gutter - only show in debug mode */}
|
|
637
|
+
{debugState.mode === 'debug' && (
|
|
638
|
+
<button
|
|
639
|
+
onClick={(e) => {
|
|
640
|
+
e.stopPropagation();
|
|
641
|
+
toggleBreakpoint(index);
|
|
642
|
+
}}
|
|
643
|
+
style={{
|
|
644
|
+
width: '24px',
|
|
645
|
+
flexShrink: 0,
|
|
646
|
+
display: 'flex',
|
|
647
|
+
alignItems: 'center',
|
|
648
|
+
justifyContent: 'center',
|
|
649
|
+
border: 'none',
|
|
650
|
+
borderRight: '1px solid var(--border)',
|
|
651
|
+
background: hoveredBreakpoint === index ? 'var(--bg-hover)' : 'transparent',
|
|
652
|
+
transition: 'background 0.15s',
|
|
653
|
+
cursor: 'pointer',
|
|
654
|
+
padding: 0,
|
|
655
|
+
}}
|
|
656
|
+
onMouseEnter={() => setHoveredBreakpoint(index)}
|
|
657
|
+
onMouseLeave={() => setHoveredBreakpoint(null)}
|
|
658
|
+
title={debugState.breakpoints.has(index) ? "Remove breakpoint (F9)" : "Add breakpoint (F9)"}
|
|
659
|
+
>
|
|
660
|
+
{debugState.breakpoints.has(index) ? (
|
|
661
|
+
<BreakpointIcon style={{ width: 12, height: 12, color: '#ef4444' }} />
|
|
662
|
+
) : (
|
|
663
|
+
<BreakpointEmptyIcon style={{ width: 12, height: 12, color: hoveredBreakpoint === index ? '#f87171' : 'var(--text-muted)' }} />
|
|
664
|
+
)}
|
|
665
|
+
</button>
|
|
666
|
+
)}
|
|
628
667
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
668
|
+
<div style={{ flex: 1 }}>
|
|
669
|
+
<button
|
|
670
|
+
onClick={() => step.error && toggleStep(index)}
|
|
671
|
+
style={{
|
|
672
|
+
width: '100%',
|
|
673
|
+
padding: '12px',
|
|
674
|
+
display: 'flex',
|
|
675
|
+
alignItems: 'center',
|
|
676
|
+
gap: '8px',
|
|
677
|
+
textAlign: 'left',
|
|
678
|
+
cursor: step.error ? 'pointer' : 'default',
|
|
679
|
+
border: 'none',
|
|
680
|
+
background: 'transparent',
|
|
681
|
+
color: 'inherit',
|
|
682
|
+
}}
|
|
683
|
+
disabled={!step.error}
|
|
684
|
+
>
|
|
685
|
+
{/* Current step indicator */}
|
|
686
|
+
{debugState.mode === 'debug' && debugState.currentStepIndex === index && (
|
|
687
|
+
<span style={{ color: '#f97316', fontWeight: 700 }}>▶</span>
|
|
688
|
+
)}
|
|
645
689
|
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
690
|
+
{/* Status icon */}
|
|
691
|
+
{step.status === "pending" && (
|
|
692
|
+
<div style={{ width: '16px', height: '16px', borderRadius: '9999px', border: '2px solid var(--text-muted)' }} />
|
|
693
|
+
)}
|
|
694
|
+
{step.status === "running" && (
|
|
695
|
+
<LoadingIcon style={{ width: 16, height: 16, animation: 'spin 1s linear infinite', color: stepColors.icon }} />
|
|
696
|
+
)}
|
|
697
|
+
{step.status === "paused" && (
|
|
698
|
+
<PauseIcon style={{ width: 16, height: 16, color: stepColors.icon }} />
|
|
699
|
+
)}
|
|
700
|
+
{step.status === "passed" && (
|
|
701
|
+
<CheckIcon style={{ width: 16, height: 16, color: stepColors.icon }} />
|
|
702
|
+
)}
|
|
703
|
+
{step.status === "failed" && (
|
|
704
|
+
<XIcon style={{ width: 16, height: 16, color: stepColors.icon }} />
|
|
655
705
|
)}
|
|
656
|
-
>
|
|
657
|
-
{step.name}
|
|
658
|
-
</span>
|
|
659
|
-
|
|
660
|
-
{/* Duration */}
|
|
661
|
-
{step.duration !== undefined && (
|
|
662
|
-
<span className="text-xs text-gray-400 dark:text-gray-500">
|
|
663
|
-
{formatDuration(step.duration)}
|
|
664
|
-
</span>
|
|
665
|
-
)}
|
|
666
706
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
707
|
+
{/* Step name */}
|
|
708
|
+
<Text size="sm" style={{ flex: 1, color: stepColors.text }}>
|
|
709
|
+
{step.name}
|
|
710
|
+
</Text>
|
|
711
|
+
|
|
712
|
+
{/* Duration */}
|
|
713
|
+
{step.duration !== undefined && (
|
|
714
|
+
<Text size="xs" color="tertiary">
|
|
715
|
+
{formatDuration(step.duration)}
|
|
716
|
+
</Text>
|
|
717
|
+
)}
|
|
718
|
+
|
|
719
|
+
{/* Expand icon for errors */}
|
|
720
|
+
{step.error && (
|
|
721
|
+
expandedSteps.has(index) ? (
|
|
722
|
+
<ChevronDownIcon style={{ width: 16, height: 16, color: 'var(--text-muted)' }} />
|
|
723
|
+
) : (
|
|
724
|
+
<ChevronRightIcon style={{ width: 16, height: 16, color: 'var(--text-muted)' }} />
|
|
725
|
+
)
|
|
726
|
+
)}
|
|
727
|
+
</button>
|
|
728
|
+
|
|
729
|
+
{/* Error details */}
|
|
730
|
+
{step.error && expandedSteps.has(index) && (
|
|
731
|
+
<div style={{ padding: '0 12px 12px 36px' }}>
|
|
732
|
+
<pre style={{ fontSize: '12px', color: '#dc2626', background: 'color-mix(in srgb, #ef4444 10%, transparent)', padding: '8px', borderRadius: '4px', overflowX: 'auto', whiteSpace: 'pre-wrap' }}>
|
|
733
|
+
{step.error}
|
|
734
|
+
</pre>
|
|
735
|
+
</div>
|
|
674
736
|
)}
|
|
675
|
-
</
|
|
676
|
-
|
|
677
|
-
{/* Error details */}
|
|
678
|
-
{step.error && expandedSteps.has(index) && (
|
|
679
|
-
<div className="px-3 pb-3 pl-9">
|
|
680
|
-
<pre className="text-xs text-red-600 dark:text-red-400 bg-red-100 dark:bg-red-950/50 p-2 rounded overflow-x-auto whitespace-pre-wrap">
|
|
681
|
-
{step.error}
|
|
682
|
-
</pre>
|
|
683
|
-
</div>
|
|
684
|
-
)}
|
|
737
|
+
</div>
|
|
685
738
|
</div>
|
|
686
|
-
|
|
687
|
-
)
|
|
688
|
-
</
|
|
739
|
+
);
|
|
740
|
+
})}
|
|
741
|
+
</Stack>
|
|
689
742
|
)}
|
|
690
743
|
|
|
691
744
|
{/* Keyboard shortcuts help in debug mode */}
|
|
692
745
|
{debugState.mode === 'debug' && (
|
|
693
|
-
<div
|
|
694
|
-
<
|
|
695
|
-
<div
|
|
696
|
-
<
|
|
697
|
-
<
|
|
698
|
-
<
|
|
699
|
-
<
|
|
746
|
+
<div style={{ marginTop: '16px', padding: '12px', background: 'var(--bg-secondary)', borderRadius: '8px' }}>
|
|
747
|
+
<Text size="xs" weight="medium" color="tertiary" style={{ marginBottom: '4px' }}>Keyboard shortcuts:</Text>
|
|
748
|
+
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '4px' }}>
|
|
749
|
+
<Text size="xs" color="tertiary"><Badge size="sm" variant="default">F5</Badge> Run with debugger</Text>
|
|
750
|
+
<Text size="xs" color="tertiary"><Badge size="sm" variant="default">F8</Badge> Continue</Text>
|
|
751
|
+
<Text size="xs" color="tertiary"><Badge size="sm" variant="default">F9</Badge> Toggle breakpoint</Text>
|
|
752
|
+
<Text size="xs" color="tertiary"><Badge size="sm" variant="default">F10</Badge> Step over</Text>
|
|
700
753
|
</div>
|
|
701
754
|
</div>
|
|
702
755
|
)}
|
|
703
756
|
|
|
704
757
|
{/* Top-level error (if no steps failed) */}
|
|
705
758
|
{result.error && !result.steps.some((s) => s.status === "failed") && (
|
|
706
|
-
<div
|
|
707
|
-
<
|
|
759
|
+
<div style={{ marginTop: '16px' }}>
|
|
760
|
+
<Text size="xs" weight="medium" color="tertiary" style={{ textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: '8px' }}>
|
|
708
761
|
Error
|
|
709
|
-
</
|
|
710
|
-
<pre
|
|
762
|
+
</Text>
|
|
763
|
+
<pre style={{ fontSize: '12px', color: '#dc2626', background: 'color-mix(in srgb, #ef4444 10%, transparent)', padding: '12px', borderRadius: '4px', overflowX: 'auto', whiteSpace: 'pre-wrap' }}>
|
|
711
764
|
{result.error}
|
|
712
765
|
</pre>
|
|
713
766
|
</div>
|
|
714
767
|
)}
|
|
715
|
-
</
|
|
768
|
+
</Stack>
|
|
716
769
|
)}
|
|
717
770
|
</div>
|
|
718
771
|
</div>
|