@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.
Files changed (124) hide show
  1. package/dist/bin.js +996 -79
  2. package/dist/bin.js.map +1 -1
  3. package/dist/{chunk-ICAIQ57V.js → chunk-6JBGU74P.js} +5 -3
  4. package/dist/chunk-6JBGU74P.js.map +1 -0
  5. package/dist/chunk-7OPWMLOE.js +1625 -0
  6. package/dist/chunk-7OPWMLOE.js.map +1 -0
  7. package/dist/{chunk-2H2JAA3U.js → chunk-CVXKXVOY.js} +3 -3
  8. package/dist/{chunk-2H2JAA3U.js.map → chunk-CVXKXVOY.js.map} +1 -1
  9. package/dist/{chunk-IOJE35DZ.js → chunk-NWQ4CJOQ.js} +3 -3
  10. package/dist/{chunk-2DJH4F4P.js → chunk-RVRTRESS.js} +3 -3
  11. package/dist/{chunk-V7YLRR4C.js → chunk-TJ34N7C7.js} +41 -4
  12. package/dist/{chunk-V7YLRR4C.js.map → chunk-TJ34N7C7.js.map} +1 -1
  13. package/dist/{chunk-XNWDI6UT.js → chunk-XHUDJNN3.js} +5 -5
  14. package/dist/{core-DKHB7FYV.js → core-W2HYIQW6.js} +4 -4
  15. package/dist/{generate-KL24VZVD.js → generate-LMTISDIJ.js} +5 -5
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.js +15 -7
  18. package/dist/index.js.map +1 -1
  19. package/dist/{init-NION5S3M.js → init-7CHRKQ7P.js} +5 -5
  20. package/dist/mcp-bin.js +8 -220
  21. package/dist/mcp-bin.js.map +1 -1
  22. package/dist/scan-WY23TJCP.js +12 -0
  23. package/dist/{service-RWUMZ3EW.js → service-T2L7VLTE.js} +5 -5
  24. package/dist/static-viewer-GBR7YNF3.js +12 -0
  25. package/dist/{test-ECPEXFDN.js → test-OJRXNDO2.js} +4 -4
  26. package/dist/{tokens-ITADYVPF.js → tokens-3BWDESVM.js} +6 -6
  27. package/dist/viewer-SUFOISZM.js +1822 -0
  28. package/dist/viewer-SUFOISZM.js.map +1 -0
  29. package/package.json +6 -5
  30. package/src/bin.ts +31 -0
  31. package/src/build.ts +147 -13
  32. package/src/cli-commands.ts +18 -0
  33. package/src/commands/__tests__/a11y-scoring.test.ts +278 -0
  34. package/src/commands/a11y-report.ts +625 -0
  35. package/src/commands/a11y.ts +168 -14
  36. package/src/commands/build.ts +16 -0
  37. package/src/commands/graph.ts +274 -0
  38. package/src/core/auto-props.ts +464 -0
  39. package/src/core/composition.ts +64 -1
  40. package/src/core/graph-extractor.test.ts +542 -0
  41. package/src/core/graph-extractor.ts +601 -0
  42. package/src/core/importAnalyzer.ts +5 -0
  43. package/src/core/schema.ts +2 -0
  44. package/src/core/types.ts +3 -1
  45. package/src/index.ts +4 -0
  46. package/src/mcp/server.ts +13 -220
  47. package/src/theme/__tests__/component-contrast.test.ts +338 -0
  48. package/src/theme/__tests__/contrast-validation.test.ts +326 -0
  49. package/src/theme/contrast.test.ts +331 -0
  50. package/src/theme/contrast.ts +246 -0
  51. package/src/theme/generator.ts +213 -1
  52. package/src/theme/index.ts +16 -0
  53. package/src/theme/types.ts +51 -0
  54. package/src/viewer/__tests__/a11y-fixes.test.ts +358 -0
  55. package/src/viewer/__tests__/viewer-integration.test.ts +2 -7
  56. package/src/viewer/components/AccessibilityPanel.tsx +493 -433
  57. package/src/viewer/components/ActionCapture.tsx +1 -1
  58. package/src/viewer/components/ActionsPanel.tsx +142 -183
  59. package/src/viewer/components/App.tsx +276 -183
  60. package/src/viewer/components/BottomPanel.tsx +40 -80
  61. package/src/viewer/components/CodePanel.tsx +9 -87
  62. package/src/viewer/components/CommandPalette.tsx +117 -74
  63. package/src/viewer/components/ComponentGraph.tsx +143 -126
  64. package/src/viewer/components/ComponentHeader.tsx +46 -43
  65. package/src/viewer/components/ContractPanel.tsx +124 -117
  66. package/src/viewer/components/ErrorBoundary.tsx +47 -35
  67. package/src/viewer/components/FigmaEmbed.tsx +18 -13
  68. package/src/viewer/components/FragmentEditor.tsx +126 -63
  69. package/src/viewer/components/HealthDashboard.tsx +146 -171
  70. package/src/viewer/components/HmrStatusIndicator.tsx +31 -41
  71. package/src/viewer/components/Icons.tsx +151 -98
  72. package/src/viewer/components/InteractionsPanel.tsx +317 -264
  73. package/src/viewer/components/IsolatedPreviewFrame.tsx +52 -27
  74. package/src/viewer/components/IsolatedRender.tsx +12 -6
  75. package/src/viewer/components/KeyboardShortcutsHelp.tsx +34 -70
  76. package/src/viewer/components/LandingPage.tsx +285 -305
  77. package/src/viewer/components/Layout.tsx +12 -10
  78. package/src/viewer/components/LeftSidebar.tsx +103 -155
  79. package/src/viewer/components/MultiViewportPreview.tsx +254 -63
  80. package/src/viewer/components/PreviewArea.tsx +113 -44
  81. package/src/viewer/components/PreviewFrameHost.tsx +36 -6
  82. package/src/viewer/components/PreviewPane.tsx +2 -3
  83. package/src/viewer/components/PreviewToolbar.tsx +109 -105
  84. package/src/viewer/components/PropsEditor.tsx +154 -74
  85. package/src/viewer/components/PropsTable.tsx +95 -82
  86. package/src/viewer/components/RelationsSection.tsx +71 -40
  87. package/src/viewer/components/ResizablePanel.tsx +158 -55
  88. package/src/viewer/components/RightSidebar.tsx +46 -56
  89. package/src/viewer/components/ScreenshotButton.tsx +12 -12
  90. package/src/viewer/components/SkeletonLoader.tsx +99 -83
  91. package/src/viewer/components/StoryRenderer.tsx +4 -11
  92. package/src/viewer/components/Toast.tsx +3 -67
  93. package/src/viewer/components/TokenStylePanel.tsx +136 -118
  94. package/src/viewer/components/UsageSection.tsx +26 -26
  95. package/src/viewer/components/VariantMatrix.tsx +140 -47
  96. package/src/viewer/components/VariantTabs.tsx +24 -68
  97. package/src/viewer/components/ViewportSelector.tsx +121 -114
  98. package/src/viewer/constants/ui.ts +23 -22
  99. package/src/viewer/entry.tsx +8 -3
  100. package/src/viewer/index.ts +3 -6
  101. package/src/viewer/preview-frame.html +43 -18
  102. package/src/viewer/server.ts +7 -16
  103. package/src/viewer/styles/globals.css +46 -85
  104. package/src/viewer/utils/a11y-fixes.ts +53 -30
  105. package/dist/chunk-ICAIQ57V.js.map +0 -1
  106. package/dist/chunk-U4GQ2JTD.js +0 -832
  107. package/dist/chunk-U4GQ2JTD.js.map +0 -1
  108. package/dist/scan-ESEXV7LF.js +0 -12
  109. package/dist/static-viewer-O37MJ5B6.js +0 -12
  110. package/dist/viewer-YDGFDTK5.js +0 -11104
  111. package/dist/viewer-YDGFDTK5.js.map +0 -1
  112. package/src/viewer/postcss.config.js +0 -6
  113. package/src/viewer/tailwind.config.js +0 -37
  114. /package/dist/{chunk-IOJE35DZ.js.map → chunk-NWQ4CJOQ.js.map} +0 -0
  115. /package/dist/{chunk-2DJH4F4P.js.map → chunk-RVRTRESS.js.map} +0 -0
  116. /package/dist/{chunk-XNWDI6UT.js.map → chunk-XHUDJNN3.js.map} +0 -0
  117. /package/dist/{core-DKHB7FYV.js.map → core-W2HYIQW6.js.map} +0 -0
  118. /package/dist/{generate-KL24VZVD.js.map → generate-LMTISDIJ.js.map} +0 -0
  119. /package/dist/{init-NION5S3M.js.map → init-7CHRKQ7P.js.map} +0 -0
  120. /package/dist/{scan-ESEXV7LF.js.map → scan-WY23TJCP.js.map} +0 -0
  121. /package/dist/{service-RWUMZ3EW.js.map → service-T2L7VLTE.js.map} +0 -0
  122. /package/dist/{static-viewer-O37MJ5B6.js.map → static-viewer-GBR7YNF3.js.map} +0 -0
  123. /package/dist/{test-ECPEXFDN.js.map → test-OJRXNDO2.js.map} +0 -0
  124. /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} className="contents">
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, useRef, useEffect } from "react";
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 className="h-full flex flex-col">
112
- <div className="p-4 border-b border-[--border] flex items-center justify-between">
113
- <h3 className="font-medium text-primary flex items-center gap-2">
114
- <PlayIcon className="w-4 h-4" />
115
- Actions
116
- </h3>
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 className="flex-1 flex items-center justify-center p-8 text-center">
119
- <div className="max-w-md">
120
- <div className="w-12 h-12 rounded-full bg-[--bg-tertiary] flex items-center justify-center mx-auto mb-4">
121
- <PlayIcon className="w-6 h-6 text-tertiary" />
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
- <h4 className="font-medium text-secondary mb-2">
108
+ <Text weight="medium" color="secondary" style={{ marginBottom: '8px' }}>
124
109
  No actions logged yet
