devintelligence 0.0.0-stage → 1.0.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 (45) hide show
  1. package/.env.example +14 -0
  2. package/LICENSE +22 -0
  3. package/README.md +145 -2
  4. package/bin/devintelligence.js +17 -0
  5. package/bin/devintelligence.ts +275 -0
  6. package/fixtures/django-analytics-service/requirements.txt +4 -0
  7. package/fixtures/django-analytics-service/settings.py +16 -0
  8. package/fixtures/express-api-gateway/package.json +14 -0
  9. package/fixtures/express-api-gateway/src/server.ts +16 -0
  10. package/fixtures/nextjs-saas-portal/package.json +21 -0
  11. package/fixtures/nextjs-saas-portal/src/app/dashboard/page.tsx +26 -0
  12. package/fixtures/rn-bare-fitness/android/build.gradle +19 -0
  13. package/fixtures/rn-bare-fitness/ios/Info.plist +14 -0
  14. package/fixtures/rn-bare-fitness/package.json +23 -0
  15. package/fixtures/rn-bare-fitness/src/WorkoutTracker.tsx +31 -0
  16. package/fixtures/rn-expo-commerce/android/app/build.gradle +20 -0
  17. package/fixtures/rn-expo-commerce/android/app/src/main/AndroidManifest.xml +21 -0
  18. package/fixtures/rn-expo-commerce/android/gradle.properties +6 -0
  19. package/fixtures/rn-expo-commerce/app.json +17 -0
  20. package/fixtures/rn-expo-commerce/babel.config.js +10 -0
  21. package/fixtures/rn-expo-commerce/ios/Podfile +13 -0
  22. package/fixtures/rn-expo-commerce/metro.config.js +7 -0
  23. package/fixtures/rn-expo-commerce/package.json +31 -0
  24. package/fixtures/rn-expo-commerce/src/components/LegacyPromoBanner.tsx +11 -0
  25. package/fixtures/rn-expo-commerce/src/screens/ProductFeedScreen.tsx +48 -0
  26. package/fixtures/rn-expo-commerce/src/services/api.ts +15 -0
  27. package/index.html +21 -0
  28. package/package.json +75 -4
  29. package/server.ts +275 -0
  30. package/src/App.tsx +783 -0
  31. package/src/components/DeepAnalysisViews.tsx +526 -0
  32. package/src/components/MobileAndPlatformViews.tsx +625 -0
  33. package/src/components/OverviewAndExplorerViews.tsx +753 -0
  34. package/src/components/RepairAndGateViews.tsx +814 -0
  35. package/src/engine/aiEngine.test.ts +86 -0
  36. package/src/engine/aiEngine.ts +346 -0
  37. package/src/engine/analyzerEngine.test.ts +110 -0
  38. package/src/engine/analyzerEngine.ts +1579 -0
  39. package/src/engine/repairEngine.ts +545 -0
  40. package/src/index.css +44 -0
  41. package/src/index.ts +59 -0
  42. package/src/main.tsx +5 -0
  43. package/src/types/devintelligence.ts +310 -0
  44. package/tsconfig.json +28 -0
  45. package/vite.config.ts +22 -0
