par_term_config/config/config_struct/mod.rs
1//! Core `Config` struct definition.
2//!
3//! This module contains the main configuration struct with all terminal,
4//! display, input, and feature settings.
5//!
6//! # Splitting Strategy
7//!
8//! Thematic field groups live in sibling `*_config.rs` sub-structs held here as
9//! `#[serde(flatten)]` members. Flattening is what makes the split a pure
10//! refactor: every field still serialises at the top level of `config.yaml`,
11//! indistinguishable from a direct struct field, so a config written by any
12//! release keeps loading unchanged.
13//!
14//! **New settings belong in the sub-struct for their area, not on `Config`.**
15//! The root keeps only the handful of fields below, and adding to it is how
16//! this file grew to 1,529 lines in the first place.
17//!
18//! Two rules the sub-structs must follow, both enforced by
19//! `tests/config_yaml_compat.rs`:
20//!
21//! - The member carries `#[serde(flatten)]`. Without it the group's keys nest
22//! under the member name and vanish from every existing config file.
23//! - `Default` is hand-written from the same expressions as the field's
24//! `#[serde(default = "…")]`, unless every field genuinely defaults to its
25//! type's `Default`. Most do not — deriving resets non-zero durations,
26//! opacities and `true` flags while compiling cleanly.
27//!
28//! # Still on the root
29//!
30//! Terminal size (`cols`/`rows`), the font family/metrics block, the window
31//! title, and a scattering of one-field settings (`screenshot_format`,
32//! `log_level`, `max_osc_data_length`, `command_history_max_entries`,
33//! `collapsed_settings_sections`, `dynamic_profile_sources`, the file-transfer
34//! pair, keybindings and snippets) plus the two `#[serde(skip)]` runtime
35//! security lists, which are computed state rather than configuration.
36
37mod agent_usage_config;
38mod ai_inspector_config;
39mod automation_config;
40mod background_config;
41mod badge_config;
42mod clipboard_config;
43mod command_separator_config;
44mod copy_mode_config;
45mod cursor_config;
46mod default_impl;
47mod font_config;
48mod global_shader_config;
49mod image_config;
50mod input_config;
51mod integration_config;
52mod mouse_config;
53mod notification_config;
54mod pane_config;
55mod power_config;
56mod progress_bar_config;
57mod rendering_config;
58mod scrollback_config;
59mod scrollbar_config;
60mod search_config;
61mod security_config;
62mod selection_config;
63mod semantic_history_config;
64mod session_log_config;
65mod session_restore_config;
66mod shader_overrides_config;
67mod shader_watch_config;
68mod shell_config;
69mod ssh_config;
70mod status_bar_config;
71mod tab_bar_colors_config;
72mod tab_config;
73mod theme_colors_config;
74mod tmux_config;
75mod unicode_config;
76mod update;
77mod window_config;
78mod window_placement_config;
79mod word_selection_config;
80
81pub use agent_usage_config::AgentUsageConfig;
82pub use ai_inspector_config::{AiInspectorConfig, AssistantInputHistoryMode};
83pub use automation_config::{AutomationConfig, PluginStateConfig};
84pub use background_config::BackgroundConfig;
85pub use badge_config::BadgeConfig;
86pub use clipboard_config::ClipboardConfig;
87pub use command_separator_config::CommandSeparatorConfig;
88pub use copy_mode_config::CopyModeConfig;
89pub use cursor_config::CursorConfig;
90pub use font_config::FontRenderingConfig;
91pub use global_shader_config::GlobalShaderConfig;
92pub use image_config::ImageConfig;
93pub use input_config::InputConfig;
94pub use integration_config::IntegrationConfig;
95pub use mouse_config::MouseConfig;
96pub use notification_config::NotificationConfig;
97pub use pane_config::PaneConfig;
98pub use power_config::PowerConfig;
99pub use progress_bar_config::ProgressBarConfig;
100pub use rendering_config::RenderingConfig;
101pub use scrollback_config::ScrollbackConfig;
102pub use scrollbar_config::ScrollbarConfig;
103pub use search_config::SearchConfig;
104pub use security_config::SecurityConfig;
105pub use selection_config::SelectionConfig;
106pub use semantic_history_config::SemanticHistoryConfig;
107pub use session_log_config::SessionLogConfig;
108pub use session_restore_config::SessionRestoreConfig;
109pub use shader_overrides_config::ShaderOverridesConfig;
110pub use shader_watch_config::ShaderWatchConfig;
111pub use shell_config::ShellConfig;
112pub use ssh_config::SshConfig;
113pub use status_bar_config::StatusBarConfig;
114pub use tab_bar_colors_config::TabBarColorsConfig;
115pub use tab_config::TabConfig;
116pub use theme_colors_config::ThemeColorsConfig;
117pub use tmux_config::TmuxConfig;
118pub use unicode_config::UnicodeConfig;
119pub use update::UpdateConfig;
120pub use window_config::WindowConfig;
121pub use window_placement_config::WindowPlacementConfig;
122pub use word_selection_config::WordSelectionConfig;
123
124use crate::snippets::{CustomActionConfig, SnippetConfig};
125use crate::types::{DownloadSaveLocation, FontRange, KeyBinding, LogLevel, ShellExitAction};
126use serde::{Deserialize, Serialize};
127
128/// Custom deserializer for `ShellExitAction` that supports backward compatibility.
129///
130/// Accepts either:
131/// - Boolean: `true` → `Close`, `false` → `Keep` (legacy format)
132/// - String enum: `"close"`, `"keep"`, `"restart_immediately"`, etc.
133pub(crate) fn deserialize_shell_exit_action<'de, D>(
134 deserializer: D,
135) -> Result<ShellExitAction, D::Error>
136where
137 D: serde::Deserializer<'de>,
138{
139 #[derive(Deserialize)]
140 #[serde(untagged)]
141 enum BoolOrAction {
142 Bool(bool),
143 Action(ShellExitAction),
144 }
145
146 match BoolOrAction::deserialize(deserializer)? {
147 BoolOrAction::Bool(true) => Ok(ShellExitAction::Close),
148 BoolOrAction::Bool(false) => Ok(ShellExitAction::Keep),
149 BoolOrAction::Action(action) => Ok(action),
150 }
151}
152
153/// Configuration for the terminal emulator
154/// Aligned with par-tui-term naming conventions for consistency
155#[derive(Debug, Clone, Serialize, Deserialize)]
156pub struct Config {
157 // ========================================================================
158 // Window & Display (GUI-specific)
159 // ========================================================================
160
161 // --- Terminal Size ---
162 /// Number of columns in the terminal
163 #[serde(default = "crate::defaults::cols")]
164 pub cols: usize,
165
166 /// Number of rows in the terminal
167 #[serde(default = "crate::defaults::rows")]
168 pub rows: usize,
169
170 // --- Font Settings ---
171 /// Font size in points
172 #[serde(default = "crate::defaults::font_size")]
173 pub font_size: f32,
174
175 /// Font family name (regular/normal weight)
176 #[serde(default = "crate::defaults::font_family")]
177 pub font_family: String,
178
179 /// Bold font family name (optional, defaults to font_family)
180 #[serde(default)]
181 pub font_family_bold: Option<String>,
182
183 /// Italic font family name (optional, defaults to font_family)
184 #[serde(default)]
185 pub font_family_italic: Option<String>,
186
187 /// Bold italic font family name (optional, defaults to font_family)
188 #[serde(default)]
189 pub font_family_bold_italic: Option<String>,
190
191 /// Custom font mappings for specific Unicode ranges
192 /// Format: Vec of (start_codepoint, end_codepoint, font_family_name)
193 /// Example: [(0x4E00, 0x9FFF, "Noto Sans CJK SC")] for CJK Unified Ideographs
194 #[serde(default)]
195 pub font_ranges: Vec<FontRange>,
196
197 /// Line height multiplier (1.0 = default/tight, 1.2 = comfortable, 1.5 = spacious)
198 #[serde(default = "crate::defaults::line_spacing")]
199 pub line_spacing: f32,
200
201 /// Character width multiplier (1.0 = default, values < 1.0 = narrow, values > 1.0 = wide)
202 #[serde(default = "crate::defaults::char_spacing")]
203 pub char_spacing: f32,
204
205 /// Enable text shaping for ligatures and complex scripts
206 /// When enabled, uses HarfBuzz for proper ligature, emoji, and complex script rendering
207 #[serde(default = "crate::defaults::text_shaping")]
208 pub enable_text_shaping: bool,
209
210 /// Enable ligatures (requires enable_text_shaping)
211 #[serde(default = "crate::defaults::bool_true")]
212 pub enable_ligatures: bool,
213
214 /// Enable kerning adjustments (requires enable_text_shaping)
215 #[serde(default = "crate::defaults::bool_true")]
216 pub enable_kerning: bool,
217
218 // --- Font Rendering Quality (extracted to FontRenderingConfig) ---
219 /// Font rendering quality settings: anti-aliasing, hinting, stroke weight, minimum contrast.
220 ///
221 /// Flattened into the top-level YAML so existing config files remain compatible.
222 /// Access via `config.font_rendering.font_antialias`, etc.
223 ///
224 /// See [`FontRenderingConfig`] for field documentation.
225 #[serde(flatten)]
226 pub font_rendering: FontRenderingConfig,
227
228 /// Window title
229 #[serde(default = "crate::defaults::window_title")]
230 pub window_title: String,
231
232 /// Allow applications to change the window title via OSC escape sequences
233 /// When false, the window title will always be the configured window_title
234 #[serde(default = "crate::defaults::bool_true")]
235 pub allow_title_change: bool,
236
237 // ========================================================================
238 // Frame Pacing & GPU
239 // ========================================================================
240 /// Frame rate target, VSync mode, GPU preference and output batching.
241 ///
242 /// Flattened into the top-level YAML so existing config files remain
243 /// compatible. See [`RenderingConfig`].
244 #[serde(flatten)]
245 pub rendering: RenderingConfig,
246
247 /// Window appearance settings (opacity, padding, decorations, blur, etc.)
248 /// Serialised flat at the top level via `#[serde(flatten)]` so that
249 /// existing YAML config files require no changes.
250 #[serde(flatten)]
251 pub window: WindowConfig,
252
253 // ========================================================================
254 // Window Placement
255 // ========================================================================
256 /// Window type, target monitor/Space, resize lock and window numbering.
257 ///
258 /// Flattened into the top-level YAML so existing config files remain
259 /// compatible. See [`WindowPlacementConfig`].
260 #[serde(flatten)]
261 pub placement: WindowPlacementConfig,
262
263 // ========================================================================
264 // Background Image & Transparency
265 // ========================================================================
266 /// Background image source and how transparency applies to cells and text.
267 ///
268 /// Flattened into the top-level YAML so existing config files remain
269 /// compatible. See [`BackgroundConfig`].
270 #[serde(flatten)]
271 pub background: BackgroundConfig,
272
273 // ========================================================================
274 // Inline Image Settings (Sixel, iTerm2, Kitty)
275 // ========================================================================
276 /// Inline image scaling and the terminal background source.
277 ///
278 /// Flattened into the top-level YAML so existing config files remain
279 /// compatible. See [`ImageConfig`].
280 #[serde(flatten)]
281 pub image: ImageConfig,
282
283 // ========================================================================
284 // File Transfer Settings
285 // ========================================================================
286 /// Default save location for downloaded files
287 #[serde(default)]
288 pub download_save_location: DownloadSaveLocation,
289
290 /// Last used download directory (persisted internally)
291 #[serde(default, skip_serializing_if = "Option::is_none")]
292 pub last_download_directory: Option<String>,
293
294 // ========================================================================
295 // Shader Settings (background + cursor) — extracted to GlobalShaderConfig
296 // ========================================================================
297 /// All `custom_shader_*` and `cursor_shader_*` settings.
298 ///
299 /// Flattened into the top-level YAML so existing config files remain compatible.
300 #[serde(flatten)]
301 pub shader: GlobalShaderConfig,
302
303 // ========================================================================
304 // Keyboard Input
305 // ========================================================================
306 /// Option/Alt key behaviour, modifier remapping and physical key positions.
307 ///
308 /// Flattened into the top-level YAML so existing config files remain
309 /// compatible. See [`InputConfig`].
310 #[serde(flatten)]
311 pub input: InputConfig,
312
313 // ========================================================================
314 // Selection & Clipboard
315 // ========================================================================
316 /// Copy-on-select, paste behaviour and dropped-file quoting.
317 ///
318 /// Flattened into the top-level YAML so existing config files remain
319 /// compatible. See [`SelectionConfig`].
320 #[serde(flatten)]
321 pub selection: SelectionConfig,
322
323 // ========================================================================
324 // Mouse — extracted to MouseConfig
325 // ========================================================================
326 /// Mouse behavior settings (see [`MouseConfig`]).
327 ///
328 /// Flattened into the top-level YAML so existing config files remain compatible.
329 #[serde(flatten)]
330 pub mouse: MouseConfig,
331
332 // ========================================================================
333 // Word Selection
334 // ========================================================================
335 /// Word boundary characters and pattern-based smart selection rules.
336 ///
337 /// Flattened into the top-level YAML so existing config files remain
338 /// compatible. See [`WordSelectionConfig`].
339 #[serde(flatten)]
340 pub word_selection: WordSelectionConfig,
341
342 // ========================================================================
343 // Copy Mode (vi-style keyboard-driven selection)
344 // ========================================================================
345 /// Vi-style copy mode settings (see [`CopyModeConfig`]).
346 #[serde(flatten)]
347 pub copy_mode: CopyModeConfig,
348
349 // ========================================================================
350 // Scrollback & Cursor
351 // ========================================================================
352 /// Scrollback buffer size settings (see [`ScrollbackConfig`]).
353 ///
354 /// Flattened into the top-level YAML so existing config files remain compatible.
355 #[serde(flatten)]
356 pub scrollback: ScrollbackConfig,
357
358 // ========================================================================
359 // Unicode Width Settings
360 // ========================================================================
361 /// Unicode character width and normalization settings (see [`UnicodeConfig`]).
362 #[serde(flatten)]
363 pub unicode: UnicodeConfig,
364
365 // ========================================================================
366 // Cursor — extracted to CursorConfig
367 // ========================================================================
368 /// Cursor appearance and behavior settings (see [`CursorConfig`]).
369 ///
370 /// Flattened into the top-level YAML so existing config files remain compatible.
371 #[serde(flatten)]
372 pub cursor: CursorConfig,
373
374 // ========================================================================
375 // Scrollbar
376 // ========================================================================
377 /// Scrollbar placement, size, colours, command marks and auto-hide.
378 ///
379 /// Flattened into the top-level YAML so existing config files remain
380 /// compatible. See [`ScrollbarConfig`].
381 #[serde(flatten)]
382 pub scrollbar: ScrollbarConfig,
383
384 // ========================================================================
385 // Theme & Colors
386 // ========================================================================
387 /// Terminal colour theme and the light/dark pair used by auto dark mode.
388 ///
389 /// Flattened into the top-level YAML so existing config files remain
390 /// compatible. See [`ThemeColorsConfig`].
391 #[serde(flatten)]
392 pub theme_colors: ThemeColorsConfig,
393
394 // ========================================================================
395 // Screenshot
396 // ========================================================================
397 /// File format for screenshots (png, jpeg, svg, html)
398 #[serde(default = "crate::defaults::screenshot_format")]
399 pub screenshot_format: String,
400
401 // ========================================================================
402 // Shell Behavior
403 // ========================================================================
404 /// Which shell runs, where it starts, what it is sent, and exit handling.
405 ///
406 /// Flattened into the top-level YAML so existing config files remain
407 /// compatible. See [`ShellConfig`].
408 #[serde(flatten)]
409 pub shell: ShellConfig,
410
411 // ========================================================================
412 // Semantic History
413 // ========================================================================
414 /// File path/URL detection, the editor that opens them, and link highlighting.
415 ///
416 /// Flattened into the top-level YAML so existing config files remain
417 /// compatible. See [`SemanticHistoryConfig`].
418 #[serde(flatten)]
419 pub semantic_history: SemanticHistoryConfig,
420
421 // ========================================================================
422 // Scrollbar (GUI-specific)
423 // ========================================================================
424 // ========================================================================
425 // Command Separator Lines
426 // ========================================================================
427 /// Horizontal separator lines drawn between shell commands.
428 ///
429 /// Flattened into the top-level YAML so existing config files remain
430 /// compatible. See [`CommandSeparatorConfig`].
431 #[serde(flatten)]
432 pub command_separator: CommandSeparatorConfig,
433
434 // ========================================================================
435 // Clipboard Sync Limits
436 // ========================================================================
437 /// Clipboard sync event retention and OSC 52 clipboard-set policy.
438 ///
439 /// Flattened into the top-level YAML so existing config files remain
440 /// compatible. See [`ClipboardConfig`].
441 #[serde(flatten)]
442 pub clipboard: ClipboardConfig,
443
444 // ========================================================================
445 // OSC Sequence Limits
446 // ========================================================================
447 /// Maximum total OSC data length in bytes before a sequence is rejected
448 /// by the core terminal (QA-012 memory-exhaustion guard). Must be large
449 /// enough for inline images (iTerm2/Kitty base64) if used.
450 #[serde(default = "crate::defaults::max_osc_data_length")]
451 pub max_osc_data_length: usize,
452
453 // ========================================================================
454 // Command History
455 // ========================================================================
456 /// Maximum number of commands to persist in fuzzy search history
457 #[serde(default = "crate::defaults::command_history_max_entries")]
458 pub command_history_max_entries: usize,
459
460 // ========================================================================
461 // Notifications — extracted to NotificationConfig
462 // ========================================================================
463 /// Bell, activity/silence alerts, anti-idle keep-alive, and OSC 9/777 buffer
464 /// settings (see [`NotificationConfig`]).
465 ///
466 /// Flattened into the top-level YAML so existing config files remain compatible.
467 #[serde(flatten)]
468 pub notifications: NotificationConfig,
469
470 // ========================================================================
471 // SSH Settings
472 // ========================================================================
473 /// SSH discovery and profile-switching settings (see [`SshConfig`]).
474 #[serde(flatten)]
475 pub ssh: SshConfig,
476
477 // ========================================================================
478 // Tab Settings
479 // ========================================================================
480 /// Tab style presets, tab bar placement and new-tab behaviour.
481 ///
482 /// Flattened into the top-level YAML so existing config files remain
483 /// compatible. See [`TabConfig`].
484 #[serde(flatten)]
485 pub tabs: TabConfig,
486
487 // ========================================================================
488 // Tab Bar Colors
489 // ========================================================================
490 /// Tab bar palette, dimming, sizing and border appearance.
491 ///
492 /// Flattened into the top-level YAML so existing config files remain
493 /// compatible. See [`TabBarColorsConfig`].
494 #[serde(flatten)]
495 pub tab_colors: TabBarColorsConfig,
496
497 // ========================================================================
498 // Split Pane Settings
499 // ========================================================================
500 /// Split-pane divider, padding, title bar and focus indicator settings.
501 ///
502 /// Flattened into the top-level YAML so existing config files remain
503 /// compatible. See [`PaneConfig`].
504 #[serde(flatten)]
505 pub panes: PaneConfig,
506
507 // ========================================================================
508 // tmux Integration
509 // ========================================================================
510 /// tmux control-mode integration settings.
511 ///
512 /// Flattened into the top-level YAML so existing config files remain
513 /// compatible. See [`TmuxConfig`].
514 #[serde(flatten)]
515 pub tmux: TmuxConfig,
516
517 // ========================================================================
518 // Focus/Blur Power Saving
519 // ========================================================================
520 /// Shader pausing and reduced frame rates when unfocused or inactive.
521 ///
522 /// Flattened into the top-level YAML so existing config files remain
523 /// compatible. See [`PowerConfig`].
524 #[serde(flatten)]
525 pub power: PowerConfig,
526
527 // ========================================================================
528 // Shader Hot Reload
529 // ========================================================================
530 /// Automatic reloading of shader files when they change on disk.
531 ///
532 /// Flattened into the top-level YAML so existing config files remain
533 /// compatible. See [`ShaderWatchConfig`].
534 #[serde(flatten)]
535 pub shader_watch: ShaderWatchConfig,
536
537 // ========================================================================
538 // Per-Shader Configuration Overrides
539 // ========================================================================
540 /// Per-file overrides for background and cursor shader settings.
541 ///
542 /// Flattened into the top-level YAML so existing config files remain
543 /// compatible. See [`ShaderOverridesConfig`].
544 #[serde(flatten)]
545 pub shader_overrides: ShaderOverridesConfig,
546
547 // ========================================================================
548 // Keybindings
549 // ========================================================================
550 /// Custom keybindings (checked before built-in shortcuts)
551 /// Format: key = "CmdOrCtrl+Shift+B", action = "toggle_background_shader"
552 #[serde(default = "crate::defaults::keybindings")]
553 pub keybindings: Vec<KeyBinding>,
554
555 /// Optional global prefix key for custom actions using per-action prefix chars.
556 /// Uses the same combo format as normal keybindings (for example "Ctrl+B").
557 /// Leave empty to disable prefix-mode custom actions.
558 #[serde(default = "crate::defaults::custom_action_prefix_key")]
559 pub custom_action_prefix_key: String,
560
561 // ========================================================================
562 // Shader & Shell Integration Installation
563 // ========================================================================
564 /// Install prompts and version tracking for bundled integrations.
565 ///
566 /// Flattened into the top-level YAML so existing config files remain
567 /// compatible. See [`IntegrationConfig`].
568 #[serde(flatten)]
569 pub integrations: IntegrationConfig,
570
571 // ========================================================================
572 // Update Checking
573 // ========================================================================
574 /// Configuration for automatic update checking
575 #[serde(flatten)]
576 pub updates: UpdateConfig,
577
578 // ========================================================================
579 // Window Arrangements & Session Restore
580 // ========================================================================
581 /// Startup arrangement restore, session restore and closed-tab undo.
582 ///
583 /// Flattened into the top-level YAML so existing config files remain
584 /// compatible. See [`SessionRestoreConfig`].
585 #[serde(flatten)]
586 pub session_restore: SessionRestoreConfig,
587
588 // ========================================================================
589 // Search Settings
590 // ========================================================================
591 /// Terminal search settings (see [`SearchConfig`]).
592 #[serde(flatten)]
593 pub search: SearchConfig,
594
595 // ========================================================================
596 // Session Logging
597 // ========================================================================
598 /// Automatic session recording: format, destination and redaction.
599 ///
600 /// Flattened into the top-level YAML so existing config files remain
601 /// compatible. See [`SessionLogConfig`].
602 #[serde(flatten)]
603 pub session_log: SessionLogConfig,
604
605 // ========================================================================
606 // Debug Logging
607 // ========================================================================
608 /// Log level for debug log file output.
609 /// Controls verbosity of `par_term_debug.log` in the system temp directory
610 /// (`$TMPDIR` on macOS, `/tmp` on Linux, `%TEMP%` on Windows).
611 /// Environment variable RUST_LOG and --log-level CLI flag take precedence.
612 #[serde(default)]
613 pub log_level: LogLevel,
614
615 // ========================================================================
616 // Badge Settings (iTerm2-style session labels)
617 // ========================================================================
618 /// Badge overlay text, colour, font and placement.
619 ///
620 /// Flattened into the top-level YAML so existing config files remain
621 /// compatible. See [`BadgeConfig`].
622 #[serde(flatten)]
623 pub badge: BadgeConfig,
624
625 // ========================================================================
626 // Status Bar Settings
627 // ========================================================================
628 /// Status bar settings (see [`StatusBarConfig`]).
629 ///
630 /// All `status_bar_*` fields are flattened here for YAML backward-compatibility.
631 #[serde(flatten)]
632 pub status_bar: StatusBarConfig,
633
634 // ========================================================================
635 // Agent Usage Settings
636 // ========================================================================
637 /// Agent usage panel settings (see [`AgentUsageConfig`]).
638 ///
639 /// All `agent_usage_*` fields are flattened here for YAML backward-compatibility.
640 #[serde(flatten)]
641 pub agent_usage: AgentUsageConfig,
642
643 // ========================================================================
644 // Progress Bar Settings (OSC 9;4 and OSC 934)
645 // ========================================================================
646 /// Progress bar overlay style, placement and state colours.
647 ///
648 /// Flattened into the top-level YAML so existing config files remain
649 /// compatible. See [`ProgressBarConfig`].
650 #[serde(flatten)]
651 pub progress_bar: ProgressBarConfig,
652
653 // ========================================================================
654 // Triggers & Automation
655 // ========================================================================
656 /// Regex triggers, coprocess definitions and external observer scripts.
657 ///
658 /// Flattened into the top-level YAML so existing config files remain
659 /// compatible. See [`AutomationConfig`].
660 #[serde(flatten)]
661 pub automation: AutomationConfig,
662
663 // ========================================================================
664 // Snippets & Actions
665 // ========================================================================
666 /// Text snippets for quick insertion
667 #[serde(default)]
668 pub snippets: Vec<SnippetConfig>,
669
670 /// Custom actions (shell commands, text insertion, key sequences)
671 #[serde(default)]
672 pub actions: Vec<CustomActionConfig>,
673
674 // ========================================================================
675 // UI State (persisted across sessions)
676 // ========================================================================
677 /// Settings window section IDs that have been toggled from their default collapse state.
678 /// Sections default to open unless specified otherwise; IDs in this set invert the default.
679 #[serde(default, skip_serializing_if = "Vec::is_empty")]
680 pub collapsed_settings_sections: Vec<String>,
681
682 // ========================================================================
683 // Dynamic Profile Sources
684 // ========================================================================
685 /// Remote URLs to fetch profile definitions from
686 #[serde(default, skip_serializing_if = "Vec::is_empty")]
687 pub dynamic_profile_sources: Vec<crate::profile::DynamicProfileSource>,
688
689 // ========================================================================
690 // Security
691 // ========================================================================
692 /// Environment variable substitution allowlist and plain-HTTP profile fetching.
693 ///
694 /// Flattened into the top-level YAML so existing config files remain
695 /// compatible. See [`SecurityConfig`].
696 #[serde(flatten)]
697 pub security: SecurityConfig,
698
699 // ========================================================================
700 // AI Inspector
701 // ========================================================================
702 /// AI Inspector side panel settings (see [`AiInspectorConfig`]).
703 ///
704 /// All `ai_inspector_*` fields are flattened here for YAML backward-compatibility.
705 #[serde(flatten)]
706 pub ai_inspector: AiInspectorConfig,
707
708 // ========================================================================
709 // Runtime security state (never serialized to disk)
710 // ========================================================================
711 /// Names of triggers that have `prompt_before_run: false` with dangerous
712 /// actions (`RunCommand` or `SendText`).
713 ///
714 /// Populated by `Config::warn_insecure_triggers` during config load.
715 /// The UI reads this list to display a persistent visual warning banner
716 /// so users are aware of the reduced security posture.
717 ///
718 /// This field is intentionally skipped by serde — it is computed at
719 /// runtime and never written to or read from the config file.
720 #[serde(skip)]
721 pub insecure_trigger_names: Vec<String>,
722
723 /// Names of triggers with `prompt_before_run: false` that are missing the
724 /// explicit `i_accept_the_risk: true` opt-in.
725 ///
726 /// Dangerous actions for these triggers are **blocked** at execution time.
727 /// Users must add `i_accept_the_risk: true` to each such trigger to permit
728 /// automatic execution without the confirmation dialog.
729 ///
730 /// This field is intentionally skipped by serde — it is computed at
731 /// runtime and never written to or read from the config file.
732 #[serde(skip)]
733 pub unaccepted_risk_trigger_names: Vec<String>,
734}
735
736#[cfg(test)]
737mod new_tab_position_config_tests {
738 use super::*;
739 use crate::NewTabPosition;
740
741 #[test]
742 fn new_tab_position_defaults_to_end() {
743 let config = Config::default();
744 assert_eq!(config.tabs.new_tab_position, NewTabPosition::End);
745 }
746
747 #[test]
748 fn new_tab_position_deserializes_from_yaml() {
749 let yaml = "new_tab_position: after_active";
750 let config: Config = serde_yaml_ng::from_str(yaml).unwrap();
751 assert_eq!(config.tabs.new_tab_position, NewTabPosition::AfterActive);
752 }
753
754 #[test]
755 fn config_without_new_tab_position_deserializes_to_default() {
756 // Existing configs that don't have this field must deserialize cleanly — zero migration.
757 let yaml = "tab_inherit_cwd: true";
758 let config: Config = serde_yaml_ng::from_str(yaml).unwrap();
759 assert_eq!(config.tabs.new_tab_position, NewTabPosition::End);
760 }
761}