@aiready/visualizer 0.1.37 → 0.1.38
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/cli.js +46 -17
- package/dist/cli.js.map +1 -1
- package/dist/graph/index.js +36 -13
- package/dist/graph/index.js.map +1 -1
- package/dist/index.js +36 -13
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/web/dist/assets/{index-C9b__qGk.js → index-5leRjvmV.js} +1 -1
- package/web/dist/index.html +2 -2
- package/web/dist/report-data.json +224 -742
- package/web/index.html +1 -1
- package/web/package.json +1 -1
- package/web/public/report-data.json +224 -742
- package/web/src/App.tsx +61 -47
- package/web/src/components/ErrorDisplay.tsx +31 -17
- package/web/src/components/GraphCanvas.tsx +90 -24
- package/web/src/components/LegendPanel.tsx +242 -139
- package/web/src/components/LoadingSpinner.tsx +6 -3
- package/web/src/components/Navbar.tsx +66 -52
- package/web/src/components/NodeDetails.tsx +98 -68
- package/web/src/hooks/useTheme.ts +5 -2
- package/web/src/main.tsx +1 -1
- package/web/src/style.css +14 -2
- package/web/src/styles/index.css +7 -5
- package/web/src/utils.ts +86 -25
- package/web/tsconfig.json +1 -1
- package/web/vite.config.ts +9 -4
|
@@ -1,16 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ThemeColors,
|
|
3
|
+
SeverityLevel,
|
|
4
|
+
EdgeType,
|
|
5
|
+
BusinessMetrics,
|
|
6
|
+
} from '../types';
|
|
2
7
|
import { severityColors, edgeColors } from '../constants';
|
|
3
8
|
|
|
4
9
|
// Checkbox/Toggle Icon
|
|
5
10
|
const CheckIcon = ({ checked }: { checked: boolean }) => (
|
|
6
|
-
<svg
|
|
7
|
-
width="14"
|
|
8
|
-
height="14"
|
|
9
|
-
viewBox="0 0 24 24"
|
|
10
|
-
fill="none"
|
|
11
|
-
stroke={checked ? '#10b981' : '#6b7280'}
|
|
11
|
+
<svg
|
|
12
|
+
width="14"
|
|
13
|
+
height="14"
|
|
14
|
+
viewBox="0 0 24 24"
|
|
15
|
+
fill="none"
|
|
16
|
+
stroke={checked ? '#10b981' : '#6b7280'}
|
|
12
17
|
strokeWidth="3"
|
|
13
|
-
strokeLinecap="round"
|
|
18
|
+
strokeLinecap="round"
|
|
14
19
|
strokeLinejoin="round"
|
|
15
20
|
>
|
|
16
21
|
{checked && <path d="M20 6L9 17l-5-5" />}
|
|
@@ -18,16 +23,16 @@ const CheckIcon = ({ checked }: { checked: boolean }) => (
|
|
|
18
23
|
);
|
|
19
24
|
|
|
20
25
|
// Legend Item with Toggle
|
|
21
|
-
function LegendItemWithToggle({
|
|
22
|
-
color,
|
|
23
|
-
label,
|
|
26
|
+
function LegendItemWithToggle({
|
|
27
|
+
color,
|
|
28
|
+
label,
|
|
24
29
|
isGlow = false,
|
|
25
30
|
isLine = false,
|
|
26
31
|
colors,
|
|
27
32
|
isVisible,
|
|
28
|
-
onToggle
|
|
29
|
-
}: {
|
|
30
|
-
color: string;
|
|
33
|
+
onToggle,
|
|
34
|
+
}: {
|
|
35
|
+
color: string;
|
|
31
36
|
label: string;
|
|
32
37
|
isGlow?: boolean;
|
|
33
38
|
isLine?: boolean;
|
|
@@ -36,13 +41,13 @@ function LegendItemWithToggle({
|
|
|
36
41
|
onToggle: () => void;
|
|
37
42
|
}) {
|
|
38
43
|
return (
|
|
39
|
-
<button
|
|
44
|
+
<button
|
|
40
45
|
onClick={onToggle}
|
|
41
46
|
className="group cursor-pointer transition-all hover:bg-white/5 w-full"
|
|
42
|
-
style={{
|
|
43
|
-
display: 'flex',
|
|
44
|
-
alignItems: 'center',
|
|
45
|
-
gap: '8px',
|
|
47
|
+
style={{
|
|
48
|
+
display: 'flex',
|
|
49
|
+
alignItems: 'center',
|
|
50
|
+
gap: '8px',
|
|
46
51
|
padding: '6px 8px',
|
|
47
52
|
borderRadius: '8px',
|
|
48
53
|
opacity: isVisible ? 1 : 0.4,
|
|
@@ -50,35 +55,46 @@ function LegendItemWithToggle({
|
|
|
50
55
|
title={isVisible ? `Click to hide ${label}` : `Click to show ${label}`}
|
|
51
56
|
>
|
|
52
57
|
{/* Toggle checkbox */}
|
|
53
|
-
<div
|
|
58
|
+
<div
|
|
54
59
|
className="flex-shrink-0 w-5 h-5 rounded border-2 flex items-center justify-center transition-colors"
|
|
55
|
-
style={{
|
|
60
|
+
style={{
|
|
56
61
|
borderColor: isVisible ? '#10b981' : colors.panelBorder,
|
|
57
|
-
backgroundColor: isVisible ? `${color}20` : 'transparent'
|
|
62
|
+
backgroundColor: isVisible ? `${color}20` : 'transparent',
|
|
58
63
|
}}
|
|
59
64
|
>
|
|
60
65
|
{isVisible && (
|
|
61
|
-
<svg
|
|
62
|
-
|
|
66
|
+
<svg
|
|
67
|
+
width="12"
|
|
68
|
+
height="12"
|
|
69
|
+
viewBox="0 0 24 24"
|
|
70
|
+
fill="none"
|
|
71
|
+
stroke={color}
|
|
72
|
+
strokeWidth="3"
|
|
73
|
+
>
|
|
74
|
+
<path
|
|
75
|
+
d="M20 6L9 17l-5-5"
|
|
76
|
+
strokeLinecap="round"
|
|
77
|
+
strokeLinejoin="round"
|
|
78
|
+
/>
|
|
63
79
|
</svg>
|
|
64
80
|
)}
|
|
65
81
|
</div>
|
|
66
|
-
|
|
82
|
+
|
|
67
83
|
{isLine ? (
|
|
68
|
-
<span
|
|
69
|
-
className="w-10 h-1 rounded-full flex-shrink-0"
|
|
70
|
-
style={{ backgroundColor: color }}
|
|
84
|
+
<span
|
|
85
|
+
className="w-10 h-1 rounded-full flex-shrink-0"
|
|
86
|
+
style={{ backgroundColor: color }}
|
|
71
87
|
/>
|
|
72
88
|
) : (
|
|
73
|
-
<span
|
|
89
|
+
<span
|
|
74
90
|
className={`w-4 h-4 rounded-full flex-shrink-0 ${isGlow ? 'shadow-lg' : ''}`}
|
|
75
|
-
style={{
|
|
76
|
-
backgroundColor: color,
|
|
77
|
-
boxShadow: isGlow && isVisible ? `0 0 10px ${color}90` : 'none'
|
|
78
|
-
}}
|
|
91
|
+
style={{
|
|
92
|
+
backgroundColor: color,
|
|
93
|
+
boxShadow: isGlow && isVisible ? `0 0 10px ${color}90` : 'none',
|
|
94
|
+
}}
|
|
79
95
|
/>
|
|
80
96
|
)}
|
|
81
|
-
<span
|
|
97
|
+
<span
|
|
82
98
|
className="text-sm font-medium transition-colors leading-tight"
|
|
83
99
|
style={{ color: colors.textMuted }}
|
|
84
100
|
>
|
|
@@ -89,45 +105,45 @@ function LegendItemWithToggle({
|
|
|
89
105
|
}
|
|
90
106
|
|
|
91
107
|
// Regular Legend Item (non-toggleable)
|
|
92
|
-
function LegendItem({
|
|
93
|
-
color,
|
|
94
|
-
label,
|
|
108
|
+
function LegendItem({
|
|
109
|
+
color,
|
|
110
|
+
label,
|
|
95
111
|
isGlow = false,
|
|
96
112
|
isLine = false,
|
|
97
|
-
colors
|
|
98
|
-
}: {
|
|
99
|
-
color: string;
|
|
113
|
+
colors,
|
|
114
|
+
}: {
|
|
115
|
+
color: string;
|
|
100
116
|
label: string;
|
|
101
117
|
isGlow?: boolean;
|
|
102
118
|
isLine?: boolean;
|
|
103
119
|
colors: ThemeColors;
|
|
104
120
|
}) {
|
|
105
121
|
return (
|
|
106
|
-
<div
|
|
122
|
+
<div
|
|
107
123
|
className="group cursor-default transition-all hover:bg-white/5"
|
|
108
|
-
style={{
|
|
109
|
-
display: 'flex',
|
|
110
|
-
alignItems: 'center',
|
|
111
|
-
gap: '8px',
|
|
124
|
+
style={{
|
|
125
|
+
display: 'flex',
|
|
126
|
+
alignItems: 'center',
|
|
127
|
+
gap: '8px',
|
|
112
128
|
padding: '6px 0',
|
|
113
|
-
borderRadius: '8px'
|
|
129
|
+
borderRadius: '8px',
|
|
114
130
|
}}
|
|
115
131
|
>
|
|
116
132
|
{isLine ? (
|
|
117
|
-
<span
|
|
118
|
-
className="w-10 h-1 rounded-full transition-transform group-hover:scale-y-150 flex-shrink-0"
|
|
119
|
-
style={{ backgroundColor: color }}
|
|
133
|
+
<span
|
|
134
|
+
className="w-10 h-1 rounded-full transition-transform group-hover:scale-y-150 flex-shrink-0"
|
|
135
|
+
style={{ backgroundColor: color }}
|
|
120
136
|
/>
|
|
121
137
|
) : (
|
|
122
|
-
<span
|
|
138
|
+
<span
|
|
123
139
|
className={`w-4 h-4 rounded-full transition-transform group-hover:scale-125 flex-shrink-0 ${isGlow ? 'shadow-lg' : ''}`}
|
|
124
|
-
style={{
|
|
125
|
-
backgroundColor: color,
|
|
126
|
-
boxShadow: isGlow ? `0 0 10px ${color}90` : 'none'
|
|
127
|
-
}}
|
|
140
|
+
style={{
|
|
141
|
+
backgroundColor: color,
|
|
142
|
+
boxShadow: isGlow ? `0 0 10px ${color}90` : 'none',
|
|
143
|
+
}}
|
|
128
144
|
/>
|
|
129
145
|
)}
|
|
130
|
-
<span
|
|
146
|
+
<span
|
|
131
147
|
className="text-sm font-medium transition-colors leading-tight"
|
|
132
148
|
style={{ color: colors.textMuted }}
|
|
133
149
|
>
|
|
@@ -146,9 +162,9 @@ interface LegendPanelProps {
|
|
|
146
162
|
metadata?: BusinessMetrics;
|
|
147
163
|
}
|
|
148
164
|
|
|
149
|
-
export function LegendPanel({
|
|
150
|
-
colors,
|
|
151
|
-
visibleSeverities,
|
|
165
|
+
export function LegendPanel({
|
|
166
|
+
colors,
|
|
167
|
+
visibleSeverities,
|
|
152
168
|
visibleEdgeTypes,
|
|
153
169
|
onToggleSeverity,
|
|
154
170
|
onToggleEdgeType,
|
|
@@ -158,15 +174,22 @@ export function LegendPanel({
|
|
|
158
174
|
const visibleSeverityCount = visibleSeverities.size;
|
|
159
175
|
const totalSeverities = Object.keys(severityColors).length - 1; // -1 for 'default'
|
|
160
176
|
const visibleEdgeCount = visibleEdgeTypes.size;
|
|
161
|
-
const totalEdgeTypes = Object.keys(edgeColors).filter(
|
|
177
|
+
const totalEdgeTypes = Object.keys(edgeColors).filter(
|
|
178
|
+
(k) => k !== 'default' && k !== 'reference'
|
|
179
|
+
).length;
|
|
162
180
|
|
|
163
181
|
return (
|
|
164
182
|
<div style={{ padding: '16px 16px', animation: 'fadeIn 0.2s ease-in' }}>
|
|
165
183
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '20px' }}>
|
|
166
184
|
{/* Header */}
|
|
167
|
-
<div
|
|
168
|
-
|
|
169
|
-
|
|
185
|
+
<div
|
|
186
|
+
style={{
|
|
187
|
+
paddingBottom: '16px',
|
|
188
|
+
borderBottom: `1px solid ${colors.cardBorder}`,
|
|
189
|
+
}}
|
|
190
|
+
>
|
|
191
|
+
<h2
|
|
192
|
+
className="text-base font-bold tracking-wide"
|
|
170
193
|
style={{ color: colors.text }}
|
|
171
194
|
>
|
|
172
195
|
Legend
|
|
@@ -175,16 +198,28 @@ export function LegendPanel({
|
|
|
175
198
|
|
|
176
199
|
{/* Severity Legend with Toggles */}
|
|
177
200
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
|
178
|
-
<div
|
|
179
|
-
|
|
180
|
-
|
|
201
|
+
<div
|
|
202
|
+
style={{
|
|
203
|
+
display: 'flex',
|
|
204
|
+
justifyContent: 'space-between',
|
|
205
|
+
alignItems: 'center',
|
|
206
|
+
marginBottom: '4px',
|
|
207
|
+
}}
|
|
208
|
+
>
|
|
209
|
+
<h3
|
|
210
|
+
className="text-xs font-bold uppercase tracking-widest"
|
|
181
211
|
style={{ color: colors.textMuted }}
|
|
182
212
|
>
|
|
183
213
|
Severity
|
|
184
214
|
</h3>
|
|
185
|
-
<span
|
|
186
|
-
className="text-xs font-medium"
|
|
187
|
-
style={{
|
|
215
|
+
<span
|
|
216
|
+
className="text-xs font-medium"
|
|
217
|
+
style={{
|
|
218
|
+
color:
|
|
219
|
+
visibleSeverityCount === totalSeverities
|
|
220
|
+
? '#10b981'
|
|
221
|
+
: '#f59e0b',
|
|
222
|
+
}}
|
|
188
223
|
>
|
|
189
224
|
{visibleSeverityCount}/{totalSeverities}
|
|
190
225
|
</span>
|
|
@@ -193,7 +228,7 @@ export function LegendPanel({
|
|
|
193
228
|
{Object.entries(severityColors)
|
|
194
229
|
.filter(([key]) => key !== 'default') // Filter out "No Issues" - not useful for visualization
|
|
195
230
|
.map(([key, color]) => (
|
|
196
|
-
<LegendItemWithToggle
|
|
231
|
+
<LegendItemWithToggle
|
|
197
232
|
key={key}
|
|
198
233
|
color={color}
|
|
199
234
|
label={key.charAt(0).toUpperCase() + key.slice(1)}
|
|
@@ -208,16 +243,26 @@ export function LegendPanel({
|
|
|
208
243
|
|
|
209
244
|
{/* Connections Legend with Toggles */}
|
|
210
245
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
|
211
|
-
<div
|
|
212
|
-
|
|
213
|
-
|
|
246
|
+
<div
|
|
247
|
+
style={{
|
|
248
|
+
display: 'flex',
|
|
249
|
+
justifyContent: 'space-between',
|
|
250
|
+
alignItems: 'center',
|
|
251
|
+
marginBottom: '4px',
|
|
252
|
+
}}
|
|
253
|
+
>
|
|
254
|
+
<h3
|
|
255
|
+
className="text-xs font-bold uppercase tracking-widest"
|
|
214
256
|
style={{ color: colors.textMuted }}
|
|
215
257
|
>
|
|
216
258
|
Connections
|
|
217
259
|
</h3>
|
|
218
|
-
<span
|
|
219
|
-
className="text-xs font-medium"
|
|
220
|
-
style={{
|
|
260
|
+
<span
|
|
261
|
+
className="text-xs font-medium"
|
|
262
|
+
style={{
|
|
263
|
+
color:
|
|
264
|
+
visibleEdgeCount === totalEdgeTypes ? '#10b981' : '#f59e0b',
|
|
265
|
+
}}
|
|
221
266
|
>
|
|
222
267
|
{visibleEdgeCount}/{totalEdgeTypes}
|
|
223
268
|
</span>
|
|
@@ -226,7 +271,7 @@ export function LegendPanel({
|
|
|
226
271
|
{Object.entries(edgeColors)
|
|
227
272
|
.filter(([k]) => k !== 'default' && k !== 'reference')
|
|
228
273
|
.map(([key, color]) => (
|
|
229
|
-
<LegendItemWithToggle
|
|
274
|
+
<LegendItemWithToggle
|
|
230
275
|
key={key}
|
|
231
276
|
color={color}
|
|
232
277
|
label={key.charAt(0).toUpperCase() + key.slice(1)}
|
|
@@ -241,93 +286,151 @@ export function LegendPanel({
|
|
|
241
286
|
|
|
242
287
|
{/* Node Size Info */}
|
|
243
288
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
|
|
244
|
-
<h3
|
|
245
|
-
className="text-xs font-bold uppercase tracking-widest"
|
|
289
|
+
<h3
|
|
290
|
+
className="text-xs font-bold uppercase tracking-widest"
|
|
246
291
|
style={{ color: colors.textMuted, marginBottom: '4px' }}
|
|
247
292
|
>
|
|
248
293
|
Node Size
|
|
249
294
|
</h3>
|
|
250
|
-
<div
|
|
251
|
-
|
|
252
|
-
|
|
295
|
+
<div
|
|
296
|
+
style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}
|
|
297
|
+
>
|
|
298
|
+
<p
|
|
299
|
+
className="text-xs leading-relaxed"
|
|
253
300
|
style={{ color: colors.textMuted }}
|
|
254
301
|
>
|
|
255
|
-
Larger nodes indicate higher
|
|
302
|
+
Larger nodes indicate higher{' '}
|
|
303
|
+
<span className="text-cyan-400 font-medium">token cost</span> and
|
|
304
|
+
more <span className="text-purple-400 font-medium">issues</span>.
|
|
256
305
|
</p>
|
|
257
306
|
<div className="flex items-center gap-3">
|
|
258
307
|
<div className="flex -space-x-2">
|
|
259
|
-
<span
|
|
260
|
-
|
|
261
|
-
|
|
308
|
+
<span
|
|
309
|
+
className="w-3 h-3 rounded-full bg-cyan-400 border-2"
|
|
310
|
+
style={{ borderColor: colors.panel }}
|
|
311
|
+
/>
|
|
312
|
+
<span
|
|
313
|
+
className="w-5 h-5 rounded-full bg-cyan-400 border-2"
|
|
314
|
+
style={{ borderColor: colors.panel }}
|
|
315
|
+
/>
|
|
316
|
+
<span
|
|
317
|
+
className="w-7 h-7 rounded-full bg-cyan-400 border-2"
|
|
318
|
+
style={{ borderColor: colors.panel }}
|
|
319
|
+
/>
|
|
262
320
|
</div>
|
|
263
|
-
<span className="text-xs" style={{ color: colors.textMuted }}
|
|
264
|
-
|
|
321
|
+
<span className="text-xs" style={{ color: colors.textMuted }}>
|
|
322
|
+
→
|
|
323
|
+
</span>
|
|
324
|
+
<span
|
|
325
|
+
className="text-xs font-medium"
|
|
326
|
+
style={{ color: colors.text }}
|
|
327
|
+
>
|
|
328
|
+
More Impact
|
|
329
|
+
</span>
|
|
265
330
|
</div>
|
|
266
331
|
</div>
|
|
267
332
|
</div>
|
|
268
333
|
|
|
269
334
|
{/* Business Impact Section */}
|
|
270
|
-
{metadata &&
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
335
|
+
{metadata &&
|
|
336
|
+
(metadata.estimatedMonthlyCost ||
|
|
337
|
+
metadata.estimatedDeveloperHours ||
|
|
338
|
+
metadata.aiAcceptanceRate) && (
|
|
339
|
+
<div
|
|
340
|
+
style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}
|
|
275
341
|
>
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
>
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
342
|
+
<h3
|
|
343
|
+
className="text-xs font-bold uppercase tracking-widest"
|
|
344
|
+
style={{ color: colors.textMuted, marginBottom: '4px' }}
|
|
345
|
+
>
|
|
346
|
+
Business Impact
|
|
347
|
+
</h3>
|
|
348
|
+
<div
|
|
349
|
+
className="p-3 rounded-xl"
|
|
350
|
+
style={{
|
|
351
|
+
backgroundColor: `${colors.cardBg}80`,
|
|
352
|
+
display: 'flex',
|
|
353
|
+
flexDirection: 'column',
|
|
354
|
+
gap: '6px',
|
|
355
|
+
}}
|
|
356
|
+
>
|
|
357
|
+
{metadata.estimatedMonthlyCost !== undefined && (
|
|
358
|
+
<div className="flex justify-between items-center py-1 px-2 rounded-lg hover:bg-white/5 transition-colors">
|
|
359
|
+
<span
|
|
360
|
+
className="text-xs font-medium"
|
|
361
|
+
style={{ color: colors.textMuted }}
|
|
362
|
+
>
|
|
363
|
+
💰 Monthly Cost
|
|
364
|
+
</span>
|
|
365
|
+
<span
|
|
366
|
+
className="text-xs font-bold"
|
|
367
|
+
style={{ color: '#f59e0b' }}
|
|
368
|
+
>
|
|
369
|
+
{metadata.estimatedMonthlyCost >= 1000
|
|
370
|
+
? `$${(metadata.estimatedMonthlyCost / 1000).toFixed(1)}k`
|
|
371
|
+
: `$${metadata.estimatedMonthlyCost.toFixed(0)}`}
|
|
372
|
+
</span>
|
|
373
|
+
</div>
|
|
374
|
+
)}
|
|
375
|
+
{metadata.estimatedDeveloperHours !== undefined && (
|
|
376
|
+
<div className="flex justify-between items-center py-1 px-2 rounded-lg hover:bg-white/5 transition-colors">
|
|
377
|
+
<span
|
|
378
|
+
className="text-xs font-medium"
|
|
379
|
+
style={{ color: colors.textMuted }}
|
|
380
|
+
>
|
|
381
|
+
⏱️ Fix Time
|
|
382
|
+
</span>
|
|
383
|
+
<span
|
|
384
|
+
className="text-xs font-bold"
|
|
385
|
+
style={{ color: '#22d3ee' }}
|
|
386
|
+
>
|
|
387
|
+
{metadata.estimatedDeveloperHours >= 40
|
|
388
|
+
? `${(metadata.estimatedDeveloperHours / 40).toFixed(1)}w`
|
|
389
|
+
: `${metadata.estimatedDeveloperHours.toFixed(1)}h`}
|
|
390
|
+
</span>
|
|
391
|
+
</div>
|
|
392
|
+
)}
|
|
393
|
+
{metadata.aiAcceptanceRate !== undefined && (
|
|
394
|
+
<div className="flex justify-between items-center py-1 px-2 rounded-lg hover:bg-white/5 transition-colors">
|
|
395
|
+
<span
|
|
396
|
+
className="text-xs font-medium"
|
|
397
|
+
style={{ color: colors.textMuted }}
|
|
398
|
+
>
|
|
399
|
+
🤖 AI Acceptance
|
|
400
|
+
</span>
|
|
401
|
+
<span
|
|
402
|
+
className="text-xs font-bold"
|
|
403
|
+
style={{
|
|
404
|
+
color:
|
|
405
|
+
metadata.aiAcceptanceRate >= 0.7
|
|
406
|
+
? '#4ade80'
|
|
407
|
+
: metadata.aiAcceptanceRate >= 0.5
|
|
408
|
+
? '#fb923c'
|
|
409
|
+
: '#f87171',
|
|
410
|
+
}}
|
|
411
|
+
>
|
|
412
|
+
{Math.round(metadata.aiAcceptanceRate * 100)}%
|
|
413
|
+
</span>
|
|
414
|
+
</div>
|
|
415
|
+
)}
|
|
416
|
+
</div>
|
|
315
417
|
</div>
|
|
316
|
-
|
|
317
|
-
)}
|
|
418
|
+
)}
|
|
318
419
|
|
|
319
420
|
{/* Quick Tips */}
|
|
320
|
-
<div
|
|
421
|
+
<div
|
|
321
422
|
className="p-4 rounded-xl"
|
|
322
|
-
style={{
|
|
323
|
-
backgroundColor: `${colors.cardBg}80
|
|
423
|
+
style={{
|
|
424
|
+
backgroundColor: `${colors.cardBg}80`,
|
|
324
425
|
}}
|
|
325
426
|
>
|
|
326
|
-
<p
|
|
327
|
-
className="text-xs leading-relaxed"
|
|
427
|
+
<p
|
|
428
|
+
className="text-xs leading-relaxed"
|
|
328
429
|
style={{ color: colors.textMuted }}
|
|
329
430
|
>
|
|
330
|
-
<span className="font-semibold text-amber-400">💡 Tip:</span> Click
|
|
431
|
+
<span className="font-semibold text-amber-400">💡 Tip:</span> Click
|
|
432
|
+
and drag nodes to rearrange. Scroll to zoom. Toggle items above to
|
|
433
|
+
filter.
|
|
331
434
|
</p>
|
|
332
435
|
</div>
|
|
333
436
|
</div>
|
|
@@ -6,10 +6,13 @@ interface LoadingSpinnerProps {
|
|
|
6
6
|
|
|
7
7
|
export function LoadingSpinner({ colors }: LoadingSpinnerProps) {
|
|
8
8
|
return (
|
|
9
|
-
<div
|
|
9
|
+
<div
|
|
10
|
+
className="flex h-screen items-center justify-center"
|
|
11
|
+
style={{ backgroundColor: colors.bg, color: colors.text }}
|
|
12
|
+
>
|
|
10
13
|
<div className="text-center">
|
|
11
|
-
<div
|
|
12
|
-
className="animate-spin rounded-full h-12 w-12 border-b-2 mx-auto mb-4"
|
|
14
|
+
<div
|
|
15
|
+
className="animate-spin rounded-full h-12 w-12 border-b-2 mx-auto mb-4"
|
|
13
16
|
style={{ borderColor: colors.text }}
|
|
14
17
|
/>
|
|
15
18
|
<p>Loading visualization...</p>
|