1use eframe::egui::{self, Color32, Rounding, Stroke};
4
5#[derive(Debug, Clone)]
7pub struct AccNetTheme {
8 pub canvas_bg: Color32,
10 pub grid_color: Color32,
12 pub panel_bg: Color32,
14 pub text_primary: Color32,
16 pub text_secondary: Color32,
18 pub accent: Color32,
20
21 pub asset_color: Color32,
24 pub liability_color: Color32,
26 pub equity_color: Color32,
28 pub revenue_color: Color32,
30 pub expense_color: Color32,
32 pub contra_color: Color32,
34
35 pub alert_low: Color32,
38 pub alert_medium: Color32,
40 pub alert_high: Color32,
42 pub alert_critical: Color32,
44
45 pub flow_normal: Color32,
48 pub flow_suspicious: Color32,
50 pub flow_fraud: Color32,
52
53 pub node_radius: f32,
56 pub edge_width: f32,
58 pub panel_rounding: Rounding,
60}
61
62impl Default for AccNetTheme {
63 fn default() -> Self {
64 Self::dark()
65 }
66}
67
68impl AccNetTheme {
69 pub fn dark() -> Self {
71 Self {
72 canvas_bg: Color32::from_rgb(18, 18, 24),
73 grid_color: Color32::from_rgb(40, 40, 50),
74 panel_bg: Color32::from_rgb(28, 28, 36),
75 text_primary: Color32::from_rgb(240, 240, 245),
76 text_secondary: Color32::from_rgb(140, 140, 155),
77 accent: Color32::from_rgb(100, 180, 255),
78
79 asset_color: Color32::from_rgb(66, 165, 245), liability_color: Color32::from_rgb(239, 83, 80), equity_color: Color32::from_rgb(102, 187, 106), revenue_color: Color32::from_rgb(255, 202, 40), expense_color: Color32::from_rgb(171, 71, 188), contra_color: Color32::from_rgb(158, 158, 158), alert_low: Color32::from_rgb(100, 180, 255),
89 alert_medium: Color32::from_rgb(255, 202, 40),
90 alert_high: Color32::from_rgb(255, 152, 0),
91 alert_critical: Color32::from_rgb(244, 67, 54),
92
93 flow_normal: Color32::from_rgb(100, 255, 180),
95 flow_suspicious: Color32::from_rgb(255, 202, 40),
96 flow_fraud: Color32::from_rgb(244, 67, 54),
97
98 node_radius: 20.0,
100 edge_width: 2.0,
101 panel_rounding: Rounding::same(8.0),
102 }
103 }
104
105 pub fn light() -> Self {
107 Self {
108 canvas_bg: Color32::from_rgb(245, 245, 250),
109 grid_color: Color32::from_rgb(220, 220, 230),
110 panel_bg: Color32::from_rgb(255, 255, 255),
111 text_primary: Color32::from_rgb(30, 30, 40),
112 text_secondary: Color32::from_rgb(100, 100, 115),
113 accent: Color32::from_rgb(25, 118, 210),
114
115 asset_color: Color32::from_rgb(25, 118, 210),
117 liability_color: Color32::from_rgb(211, 47, 47),
118 equity_color: Color32::from_rgb(56, 142, 60),
119 revenue_color: Color32::from_rgb(255, 160, 0),
120 expense_color: Color32::from_rgb(142, 36, 170),
121 contra_color: Color32::from_rgb(117, 117, 117),
122
123 alert_low: Color32::from_rgb(25, 118, 210),
125 alert_medium: Color32::from_rgb(255, 160, 0),
126 alert_high: Color32::from_rgb(230, 81, 0),
127 alert_critical: Color32::from_rgb(198, 40, 40),
128
129 flow_normal: Color32::from_rgb(0, 150, 136),
131 flow_suspicious: Color32::from_rgb(255, 160, 0),
132 flow_fraud: Color32::from_rgb(198, 40, 40),
133
134 node_radius: 20.0,
136 edge_width: 2.0,
137 panel_rounding: Rounding::same(8.0),
138 }
139 }
140
141 pub fn account_color(&self, account_type: crate::models::AccountType) -> Color32 {
143 use crate::models::AccountType;
144 match account_type {
145 AccountType::Asset => self.asset_color,
146 AccountType::Liability => self.liability_color,
147 AccountType::Equity => self.equity_color,
148 AccountType::Revenue => self.revenue_color,
149 AccountType::Expense => self.expense_color,
150 AccountType::Contra => self.contra_color,
151 }
152 }
153
154 pub fn edge_stroke(&self, suspicious: bool, fraud: bool) -> Stroke {
156 let color = if fraud {
157 self.flow_fraud
158 } else if suspicious {
159 self.flow_suspicious
160 } else {
161 self.flow_normal
162 };
163 Stroke::new(self.edge_width, color)
164 }
165
166 pub fn edge_color_futuristic(
169 &self,
170 weight: f32,
171 suspicious: bool,
172 fraud: bool,
173 max_weight: f32,
174 ) -> Color32 {
175 let normalized = (weight / max_weight.max(1.0)).min(1.0);
177
178 let base = if fraud {
180 self.flow_fraud
181 } else if suspicious {
182 self.flow_suspicious
183 } else {
184 let t = normalized;
186 Color32::from_rgb(
187 (60.0 + t * 40.0) as u8, (180.0 + t * 40.0) as u8, (220.0 + t * 35.0) as u8, )
191 };
192
193 let alpha = (40.0 + normalized * 120.0) as u8;
196
197 Color32::from_rgba_unmultiplied(base.r(), base.g(), base.b(), alpha)
198 }
199
200 pub fn apply(&self, ctx: &egui::Context) {
202 let mut style = (*ctx.style()).clone();
203
204 style.visuals.dark_mode = self.canvas_bg.r() < 128;
205 style.visuals.panel_fill = self.panel_bg;
206 style.visuals.window_fill = self.panel_bg;
207 style.visuals.widgets.noninteractive.fg_stroke.color = self.text_primary;
208 style.visuals.widgets.inactive.fg_stroke.color = self.text_secondary;
209 style.visuals.selection.bg_fill = self.accent;
210 style.visuals.window_rounding = self.panel_rounding;
211
212 ctx.set_style(style);
213 }
214}