Skip to main content

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 VisualizationTheme {
101    /// 转换为渲染配置
102    pub fn to_render_config(&self) -> crate::render::RenderConfig {
103        crate::render::RenderConfig {
104            background_color: self.background_color.clone(),
105            node_fill_color: self.node.fill_color.clone(),
106            node_stroke_color: self.node.stroke_color.clone(),
107            node_stroke_width: self.node.stroke_width as f64,
108            edge_color: self.edge.color.clone(),
109            edge_width: self.edge.width as f64,
110            text_color: self.text.color.clone(),
111            text_size: self.text.font_size as f64,
112            font_family: self.text.font_family.clone(),
113            show_arrows: self.edge.arrow.enabled,
114            arrow_size: self.edge.arrow.size as f64,
115            ..Default::default()
116        }
117    }
118}
119
120impl Default for VisualizationTheme {
121    fn default() -> Self {
122        Self::light()
123    }
124}
125
126impl VisualizationTheme {
127    /// 浅色主题
128    pub fn light() -> Self {
129        Self {
130            name: "Light".to_string(),
131            background_color: "#FFFFFF".to_string(),
132            node: NodeTheme {
133                fill_color: "#F8F9FA".to_string(),
134                stroke_color: "#DEE2E6".to_string(),
135                stroke_width: 1.0,
136                border_radius: 4.0,
137                shadow: ShadowConfig { enabled: true, color: "rgba(0, 0, 0, 0.1)".to_string(), offset_x: 0.0, offset_y: 2.0, blur_radius: 4.0 },
138            },
139            edge: EdgeTheme { color: "#6C757D".to_string(), width: 1.5, style: "solid".to_string(), arrow: ArrowConfig { enabled: true, size: 8.0, arrow_type: "triangle".to_string() } },
140            text: TextTheme { font_family: "Arial, sans-serif".to_string(), font_size: 12.0, color: "#212529".to_string(), font_weight: "normal".to_string() },
141            highlight: HighlightTheme { selected_color: "#007BFF".to_string(), hover_color: "#0056B3".to_string(), error_color: "#DC3545".to_string(), warning_color: "#FFC107".to_string() },
142        }
143    }
144
145    /// 深色主题
146    pub fn dark() -> Self {
147        Self {
148            name: "Dark".to_string(),
149            background_color: "#1E1E1E".to_string(),
150            node: NodeTheme {
151                fill_color: "#2D2D30".to_string(),
152                stroke_color: "#3E3E42".to_string(),
153                stroke_width: 1.0,
154                border_radius: 4.0,
155                shadow: ShadowConfig { enabled: true, color: "rgba(0, 0, 0, 0.3)".to_string(), offset_x: 0.0, offset_y: 2.0, blur_radius: 4.0 },
156            },
157            edge: EdgeTheme { color: "#CCCCCC".to_string(), width: 1.5, style: "solid".to_string(), arrow: ArrowConfig { enabled: true, size: 8.0, arrow_type: "triangle".to_string() } },
158            text: TextTheme { font_family: "Arial, sans-serif".to_string(), font_size: 12.0, color: "#CCCCCC".to_string(), font_weight: "normal".to_string() },
159            highlight: HighlightTheme { selected_color: "#0E639C".to_string(), hover_color: "#1177BB".to_string(), error_color: "#F14C4C".to_string(), warning_color: "#FFCC02".to_string() },
160        }
161    }
162
163    /// One Light 主题 - 基于 Atom One Light
164    pub fn one_light() -> Self {
165        Self {
166            name: "One Light".to_string(),
167            background_color: "#FAFAFA".to_string(),
168            node: NodeTheme {
169                fill_color: "#FFFFFF".to_string(),
170                stroke_color: "#E1E4E8".to_string(),
171                stroke_width: 1.0,
172                border_radius: 6.0,
173                shadow: ShadowConfig { enabled: true, color: "rgba(149, 157, 165, 0.2)".to_string(), offset_x: 0.0, offset_y: 8.0, blur_radius: 24.0 },
174            },
175            edge: EdgeTheme { color: "#586069".to_string(), width: 1.5, style: "solid".to_string(), arrow: ArrowConfig { enabled: true, size: 8.0, arrow_type: "triangle".to_string() } },
176            text: TextTheme { font_family: "SF Pro Display, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif".to_string(), font_size: 12.0, color: "#24292E".to_string(), font_weight: "400".to_string() },
177            highlight: HighlightTheme { selected_color: "#0366D6".to_string(), hover_color: "#0256CC".to_string(), error_color: "#D73A49".to_string(), warning_color: "#F66A0A".to_string() },
178        }
179    }
180
181    /// One Dark Pro 主题 - 基于 Atom One Dark Pro
182    pub fn one_dark_pro() -> Self {
183        Self {
184            name: "One Dark Pro".to_string(),
185            background_color: "#282C34".to_string(),
186            node: NodeTheme {
187                fill_color: "#21252B".to_string(),
188                stroke_color: "#3E4451".to_string(),
189                stroke_width: 1.0,
190                border_radius: 6.0,
191                shadow: ShadowConfig { enabled: true, color: "rgba(0, 0, 0, 0.4)".to_string(), offset_x: 0.0, offset_y: 8.0, blur_radius: 24.0 },
192            },
193            edge: EdgeTheme { color: "#ABB2BF".to_string(), width: 1.5, style: "solid".to_string(), arrow: ArrowConfig { enabled: true, size: 8.0, arrow_type: "triangle".to_string() } },
194            text: TextTheme { font_family: "SF Mono, Monaco, Inconsolata, Roboto Mono, monospace".to_string(), font_size: 12.0, color: "#ABB2BF".to_string(), font_weight: "400".to_string() },
195            highlight: HighlightTheme { selected_color: "#61AFEF".to_string(), hover_color: "#528BFF".to_string(), error_color: "#E06C75".to_string(), warning_color: "#E5C07B".to_string() },
196        }
197    }
198
199    /// GitHub 主题
200    pub fn github() -> Self {
201        Self {
202            name: "GitHub".to_string(),
203            background_color: "#FFFFFF".to_string(),
204            node: NodeTheme {
205                fill_color: "#F6F8FA".to_string(),
206                stroke_color: "#D0D7DE".to_string(),
207                stroke_width: 1.0,
208                border_radius: 6.0,
209                shadow: ShadowConfig { enabled: true, color: "rgba(31, 35, 40, 0.04)".to_string(), offset_x: 0.0, offset_y: 1.0, blur_radius: 0.0 },
210            },
211            edge: EdgeTheme { color: "#656D76".to_string(), width: 1.0, style: "solid".to_string(), arrow: ArrowConfig { enabled: true, size: 6.0, arrow_type: "triangle".to_string() } },
212            text: TextTheme { font_family: "-apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif".to_string(), font_size: 12.0, color: "#24292F".to_string(), font_weight: "400".to_string() },
213            highlight: HighlightTheme { selected_color: "#0969DA".to_string(), hover_color: "#0860CA".to_string(), error_color: "#CF222E".to_string(), warning_color: "#9A6700".to_string() },
214        }
215    }
216}