osd_core/
theme.rs

1//! Theme definitions for sequence diagrams
2
3/// Participant box shape
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
5pub enum ParticipantShape {
6    /// Rectangle with square corners
7    #[default]
8    Rectangle,
9    /// Rectangle with rounded corners
10    RoundedRect,
11    /// Circle/ellipse
12    Circle,
13}
14
15/// Line style for lifelines
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
17pub enum LifelineStyle {
18    /// Dashed line (default)
19    #[default]
20    Dashed,
21    /// Solid line
22    Solid,
23}
24
25/// Theme colors and styles
26#[derive(Debug, Clone)]
27pub struct Theme {
28    /// Theme name
29    pub name: String,
30    /// Background color
31    pub background: String,
32    /// Participant box fill color
33    pub participant_fill: String,
34    /// Participant box stroke color
35    pub participant_stroke: String,
36    /// Participant text color
37    pub participant_text: String,
38    /// Participant box shape
39    pub participant_shape: ParticipantShape,
40    /// Lifeline color
41    pub lifeline_color: String,
42    /// Lifeline style
43    pub lifeline_style: LifelineStyle,
44    /// Message line color
45    pub message_color: String,
46    /// Message text color
47    pub message_text_color: String,
48    /// Note background color
49    pub note_fill: String,
50    /// Note stroke color
51    pub note_stroke: String,
52    /// Note text color
53    pub note_text_color: String,
54    /// Activation box fill color
55    pub activation_fill: String,
56    /// Activation box stroke color
57    pub activation_stroke: String,
58    /// Block stroke color
59    pub block_stroke: String,
60    /// Block label background
61    pub block_label_fill: String,
62    /// Block background fill (inside the block area)
63    pub block_fill: String,
64    /// Font family
65    pub font_family: String,
66    /// Actor head fill color
67    pub actor_fill: String,
68    /// Actor stroke color
69    pub actor_stroke: String,
70    /// State box fill color
71    pub state_fill: String,
72    /// State box stroke color
73    pub state_stroke: String,
74    /// State text color
75    pub state_text_color: String,
76    /// Ref box fill color
77    pub ref_fill: String,
78    /// Ref box stroke color
79    pub ref_stroke: String,
80    /// Ref text color
81    pub ref_text_color: String,
82    /// Description text color
83    pub description_text_color: String,
84}
85
86impl Default for Theme {
87    fn default() -> Self {
88        Self::default_theme()
89    }
90}
91
92impl Theme {
93    /// Default theme (simple black and white)
94    pub fn default_theme() -> Self {
95        Self {
96            name: "default".to_string(),
97            background: "#fff".to_string(),
98            participant_fill: "#fff".to_string(),
99            participant_stroke: "#333".to_string(),
100            participant_text: "#000".to_string(),
101            participant_shape: ParticipantShape::Rectangle,
102            lifeline_color: "#999".to_string(),
103            lifeline_style: LifelineStyle::Dashed,
104            message_color: "#333".to_string(),
105            message_text_color: "#000".to_string(),
106            note_fill: "#ffffcc".to_string(),
107            note_stroke: "#333".to_string(),
108            note_text_color: "#000".to_string(),
109            activation_fill: "#e0e0e0".to_string(),
110            activation_stroke: "#333".to_string(),
111            block_stroke: "#666".to_string(),
112            block_label_fill: "#fff".to_string(),
113            block_fill: "rgba(240, 240, 240, 0.6)".to_string(),
114            font_family: "sans-serif".to_string(),
115            actor_fill: "#fff".to_string(),
116            actor_stroke: "#333".to_string(),
117            state_fill: "#e8f5e9".to_string(),
118            state_stroke: "#4caf50".to_string(),
119            state_text_color: "#000".to_string(),
120            ref_fill: "#fff3e0".to_string(),
121            ref_stroke: "#ff9800".to_string(),
122            ref_text_color: "#000".to_string(),
123            description_text_color: "#666".to_string(),
124        }
125    }
126
127    /// Modern blue theme
128    pub fn modern_blue() -> Self {
129        Self {
130            name: "modern-blue".to_string(),
131            background: "#fff".to_string(),
132            participant_fill: "#4a90d9".to_string(),
133            participant_stroke: "#2a5a8a".to_string(),
134            participant_text: "#fff".to_string(),
135            participant_shape: ParticipantShape::RoundedRect,
136            lifeline_color: "#4a90d9".to_string(),
137            lifeline_style: LifelineStyle::Solid,
138            message_color: "#333".to_string(),
139            message_text_color: "#000".to_string(),
140            note_fill: "#e8f4fd".to_string(),
141            note_stroke: "#4a90d9".to_string(),
142            note_text_color: "#000".to_string(),
143            activation_fill: "#b8d4f0".to_string(),
144            activation_stroke: "#4a90d9".to_string(),
145            block_stroke: "#4a90d9".to_string(),
146            block_label_fill: "#e8f4fd".to_string(),
147            block_fill: "rgba(74, 144, 217, 0.1)".to_string(),
148            font_family: "sans-serif".to_string(),
149            actor_fill: "#4a90d9".to_string(),
150            actor_stroke: "#2a5a8a".to_string(),
151            state_fill: "#e3f2fd".to_string(),
152            state_stroke: "#1976d2".to_string(),
153            state_text_color: "#000".to_string(),
154            ref_fill: "#e8f4fd".to_string(),
155            ref_stroke: "#4a90d9".to_string(),
156            ref_text_color: "#000".to_string(),
157            description_text_color: "#666".to_string(),
158        }
159    }
160
161    /// Modern green theme
162    pub fn modern_green() -> Self {
163        Self {
164            name: "modern-green".to_string(),
165            background: "#fff".to_string(),
166            participant_fill: "#2d8659".to_string(),
167            participant_stroke: "#1a5c3a".to_string(),
168            participant_text: "#fff".to_string(),
169            participant_shape: ParticipantShape::RoundedRect,
170            lifeline_color: "#2d8659".to_string(),
171            lifeline_style: LifelineStyle::Dashed,
172            message_color: "#2d8659".to_string(),
173            message_text_color: "#000".to_string(),
174            note_fill: "#e8f5e9".to_string(),
175            note_stroke: "#2d8659".to_string(),
176            note_text_color: "#000".to_string(),
177            activation_fill: "#a5d6a7".to_string(),
178            activation_stroke: "#2d8659".to_string(),
179            block_stroke: "#2d8659".to_string(),
180            block_label_fill: "#e8f5e9".to_string(),
181            block_fill: "rgba(45, 134, 89, 0.1)".to_string(),
182            font_family: "sans-serif".to_string(),
183            actor_fill: "#2d8659".to_string(),
184            actor_stroke: "#1a5c3a".to_string(),
185            state_fill: "#e8f5e9".to_string(),
186            state_stroke: "#2d8659".to_string(),
187            state_text_color: "#000".to_string(),
188            ref_fill: "#e8f5e9".to_string(),
189            ref_stroke: "#2d8659".to_string(),
190            ref_text_color: "#000".to_string(),
191            description_text_color: "#666".to_string(),
192        }
193    }
194
195    /// Rose/pink theme with circles
196    pub fn rose() -> Self {
197        Self {
198            name: "rose".to_string(),
199            background: "#fff".to_string(),
200            participant_fill: "#c2185b".to_string(),
201            participant_stroke: "#880e4f".to_string(),
202            participant_text: "#fff".to_string(),
203            participant_shape: ParticipantShape::Circle,
204            lifeline_color: "#c2185b".to_string(),
205            lifeline_style: LifelineStyle::Solid,
206            message_color: "#c2185b".to_string(),
207            message_text_color: "#000".to_string(),
208            note_fill: "#fce4ec".to_string(),
209            note_stroke: "#c2185b".to_string(),
210            note_text_color: "#000".to_string(),
211            activation_fill: "#f48fb1".to_string(),
212            activation_stroke: "#c2185b".to_string(),
213            block_stroke: "#c2185b".to_string(),
214            block_label_fill: "#fce4ec".to_string(),
215            block_fill: "rgba(194, 24, 91, 0.1)".to_string(),
216            font_family: "sans-serif".to_string(),
217            actor_fill: "#c2185b".to_string(),
218            actor_stroke: "#880e4f".to_string(),
219            state_fill: "#fce4ec".to_string(),
220            state_stroke: "#c2185b".to_string(),
221            state_text_color: "#000".to_string(),
222            ref_fill: "#fce4ec".to_string(),
223            ref_stroke: "#c2185b".to_string(),
224            ref_text_color: "#000".to_string(),
225            description_text_color: "#666".to_string(),
226        }
227    }
228
229    /// Napkin/sketch style theme
230    pub fn napkin() -> Self {
231        Self {
232            name: "napkin".to_string(),
233            background: "#fff".to_string(),
234            participant_fill: "#fff".to_string(),
235            participant_stroke: "#333".to_string(),
236            participant_text: "#000".to_string(),
237            participant_shape: ParticipantShape::Rectangle,
238            lifeline_color: "#666".to_string(),
239            lifeline_style: LifelineStyle::Dashed,
240            message_color: "#333".to_string(),
241            message_text_color: "#000".to_string(),
242            note_fill: "#fff".to_string(),
243            note_stroke: "#333".to_string(),
244            note_text_color: "#000".to_string(),
245            activation_fill: "#f5f5f5".to_string(),
246            activation_stroke: "#333".to_string(),
247            block_stroke: "#333".to_string(),
248            block_label_fill: "#fff".to_string(),
249            block_fill: "rgba(200, 200, 200, 0.3)".to_string(),
250            font_family: "'Comic Sans MS', 'Chalkboard', cursive".to_string(),
251            actor_fill: "#fff".to_string(),
252            actor_stroke: "#333".to_string(),
253            state_fill: "#f5f5f5".to_string(),
254            state_stroke: "#333".to_string(),
255            state_text_color: "#000".to_string(),
256            ref_fill: "#f5f5f5".to_string(),
257            ref_stroke: "#333".to_string(),
258            ref_text_color: "#000".to_string(),
259            description_text_color: "#666".to_string(),
260        }
261    }
262
263    /// Earth tones theme
264    pub fn earth() -> Self {
265        Self {
266            name: "earth".to_string(),
267            background: "#faf8f5".to_string(),
268            participant_fill: "#8d6e63".to_string(),
269            participant_stroke: "#5d4037".to_string(),
270            participant_text: "#fff".to_string(),
271            participant_shape: ParticipantShape::RoundedRect,
272            lifeline_color: "#8d6e63".to_string(),
273            lifeline_style: LifelineStyle::Dashed,
274            message_color: "#5d4037".to_string(),
275            message_text_color: "#3e2723".to_string(),
276            note_fill: "#efebe9".to_string(),
277            note_stroke: "#8d6e63".to_string(),
278            note_text_color: "#3e2723".to_string(),
279            activation_fill: "#bcaaa4".to_string(),
280            activation_stroke: "#8d6e63".to_string(),
281            block_stroke: "#8d6e63".to_string(),
282            block_label_fill: "#efebe9".to_string(),
283            block_fill: "rgba(141, 110, 99, 0.1)".to_string(),
284            font_family: "Georgia, serif".to_string(),
285            actor_fill: "#8d6e63".to_string(),
286            actor_stroke: "#5d4037".to_string(),
287            state_fill: "#efebe9".to_string(),
288            state_stroke: "#8d6e63".to_string(),
289            state_text_color: "#3e2723".to_string(),
290            ref_fill: "#efebe9".to_string(),
291            ref_stroke: "#8d6e63".to_string(),
292            ref_text_color: "#3e2723".to_string(),
293            description_text_color: "#5d4037".to_string(),
294        }
295    }
296
297    /// Plain monochrome theme
298    pub fn plain() -> Self {
299        Self {
300            name: "plain".to_string(),
301            background: "#fff".to_string(),
302            participant_fill: "#fff".to_string(),
303            participant_stroke: "#000".to_string(),
304            participant_text: "#000".to_string(),
305            participant_shape: ParticipantShape::Rectangle,
306            lifeline_color: "#000".to_string(),
307            lifeline_style: LifelineStyle::Solid,
308            message_color: "#000".to_string(),
309            message_text_color: "#000".to_string(),
310            note_fill: "#fff".to_string(),
311            note_stroke: "#000".to_string(),
312            note_text_color: "#000".to_string(),
313            activation_fill: "#ccc".to_string(),
314            activation_stroke: "#000".to_string(),
315            block_stroke: "#000".to_string(),
316            block_label_fill: "#fff".to_string(),
317            block_fill: "rgba(200, 200, 200, 0.3)".to_string(),
318            font_family: "sans-serif".to_string(),
319            actor_fill: "#fff".to_string(),
320            actor_stroke: "#000".to_string(),
321            state_fill: "#f0f0f0".to_string(),
322            state_stroke: "#000".to_string(),
323            state_text_color: "#000".to_string(),
324            ref_fill: "#f0f0f0".to_string(),
325            ref_stroke: "#000".to_string(),
326            ref_text_color: "#000".to_string(),
327            description_text_color: "#333".to_string(),
328        }
329    }
330
331    /// Mellow/pastel theme with circles
332    pub fn mellow() -> Self {
333        Self {
334            name: "mellow".to_string(),
335            background: "#fff".to_string(),
336            participant_fill: "#a8e6cf".to_string(),
337            participant_stroke: "#56ab91".to_string(),
338            participant_text: "#2d5a4a".to_string(),
339            participant_shape: ParticipantShape::Circle,
340            lifeline_color: "#56ab91".to_string(),
341            lifeline_style: LifelineStyle::Dashed,
342            message_color: "#56ab91".to_string(),
343            message_text_color: "#2d5a4a".to_string(),
344            note_fill: "#dcedc1".to_string(),
345            note_stroke: "#56ab91".to_string(),
346            note_text_color: "#2d5a4a".to_string(),
347            activation_fill: "#a8e6cf".to_string(),
348            activation_stroke: "#56ab91".to_string(),
349            block_stroke: "#56ab91".to_string(),
350            block_label_fill: "#dcedc1".to_string(),
351            block_fill: "rgba(86, 171, 145, 0.1)".to_string(),
352            font_family: "sans-serif".to_string(),
353            actor_fill: "#a8e6cf".to_string(),
354            actor_stroke: "#56ab91".to_string(),
355            state_fill: "#dcedc1".to_string(),
356            state_stroke: "#56ab91".to_string(),
357            state_text_color: "#2d5a4a".to_string(),
358            ref_fill: "#dcedc1".to_string(),
359            ref_stroke: "#56ab91".to_string(),
360            ref_text_color: "#2d5a4a".to_string(),
361            description_text_color: "#56ab91".to_string(),
362        }
363    }
364
365    /// Blue outline theme
366    pub fn blue_outline() -> Self {
367        Self {
368            name: "blue-outline".to_string(),
369            background: "#fff".to_string(),
370            participant_fill: "#fff".to_string(),
371            participant_stroke: "#1976d2".to_string(),
372            participant_text: "#1976d2".to_string(),
373            participant_shape: ParticipantShape::Rectangle,
374            lifeline_color: "#1976d2".to_string(),
375            lifeline_style: LifelineStyle::Dashed,
376            message_color: "#1976d2".to_string(),
377            message_text_color: "#1976d2".to_string(),
378            note_fill: "#e3f2fd".to_string(),
379            note_stroke: "#1976d2".to_string(),
380            note_text_color: "#1976d2".to_string(),
381            activation_fill: "#bbdefb".to_string(),
382            activation_stroke: "#1976d2".to_string(),
383            block_stroke: "#1976d2".to_string(),
384            block_label_fill: "#e3f2fd".to_string(),
385            block_fill: "rgba(25, 118, 210, 0.1)".to_string(),
386            font_family: "sans-serif".to_string(),
387            actor_fill: "#fff".to_string(),
388            actor_stroke: "#1976d2".to_string(),
389            state_fill: "#e3f2fd".to_string(),
390            state_stroke: "#1976d2".to_string(),
391            state_text_color: "#1976d2".to_string(),
392            ref_fill: "#e3f2fd".to_string(),
393            ref_stroke: "#1976d2".to_string(),
394            ref_text_color: "#1976d2".to_string(),
395            description_text_color: "#1976d2".to_string(),
396        }
397    }
398
399    /// Orange/yellow warm theme
400    pub fn warm() -> Self {
401        Self {
402            name: "warm".to_string(),
403            background: "#fffbf0".to_string(),
404            participant_fill: "#ffcc80".to_string(),
405            participant_stroke: "#ef6c00".to_string(),
406            participant_text: "#000".to_string(),
407            participant_shape: ParticipantShape::RoundedRect,
408            lifeline_color: "#ef6c00".to_string(),
409            lifeline_style: LifelineStyle::Dashed,
410            message_color: "#ef6c00".to_string(),
411            message_text_color: "#000".to_string(),
412            note_fill: "#fff3e0".to_string(),
413            note_stroke: "#ef6c00".to_string(),
414            note_text_color: "#000".to_string(),
415            activation_fill: "#ffcc80".to_string(),
416            activation_stroke: "#ef6c00".to_string(),
417            block_stroke: "#ef6c00".to_string(),
418            block_label_fill: "#fff3e0".to_string(),
419            block_fill: "rgba(239, 108, 0, 0.1)".to_string(),
420            font_family: "sans-serif".to_string(),
421            actor_fill: "#ffcc80".to_string(),
422            actor_stroke: "#ef6c00".to_string(),
423            state_fill: "#fff3e0".to_string(),
424            state_stroke: "#ef6c00".to_string(),
425            state_text_color: "#000".to_string(),
426            ref_fill: "#fff3e0".to_string(),
427            ref_stroke: "#ef6c00".to_string(),
428            ref_text_color: "#000".to_string(),
429            description_text_color: "#ef6c00".to_string(),
430        }
431    }
432
433    /// Gray professional theme
434    pub fn gray() -> Self {
435        Self {
436            name: "gray".to_string(),
437            background: "#fafafa".to_string(),
438            participant_fill: "#757575".to_string(),
439            participant_stroke: "#424242".to_string(),
440            participant_text: "#fff".to_string(),
441            participant_shape: ParticipantShape::Rectangle,
442            lifeline_color: "#757575".to_string(),
443            lifeline_style: LifelineStyle::Solid,
444            message_color: "#424242".to_string(),
445            message_text_color: "#212121".to_string(),
446            note_fill: "#eeeeee".to_string(),
447            note_stroke: "#757575".to_string(),
448            note_text_color: "#212121".to_string(),
449            activation_fill: "#bdbdbd".to_string(),
450            activation_stroke: "#757575".to_string(),
451            block_stroke: "#757575".to_string(),
452            block_label_fill: "#eeeeee".to_string(),
453            block_fill: "rgba(117, 117, 117, 0.1)".to_string(),
454            font_family: "sans-serif".to_string(),
455            actor_fill: "#757575".to_string(),
456            actor_stroke: "#424242".to_string(),
457            state_fill: "#eeeeee".to_string(),
458            state_stroke: "#757575".to_string(),
459            state_text_color: "#212121".to_string(),
460            ref_fill: "#eeeeee".to_string(),
461            ref_stroke: "#757575".to_string(),
462            ref_text_color: "#212121".to_string(),
463            description_text_color: "#757575".to_string(),
464        }
465    }
466
467    /// Dark theme with vibrant green accents
468    pub fn dark_green() -> Self {
469        Self {
470            name: "dark-green".to_string(),
471            background: "#08090a".to_string(),
472            participant_fill: "#08090a".to_string(),
473            participant_stroke: "#22c55e".to_string(),
474            participant_text: "#22c55e".to_string(),
475            participant_shape: ParticipantShape::Rectangle,
476            lifeline_color: "#22c55e".to_string(),
477            lifeline_style: LifelineStyle::Dashed,
478            message_color: "#22c55e".to_string(),
479            message_text_color: "#22c55e".to_string(),
480            note_fill: "#08090a".to_string(),
481            note_stroke: "#22c55e".to_string(),
482            note_text_color: "#22c55e".to_string(),
483            activation_fill: "#08090a".to_string(),
484            activation_stroke: "#22c55e".to_string(),
485            block_stroke: "#22c55e".to_string(),
486            block_label_fill: "#08090a".to_string(),
487            block_fill: "transparent".to_string(),
488            font_family: "sans-serif".to_string(),
489            actor_fill: "#08090a".to_string(),
490            actor_stroke: "#22c55e".to_string(),
491            state_fill: "#08090a".to_string(),
492            state_stroke: "#22c55e".to_string(),
493            state_text_color: "#22c55e".to_string(),
494            ref_fill: "#08090a".to_string(),
495            ref_stroke: "#22c55e".to_string(),
496            ref_text_color: "#22c55e".to_string(),
497            description_text_color: "#22c55e".to_string(),
498        }
499    }
500
501    /// Get theme by name
502    pub fn by_name(name: &str) -> Option<Self> {
503        match name.to_lowercase().as_str() {
504            "default" => Some(Self::default_theme()),
505            "modern-blue" | "modernblue" | "blue" => Some(Self::modern_blue()),
506            "modern-green" | "moderngreen" | "green" => Some(Self::modern_green()),
507            "rose" | "pink" => Some(Self::rose()),
508            "napkin" | "sketch" => Some(Self::napkin()),
509            "earth" | "brown" => Some(Self::earth()),
510            "plain" | "monochrome" => Some(Self::plain()),
511            "mellow" | "pastel" => Some(Self::mellow()),
512            "blue-outline" | "blueoutline" => Some(Self::blue_outline()),
513            "warm" | "orange" => Some(Self::warm()),
514            "gray" | "grey" => Some(Self::gray()),
515            "dark-green" | "darkgreen" => Some(Self::dark_green()),
516            _ => None,
517        }
518    }
519
520    /// List all available theme names
521    pub fn available_themes() -> Vec<&'static str> {
522        vec![
523            "default",
524            "modern-blue",
525            "modern-green",
526            "rose",
527            "napkin",
528            "earth",
529            "plain",
530            "mellow",
531            "blue-outline",
532            "warm",
533            "gray",
534            "dark-green",
535        ]
536    }
537}