1use std::collections::HashMap;
7
8use serde::{Deserialize, Serialize};
9use serde_json::Value;
10
11use crate::backend::obj::MetaMapType;
12
13pub(crate) fn is_false(v: &bool) -> bool {
16 !v
17}
18
19pub(crate) fn is_zero_f64(v: &f64) -> bool {
20 *v == 0.0
21}
22
23pub(crate) fn is_zero_f32(v: &f32) -> bool {
24 *v == 0.0
25}
26
27fn deserialize_string_list<'de, D>(d: D) -> Result<Option<Vec<String>>, D::Error>
31where
32 D: serde::Deserializer<'de>,
33{
34 Ok(match Value::deserialize(d)? {
35 Value::Null => None,
36 Value::Array(items) => Some(
37 items
38 .into_iter()
39 .filter_map(|item| match item {
40 Value::String(s) => Some(s),
41 _ => None,
42 })
43 .collect(),
44 ),
45 _ => {
46 tracing::warn!("settings.json: a widget list setting is not a list of names; ignoring it");
47 None
48 }
49 })
50}
51
52fn deserialize_connections<'de, D>(d: D) -> Result<HashMap<String, ConnKeywords>, D::Error>
57where
58 D: serde::Deserializer<'de>,
59{
60 let raw = Value::deserialize(d)?;
61 let Value::Object(entries) = raw else {
62 if !raw.is_null() {
63 tracing::warn!("settings.json: `connections` is not an object; ignoring it");
64 }
65 return Ok(HashMap::new());
66 };
67 Ok(entries
68 .into_iter()
69 .filter_map(|(name, entry)| match serde_json::from_value::<ConnKeywords>(entry) {
70 Ok(keywords) => Some((name, keywords)),
71 Err(e) => {
72 tracing::warn!(connection = %name, error = %e, "settings.json: ignoring this connection's settings");
73 None
74 }
75 })
76 .collect())
77}
78
79pub(crate) fn is_zero_i32(v: &i32) -> bool {
80 *v == 0
81}
82
83#[derive(Debug, Clone, Default, Serialize, Deserialize)]
89pub struct SettingsType {
90 #[serde(rename = "app:*", default, skip_serializing_if = "is_false")]
92 pub app_clear: bool,
93
94 #[serde(rename = "app:dismissarchitecturewarning", default, skip_serializing_if = "is_false")]
95 pub app_dismiss_architecture_warning: bool,
96
97 #[serde(rename = "app:defaultnewblock", default, skip_serializing_if = "String::is_empty")]
98 pub app_default_new_block: String,
99
100 #[serde(rename = "app:showoverlayblocknums", default, skip_serializing_if = "Option::is_none")]
101 pub app_show_overlay_block_nums: Option<bool>,
102
103 #[serde(rename = "term:*", default, skip_serializing_if = "is_false")]
105 pub term_clear: bool,
106
107 #[serde(rename = "term:fontsize", default, skip_serializing_if = "is_zero_f64")]
108 pub term_font_size: f64,
109
110 #[serde(rename = "term:fontfamily", default, skip_serializing_if = "String::is_empty")]
111 pub term_font_family: String,
112
113 #[serde(rename = "term:theme", default, skip_serializing_if = "String::is_empty")]
114 pub term_theme: String,
115
116 #[serde(rename = "term:disablewebgl", default, skip_serializing_if = "is_false")]
117 pub term_disable_web_gl: bool,
118
119 #[serde(rename = "term:localshellpath", default, skip_serializing_if = "String::is_empty")]
120 pub term_local_shell_path: String,
121
122 #[serde(rename = "term:localshellopts", default, skip_serializing_if = "Vec::is_empty")]
123 pub term_local_shell_opts: Vec<String>,
124
125 #[serde(rename = "term:scrollback", default, skip_serializing_if = "Option::is_none")]
126 pub term_scrollback: Option<i64>,
127
128 #[serde(rename = "term:durable", default, skip_serializing_if = "Option::is_none")]
133 pub term_durable: Option<bool>,
134
135 #[serde(rename = "term:copyonselect", default, skip_serializing_if = "Option::is_none")]
136 pub term_copy_on_select: Option<bool>,
137
138 #[serde(rename = "term:transparency", default, skip_serializing_if = "Option::is_none")]
139 pub term_transparency: Option<f64>,
140
141 #[serde(rename = "term:scrollsensitivity", default, skip_serializing_if = "Option::is_none")]
142 pub term_scroll_sensitivity: Option<f64>,
143
144 #[serde(rename = "term:allowbracketedpaste", default, skip_serializing_if = "Option::is_none")]
145 pub term_allow_bracketed_paste: Option<bool>,
146
147 #[serde(rename = "term:shiftenternewline", default, skip_serializing_if = "Option::is_none")]
148 pub term_shift_enter_newline: Option<bool>,
149
150 #[serde(rename = "browser:searchsuggestions", default, skip_serializing_if = "Option::is_none")]
155 pub browser_search_suggestions: Option<bool>,
156
157 #[serde(rename = "term:agentmaxruntimehours", default, skip_serializing_if = "is_zero_f64")]
160 pub term_agent_max_runtime_hours: f64,
161
162 #[serde(rename = "term:agentidletimeoutmins", default, skip_serializing_if = "is_zero_f64")]
165 pub term_agent_idle_timeout_mins: f64,
166
167 #[serde(rename = "cmd:env", default, skip_serializing_if = "HashMap::is_empty")]
169 pub cmd_env: HashMap<String, String>,
170
171 #[serde(rename = "blockheader:*", default, skip_serializing_if = "is_false")]
173 pub block_header_clear: bool,
174
175 #[serde(rename = "blockheader:showblockids", default, skip_serializing_if = "is_false")]
176 pub block_header_show_block_ids: bool,
177
178 #[serde(rename = "preview:showhiddenfiles", default, skip_serializing_if = "Option::is_none")]
180 pub preview_show_hidden_files: Option<bool>,
181
182 #[serde(rename = "tab:preset", default, skip_serializing_if = "String::is_empty")]
184 pub tab_preset: String,
185
186 #[serde(rename = "widget:*", default, skip_serializing_if = "is_false")]
188 pub widget_clear: bool,
189
190 #[serde(rename = "widget:icononly", default, skip_serializing_if = "Option::is_none")]
191 pub widget_icon_only: Option<bool>,
192
193 #[serde(
197 rename = "widget:pinned",
198 default,
199 skip_serializing_if = "Option::is_none",
200 deserialize_with = "deserialize_string_list"
201 )]
202 pub widget_pinned: Option<Vec<String>>,
203
204 #[serde(
208 rename = "widget:hidden",
209 default,
210 skip_serializing_if = "Option::is_none",
211 deserialize_with = "deserialize_string_list"
212 )]
213 pub widget_hidden: Option<Vec<String>>,
214
215 #[serde(rename = "statusbar:showmuxbuscloud", default, skip_serializing_if = "Option::is_none")]
220 pub statusbar_show_muxbus_cloud: Option<bool>,
221
222 #[serde(rename = "window:*", default, skip_serializing_if = "is_false")]
224 pub window_clear: bool,
225
226 #[serde(rename = "window:transparent", default, skip_serializing_if = "is_false")]
227 pub window_transparent: bool,
228
229 #[serde(rename = "window:blur", default, skip_serializing_if = "is_false")]
230 pub window_blur: bool,
231
232 #[serde(rename = "window:opacity", default, skip_serializing_if = "Option::is_none")]
233 pub window_opacity: Option<f64>,
234
235 #[serde(rename = "window:bgcolor", default, skip_serializing_if = "String::is_empty")]
236 pub window_bg_color: String,
237
238 #[serde(rename = "window:reducedmotion", default, skip_serializing_if = "is_false")]
239 pub window_reduced_motion: bool,
240
241 #[serde(rename = "window:tilegapsize", default, skip_serializing_if = "Option::is_none")]
242 pub window_tile_gap_size: Option<i64>,
243
244 #[serde(rename = "window:showmenubar", default, skip_serializing_if = "is_false")]
245 pub window_show_menu_bar: bool,
246
247 #[serde(rename = "window:nativetitlebar", default, skip_serializing_if = "is_false")]
248 pub window_native_title_bar: bool,
249
250 #[serde(rename = "window:disablehardwareacceleration", default, skip_serializing_if = "is_false")]
251 pub window_disable_hardware_acceleration: bool,
252
253 #[serde(rename = "window:maxtabcachesize", default, skip_serializing_if = "is_zero_i32")]
254 pub window_max_tab_cache_size: i32,
255
256 #[serde(rename = "window:magnifiedblockopacity", default, skip_serializing_if = "Option::is_none")]
257 pub window_magnified_block_opacity: Option<f64>,
258
259 #[serde(rename = "window:magnifiedblocksize", default, skip_serializing_if = "Option::is_none")]
260 pub window_magnified_block_size: Option<f64>,
261
262 #[serde(rename = "window:magnifiedblockblurprimarypx", default, skip_serializing_if = "Option::is_none")]
263 pub window_magnified_block_blur_primary_px: Option<i64>,
264
265 #[serde(rename = "window:magnifiedblockblursecondarypx", default, skip_serializing_if = "Option::is_none")]
266 pub window_magnified_block_blur_secondary_px: Option<i64>,
267
268 #[serde(rename = "window:confirmclose", default, skip_serializing_if = "is_false")]
269 pub window_confirm_close: bool,
270
271 #[serde(rename = "window:savelastwindow", default, skip_serializing_if = "is_false")]
272 pub window_save_last_window: bool,
273
274 #[serde(rename = "window:keepinactivetabslaidout", default, skip_serializing_if = "Option::is_none")]
280 pub window_keep_inactive_tabs_laid_out: Option<bool>,
281
282 #[serde(rename = "window:dimensions", default, skip_serializing_if = "String::is_empty")]
283 pub window_dimensions: String,
284
285 #[serde(rename = "window:zoom", default, skip_serializing_if = "Option::is_none")]
286 pub window_zoom: Option<f64>,
287
288 #[serde(rename = "telemetry:*", default, skip_serializing_if = "is_false")]
290 pub telemetry_clear: bool,
291
292 #[serde(rename = "telemetry:enabled", default, skip_serializing_if = "is_false")]
293 pub telemetry_enabled: bool,
294
295 #[serde(rename = "telemetry:interval", default, skip_serializing_if = "is_zero_f64")]
296 pub telemetry_interval: f64,
297
298 #[serde(rename = "telemetry:numpoints", default, skip_serializing_if = "Option::is_none")]
299 pub telemetry_numpoints: Option<i64>,
300
301 #[serde(rename = "conn:*", default, skip_serializing_if = "is_false")]
303 pub conn_clear: bool,
304
305 #[serde(rename = "conn:helper", default, skip_serializing_if = "String::is_empty")]
308 pub conn_helper: String,
309
310 #[serde(rename = "network:lan_discovery", default, skip_serializing_if = "is_false")]
312 pub network_lan_discovery: bool,
313
314 #[serde(rename = "voice:enabled", default, skip_serializing_if = "Option::is_none")]
324 pub voice_enabled: Option<bool>,
325
326 #[serde(rename = "voice:engine", default, skip_serializing_if = "Option::is_none")]
331 pub voice_engine: Option<String>,
332
333 #[serde(rename = "voice:groqApiKey", default, skip_serializing_if = "Option::is_none")]
336 pub voice_groq_api_key: Option<String>,
337
338 #[serde(rename = "voice:whisperCliPath", default, skip_serializing_if = "Option::is_none")]
342 pub voice_whisper_cli_path: Option<String>,
343 #[serde(rename = "voice:whisperModel", default, skip_serializing_if = "Option::is_none")]
346 pub voice_whisper_model: Option<String>,
347 #[serde(rename = "voice:whisperModelPath", default, skip_serializing_if = "Option::is_none")]
348 pub voice_whisper_model_path: Option<String>,
349
350 #[serde(rename = "notify:sounds:enabled", default, skip_serializing_if = "Option::is_none")]
358 pub notify_sounds_enabled: Option<bool>,
359
360 #[serde(rename = "notify:sounds:volume", default, skip_serializing_if = "Option::is_none")]
362 pub notify_sounds_volume: Option<f32>,
363
364 #[serde(rename = "notify:sounds:suppresswhenfocused", default, skip_serializing_if = "Option::is_none")]
367 pub notify_sounds_suppress_when_focused: Option<bool>,
368
369 #[serde(rename = "notify:sound:agent.turn.complete", default, skip_serializing_if = "Option::is_none")]
371 pub notify_sound_agent_turn_complete: Option<bool>,
372
373 #[serde(rename = "notify:sound:agent.turn.error", default, skip_serializing_if = "Option::is_none")]
374 pub notify_sound_agent_turn_error: Option<bool>,
375
376 #[serde(rename = "notify:sound:agent.turn.interrupted", default, skip_serializing_if = "Option::is_none")]
377 pub notify_sound_agent_turn_interrupted: Option<bool>,
378
379 #[serde(rename = "notify:sound:agent.message.accepted", default, skip_serializing_if = "Option::is_none")]
380 pub notify_sound_agent_message_accepted: Option<bool>,
381
382 #[serde(rename = "notify:sound:agent.message.rejected", default, skip_serializing_if = "Option::is_none")]
383 pub notify_sound_agent_message_rejected: Option<bool>,
384
385 #[serde(rename = "notify:tooltones:enabled", default, skip_serializing_if = "Option::is_none")]
392 pub notify_tooltones_enabled: Option<bool>,
393
394 #[serde(rename = "notify:tooltones:volume", default, skip_serializing_if = "Option::is_none")]
397 pub notify_tooltones_volume: Option<f32>,
398
399 #[serde(rename = "notify:tooltones:scope", default, skip_serializing_if = "Option::is_none")]
403 pub notify_tooltones_scope: Option<String>,
404
405 #[serde(rename = "notify:tooltones:flash", default, skip_serializing_if = "Option::is_none")]
413 pub notify_tooltones_flash: Option<bool>,
414 #[serde(rename = "notify:waiting:browser", default, skip_serializing_if = "Option::is_none")]
418 pub notify_waiting_browser: Option<bool>,
419
420 #[serde(rename = "messaging:discord:enabled", default, skip_serializing_if = "is_false")]
425 pub messaging_discord_enabled: bool,
426
427 #[serde(rename = "messaging:discord:token", default, skip_serializing_if = "Option::is_none")]
430 pub messaging_discord_token: Option<String>,
431
432 #[serde(rename = "messaging:discord:channel", default, skip_serializing_if = "String::is_empty")]
434 pub messaging_discord_channel: String,
435
436 #[serde(rename = "messaging:discord:target", default, skip_serializing_if = "Option::is_none")]
439 pub messaging_discord_target: Option<String>,
440
441 #[serde(rename = "messaging:discord:guild", default, skip_serializing_if = "Option::is_none")]
443 pub messaging_discord_guild: Option<String>,
444
445 #[serde(rename = "messaging:telegram:enabled", default, skip_serializing_if = "is_false")]
450 pub messaging_telegram_enabled: bool,
451
452 #[serde(rename = "messaging:telegram:token", default, skip_serializing_if = "Option::is_none")]
454 pub messaging_telegram_token: Option<String>,
455
456 #[serde(rename = "messaging:telegram:allowed_chats", default, skip_serializing_if = "String::is_empty")]
461 pub messaging_telegram_allowed_chats: String,
462
463 #[serde(rename = "messaging:telegram:default_chat", default, skip_serializing_if = "Option::is_none")]
465 pub messaging_telegram_default_chat: Option<String>,
466
467 #[serde(rename = "messaging:telegram:target", default, skip_serializing_if = "Option::is_none")]
470 pub messaging_telegram_target: Option<String>,
471
472 #[serde(rename = "messaging:slack:enabled", default, skip_serializing_if = "is_false")]
477 pub messaging_slack_enabled: bool,
478
479 #[serde(rename = "messaging:slack:bot_token", default, skip_serializing_if = "Option::is_none")]
483 pub messaging_slack_bot_token: Option<String>,
484
485 #[serde(rename = "messaging:slack:app_token", default, skip_serializing_if = "Option::is_none")]
489 pub messaging_slack_app_token: Option<String>,
490
491 #[serde(rename = "messaging:slack:channel", default, skip_serializing_if = "String::is_empty")]
493 pub messaging_slack_channel: String,
494
495 #[serde(rename = "messaging:slack:target", default, skip_serializing_if = "Option::is_none")]
498 pub messaging_slack_target: Option<String>,
499
500 #[serde(rename = "messaging:whatsapp:enabled", default, skip_serializing_if = "is_false")]
512 pub messaging_whatsapp_enabled: bool,
513
514 #[serde(rename = "messaging:whatsapp:phone_number_id", default, skip_serializing_if = "String::is_empty")]
516 pub messaging_whatsapp_phone_number_id: String,
517
518 #[serde(rename = "messaging:whatsapp:access_token", default, skip_serializing_if = "Option::is_none")]
520 pub messaging_whatsapp_access_token: Option<String>,
521
522 #[serde(rename = "messaging:whatsapp:app_secret", default, skip_serializing_if = "Option::is_none")]
525 pub messaging_whatsapp_app_secret: Option<String>,
526
527 #[serde(rename = "messaging:whatsapp:webhook_verify_token", default, skip_serializing_if = "Option::is_none")]
531 pub messaging_whatsapp_webhook_verify_token: Option<String>,
532
533 #[serde(rename = "messaging:whatsapp:target", default, skip_serializing_if = "Option::is_none")]
536 pub messaging_whatsapp_target: Option<String>,
537
538 #[serde(rename = "messaging:whatsapp:fallback_template", default, skip_serializing_if = "Option::is_none")]
542 pub messaging_whatsapp_fallback_template: Option<String>,
543
544 #[serde(rename = "messaging:whatsapp:fallback_template_lang", default, skip_serializing_if = "Option::is_none")]
546 pub messaging_whatsapp_fallback_template_lang: Option<String>,
547
548 #[serde(rename = "messaging:whatsapp:tunnel_domain", default, skip_serializing_if = "String::is_empty")]
557 pub messaging_whatsapp_tunnel_domain: String,
558
559 #[serde(default, skip_serializing_if = "HashMap::is_empty", deserialize_with = "deserialize_connections")]
568 pub connections: HashMap<String, ConnKeywords>,
569
570 #[serde(flatten, default, skip_serializing_if = "HashMap::is_empty")]
573 pub extra: HashMap<String, serde_json::Value>,
574}
575
576#[derive(Debug, Clone, Default, Serialize, Deserialize)]
580pub struct MimeTypeConfigType {
581 #[serde(default, skip_serializing_if = "String::is_empty")]
582 pub icon: String,
583
584 #[serde(default, skip_serializing_if = "String::is_empty")]
585 pub color: String,
586}
587
588#[derive(Debug, Clone, Default, Serialize, Deserialize)]
590pub struct FileDef {
591 #[serde(default, skip_serializing_if = "String::is_empty")]
592 pub content: String,
593
594 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
595 pub meta: HashMap<String, Value>,
596}
597
598#[derive(Debug, Clone, Default, Serialize, Deserialize)]
600pub struct BlockDef {
601 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
602 pub files: HashMap<String, FileDef>,
603
604 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
605 pub meta: MetaMapType,
606}
607
608#[derive(Debug, Clone, Default, Serialize, Deserialize)]
610pub struct WidgetConfigType {
611 #[serde(rename = "display:order", default, skip_serializing_if = "is_zero_f64")]
612 pub display_order: f64,
613
614 #[serde(rename = "display:grouponly", alias = "display:hidden", default, skip_serializing_if = "is_false")]
617 pub display_grouponly: bool,
618
619 #[serde(rename = "display:pinned", default, skip_serializing_if = "is_false")]
622 pub display_pinned: bool,
623
624 #[serde(default, skip_serializing_if = "String::is_empty")]
625 pub icon: String,
626
627 #[serde(default, skip_serializing_if = "String::is_empty")]
628 pub color: String,
629
630 #[serde(default, skip_serializing_if = "String::is_empty")]
631 pub label: String,
632
633 #[serde(default, skip_serializing_if = "String::is_empty")]
634 pub description: String,
635
636 #[serde(default, skip_serializing_if = "is_false")]
637 pub magnified: bool,
638
639 #[serde(default, skip_serializing_if = "Vec::is_empty")]
645 pub children: Vec<String>,
646
647 #[serde(default, skip_serializing_if = "String::is_empty")]
652 pub module: String,
653
654 #[serde(rename = "blockdef", default)]
655 pub block_def: BlockDef,
656}
657
658#[derive(Debug, Clone, Default, Serialize, Deserialize)]
660pub struct TermThemeType {
661 #[serde(rename = "display:name", default, skip_serializing_if = "String::is_empty")]
662 pub display_name: String,
663
664 #[serde(rename = "display:order", default, skip_serializing_if = "is_zero_f64")]
665 pub display_order: f64,
666
667 #[serde(default, skip_serializing_if = "String::is_empty")]
668 pub black: String,
669 #[serde(default, skip_serializing_if = "String::is_empty")]
670 pub red: String,
671 #[serde(default, skip_serializing_if = "String::is_empty")]
672 pub green: String,
673 #[serde(default, skip_serializing_if = "String::is_empty")]
674 pub yellow: String,
675 #[serde(default, skip_serializing_if = "String::is_empty")]
676 pub blue: String,
677 #[serde(default, skip_serializing_if = "String::is_empty")]
678 pub magenta: String,
679 #[serde(default, skip_serializing_if = "String::is_empty")]
680 pub cyan: String,
681 #[serde(default, skip_serializing_if = "String::is_empty")]
682 pub white: String,
683
684 #[serde(rename = "brightBlack", default, skip_serializing_if = "String::is_empty")]
685 pub bright_black: String,
686 #[serde(rename = "brightRed", default, skip_serializing_if = "String::is_empty")]
687 pub bright_red: String,
688 #[serde(rename = "brightGreen", default, skip_serializing_if = "String::is_empty")]
689 pub bright_green: String,
690 #[serde(rename = "brightYellow", default, skip_serializing_if = "String::is_empty")]
691 pub bright_yellow: String,
692 #[serde(rename = "brightBlue", default, skip_serializing_if = "String::is_empty")]
693 pub bright_blue: String,
694 #[serde(rename = "brightMagenta", default, skip_serializing_if = "String::is_empty")]
695 pub bright_magenta: String,
696 #[serde(rename = "brightCyan", default, skip_serializing_if = "String::is_empty")]
697 pub bright_cyan: String,
698 #[serde(rename = "brightWhite", default, skip_serializing_if = "String::is_empty")]
699 pub bright_white: String,
700
701 #[serde(default, skip_serializing_if = "String::is_empty")]
702 pub gray: String,
703 #[serde(rename = "cmdtext", default, skip_serializing_if = "String::is_empty")]
704 pub cmd_text: String,
705 #[serde(default, skip_serializing_if = "String::is_empty")]
706 pub foreground: String,
707 #[serde(rename = "selectionBackground", default, skip_serializing_if = "String::is_empty")]
708 pub selection_background: String,
709 #[serde(default, skip_serializing_if = "String::is_empty")]
710 pub background: String,
711 #[serde(default, skip_serializing_if = "String::is_empty")]
712 pub cursor: String,
713}
714
715#[derive(Debug, Clone, Default, Serialize, Deserialize)]
717pub struct WebBookmark {
718 #[serde(default)]
719 pub url: String,
720
721 #[serde(default, skip_serializing_if = "String::is_empty")]
722 pub title: String,
723
724 #[serde(default, skip_serializing_if = "String::is_empty")]
725 pub icon: String,
726
727 #[serde(rename = "iconcolor", default, skip_serializing_if = "String::is_empty")]
728 pub icon_color: String,
729
730 #[serde(rename = "iconurl", default, skip_serializing_if = "String::is_empty")]
731 pub icon_url: String,
732
733 #[serde(rename = "display:order", default, skip_serializing_if = "is_zero_f64")]
734 pub display_order: f64,
735}
736
737#[derive(Debug, Clone, Default, Serialize, Deserialize)]
740pub struct ConnKeywords {
741 #[serde(rename = "conn:shellpath", default, skip_serializing_if = "String::is_empty")]
743 pub conn_shell_path: String,
744
745 #[serde(rename = "conn:ignoresshconfig", default, skip_serializing_if = "Option::is_none")]
746 pub conn_ignore_ssh_config: Option<bool>,
747
748 #[serde(rename = "conn:helper", default, skip_serializing_if = "String::is_empty")]
751 pub conn_helper: String,
752
753 #[serde(rename = "display:name", default, skip_serializing_if = "String::is_empty")]
756 pub display_name: String,
757
758 #[serde(rename = "display:color", default, skip_serializing_if = "String::is_empty")]
760 pub display_color: String,
761
762 #[serde(rename = "display:pinned", default, skip_serializing_if = "Option::is_none")]
764 pub display_pinned: Option<bool>,
765
766 #[serde(rename = "display:hidden", default, skip_serializing_if = "Option::is_none")]
767 pub display_hidden: Option<bool>,
768
769 #[serde(rename = "display:order", default, skip_serializing_if = "is_zero_f32")]
770 pub display_order: f32,
771
772 #[serde(rename = "term:*", default, skip_serializing_if = "is_false")]
774 pub term_clear: bool,
775
776 #[serde(rename = "term:fontsize", default, skip_serializing_if = "is_zero_f64")]
777 pub term_font_size: f64,
778
779 #[serde(rename = "term:fontfamily", default, skip_serializing_if = "String::is_empty")]
780 pub term_font_family: String,
781
782 #[serde(rename = "term:theme", default, skip_serializing_if = "String::is_empty")]
783 pub term_theme: String,
784
785 #[serde(rename = "term:durable", default, skip_serializing_if = "Option::is_none")]
787 pub term_durable: Option<bool>,
788
789 #[serde(rename = "cmd:env", default, skip_serializing_if = "HashMap::is_empty")]
791 pub cmd_env: HashMap<String, String>,
792
793 #[serde(rename = "cmd:initscript", default, skip_serializing_if = "String::is_empty")]
794 pub cmd_init_script: String,
795
796 #[serde(rename = "cmd:initscript.sh", default, skip_serializing_if = "String::is_empty")]
797 pub cmd_init_script_sh: String,
798
799 #[serde(rename = "cmd:initscript.bash", default, skip_serializing_if = "String::is_empty")]
800 pub cmd_init_script_bash: String,
801
802 #[serde(rename = "cmd:initscript.zsh", default, skip_serializing_if = "String::is_empty")]
803 pub cmd_init_script_zsh: String,
804
805 #[serde(rename = "cmd:initscript.pwsh", default, skip_serializing_if = "String::is_empty")]
806 pub cmd_init_script_pwsh: String,
807
808 #[serde(rename = "cmd:initscript.fish", default, skip_serializing_if = "String::is_empty")]
809 pub cmd_init_script_fish: String,
810
811 #[serde(rename = "ssh:user", default, skip_serializing_if = "Option::is_none")]
813 pub ssh_user: Option<String>,
814
815 #[serde(rename = "ssh:hostname", default, skip_serializing_if = "Option::is_none")]
816 pub ssh_hostname: Option<String>,
817
818 #[serde(rename = "ssh:port", default, skip_serializing_if = "Option::is_none")]
819 pub ssh_port: Option<String>,
820
821 #[serde(rename = "ssh:identityfile", default, skip_serializing_if = "Vec::is_empty")]
822 pub ssh_identity_file: Vec<String>,
823
824 #[serde(rename = "ssh:batchmode", default, skip_serializing_if = "Option::is_none")]
825 pub ssh_batch_mode: Option<bool>,
826
827 #[serde(rename = "ssh:pubkeyauthentication", default, skip_serializing_if = "Option::is_none")]
828 pub ssh_pubkey_authentication: Option<bool>,
829
830 #[serde(rename = "ssh:passwordauthentication", default, skip_serializing_if = "Option::is_none")]
831 pub ssh_password_authentication: Option<bool>,
832
833 #[serde(rename = "ssh:kbdinteractiveauthentication", default, skip_serializing_if = "Option::is_none")]
834 pub ssh_kbd_interactive_authentication: Option<bool>,
835
836 #[serde(rename = "ssh:preferredauthentications", default, skip_serializing_if = "Vec::is_empty")]
837 pub ssh_preferred_authentications: Vec<String>,
838
839 #[serde(rename = "ssh:addkeystoagent", default, skip_serializing_if = "Option::is_none")]
840 pub ssh_add_keys_to_agent: Option<bool>,
841
842 #[serde(rename = "ssh:identityagent", default, skip_serializing_if = "Option::is_none")]
843 pub ssh_identity_agent: Option<String>,
844
845 #[serde(rename = "ssh:identitiesonly", default, skip_serializing_if = "Option::is_none")]
846 pub ssh_identities_only: Option<bool>,
847
848 #[serde(rename = "ssh:proxyjump", default, skip_serializing_if = "Vec::is_empty")]
849 pub ssh_proxy_jump: Vec<String>,
850
851 #[serde(rename = "ssh:userknownhostsfile", default, skip_serializing_if = "Vec::is_empty")]
852 pub ssh_user_known_hosts_file: Vec<String>,
853
854 #[serde(rename = "ssh:globalknownhostsfile", default, skip_serializing_if = "Vec::is_empty")]
855 pub ssh_global_known_hosts_file: Vec<String>,
856}
857
858#[derive(Debug, Clone, Default, Serialize, Deserialize)]
860pub struct ConfigError {
861 pub file: String,
862 pub err: String,
863}
864
865#[allow(dead_code)]
867#[derive(Debug, Clone, Default, Serialize, Deserialize)]
868pub struct WebhookConfigType {
869 #[serde(default)]
870 pub version: String,
871
872 #[serde(rename = "workspaceId", default)]
873 pub workspace_id: String,
874
875 #[serde(rename = "authToken", default)]
876 pub auth_token: String,
877
878 #[serde(rename = "cloudEndpoint", default)]
879 pub cloud_endpoint: String,
880
881 #[serde(default)]
882 pub enabled: bool,
883
884 #[serde(default)]
885 pub terminals: Vec<String>,
886}
887
888#[derive(Debug, Clone, Default, Serialize, Deserialize)]
893pub struct FullConfigType {
894 #[serde(default)]
895 pub settings: SettingsType,
896
897 #[serde(rename = "mimetypes", default)]
898 pub mime_types: HashMap<String, MimeTypeConfigType>,
899
900 #[serde(default)]
901 pub widgets: HashMap<String, WidgetConfigType>,
902
903 #[serde(default)]
904 pub presets: HashMap<String, MetaMapType>,
905
906 #[serde(rename = "termthemes", default)]
907 pub term_themes: HashMap<String, TermThemeType>,
908
909 #[serde(default)]
910 pub connections: HashMap<String, ConnKeywords>,
911
912 #[serde(default)]
913 pub bookmarks: HashMap<String, WebBookmark>,
914
915 #[serde(rename = "configerrors", default, skip_serializing_if = "Vec::is_empty")]
916 pub config_errors: Vec<ConfigError>,
917
918 #[serde(rename = "browserstartpage", default, skip_serializing_if = "Option::is_none")]
928 pub browser_start_page: Option<String>,
929}
930
931#[cfg(test)]
932mod statusbar_muxbus_cloud_tests {
933 use super::SettingsType;
934
935 #[test]
938 fn absent_by_default_and_not_serialised() {
939 let s = SettingsType::default();
940 assert_eq!(s.statusbar_show_muxbus_cloud, None);
941 let json = serde_json::to_string(&s).unwrap();
942 assert!(!json.contains("statusbar:showmuxbuscloud"), "{json}");
943 }
944
945 #[test]
946 fn explicit_false_round_trips() {
947 let s: SettingsType = serde_json::from_str(r#"{"statusbar:showmuxbuscloud": false}"#).unwrap();
948 assert_eq!(s.statusbar_show_muxbus_cloud, Some(false));
949 let json = serde_json::to_string(&s).unwrap();
950 assert!(json.contains(r#""statusbar:showmuxbuscloud":false"#), "{json}");
951 }
952
953 #[test]
954 fn explicit_true_round_trips() {
955 let s: SettingsType = serde_json::from_str(r#"{"statusbar:showmuxbuscloud": true}"#).unwrap();
956 assert_eq!(s.statusbar_show_muxbus_cloud, Some(true));
957 }
958}