1use serde::{Serialize, Serializer};
5
6macro_rules! string_enum {
8 ($(#[$m:meta])* $name:ident { $($(#[$vm:meta])* $var:ident = $s:literal),* $(,)? }) => {
9 $(#[$m])*
10 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
11 pub enum $name {
12 $($(#[$vm])* $var,)*
13 Other(String),
15 }
16
17 impl $name {
18 pub fn as_str(&self) -> &str {
20 match self {
21 $(Self::$var => $s,)*
22 Self::Other(s) => s,
23 }
24 }
25 }
26
27 impl From<&str> for $name {
28 fn from(s: &str) -> Self {
29 match s {
30 $($s => Self::$var,)*
31 _ => Self::Other(s.to_owned()),
32 }
33 }
34 }
35
36 impl From<String> for $name {
37 fn from(s: String) -> Self {
38 Self::from(s.as_str())
39 }
40 }
41
42 impl ::std::fmt::Display for $name {
43 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
44 f.write_str(self.as_str())
45 }
46 }
47
48 impl ::serde::Serialize for $name {
49 fn serialize<S: ::serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
50 s.serialize_str(self.as_str())
51 }
52 }
53 };
54}
55
56pub(crate) use string_enum;
57
58string_enum!(
59 Tone {
61 Neutral = "neutral",
63 Accent = "accent",
65 Info = "info",
67 Success = "success",
69 Warning = "warning",
71 Error = "error",
73 Pending = "pending",
75 Muted = "muted",
77 User = "user",
79 }
80);
81
82string_enum!(
83 Status {
85 Pending = "pending",
87 Running = "running",
89 Done = "done",
91 Error = "error",
93 Cancelled = "cancelled",
95 }
96);
97
98string_enum!(
99 AgentStatus {
101 Pending = "pending",
103 Running = "running",
105 Done = "done",
107 Failed = "failed",
109 Aborted = "aborted",
111 Idle = "idle",
113 Parked = "parked",
115 }
116);
117
118string_enum!(
119 Mark {
121 Pick = "pick",
123 Drop = "drop",
125 }
126);
127
128string_enum!(
129 Gap {
131 None = "none",
133 Xs = "xs",
135 Sm = "sm",
137 Md = "md",
139 Lg = "lg",
141 }
142);
143
144string_enum!(
145 Align {
147 Start = "start",
149 Center = "center",
151 End = "end",
153 Baseline = "baseline",
155 Stretch = "stretch",
157 }
158);
159
160string_enum!(
161 Justify {
163 Between = "between",
165 End = "end",
167 }
168);
169
170string_enum!(
171 Wrap {
173 Word = "word",
175 Char = "char",
177 None = "none",
179 }
180);
181
182string_enum!(
183 Truncate {
185 End = "end",
187 Start = "start",
189 Middle = "middle",
191 }
192);
193
194string_enum!(
195 TextAlign {
197 Start = "start",
199 Center = "center",
201 End = "end",
203 }
204);
205
206string_enum!(
207 Fx {
209 Shimmer = "shimmer",
211 Pulse = "pulse",
213 None = "none",
215 }
216);
217
218string_enum!(
219 Token {
222 Muted = "muted",
224 Dim = "dim",
226 Strong = "strong",
228 Em = "em",
230 Accent = "accent",
232 Success = "success",
234 Warning = "warning",
236 Error = "error",
238 Info = "info",
240 Code = "code",
242 Mono = "mono",
244 Path = "path",
246 Key = "key",
248 Link = "link",
250 Num = "num",
252 Ins = "ins",
254 Del = "del",
256 Mark = "mark",
258 Typo = "typo",
260 Icon = "icon",
262 Hide = "hide",
264 }
265);
266
267string_enum!(
268 SpinnerStyle {
270 Braille = "braille",
272 Dots = "dots",
274 Starburst = "starburst",
276 Orbit = "orbit",
278 }
279);
280
281string_enum!(
282 ShimmerMode {
284 Classic = "classic",
286 Kitt = "kitt",
288 }
289);
290
291string_enum!(
292 ElapsedFormat {
294 Short = "short",
296 Clock = "clock",
298 }
299);
300
301string_enum!(
302 MeterStyle {
304 Bar = "bar",
306 Ring = "ring",
308 Blocks = "blocks",
310 }
311);
312
313string_enum!(
314 Size {
316 Sm = "sm",
318 Md = "md",
320 Lg = "lg",
322 }
323);
324
325string_enum!(
326 ChartKind {
328 Heatmap = "heatmap",
330 Bars = "bars",
332 Spark = "spark",
334 }
335);
336
337string_enum!(
338 EffortLevel {
340 Off = "off",
342 Minimal = "minimal",
344 Low = "low",
346 Medium = "medium",
348 High = "high",
350 Xhigh = "xhigh",
352 Max = "max",
354 }
355);
356
357string_enum!(
358 DiffMode {
360 Unified = "unified",
362 Split = "split",
364 Auto = "auto",
366 }
367);
368
369string_enum!(
370 KvLayout {
372 Grid = "grid",
374 Inline = "inline",
376 }
377);
378
379string_enum!(
380 Measure {
382 Prose = "prose",
384 }
385);
386
387string_enum!(
388 CardVariant {
390 Bare = "bare",
392 }
393);
394
395string_enum!(
396 PickerSize {
398 Md = "md",
400 Lg = "lg",
402 Screen = "screen",
404 }
405);
406
407string_enum!(
408 PickerLayout {
410 Rows = "rows",
412 Cards = "cards",
414 Timeline = "timeline",
416 Tree = "tree",
418 }
419);
420
421string_enum!(
422 PickerPreview {
424 Side = "side",
426 Below = "below",
428 None = "none",
430 }
431);
432
433string_enum!(
434 PickerFocus {
436 List = "list",
438 Scopes = "scopes",
440 Tabs = "tabs",
442 Strip = "strip",
444 Preview = "preview",
446 }
447);
448
449string_enum!(
450 PickerState {
452 Ready = "ready",
454 Loading = "loading",
456 Error = "error",
458 }
459);
460
461string_enum!(
462 TargetKind {
464 Command = "command",
466 Path = "path",
468 Pattern = "pattern",
470 Query = "query",
472 Text = "text",
474 }
475);
476
477string_enum!(
478 ToolFrame {
480 Card = "card",
482 Inline = "inline",
484 }
485);
486
487string_enum!(
488 ChecklistMode {
490 Full = "full",
492 Hud = "hud",
494 Reminder = "reminder",
496 }
497);
498
499string_enum!(
500 ItemStatus {
502 Pending = "pending",
504 Active = "active",
506 Done = "done",
508 Dropped = "dropped",
510 Blocked = "blocked",
512 }
513);
514
515string_enum!(
516 OverlaySize {
518 Sm = "sm",
520 Md = "md",
522 Lg = "lg",
524 Full = "full",
526 }
527);
528
529string_enum!(
530 Side {
532 Right = "right",
534 }
535);
536
537string_enum!(
538 ColumnFormat {
540 Text = "text",
542 Num = "num",
544 Price = "price",
546 Bar = "bar",
548 Time = "time",
550 Elapsed = "elapsed",
552 Dim = "dim",
554 }
555);
556
557string_enum!(
558 ChoiceStyle {
560 Auto = "auto",
562 Segmented = "segmented",
564 Menu = "menu",
566 }
567);
568
569string_enum!(
570 InputType {
572 Checkbox = "checkbox",
574 Radio = "radio",
576 }
577);
578
579#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize)]
581pub struct Span {
582 pub t: String,
584 #[serde(skip_serializing_if = "Option::is_none")]
586 pub s: Option<String>,
587 #[serde(skip_serializing_if = "Option::is_none")]
589 pub fx: Option<Fx>,
590 #[serde(skip_serializing_if = "Option::is_none")]
592 pub href: Option<String>,
593}
594
595impl Span {
596 pub fn new(t: impl Into<String>) -> Self {
598 Self { t: t.into(), ..Self::default() }
599 }
600
601 pub fn style(mut self, token: impl Into<Token>) -> Self {
603 let token = token.into();
604 self.s = Some(match self.s.take() {
605 Some(s) if !s.is_empty() => format!("{s} {token}"),
606 _ => token.to_string(),
607 });
608 self
609 }
610
611 pub fn fx(mut self, fx: impl Into<Fx>) -> Self {
613 self.fx = Some(fx.into());
614 self
615 }
616
617 pub fn href(mut self, href: impl Into<String>) -> Self {
619 self.href = Some(href.into());
620 self
621 }
622}
623
624impl From<&str> for Span {
625 fn from(t: &str) -> Self {
626 Self::new(t)
627 }
628}
629
630impl From<String> for Span {
631 fn from(t: String) -> Self {
632 Self::new(t)
633 }
634}
635
636#[derive(Clone, Debug, PartialEq, Eq)]
638pub enum Text {
639 Plain(String),
641 Spans(Vec<Span>),
643}
644
645impl Text {
646 pub fn plain(&self) -> String {
648 match self {
649 Self::Plain(s) => s.clone(),
650 Self::Spans(spans) => spans.iter().map(|s| s.t.as_str()).collect(),
651 }
652 }
653}
654
655impl Default for Text {
656 fn default() -> Self {
657 Self::Plain(String::new())
658 }
659}
660
661impl Serialize for Text {
662 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
663 match self {
664 Self::Plain(t) => s.serialize_str(t),
665 Self::Spans(spans) => spans.serialize(s),
666 }
667 }
668}
669
670impl From<&str> for Text {
671 fn from(t: &str) -> Self {
672 Self::Plain(t.to_owned())
673 }
674}
675
676impl From<String> for Text {
677 fn from(t: String) -> Self {
678 Self::Plain(t)
679 }
680}
681
682impl From<&String> for Text {
683 fn from(t: &String) -> Self {
684 Self::Plain(t.clone())
685 }
686}
687
688impl From<Span> for Text {
689 fn from(span: Span) -> Self {
690 Self::Spans(vec![span])
691 }
692}
693
694impl From<Vec<Span>> for Text {
695 fn from(spans: Vec<Span>) -> Self {
696 Self::Spans(spans)
697 }
698}
699
700impl<const N: usize> From<[Span; N]> for Text {
701 fn from(spans: [Span; N]) -> Self {
702 Self::Spans(spans.into())
703 }
704}
705
706#[derive(Clone, Copy, Debug, PartialEq)]
708pub enum Extent {
709 Ch(f64),
711 Lines(f64),
713 Fraction(f64),
715}
716
717impl Serialize for Extent {
718 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
719 match *self {
720 Self::Ch(n) => s.collect_str(&format_args!("{n}ch")),
721 Self::Lines(n) => s.collect_str(&format_args!("{n}lines")),
722 Self::Fraction(f) => s.serialize_f64(f),
723 }
724 }
725}
726
727#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize)]
729pub struct Bound {
730 #[serde(skip_serializing_if = "Option::is_none")]
732 pub w: Option<Extent>,
733 #[serde(skip_serializing_if = "Option::is_none")]
735 pub h: Option<Extent>,
736}
737
738impl Bound {
739 pub const fn w(w: Extent) -> Self {
741 Self { w: Some(w), h: None }
742 }
743
744 pub const fn h(h: Extent) -> Self {
746 Self { w: None, h: Some(h) }
747 }
748
749 pub const fn wh(w: Extent, h: Extent) -> Self {
751 Self { w: Some(w), h: Some(h) }
752 }
753}
754
755#[derive(Clone, Copy, Debug, PartialEq)]
757pub enum Basis {
758 Fraction(f64),
760 Content,
762}
763
764impl Serialize for Basis {
765 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
766 match *self {
767 Self::Fraction(f) => s.serialize_f64(f),
768 Self::Content => s.serialize_str("content"),
769 }
770 }
771}
772
773impl From<f64> for Basis {
774 fn from(f: f64) -> Self {
775 Self::Fraction(f)
776 }
777}
778
779#[derive(Clone, Debug, PartialEq, Eq, Hash)]
781pub enum NodeKey {
782 Str(String),
784 Int(i64),
786}
787
788impl Serialize for NodeKey {
789 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
790 match self {
791 Self::Str(k) => s.serialize_str(k),
792 Self::Int(n) => s.serialize_i64(*n),
793 }
794 }
795}
796
797impl From<&str> for NodeKey {
798 fn from(k: &str) -> Self {
799 Self::Str(k.to_owned())
800 }
801}
802
803impl From<String> for NodeKey {
804 fn from(k: String) -> Self {
805 Self::Str(k)
806 }
807}
808
809impl From<&String> for NodeKey {
810 fn from(k: &String) -> Self {
811 Self::Str(k.clone())
812 }
813}
814
815macro_rules! int_key {
817 ($($t:ty),*) => {
818 $(impl From<$t> for NodeKey {
819 fn from(n: $t) -> Self {
820 Self::Int(n as i64)
821 }
822 })*
823 };
824}
825
826int_key!(i32, i64, u32, u64, usize);
827
828#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize)]
830pub struct Actions {
831 #[serde(skip_serializing_if = "Option::is_none")]
833 pub click: Option<String>,
834 #[serde(skip_serializing_if = "Option::is_none")]
836 pub dblclick: Option<String>,
837 #[serde(skip_serializing_if = "Vec::is_empty")]
839 pub menu: Vec<String>,
840}
841
842impl Actions {
843 pub fn new() -> Self {
845 Self::default()
846 }
847
848 pub fn click(mut self, act: impl Into<String>) -> Self {
850 self.click = Some(act.into());
851 self
852 }
853
854 pub fn dblclick(mut self, act: impl Into<String>) -> Self {
856 self.dblclick = Some(act.into());
857 self
858 }
859
860 pub fn menu(mut self, act: impl Into<String>) -> Self {
862 self.menu.push(act.into());
863 self
864 }
865}