docs(comments): rewrite comments across all crates to the guidelines

Sweep every first-party crate source (1956 .rs files) to the project comment
guidelines: delete redundant restatements, decorative banners, change
narration, and end-of-line comments; keep and tighten the crucial ones
(invariants, bug rationale, SAFETY blocks, ported-source attribution).

No functional code changed. Every edit is proven comment-only against the
prior tree by a comment-stripping lexer (string/char/raw-string aware) plus a
separate doctest-fence check. Where removing a comment made rustfmt or clippy
want to re-lay-out adjacent code, the minimal triggering comment is restored so
code tokens stay byte-identical.

Gates green: cargo fmt --all --check (0 diffs), cargo check and cargo clippy
--workspace --all-targets (0 warnings).

Adds scripts/check_codegen_comment_guidelines.py — the enforcement gate for
these guidelines (flags banners, end-of-line comments, change narration, and
commented-out code).
This commit is contained in:
2026-07-23 16:55:39 -04:00
parent ff0fb56c67
commit a02b555e66
1458 changed files with 10729 additions and 21750 deletions
@@ -1,9 +1,8 @@
//! Reproducible harness for benchmarking clipboard attachment reads
//! (the paste hot path).
//! Benchmark harness for the clipboard paste hot path.
//!
//! Runs one `get_attachments()` — the same unified probe the pager's paste
//! pipeline executes — and prints the outcome plus wall time. Benchmark the
//! native in-process read against the `osascript` fallback with hyperfine:
//! Runs the same `get_attachments()` probe the pager's paste pipeline executes
//! and prints the outcome plus wall time. Compare the native in-process read
//! against the `osascript` fallback with hyperfine:
//!
//! ```text
//! # put an image on the pasteboard first, e.g.:
+10 -40
View File
@@ -469,9 +469,7 @@ pub fn is_containerized_without_display() -> bool {
false
}
// ---------------------------------------------------------------------------
// macOS unified attachments `osascript` stdout parsing (pure, no I/O)
// ---------------------------------------------------------------------------
#[cfg(any(target_os = "macos", test))]
mod attachments_protocol {
@@ -546,9 +544,7 @@ mod attachments_protocol {
}
}
// ---------------------------------------------------------------------------
// macOS: subprocess-based clipboard (no AppKit linkage)
// ---------------------------------------------------------------------------
#[cfg(target_os = "macos")]
mod platform {
use std::process::{Command, Stdio};
@@ -557,7 +553,7 @@ mod platform {
use super::attachments_protocol::{FURL_MARKER, IMAGE_MARKER, parse_attachments_output};
use super::{ClipboardAttachments, ImageData};
// -- Fast pasteboard probes (NSPasteboard via lazy dlopen) -------------
// Fast pasteboard probes (NSPasteboard via lazy dlopen)
//
// These deliberately do NOT use `objc2-app-kit`: that crate emits a
// `#[link]` against AppKit, and linking AppKit is exactly what this
@@ -575,7 +571,7 @@ mod platform {
*LOADED.get_or_init(|| {
let path = c"/System/Library/Frameworks/AppKit.framework/AppKit";
// SAFETY: dlopen with a constant NUL-terminated path; the handle
// is intentionally leaked (AppKit stays loaded for the process).
// is deliberately leaked (AppKit stays loaded for the process).
let handle = unsafe { libc::dlopen(path.as_ptr(), libc::RTLD_LAZY) };
!handle.is_null()
})
@@ -602,7 +598,7 @@ mod platform {
/// probes — and AppKit reached via a bare `dlopen` (no NSApplication)
/// is NOT safe against concurrent pasteboard messaging (parallel probe
/// smoke tests crash with SIGSEGV/SIGABRT). The invariant is therefore
/// now held by lock: every native pasteboard entry point takes this
/// held by lock: every native pasteboard entry point takes this
/// mutex for the duration of its autoreleasepool.
static PASTEBOARD_LOCK: parking_lot::Mutex<()> = parking_lot::Mutex::new(());
@@ -1164,9 +1160,7 @@ mod platform {
}
}
// ---------------------------------------------------------------------------
// Linux / Windows: arboard with CLI-tool fallback on Linux
// ---------------------------------------------------------------------------
#[cfg(not(target_os = "macos"))]
mod platform {
use super::ImageData;
@@ -1185,7 +1179,7 @@ mod platform {
/// No AppKit to pre-warm off-macOS.
pub(super) fn clipboard_prewarm() {}
// -- arboard helpers (the in-process leg on all non-macOS platforms) ------
// arboard helpers (the in-process leg on all non-macOS platforms)
/// Run `f` on a named worker thread and wait up to `deadline` for its
/// result. `Err(Timeout)` abandons the worker (it stays parked on the
@@ -1369,7 +1363,7 @@ mod platform {
})
}
// -- Linux CLI tools ------------------------------------------------------
// Linux CLI tools
//
// arboard is built with `wayland-data-control`: on compositors exposing the
// data-control protocol (probe: `wayland_data_control_supported`) it sets
@@ -1429,7 +1423,8 @@ mod platform {
write_text: &["xsel", "--clipboard", "--input"],
read_text: &["xsel", "--clipboard", "--output"],
read_primary: Some(&["xsel", "--primary", "--output"]),
write_png: None, // xsel doesn't support typed clipboard
// xsel doesn't support typed clipboard
write_png: None,
read_png: None,
};
@@ -1928,7 +1923,7 @@ mod platform {
reads_wayland_selection && !(data_control && arboard_ok)
}
// -- Public API ----------------------------------------------------------
// Public API
pub fn get_text() -> anyhow::Result<Option<String>> {
let mut arboard_error = None;
@@ -2719,9 +2714,7 @@ mod tests {
assert_eq!(got.as_deref(), Some(sentinel.as_str()));
}
// -----------------------------------------------------------------------
// wait_with_deadline (real child processes; unix `sleep`)
// -----------------------------------------------------------------------
#[cfg(unix)]
fn spawn_sleep(seconds: &str) -> std::process::Child {
@@ -2759,9 +2752,7 @@ mod tests {
assert!(child.try_wait().expect("child reaped").is_some());
}
// -----------------------------------------------------------------------
// spool_for_stdin (unlink-then-read contract)
// -----------------------------------------------------------------------
/// The two contracts callers rely on: the returned fd stays readable after
/// the temp file is unlinked on return, and a payload well past the
@@ -2776,9 +2767,7 @@ mod tests {
assert_eq!(read_back, payload);
}
// -----------------------------------------------------------------------
// OSC 52 sequence construction (pure; base64("hi") == "aGk=")
// -----------------------------------------------------------------------
#[test]
fn osc52_sequence_plain() {
@@ -2793,9 +2782,7 @@ mod tests {
);
}
// -----------------------------------------------------------------------
// MIME detection from magic bytes
// -----------------------------------------------------------------------
#[test]
fn mime_from_bytes_png() {
@@ -2844,9 +2831,7 @@ mod tests {
assert_eq!(mime_from_bytes(b"\x00\x01"), "application/octet-stream");
}
// -----------------------------------------------------------------------
// MIME to extension mapping
// -----------------------------------------------------------------------
#[test]
fn mime_to_extension_known() {
@@ -2864,9 +2849,7 @@ mod tests {
assert_eq!(mime_to_extension("text/plain"), "bin");
}
// -----------------------------------------------------------------------
// Linux RGBA-to-PNG encoding (only compiled on non-macOS)
// -----------------------------------------------------------------------
#[cfg(not(target_os = "macos"))]
mod linux_encoding {
@@ -2905,9 +2888,7 @@ mod tests {
}
}
// -----------------------------------------------------------------------
// macOS unified attachments osascript stdout protocol (pure parsing)
// -----------------------------------------------------------------------
mod attachments_protocol_tests {
use super::super::attachments_protocol::{
@@ -2918,9 +2899,7 @@ mod tests {
format!("{FURL_MARKER}\n{furl_body}\n{IMAGE_MARKER}\nIMAGE:{image}")
}
// -----------------------------------------------------------
// parse_osascript_furl_output: deterministic parsing surface
// -----------------------------------------------------------
#[test]
fn parse_furl_empty_inputs_are_none() {
@@ -2971,9 +2950,7 @@ mod tests {
);
}
// -----------------------------------------------------------
// parse_attachments_output: unified osascript stdout protocol
// -----------------------------------------------------------
#[test]
fn parse_attachments_none_none() {
@@ -3078,9 +3055,7 @@ mod tests {
}
}
// -----------------------------------------------------------------------
// macOS extension helper
// -----------------------------------------------------------------------
#[cfg(target_os = "macos")]
mod macos_helpers {
@@ -3095,12 +3070,11 @@ mod tests {
}
}
// -----------------------------------------------------------------------
// get_image returns Ok(None) when no image is on the clipboard
// -----------------------------------------------------------------------
#[test]
#[ignore] // requires real clipboard access
// requires real clipboard access
#[ignore]
fn get_image_text_only_clipboard() {
// Put text on the clipboard, then check that get_image returns None.
set_text("just text").expect("set_text failed");
@@ -3111,9 +3085,7 @@ mod tests {
);
}
// -----------------------------------------------------------------------
// Fast-probe type-list classification (clipboard_image_snapshot)
// -----------------------------------------------------------------------
fn types<'a>(list: &'a [&'static [u8]]) -> impl Iterator<Item = &'static [u8]> + 'a {
list.iter().copied()
@@ -3160,9 +3132,7 @@ mod tests {
assert!(!image_pasteable_from_types(types(&[])));
}
// -----------------------------------------------------------------------
// Native paste-time read type selection (native_image_type_from_types)
// -----------------------------------------------------------------------
/// The native read requests raster types in the osascript coercion
/// order — PNG first, TIFF, then JPEG — regardless of advertised order.
@@ -16,7 +16,7 @@
//! explicitly opt in to reading any arbitrary file — they paste a chat
//! transcript fragment and the agent may resurrect it across sessions.
//! To stop the placeholder mechanism from becoming a generic file
//! exfiltration sink, the loader is intentionally conservative:
//! exfiltration sink, the loader is deliberately conservative:
//!
//! * Canonicalises every candidate path (resolves `..` and symlinks).
//! * Asserts the canonical target lives under an explicit prefix
@@ -105,7 +105,7 @@ pub fn display_number_from_meta(meta: Option<&agent_client_protocol::Meta>) -> O
/// File extensions accepted by the placeholder loader.
///
/// SVG is intentionally **not** in this list: SVG is XML text with no
/// SVG is deliberately **not** in this list: SVG is XML text with no
/// reliable magic-byte signature, and adding it would expand the attack
/// surface (script tags, XXE) without a corresponding image-decoder
/// validation pass. Any future SVG support must be gated by a script
@@ -354,7 +354,7 @@ pub fn default_allowed_prefixes_with_home(
///
/// Chosen to match the directories users actually paste images from in
/// practice. Sensitive subtrees (`~/.ssh`, `~/.aws`, `~/.config`,
/// `~/.gnupg`, `~/Library/Keychains`) are intentionally excluded — they
/// `~/.gnupg`, `~/Library/Keychains`) are deliberately excluded — they
/// are never added to the prefix list, and any path resolving into
/// [`DENY_PATH_CONTAINS`] is rejected even from inside an allowed
/// prefix.
@@ -388,7 +388,7 @@ pub const HOME_IMAGE_SUBDIRS: &[&str] = &[
/// `[Image #N: <path>]` placeholder recovery callers — the
/// server-side `handle_prompt` fallback and the TUI orphan-placeholder
/// fallback. The legacy user-initiated drag/paste path in
/// `read_image_at_path` is intentionally outside this allowlist (the
/// `read_image_at_path` is deliberately outside this allowlist (the
/// user explicitly chose those files via the OS file picker).
pub fn load_placeholder_image(
path_str: &str,
@@ -680,7 +680,7 @@ mod tests {
path
}
// ----- strip_paths_from_image_placeholders ---------------------------
// strip_paths_from_image_placeholders
#[test]
fn strip_paths_drops_path_keeps_anchor() {
@@ -735,7 +735,7 @@ mod tests {
assert_eq!(strip_paths_from_image_placeholders(text.to_owned()), text);
}
// ----- extract_placeholders ------------------------------------------
// extract_placeholders
#[test]
fn extract_placeholders_basic() {
@@ -764,7 +764,7 @@ mod tests {
assert!(extract_placeholders("[image #1: /tmp/x.png]").is_empty());
assert!(extract_placeholders("[Image #: /tmp/x.png]").is_empty());
// Missing space after colon: producer always emits ": " so the
// shorthand form is intentionally rejected. Pinned here.
// shorthand form is deliberately rejected. Pinned here.
assert!(extract_placeholders("[Image #5:foo.png]").is_empty());
}
@@ -850,7 +850,7 @@ mod tests {
assert_eq!(&text[start..end], "[Image #1: /tmp/[odd]");
}
// ----- load_placeholder_image ----------------------------------------
// load_placeholder_image
#[test]
fn load_placeholder_image_happy_path() {
@@ -1046,7 +1046,7 @@ mod tests {
);
}
// ----- default_allowed_prefixes / _with_home --------------------------
// default_allowed_prefixes / _with_home
#[test]
fn default_allowed_prefixes_with_home_includes_workspace_and_every_subdir() {
@@ -1121,7 +1121,7 @@ mod tests {
);
}
// ----- canonical_from_file_uri ----------------------------------------
// canonical_from_file_uri
#[test]
fn canonical_from_file_uri_rejects_non_file_scheme() {
@@ -1143,7 +1143,7 @@ mod tests {
assert_eq!(parsed, canon);
}
// ----- recover_orphan_placeholders (hermetic, no ambient $HOME) -------
// recover_orphan_placeholders (hermetic, no ambient $HOME)
/// Build a non-empty ACP `ImageContent` so a future dedup change
/// that short-circuits on `data.is_empty()` cannot silently pass
@@ -1197,7 +1197,8 @@ mod tests {
let link = dir.path().join("link.png");
std::os::unix::fs::symlink(&real_target, &link).unwrap();
let attached_uri = format!("file://{}", link.display()); // non-canonical
// non-canonical
let attached_uri = format!("file://{}", link.display());
let mut raw = vec![make_acp_image(&attached_uri)];
let canonical_placeholder = dunce::canonicalize(&real_target).unwrap();
let query = format!("[Image #1: {}]", canonical_placeholder.display());
@@ -1300,7 +1301,7 @@ mod tests {
assert!(raw.is_empty());
}
// ----- Aggregate cap -------------------------------------------------
// Aggregate cap
/// Two placeholders, aggregate cap below the cumulative byte
/// total of both. The first image fits; the second pushes the
@@ -1379,7 +1380,7 @@ mod tests {
assert!(raw.is_empty());
}
// ----- DENY_PATH_CONTAINS --------------------------------------------
// DENY_PATH_CONTAINS
/// Every entry of `DENY_PATH_CONTAINS` produces an
/// `OutsideAllowedPrefixes` rejection. Loops the constant so a
@@ -1,6 +1,5 @@
use agent_client_protocol as acp;
/// Session identity: `id` + `cwd`.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Info {
pub id: acp::SessionId,
@@ -13,15 +13,12 @@ pub use info::Info;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FeedbackTerminalInfo {
/// Terminal emulator brand (e.g. "Ghostty", "iTerm2", "Unknown").
/// e.g. "Ghostty", "iTerm2", "Unknown".
pub brand: String,
/// Multiplexer wrapping the session (e.g. "tmux", "Zellij", "None detected").
/// e.g. "tmux", "Zellij", "None detected".
pub multiplexer: String,
/// Whether the session is over SSH.
pub is_ssh: bool,
/// Whether Byobu is wrapping the session.
pub is_byobu: bool,
/// Raw `TERM` environment variable value.
pub term_var: String,
/// tmux server version if inside tmux, otherwise "n/a".
#[serde(default, skip_serializing_if = "Option::is_none")]
+3 -3
View File
@@ -21,9 +21,9 @@ pub fn stderr_lock() -> MutexGuard<'static, ()> {
pub fn with_locked_stderr<T>(f: impl FnOnce(&mut std::fs::File) -> T) -> T {
let _guard = stderr_lock();
let mut file = kigi_tty_utils::dup_tui_stderr().unwrap_or_else(|_| {
// Fallback: try_clone stderr to get an independently-owned
// File. This path is hit if redirect_native_stderr was never
// called or fd dup fails.
// Reached when redirect_native_stderr was never called or the dup
// fails: duplicate fd 2 so the File owns its own descriptor and
// dropping it does not close the process-wide stderr.
let stderr = std::io::stderr();
let stderr_file: std::fs::File;
#[cfg(unix)]
+37 -49
View File
@@ -7,19 +7,19 @@ pub struct UiConfig {
pub max_thoughts_width: u16,
#[serde(skip_serializing_if = "Option::is_none")]
pub theme: Option<String>,
/// Model ID to use for the secondary agent when forking.
/// Defaults to the main default model (from default_models.json).
/// Secondary-agent model when forking; defaults to the main default model
/// (from default_models.json).
pub fork_secondary_model: String,
/// YOLO mode. Read by `util::config`, declared here for `serde_ignored`.
/// Read by `util::config`, declared here for `serde_ignored`.
#[serde(default)]
pub yolo: bool,
/// UI theme alias. Read by `util::config`, declared here for `serde_ignored`.
/// Theme alias. Read by `util::config`, declared here for `serde_ignored`.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ui_theme: Option<String>,
/// Compact mode. Read by pager, declared here for `serde_ignored`.
/// Read by the pager, declared here for `serde_ignored`.
#[serde(default)]
pub compact_mode: bool,
/// Simple mode. Read by pager, declared here for `serde_ignored`.
/// Read by the pager, declared here for `serde_ignored`.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub simple_mode: Option<bool>,
/// Read by `load_permission_mode()`. Declared for `serde_ignored`.
@@ -32,28 +32,26 @@ pub struct UiConfig {
/// permission prompt of a session. One of `allow_once`, `allow_always`,
/// or `reject`. After the first prompt, the cursor sticks to the user's
/// last-used option kind. When unset, the first prompt preselects the
/// "Always allow on all sessions" (enable-always-approve) row. Read by
/// the pager's permission view.
/// "Always allow on all sessions" (enable-always-approve) row.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub default_selected_permission: Option<String>,
/// Written by the pager's appearance persist module.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub show_timestamps: Option<bool>,
/// Timeline sidebar (per-turn tick rail in place of the scrollbar).
/// `None` = off (client default; opt-in). Written by the pager's settings modal.
/// `None` = off (client default; opt-in).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub show_timeline: Option<bool>,
/// Theme to use when the OS is in dark mode. Written by the pager's theme persist module.
/// Theme used while the OS is in dark mode.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub auto_dark_theme: Option<String>,
/// Theme to use when the OS is in light mode. Written by the pager's theme persist module.
/// Theme used while the OS is in light mode.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub auto_light_theme: Option<String>,
/// Mouse-wheel and trackpad scroll speed multiplier (1100).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub scroll_speed: Option<u8>,
/// Force scroll input classification (`auto` | `wheel` | `trackpad`).
/// Written by the pager's settings modal; unset defaults to `auto`.
/// Force scroll input classification (`auto` | `wheel` | `trackpad`);
/// unset defaults to `auto`.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub scroll_mode: Option<String>,
/// Invert vertical scroll direction ("natural" scrolling).
@@ -67,24 +65,23 @@ pub struct UiConfig {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub vim_mode: Option<bool>,
/// How ` ```mermaid ` code blocks are rendered (`auto` | `on` | `off`).
/// Written by the pager's settings modal.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub render_mermaid: Option<String>,
/// Hunk-tracker mode the pager advertises to the agent (`agent_only` |
/// `all_dirty` | `off`). Written by the pager's settings modal; read at
/// connect time (CLI `--hunk-tracker-mode` / `KIGI_HUNK_TRACKER` override
/// it). `off` disables hunk tracking entirely.
/// `all_dirty` | `off`). Read at connect time; CLI `--hunk-tracker-mode`
/// and `KIGI_HUNK_TRACKER` override it. `off` disables hunk tracking
/// entirely.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hunk_tracker_mode: Option<String>,
/// Voice capture chord behavior: `toggle` or `hold` (hold-to-talk; needs a
/// Kitty-protocol terminal, else falls back to toggle). Written by the
/// settings modal; unset defaults to `hold`.
/// Kitty-protocol terminal, else falls back to toggle). Unset defaults to
/// `hold`.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub voice_capture_mode: Option<String>,
/// Speech-to-text language preference for voice dictation. A Kigi STT
/// catalog code (`en`, `es`, `ja`, … — see xAI STT supported languages) or
/// `auto` (system locale, resolved at connect). Written by the settings
/// modal; unset leaves `[voice].language` / default `en`. When set, overrides
/// `auto` (system locale, resolved at connect). Unset leaves
/// `[voice].language` / default `en`; when set it overrides
/// `[voice].language` for the session.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub voice_stt_language: Option<String>,
@@ -97,14 +94,12 @@ pub struct UiConfig {
pub mouse_reporting_toggle: Option<bool>,
/// When cancelling a parent turn with running subagents: `always_stop` stops
/// them without prompting, `always_continue` leaves them running without
/// prompting. Unset/`ask` shows the cancel-turn picker. Written by the pager
/// when the user picks "Always stop" / "Always continue".
/// prompting. Unset/`ask` shows the cancel-turn picker.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cancel_subagents_on_turn_cancel: Option<String>,
/// User knob for the `remember_tool_approvals` gate: when `true`, permission
/// prompts show the granular per-tool "Always allow …" options. Written by
/// the settings modal; requirements/env/managed/remote settings also feed the
/// effective gate.
/// prompts show the granular per-tool "Always allow …" options.
/// Requirements/env/managed/remote settings also feed the effective gate.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub remember_tool_approvals: Option<bool>,
/// In-app drag selection highlight: `flash` | `hold` (legacy bool accepted).
@@ -118,22 +113,20 @@ pub struct UiConfig {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub selection_highlight_duration_ms: Option<u64>,
/// Show agent thinking/reasoning blocks in the TUI scrollback.
/// `None` = on (client default). Written by the pager's settings modal.
/// `None` = on (client default).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub show_thinking_blocks: Option<bool>,
/// Fold runs of consecutive non-destructive tool calls (reads, searches,
/// lists) into one transcript row. `None` = on (client default). Written
/// by the pager's settings modal.
/// lists) into one transcript row. `None` = on (client default).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub group_tool_verbs: Option<bool>,
/// Show Edit tool calls as a collapsed one-line `+N/-M` diffstat summary
/// by default (expand for the diff). `None` = off (client default).
/// Written by the pager's settings modal.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub collapsed_edit_blocks: Option<bool>,
/// Next-prompt suggestions (tab autocomplete ghost text) after each turn.
/// `None` = on (client default). Written by the pager's settings modal;
/// the `KIGI_PROMPT_SUGGESTIONS` env var overrides at runtime.
/// `None` = on (client default); the `KIGI_PROMPT_SUGGESTIONS` env var
/// overrides at runtime.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub prompt_suggestions: Option<bool>,
/// Startup cursor style: `None` (default) inherits the terminal's own
@@ -144,16 +137,13 @@ pub struct UiConfig {
/// `"fullscreen"` | `"minimal"`; unset → product default fullscreen.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub screen_mode: Option<String>,
/// Retired hidden opt-in for terminal-like double/triple-click word/line
/// selection. Superseded by `keep_text_selection = "word_select"`. Still
/// read only when `keep_text_selection` is unset; Settings clears this on
/// write. `"word_select"` | unset.
/// Hidden legacy opt-in for terminal-like double/triple-click word/line
/// selection (`"word_select"` | unset), superseded by
/// `keep_text_selection = "word_select"`. Consulted only while
/// `keep_text_selection` is unset; Settings clears it on write.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub double_click_action: Option<String>,
/// Per-tip contextual-hint opt-outs (`[ui.contextual_hints]`). Each `None`
/// inherits the remote/default (on); `Some` is a user-explicit choice that
/// beats the remote tier. Skipped on the wire when untouched so the section
/// only appears once a user toggles a tip.
/// Per-tip contextual-hint opt-outs (`[ui.contextual_hints]`).
#[serde(default, skip_serializing_if = "ContextualHints::is_default")]
pub contextual_hints: ContextualHints,
/// Display-refresh probe + auto-cadence (`[ui.display_refresh]`). Per-field
@@ -190,8 +180,7 @@ pub struct ContextualHints {
}
impl ContextualHints {
/// True when no tip has a user-explicit value (all inherit). Lets the
/// section stay absent from `config.toml` until the user toggles a tip.
/// Keeps the section absent from `config.toml` until the user toggles a tip.
pub fn is_default(&self) -> bool {
self.undo.is_none()
&& self.plan_mode.is_none()
@@ -269,8 +258,8 @@ impl Default for UiConfig {
}
impl UiConfig {
/// The single source of truth for the timeline-sidebar default (opt-in).
/// Flip this one line to change the default everywhere.
/// The single source of truth for the timeline-sidebar default (opt-in);
/// flipping it changes the default everywhere.
///
// TODO: migrate the other boolean UI settings (show_timestamps,
// simple_mode, show_thinking_blocks, …) to the same const + resolver
@@ -279,10 +268,9 @@ impl UiConfig {
// the registry drift-guard test to catch mismatches.
pub const SHOW_TIMELINE_DEFAULT: bool = false;
/// Resolved timeline-sidebar setting: the configured value, or
/// [`Self::SHOW_TIMELINE_DEFAULT`] when unset. The one place the default
/// is applied — every layer (cache, appearance config, settings modal)
/// reads through here so they cannot drift.
/// The one place [`Self::SHOW_TIMELINE_DEFAULT`] is applied — cache,
/// appearance config and the settings modal all resolve through here so
/// they cannot drift.
pub fn show_timeline_enabled(&self) -> bool {
self.show_timeline.unwrap_or(Self::SHOW_TIMELINE_DEFAULT)
}