125
- </h4>
126
- <p className="text-sm text-tertiary">
110
+ </Text>
111
+ <Text size="sm" color="tertiary">
127
112
  Interact with the component to see callback invocations here.
128
- Actions like <code className="px-1 py-0.5 bg-[--bg-tertiary] rounded text-xs">onClick</code>, <code className="px-1 py-0.5 bg-[--bg-tertiary] rounded text-xs">onChange</code>, etc. will be logged automatically.
129
- </p>
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 className="h-full flex flex-col">
122
+ <div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
138
123
  {/* Header */}
139
- <div className="p-3 border-b border-[--border] flex items-center justify-between gap-2">
140
- <div className="flex items-center gap-2 flex-1">
141
- <h3 className="font-medium text-primary flex items-center gap-2 text-sm">
142
- <PlayIcon className="w-4 h-4" />
143
- Actions
144
- </h3>
145
- <span className="text-xs text-tertiary bg-[--bg-tertiary] px-1.5 py-0.5 rounded">
146
- {logs.length}
147
- </span>
148
- </div>
149
- <div className="flex items-center gap-2">
150
- <input
151
- type="text"
152
- placeholder="Filter..."
153
- value={filter}
154
- onChange={(e) => setFilter(e.target.value)}
155
- className="px-2 py-1 text-xs border border-[--border] rounded bg-[--bg-elevated] text-primary w-24 focus:outline-none focus:ring-1 focus:ring-[--color-accent]"
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
- {/* Export dropdown */}
159
- <div className="relative" ref={exportMenuRef}>
160
- <button
161
- onClick={() => setShowExportMenu(!showExportMenu)}
162
- className={clsx(
163
- "p-1.5 rounded transition-colors",
164
- showExportMenu
165
- ? "text-[--color-accent] bg-[--bg-hover]"
166
- : "text-tertiary hover:text-secondary hover:bg-[--bg-hover]"
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 className="px-1">
181
- <div className="flex items-center justify-between px-2 py-1.5 hover:bg-[--bg-hover] rounded">
182
- <span className="text-sm text-secondary">JSON</span>
183
- <div className="flex items-center gap-1">
184
- <button
185
- onClick={() => handleExport('json', 'copy')}
186
- className="p-1 text-tertiary hover:text-secondary hover:bg-[--bg-hover] rounded"
187
- title="Copy to clipboard"
188
- >
189
- {copyFeedback === 'json' ? <CheckIcon className="w-3.5 h-3.5 text-green-500" /> : <CopyIcon className="w-3.5 h-3.5" />}
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 className="px-1">
204
- <div className="flex items-center justify-between px-2 py-1.5 hover:bg-[--bg-hover] rounded">
205
- <span className="text-sm text-secondary">Jest Assertions</span>
206
- <div className="flex items-center gap-1">
207
- <button
208
- onClick={() => handleExport('jest', 'copy')}
209
- className="p-1 text-tertiary hover:text-secondary hover:bg-[--bg-hover] rounded"
210
- title="Copy to clipboard"
211
- >
212
- {copyFeedback === 'jest' ? <CheckIcon className="w-3.5 h-3.5 text-green-500" /> : <CopyIcon className="w-3.5 h-3.5" />}
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 className="px-1">
227
- <div className="flex items-center justify-between px-2 py-1.5 hover:bg-[--bg-hover] rounded">
228
- <span className="text-sm text-secondary">Playwright Test</span>
229
- <div className="flex items-center gap-1">
230
- <button
231
- onClick={() => handleExport('playwright', 'copy')}
232
- className="p-1 text-tertiary hover:text-secondary hover:bg-[--bg-hover] rounded"
233
- title="Copy to clipboard"
234
- >
235
- {copyFeedback === 'playwright' ? <CheckIcon className="w-3.5 h-3.5 text-green-500" /> : <CopyIcon className="w-3.5 h-3.5" />}
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
- <div className="border-t border-[--border-subtle] my-1" />
249
- <div className="px-3 py-1.5 text-xs text-tertiary">
250
- {logs.length} action{logs.length !== 1 ? 's' : ''} recorded
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
- </div>
253
- )}
254
- </div>
199
+ </Menu.Content>
200
+ </Menu>
255
201
 
