oak_visualize/
theme.rs

1use serde::{Deserialize, Serialize};
2
3/// 可视化主题配置
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct VisualizationTheme {
6    /// 主题名称
7    pub name: String,
8    /// 背景颜色
9    pub background_color: String,
10    /// 节点样式
11    pub node: NodeTheme,
12    /// 边样式
13    pub edge: EdgeTheme,
14    /// 文本样式
15    pub text: TextTheme,
16    /// 高亮样式
17    pub highlight: HighlightTheme,
18}
19
20/// 节点主题配置
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct NodeTheme {
23    /// 默认填充颜色
24    pub fill_color: String,
25    /// 默认边框颜色
26    pub stroke_color: String,
27    /// 边框宽度
28    pub stroke_width: f32,
29    /// 圆角半径
30    pub border_radius: f32,
31    /// 阴影配置
32    pub shadow: ShadowConfig,
33}
34
35/// 边主题配置
36#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct EdgeTheme {
38    /// 默认颜色
39    pub color: String,
40    /// 线宽
41    pub width: f32,
42    /// 线型 (solid, dashed, dotted)
43    pub style: String,
44    /// 箭头样式
45    pub arrow: ArrowConfig,
46}
47
48/// 文本主题配置
49#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct TextTheme {
51    /// 字体族
52    pub font_family: String,
53    /// 字体大小
54    pub font_size: f32,
55    /// 字体颜色
56    pub color: String,
57    /// 字体粗细
58    pub font_weight: String,
59}
60
61/// 高亮主题配置
62#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct HighlightTheme {
64    /// 选中状态颜色
65    pub selected_color: String,
66    /// 悬停状态颜色
67    pub hover_color: String,
68    /// 错误状态颜色
69    pub error_color: String,
70    /// 警告状态颜色
71    pub warning_color: String,
72}
73
74/// 阴影配置
75#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct ShadowConfig {
77    /// 是否启用阴影
78    pub enabled: bool,
79    /// 阴影颜色
80    pub color: String,
81    /// 阴影偏移X
82    pub offset_x: f32,
83    /// 阴影偏移Y
84    pub offset_y: f32,
85    /// 阴影模糊半径
86    pub blur_radius: f32,
87}
88
89/// 箭头配置
90#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct ArrowConfig {
92    /// 是否显示箭头
93    pub enabled: bool,
94    /// 箭头大小
95    pub size: f32,
96    /// 箭头类型 (triangle, circle, diamond)
97    pub arrow_type: String,
98}
99
100impl Default for VisualizationTheme {
101    fn default() -> Self {
102        Self::light()
103    }
104}
105
106impl VisualizationTheme {
107    /// 浅色主题
108    pub fn light() -> Self {
109        Self {
110            name: "Light".to_string(),
111            background_color: "#FFFFFF".to_string(),
112            node: NodeTheme {
113                fill_color: "#F8F9FA".to_string(),
114                stroke_color: "#DEE2E6".to_string(),
115                stroke_width: 1.0,
116                border_radius: 4.0,
117                shadow: ShadowConfig {
118                    enabled: true,
119                    color: "rgba(0, 0, 0, 0.1)".to_string(),
120                    offset_x: 0.0,
121                    offset_y: 2.0,
122                    blur_radius: 4.0,
123                },
124            },
125            edge: EdgeTheme {
126                color: "#6C757D".to_string(),
127                width: 1.5,
128                style: "solid".to_string(),
129                arrow: ArrowConfig { enabled: true, size: 8.0, arrow_type: "triangle".to_string() },
130            },
131            text: TextTheme {
132                font_family: "Arial, sans-serif".to_string(),
133                font_size: 12.0,
134                color: "#212529".to_string(),
135                font_weight: "normal".to_string(),
136            },
137            highlight: HighlightTheme {
138                selected_color: "#007BFF".to_string(),
139                hover_color: "#0056B3".to_string(),
140                error_color: "#DC3545".to_string(),
141                warning_color: "#FFC107".to_string(),
142            },
143        }
144    }
145
146    /// 深色主题
147    pub fn dark() -> Self {
148        Self {
149            name: "Dark".to_string(),
150            background_color: "#1E1E1E".to_string(),
151            node: NodeTheme {
152                fill_color: "#2D2D30".to_string(),
153                stroke_color: "#3E3E42".to_string(),
154                stroke_width: 1.0,
155                border_radius: 4.0,
156                shadow: ShadowConfig {
157                    enabled: true,
158                    color: "rgba(0, 0, 0, 0.3)".to_string(),
159                    offset_x: 0.0,
160                    offset_y: 2.0,
161                    blur_radius: 4.0,
162                },
163            },
164            edge: EdgeTheme {
165                color: "#CCCCCC".to_string(),
166                width: 1.5,
167                style: "solid".to_string(),
168                arrow: ArrowConfig { enabled: true, size: 8.0, arrow_type: "triangle".to_string() },
169            },
170            text: TextTheme {
171                font_family: "Arial, sans-serif".to_string(),
172                font_size: 12.0,
173                color: "#CCCCCC".to_string(),
174                font_weight: "normal".to_string(),
175            },
176            highlight: HighlightTheme {
177                selected_color: "#0E639C".to_string(),
178                hover_color: "#1177BB".to_string(),
179                error_color: "#F14C4C".to_string(),
180                warning_color: "#FFCC02".to_string(),
181            },
182        }
183    }
184
185    /// One Light 主题 - 基于 Atom One Light
186    pub fn one_light() -> Self {
187        Self {
188            name: "One Light".to_string(),
189            background_color: "#FAFAFA".to_string(),
190            node: NodeTheme {
191                fill_color: "#FFFFFF".to_string(),
192                stroke_color: "#E1E4E8".to_string(),
193                stroke_width: 1.0,
194                border_radius: 6.0,
195                shadow: ShadowConfig {
196                    enabled: true,
197                    color: "rgba(149, 157, 165, 0.2)".to_string(),
198                    offset_x: 0.0,
199                    offset_y: 8.0,
200                    blur_radius: 24.0,
201                },
202            },
203            edge: EdgeTheme {
204                color: "#586069".to_string(),
205                width: 1.5,
206                style: "solid".to_string(),
207                arrow: ArrowConfig { enabled: true, size: 8.0, arrow_type: "triangle".to_string() },
208            },
209            text: TextTheme {
210                font_family: "SF Pro Display, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif".to_string(),
211                font_size: 12.0,
212                color: "#24292E".to_string(),
213                font_weight: "400".to_string(),
214            },
215            highlight: HighlightTheme {
216                selected_color: "#0366D6".to_string(),
217                hover_color: "#0256CC".to_string(),
218                error_color: "#D73A49".to_string(),
219                warning_color: "#F66A0A".to_string(),
220            },
221        }
222    }
223
224    /// One Dark Pro 主题 - 基于 Atom One Dark Pro
225    pub fn one_dark_pro() -> Self {
226        Self {
227            name: "One Dark Pro".to_string(),
228            background_color: "#282C34".to_string(),
229            node: NodeTheme {
230                fill_color: "#21252B".to_string(),
231                stroke_color: "#3E4451".to_string(),
232                stroke_width: 1.0,
233                border_radius: 6.0,
234                shadow: ShadowConfig {
235                    enabled: true,
236                    color: "rgba(0, 0, 0, 0.4)".to_string(),
237                    offset_x: 0.0,
238                    offset_y: 8.0,
239                    blur_radius: 24.0,
240                },
241            },
242            edge: EdgeTheme {
243                color: "#ABB2BF".to_string(),
244                width: 1.5,
245                style: "solid".to_string(),
246                arrow: ArrowConfig { enabled: true, size: 8.0, arrow_type: "triangle".to_string() },
247            },
248            text: TextTheme {
249                font_family: "SF Mono, Monaco, Inconsolata, Roboto Mono, monospace".to_string(),
250                font_size: 12.0,
251                color: "#ABB2BF".to_string(),
252                font_weight: "400".to_string(),
253            },
254            highlight: HighlightTheme {
255                selected_color: "#61AFEF".to_string(),
256                hover_color: "#528BFF".to_string(),
257                error_color: "#E06C75".to_string(),
258                warning_color: "#E5C07B".to_string(),
259            },
260        }
261    }
262
263    /// GitHub 主题
264    pub fn github() -> Self {
265        Self {
266            name: "GitHub".to_string(),
267            background_color: "#FFFFFF".to_string(),
268            node: NodeTheme {
269                fill_color: "#F6F8FA".to_string(),
270                stroke_color: "#D0D7DE".to_string(),
271                stroke_width: 1.0,
272                border_radius: 6.0,
273                shadow: ShadowConfig {
274                    enabled: true,
275                    color: "rgba(31, 35, 40, 0.04)".to_string(),
276                    offset_x: 0.0,
277                    offset_y: 1.0,
278                    blur_radius: 0.0,
279                },
280            },
281            edge: EdgeTheme {
282                color: "#656D76".to_string(),
283                width: 1.0,
284                style: "solid".to_string(),
285                arrow: ArrowConfig { enabled: true, size: 6.0, arrow_type: "triangle".to_string() },
286            },
287            text: TextTheme {
288                font_family: "-apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif".to_string(),
289                font_size: 12.0,
290                color: "#24292F".to_string(),
291                font_weight: "400".to_string(),
292            },
293            highlight: HighlightTheme {
294                selected_color: "#0969DA".to_string(),
295                hover_color: "#0860CA".to_string(),
296                error_color: "#CF222E".to_string(),
297                warning_color: "#9A6700".to_string(),
298            },
299        }
300    }
301}