Skip to content

Commit f6cd0de

Browse files
committed
feat(dependency-graph): add light mode support
1 parent a8b46b2 commit f6cd0de

4 files changed

Lines changed: 125 additions & 129 deletions

File tree

frontend/src/components/DependencyGraph/GraphNode.tsx

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -30,29 +30,29 @@ const FILE_ICONS: Record<string, typeof FileCode2> = {
3030
}
3131

3232
const LANGUAGE_COLORS: Record<string, string> = {
33-
python: 'text-blue-400',
34-
javascript: 'text-yellow-400',
35-
typescript: 'text-blue-500',
36-
json: 'text-zinc-400',
37-
yaml: 'text-zinc-400',
38-
config: 'text-zinc-400',
39-
test: 'text-purple-400',
40-
unknown: 'text-zinc-500',
33+
python: 'text-blue-500 dark:text-blue-400',
34+
javascript: 'text-yellow-600 dark:text-yellow-400',
35+
typescript: 'text-blue-600 dark:text-blue-500',
36+
json: 'text-zinc-500 dark:text-zinc-400',
37+
yaml: 'text-zinc-500 dark:text-zinc-400',
38+
config: 'text-zinc-500 dark:text-zinc-400',
39+
test: 'text-purple-500 dark:text-purple-400',
40+
unknown: 'text-zinc-400 dark:text-zinc-500',
4141
}
4242

4343
const STATE_STYLES: Record<GraphNodeData['state'], string> = {
44-
default: 'border-zinc-700 bg-zinc-900/90',
45-
selected: 'border-indigo-500 bg-zinc-900 ring-2 ring-indigo-500/50 shadow-lg shadow-indigo-500/20',
46-
direct: 'border-rose-500 bg-zinc-900 ring-1 ring-rose-500/30',
47-
transitive: 'border-amber-500 bg-zinc-900 ring-1 ring-amber-500/30',
48-
dimmed: 'border-zinc-800 bg-zinc-900/50 opacity-40',
44+
default: 'border-zinc-300 bg-white dark:border-zinc-700 dark:bg-zinc-900/90',
45+
selected: 'border-indigo-500 bg-white dark:bg-zinc-900 ring-2 ring-indigo-500/50 shadow-lg shadow-indigo-500/20',
46+
direct: 'border-rose-500 bg-white dark:bg-zinc-900 ring-1 ring-rose-500/30',
47+
transitive: 'border-amber-500 bg-white dark:bg-zinc-900 ring-1 ring-amber-500/30',
48+
dimmed: 'border-zinc-200 bg-zinc-50/50 opacity-40 dark:border-zinc-800 dark:bg-zinc-900/50',
4949
}
5050

5151
const RISK_BADGES: Record<RiskLevel, { bg: string; text: string; label: string }> = {
52-
low: { bg: 'bg-emerald-500/10', text: 'text-emerald-400', label: 'Low' },
53-
medium: { bg: 'bg-yellow-500/10', text: 'text-yellow-400', label: 'Med' },
54-
high: { bg: 'bg-orange-500/10', text: 'text-orange-400', label: 'High' },
55-
critical: { bg: 'bg-rose-500/10', text: 'text-rose-400', label: 'Crit' },
52+
low: { bg: 'bg-emerald-100 dark:bg-emerald-500/10', text: 'text-emerald-600 dark:text-emerald-400', label: 'Low' },
53+
medium: { bg: 'bg-yellow-100 dark:bg-yellow-500/10', text: 'text-yellow-600 dark:text-yellow-400', label: 'Med' },
54+
high: { bg: 'bg-orange-100 dark:bg-orange-500/10', text: 'text-orange-600 dark:text-orange-400', label: 'High' },
55+
critical: { bg: 'bg-rose-100 dark:bg-rose-500/10', text: 'text-rose-600 dark:text-rose-400', label: 'Crit' },
5656
}
5757

