@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.
@@ -1,16 +1,21 @@
1
- import { ThemeColors, SeverityLevel, EdgeType, BusinessMetrics } from '../types';
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 width="12" height="12" viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth="3">
62
- <path d="M20 6L9 17l-5-5" strokeLinecap="round" strokeLinejoin="round" />
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(k => k !== 'default' && k !== 'reference').length;
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 style={{ paddingBottom: '16px', borderBottom: `1px solid ${colors.cardBorder}` }}>
168
- <h2
169
- className="text-base font-bold tracking-wide"
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 style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '4px' }}>
179
- <h3
180
- className="text-xs font-bold uppercase tracking-widest"
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={{ color: visibleSeverityCount === totalSeverities ? '#10b981' : '#f59e0b' }}
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 style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '4px' }}>
212
- <h3
213
- className="text-xs font-bold uppercase tracking-widest"
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={{ color: visibleEdgeCount === totalEdgeTypes ? '#10b981' : '#f59e0b' }}
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 style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
251
- <p
252
- className="text-xs leading-relaxed"
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 <span className="text-cyan-400 font-medium">token cost</span> and more <span className="text-purple-400 font-medium">issues</span>.
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 className="w-3 h-3 rounded-full bg-cyan-400 border-2" style={{ borderColor: colors.panel }} />
260
- <span className="w-5 h-5 rounded-full bg-cyan-400 border-2" style={{ borderColor: colors.panel }} />
261
- <span className="w-7 h-7 rounded-full bg-cyan-400 border-2" style={{ borderColor: colors.panel }} />
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 }}>→</span>
264
- <span className="text-xs font-medium" style={{ color: colors.text }}>More Impact</span>
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 && (metadata.estimatedMonthlyCost || metadata.estimatedDeveloperHours || metadata.aiAcceptanceRate) && (
271
- <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
272
- <h3
273
- className="text-xs font-bold uppercase tracking-widest"
274
- style={{ color: colors.textMuted, marginBottom: '4px' }}
335
+ {metadata &&
336
+ (metadata.estimatedMonthlyCost ||
337
+ metadata.estimatedDeveloperHours ||
338
+ metadata.aiAcceptanceRate) && (
339
+ <div
340
+ style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}
275
341
  >
276
- Business Impact
277
- </h3>
278
- <div
279
- className="p-3 rounded-xl"
280
- style={{ backgroundColor: `${colors.cardBg}80`, display: 'flex', flexDirection: 'column', gap: '6px' }}
281
- >
282
- {metadata.estimatedMonthlyCost !== undefined && (
283
- <div className="flex justify-between items-center py-1 px-2 rounded-lg hover:bg-white/5 transition-colors">
284
- <span className="text-xs font-medium" style={{ color: colors.textMuted }}>💰 Monthly Cost</span>
285
- <span className="text-xs font-bold" style={{ color: '#f59e0b' }}>
286
- {metadata.estimatedMonthlyCost >= 1000
287
- ? `$${(metadata.estimatedMonthlyCost / 1000).toFixed(1)}k`
288
- : `$${metadata.estimatedMonthlyCost.toFixed(0)}`}
289
- </span>
290
- </div>
291
- )}
292
- {metadata.estimatedDeveloperHours !== undefined && (
293
- <div className="flex justify-between items-center py-1 px-2 rounded-lg hover:bg-white/5 transition-colors">
294
- <span className="text-xs font-medium" style={{ color: colors.textMuted }}>⏱️ Fix Time</span>
295
- <span className="text-xs font-bold" style={{ color: '#22d3ee' }}>
296
- {metadata.estimatedDeveloperHours >= 40
297
- ? `${(metadata.estimatedDeveloperHours / 40).toFixed(1)}w`
298
- : `${metadata.estimatedDeveloperHours.toFixed(1)}h`}
299
- </span>
300
- </div>
301
- )}
302
- {metadata.aiAcceptanceRate !== undefined && (
303
- <div className="flex justify-between items-center py-1 px-2 rounded-lg hover:bg-white/5 transition-colors">
304
- <span className="text-xs font-medium" style={{ color: colors.textMuted }}>🤖 AI Acceptance</span>
305
- <span
306
- className="text-xs font-bold"
307
- style={{
308
- color: metadata.aiAcceptanceRate >= 0.7 ? '#4ade80' : metadata.aiAcceptanceRate >= 0.5 ? '#fb923c' : '#f87171'
309
- }}
310
- >
311
- {Math.round(metadata.aiAcceptanceRate * 100)}%
312
- </span>
313
- </div>
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
- </div>
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 and drag nodes to rearrange. Scroll to zoom. Toggle items above to filter.
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 className="flex h-screen items-center justify-center" style={{ backgroundColor: colors.bg, color: colors.text }}>
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>