diff --git a/Cargo.lock b/Cargo.lock index 98471960..de75c8f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1662,15 +1662,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "convert_case" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "core-foundation" version = "0.9.4" @@ -2212,7 +2203,7 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ - "convert_case 0.10.0", + "convert_case", "proc-macro2", "quote", "rustc_version", @@ -4232,7 +4223,6 @@ dependencies = [ "anyhow", "async-channel 2.5.0", "async-task", - "backtrace", "bindgen", "bitflags 2.11.1", "block", @@ -4275,7 +4265,6 @@ dependencies = [ "pollster 0.4.0", "postage", "profiling", - "proptest", "rand 0.9.4", "raw-window-handle", "refineable", @@ -5167,7 +5156,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" dependencies = [ "byteorder-lite", - "quick-error 2.0.1", + "quick-error", ] [[package]] @@ -7452,36 +7441,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "proptest" -version = "1.10.0" -source = "git+https://github.com/proptest-rs/proptest?rev=3dca198a8fef1b32e3a66f1e1897c955b4dc5b5b#3dca198a8fef1b32e3a66f1e1897c955b4dc5b5b" -dependencies = [ - "bit-set 0.8.0", - "bit-vec 0.8.0", - "bitflags 2.11.1", - "num-traits", - "proptest-macro", - "rand 0.9.4", - "rand_chacha 0.9.0", - "rand_xorshift", - "regex-syntax", - "rusty-fork", - "tempfile", - "unarray", -] - -[[package]] -name = "proptest-macro" -version = "0.5.0" -source = "git+https://github.com/proptest-rs/proptest?rev=3dca198a8fef1b32e3a66f1e1897c955b4dc5b5b#3dca198a8fef1b32e3a66f1e1897c955b4dc5b5b" -dependencies = [ - "convert_case 0.11.0", - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "psm" version = "0.1.31" @@ -7526,12 +7485,6 @@ dependencies = [ "bytemuck", ] -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - [[package]] name = "quick-error" version = "2.0.1" @@ -7676,15 +7629,6 @@ dependencies = [ "rand 0.8.6", ] -[[package]] -name = "rand_xorshift" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" -dependencies = [ - "rand_core 0.9.5", -] - [[package]] name = "range-alloc" version = "0.1.5" @@ -7761,7 +7705,7 @@ dependencies = [ "avif-serialize", "imgref", "loop9", - "quick-error 2.0.1", + "quick-error", "rav1e", "rayon", "rgb", @@ -8307,18 +8251,6 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" -[[package]] -name = "rusty-fork" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" -dependencies = [ - "fnv", - "quick-error 1.2.3", - "tempfile", - "wait-timeout", -] - [[package]] name = "rustybuzz" version = "0.20.1" @@ -9548,17 +9480,6 @@ dependencies = [ "parking_lot", ] -[[package]] -name = "terminal-test-app" -version = "0.1.0" -dependencies = [ - "gpui", - "gpui-component", - "gpui_platform", - "terminal", - "terminal_view", -] - [[package]] name = "terminal_size" version = "0.4.4" @@ -9660,7 +9581,7 @@ dependencies = [ "fax", "flate2", "half", - "quick-error 2.0.1", + "quick-error", "weezl", "zune-jpeg 0.5.15", ] @@ -10275,12 +10196,6 @@ dependencies = [ "zip 2.4.2", ] -[[package]] -name = "unarray" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" - [[package]] name = "unicase" version = "2.9.0" @@ -10634,15 +10549,6 @@ dependencies = [ "serde", ] -[[package]] -name = "wait-timeout" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" -dependencies = [ - "libc", -] - [[package]] name = "waker-fn" version = "1.2.0" diff --git a/Cargo.toml b/Cargo.toml index 643439f3..467f4502 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["crates/code_assistant", "crates/command_executor", "crates/fs_explorer", "crates/git", "crates/llm", "crates/sandbox", "crates/terminal", "crates/terminal_test_app", "crates/terminal_view", "crates/web"] +members = ["crates/code_assistant", "crates/command_executor", "crates/fs_explorer", "crates/git", "crates/llm", "crates/sandbox", "crates/web"] resolver = "2" diff --git a/crates/code_assistant/Cargo.toml b/crates/code_assistant/Cargo.toml index 28d76c6f..f0e4857f 100644 --- a/crates/code_assistant/Cargo.toml +++ b/crates/code_assistant/Cargo.toml @@ -6,6 +6,9 @@ edition = "2021" [features] default = ["document-conversion"] document-conversion = ["transmutation", "fs_explorer/document-conversion"] +# Enable the GPUI desktop UI. Requires Xcode / Metal on macOS. +# Without this flag the binary is TUI-only and compiles without Metal tools. +gpui-ui = ["dep:gpui", "dep:gpui_platform", "dep:gpui-component", "dep:terminal", "dep:terminal_view"] [dependencies] command_executor = { path = "../command_executor" } @@ -13,8 +16,8 @@ fs_explorer = { path = "../fs_explorer" } git = { path = "../git" } llm = { path = "../llm" } sandbox = { path = "../sandbox" } -terminal = { path = "../terminal" } -terminal_view = { path = "../terminal_view" } +terminal = { path = "../terminal", optional = true } +terminal_view = { path = "../terminal_view", optional = true } web = { path = "../web" } glob = "0.3" @@ -42,10 +45,10 @@ textwrap = "0.16" tui-markdown = "=0.3.6" derive_more = { version = "2", features = ["is_variant"] } -# GPUI related -gpui = "0.2.2" -gpui_platform = { version = "0.1", features = ["font-kit"] } -gpui-component = "0.5.2" +# GPUI related — only needed when the "gpui" feature is enabled +gpui = { version = "0.2.2", optional = true } +gpui_platform = { version = "0.1", features = ["font-kit"], optional = true } +gpui-component = { version = "0.5.2", optional = true } smallvec = "1.15" rust-embed = { version = "8.9", features = ["include-exclude"] } @@ -110,6 +113,5 @@ tokio-util = { version = "0.7", features = ["compat"] } core-text = "=21.0.0" [dev-dependencies] -gpui = { version = "0.2.2", features = ["test-support"] } axum = "0.7" bytes = "1.10" diff --git a/crates/code_assistant/src/app/mod.rs b/crates/code_assistant/src/app/mod.rs index 972a6ada..89c5178d 100644 --- a/crates/code_assistant/src/app/mod.rs +++ b/crates/code_assistant/src/app/mod.rs @@ -1,4 +1,5 @@ pub mod acp; +#[cfg(feature = "gpui-ui")] pub mod gpui; pub mod server; pub mod terminal; diff --git a/crates/code_assistant/src/cli.rs b/crates/code_assistant/src/cli.rs index 486a2f87..b740476c 100644 --- a/crates/code_assistant/src/cli.rs +++ b/crates/code_assistant/src/cli.rs @@ -179,11 +179,14 @@ impl Args { ); } - // Check persisted default model from settings - let settings = crate::ui::gpui::shared::settings::UiSettings::load(); - if let Some(ref default_model) = settings.default_model { - if config.get_model(default_model).is_some() { - return Ok(default_model.clone()); + // Check persisted default model from settings (only available in GPUI builds) + #[cfg(feature = "gpui-ui")] + { + let settings = crate::ui::gpui::shared::settings::UiSettings::load(); + if let Some(ref default_model) = settings.default_model { + if config.get_model(default_model).is_some() { + return Ok(default_model.clone()); + } } } diff --git a/crates/code_assistant/src/main.rs b/crates/code_assistant/src/main.rs index 3f70514a..7f08e9bf 100644 --- a/crates/code_assistant/src/main.rs +++ b/crates/code_assistant/src/main.rs @@ -82,6 +82,12 @@ async fn main() -> Result<()> { None => { if args.ui { // GPUI mode - use stderr to keep stdout clean + #[cfg(not(feature = "gpui-ui"))] + anyhow::bail!( + "This binary was compiled without the 'gpui' feature. \ + Rebuild with `cargo build --features gpui` to enable the desktop UI." + ); + #[cfg(feature = "gpui-ui")] setup_logging(args.verbose, false); } else { // Terminal UI mode - log to file to prevent UI interference @@ -96,6 +102,9 @@ async fn main() -> Result<()> { // In GUI mode, allow starting without a valid model config // (the settings screen will guide the user through setup). let model_name = if args.ui { + #[cfg(not(feature = "gpui-ui"))] + unreachable!("--ui requires the gpui feature"); + #[cfg(feature = "gpui-ui")] args.get_model_name().unwrap_or_default() } else { args.get_model_name()? @@ -116,6 +125,9 @@ async fn main() -> Result<()> { }; if args.ui { + #[cfg(not(feature = "gpui-ui"))] + unreachable!("--ui requires the gpui feature"); + #[cfg(feature = "gpui-ui")] app::gpui::run(config) } else { app::terminal::run(config).await diff --git a/crates/code_assistant/src/ui/backend.rs b/crates/code_assistant/src/ui/backend.rs index 11b02f97..ddc61dd8 100644 --- a/crates/code_assistant/src/ui/backend.rs +++ b/crates/code_assistant/src/ui/backend.rs @@ -2,9 +2,11 @@ use crate::config::{save_project, DefaultProjectManager}; use crate::persistence::{ChatMetadata, DraftAttachment, SessionModelConfig}; use crate::session::SessionManager; use crate::types::Project; +#[cfg(feature = "gpui-ui")] use crate::ui::gpui::terminal::executor::GpuiTerminalCommandExecutor; use crate::ui::UserInterface; use crate::utils::content::content_blocks_from; +use command_executor::DefaultCommandExecutor; use llm::factory::create_llm_client_from_model; use llm::provider_config::ConfigurationSystem; use sandbox::SandboxPolicy; @@ -649,7 +651,10 @@ async fn handle_send_user_message( // Start the agent (message already added) let result = { let project_manager = Box::new(DefaultProjectManager::new()); + #[cfg(feature = "gpui-ui")] let command_executor = Box::new(GpuiTerminalCommandExecutor::new(session_id.to_string())); + #[cfg(not(feature = "gpui-ui"))] + let command_executor = Box::new(DefaultCommandExecutor); let user_interface = ui.clone(); // Check if session has stored model config, otherwise use global config diff --git a/crates/code_assistant/src/ui/mod.rs b/crates/code_assistant/src/ui/mod.rs index d0a241ac..108ce6de 100644 --- a/crates/code_assistant/src/ui/mod.rs +++ b/crates/code_assistant/src/ui/mod.rs @@ -1,4 +1,5 @@ pub mod backend; +#[cfg(feature = "gpui-ui")] pub mod gpui; pub mod streaming; pub mod terminal; diff --git a/crates/code_assistant/src/ui/ui_events.rs b/crates/code_assistant/src/ui/ui_events.rs index a44c2eed..15751401 100644 --- a/crates/code_assistant/src/ui/ui_events.rs +++ b/crates/code_assistant/src/ui/ui_events.rs @@ -6,6 +6,17 @@ use crate::ui::{DisplayFragment, ToolStatus}; use sandbox::SandboxPolicy; use std::path::PathBuf; +// When the gpui-ui feature is enabled, StyledLine comes from the `terminal` crate +// (which captures ANSI-coloured output from the interactive terminal tool). +// Without gpui-ui the field is always `None`, so we use a zero-sized stub that +// satisfies the type checker without pulling in the Metal dependency chain. +#[cfg(feature = "gpui-ui")] +pub use terminal::StyledLine; + +#[cfg(not(feature = "gpui-ui"))] +#[derive(Debug, Clone)] +pub struct StyledLine; + /// Role of a message in the conversation. #[derive(Debug, Clone, PartialEq)] pub enum MessageRole { @@ -34,7 +45,7 @@ pub struct ToolResultData { pub message: Option, pub output: Option, /// Styled terminal output with ANSI color information preserved. - pub styled_output: Option>, + pub styled_output: Option>, /// Duration of the tool execution in seconds, computed from persisted ContentBlock timestamps. pub duration_seconds: Option, /// Image data from tools that produce visual output (e.g. view_images). @@ -79,7 +90,7 @@ pub enum UiEvent { message: Option, output: Option, /// Styled terminal output with ANSI color information preserved. - styled_output: Option>, + styled_output: Option>, /// Execution duration in seconds, set from ContentBlock timestamps on completion. duration_seconds: Option, /// Image data from tools that produce visual output (e.g. view_images).