5858
function getFileType(path: string, language: string): string {
@@ -70,7 +70,7 @@ function getFileType(path: string, language: string): string {
7070
return 'unknown'
7171
}
7272

73-
function GraphNodeComponent({ data, selected }: NodeProps<GraphNodeData>) {
73+
function GraphNodeComponent({ data }: NodeProps<GraphNodeData>) {
7474
const fileType = getFileType(data.fullPath, data.language)
7575
const Icon = FILE_ICONS[fileType] || FILE_ICONS.unknown
7676
const iconColor = LANGUAGE_COLORS[fileType] || LANGUAGE_COLORS.unknown
@@ -79,7 +79,11 @@ function GraphNodeComponent({ data, selected }: NodeProps<GraphNodeData>) {
7979

8080
return (
8181
<>
82-
<Handle type="target" position={Position.Left} className="!bg-zinc-600 !w-2 !h-2 !border-0" />
82+
<Handle
83+
type="target"
84+
position={Position.Left}
85+
className="!bg-zinc-400 dark:!bg-zinc-600 !w-2 !h-2 !border-0"
86+
/>
8387

8488
<div
8589
className={cn(
@@ -89,10 +93,13 @@ function GraphNodeComponent({ data, selected }: NodeProps<GraphNodeData>) {
8993
data.isEntryPoint && 'border-l-4 border-l-emerald-500'
9094
)}
9195
>
92-
{/* Header row: icon + filename + risk badge */}
96+
{/* Header row */}
9397
<div className="flex items-center gap-2 mb-1">
9498
<Icon className={cn('w-4 h-4 flex-shrink-0', iconColor)} />
95-
<span className="font-semibold text-sm text-zinc-100 truncate flex-1" title={data.fullPath}>
99+
<span
100+
className="font-semibold text-sm text-zinc-800 dark:text-zinc-100 truncate flex-1"
101+
title={data.fullPath}
102+
>
96103
{data.label}
97104
</span>
98105
{data.dependentCount > 0 && (
@@ -103,11 +110,12 @@ function GraphNodeComponent({ data, selected }: NodeProps<GraphNodeData>) {
103110
</div>
104111

105112
{/* Stats row */}
106-
<div className="flex items-center gap-3 text-[11px] text-zinc-400">
113+
<div className="flex items-center gap-3 text-[11px] text-zinc-500 dark:text-zinc-400">
107114
<span className={cn(
108115
'font-medium',
109-
data.dependentCount >= 15 ? 'text-rose-400' :
110-
data.dependentCount >= 5 ? 'text-amber-400' : 'text-zinc-400'
116+
data.dependentCount >= 15 ? 'text-rose-600 dark:text-rose-400' :
117+
data.dependentCount >= 5 ? 'text-amber-600 dark:text-amber-400' :
118+
'text-zinc-500 dark:text-zinc-400'
111119
)}>
112120
{data.dependentCount} dependents
113121
</span>
@@ -122,7 +130,11 @@ function GraphNodeComponent({ data, selected }: NodeProps<GraphNodeData>) {
122130
</div>
123131
</div>
124132

125-
<Handle type="source" position={Position.Right} className="!bg-zinc-600 !w-2 !h-2 !border-0" />
133+
<Handle
134+
type="source"
135+
position={Position.Right}
136+
className="!bg-zinc-400 dark:!bg-zinc-600 !w-2 !h-2 !border-0"
137+
/>
126138
</>
127139
)
128140
}

frontend/src/components/DependencyGraph/GraphToolbar.tsx

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ function GraphToolbarComponent({
2424
onFullscreen,
2525
}: GraphToolbarProps) {
2626
return (
27-
<div className="flex items-center justify-between gap-4 px-4 py-2 bg-zinc-900/80 border-b border-zinc-800 backdrop-blur-sm">
27+
<div className="flex items-center justify-between gap-4 px-4 py-2 bg-white/80 dark:bg-zinc-900/80 border-b border-zinc-200 dark:border-zinc-800 backdrop-blur-sm">
2828
{/* Left: Stats */}
2929
<div className="flex items-center gap-4 text-sm">
30-
<span className="text-zinc-400">
31-
Showing <span className="text-zinc-200 font-medium">{visibleFiles}</span>
30+
<span className="text-zinc-500 dark:text-zinc-400">
31+
Showing <span className="text-zinc-700 dark:text-zinc-200 font-medium">{visibleFiles}</span>
3232
{!showAll && totalFiles > visibleFiles && (
33-
<span className="text-zinc-500"> of {totalFiles}</span>
33+
<span className="text-zinc-400 dark:text-zinc-500"> of {totalFiles}</span>
3434
)}
3535
{' '}files
3636
</span>
@@ -44,7 +44,7 @@ function GraphToolbarComponent({
4444
'flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-medium transition-colors',
4545
showAll
4646
? 'bg-indigo-600 text-white'
47-
: 'bg-zinc-800 text-zinc-300 hover:bg-zinc-700'
47+
: 'bg-zinc-100 dark:bg-zinc-800 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-200 dark:hover:bg-zinc-700'
4848
)}
4949
>
5050
<Filter className="w-3.5 h-3.5" />
@@ -56,8 +56,8 @@ function GraphToolbarComponent({
5656
className={cn(
5757
'flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-medium transition-colors',
5858
showTests
59-
? 'bg-zinc-800 text-zinc-300 hover:bg-zinc-700'
60-
: 'bg-zinc-800/50 text-zinc-500 hover:bg-zinc-800 hover:text-zinc-300'
59+
? 'bg-zinc-100 dark:bg-zinc-800 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-200 dark:hover:bg-zinc-700'
60+
: 'bg-zinc-50 dark:bg-zinc-800/50 text-zinc-400 dark:text-zinc-500 hover:bg-zinc-100 dark:hover:bg-zinc-800 hover:text-zinc-600 dark:hover:text-zinc-300'
6161
)}
6262
>
6363
{showTests ? <Eye className="w-3.5 h-3.5" /> : <EyeOff className="w-3.5 h-3.5" />}
@@ -69,19 +69,19 @@ function GraphToolbarComponent({
6969
<div className="flex items-center gap-1">
7070
<button
7171
onClick={onResetView}
72-
className="p-2 hover:bg-zinc-800 rounded-md transition-colors group"
72+
className="p-2 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-md transition-colors group"
7373
title="Reset View"
7474
>
75-
<RotateCcw className="w-4 h-4 text-zinc-500 group-hover:text-zinc-300" />
75+
<RotateCcw className="w-4 h-4 text-zinc-400 dark:text-zinc-500 group-hover:text-zinc-600 dark:group-hover:text-zinc-300" />
7676
</button>
7777

7878
{onFullscreen && (
7979
<button
8080
onClick={onFullscreen}
81-
className="p-2 hover:bg-zinc-800 rounded-md transition-colors group"
81+
className="p-2 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-md transition-colors group"
8282
title="Fullscreen"
8383
>
84-
<Maximize2 className="w-4 h-4 text-zinc-500 group-hover:text-zinc-300" />
84+
<Maximize2 className="w-4 h-4 text-zinc-400 dark:text-zinc-500 group-hover:text-zinc-600 dark:group-hover:text-zinc-300" />
8585
</button>
8686
)}
8787
</div>

frontend/src/components/DependencyGraph/ImpactPanel.tsx

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,30 @@ interface ImpactPanelProps {
1515

1616
const RISK_CONFIG: Record<RiskLevel, { bg: string; border: string; text: string; icon: string; label: string }> = {
1717
low: {
18-
bg: 'bg-emerald-500/10',
19-
border: 'border-emerald-500/30',
20-
text: 'text-emerald-400',
18+
bg: 'bg-emerald-50 dark:bg-emerald-500/10',
19+
border: 'border-emerald-200 dark:border-emerald-500/30',
20+
text: 'text-emerald-600 dark:text-emerald-400',
2121
icon: '✓',
2222
label: 'Low Risk'
2323
},
2424
medium: {
25-
bg: 'bg-yellow-500/10',
26-
border: 'border-yellow-500/30',
27-
text: 'text-yellow-400',
25+
bg: 'bg-yellow-50 dark:bg-yellow-500/10',
26+
border: 'border-yellow-200 dark:border-yellow-500/30',
27+
text: 'text-yellow-600 dark:text-yellow-400',
2828
icon: '⚠',
2929
label: 'Medium Risk'
3030
},
3131
high: {
32-
bg: 'bg-orange-500/10',
33-
border: 'border-orange-500/30',
34-
text: 'text-orange-400',
32+
bg: 'bg-orange-50 dark:bg-orange-500/10',
33+
border: 'border-orange-200 dark:border-orange-500/30',
34+
text: 'text-orange-600 dark:text-orange-400',
3535
icon: '⚠',
3636
label: 'High Risk'
3737
},
3838
critical: {
39-
bg: 'bg-rose-500/10',
40-
border: 'border-rose-500/30',
41-
text: 'text-rose-400',
39+
bg: 'bg-rose-50 dark:bg-rose-500/10',
40+
border: 'border-rose-200 dark:border-rose-500/30',
41+
text: 'text-rose-600 dark:text-rose-400',
4242
icon: '🔥',
4343
label: 'Critical'
4444
},
@@ -61,15 +61,15 @@ function FileListItem({
6161
onClick={onClick}
6262
onMouseEnter={() => onHover(true)}
6363
onMouseLeave={() => onHover(false)}
64-
className="w-full text-left px-3 py-2 rounded-md hover:bg-zinc-800 transition-colors group"
64+
className="w-full text-left px-3 py-2 rounded-md hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors group"
6565
>
6666
<div className="flex items-center gap-2">
67-
<FileCode2 className="w-3.5 h-3.5 text-zinc-500 flex-shrink-0" />
68-
<span className="text-sm text-zinc-200 truncate font-medium">{fileName}</span>
69-
<ExternalLink className="w-3 h-3 text-zinc-600 opacity-0 group-hover:opacity-100 transition-opacity ml-auto" />
67+
<FileCode2 className="w-3.5 h-3.5 text-zinc-400 dark:text-zinc-500 flex-shrink-0" />
68+
<span className="text-sm text-zinc-700 dark:text-zinc-200 truncate font-medium">{fileName}</span>
69+
<ExternalLink className="w-3 h-3 text-zinc-400 dark:text-zinc-600 opacity-0 group-hover:opacity-100 transition-opacity ml-auto" />
7070
</div>
7171
{dirPath && (
72-
<div className="text-[10px] text-zinc-500 mt-0.5 truncate pl-5">{dirPath}</div>
72+
<div className="text-[10px] text-zinc-400 dark:text-zinc-500 mt-0.5 truncate pl-5">{dirPath}</div>
7373
)}
7474
</button>
7575
)
@@ -95,26 +95,26 @@ function CollapsibleSection({
9595
const [isOpen, setIsOpen] = useState(defaultOpen)
9696

9797
const variantStyles = {
98-
direct: 'text-rose-400',
99-
transitive: 'text-amber-400',
100-
default: 'text-zinc-300',
98+
direct: 'text-rose-600 dark:text-rose-400',
99+
transitive: 'text-amber-600 dark:text-amber-400',
100+
default: 'text-zinc-700 dark:text-zinc-300',
101101
}
102102

103103
if (count === 0) return null
104104

105105
return (
106-
<div className="border-t border-zinc-800 pt-3">
106+
<div className="border-t border-zinc-200 dark:border-zinc-800 pt-3">
107107
<button
108108
onClick={() => setIsOpen(!isOpen)}
109-
className="w-full flex items-center gap-2 text-left hover:bg-zinc-800/50 -mx-2 px-2 py-1 rounded transition-colors"
109+
className="w-full flex items-center gap-2 text-left hover:bg-zinc-100 dark:hover:bg-zinc-800/50 -mx-2 px-2 py-1 rounded transition-colors"
110110
>
111111
{isOpen ? (
112-
<ChevronDown className="w-4 h-4 text-zinc-500" />
112+
<ChevronDown className="w-4 h-4 text-zinc-400 dark:text-zinc-500" />
113113
) : (
114-
<ChevronRight className="w-4 h-4 text-zinc-500" />
114+
<ChevronRight className="w-4 h-4 text-zinc-400 dark:text-zinc-500" />
115115
)}
116116
<span className={cn('text-sm font-medium', variantStyles[variant])}>{title}</span>
117-
<span className="text-xs text-zinc-500 ml-auto">{count}</span>
117+
<span className="text-xs text-zinc-400 dark:text-zinc-500 ml-auto">{count}</span>
118118
</button>
119119

120120
{isOpen && (
@@ -146,23 +146,23 @@ function ImpactPanelComponent({
146146
const totalDependents = impact.allDependents.length
147147

148148
return (
149-
<div className="w-80 bg-zinc-900 border-l border-zinc-800 h-full flex flex-col animate-in slide-in-from-right duration-200">
149+
<div className="w-80 bg-white dark:bg-zinc-900 border-l border-zinc-200 dark:border-zinc-800 h-full flex flex-col animate-in slide-in-from-right duration-200">
150150
{/* Header */}
151-
<div className="p-4 border-b border-zinc-800">
151+
<div className="p-4 border-b border-zinc-200 dark:border-zinc-800">
152152
<div className="flex items-start justify-between gap-2">
153153
<div className="min-w-0 flex-1">
154-
<h3 className="font-semibold text-zinc-100 truncate" title={fullPath}>
154+
<h3 className="font-semibold text-zinc-800 dark:text-zinc-100 truncate" title={fullPath}>
155155
{fileName}
156156
</h3>
157-
<p className="text-xs text-zinc-500 truncate mt-0.5" title={fullPath}>
157+
<p className="text-xs text-zinc-400 dark:text-zinc-500 truncate mt-0.5" title={fullPath}>
158158
{fullPath}
159159
</p>
160160
</div>
161161
<button
162162
onClick={onClose}
163-
className="p-1 hover:bg-zinc-800 rounded transition-colors flex-shrink-0"
163+
className="p-1 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded transition-colors flex-shrink-0"
164164
>
165-
<X className="w-4 h-4 text-zinc-400" />
165+
<X className="w-4 h-4 text-zinc-500 dark:text-zinc-400" />
166166
</button>
167167
</div>
168168

@@ -174,7 +174,7 @@ function ImpactPanelComponent({
174174
<span className="text-lg">{risk.icon}</span>
175175
<div>
176176
<div className={cn('font-semibold text-sm', risk.text)}>{risk.label}</div>
177-
<div className="text-xs text-zinc-400">
177+
<div className="text-xs text-zinc-500 dark:text-zinc-400">
178178
{totalDependents === 0
179179
? 'No files depend on this'
180180
: `${totalDependents} file${totalDependents === 1 ? '' : 's'} will be affected`
@@ -185,15 +185,15 @@ function ImpactPanelComponent({
185185

186186
{/* Warning for critical files */}
187187
{impact.riskLevel === 'critical' && (
188-
<div className="mt-2 flex items-start gap-2 text-xs text-rose-400 bg-rose-500/10 px-3 py-2 rounded-lg">
188+
<div className="mt-2 flex items-start gap-2 text-xs text-rose-600 dark:text-rose-400 bg-rose-50 dark:bg-rose-500/10 px-3 py-2 rounded-lg">
189189
<AlertTriangle className="w-3.5 h-3.5 flex-shrink-0 mt-0.5" />
190190
<span>Changes to this file have high blast radius. Test thoroughly.</span>
191191
</div>
192192
)}
193193

194194
{/* Entry Point indicator */}
195195
{impact.isEntryPoint && (
196-
<div className="mt-2 flex items-center gap-2 text-xs text-emerald-400 bg-emerald-500/10 px-3 py-2 rounded-lg">
196+
<div className="mt-2 flex items-center gap-2 text-xs text-emerald-600 dark:text-emerald-400 bg-emerald-50 dark:bg-emerald-500/10 px-3 py-2 rounded-lg">
197197
<span>📍</span>
198198
<span>Entry point - this file is a root of the dependency tree</span>
199199
</div>
@@ -225,7 +225,7 @@ function ImpactPanelComponent({
225225

226226
{/* Actions Footer */}
227227
{onAnalyzeInSearch && (
228-
<div className="p-4 border-t border-zinc-800">
228+
<div className="p-4 border-t border-zinc-200 dark:border-zinc-800">
229229
<button
230230
onClick={() => onAnalyzeInSearch(fullPath)}
231231
className="w-full flex items-center justify-center gap-2 px-4 py-2 bg-indigo-600 hover:bg-indigo-500 text-white text-sm font-medium rounded-lg transition-colors"

0 commit comments

Comments
 (0)