diff --git a/app/src/ai/agent_sdk/driver/harness/claude_transcript.rs b/app/src/ai/agent_sdk/driver/harness/claude_transcript.rs index 0c8a42a9..def5c462 100644 --- a/app/src/ai/agent_sdk/driver/harness/claude_transcript.rs +++ b/app/src/ai/agent_sdk/driver/harness/claude_transcript.rs @@ -180,7 +180,6 @@ pub(crate) fn read_envelope( /// - `/projects//.jsonl` - main transcript /// - `/projects///subagents/.jsonl` - subagents /// - `/todos/.json` - per-agent todo lists - fn validated_stem<'a>(stem: &'a str, field_name: &str) -> Result<&'a str> { anyhow::ensure!(!stem.is_empty(), "{field_name} stem cannot be empty"); let path = Path::new(stem); diff --git a/app/src/ai/coven_brand.rs b/app/src/ai/coven_brand.rs index 2e0ac05d..22e999f0 100644 --- a/app/src/ai/coven_brand.rs +++ b/app/src/ai/coven_brand.rs @@ -26,6 +26,7 @@ pub(crate) const OPENCOVEN_WARNING: ColorU = ColorU { /// Brand muted text colour (`#5A5A65`) — used for closed-session status /// dots and secondary metadata in Coven session rows. +#[allow(dead_code)] pub(crate) const OPENCOVEN_MUTED: ColorU = ColorU { r: 90, g: 90, diff --git a/app/src/browser/browser_model.rs b/app/src/browser/browser_model.rs index 0d419c92..f3d0d4fb 100644 --- a/app/src/browser/browser_model.rs +++ b/app/src/browser/browser_model.rs @@ -144,18 +144,25 @@ impl BrowserTab { true } + // Tab pin + favicon accessors/mutators are scaffolding for an upcoming + // tab-strip UI revision; no caller wires them yet but they're read by + // the matching BrowserModel helpers below. + #[allow(dead_code)] pub fn pinned(&self) -> bool { self.pinned } + #[allow(dead_code)] pub fn favicon(&self) -> Option<&str> { self.favicon.as_deref() } + #[allow(dead_code)] fn set_pinned(&mut self, pinned: bool) { self.pinned = pinned; } + #[allow(dead_code)] fn set_favicon(&mut self, favicon: Option) { self.favicon = favicon; } @@ -324,6 +331,7 @@ impl BrowserModel { self.tabs[idx].replace_current_url(url) } + #[allow(dead_code)] pub fn set_pinned(&mut self, id: TabId, pinned: bool) -> bool { let Some(idx) = self.index_of(id) else { return false; @@ -332,6 +340,7 @@ impl BrowserModel { true } + #[allow(dead_code)] pub fn set_favicon(&mut self, id: TabId, favicon: Option) -> bool { let Some(idx) = self.index_of(id) else { return false; diff --git a/app/src/browser/browser_view.rs b/app/src/browser/browser_view.rs index 193c7fa6..a48e5798 100644 --- a/app/src/browser/browser_view.rs +++ b/app/src/browser/browser_view.rs @@ -392,13 +392,11 @@ impl BrowserView { editor }); - ctx.subscribe_to_view(&find_editor, move |view, _, event, ctx| { - match event { - EditorEvent::Edited(_) => view.handle_find_query_changed(ctx), - EditorEvent::Enter => view.handle_action(&BrowserViewAction::FindNext, ctx), - EditorEvent::Escape => view.handle_action(&BrowserViewAction::CloseFind, ctx), - _ => {} - } + ctx.subscribe_to_view(&find_editor, move |view, _, event, ctx| match event { + EditorEvent::Edited(_) => view.handle_find_query_changed(ctx), + EditorEvent::Enter => view.handle_action(&BrowserViewAction::FindNext, ctx), + EditorEvent::Escape => view.handle_action(&BrowserViewAction::CloseFind, ctx), + _ => {} }); Self { @@ -512,13 +510,11 @@ impl BrowserView { editor }); - ctx.subscribe_to_view(&find_editor, move |view, _, event, ctx| { - match event { - EditorEvent::Edited(_) => view.handle_find_query_changed(ctx), - EditorEvent::Enter => view.handle_action(&BrowserViewAction::FindNext, ctx), - EditorEvent::Escape => view.handle_action(&BrowserViewAction::CloseFind, ctx), - _ => {} - } + ctx.subscribe_to_view(&find_editor, move |view, _, event, ctx| match event { + EditorEvent::Edited(_) => view.handle_find_query_changed(ctx), + EditorEvent::Enter => view.handle_action(&BrowserViewAction::FindNext, ctx), + EditorEvent::Escape => view.handle_action(&BrowserViewAction::CloseFind, ctx), + _ => {} }); Self { @@ -567,7 +563,10 @@ impl BrowserView { fn zoom_in(&mut self, ctx: &mut ViewContext) { let tab_id = self.model.active_tab().id(); - let cur = *self.tab_zoom_steps.get(&tab_id).unwrap_or(&DEFAULT_ZOOM_STEP); + let cur = *self + .tab_zoom_steps + .get(&tab_id) + .unwrap_or(&DEFAULT_ZOOM_STEP); let next = zoom_step_in(cur); if next == cur { return; @@ -579,7 +578,10 @@ impl BrowserView { fn zoom_out(&mut self, ctx: &mut ViewContext) { let tab_id = self.model.active_tab().id(); - let cur = *self.tab_zoom_steps.get(&tab_id).unwrap_or(&DEFAULT_ZOOM_STEP); + let cur = *self + .tab_zoom_steps + .get(&tab_id) + .unwrap_or(&DEFAULT_ZOOM_STEP); let next = zoom_step_out(cur); if next == cur { return; @@ -936,6 +938,7 @@ impl BrowserView { } } + #[allow(clippy::too_many_arguments)] fn render_toolbar_button( &self, icon: Icon, @@ -1289,6 +1292,7 @@ impl BrowserView { .finish() } + #[allow(clippy::too_many_arguments)] fn render_tab_chip( &self, idx: usize, @@ -1324,7 +1328,7 @@ impl BrowserView { Icon::X, TAB_CLOSE_BUTTON_SIZE, close_mouse, - chip_text_color.into(), + chip_text_color, ) .with_tooltip(move || { ui_builder @@ -1474,13 +1478,15 @@ impl TypedActionView for BrowserView { } BrowserViewAction::ToggleFind => self.toggle_find(ctx), BrowserViewAction::CloseFind => self.close_find(ctx), - BrowserViewAction::FindNext => { + BrowserViewAction::FindNext => + { #[cfg(not(target_family = "wasm"))] if let Some(webview) = self.active_webview() { webview.borrow().find_next(); } } - BrowserViewAction::FindPrev => { + BrowserViewAction::FindPrev => + { #[cfg(not(target_family = "wasm"))] if let Some(webview) = self.active_webview() { webview.borrow().find_prev(); @@ -1503,7 +1509,10 @@ impl BackingView for BrowserView { _ctx: &AppContext, ) -> Vec> { let tab_id = self.model.active_tab().id(); - let cur = *self.tab_zoom_steps.get(&tab_id).unwrap_or(&DEFAULT_ZOOM_STEP); + let cur = *self + .tab_zoom_steps + .get(&tab_id) + .unwrap_or(&DEFAULT_ZOOM_STEP); let pct = (zoom_level_for_step(cur) * 100.0).round() as i32; let reset_label = format!("Reset zoom ({pct}%)"); let modifier = if cfg!(target_os = "macos") { diff --git a/app/src/browser/downloads.rs b/app/src/browser/downloads.rs index 0ab353f2..32edd261 100644 --- a/app/src/browser/downloads.rs +++ b/app/src/browser/downloads.rs @@ -1,3 +1,9 @@ +// Helpers are consumed by `browser/webview_host.rs`, which is itself +// scaffolding for the in-progress download pipeline. Until that pipeline +// gets wired up to a real wry handler, the call chain is unreachable from +// production code paths, so allow dead_code at the module level. +#![allow(dead_code)] + //! Download destination resolution for the embedded browser pane. //! //! wry exposes `with_download_started_handler` (a `Fn(url, &mut PathBuf) diff --git a/app/src/browser/find.rs b/app/src/browser/find.rs index 6b4b61b0..20987aa9 100644 --- a/app/src/browser/find.rs +++ b/app/src/browser/find.rs @@ -63,6 +63,12 @@ pub(crate) fn clear_script() -> &'static str { /// Shape of the JSON message the find script posts back via /// `window.ipc.postMessage`. See `assets/bundled/js/find.js`. +// +// Constructed only by serde inside webview_host's macOS-gated IPC +// handler; on Linux/Windows the type compiles but nothing deserializes +// into it. Allowed at the struct level until the non-macOS wry wiring +// lands. +#[allow(dead_code)] #[derive(Debug, Clone, Deserialize)] pub(crate) struct FindResultsMessage { pub kind: String, diff --git a/app/src/browser/popup_policy.rs b/app/src/browser/popup_policy.rs index a63ceb96..2ca3c6cd 100644 --- a/app/src/browser/popup_policy.rs +++ b/app/src/browser/popup_policy.rs @@ -1,3 +1,8 @@ +// Consumed by `browser/webview_host.rs`'s in-progress popup wiring; until +// that handler is hooked to wry, the call chain is unreachable from +// production paths. +#![allow(dead_code)] + //! Policy decisions for popups (`window.open` / `target="_blank"`) inside //! the embedded browser pane. //! diff --git a/app/src/browser/webview_host.rs b/app/src/browser/webview_host.rs index fc23568e..5299e5cd 100644 --- a/app/src/browser/webview_host.rs +++ b/app/src/browser/webview_host.rs @@ -11,8 +11,10 @@ use super::browser_model::TabId; #[cfg(target_os = "macos")] use super::downloads; #[cfg(not(target_family = "wasm"))] -use super::find::{self, FindResultsMessage}; -#[cfg(not(target_family = "wasm"))] +use super::find; +#[cfg(target_os = "macos")] +use super::find::FindResultsMessage; +#[cfg(target_os = "macos")] use super::popup_policy::{self, Decision}; /// Events the native webview layer can push back to `BrowserView`. @@ -21,6 +23,13 @@ use super::popup_policy::{self, Decision}; /// receiver doesn't need a parallel mapping. Popup events don't carry a /// `TabId` — the host treats them as "open a new tab" and the new tab gets /// its own id. +// +// Variants are constructed only by the macOS-gated `attach_if_needed` +// path (wry handlers); `browser_view.rs` matches on them cross-platform, +// so on Linux/Windows builds the variants compile but never get +// constructed. Allowed at the enum level until the non-macOS wry wiring +// lands. +#[allow(dead_code)] #[derive(Debug, Clone)] pub(crate) enum NativeWebViewEvent { /// Document title changed (raw from WKWebView). @@ -44,6 +53,11 @@ pub(crate) enum NativeWebViewEvent { #[cfg(not(target_family = "wasm"))] pub(crate) type SharedWebContext = Rc>; +// `tab_id`, `event_tx`, and `web_context` are read only by the macOS +// `attach_if_needed` path; on Linux/Windows builds they get stored at +// construction but never read. Allowed at the struct level until the +// non-macOS wry wiring lands. +#[allow(dead_code)] pub(crate) struct NativeBrowserWebView { tab_id: TabId, #[cfg(not(target_family = "wasm"))] diff --git a/app/src/cli_chat/mod.rs b/app/src/cli_chat/mod.rs index 3eb5ee92..b34ae127 100644 --- a/app/src/cli_chat/mod.rs +++ b/app/src/cli_chat/mod.rs @@ -6,14 +6,28 @@ //! //! See `specs/castcodes-chat-panel/PRODUCT.md` and `TECH.md`. +// The chat panel feature is half-wired: model/store/view layers are in +// tree but no caller binds them to a live `CLIAgentSessionsModel` yet. +// Until that wiring lands, scaffolding accessors / strings / setup +// helpers are unconstructed. Allow at each submodule declaration so the +// scaffolding compiles without churn. +#[allow(dead_code)] pub mod conversation; +#[allow(dead_code)] pub mod entry; +#[allow(dead_code)] pub mod feature_flag; +#[allow(dead_code)] pub mod model; +#[allow(dead_code)] pub mod paths; +#[allow(dead_code)] pub mod store; +#[allow(dead_code)] pub mod store_schema; +#[allow(dead_code)] pub mod strings; +#[allow(dead_code)] pub mod view; pub use model::ChatModel; diff --git a/app/src/cli_chat/store.rs b/app/src/cli_chat/store.rs index 9d5fedcd..7c99092c 100644 --- a/app/src/cli_chat/store.rs +++ b/app/src/cli_chat/store.rs @@ -145,7 +145,7 @@ impl ChatStore { )?; let convs = stmt - .query_map([], |row| row_to_conversation(row))? + .query_map([], row_to_conversation)? .collect::>>()?; let mut result = Vec::with_capacity(convs.len()); diff --git a/app/src/cli_chat/store_schema.rs b/app/src/cli_chat/store_schema.rs index 0e1218f7..11e82519 100644 --- a/app/src/cli_chat/store_schema.rs +++ b/app/src/cli_chat/store_schema.rs @@ -49,8 +49,8 @@ pub fn migrate(conn: &Connection) -> Result<()> { return Err(rusqlite::Error::InvalidQuery); } - for v in (current as usize)..MIGRATIONS.len() { - for stmt in MIGRATIONS[v] { + for (v, migration) in MIGRATIONS.iter().enumerate().skip(current as usize) { + for stmt in *migration { conn.execute(stmt, [])?; } conn.execute( diff --git a/app/src/cli_chat/view/empty_state.rs b/app/src/cli_chat/view/empty_state.rs index 5b177441..7e483c10 100644 --- a/app/src/cli_chat/view/empty_state.rs +++ b/app/src/cli_chat/view/empty_state.rs @@ -13,6 +13,7 @@ use warpui::fonts::FamilyId; use crate::cli_chat::strings; /// Which empty state to display. +#[allow(clippy::enum_variant_names)] pub(crate) enum EmptyKind { /// No conversations at all — invite the user to run a CLI. NoHistory, diff --git a/app/src/cli_chat/view/model_picker.rs b/app/src/cli_chat/view/model_picker.rs index 049e8fba..5c86a1d8 100644 --- a/app/src/cli_chat/view/model_picker.rs +++ b/app/src/cli_chat/view/model_picker.rs @@ -36,7 +36,7 @@ pub fn render(view: &ChatPanelView, app: &AppContext) -> Box { // Determine the label for the current agent/model. let chat = view.chat_model.as_ref(app); - let label = current_agent_model_label(chat.binding(), &chat, app); + let label = current_agent_model_label(chat.binding(), chat, app); let label_element = Text::new(label, font_family, font_size).finish(); diff --git a/app/src/pane_group/pane/browser_pane.rs b/app/src/pane_group/pane/browser_pane.rs index 16b93cc1..716105d3 100644 --- a/app/src/pane_group/pane/browser_pane.rs +++ b/app/src/pane_group/pane/browser_pane.rs @@ -37,11 +37,7 @@ impl BrowserPane { /// platform notes). It is accepted unconditionally so callers don't /// need to cfg-gate; on wasm it is discarded because there is no /// WebKit data store to scope. - pub fn new( - url: Option, - session_id: String, - ctx: &mut ViewContext, - ) -> Self { + pub fn new(url: Option, session_id: String, ctx: &mut ViewContext) -> Self { let view = ctx.add_typed_action_view(move |ctx| { #[cfg(not(target_family = "wasm"))] { @@ -62,8 +58,8 @@ impl BrowserPane { session_id: String, ctx: &mut ViewContext, ) -> Self { - let view = ctx - .add_typed_action_view(move |ctx| BrowserView::from_state(state, &session_id, ctx)); + let view = + ctx.add_typed_action_view(move |ctx| BrowserView::from_state(state, &session_id, ctx)); Self::from_view(view, ctx) } @@ -142,11 +138,7 @@ impl PaneContent for BrowserPane { self.view.as_ref(ctx).is_being_dragged() } - fn on_workspace_tab_visibility_changed( - &self, - visible: bool, - ctx: &mut ViewContext, - ) { + fn on_workspace_tab_visibility_changed(&self, visible: bool, ctx: &mut ViewContext) { let browser_view = self.browser_view(ctx); browser_view.update(ctx, |view, _ctx| view.set_workspace_tab_visible(visible)); } diff --git a/app/src/server/server_api.rs b/app/src/server/server_api.rs index ad683b11..96267b4e 100644 --- a/app/src/server/server_api.rs +++ b/app/src/server/server_api.rs @@ -367,7 +367,9 @@ pub enum ServerApiEvent { UserAccountDisabled, /// The current bearer token was refreshed. AccessTokenRefreshed { - #[cfg_attr(target_family = "wasm", allow(dead_code))] + // Currently only the Debug impl reads this; consumers don't yet + // observe the refreshed value. Allow until a consumer wires up. + #[allow(dead_code)] token: String, }, } diff --git a/app/src/settings/import/config.rs b/app/src/settings/import/config.rs index d0efdd91..7337d8a2 100644 --- a/app/src/settings/import/config.rs +++ b/app/src/settings/import/config.rs @@ -27,6 +27,9 @@ use super::{alacritty_parser::AlacrittyConfig, model::TerminalType}; #[cfg(target_os = "macos")] use super::iterm_parser::ITermProfile; +// Theme imports surface this enum once per UI action, so the size delta +// between variants isn't a hot-path concern — allow the lint here. +#[allow(clippy::large_enum_variant)] #[derive(Debug)] pub enum ThemeType { LightAndDark { light: WarpTheme, dark: WarpTheme }, diff --git a/app/src/settings_view/import_theme_modal.rs b/app/src/settings_view/import_theme_modal.rs index 1da1859e..ec3345ec 100644 --- a/app/src/settings_view/import_theme_modal.rs +++ b/app/src/settings_view/import_theme_modal.rs @@ -189,8 +189,7 @@ impl ImportThemeBody { ctx.emit(ImportThemeBodyEvent::Close); } Ok(_) => { - self.show_error = - Some("No color blocks were found in the theme.".to_string()); + self.show_error = Some("No color blocks were found in the theme.".to_string()); ctx.notify(); } Err(e) => { @@ -282,12 +281,7 @@ impl View for ImportThemeBody { // ── Status line ─────────────────────────────────────────────────── let (status_text, status_color, has_light, has_dark) = match &self.state { - FetchState::Idle => ( - String::new(), - theme.disabled_ui_text_color(), - false, - false, - ), + FetchState::Idle => (String::new(), theme.disabled_ui_text_color(), false, false), FetchState::Fetching => ( "Fetching theme…".to_string(), theme.disabled_ui_text_color(), diff --git a/app/src/settings_view/main_page.rs b/app/src/settings_view/main_page.rs index bc3f4587..cff455be 100644 --- a/app/src/settings_view/main_page.rs +++ b/app/src/settings_view/main_page.rs @@ -1099,19 +1099,6 @@ impl SettingsWidget for LogoutWidget { } } -#[cfg(test)] -mod tests { - use super::VersionInfoWidget; - use warp_core::channel::ChannelState; - - #[test] - fn version_info_uses_package_version_when_release_tag_missing() { - ChannelState::set_app_version(None); - - assert_eq!(VersionInfoWidget::version_label(), "v0.1.0-local"); - } -} - impl SettingsPageMeta for MainSettingsPageView { fn section() -> SettingsSection { SettingsSection::Account @@ -1147,3 +1134,16 @@ impl From> for SettingsPageViewHandle { SettingsPageViewHandle::Main(view_handle) } } + +#[cfg(test)] +mod tests { + use super::VersionInfoWidget; + use warp_core::channel::ChannelState; + + #[test] + fn version_info_uses_package_version_when_release_tag_missing() { + ChannelState::set_app_version(None); + + assert_eq!(VersionInfoWidget::version_label(), "v0.1.0-local"); + } +} diff --git a/app/src/terminal/view/pane_impl.rs b/app/src/terminal/view/pane_impl.rs index 39e47a53..a528eab3 100644 --- a/app/src/terminal/view/pane_impl.rs +++ b/app/src/terminal/view/pane_impl.rs @@ -1141,10 +1141,9 @@ impl TerminalView { } } -fn default_agent_conversation_title(is_ambient_agent: bool) -> String { - if is_ambient_agent { - "New agent conversation".to_owned() - } else { - "New agent conversation".to_owned() - } +fn default_agent_conversation_title(_is_ambient_agent: bool) -> String { + // Ambient vs. regular agents currently share the same default title; + // the parameter is retained because callers will distinguish them in + // a follow-up that customises the ambient label. + "New agent conversation".to_owned() } diff --git a/app/src/themes/tweakcn_import.rs b/app/src/themes/tweakcn_import.rs index 70392fcb..798821d2 100644 --- a/app/src/themes/tweakcn_import.rs +++ b/app/src/themes/tweakcn_import.rs @@ -222,8 +222,8 @@ pub fn parse_blocks(css: &str) -> Result { blocks.name_comment = name_hint; fn extract_block<'a>(haystack: &'a str, selector: &str) -> Option<&'a str> { - let needle = format!("{}", selector); - let start = haystack.find(&needle)?; + let needle = selector; + let start = haystack.find(needle)?; let body_start = haystack[start + needle.len()..].find('{')? + start + needle.len() + 1; let mut depth = 1; let mut end = body_start; @@ -523,7 +523,9 @@ fn io_to_import(e: std::io::Error) -> ImportError { ImportError::Io(e.to_string()) } -fn tweakcn_ui_mapping() -> &'static [(&'static str, fn(&mut UiTokens, ColorU))] { +type UiTokenSetter = fn(&mut UiTokens, ColorU); + +fn tweakcn_ui_mapping() -> &'static [(&'static str, UiTokenSetter)] { &[ ("card", |u, c| u.card = Some(c)), ("card-foreground", |u, c| u.card_foreground = Some(c)), diff --git a/app/src/util/git_tests.rs b/app/src/util/git_tests.rs index 39643dcd..646dd54d 100644 --- a/app/src/util/git_tests.rs +++ b/app/src/util/git_tests.rs @@ -91,7 +91,7 @@ async fn detached_tag_display_returns_short_sha() { #[cfg(feature = "local_fs")] #[test] fn detect_repo_root_sync_returns_repo_root() { - use std::process::Command as StdCommand; + use command::blocking::Command as StdCommand; let dir = tempfile::tempdir().expect("failed to create temp dir"); let repo = dir.path(); diff --git a/app/src/util/worktree_tests.rs b/app/src/util/worktree_tests.rs index 571878f5..6161db00 100644 --- a/app/src/util/worktree_tests.rs +++ b/app/src/util/worktree_tests.rs @@ -181,7 +181,7 @@ fn parse_main_is_first_entry() { #[cfg(feature = "local_fs")] #[tokio::test] async fn list_worktrees_round_trip_on_temp_repo() { - use std::process::Command; + use command::blocking::Command; let td = TempDir::new().unwrap(); let repo = td.path(); let run = |args: &[&str]| { @@ -222,7 +222,7 @@ async fn list_worktrees_round_trip_on_temp_repo() { #[cfg(feature = "local_fs")] #[tokio::test] async fn add_worktree_existing_branch() { - use std::process::Command; + use command::blocking::Command; let td = TempDir::new().unwrap(); let repo = td.path(); let run = |args: &[&str]| { @@ -250,7 +250,7 @@ async fn add_worktree_existing_branch() { #[cfg(feature = "local_fs")] #[tokio::test] async fn add_worktree_creates_new_branch() { - use std::process::Command; + use command::blocking::Command; let td = TempDir::new().unwrap(); let repo = td.path(); let run = |args: &[&str]| { @@ -282,7 +282,7 @@ async fn add_worktree_creates_new_branch() { #[cfg(feature = "local_fs")] #[tokio::test] async fn remove_worktree_clean() { - use std::process::Command; + use command::blocking::Command; let td = TempDir::new().unwrap(); let repo = td.path(); let run = |args: &[&str]| { @@ -315,7 +315,7 @@ async fn remove_worktree_clean() { #[cfg(feature = "local_fs")] #[tokio::test] async fn remove_worktree_dirty_requires_force() { - use std::process::Command; + use command::blocking::Command; let td = TempDir::new().unwrap(); let repo = td.path(); let run = |args: &[&str]| { diff --git a/app/src/workspace/view.rs b/app/src/workspace/view.rs index f6df8c04..f5e3c456 100644 --- a/app/src/workspace/view.rs +++ b/app/src/workspace/view.rs @@ -25483,28 +25483,6 @@ fn compute_default_panel_widths( } } -#[cfg(test)] -mod tab_bar_collapse_tests { - use super::*; - - #[test] - fn manual_collapse_hides_tab_bar_until_revealed() { - assert_eq!( - resolve_manual_tab_bar_mode(true, false), - Some(ShowTabBar::Hidden) - ); - assert_eq!( - resolve_manual_tab_bar_mode(true, true), - Some(ShowTabBar::Stacked) - ); - } - - #[test] - fn manual_collapse_does_not_override_regular_mode_when_disabled() { - assert_eq!(resolve_manual_tab_bar_mode(false, false), None); - } -} - /// Idempotently sets the opencode-warp plugin entry in `~/.config/opencode/opencode.json`. /// Removes any existing opencode-warp plugin entries (both local file:// and github:) and adds /// the given `new_entry`. Creates the config file with a default structure if it doesn't exist. @@ -25561,3 +25539,25 @@ fn set_opencode_warp_plugin(new_entry: &str) -> String { Err(e) => format!("Failed to serialize opencode.json: {e}"), } } + +#[cfg(test)] +mod tab_bar_collapse_tests { + use super::*; + + #[test] + fn manual_collapse_hides_tab_bar_until_revealed() { + assert_eq!( + resolve_manual_tab_bar_mode(true, false), + Some(ShowTabBar::Hidden) + ); + assert_eq!( + resolve_manual_tab_bar_mode(true, true), + Some(ShowTabBar::Stacked) + ); + } + + #[test] + fn manual_collapse_does_not_override_regular_mode_when_disabled() { + assert_eq!(resolve_manual_tab_bar_mode(false, false), None); + } +}