256
- <button
257
- onClick={onClear}
258
- className="p-1.5 text-tertiary hover:text-secondary hover:bg-[--bg-hover] rounded transition-colors"
259
- title="Clear all actions"
260
- >
261
- <TrashIcon className="w-4 h-4" />
262
- </button>
263
- </div>
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 className="flex-1 overflow-y-auto">
215
+ <div style={{ flex: 1, overflowY: 'auto' }}>
268
216
  {filteredLogs.length === 0 ? (
269
- <div className="p-4 text-center text-sm text-tertiary">
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 className="divide-y divide-[--border-subtle]">
274
- {filteredLogs.map((log) => (
275
- <ActionLogItem
276
- key={log.id}
277
- log={log}
278
- isExpanded={expandedLogs.has(log.id)}
279
- onToggle={() => toggleExpanded(log.id)}
280
- formatTime={formatTime}
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 className="hover:bg-[--bg-secondary] transition-colors">
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
- className={clsx(
309
- "w-full px-3 py-2 flex items-start gap-2 text-left",
310
- hasArgs && "cursor-pointer",
311
- !hasArgs && "cursor-default"
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 className="flex-shrink-0 w-4 h-4 mt-0.5">
276
+ <div style={{ flexShrink: 0, width: '16px', height: '16px', marginTop: '2px' }}>
316
277
  {hasArgs ? (
317
278
  isExpanded ? (
318
- <ChevronDownIcon className="w-4 h-4 text-tertiary" />
279
+ <ChevronDownIcon style={{ width: 16, height: 16, color: 'var(--text-tertiary)' }} />
319
280
  ) : (
320
- <ChevronRightIcon className="w-4 h-4 text-tertiary" />
281
+ <ChevronRightIcon style={{ width: 16, height: 16, color: 'var(--text-tertiary)' }} />
321
282
  )
322
283
  ) : (
323
- <div className="w-4 h-4" />
284
+ <div style={{ width: '16px', height: '16px' }} />
324
285
  )}
325
286
  </div>
326
287
 
327
288
  {/* Action name */}
328
- <div className="flex-1 min-w-0">
329
- <div className="flex items-center gap-2">
330
- <span className="font-mono text-sm text-[--color-accent]">
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
- </span>
293
+ </Text>
333
294
  {log.count > 1 && (
334
- <span className="text-xs text-tertiary bg-[--bg-tertiary] px-1.5 py-0.5 rounded-full">
335
- ×{log.count}
336
- </span>
295
+ <Badge size="sm">x{log.count}</Badge>
337
296
  )}
338
- </div>
297
+ </Stack>
339
298
  {!isExpanded && hasArgs && (
340
- <div className="text-xs text-tertiary truncate mt-0.5 font-mono">
299
+ <Text size="xs" color="tertiary" font="mono" style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', marginTop: '2px' }}>
341
300
  ({argsPreview})
342
- </div>
301
+ </Text>
343
302
  )}
344
303
  </div>
345
304
 
346
305
  {/* Timestamp */}
347
- <span className="flex-shrink-0 text-xs text-tertiary font-mono">
306
+ <Text size="xs" color="tertiary" font="mono" style={{ flexShrink: 0 }}>
348
307
  {formatTime(log.timestamp)}
349
- </span>
308
+ </Text>
350
309
  </button>
351
310
 
352
311
  {/* Expanded args */}
353
312
  {isExpanded && hasArgs && (
354
- <div className="px-3 pb-3 pl-9">
355
- <div className="bg-[--bg-secondary] rounded-lg p-2 overflow-x-auto">
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} className="mb-1 last:mb-0">
358
- <span className="text-xs text-tertiary">
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
- </span>
361
- <pre className="inline text-xs font-mono text-secondary whitespace-pre-wrap">
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>