@@ -0,0 +1,526 @@
1
+ import React from 'react';
2
+ import { ProjectIntelligenceReport } from '../types/devintelligence.ts';
3
+ import { SeverityText } from './OverviewAndExplorerViews.tsx';
4
+
5
+ interface AnalysisViewProps {
6
+ report: ProjectIntelligenceReport;
7
+ onFixIssue: (issueId: string, allowReview?: boolean) => void;
8
+ isBusy: boolean;
9
+ }
10
+
11
+ export function CodeIntelligenceView({ report, onFixIssue, isBusy }: AnalysisViewProps) {
12
+ const codeIssues = report.issues.filter(
13
+ (i) => i.domain === 'code' || i.domain === 'react-native'
14
+ );
15
+
16
+ return (
17
+ <div className="space-y-8">
18
+ <div className="pb-5 border-b border-slate-800/80 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
19
+ <div>
20
+ <h1 className="text-xl font-bold text-slate-100">
21
+ Deep Code & Architecture Intelligence
22
+ </h1>
23
+ <p className="text-xs text-slate-400 mt-1">
24
+ Detects unreferenced modules, unused exports, hook subscription memory leaks, and async error handling gaps while respecting framework entrypoints.
25
+ </p>
26
+ </div>
27
+ <div className="text-xs font-mono tabular-nums text-slate-300">
28
+ Code Quality Score: <strong className="text-blue-400">{report.scores.codeQuality}/100</strong>
29
+ </div>
30
+ </div>
31
+
32
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
33
+ <div className="p-4 bg-slate-900/70 border border-slate-800/80 rounded-lg">
34
+ <div className="text-xs text-slate-400">Indexed Source & Config Files</div>
35
+ <div className="mt-1.5 text-xl font-bold font-mono tabular-nums text-slate-100">
36
+ {report.filesAnalyzed.length}
37
+ </div>
38
+ <div className="mt-1 text-xs text-slate-400">
39
+ Dynamic routes & framework conventions preserved
40
+ </div>
41
+ </div>
42
+
43
+ <div className="p-4 bg-slate-900/70 border border-slate-800/80 rounded-lg">
44
+ <div className="text-xs text-slate-400">Unreferenced / Dead Modules</div>
45
+ <div className="mt-1.5 text-xl font-bold font-mono tabular-nums text-amber-400">
46
+ {report.issues.filter((i) => i.ruleId === 'DEAD_CODE_UNUSED_MODULE').length}
47
+ </div>
48
+ <div className="mt-1 text-xs text-slate-400">
49
+ Flagged as REVIEW_REQUIRED (never auto-deleted without consent)
50
+ </div>
51
+ </div>
52
+
53
+ <div className="p-4 bg-slate-900/70 border border-slate-800/80 rounded-lg">
54
+ <div className="text-xs text-slate-400">Lifecycle & Hook Findings</div>
55
+ <div className="mt-1.5 text-xl font-bold font-mono tabular-nums text-slate-100">
56
+ {codeIssues.length}
57
+ </div>
58
+ <div className="mt-1 text-xs text-slate-400">
59
+ Includes event listener & interval cleanup verification
60
+ </div>
61
+ </div>
62
+ </div>
63
+
64
+ <div className="space-y-3">
65
+ <h2 className="text-base font-semibold text-slate-100">
66
+ Code & Lifecycle Findings ({codeIssues.length})
67
+ </h2>
68
+ {codeIssues.length === 0 ? (
69
+ <div className="p-6 bg-slate-900/50 border border-slate-800 rounded-lg text-xs text-emerald-400">
70
+ No dead code or lifecycle defects detected in {report.name}.
71
+ </div>
72
+ ) : (
73
+ <div className="space-y-3">
74
+ {codeIssues.map((issue) => (
75
+ <div
76
+ key={issue.id}
77
+ className="p-4 bg-slate-900/70 border border-slate-800/80 rounded-lg space-y-3"
78
+ >
79
+ <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
80
+ <div className="space-y-1">
81
+ <div className="flex items-center gap-2 text-xs">
82
+ <SeverityText severity={issue.severity} />
83
+ <span className="text-slate-500">·</span>
84
+ <span className="font-mono text-slate-300">
85
+ {issue.filePath}:{issue.line}
86
+ </span>
87
+ <span className="text-slate-500">·</span>
88
+ <span className="font-mono text-slate-400">{issue.repairTier}</span>
89
+ </div>
90
+ <div className="text-sm font-semibold text-slate-100">{issue.title}</div>
91
+ </div>
92
+ {issue.fixAvailable && (
93
+ <button
94
+ type="button"
95
+ disabled={isBusy}
96
+ onClick={() => onFixIssue(issue.id, issue.repairTier !== 'SAFE')}
97
+ className="px-4 py-2 text-xs font-semibold text-white bg-blue-600 hover:bg-blue-500 rounded-lg shrink-0 cursor-pointer whitespace-nowrap"
98
+ >
99
+ Apply Safe Repair
100
+ </button>
101
+ )}
102
+ </div>
103
+ <p className="text-xs text-slate-400">{issue.explanation}</p>
104
+ <pre className="p-3 bg-slate-950 border border-slate-800 rounded text-[11px] font-mono text-slate-300 overflow-x-auto">
105
+ {issue.unifiedDiff}
106
+ </pre>
107
+ </div>
108
+ ))}
109
+ </div>
110
+ )}
111
+ </div>
112
+ </div>
113
+ );
114
+ }
115
+
116
+ export function DesignIntelligenceView({ report, onFixIssue, isBusy }: AnalysisViewProps) {
117
+ const designIssues = report.issues.filter((i) => i.domain === 'design');
118
+
119
+ return (
120
+ <div className="space-y-8">
121
+ <div className="pb-5 border-b border-slate-800/80 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
122
+ <div>
123
+ <h1 className="text-xl font-bold text-slate-100">
124
+ Design System & UI/UX Token Intelligence
125
+ </h1>
126
+ <p className="text-xs text-slate-400 mt-1">
127
+ Discovers existing project design tokens first, then detects hardcoded hex colors, non-grid border-radius/spacing, emoji UI icons, and missing accessibility labels.
128
+ </p>
129
+ </div>
130
+ <div className="text-xs font-mono tabular-nums text-slate-300">
131
+ Token Compliance: <strong className="text-blue-400">{report.designSystem.tokenComplianceScore}/100</strong>
132
+ </div>
133
+ </div>
134
+
135
+ {/* Discovered Design System Tokens */}
136
+ <div className="p-5 bg-slate-900/70 border border-slate-800/80 rounded-lg space-y-4">
137
+ <div className="text-sm font-semibold text-slate-100">
138
+ Discovered Project Design System Baseline
139
+ </div>
140
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-xs">
141
+ <div className="space-y-2">
142
+ <div className="text-slate-400">Semantic Color Palette</div>
143
+ <div className="flex flex-wrap gap-2">
144
+ {report.designSystem.discoveredTokens.colors.map((hex) => (
145
+ <div
146
+ key={hex}
147
+ className="flex items-center gap-1.5 px-2.5 py-1 bg-slate-950 border border-slate-800 rounded font-mono text-slate-300"
148
+ >
149
+ <span
150
+ className="w-3 h-3 rounded-sm border border-slate-700"
151
+ style={{ backgroundColor: hex }}
152
+ />
153
+ <span>{hex}</span>
154
+ </div>
155
+ ))}
156
+ </div>
157
+ </div>
158
+ <div className="space-y-2">
159
+ <div className="text-slate-400">4pt/8pt Spacing & Radius Scale</div>
160
+ <div className="font-mono text-slate-300 leading-relaxed">
161
+ Spacing: {report.designSystem.discoveredTokens.spacingScale.join(' · ')}
162
+ <br />
163
+ Radii: {report.designSystem.discoveredTokens.radii.join(' · ')}
164
+ </div>
165
+ </div>
166
+ <div className="space-y-2">
167
+ <div className="text-slate-400">Violation Telemetry</div>
168
+ <div className="font-mono tabular-nums text-slate-300 space-y-1">
169
+ <div>Hardcoded Hex Literals: {report.designSystem.hardcodedColorsCount}</div>
170
+ <div>Non-Grid Spacing / Radii: {report.designSystem.inconsistentSpacingCount + report.designSystem.randomRadiiCount}</div>
171
+ <div>Emoji Used as UI Icons: {report.designSystem.emojiIconsCount}</div>
172
+ <div>Missing Accessibility Labels: {report.designSystem.accessibilityGapsCount}</div>
173
+ </div>
174
+ </div>
175
+ </div>
176
+ </div>
177
+
178
+ <div className="space-y-3">
179
+ <h2 className="text-base font-semibold text-slate-100">
180
+ Detected UI/UX & Accessibility Violations ({designIssues.length})
181
+ </h2>
182
+ {designIssues.length === 0 ? (
183
+ <div className="p-6 bg-slate-900/50 border border-slate-800 rounded-lg text-xs text-emerald-400">
184
+ Zero design token or emoji icon violations detected in {report.name}.
185
+ </div>
186
+ ) : (
187
+ <div className="space-y-3">
188
+ {designIssues.map((issue) => (
189
+ <div
190
+ key={issue.id}
191
+ className="p-4 bg-slate-900/70 border border-slate-800/80 rounded-lg space-y-3"
192
+ >
193
+ <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
194
+ <div className="space-y-1">
195
+ <div className="flex items-center gap-2 text-xs">
196
+ <SeverityText severity={issue.severity} />
197
+ <span className="text-slate-500">·</span>
198
+ <span className="font-mono text-slate-300">
199
+ {issue.filePath}:{issue.line}
200
+ </span>
201
+ </div>
202
+ <div className="text-sm font-semibold text-slate-100">{issue.title}</div>
203
+ </div>
204
+ {issue.fixAvailable && (
205
+ <button
206
+ type="button"
207
+ disabled={isBusy}
208
+ onClick={() => onFixIssue(issue.id, false)}
209
+ className="px-4 py-2 text-xs font-semibold text-white bg-blue-600 hover:bg-blue-500 rounded-lg shrink-0 cursor-pointer whitespace-nowrap"
210
+ >
211
+ Normalize Design & Accessibility
212
+ </button>
213
+ )}
214
+ </div>
215
+ <p className="text-xs text-slate-400">{issue.explanation}</p>
216
+ <pre className="p-3 bg-slate-950 border border-slate-800 rounded text-[11px] font-mono text-slate-300 overflow-x-auto">
217
+ {issue.unifiedDiff}
218
+ </pre>
219
+ </div>
220
+ ))}
221
+ </div>
222
+ )}
223
+ </div>
224
+ </div>
225
+ );
226
+ }
227
+
228
+ export function PerformanceLabView({ report, onFixIssue, isBusy }: AnalysisViewProps) {
229
+ const perfIssues = report.issues.filter(
230
+ (i) => i.domain === 'performance' || i.ruleId.includes('SYNC_IO') || i.ruleId.includes('LEAK')
231
+ );
232
+
233
+ return (
234
+ <div className="space-y-8">
235
+ <div className="pb-5 border-b border-slate-800/80 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
236
+ <div>
237
+ <h1 className="text-xl font-bold text-slate-100">
238
+ Performance Lab & Bundle Weight Analyzer
239
+ </h1>
240
+ <p className="text-xs text-slate-400 mt-1">
241
+ Real measurements of source code footprint, heavy third-party packages, React Native FlatList virtualization, and blocking Node.js I/O.
242
+ </p>
243
+ </div>
244
+ <div className="text-xs font-mono tabular-nums text-slate-300">
245
+ Performance Score: <strong className="text-blue-400">{report.performance.performanceScore}/100</strong>
246
+ </div>
247
+ </div>
248
+
249
+ <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
250
+ <div className="p-4 bg-slate-900/70 border border-slate-800/80 rounded-lg">
251
+ <div className="text-xs text-slate-400">Measured Source Payload</div>
252
+ <div className="mt-1.5 text-xl font-bold font-mono tabular-nums text-slate-100">
253
+ {report.performance.totalSourceSizeKb} KB
254
+ </div>
255
+ <div className="mt-1 text-xs text-slate-400 font-mono">
256
+ Across {report.performance.totalSourceFiles} source files
257
+ </div>
258
+ </div>
259
+
260
+ <div className="p-4 bg-slate-900/70 border border-slate-800/80 rounded-lg">
261
+ <div className="text-xs text-slate-400">Estimated Bundle Weight</div>
262
+ <div className="mt-1.5 text-xl font-bold font-mono tabular-nums text-slate-100">
263
+ {report.performance.estimatedBundleWeightKb} KB
264
+ </div>
265
+ <div className="mt-1 text-xs text-slate-400 font-mono">
266
+ {report.performance.heavyDependencies.length} heavy package(s)
267
+ </div>
268
+ </div>
269
+
270
+ <div className="p-4 bg-slate-900/70 border border-slate-800/80 rounded-lg">
271
+ <div className="text-xs text-slate-400">Render & List Bottlenecks</div>
272
+ <div className="mt-1.5 text-xl font-bold font-mono tabular-nums text-amber-400">
273
+ {report.performance.renderBottlenecksCount}
274
+ </div>
275
+ <div className="mt-1 text-xs text-slate-400">
276
+ FlatList windowing & re-render hotspots
277
+ </div>
278
+ </div>
279
+
280
+ <div className="p-4 bg-slate-900/70 border border-slate-800/80 rounded-lg">
281
+ <div className="text-xs text-slate-400">Blocking Event-Loop I/O</div>
282
+ <div className="mt-1.5 text-xl font-bold font-mono tabular-nums text-slate-100">
283
+ {report.performance.blockingIoCount}
284
+ </div>
285
+ <div className="mt-1 text-xs text-slate-400">
286
+ Synchronous fs calls in request handlers
287
+ </div>
288
+ </div>
289
+ </div>
290
+
291
+ {report.performance.heavyDependencies.length > 0 && (
292
+ <div className="space-y-2">
293
+ <h2 className="text-base font-semibold text-slate-100">
294
+ Heavy Dependency Footprint Breakdown
295
+ </h2>
296
+ <div className="overflow-x-auto border border-slate-800/80 rounded-lg bg-slate-900/50">
297
+ <table className="w-full text-left border-collapse text-xs">
298
+ <thead>
299
+ <tr className="border-b border-slate-800 text-[11px] text-slate-400">
300
+ <th className="py-2.5 px-4">Package</th>
301
+ <th className="py-2.5 px-4">Estimated Impact</th>
302
+ <th className="py-2.5 px-4">Engineering Bottleneck Analysis</th>
303
+ </tr>
304
+ </thead>
305
+ <tbody className="divide-y divide-slate-800/60">
306
+ {report.performance.heavyDependencies.map((hd) => (
307
+ <tr key={hd.name}>
308
+ <td className="py-3 px-4 font-mono font-semibold text-slate-100">{hd.name}</td>
309
+ <td className="py-3 px-4 font-mono tabular-nums text-amber-400">
310
+ +{hd.sizeKb} KB
311
+ </td>
312
+ <td className="py-3 px-4 text-slate-300">{hd.reason}</td>
313
+ </tr>
314
+ ))}
315
+ </tbody>
316
+ </table>
317
+ </div>
318
+ </div>
319
+ )}
320
+
321
+ <div className="space-y-3">
322
+ <h2 className="text-base font-semibold text-slate-100">
323
+ Repairable Performance & Memory Findings ({perfIssues.length})
324
+ </h2>
325
+ {perfIssues.map((issue) => (
326
+ <div
327
+ key={issue.id}
328
+ className="p-4 bg-slate-900/70 border border-slate-800/80 rounded-lg space-y-3"
329
+ >
330
+ <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
331
+ <div className="space-y-1">
332
+ <div className="flex items-center gap-2 text-xs">
333
+ <SeverityText severity={issue.severity} />
334
+ <span className="text-slate-500">·</span>
335
+ <span className="font-mono text-slate-300">
336
+ {issue.filePath}:{issue.line}
337
+ </span>
338
+ </div>
339
+ <div className="text-sm font-semibold text-slate-100">{issue.title}</div>
340
+ </div>
341
+ {issue.fixAvailable && (
342
+ <button
343
+ type="button"
344
+ disabled={isBusy}
345
+ onClick={() => onFixIssue(issue.id, false)}
346
+ className="px-4 py-2 text-xs font-semibold text-white bg-blue-600 hover:bg-blue-500 rounded-lg shrink-0 cursor-pointer whitespace-nowrap"
347
+ >
348
+ Apply Performance Repair
349
+ </button>
350
+ )}
351
+ </div>
352
+ <p className="text-xs text-slate-400">{issue.explanation}</p>
353
+ <pre className="p-3 bg-slate-950 border border-slate-800 rounded text-[11px] font-mono text-slate-300 overflow-x-auto">
354
+ {issue.unifiedDiff}
355
+ </pre>
356
+ </div>
357
+ ))}
358
+ </div>
359
+ </div>
360
+ );
361
+ }
362
+
363
+ export function SecurityAuditView({ report, onFixIssue, isBusy }: AnalysisViewProps) {
364
+ const secIssues = report.issues.filter((i) => i.domain === 'security');
365
+
366
+ return (
367
+ <div className="space-y-8">
368
+ <div className="pb-5 border-b border-slate-800/80 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
369
+ <div>
370
+ <h1 className="text-xl font-bold text-slate-100">
371
+ Security & Secret Hygiene Auditor
372
+ </h1>
373
+ <p className="text-xs text-slate-400 mt-1">
374
+ Audits source files and manifests for hardcoded API keys, SQL injection string interpolation, cleartext HTTP traffic, and unsafe production debug flags.
375
+ </p>
376
+ </div>
377
+ <div className="text-xs font-mono tabular-nums text-slate-300">
378
+ Security Score: <strong className="text-blue-400">{report.scores.security}/100</strong>
379
+ </div>
380
+ </div>
381
+
382
+ {secIssues.length === 0 ? (
383
+ <div className="p-8 bg-slate-900/50 border border-slate-800 rounded-lg text-xs text-emerald-400">
384
+ Zero active security vulnerabilities or hardcoded secrets detected in {report.name}.
385
+ </div>
386
+ ) : (
387
+ <div className="space-y-4">
388
+ {secIssues.map((issue) => (
389
+ <div
390
+ key={issue.id}
391
+ className="p-5 bg-slate-900/70 border border-slate-800/80 rounded-lg space-y-3"
392
+ >
393
+ <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
394
+ <div className="space-y-1">
395
+ <div className="flex items-center gap-2 text-xs">
396
+ <SeverityText severity={issue.severity} />
397
+ <span className="text-slate-500">·</span>
398
+ <span className="font-mono text-slate-300">
399
+ {issue.filePath}:{issue.line}
400
+ </span>
401
+ <span className="text-slate-500">·</span>
402
+ <span className="font-mono text-slate-400">{issue.ruleId}</span>
403
+ </div>
404
+ <div className="text-sm font-semibold text-slate-100">{issue.title}</div>
405
+ </div>
406
+ {issue.fixAvailable && (
407
+ <button
408
+ type="button"
409
+ disabled={isBusy}
410
+ onClick={() => onFixIssue(issue.id, false)}
411
+ className="px-4 py-2 text-xs font-semibold text-white bg-blue-600 hover:bg-blue-500 rounded-lg shrink-0 cursor-pointer whitespace-nowrap"
412
+ >
413
+ Remediate Security Issue
414
+ </button>
415
+ )}
416
+ </div>
417
+ <p className="text-xs text-slate-300">{issue.rootCause}</p>
418
+ <p className="text-xs text-slate-400">{issue.explanation}</p>
419
+ <pre className="p-3 bg-slate-950 border border-slate-800 rounded text-[11px] font-mono text-slate-300 overflow-x-auto">
420
+ {issue.unifiedDiff}
421
+ </pre>
422
+ </div>
423
+ ))}
424
+ </div>
425
+ )}
426
+ </div>
427
+ );
428
+ }
429
+
430
+ export function DependencyIntelligenceView({ report, onFixIssue, isBusy }: AnalysisViewProps) {
431
+ const depIssues = report.issues.filter((i) => i.domain === 'dependencies');
432
+
433
+ return (
434
+ <div className="space-y-8">
435
+ <div className="pb-5 border-b border-slate-800/80">
436
+ <h1 className="text-xl font-bold text-slate-100">
437
+ Dependency Compatibility & Weight Matrix
438
+ </h1>
439
+ <p className="text-xs text-slate-400 mt-1">
440
+ Verifies Expo SDK / React Native peer compatibility before suggesting version changes — never blindly upgrades or downgrades packages.
441
+ </p>
442
+ </div>
443
+
444
+ {depIssues.length > 0 && (
445
+ <div className="space-y-3">
446
+ <h2 className="text-base font-semibold text-slate-100">
447
+ Detected Dependency Version Conflicts ({depIssues.length})
448
+ </h2>
449
+ {depIssues.map((issue) => (
450
+ <div
451
+ key={issue.id}
452
+ className="p-4 bg-slate-900/70 border border-slate-800/80 rounded-lg flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4"
453
+ >
454
+ <div className="space-y-1">
455
+ <div className="flex items-center gap-2 text-xs">
456
+ <SeverityText severity={issue.severity} />
457
+ <span className="text-slate-500">·</span>
458
+ <span className="font-mono text-slate-300">{issue.filePath}</span>
459
+ </div>
460
+ <div className="text-sm font-semibold text-slate-100">{issue.title}</div>
461
+ <p className="text-xs text-slate-400">{issue.explanation}</p>
462
+ </div>
463
+ {issue.fixAvailable && (
464
+ <button
465
+ type="button"
466
+ disabled={isBusy}
467
+ onClick={() => onFixIssue(issue.id, false)}
468
+ className="px-4 py-2 text-xs font-semibold text-white bg-blue-600 hover:bg-blue-500 rounded-lg shrink-0 cursor-pointer whitespace-nowrap"
469
+ >
470
+ Align Compatible Version
471
+ </button>
472
+ )}
473
+ </div>
474
+ ))}
475
+ </div>
476
+ )}
477
+
478
+ <div className="overflow-x-auto border border-slate-800/80 rounded-lg bg-slate-900/50">
479
+ <table className="w-full text-left border-collapse text-xs">
480
+ <thead>
481
+ <tr className="border-b border-slate-800 text-[11px] font-medium text-slate-400">
482
+ <th className="py-2.5 px-4">Package Name</th>
483
+ <th className="py-2.5 px-4">Installed Spec</th>
484
+ <th className="py-2.5 px-4">Compatible Target</th>
485
+ <th className="py-2.5 px-4">Est. Weight</th>
486
+ <th className="py-2.5 px-4">Status</th>
487
+ <th className="py-2.5 px-4">Compatibility Analysis</th>
488
+ </tr>
489
+ </thead>
490
+ <tbody className="divide-y divide-slate-800/60">
491
+ {report.dependencies.map((dep) => (
492
+ <tr key={dep.name} className="hover:bg-slate-800/40">
493
+ <td className="py-3 px-4 font-mono font-semibold text-slate-100 whitespace-nowrap">
494
+ {dep.name}
495
+ </td>
496
+ <td className="py-3 px-4 font-mono tabular-nums text-slate-300 whitespace-nowrap">
497
+ {dep.currentVersion}
498
+ </td>
499
+ <td className="py-3 px-4 font-mono tabular-nums text-blue-300 whitespace-nowrap">
500
+ {dep.recommendedVersion}
501
+ </td>
502
+ <td className="py-3 px-4 font-mono tabular-nums text-slate-300 whitespace-nowrap">
503
+ {dep.estimatedSizeKb} KB
504
+ </td>
505
+ <td className="py-3 px-4 font-mono whitespace-nowrap">
506
+ <span
507
+ className={
508
+ dep.status === 'conflict'
509
+ ? 'text-red-400 font-semibold'
510
+ : dep.status === 'heavy'
511
+ ? 'text-amber-400 font-semibold'
512
+ : 'text-emerald-400'
513
+ }
514
+ >
515
+ {dep.status}
516
+ </span>
517
+ </td>
518
+ <td className="py-3 px-4 text-slate-300">{dep.note}</td>
519
+ </tr>
520
+ ))}
521
+ </tbody>
522
+ </table>
523
+ </div>
524
+ </div>
525
+ );
526
+ }