@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
|
@@ -165,7 +165,7 @@ export function ActionCapture({ children, onAction, enabled = true }: ActionCapt
|
|
|
165
165
|
}, [onAction, enabled]);
|
|
166
166
|
|
|
167
167
|
return (
|
|
168
|
-
<div ref={containerRef}
|
|
168
|
+
<div ref={containerRef} style={{ display: 'contents' }}>
|
|
169
169
|
{children}
|
|
170
170
|
</div>
|
|
171
171
|
);
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
* with expandable argument details and timestamps.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { useState, useMemo
|
|
9
|
-
import clsx from "clsx";
|
|
8
|
+
import { useState, useMemo } from "react";
|
|
10
9
|
import type { ActionLog } from "../hooks/useActions.js";
|
|
11
10
|
import { formatActionArg } from "../hooks/useActions.js";
|
|
11
|
+
import { Button, Stack, Text, Badge, Input, Menu, Separator } from "@fragments/ui";
|
|
12
12
|
import {
|
|
13
13
|
TrashIcon,
|
|
14
14
|
ChevronDownIcon,
|
|
@@ -41,20 +41,7 @@ interface ActionsPanelProps {
|
|
|
41
41
|
export function ActionsPanel({ logs, onClear, componentName = 'Component', variantName = 'Default' }: ActionsPanelProps) {
|
|
42
42
|
const [expandedLogs, setExpandedLogs] = useState<Set<string>>(new Set());
|
|
43
43
|
const [filter, setFilter] = useState("");
|
|
44
|
-
const [showExportMenu, setShowExportMenu] = useState(false);
|
|
45
44
|
const [copyFeedback, setCopyFeedback] = useState<string | null>(null);
|
|
46
|
-
const exportMenuRef = useRef<HTMLDivElement>(null);
|
|
47
|
-
|
|
48
|
-
// Close export menu when clicking outside
|
|
49
|
-
useEffect(() => {
|
|
50
|
-
const handleClickOutside = (event: MouseEvent) => {
|
|
51
|
-
if (exportMenuRef.current && !exportMenuRef.current.contains(event.target as Node)) {
|
|
52
|
-
setShowExportMenu(false);
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
document.addEventListener('mousedown', handleClickOutside);
|
|
56
|
-
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
57
|
-
}, []);
|
|
58
45
|
|
|
59
46
|
const filteredLogs = useMemo(() => {
|
|
60
47
|
if (!filter) return logs;
|
|
@@ -101,32 +88,30 @@ export function ActionsPanel({ logs, onClear, componentName = 'Component', varia
|
|
|
101
88
|
const filename = `${componentName}-${variantName}-actions${getFileExtension(format)}`;
|
|
102
89
|
downloadAsFile(content, filename.toLowerCase().replace(/\s+/g, '-'));
|
|
103
90
|
}
|
|
104
|
-
|
|
105
|
-
setShowExportMenu(false);
|
|
106
91
|
};
|
|
107
92
|
|
|
108
93
|
// No logs state
|
|
109
94
|
if (logs.length === 0) {
|
|
110
95
|
return (
|
|
111
|
-
<div
|
|
112
|
-
<div
|
|
113
|
-
<
|
|
114
|
-
<PlayIcon
|
|
115
|
-
Actions
|
|
116
|
-
</
|
|
96
|
+
<div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
|
|
97
|
+
<div style={{ padding: '16px', borderBottom: '1px solid var(--border)' }}>
|
|
98
|
+
<Stack direction="row" align="center" gap="sm">
|
|
99
|
+
<PlayIcon style={{ width: 16, height: 16 }} />
|
|
100
|
+
<Text weight="medium">Actions</Text>
|
|
101
|
+
</Stack>
|
|
117
102
|
</div>
|
|
118
|
-
<div
|
|
119
|
-
<div
|
|
120
|
-
<div
|
|
121
|
-
<PlayIcon
|
|
103
|
+
<div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '32px', textAlign: 'center' }}>
|
|
104
|
+
<div style={{ maxWidth: '28rem' }}>
|
|
105
|
+
<div style={{ width: '48px', height: '48px', borderRadius: '9999px', background: 'var(--bg-secondary)', display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '0 auto 16px' }}>
|
|
106
|
+
<PlayIcon style={{ width: 24, height: 24, color: 'var(--text-tertiary)' }} />
|
|
122
107
|
</div>
|
|
123
|
-
<
|
|
108
|
+
<Text weight="medium" color="secondary" style={{ marginBottom: '8px' }}>
|
|
124
109
|
No actions logged yet
|
|
125
|
-
</
|
|
126
|
-
<
|
|
110
|
+
</Text>
|
|
111
|
+
<Text size="sm" color="tertiary">
|
|
127
112
|
Interact with the component to see callback invocations here.
|
|
128
|
-
Actions like <code
|
|
129
|
-
</
|
|
113
|
+
Actions like <code style={{ padding: '2px 4px', background: 'var(--bg-secondary)', borderRadius: '4px', fontSize: '12px' }}>onClick</code>, <code style={{ padding: '2px 4px', background: 'var(--bg-secondary)', borderRadius: '4px', fontSize: '12px' }}>onChange</code>, etc. will be logged automatically.
|
|
114
|
+
</Text>
|
|
130
115
|
</div>
|
|
131
116
|
</div>
|
|
132
117
|
</div>
|
|
@@ -134,151 +119,115 @@ export function ActionsPanel({ logs, onClear, componentName = 'Component', varia
|
|
|
134
119
|
}
|
|
135
120
|
|
|
136
121
|
return (
|
|
137
|
-
<div
|
|
122
|
+
<div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
|
|
138
123
|
{/* Header */}
|
|
139
|
-
<div
|
|
140
|
-
<
|
|
141
|
-
<
|
|
142
|
-
<
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
{logs.length}
|
|
147
|
-
</
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
/>
|
|
124
|
+
<div style={{ padding: '12px', borderBottom: '1px solid var(--border)' }}>
|
|
125
|
+
<Stack direction="row" align="center" justify="between" gap="sm">
|
|
126
|
+
<Stack direction="row" align="center" gap="sm" style={{ flex: 1 }}>
|
|
127
|
+
<Stack direction="row" align="center" gap="sm">
|
|
128
|
+
<PlayIcon style={{ width: 16, height: 16 }} />
|
|
129
|
+
<Text weight="medium" size="sm">Actions</Text>
|
|
130
|
+
</Stack>
|
|
131
|
+
<Badge size="sm">{logs.length}</Badge>
|
|
132
|
+
</Stack>
|
|
133
|
+
<Stack direction="row" align="center" gap="sm">
|
|
134
|
+
<Input
|
|
135
|
+
size="sm"
|
|
136
|
+
placeholder="Filter..."
|
|
137
|
+
value={filter}
|
|
138
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setFilter(e.target.value)}
|
|
139
|
+
style={{ width: '96px' }}
|
|
140
|
+
/>
|
|
157
141
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
)}
|
|
168
|
-
title="Export actions"
|
|
169
|
-
>
|
|
170
|
-
<ExportIcon className="w-4 h-4" />
|
|
171
|
-
</button>
|
|
172
|
-
|
|
173
|
-
{showExportMenu && (
|
|
174
|
-
<div className="absolute right-0 top-full mt-1 w-56 bg-[--bg-elevated] rounded-lg shadow-[--shadow-lg] border border-[--border] py-1 z-50">
|
|
175
|
-
<div className="px-3 py-1.5 text-xs font-medium text-tertiary uppercase tracking-wide">
|
|
142
|
+
{/* Export dropdown */}
|
|
143
|
+
<Menu>
|
|
144
|
+
<Menu.Trigger>
|
|
145
|
+
<Button variant="ghost" size="sm" title="Export actions">
|
|
146
|
+
<ExportIcon style={{ width: 16, height: 16 }} />
|
|
147
|
+
</Button>
|
|
148
|
+
</Menu.Trigger>
|
|
149
|
+
<Menu.Content>
|
|
150
|
+
<div style={{ padding: '6px 12px', fontSize: '12px', fontWeight: 500, color: 'var(--text-tertiary)', textTransform: 'uppercase', letterSpacing: '0.05em' }}>
|
|
176
151
|
Export as
|
|
177
152
|
</div>
|
|
178
153
|
|
|
179
154
|
{/* JSON */}
|
|
180
|
-
<div
|
|
181
|
-
<
|
|
182
|
-
|
|
183
|
-
<
|
|
184
|
-
<
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
</button>
|
|
191
|
-
<button
|
|
192
|
-
onClick={() => handleExport('json', 'download')}
|
|
193
|
-
className="p-1 text-tertiary hover:text-secondary hover:bg-[--bg-hover] rounded"
|
|
194
|
-
title="Download file"
|
|
195
|
-
>
|
|
196
|
-
<DownloadIcon className="w-3.5 h-3.5" />
|
|
197
|
-
</button>
|
|
198
|
-
</div>
|
|
199
|
-
</div>
|
|
155
|
+
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '6px 12px' }}>
|
|
156
|
+
<Text size="sm" color="secondary">JSON</Text>
|
|
157
|
+
<Stack direction="row" gap="xs">
|
|
158
|
+
<Menu.Item onSelect={() => handleExport('json', 'copy')}>
|
|
159
|
+
{copyFeedback === 'json' ? <CheckIcon style={{ width: 14, height: 14, color: '#22c55e' }} /> : <CopyIcon style={{ width: 14, height: 14 }} />}
|
|
160
|
+
</Menu.Item>
|
|
161
|
+
<Menu.Item onSelect={() => handleExport('json', 'download')}>
|
|
162
|
+
<DownloadIcon style={{ width: 14, height: 14 }} />
|
|
163
|
+
</Menu.Item>
|
|
164
|
+
</Stack>
|
|
200
165
|
</div>
|
|
201
166
|
|
|
202
167
|
{/* Jest */}
|
|
203
|
-
<div
|
|
204
|
-
<
|
|
205
|
-
|
|
206
|
-
<
|
|
207
|
-
<
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
</button>
|
|
214
|
-
<button
|
|
215
|
-
onClick={() => handleExport('jest', 'download')}
|
|
216
|
-
className="p-1 text-tertiary hover:text-secondary hover:bg-[--bg-hover] rounded"
|
|
217
|
-
title="Download file"
|
|
218
|
-
>
|
|
219
|
-
<DownloadIcon className="w-3.5 h-3.5" />
|
|
220
|
-
</button>
|
|
221
|
-
</div>
|
|
222
|
-
</div>
|
|
168
|
+
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '6px 12px' }}>
|
|
169
|
+
<Text size="sm" color="secondary">Jest Assertions</Text>
|
|
170
|
+
<Stack direction="row" gap="xs">
|
|
171
|
+
<Menu.Item onSelect={() => handleExport('jest', 'copy')}>
|
|
172
|
+
{copyFeedback === 'jest' ? <CheckIcon style={{ width: 14, height: 14, color: '#22c55e' }} /> : <CopyIcon style={{ width: 14, height: 14 }} />}
|
|
173
|
+
</Menu.Item>
|
|
174
|
+
<Menu.Item onSelect={() => handleExport('jest', 'download')}>
|
|
175
|
+
<DownloadIcon style={{ width: 14, height: 14 }} />
|
|
176
|
+
</Menu.Item>
|
|
177
|
+
</Stack>
|
|
223
178
|
</div>
|
|
224
179
|
|
|
225
180
|
{/* Playwright */}
|
|
226
|
-
<div
|
|
227
|
-
<
|
|
228
|
-
|
|
229
|
-
<
|
|
230
|
-
<
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
</button>
|
|
237
|
-
<button
|
|
238
|
-
onClick={() => handleExport('playwright', 'download')}
|
|
239
|
-
className="p-1 text-tertiary hover:text-secondary hover:bg-[--bg-hover] rounded"
|
|
240
|
-
title="Download file"
|
|
241
|
-
>
|
|
242
|
-
<DownloadIcon className="w-3.5 h-3.5" />
|
|
243
|
-
</button>
|
|
244
|
-
</div>
|
|
245
|
-
</div>
|
|
181
|
+
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '6px 12px' }}>
|
|
182
|
+
<Text size="sm" color="secondary">Playwright Test</Text>
|
|
183
|
+
<Stack direction="row" gap="xs">
|
|
184
|
+
<Menu.Item onSelect={() => handleExport('playwright', 'copy')}>
|
|
185
|
+
{copyFeedback === 'playwright' ? <CheckIcon style={{ width: 14, height: 14, color: '#22c55e' }} /> : <CopyIcon style={{ width: 14, height: 14 }} />}
|
|
186
|
+
</Menu.Item>
|
|
187
|
+
<Menu.Item onSelect={() => handleExport('playwright', 'download')}>
|
|
188
|
+
<DownloadIcon style={{ width: 14, height: 14 }} />
|
|
189
|
+
</Menu.Item>
|
|
190
|
+
</Stack>
|
|
246
191
|
</div>
|
|
247
192
|
|
|
248
|
-
<
|
|
249
|
-
<div
|
|
250
|
-
|
|
193
|
+
<Separator />
|
|
194
|
+
<div style={{ padding: '6px 12px' }}>
|
|
195
|
+
<Text size="xs" color="tertiary">
|
|
196
|
+
{logs.length} action{logs.length !== 1 ? 's' : ''} recorded
|
|
197
|
+
</Text>
|
|
251
198
|
</div>
|
|
252
|
-
</
|
|
253
|
-
|
|
254
|
-
</div>
|
|
199
|
+
</Menu.Content>
|
|
200
|
+
</Menu>
|
|
255
201
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
202
|
+
<Button
|
|
203
|
+
onClick={onClear}
|
|
204
|
+
variant="ghost"
|
|
205
|
+
size="sm"
|
|
206
|
+
title="Clear all actions"
|
|
207
|
+
>
|
|
208
|
+
<TrashIcon style={{ width: 16, height: 16 }} />
|
|
209
|
+
</Button>
|
|
210
|
+
</Stack>
|
|
211
|
+
</Stack>
|
|
264
212
|
</div>
|
|
265
213
|
|
|
266
214
|
{/* Log list */}
|
|
267
|
-
<div
|
|
215
|
+
<div style={{ flex: 1, overflowY: 'auto' }}>
|
|
268
216
|
{filteredLogs.length === 0 ? (
|
|
269
|
-
<div
|
|
270
|
-
No actions match "{filter}"
|
|
217
|
+
<div style={{ padding: '16px', textAlign: 'center' }}>
|
|
218
|
+
<Text size="sm" color="tertiary">No actions match "{filter}"</Text>
|
|
271
219
|
</div>
|
|
272
220
|
) : (
|
|
273
|
-
<div
|
|
274
|
-
{filteredLogs.map((log) => (
|
|
275
|
-
<
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
221
|
+
<div>
|
|
222
|
+
{filteredLogs.map((log, index) => (
|
|
223
|
+
<div key={log.id} style={{ borderBottom: index < filteredLogs.length - 1 ? '1px solid var(--border)' : undefined }}>
|
|
224
|
+
<ActionLogItem
|
|
225
|
+
log={log}
|
|
226
|
+
isExpanded={expandedLogs.has(log.id)}
|
|
227
|
+
onToggle={() => toggleExpanded(log.id)}
|
|
228
|
+
formatTime={formatTime}
|
|
229
|
+
/>
|
|
230
|
+
</div>
|
|
282
231
|
))}
|
|
283
232
|
</div>
|
|
284
233
|
)}
|
|
@@ -295,70 +244,80 @@ interface ActionLogItemProps {
|
|
|
295
244
|
}
|
|
296
245
|
|
|
297
246
|
function ActionLogItem({ log, isExpanded, onToggle, formatTime }: ActionLogItemProps) {
|
|
247
|
+
const [hovered, setHovered] = useState(false);
|
|
298
248
|
const hasArgs = log.args.length > 0;
|
|
299
249
|
const argsPreview = hasArgs
|
|
300
250
|
? log.args.map((arg) => formatActionArg(arg, 50)).join(", ")
|
|
301
251
|
: "";
|
|
302
252
|
|
|
303
253
|
return (
|
|
304
|
-
<div
|
|
254
|
+
<div
|
|
255
|
+
style={{ background: hovered ? 'var(--bg-secondary)' : 'transparent', transition: 'background 0.15s' }}
|
|
256
|
+
onMouseEnter={() => setHovered(true)}
|
|
257
|
+
onMouseLeave={() => setHovered(false)}
|
|
258
|
+
>
|
|
305
259
|
<button
|
|
306
260
|
onClick={onToggle}
|
|
307
261
|
disabled={!hasArgs}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
262
|
+
style={{
|
|
263
|
+
width: '100%',
|
|
264
|
+
padding: '8px 12px',
|
|
265
|
+
display: 'flex',
|
|
266
|
+
alignItems: 'flex-start',
|
|
267
|
+
gap: '8px',
|
|
268
|
+
textAlign: 'left',
|
|
269
|
+
cursor: hasArgs ? 'pointer' : 'default',
|
|
270
|
+
border: 'none',
|
|
271
|
+
background: 'transparent',
|
|
272
|
+
color: 'inherit',
|
|
273
|
+
}}
|
|
313
274
|
>
|
|
314
275
|
{/* Expand icon */}
|
|
315
|
-
<div
|
|
276
|
+
<div style={{ flexShrink: 0, width: '16px', height: '16px', marginTop: '2px' }}>
|
|
316
277
|
{hasArgs ? (
|
|
317
278
|
isExpanded ? (
|
|
318
|
-
<ChevronDownIcon
|
|
279
|
+
<ChevronDownIcon style={{ width: 16, height: 16, color: 'var(--text-tertiary)' }} />
|
|
319
280
|
) : (
|
|
320
|
-
<ChevronRightIcon
|
|
281
|
+
<ChevronRightIcon style={{ width: 16, height: 16, color: 'var(--text-tertiary)' }} />
|
|
321
282
|
)
|
|
322
283
|
) : (
|
|
323
|
-
<div
|
|
284
|
+
<div style={{ width: '16px', height: '16px' }} />
|
|
324
285
|
)}
|
|
325
286
|
</div>
|
|
326
287
|
|
|
327
288
|
{/* Action name */}
|
|
328
|
-
<div
|
|
329
|
-
<
|
|
330
|
-
<
|
|
289
|
+
<div style={{ flex: 1, minWidth: 0 }}>
|
|
290
|
+
<Stack direction="row" align="center" gap="sm">
|
|
291
|
+
<Text font="mono" size="sm" style={{ color: 'var(--color-accent)' }}>
|
|
331
292
|
{log.name}
|
|
332
|
-
</
|
|
293
|
+
</Text>
|
|
333
294
|
{log.count > 1 && (
|
|
334
|
-
<
|
|
335
|
-
×{log.count}
|
|
336
|
-
</span>
|
|
295
|
+
<Badge size="sm">x{log.count}</Badge>
|
|
337
296
|
)}
|
|
338
|
-
</
|
|
297
|
+
</Stack>
|
|
339
298
|
{!isExpanded && hasArgs && (
|
|
340
|
-
<
|
|
299
|
+
<Text size="xs" color="tertiary" font="mono" style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', marginTop: '2px' }}>
|
|
341
300
|
({argsPreview})
|
|
342
|
-
</
|
|
301
|
+
</Text>
|
|
343
302
|
)}
|
|
344
303
|
</div>
|
|
345
304
|
|
|
346
305
|
{/* Timestamp */}
|
|
347
|
-
<
|
|
306
|
+
<Text size="xs" color="tertiary" font="mono" style={{ flexShrink: 0 }}>
|
|
348
307
|
{formatTime(log.timestamp)}
|
|
349
|
-
</
|
|
308
|
+
</Text>
|
|
350
309
|
</button>
|
|
351
310
|
|
|
352
311
|
{/* Expanded args */}
|
|
353
312
|
{isExpanded && hasArgs && (
|
|
354
|
-
<div
|
|
355
|
-
<div
|
|
313
|
+
<div style={{ padding: '0 12px 12px 36px' }}>
|
|
314
|
+
<div style={{ background: 'var(--bg-secondary)', borderRadius: '8px', padding: '8px', overflowX: 'auto' }}>
|
|
356
315
|
{log.args.map((arg, index) => (
|
|
357
|
-
<div key={index}
|
|
358
|
-
<
|
|
316
|
+
<div key={index} style={{ marginBottom: index < log.args.length - 1 ? '4px' : 0 }}>
|
|
317
|
+
<Text size="xs" color="tertiary" as="span">
|
|
359
318
|
{log.args.length > 1 ? `[${index}] ` : ""}
|
|
360
|
-
</
|
|
361
|
-
<pre
|
|
319
|
+
</Text>
|
|
320
|
+
<pre style={{ display: 'inline', fontSize: '12px', fontFamily: 'monospace', color: 'var(--text-secondary)', whiteSpace: 'pre-wrap' }}>
|
|
362
321
|
{formatActionArg(arg, 500)}
|
|
363
322
|
</pre>
|
|
364
323
|
</div>
|