1mod data;
28pub mod html;
29mod icon;
30pub(crate) mod node;
31mod types;
32
33use std::time::Instant;
34
35pub use data::*;
36pub use icon::Icon;
37pub use node::{Node, View};
38pub use types::*;
39
40use self::node::{
41 actions_methods, base_methods, children_methods, event_methods, kind_type, max_method,
42 min_method, props, title_method,
43};
44
45pub type CardStatus = Status;
47
48#[macro_export]
58macro_rules! spans {
59 ($($s:expr),* $(,)?) => {
60 vec![$($crate::ui::Span::from($s)),*]
61 };
62}
63
64#[macro_export]
74macro_rules! nodes {
75 ($($n:expr),* $(,)?) => {
76 vec![$($crate::ui::Node::from($n)),*]
77 };
78}
79
80pub fn span(t: impl Into<String>) -> Span {
82 Span::new(t)
83}
84
85pub fn node<M>(kind: impl Into<String>) -> Node<M> {
87 Node::new(kind)
88}
89
90macro_rules! common_kind {
92 ($(#[$m:meta])* $name:ident) => {
93 kind_type!($(#[$m])* $name);
94
95 impl<M> $name<M> {
96 base_methods!();
97 min_method!();
98 title_method!();
99 max_method!();
100 actions_methods!();
101 event_methods!();
102 }
103 };
104}
105
106macro_rules! container_kind {
108 ($(#[$m:meta])* $name:ident) => {
109 common_kind!($(#[$m])* $name);
110
111 impl<M> $name<M> {
112 children_methods!();
113 }
114 };
115}
116
117fn new<M, T: From<Node<M>>>(kind: &str) -> T {
119 T::from(Node::new(kind))
120}
121
122macro_rules! from_node {
124 ($($name:ident),*) => {
125 $(impl<M> From<Node<M>> for $name<M> {
126 fn from(node: Node<M>) -> Self {
127 Self(node)
128 }
129 })*
130 };
131}
132
133from_node!(
134 Col,
135 Row,
136 Card,
137 Section,
138 Rule,
139 Spacer,
140 TextNode,
141 Md,
142 Code,
143 Diff,
144 Ansi,
145 Rows,
146 Math,
147 Kv,
148 Table,
149 Tree,
150 Json,
151 Badge,
152 Kbd,
153 IconNode,
154 Image,
155 Media,
156 List,
157 Item,
158 Tabs,
159 Picker,
160 Spinner,
161 Shimmer,
162 Elapsed,
163 Rate,
164 Progress,
165 Meter,
166 Chart,
167 Effort,
168 Editor,
169 Input,
170 StatusBar,
171 Seg,
172 Toast,
173 Overlay,
174 Tool,
175 Agent,
176 Checklist,
177 Prefs,
178 Button,
179 Stats,
180 Select,
181 Switch,
182 Slider,
183 Field,
184 Otp,
185 Buttons,
186 Avatar,
187 Avatars,
188 Breadcrumb,
189 Pager,
190 Empty,
191 Skeleton,
192 Calendar,
193 Date,
194 Menu,
195 Menubar,
196 Nav,
197 Split,
198 Scroll,
199 Carousel,
200 Message,
201 Attachment,
202 Trace,
203 Stacktrace,
204 Suggestions,
205 Questions
206);
207
208pub enum Slot<M = ()> {
211 Text(Text),
213 Node(Node<M>),
216}
217
218impl<M> From<&str> for Slot<M> {
219 fn from(t: &str) -> Self {
220 Self::Text(t.into())
221 }
222}
223
224impl<M> From<String> for Slot<M> {
225 fn from(t: String) -> Self {
226 Self::Text(t.into())
227 }
228}
229
230impl<M> From<&String> for Slot<M> {
231 fn from(t: &String) -> Self {
232 Self::Text(t.into())
233 }
234}
235
236impl<M, const N: usize> From<[Span; N]> for Slot<M> {
237 fn from(s: [Span; N]) -> Self {
238 Self::Text(s.into())
239 }
240}
241
242impl<M> From<Text> for Slot<M> {
243 fn from(t: Text) -> Self {
244 Self::Text(t)
245 }
246}
247
248impl<M> From<Span> for Slot<M> {
249 fn from(s: Span) -> Self {
250 Self::Text(s.into())
251 }
252}
253
254impl<M> From<Vec<Span>> for Slot<M> {
255 fn from(s: Vec<Span>) -> Self {
256 Self::Text(s.into())
257 }
258}
259
260impl<M> From<Node<M>> for Slot<M> {
261 fn from(n: Node<M>) -> Self {
262 Self::Node(n)
263 }
264}
265
266macro_rules! slot_from {
268 ($($name:ident),*) => {
269 $(impl<M> From<$name<M>> for Slot<M> {
270 fn from(n: $name<M>) -> Self {
271 Self::Node(n.0)
272 }
273 })*
274 };
275}
276
277slot_from!(
278 Col,
279 Row,
280 Card,
281 Section,
282 Rule,
283 Spacer,
284 TextNode,
285 Md,
286 Code,
287 Diff,
288 Ansi,
289 Rows,
290 Math,
291 Kv,
292 Table,
293 Tree,
294 Json,
295 Badge,
296 Kbd,
297 IconNode,
298 Image,
299 Media,
300 List,
301 Item,
302 Tabs,
303 Picker,
304 Spinner,
305 Shimmer,
306 Elapsed,
307 Rate,
308 Progress,
309 Meter,
310 Chart,
311 Effort,
312 Editor,
313 Input,
314 StatusBar,
315 Seg,
316 Toast,
317 Overlay,
318 Tool,
319 Agent,
320 Checklist,
321 Prefs,
322 Button,
323 Stats,
324 Select,
325 Switch,
326 Slider,
327 Field,
328 Otp,
329 Buttons,
330 Avatar,
331 Avatars,
332 Breadcrumb,
333 Pager,
334 Empty,
335 Skeleton,
336 Calendar,
337 Date,
338 Menu,
339 Menubar,
340 Nav,
341 Split,
342 Scroll,
343 Carousel,
344 Message,
345 Attachment,
346 Trace,
347 Stacktrace,
348 Suggestions,
349 Questions
350);
351
352fn set_slot<M>(node: &mut Node<M>, slot: &'static str, value: Slot<M>) {
354 match value {
355 Slot::Text(text) => {
356 node.clear_slot(slot);
357 node.set(slot, &text);
358 },
359 Slot::Node(child) => node.set_slot(slot, child),
360 }
361}
362
363macro_rules! panel_head_methods {
365 () => {
366 pub fn icon(mut self, icon: impl Into<Icon>) -> Self {
368 self.0.set("icon", &icon.into());
369 self
370 }
371
372 pub fn meta(mut self, meta: impl Into<Text>) -> Self {
374 self.0.set("meta", &meta.into());
375 self
376 }
377
378 pub fn end(mut self, end: impl Into<Slot<M>>) -> Self {
381 set_slot(&mut self.0, "end", end.into());
382 self
383 }
384 };
385}
386
387container_kind!(
392 Col
394);
395
396impl<M> Col<M> {
397 props! {
398 gap(Gap) = "gap";
400 align(Align) = "align";
402 justify(Justify) = "justify";
404 wrap(bool) = "wrap";
406 }
407}
408
409pub fn col<M>() -> Col<M> {
411 new("col")
412}
413
414container_kind!(
415 Row
417);
418
419impl<M> Row<M> {
420 props! {
421 gap(Gap) = "gap";
423 align(Align) = "align";
425 justify(Justify) = "justify";
427 wrap(bool) = "wrap";
429 }
430}
431
432pub fn row<M>() -> Row<M> {
434 new("row")
435}
436
437container_kind!(
438 Card
440);
441
442impl<M> Card<M> {
443 panel_head_methods!();
444
445 props! {
446 status(Status) = "status";
448 collapsible(bool) = "collapsible";
450 collapsed(bool) = "collapsed";
452 preview(Preview) = "preview";
454 selected(bool) = "selected";
456 inset(bool) = "inset";
458 variant(CardVariant) = "variant";
460 }
461
462 pub fn head(mut self, head: impl Into<Slot<M>>) -> Self {
464 set_slot(&mut self.0, "head", head.into());
465 self
466 }
467}
468
469pub fn card<M>() -> Card<M> {
471 new("card")
472}
473
474container_kind!(
475 Section
477);
478
479impl<M> Section<M> {
480 props! {
481 collapsible(bool) = "collapsible";
483 collapsed(bool) = "collapsed";
485 }
486
487 pub fn head(mut self, head: impl Into<Slot<M>>) -> Self {
489 set_slot(&mut self.0, "head", head.into());
490 self
491 }
492}
493
494pub fn section<M>() -> Section<M> {
496 new("section")
497}
498
499common_kind!(
500 Rule
502);
503
504impl<M> Rule<M> {
505 props! {
506 label(Text) = "label";
509 variant(RuleVariant) = "variant";
511 icon(Icon) = "icon";
513 time(String) = "time";
515 }
516
517 pub fn end(mut self, end: impl Into<Slot<M>>) -> Self {
520 set_slot(&mut self.0, "end", end.into());
521 self
522 }
523}
524
525pub fn rule<M>() -> Rule<M> {
527 new("rule")
528}
529
530common_kind!(
531 Spacer
533);
534
535impl<M> Spacer<M> {
536 props! {
537 size(Gap) = "size";
539 }
540}
541
542pub fn spacer<M>() -> Spacer<M> {
544 new("spacer")
545}
546
547common_kind!(
552 TextNode
554);
555
556impl<M> TextNode<M> {
557 props! {
558 wrap(Wrap) = "wrap";
560 truncate(Truncate) = "truncate";
562 lines(u32) = "lines";
564 measure(Measure) = "measure";
566 cite(CiteStyle) = "cite";
568 sources(bool) = "sources";
571 }
572
573 pub fn text(mut self, text: impl Into<Text>) -> Self {
575 self.0.set_text(text.into());
576 self
577 }
578}
579
580pub fn text<M>(text: impl Into<Text>) -> TextNode<M> {
582 let mut node = Node::new("text");
583 node.set_text(text.into());
584 TextNode(node)
585}
586
587common_kind!(
588 Md
590);
591
592impl<M> Md<M> {
593 props! {
594 text(String) = "text";
596 stream(bool) = "stream";
598 marks(Vec<Span>) = "marks";
600 }
601}
602
603pub fn md<M>(source: impl Into<String>) -> Md<M> {
605 new::<M, Md<M>>("md").text(source)
606}
607
608common_kind!(
609 Code
611);
612
613impl<M> Code<M> {
614 props! {
615 text(String) = "text";
617 lang(String) = "lang";
619 path(String) = "path";
621 numbers(bool) = "numbers";
623 start(i64) = "start";
625 nums(Vec<u64>) = "nums";
629 marks(Vec<CodeMark>) = "marks";
631 wrap(bool) = "wrap";
633 folds(Vec<CodeFold>) = "folds";
635 overview(bool) = "overview";
638 }
639}
640
641pub fn code<M>(source: impl Into<String>) -> Code<M> {
643 new::<M, Code<M>>("code").text(source)
644}
645
646common_kind!(
647 Diff
649);
650
651impl<M> Diff<M> {
652 props! {
653 text(String) = "text";
655 hunks(Vec<Hunk>) = "hunks";
657 path(String) = "path";
659 lang(String) = "lang";
661 mode(DiffMode) = "mode";
663 }
664}
665
666pub fn diff<M>() -> Diff<M> {
668 new("diff")
669}
670
671common_kind!(
672 Ansi
674);
675
676impl<M> Ansi<M> {
677 props! {
678 text(String) = "text";
680 cols(u32) = "cols";
682 follow(bool) = "follow";
684 }
685
686 pub fn preview(mut self, lines: u32) -> Self {
688 self
689 .0
690 .set("preview", &serde_json::json!({ "lines": lines }));
691 self
692 }
693}
694
695pub fn ansi<M>(output: impl Into<String>) -> Ansi<M> {
697 new::<M, Ansi<M>>("ansi").text(output)
698}
699
700common_kind!(
701 Rows
703);
704
705impl<M> Rows<M> {
706 props! {
707 lines(Vec<String>) = "lines";
709 cols(u32) = "cols";
711 }
712}
713
714pub fn rows<M>(lines: impl Into<Vec<String>>) -> Rows<M> {
716 new::<M, Rows<M>>("rows").lines(lines)
717}
718
719common_kind!(
720 Math
722);
723
724impl<M> Math<M> {
725 props! {
726 text(String) = "text";
728 display(bool) = "display";
730 }
731}
732
733pub fn math<M>(tex: impl Into<String>) -> Math<M> {
735 new::<M, Math<M>>("math").text(tex)
736}
737
738common_kind!(
743 Kv
745);
746
747impl<M> Kv<M> {
748 props! {
749 items(Vec<KvItem>) = "items";
751 layout(KvLayout) = "layout";
753 }
754
755 pub fn item(mut self, k: impl Into<Text>, v: impl Into<Text>) -> Self {
757 push(&mut self.0, "items", &KvItem { k: k.into(), v: v.into() });
758 self
759 }
760}
761
762pub fn kv<M>() -> Kv<M> {
764 new("kv")
765}
766
767common_kind!(
768 Table
770);
771
772impl<M> Table<M> {
773 props! {
774 cols(Vec<Column>) = "cols";
776 rows(Vec<TableRow>) = "rows";
778 selectable(bool) = "selectable";
781 selected(String) = "selected";
783 empty(Text) = "empty";
785 sort(Sort) = "sort";
788 multi(bool) = "multi";
791 picked(Vec<String>) = "picked";
793 variant(TableVariant) = "variant";
795 bar(Vec<ActButton>) = "bar";
797 hint(Text) = "hint";
799 }
800
801 pub fn col(mut self, col: Column) -> Self {
803 push(&mut self.0, "cols", &col);
804 self
805 }
806
807 pub fn row(mut self, row: TableRow) -> Self {
809 push(&mut self.0, "rows", &row);
810 self
811 }
812
813 pub fn bar_button(mut self, button: ActButton) -> Self {
815 push(&mut self.0, "bar", &button);
816 self
817 }
818}
819
820pub fn table<M>() -> Table<M> {
822 new("table")
823}
824
825common_kind!(
826 Tree
828);
829
830impl<M> Tree<M> {
831 props! {
832 nodes(Vec<TreeItem>) = "nodes";
834 }
835
836 pub fn item(mut self, item: TreeItem) -> Self {
838 push(&mut self.0, "nodes", &item);
839 self
840 }
841}
842
843pub fn tree<M>() -> Tree<M> {
845 new("tree")
846}
847
848common_kind!(
849 Json
851);
852
853impl<M> Json<M> {
854 pub fn open(mut self, levels: u32) -> Self {
856 self.0.set("open", &levels);
857 self
858 }
859}
860
861pub fn json<M>(value: impl Into<serde_json::Value>) -> Json<M> {
864 let mut node: Json<M> = new("json");
865 node.0.set("value", &value.into());
866 node
867}
868
869common_kind!(
870 Badge
872);
873
874impl<M> Badge<M> {
875 props! {
876 text(String) = "text";
878 }
879}
880
881pub fn badge<M>(text: impl Into<String>) -> Badge<M> {
883 new::<M, Badge<M>>("badge").text(text)
884}
885
886common_kind!(
887 Kbd
889);
890
891impl<M> Kbd<M> {
892 props! {
893 keys(Vec<String>) = "keys";
895 }
896}
897
898pub fn kbd<M, K: Into<String>>(keys: impl IntoIterator<Item = K>) -> Kbd<M> {
900 new::<M, Kbd<M>>("kbd").keys(keys.into_iter().map(Into::into).collect::<Vec<_>>())
901}
902
903common_kind!(
904 IconNode
906);
907
908impl<M> IconNode<M> {
909 props! {
910 name(Icon) = "name";
912 }
913}
914
915pub fn icon<M>(name: impl Into<Icon>) -> IconNode<M> {
917 new::<M, IconNode<M>>("icon").name(name)
918}
919
920common_kind!(
921 Image
923);
924
925impl<M> Image<M> {
926 props! {
927 blob(String) = "blob";
929 builtin(String) = "builtin";
931 alt(String) = "alt";
933 w(f64) = "w";
935 h(f64) = "h";
937 path(String) = "path";
939 }
940}
941
942pub fn image<M>() -> Image<M> {
944 new("image")
945}
946
947common_kind!(
948 Media
950);
951
952impl<M> Media<M> {
953 props! {
954 rev(String) = "rev";
956 link(String) = "link";
958 }
959}
960
961pub fn media<M>(path: impl Into<String>) -> Media<M> {
964 let mut node = new::<M, Media<M>>("media");
965 node.0.set("path", &path.into());
966 node
967}
968
969kind_type!(
974 List
976);
977
978impl<M> List<M> {
979 base_methods!();
980
981 min_method!();
982
983 title_method!();
984
985 actions_methods!();
986
987 event_methods!();
988
989 children_methods!();
990
991 props! {
992 selected(String) = "selected";
994 filter(String) = "filter";
996 empty(Text) = "empty";
998 virtualize(bool) = "virtual";
1000 }
1001
1002 pub fn max_lines(mut self, lines: u32) -> Self {
1004 self.0.set("max", &serde_json::json!({ "lines": lines }));
1005 self
1006 }
1007
1008 pub fn max_fraction(mut self, fraction: f64) -> Self {
1010 self.0.set("max", &fraction);
1011 self
1012 }
1013}
1014
1015pub fn list<M>() -> List<M> {
1017 new("list")
1018}
1019
1020common_kind!(
1021 Item
1023);
1024
1025impl<M> Item<M> {
1026 props! {
1027 label(Text) = "label";
1029 detail(Text) = "detail";
1031 value(Text) = "value";
1033 icon(Icon) = "icon";
1035 hint(Vec<String>) = "hint";
1037 disabled(bool) = "disabled";
1039 }
1040}
1041
1042pub fn item<M>(label: impl Into<Text>) -> Item<M> {
1044 new::<M, Item<M>>("item").label(label)
1045}
1046
1047common_kind!(
1048 Tabs
1050);
1051
1052impl<M> Tabs<M> {
1053 props! {
1054 items(Vec<Tab>) = "items";
1056 active(String) = "active";
1058 variant(TabsVariant) = "variant";
1060 }
1061
1062 pub fn tab(mut self, id: impl Into<String>, label: impl Into<Text>) -> Self {
1064 push(&mut self.0, "items", &Tab { id: id.into(), label: label.into() });
1065 self
1066 }
1067}
1068
1069pub fn tabs<M>() -> Tabs<M> {
1071 new("tabs")
1072}
1073
1074kind_type!(
1075 Picker
1077);
1078
1079impl<M> Picker<M> {
1080 base_methods!();
1081
1082 min_method!();
1083
1084 max_method!();
1085
1086 event_methods!();
1087
1088 children_methods!();
1089
1090 props! {
1091 size(PickerSize) = "size";
1093 layout(PickerLayout) = "layout";
1095 preview(PickerPreview) = "preview";
1097 title(Text) = "title";
1099 subtitle(Text) = "subtitle";
1101 icon(Icon) = "icon";
1103 query(String) = "query";
1105 cursor(usize) = "cursor";
1107 placeholder(String) = "placeholder";
1109 noun(String) = "noun";
1111 focus(PickerFocus) = "focus";
1113 state(PickerState) = "state";
1115 message(Text) = "message";
1117 empty(Text) = "empty";
1119 total(u64) = "total";
1121 items(Vec<PickerItem>) = "items";
1123 items_add(Vec<PickerItem>) = "itemsAdd";
1125 items_del(Vec<String>) = "itemsDel";
1127 order(Vec<OrderEntry>) = "order";
1129 selected(String) = "selected";
1131 current(Vec<String>) = "current";
1133 hits(std::collections::BTreeMap<String, Vec<[usize; 2]>>) = "hits";
1135 columns(Vec<PickerColumn>) = "columns";
1137 confirm(Confirm) = "confirm";
1139 scopes(Vec<Scope>) = "scopes";
1141 scope(String) = "scope";
1143 tabs(Vec<PickerTab>) = "tabs";
1145 tab(String) = "tab";
1147 strip(Strip) = "strip";
1149 actions(Vec<PickerAction>) = "actions";
1151 }
1152}
1153
1154pub fn picker<M>() -> Picker<M> {
1156 new("picker")
1157}
1158
1159common_kind!(
1164 Spinner
1166);
1167
1168impl<M> Spinner<M> {
1169 props! {
1170 style(SpinnerStyle) = "style";
1172 label(Text) = "label";
1174 }
1175}
1176
1177pub fn spinner<M>() -> Spinner<M> {
1179 new("spinner")
1180}
1181
1182common_kind!(
1183 Shimmer
1185);
1186
1187impl<M> Shimmer<M> {
1188 props! {
1189 mode(ShimmerMode) = "mode";
1191 palette(ShimmerPalette) = "palette";
1193 }
1194
1195 pub fn text(mut self, text: impl Into<Text>) -> Self {
1197 self.0.set_text(text.into());
1198 self
1199 }
1200}
1201
1202pub fn shimmer<M>(text: impl Into<Text>) -> Shimmer<M> {
1204 new::<M, Shimmer<M>>("shimmer").text(text)
1205}
1206
1207common_kind!(
1208 Elapsed
1210);
1211
1212impl<M> Elapsed<M> {
1213 props! {
1214 age(i64) = "age";
1216 stopped(u64) = "stopped";
1218 format(ElapsedFormat) = "format";
1220 }
1221
1222 pub fn since(self, start: Instant) -> Self {
1224 let age = i64::try_from(start.elapsed().as_millis()).unwrap_or(i64::MAX);
1225 self.age(age)
1226 }
1227}
1228
1229pub fn elapsed<M>() -> Elapsed<M> {
1231 new("elapsed")
1232}
1233
1234common_kind!(
1235 Rate
1237);
1238
1239impl<M> Rate<M> {
1240 props! {
1241 value(f64) = "value";
1243 unit(String) = "unit";
1245 }
1246}
1247
1248pub fn rate<M>(value: f64) -> Rate<M> {
1250 new::<M, Rate<M>>("rate").value(value)
1251}
1252
1253common_kind!(
1254 Progress
1256);
1257
1258impl<M> Progress<M> {
1259 props! {
1260 value(f64) = "value";
1262 label(Text) = "label";
1264 }
1265}
1266
1267pub fn progress<M>() -> Progress<M> {
1269 new("progress")
1270}
1271
1272common_kind!(
1273 Meter
1275);
1276
1277impl<M> Meter<M> {
1278 props! {
1279 value(f64) = "value";
1281 parts(Vec<Part>) = "parts";
1283 thresholds(Thresholds) = "thresholds";
1285 style(MeterStyle) = "style";
1287 size(Size) = "size";
1289 steps(u32) = "steps";
1291 marks(Vec<MeterMark>) = "marks";
1293 label(Text) = "label";
1295 total(Text) = "total";
1297 }
1298}
1299
1300pub fn meter<M>() -> Meter<M> {
1302 new("meter")
1303}
1304
1305kind_type!(
1306 Chart
1312);
1313
1314impl<M> Chart<M> {
1315 base_methods!();
1316
1317 title_method!();
1318
1319 actions_methods!();
1320
1321 event_methods!();
1322
1323 props! {
1324 kind(ChartKind) = "kind";
1326 size(Size) = "size";
1328 token(String) = "token";
1330 summary(Text) = "summary";
1332 series(Vec<Point>) = "series";
1335 multi_series(Vec<Series>) = "series";
1337 labels(Vec<String>) = "labels";
1339 unit(String) = "unit";
1343 last(TextFlag) = "last";
1346 total(Text) = "total";
1348 caption(Text) = "caption";
1350 min(f64) = "min";
1353 max(f64) = "max";
1356 marks(Vec<ChartMark>) = "marks";
1359 cells(Vec<Vec<Option<f64>>>) = "cells";
1361 tips(Vec<Vec<Option<String>>>) = "tips";
1363 rows(Vec<String>) = "rows";
1365 cols(Vec<ChartCol>) = "cols";
1367 }
1368}
1369
1370pub fn chart<M>(kind: impl Into<ChartKind>) -> Chart<M> {
1372 new::<M, Chart<M>>("chart").kind(kind)
1373}
1374
1375common_kind!(
1376 Effort
1378);
1379
1380impl<M> Effort<M> {
1381 props! {
1382 level(EffortLevel) = "level";
1384 }
1385}
1386
1387pub fn effort<M>(level: impl Into<EffortLevel>) -> Effort<M> {
1389 new::<M, Effort<M>>("effort").level(level)
1390}
1391
1392common_kind!(
1397 Editor
1399);
1400
1401impl<M> Editor<M> {
1402 props! {
1403 text(String) = "text";
1405 cursor(usize) = "cursor";
1407 anchor(usize) = "anchor";
1409 decor(Vec<Decor>) = "decor";
1411 ghost(String) = "ghost";
1413 placeholder(Text) = "placeholder";
1415 prompt(Text) = "prompt";
1417 mode(String) = "mode";
1419 lang(String) = "lang";
1421 readonly(bool) = "readonly";
1423 sendable(bool) = "sendable";
1425 typeahead(bool) = "typeahead";
1427 max_lines(u32) = "maxLines";
1429 variant(InputVariant) = "variant";
1431 buttons(Vec<ActButton>) = "buttons";
1433 }
1434}
1435
1436pub fn editor<M>() -> Editor<M> {
1438 new("editor")
1439}
1440
1441common_kind!(
1442 Input
1444);
1445
1446impl<M> Input<M> {
1447 props! {
1448 text(String) = "text";
1450 cursor(usize) = "cursor";
1452 anchor(usize) = "anchor";
1454 decor(Vec<Decor>) = "decor";
1456 ghost(String) = "ghost";
1458 placeholder(Text) = "placeholder";
1460 prompt(Text) = "prompt";
1462 mode(String) = "mode";
1464 lang(String) = "lang";
1466 readonly(bool) = "readonly";
1468 sendable(bool) = "sendable";
1470 typeahead(bool) = "typeahead";
1472 variant(InputVariant) = "variant";
1474 icon(Icon) = "icon";
1476 mono(bool) = "mono";
1478 clear(bool) = "clear";
1480 prefix(String) = "prefix";
1482 suffix(String) = "suffix";
1484 unit(String) = "unit";
1486 stepper(bool) = "stepper";
1488 secret(bool) = "secret";
1490 reveal(bool) = "reveal";
1492 buttons(Vec<ActButton>) = "buttons";
1494 }
1495
1496 pub fn keys<K: Into<String>>(mut self, keys: impl IntoIterator<Item = K>) -> Self {
1499 self
1500 .0
1501 .set("keys", &keys.into_iter().map(Into::into).collect::<Vec<String>>());
1502 self
1503 }
1504}
1505
1506pub fn input<M>() -> Input<M> {
1508 new("input")
1509}
1510
1511container_kind!(
1516 StatusBar
1518);
1519
1520impl<M> StatusBar<M> {
1521 props! {
1522 transparent(bool) = "transparent";
1524 }
1525}
1526
1527pub fn status<M>() -> StatusBar<M> {
1529 new("status")
1530}
1531
1532container_kind!(
1533 Seg
1535);
1536
1537impl<M> Seg<M> {
1538 props! {
1539 icon(Icon) = "icon";
1541 side(Side) = "side";
1543 priority(f64) = "priority";
1545 }
1546
1547 pub fn text(mut self, text: impl Into<Text>) -> Self {
1549 self.0.set_text(text.into());
1550 self
1551 }
1552}
1553
1554pub fn seg<M>(text: impl Into<Text>) -> Seg<M> {
1556 new::<M, Seg<M>>("seg").text(text)
1557}
1558
1559common_kind!(
1560 Toast
1562);
1563
1564impl<M> Toast<M> {
1565 props! {
1566 text(String) = "text";
1568 #[allow(clippy::should_implement_trait, reason = "the prop is named `sub`")]
1570 sub(String) = "sub";
1571 ttl(u64) = "ttl";
1573 }
1574}
1575
1576pub fn toast<M>(text: impl Into<String>) -> Toast<M> {
1578 new::<M, Toast<M>>("toast").text(text)
1579}
1580
1581container_kind!(
1582 Overlay
1584);
1585
1586impl<M> Overlay<M> {
1587 props! {
1588 anchor(Anchor) = "anchor";
1590 size(OverlaySize) = "size";
1593 modal(bool) = "modal";
1595 head(Text) = "head";
1597 side(OverlaySide) = "side";
1600 variant(OverlayVariant) = "variant";
1602 }
1603
1604 panel_head_methods!();
1605
1606 pub fn size_fraction(mut self, fraction: f64) -> Self {
1609 self.0.set("size", &fraction);
1610 self
1611 }
1612
1613 pub fn foot(mut self, foot: impl Into<Slot<M>>) -> Self {
1616 set_slot(&mut self.0, "foot", foot.into());
1617 self
1618 }
1619}
1620
1621pub fn overlay<M>() -> Overlay<M> {
1623 new("overlay")
1624}
1625
1626common_kind!(
1627 Button
1630);
1631
1632impl<M> Button<M> {
1633 props! {
1634 text(String) = "text";
1636 icon(Icon) = "icon";
1638 variant(ButtonVariant) = "variant";
1640 size(ButtonSize) = "size";
1642 on(bool) = "on";
1644 disabled(Disabled) = "disabled";
1646 busy(bool) = "busy";
1648 }
1649
1650 pub fn keys<K: Into<String>>(mut self, keys: impl IntoIterator<Item = K>) -> Self {
1652 self
1653 .0
1654 .set("keys", &keys.into_iter().map(Into::into).collect::<Vec<String>>());
1655 self
1656 }
1657}
1658
1659pub fn button<M>(text: impl Into<String>) -> Button<M> {
1661 new::<M, Button<M>>("button").text(text)
1662}
1663
1664common_kind!(
1665 Stats
1667);
1668
1669impl<M> Stats<M> {
1670 props! {
1671 items(Vec<StatItem>) = "items";
1673 }
1674
1675 pub fn item(mut self, item: StatItem) -> Self {
1677 push(&mut self.0, "items", &item);
1678 self
1679 }
1680}
1681
1682pub fn stats<M>(items: impl IntoIterator<Item = StatItem>) -> Stats<M> {
1684 new::<M, Stats<M>>("stats").items(items.into_iter().collect::<Vec<_>>())
1685}
1686
1687common_kind!(
1692 Select
1698);
1699
1700impl<M> Select<M> {
1701 props! {
1702 value(String) = "value";
1704 options(Vec<SelectOption>) = "options";
1706 variant(SelectVariant) = "variant";
1708 label(String) = "label";
1710 placeholder(String) = "placeholder";
1713 size(Size) = "size";
1715 disabled(Disabled) = "disabled";
1717 open(bool) = "open";
1719 active(String) = "active";
1721 search(bool) = "search";
1723 query(String) = "query";
1725 cursor(usize) = "cursor";
1727 total(u64) = "total";
1729 empty(String) = "empty";
1731 }
1732
1733 pub fn option(mut self, option: impl Into<SelectOption>) -> Self {
1735 push(&mut self.0, "options", &option.into());
1736 self
1737 }
1738}
1739
1740pub fn select<M>() -> Select<M> {
1742 new("select")
1743}
1744
1745common_kind!(
1746 Switch
1749);
1750
1751impl<M> Switch<M> {
1752 props! {
1753 on(bool) = "on";
1755 variant(SwitchVariant) = "variant";
1757 label(String) = "label";
1759 hint(String) = "hint";
1761 disabled(Disabled) = "disabled";
1763 }
1764
1765 pub fn labels(mut self, off: impl Into<String>, on: impl Into<String>) -> Self {
1767 let labels: [String; 2] = [off.into(), on.into()];
1768 self.0.set("labels", &labels);
1769 self
1770 }
1771}
1772
1773pub fn switch<M>(label: impl Into<String>) -> Switch<M> {
1775 new::<M, Switch<M>>("switch").label(label)
1776}
1777
1778kind_type!(
1779 Slider
1785);
1786
1787impl<M> Slider<M> {
1788 base_methods!();
1789
1790 title_method!();
1791
1792 actions_methods!();
1793
1794 event_methods!();
1795
1796 props! {
1797 value(SliderValue) = "value";
1799 min(f64) = "min";
1801 max(f64) = "max";
1803 step(f64) = "step";
1805 label(String) = "label";
1807 unit(String) = "unit";
1809 format(SliderFormat) = "format";
1811 ticks(Ticks) = "ticks";
1813 variant(SliderVariant) = "variant";
1815 disabled(Disabled) = "disabled";
1817 }
1818}
1819
1820pub fn slider<M>() -> Slider<M> {
1822 new("slider")
1823}
1824
1825container_kind!(
1826 Field
1829);
1830
1831impl<M> Field<M> {
1832 props! {
1833 label(Text) = "label";
1835 hint(Text) = "hint";
1837 error(TextFlag) = "error";
1840 warning(TextFlag) = "warning";
1842 required(bool) = "required";
1844 label_width(Extent) = "labelWidth";
1846 variant(FieldVariant) = "variant";
1848 }
1849}
1850
1851pub fn field<M>(label: impl Into<Text>) -> Field<M> {
1853 new::<M, Field<M>>("field").label(label)
1854}
1855
1856common_kind!(
1857 Otp
1862);
1863
1864impl<M> Otp<M> {
1865 props! {
1866 text(String) = "text";
1868 length(u32) = "length";
1870 group(u32) = "group";
1872 variant(OtpVariant) = "variant";
1874 mask(OtpMask) = "mask";
1876 state(OtpState) = "state";
1878 hint(String) = "hint";
1880 disabled(Disabled) = "disabled";
1882 }
1883
1884 pub fn cursor(mut self, cursor: Option<u32>) -> Self {
1887 match cursor {
1888 Some(cell) => self.0.set("cursor", &cell),
1889 None => self.0.set("cursor", &false),
1890 }
1891 self
1892 }
1893}
1894
1895pub fn otp<M>(length: u32) -> Otp<M> {
1897 new::<M, Otp<M>>("otp").length(length)
1898}
1899
1900container_kind!(
1901 Buttons
1904);
1905
1906impl<M> Buttons<M> {
1907 props! {
1908 variant(ButtonsVariant) = "variant";
1910 size(ButtonSize) = "size";
1912 label(String) = "label";
1914 }
1915}
1916
1917pub fn buttons<M>() -> Buttons<M> {
1919 new("buttons")
1920}
1921
1922kind_type!(
1923 Calendar
1929);
1930
1931impl<M> Calendar<M> {
1932 base_methods!();
1933
1934 title_method!();
1935
1936 actions_methods!();
1937
1938 event_methods!();
1939
1940 props! {
1941 variant(CalendarVariant) = "variant";
1943 value(DateValue) = "value";
1945 range(bool) = "range";
1947 month(String) = "month";
1949 today(String) = "today";
1951 min(String) = "min";
1953 max(String) = "max";
1955 week_start(WeekStart) = "weekStart";
1957 marks(Vec<DayMark>) = "marks";
1959 cursor(String) = "cursor";
1961 presets(Vec<Preset>) = "presets";
1963 preset(String) = "preset";
1965 note(String) = "note";
1967 }
1968}
1969
1970pub fn calendar<M>() -> Calendar<M> {
1972 new("calendar")
1973}
1974
1975kind_type!(
1976 Date
1981);
1982
1983impl<M> Date<M> {
1984 base_methods!();
1985
1986 title_method!();
1987
1988 actions_methods!();
1989
1990 event_methods!();
1991
1992 props! {
1993 value(DateValue) = "value";
1995 range(bool) = "range";
1997 open(bool) = "open";
1999 placeholder(String) = "placeholder";
2001 month(String) = "month";
2003 today(String) = "today";
2005 min(String) = "min";
2007 max(String) = "max";
2009 week_start(WeekStart) = "weekStart";
2011 marks(Vec<DayMark>) = "marks";
2013 cursor(String) = "cursor";
2015 note(String) = "note";
2017 }
2018}
2019
2020pub fn date<M>() -> Date<M> {
2022 new("date")
2023}
2024
2025common_kind!(
2030 Menu
2036);
2037
2038impl<M> Menu<M> {
2039 props! {
2040 items(Vec<MenuEntry>) = "items";
2042 text(String) = "text";
2044 icon(Icon) = "icon";
2046 trigger(ButtonVariant) = "trigger";
2048 caret(bool) = "caret";
2050 size(ButtonSize) = "size";
2052 variant(MenuVariant) = "variant";
2054 open(bool) = "open";
2056 active(String) = "active";
2058 align(MenuAlign) = "align";
2060 disabled(Disabled) = "disabled";
2063 }
2064
2065 pub fn item(mut self, entry: impl Into<MenuEntry>) -> Self {
2067 push(&mut self.0, "items", &entry.into());
2068 self
2069 }
2070}
2071
2072pub fn menu<M>() -> Menu<M> {
2074 new("menu")
2075}
2076
2077common_kind!(
2078 Menubar
2083);
2084
2085impl<M> Menubar<M> {
2086 props! {
2087 menus(Vec<MenubarMenu>) = "menus";
2089 variant(MenubarVariant) = "variant";
2091 icon(Icon) = "icon";
2093 open(String) = "open";
2095 active(String) = "active";
2097 }
2098
2099 pub fn menu(mut self, menu: MenubarMenu) -> Self {
2101 push(&mut self.0, "menus", &menu);
2102 self
2103 }
2104}
2105
2106pub fn menubar<M>() -> Menubar<M> {
2108 new("menubar")
2109}
2110
2111common_kind!(
2112 Nav
2115);
2116
2117impl<M> Nav<M> {
2118 props! {
2119 items(Vec<NavItem>) = "items";
2121 active(String) = "active";
2123 open(String) = "open";
2125 variant(NavVariant) = "variant";
2127 }
2128
2129 pub fn item(mut self, item: NavItem) -> Self {
2131 push(&mut self.0, "items", &item);
2132 self
2133 }
2134}
2135
2136pub fn nav<M>() -> Nav<M> {
2138 new("nav")
2139}
2140
2141kind_type!(
2142 Breadcrumb
2147);
2148
2149impl<M> Breadcrumb<M> {
2150 base_methods!();
2151
2152 min_method!();
2153
2154 title_method!();
2155
2156 actions_methods!();
2157
2158 event_methods!();
2159
2160 props! {
2161 items(Vec<Crumb>) = "items";
2163 variant(BreadcrumbVariant) = "variant";
2165 max(u32) = "max";
2167 open(bool) = "open";
2169 active(String) = "active";
2171 }
2172
2173 pub fn crumb(mut self, crumb: Crumb) -> Self {
2175 push(&mut self.0, "items", &crumb);
2176 self
2177 }
2178}
2179
2180pub fn breadcrumb<M>() -> Breadcrumb<M> {
2182 new("breadcrumb")
2183}
2184
2185common_kind!(
2186 Pager
2189);
2190
2191impl<M> Pager<M> {
2192 props! {
2193 variant(PagerVariant) = "variant";
2195 page(u64) = "page";
2197 pages(u64) = "pages";
2199 total(u64) = "total";
2201 size(u64) = "size";
2203 loading(bool) = "loading";
2205 label(String) = "label";
2207 }
2208}
2209
2210pub fn pager<M>() -> Pager<M> {
2212 new("pager")
2213}
2214
2215kind_type!(
2220 Split
2226);
2227
2228impl<M> Split<M> {
2229 base_methods!();
2230
2231 title_method!();
2232
2233 max_method!();
2234
2235 actions_methods!();
2236
2237 event_methods!();
2238
2239 children_methods!();
2240
2241 props! {
2242 variant(SplitVariant) = "variant";
2244 dir(SplitDir) = "dir";
2246 sizes(Vec<f64>) = "sizes";
2248 min(PaneMin) = "min";
2250 h(Extent) = "h";
2252 active(u32) = "active";
2254 }
2255}
2256
2257pub fn split<M>() -> Split<M> {
2259 new("split")
2260}
2261
2262container_kind!(
2263 Scroll
2266);
2267
2268impl<M> Scroll<M> {
2269 props! {
2270 variant(ScrollVariant) = "variant";
2272 stick(Stick) = "stick";
2274 pinned(bool) = "pinned";
2276 bar(ScrollBar) = "bar";
2278 gap(Gap) = "gap";
2280 noun(Noun) = "noun";
2282 }
2283}
2284
2285pub fn scroll<M>() -> Scroll<M> {
2287 new("scroll")
2288}
2289
2290container_kind!(
2291 Carousel
2294);
2295
2296impl<M> Carousel<M> {
2297 props! {
2298 index(u32) = "index";
2300 looping(bool) = "loop";
2302 variant(CarouselVariant) = "variant";
2304 h(Height) = "h";
2306 }
2307}
2308
2309pub fn carousel<M>() -> Carousel<M> {
2311 new("carousel")
2312}
2313
2314common_kind!(
2319 Avatar
2321);
2322
2323impl<M> Avatar<M> {
2324 props! {
2325 variant(AvatarVariant) = "variant";
2327 text(String) = "text";
2329 seed(String) = "seed";
2331 icon(Icon) = "icon";
2333 blob(String) = "blob";
2335 size(AvatarSize) = "size";
2337 status(Tone) = "status";
2339 }
2340}
2341
2342pub fn avatar<M>(text: impl Into<String>) -> Avatar<M> {
2344 new::<M, Avatar<M>>("avatar").text(text)
2345}
2346
2347kind_type!(
2348 Avatars
2351);
2352
2353impl<M> Avatars<M> {
2354 base_methods!();
2355
2356 min_method!();
2357
2358 title_method!();
2359
2360 actions_methods!();
2361
2362 event_methods!();
2363
2364 props! {
2365 items(Vec<AvatarItem>) = "items";
2367 max(u32) = "max";
2369 size(AvatarSize) = "size";
2371 label(String) = "label";
2373 }
2374}
2375
2376pub fn avatars<M>(items: impl IntoIterator<Item = AvatarItem>) -> Avatars<M> {
2378 new::<M, Avatars<M>>("avatars").items(items.into_iter().collect::<Vec<_>>())
2379}
2380
2381kind_type!(
2382 Empty
2385);
2386
2387impl<M> Empty<M> {
2388 base_methods!();
2389
2390 min_method!();
2391
2392 max_method!();
2393
2394 actions_methods!();
2395
2396 event_methods!();
2397
2398 children_methods!();
2399
2400 props! {
2401 variant(EmptyVariant) = "variant";
2403 icon(Icon) = "icon";
2405 title(Text) = "title";
2407 text(Text) = "text";
2409 frame(bool) = "frame";
2411 over(bool) = "over";
2413 }
2414}
2415
2416pub fn empty<M>(title: impl Into<Text>) -> Empty<M> {
2418 new::<M, Empty<M>>("empty").title(title)
2419}
2420
2421common_kind!(
2422 Skeleton
2424);
2425
2426impl<M> Skeleton<M> {
2427 props! {
2428 variant(SkeletonVariant) = "variant";
2430 shape(SkeletonShape) = "shape";
2432 lines(u32) = "lines";
2434 rows(u32) = "rows";
2436 tiles(u32) = "tiles";
2438 }
2439}
2440
2441pub fn skeleton<M>() -> Skeleton<M> {
2443 new("skeleton")
2444}
2445
2446kind_type!(
2451 Tool
2453);
2454
2455impl<M> Tool<M> {
2456 base_methods!();
2457
2458 min_method!();
2459
2460 max_method!();
2461
2462 actions_methods!();
2463
2464 event_methods!();
2465
2466 children_methods!();
2467
2468 props! {
2469 name(String) = "name";
2471 title(Text) = "title";
2473 target(Text) = "target";
2475 target_kind(TargetKind) = "targetKind";
2477 meta(Vec<Text>) = "meta";
2479 badges(Vec<BadgeSpec>) = "badges";
2481 note(Text) = "note";
2483 exit(i64) = "exit";
2485 status(Status) = "status";
2487 confirm(ToolConfirm) = "confirm";
2489 ask(Ask) = "ask";
2492 approved(Approved) = "approved";
2494 age(i64) = "age";
2496 took(u64) = "took";
2498 intent(String) = "intent";
2500 frame(ToolFrame) = "frame";
2502 collapsible(bool) = "collapsible";
2504 collapsed(bool) = "collapsed";
2506 preview(ToolPreview) = "preview";
2508 tools(Vec<ToolButton>) = "tools";
2510 }
2511}
2512
2513pub fn tool<M>(name: impl Into<String>) -> Tool<M> {
2515 new::<M, Tool<M>>("tool").name(name)
2516}
2517
2518container_kind!(
2519 Agent
2521);
2522
2523impl<M> Agent<M> {
2524 props! {
2525 name(String) = "name";
2527 agent(String) = "agent";
2529 badges(Vec<BadgeSpec>) = "badges";
2531 task(Text) = "task";
2533 status(AgentStatus) = "status";
2535 model(String) = "model";
2537 thinking(String) = "thinking";
2539 stats(AgentStats) = "stats";
2541 tool(AgentTool) = "tool";
2543 retry(Retry) = "retry";
2545 depth(u32) = "depth";
2547 collapsible(bool) = "collapsible";
2549 collapsed(bool) = "collapsed";
2551 }
2552}
2553
2554pub fn agent<M>(name: impl Into<String>) -> Agent<M> {
2556 new::<M, Agent<M>>("agent").name(name)
2557}
2558
2559common_kind!(
2560 Checklist
2562);
2563
2564impl<M> Checklist<M> {
2565 props! {
2566 phases(Vec<Phase>) = "phases";
2568 mode(ChecklistMode) = "mode";
2570 note(Text) = "note";
2572 }
2573}
2574
2575pub fn checklist<M>() -> Checklist<M> {
2577 new("checklist")
2578}
2579
2580kind_type!(
2581 Prefs
2583);
2584
2585impl<M> Prefs<M> {
2586 base_methods!();
2587
2588 min_method!();
2589
2590 max_method!();
2591
2592 actions_methods!();
2593
2594 event_methods!();
2595
2596 children_methods!();
2597
2598 props! {
2599 title(String) = "title";
2601 pages(Vec<PrefsPage>) = "pages";
2603 page(String) = "page";
2605 lead(String) = "lead";
2607 sections(Vec<PrefsSection>) = "sections";
2609 query(String) = "query";
2611 cursor(usize) = "cursor";
2613 focus(String) = "focus";
2615 editing(Editing) = "editing";
2617 }
2618}
2619
2620pub fn prefs<M>() -> Prefs<M> {
2622 new("prefs")
2623}
2624
2625kind_type!(
2630 Message
2637);
2638
2639impl<M> Message<M> {
2640 base_methods!();
2641
2642 min_method!();
2643
2644 title_method!();
2645
2646 max_method!();
2647
2648 event_methods!();
2649
2650 children_methods!();
2651
2652 props! {
2653 variant(MessageVariant) = "variant";
2655 side(MessageSide) = "side";
2657 author(String) = "author";
2659 time(String) = "time";
2661 continued(bool) = "continued";
2663 reactions(Vec<Reaction>) = "reactions";
2665 actions(Vec<IconAction>) = "actions";
2667 reveal(Reveal) = "reveal";
2669 status(String) = "status";
2671 pending(bool) = "pending";
2673 }
2674
2675 pub fn avatar(mut self, avatar: impl Into<Slot<M>>) -> Self {
2678 set_slot(&mut self.0, "avatar", avatar.into());
2679 self
2680 }
2681}
2682
2683pub fn message<M>() -> Message<M> {
2685 new("message")
2686}
2687
2688kind_type!(
2689 Attachment
2694);
2695
2696impl<M> Attachment<M> {
2697 base_methods!();
2698
2699 min_method!();
2700
2701 title_method!();
2702
2703 max_method!();
2704
2705 event_methods!();
2706
2707 props! {
2708 name(String) = "name";
2710 variant(AttachmentVariant) = "variant";
2712 ext(String) = "ext";
2714 mime(String) = "mime";
2716 size(u64) = "size";
2718 meta(String) = "meta";
2720 blob(String) = "blob";
2722 path(String) = "path";
2724 state(AttachmentState) = "state";
2726 progress(f64) = "progress";
2728 error(String) = "error";
2730 removable(bool) = "removable";
2732 actions(Vec<IconAction>) = "actions";
2734 }
2735}
2736
2737pub fn attachment<M>(name: impl Into<String>) -> Attachment<M> {
2739 new::<M, Attachment<M>>("attachment").name(name)
2740}
2741
2742kind_type!(
2743 Trace
2749);
2750
2751impl<M> Trace<M> {
2752 base_methods!();
2753
2754 min_method!();
2755
2756 max_method!();
2757
2758 actions_methods!();
2759
2760 event_methods!();
2761
2762 children_methods!();
2763
2764 props! {
2765 variant(TraceVariant) = "variant";
2767 lines(Vec<TraceLine>) = "lines";
2769 status(Status) = "status";
2771 title(Text) = "title";
2773 summary(Text) = "summary";
2775 icon(Icon) = "icon";
2777 age(u64) = "age";
2779 took(u64) = "took";
2781 open(bool) = "open";
2783 sources(Vec<Source>) = "sources";
2785 }
2786
2787 pub fn line(mut self, line: TraceLine) -> Self {
2789 push(&mut self.0, "lines", &line);
2790 self
2791 }
2792}
2793
2794pub fn trace<M>() -> Trace<M> {
2796 new("trace")
2797}
2798
2799common_kind!(
2800 Stacktrace
2805);
2806
2807impl<M> Stacktrace<M> {
2808 props! {
2809 error(StackError) = "error";
2811 frames(Vec<Frame>) = "frames";
2813 selected(u32) = "selected";
2815 lang(String) = "lang";
2817 variant(StacktraceVariant) = "variant";
2819 }
2820
2821 pub fn frame(mut self, frame: Frame) -> Self {
2823 push(&mut self.0, "frames", &frame);
2824 self
2825 }
2826}
2827
2828pub fn stacktrace<M>(error: StackError) -> Stacktrace<M> {
2830 new::<M, Stacktrace<M>>("stacktrace").error(error)
2831}
2832
2833common_kind!(
2834 Suggestions
2837);
2838
2839impl<M> Suggestions<M> {
2840 props! {
2841 variant(SuggestionsVariant) = "variant";
2843 items(Vec<Suggestion>) = "items";
2845 }
2846
2847 pub fn item(mut self, item: Suggestion) -> Self {
2849 push(&mut self.0, "items", &item);
2850 self
2851 }
2852}
2853
2854pub fn suggestions<M>() -> Suggestions<M> {
2856 new("suggestions")
2857}
2858
2859container_kind!(
2860 Questions
2867);
2868
2869impl<M> Questions<M> {
2870 props! {
2871 items(Vec<Question>) = "items";
2873 step(u32) = "step";
2875 answers(std::collections::BTreeMap<String, QuestionAnswer>) = "answers";
2877 variant(QuestionsVariant) = "variant";
2879 hint(String) = "hint";
2881 submit(String) = "submit";
2883 }
2884
2885 pub fn question(mut self, question: Question) -> Self {
2887 push(&mut self.0, "items", &question);
2888 self
2889 }
2890}
2891
2892pub fn questions<M>() -> Questions<M> {
2894 new("questions")
2895}
2896
2897fn push<M>(node: &mut Node<M>, key: &str, value: &impl serde::Serialize) {
2899 let Ok(value) = serde_json::to_value(value) else {
2900 return;
2901 };
2902 match node.props.get_mut(key) {
2903 Some(serde_json::Value::Array(list)) => list.push(value),
2904 _ => {
2905 node
2906 .props
2907 .insert(key.to_owned(), serde_json::Value::Array(vec![value]));
2908 },
2909 }
2910}
2911
2912#[cfg(test)]
2913mod tests {
2914 use serde_json::json;
2915
2916 use super::*;
2917
2918 fn wire<M>(node: impl Into<Node<M>>) -> serde_json::Value {
2919 let node = node.into();
2920 json!({ "k": node.kind, "p": node.props })
2921 }
2922
2923 #[test]
2924 fn text_takes_a_string_or_spans() {
2925 assert_eq!(wire::<()>(text("hi")), json!({"k": "text", "p": {"text": "hi"}}));
2926 let styled = text::<()>(spans!["a", span("b").style(Token::Muted).style("strong")]);
2927 assert_eq!(
2928 wire(styled.clone()),
2929 json!({"k": "text", "p": {"spans": [{"t": "a"}, {"t": "b", "s": "muted strong"}]}})
2930 );
2931 assert_eq!(wire(styled.text("c")), json!({"k": "text", "p": {"text": "c"}}));
2933 }
2934
2935 #[test]
2936 fn json_carries_its_value_and_open_levels() {
2937 assert_eq!(
2938 wire::<()>(super::json(json!({"a": 1})).open(1)),
2939 json!({"k": "json", "p": {"value": {"a": 1}, "open": 1}})
2940 );
2941 }
2942
2943 #[test]
2944 fn typed_values_and_escape_hatches_serialize() {
2945 let c = card::<()>()
2946 .head("Deploy")
2947 .status(Status::Running)
2948 .tone("brand-new")
2949 .min(Bound::w(Extent::Ch(10.0)))
2950 .basis(Basis::Content)
2951 .key(7)
2952 .prop("future", json!([1]))
2953 .prop("head", serde_json::Value::Null);
2954 assert_eq!(
2955 wire(c),
2956 json!({"k": "card", "p": {
2957 "status": "running", "tone": "brand-new", "min": {"w": "10ch"},
2958 "basis": "content", "key": 7, "future": [1]
2959 }})
2960 );
2961 }
2962
2963 #[test]
2964 fn code_bars_folds_and_overview_serialize() {
2965 let c = code::<()>("a\nb\nc")
2966 .marks(vec![CodeMark { line: 1, wash: vec![0.9, 0.3], ..CodeMark::default() }])
2967 .folds(vec![
2968 CodeFold {
2969 from: 2,
2970 to: 3,
2971 note: Some("cut".into()),
2972 tone: Some(Tone::Error),
2973 hovercard: Some(Node::<()>::from(text("why")).to_json()),
2974 ..CodeFold::default()
2975 },
2976 CodeFold { from: 5, to: 6, ..CodeFold::default() },
2977 ])
2978 .overview(true)
2979 .nums(vec![1, 2, 7]);
2980 assert_eq!(
2981 wire(c),
2982 json!({"k": "code", "p": {
2983 "text": "a\nb\nc", "nums": [1, 2, 7], "marks": [{"line": 1, "wash": [0.9, 0.3]}],
2984 "folds": [
2985 {"from": 2, "to": 3, "note": "cut", "tone": "error", "hovercard": {"k": "text", "p": {"text": "why"}}},
2986 {"from": 5, "to": 6}
2987 ],
2988 "overview": true
2989 }})
2990 );
2991 }
2992
2993 #[test]
2994 fn kind_specific_props_replace_common_meanings() {
2995 let title = spans![span("Heading").style("strong")];
2996 assert_eq!(
2997 wire(tool::<()>("read").title(title.clone()))["p"]["title"],
2998 json!([{"t": "Heading", "s": "strong"}])
2999 );
3000 assert_eq!(
3001 wire(picker::<()>().title(title).actions(vec![PickerAction {
3002 id: "open".into(),
3003 primary: Some(true),
3004 ..PickerAction::default()
3005 }]))["p"],
3006 json!({
3007 "title": [{"t": "Heading", "s": "strong"}],
3008 "actions": [{"id": "open", "primary": true}]
3009 })
3010 );
3011 assert_eq!(wire(prefs::<()>().title("Settings"))["p"]["title"], "Settings");
3012 assert_eq!(
3013 wire(list::<()>().max_fraction(0.5).max_lines(4))["p"]["max"],
3014 json!({"lines": 4})
3015 );
3016 assert_eq!(wire(list::<()>().max_lines(4).max_fraction(0.5))["p"]["max"], 0.5);
3017 }
3018
3019 #[test]
3020 fn agent_statuses_have_their_own_vocabulary() {
3021 for (status, expected) in [
3022 (AgentStatus::Pending, "pending"),
3023 (AgentStatus::Running, "running"),
3024 (AgentStatus::Done, "done"),
3025 (AgentStatus::Failed, "failed"),
3026 (AgentStatus::Aborted, "aborted"),
3027 (AgentStatus::Idle, "idle"),
3028 (AgentStatus::Parked, "parked"),
3029 (AgentStatus::Other("future".into()), "future"),
3030 ] {
3031 assert_eq!(wire(agent::<()>("worker").status(status))["p"]["status"], expected);
3032 }
3033 }
3034
3035 #[test]
3036 fn table_meter_cells_keep_the_wire_wrapper() {
3037 let node = wire(table::<()>().row(TableRow::new("a").cell("usage", MeterCell {
3038 value: Some(0.5),
3039 title: Some("Half".into()),
3040 tone: Some(Tone::Success),
3041 ..MeterCell::default()
3042 })));
3043 assert_eq!(
3044 node["p"]["rows"][0]["cells"]["usage"],
3045 json!({
3046 "meter": {"value": 0.5, "title": "Half", "tone": "success"}
3047 })
3048 );
3049 assert_eq!(
3050 serde_json::to_value(Cell::Meter(MeterCell::default())).unwrap(),
3051 json!({"meter": {}})
3052 );
3053 }
3054
3055 #[test]
3056 fn form_controls_serialize_their_props() {
3057 assert_eq!(
3058 wire(
3059 select::<()>()
3060 .label("Model")
3061 .value("haiku")
3062 .option("opus")
3063 .option(
3064 SelectOption::new("haiku", "Haiku")
3065 .detail("200k")
3066 .group("Fast")
3067 )
3068 .search(true)
3069 )["p"],
3070 json!({
3071 "label": "Model", "value": "haiku", "search": true, "options": [
3072 {"value": "opus"},
3073 {"value": "haiku", "label": "Haiku", "detail": "200k", "group": "Fast"}
3074 ]
3075 })
3076 );
3077 assert_eq!(
3078 wire(
3079 switch::<()>("Wrap")
3080 .on(true)
3081 .variant(SwitchVariant::Segment)
3082 .labels("Off", "On")
3083 ),
3084 json!({"k": "switch", "p": {
3085 "label": "Wrap", "on": true, "variant": "segment", "labels": ["Off", "On"]
3086 }})
3087 );
3088 assert_eq!(
3089 wire(
3090 slider::<()>()
3091 .value((200.0, 1200.0))
3092 .min(0.0)
3093 .max(2000.0)
3094 .ticks(4u32)
3095 .unit("ms")
3096 )["p"],
3097 json!({"value": [200.0, 1200.0], "min": 0.0, "max": 2000.0, "ticks": 4, "unit": "ms"})
3098 );
3099 assert_eq!(
3100 wire(
3101 field::<()>("Name")
3102 .required(true)
3103 .error(true)
3104 .label_width(Extent::Ch(14.0))
3105 .child(input())
3106 )["p"],
3107 json!({"label": "Name", "required": true, "error": true, "labelWidth": "14ch"})
3108 );
3109 assert_eq!(
3110 wire(otp::<()>(6).text("12").mask("*").cursor(None))["p"],
3111 json!({"length": 6, "text": "12", "mask": "*", "cursor": false})
3112 );
3113 assert_eq!(
3114 wire(
3115 calendar::<()>()
3116 .range(true)
3117 .value(DateValue::Range("2026-10-19".into(), None))
3118 .week_start(WeekStart::Sun)
3119 .marks(vec![DayMark::from("2026-10-20"), DayMark::new("2026-10-21", Tone::Error)])
3120 )["p"],
3121 json!({
3122 "range": true, "value": ["2026-10-19", null], "weekStart": "sun",
3123 "marks": [{"date": "2026-10-20"}, {"date": "2026-10-21", "tone": "error"}]
3124 })
3125 );
3126 }
3127
3128 #[test]
3129 fn menu_entries_serialize_as_strings_headers_and_items() {
3130 let entries = vec![
3131 MenuEntry::from("open"),
3132 MenuEntry::header("Danger"),
3133 MenuEntry::Separator,
3134 MenuItem::new("trash", "Move to Trash")
3135 .icon("trash")
3136 .keys(["cmd", "backspace"])
3137 .danger(true)
3138 .into(),
3139 MenuItem::new("export", "Export")
3140 .item(MenuItem::new("csv", "CSV").checked(true))
3141 .into(),
3142 ];
3143 let expected = json!([
3144 "open", {"header": "Danger"}, "-",
3145 {"id": "trash", "label": "Move to Trash", "icon": "trash",
3146 "keys": ["cmd", "backspace"], "danger": true},
3147 {"id": "export", "label": "Export",
3148 "items": [{"id": "csv", "label": "CSV", "checked": true}]}
3149 ]);
3150 assert_eq!(wire(menu::<()>().text("More").items(entries.clone()))["p"]["items"], expected);
3151 let actions = entries
3152 .into_iter()
3153 .fold(Actions::new(), |actions, entry| actions.menu(entry));
3154 assert_eq!(wire(text::<()>("log").actions(actions))["p"]["actions"]["menu"], expected);
3155 }
3156
3157 #[test]
3158 fn kinds_with_their_own_min_max_title_and_actions_keep_them() {
3159 assert_eq!(wire(avatars::<()>([AvatarItem::new("Cara")]).max(4u32))["p"]["max"], 4);
3160 assert_eq!(wire(breadcrumb::<()>().crumb(Crumb::new("src", "src")).max(3u32))["p"]["max"], 3);
3161 assert_eq!(
3162 wire(
3163 split::<()>()
3164 .min(vec![Some(Extent::Ch(20.0)), None])
3165 .sizes(vec![2.0, 1.0])
3166 )["p"],
3167 json!({"min": ["20ch", null], "sizes": [2.0, 1.0]})
3168 );
3169 assert_eq!(
3170 wire(empty::<()>(spans![span("No runs").style("strong")]))["p"]["title"],
3171 json!([{"t": "No runs", "s": "strong"}])
3172 );
3173 assert_eq!(
3174 wire(attachment::<()>("a.png").actions(vec![IconAction::new("open").icon("eye")]))["p"],
3175 json!({"name": "a.png", "actions": [{"act": "open", "icon": "eye"}]})
3176 );
3177 assert_eq!(
3178 wire(carousel::<()>().looping(true).h(Height::Lines(10.0)))["p"],
3179 json!({"loop": true, "h": "10lines"})
3180 );
3181 }
3182
3183 #[test]
3184 fn work_kinds_serialize_lines_frames_and_questions() {
3185 let t = trace::<()>().status(Status::Running).line(
3186 TraceLine::new("s1", "Searched")
3187 .target("tern tsp")
3188 .status(Status::Done)
3189 .took(1200)
3190 .source(Source::new("https://x.dev").site("x")),
3191 );
3192 assert_eq!(
3193 wire(t)["p"],
3194 json!({"status": "running", "lines": [{
3195 "id": "s1", "title": "Searched", "target": "tern tsp", "status": "done",
3196 "took": 1200, "sources": [{"href": "https://x.dev", "site": "x"}]
3197 }]})
3198 );
3199 let s = stacktrace::<()>(StackError::new("TypeError", "x is undefined"))
3200 .frame(Frame::new("main", "src/a.ts").at(3, 7).app(false));
3201 assert_eq!(
3202 wire(s)["p"],
3203 json!({
3204 "error": {"type": "TypeError", "message": "x is undefined"},
3205 "frames": [{"fn": "main", "file": "src/a.ts", "line": 3, "col": 7, "app": false}]
3206 })
3207 );
3208 let q = questions::<()>()
3209 .question(
3210 Question::new("db", "Which database?")
3211 .choice("postgres")
3212 .choice(QuestionChoice::new("sqlite", "SQLite"))
3213 .other("Something else"),
3214 )
3215 .answers(std::collections::BTreeMap::from([(
3216 "db".to_owned(),
3217 QuestionAnswer::from("postgres"),
3218 )]));
3219 assert_eq!(
3220 wire(q)["p"],
3221 json!({
3222 "items": [{"id": "db", "prompt": "Which database?",
3223 "choices": [{"value": "postgres"}, {"value": "sqlite", "label": "SQLite"}],
3224 "other": {"placeholder": "Something else"}}],
3225 "answers": {"db": "postgres"}
3226 })
3227 );
3228 let m = message::<()>()
3229 .author("Cara")
3230 .side(MessageSide::End)
3231 .reactions(vec![Reaction::new("👍", 2).on(true)])
3232 .avatar(avatar("Cara"))
3233 .child(md("hi"));
3234 let node: Node<()> = m.into();
3235 assert_eq!(node.props()["reactions"], json!([{"text": "👍", "count": 2, "on": true}]));
3236 assert_eq!(node.child_nodes()[0].props()["key"], "avatar");
3237 assert_eq!(node.child_nodes()[1].kind(), "md");
3238 }
3239
3240 #[test]
3241 fn extended_kinds_take_their_new_props() {
3242 let t = table::<()>()
3243 .col(Column::new("name", "Name").sortable(true))
3244 .sort(Sort::desc("name"))
3245 .multi(true)
3246 .picked(vec!["a".to_owned()])
3247 .bar_button(ActButton::new("restart", "Restart").icon("refresh"));
3248 assert_eq!(
3249 wire(t)["p"],
3250 json!({
3251 "cols": [{"id": "name", "head": "Name", "sortable": true}],
3252 "sort": {"col": "name", "dir": "desc"}, "multi": true, "picked": ["a"],
3253 "bar": [{"act": "restart", "text": "Restart", "icon": "refresh"}]
3254 })
3255 );
3256 assert_eq!(
3257 wire(
3258 chart::<()>(ChartKind::Line)
3259 .multi_series(vec![Series::new(vec![Some(1.0), None]).label("p50")])
3260 .unit("ms")
3261 .last(false)
3262 )["p"],
3263 json!({
3264 "kind": "line", "series": [{"label": "p50", "values": [1.0, null]}],
3265 "unit": "ms", "last": false
3266 })
3267 );
3268 assert_eq!(
3269 wire(
3270 input::<()>()
3271 .icon("search")
3272 .keys(["cmd", "f"])
3273 .clear(true)
3274 .secret(true)
3275 )["p"],
3276 json!({"icon": "search", "keys": ["cmd", "f"], "clear": true, "secret": true})
3277 );
3278 assert_eq!(
3279 wire(overlay::<()>().side(OverlaySide::Right).size_fraction(0.4))["p"],
3280 json!({"side": "right", "size": 0.4})
3281 );
3282 assert_eq!(
3283 wire(
3284 tool::<()>("bash")
3285 .status(Status::Ask)
3286 .confirm(ToolConfirm::Scopes)
3287 .ask(
3288 Ask::new("Run it?")
3289 .scope_option(AskScope::new("once", "Once"))
3290 .always(AskAlways::Hidden)
3291 )
3292 )["p"],
3293 json!({
3294 "name": "bash", "status": "ask", "confirm": "scopes",
3295 "ask": {"text": "Run it?", "always": false, "scopes": [{"id": "once", "label": "Once"}]}
3296 })
3297 );
3298 assert_eq!(
3299 wire(
3300 rule::<()>()
3301 .variant(RuleVariant::Note)
3302 .label("Resumed")
3303 .time("14:02")
3304 )["p"],
3305 json!({"variant": "note", "label": "Resumed", "time": "14:02"})
3306 );
3307 let cited = text::<()>(spans![
3308 span("Rust 2024"),
3309 span("").cite(Cite::new("https://blog.rust-lang.org").n(2))
3310 ])
3311 .cite(CiteStyle::Note);
3312 assert_eq!(
3313 wire(cited)["p"],
3314 json!({"cite": "note", "spans": [
3315 {"t": "Rust 2024"}, {"t": "", "cite": [{"href": "https://blog.rust-lang.org", "n": 2}]}
3316 ]})
3317 );
3318 }
3319
3320 #[test]
3321 fn list_props_append() {
3322 let k = kv::<()>().item("a", "1").item("b", span("2").style("num"));
3323 assert_eq!(
3324 wire(k),
3325 json!({"k": "kv", "p": {"items": [
3326 {"k": "a", "v": "1"}, {"k": "b", "v": [{"t": "2", "s": "num"}]}
3327 ]}})
3328 );
3329 }
3330}