From b61d6bcbce093d6674446ab9419e4ab839737000 Mon Sep 17 00:00:00 2001 From: Ryan Ward Date: Wed, 13 May 2026 15:56:40 -0500 Subject: [PATCH] Apply local updates and test/build fixes --- app/src/ai/agent/api/impl.rs | 2 +- app/src/ai/agent/conversation.rs | 23 +- app/src/ai/agent/conversation_tests.rs | 2 +- app/src/ai/bedrock/convert_request.rs | 22 +- app/src/ai/bedrock/convert_request_tests.rs | 47 +++ app/src/ai/bedrock/e2e_tests.rs | 31 +- app/src/ai/bedrock/integration_tests.rs | 1 + app/src/drive/index.rs | 54 ++-- app/src/drive/index_test.rs | 12 +- app/src/server/server_api.rs | 7 + app/src/settings_view/about_page.rs | 5 +- app/src/settings_view/mod_test.rs | 19 +- app/src/terminal/block_list_element.rs | 30 +- app/src/workspace/view.rs | 271 ++++++++---------- .../view/cloud_agent_capacity_modal/mod.rs | 2 +- .../workspace/view/conversation_list/item.rs | 4 +- .../workspace/view/conversation_list/view.rs | 14 +- .../view/free_tier_limit_hit_modal.rs | 2 +- app/src/workspace/view/global_search/model.rs | 2 +- .../view/global_search/model_wasm.rs | 2 +- app/src/workspace/view/global_search/view.rs | 8 +- app/src/workspace/view/launch_modal/mod.rs | 2 +- app/src/workspace/view/left_panel.rs | 16 +- app/src/workspace/view/onboarding.rs | 2 +- .../view/openwarp_launch_modal/mod.rs | 2 +- .../view/openwarp_launch_modal/view.rs | 2 +- app/src/workspace/view/right_panel.rs | 25 +- app/src/workspace/view/startup_directory.rs | 2 +- app/src/workspace/view/vertical_tabs.rs | 36 +-- .../workspace/view/vertical_tabs/telemetry.rs | 2 +- app/src/workspace/view/vertical_tabs_tests.rs | 9 +- app/src/workspace/view/wasm_view.rs | 2 +- app/src/workspace/view_test.rs | 109 ++++--- .../test_save_missing_directory.rs | 1 + .../test_data/test_write/test_save_file.rs | 1 + script/install-galaxy.sh | 123 ++++++++ 36 files changed, 567 insertions(+), 327 deletions(-) create mode 100644 app/src/ai/bedrock/convert_request_tests.rs create mode 100644 crates/galaxy_files/test_data/test_write/missing-directory/test_save_missing_directory.rs create mode 100644 crates/galaxy_files/test_data/test_write/test_save_file.rs create mode 100755 script/install-galaxy.sh diff --git a/app/src/ai/agent/api/impl.rs b/app/src/ai/agent/api/impl.rs index 43a1ba9b..e83a71ef 100644 --- a/app/src/ai/agent/api/impl.rs +++ b/app/src/ai/agent/api/impl.rs @@ -260,7 +260,7 @@ pub async fn generate_multi_agent_output( format!("MultiPart[{}]", part_descs.join(", ")) } }; - log::debug!( + log::info!( "[bedrock] msg[{}]: role={:?}, content={}", i, msg.role, diff --git a/app/src/ai/agent/conversation.rs b/app/src/ai/agent/conversation.rs index d231d901..50a44124 100644 --- a/app/src/ai/agent/conversation.rs +++ b/app/src/ai/agent/conversation.rs @@ -14,7 +14,7 @@ use crate::terminal::model::block::{ }; use crate::ai::agent::api::convert_conversation::{ - ConvertToExchanges, compute_time_to_first_token_ms_from_messages, + compute_time_to_first_token_ms_from_messages, ConvertToExchanges, }; use ai::document::AIDocumentId; use chrono::{DateTime, Local, TimeZone}; @@ -29,8 +29,8 @@ use galaxy_core::execution_mode::AppExecutionMode; use galaxy_core::features::FeatureFlag; use galaxy_core::send_telemetry_from_ctx; use galaxy_core::ui::appearance::Appearance; -use galaxy_core::ui::theme::WarpTheme; use galaxy_core::ui::theme::color::internal_colors; +use galaxy_core::ui::theme::WarpTheme; use galaxyui::color::ColorU; use galaxyui::{EntityId, ModelContext, SingletonEntity}; use uuid::Uuid; @@ -40,35 +40,36 @@ use warp_multi_agent_api::{self as api, response_event::stream_finished::TokenUs use crate::ai::agent::{AIIdentifiers, CancellationReason}; use crate::{ - BlocklistAIHistoryModel, GlobalResourceHandlesProvider, ai::{ agent::{ - AIAgentOutputMessage, AIAgentOutputMessageType, MessageToAIAgentOutputMessageError, icons::{ failed_icon, gray_stop_icon, in_progress_icon, succeeded_icon, yellow_stop_icon, }, todos::AIAgentTodoList, + AIAgentOutputMessage, AIAgentOutputMessageType, MessageToAIAgentOutputMessageError, }, blocklist::BlocklistAIHistoryEvent, }, persistence::{ - ModelEvent, model::{AgentConversationData, PersistedAutoexecuteMode}, + ModelEvent, }, ui_components::icons::Icon, + BlocklistAIHistoryModel, GlobalResourceHandlesProvider, }; use super::task::{ExtractMessagesError, UpdateTaskError, UpgradeOptimisticTaskError}; use super::{ + api::ServerConversationToken, + task::{ + derive_todo_lists_from_root_task, + helper::*, + transaction::{SavedTask, Transaction}, + Task, TaskId, + }, AIAgentAction, AIAgentActionId, AIAgentContext, AIAgentExchange, AIAgentExchangeId, AIAgentInput, AIAgentOutputStatus, AIAgentTodo, AIAgentTodoId, FinishedAIAgentOutput, MessageId, RenderableAIError, RequestCost, - api::ServerConversationToken, - task::{ - Task, TaskId, derive_todo_lists_from_root_task, - helper::*, - transaction::{SavedTask, Transaction}, - }, }; use super::{ AIAgentOutput, OutputModelInfo, ServerOutputId, Shared, SuggestedLoggingId, Suggestions, diff --git a/app/src/ai/agent/conversation_tests.rs b/app/src/ai/agent/conversation_tests.rs index f31fcaa8..8c41e788 100644 --- a/app/src/ai/agent/conversation_tests.rs +++ b/app/src/ai/agent/conversation_tests.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use super::{ - AIConversation, AIConversationAutoexecuteMode, AIConversationId, artifact_from_fork_proto, + artifact_from_fork_proto, AIConversation, AIConversationAutoexecuteMode, AIConversationId, }; use crate::ai::artifacts::Artifact; use crate::persistence::model::AgentConversationData; diff --git a/app/src/ai/bedrock/convert_request.rs b/app/src/ai/bedrock/convert_request.rs index 6f2a289a..e615b8b1 100644 --- a/app/src/ai/bedrock/convert_request.rs +++ b/app/src/ai/bedrock/convert_request.rs @@ -577,10 +577,17 @@ fn ensure_tool_results_paired(messages: &mut Vec) { let insert_idx = i + 1; // If the next message is already a user message, merge synthetic results into it. + // IMPORTANT: ToolResult blocks must come BEFORE text content in a user message + // that follows an assistant tool_use. The Bedrock/Anthropic API validates this + // ordering and rejects requests where text precedes tool_result. if insert_idx < messages.len() && messages[insert_idx].role == MessageRole::User { match &mut messages[insert_idx].content { MessageContent::MultiPart(parts) => { + // Prepend synthetic results before existing parts so + // tool_result blocks appear first in the content. + let existing = std::mem::take(parts); parts.extend(synthetic_results); + parts.extend(existing); } existing => { // Convert existing single content + synthetic results into MultiPart. @@ -607,11 +614,18 @@ fn ensure_tool_results_paired(messages: &mut Vec) { }, MessageContent::MultiPart(_) => unreachable!(), }; - let mut parts = vec![existing_part]; - parts.extend(synthetic_results); + // Synthetic tool_result blocks come first, then the + // original content (text), matching the Bedrock API + // requirement that tool_result precedes other content. + let mut parts = synthetic_results; + parts.push(existing_part); *existing = MessageContent::MultiPart(parts); } } + log::info!( + "[bedrock] Merged synthetic tool_result(s) into existing user message at index {}", + insert_idx + ); } else { // No user message follows — insert a new one. let content = if synthetic_results.len() == 1 { @@ -1272,3 +1286,7 @@ fn format_tool_call_result(result: &api::message::ToolCallResult) -> String { "Tool completed.".to_string() } } + +#[cfg(test)] +#[path = "convert_request_tests.rs"] +mod tests; diff --git a/app/src/ai/bedrock/convert_request_tests.rs b/app/src/ai/bedrock/convert_request_tests.rs new file mode 100644 index 00000000..e72d7de0 --- /dev/null +++ b/app/src/ai/bedrock/convert_request_tests.rs @@ -0,0 +1,47 @@ +use serde_json::json; + +use super::super::convert::{ContentPart, ConversationMessage, MessageContent, MessageRole}; +use super::sanitize_messages_for_bedrock; + +#[test] +fn test_sanitize_messages_prepends_synthetic_tool_result_before_existing_user_text() { + let tool_use_id = "tooluse_Pzmn1QfoWgJsA8sb4RHTM3".to_string(); + let existing_user_text = "What happened?".to_string(); + + let mut messages = vec![ + ConversationMessage { + role: MessageRole::User, + content: MessageContent::Text("Run a command.".to_string()), + }, + ConversationMessage { + role: MessageRole::Assistant, + content: MessageContent::ToolUse { + tool_use_id: tool_use_id.clone(), + name: "run_shell_command".to_string(), + input: json!({ "command": "ls" }), + }, + }, + ConversationMessage { + role: MessageRole::User, + content: MessageContent::Text(existing_user_text.clone()), + }, + ]; + + sanitize_messages_for_bedrock(&mut messages); + + assert_eq!(messages.len(), 3); + + let parts = match &messages[2].content { + MessageContent::MultiPart(parts) => parts, + other => panic!("Expected MultiPart content, got: {:?}", other), + }; + + assert_eq!(parts.len(), 2); + assert!( + matches!(&parts[0], ContentPart::ToolResult { tool_use_id: id, .. } if id == &tool_use_id) + ); + assert!(matches!( + &parts[1], + ContentPart::Text(text) if text == &existing_user_text + )); +} diff --git a/app/src/ai/bedrock/e2e_tests.rs b/app/src/ai/bedrock/e2e_tests.rs index da6f7126..9609a282 100644 --- a/app/src/ai/bedrock/e2e_tests.rs +++ b/app/src/ai/bedrock/e2e_tests.rs @@ -555,6 +555,7 @@ impl AgentSimulation { 8192, None, false, + None, Arc::new(Mutex::new(Vec::new())), ) .await @@ -1140,6 +1141,7 @@ async fn test_reasoning_model_produces_substantial_output() { 8192, None, false, + None, Arc::new(Mutex::new(Vec::new())), ) .await @@ -1261,6 +1263,7 @@ async fn test_event_sequence_matches_controller_expectations() { 100, None, false, + None, Arc::new(Mutex::new(Vec::new())), ) .await @@ -1374,6 +1377,7 @@ async fn test_followup_turn_does_not_send_create_task() { 100, None, false, + None, Arc::new(Mutex::new(Vec::new())), ) .await @@ -1438,7 +1442,11 @@ async fn run_slash_command_test( let task_id = uuid::Uuid::new_v4().to_string(); println!( "\n{}\n[SLASH CMD TEST] {}\n[SLASH CMD TEST] Model: {}\n[SLASH CMD TEST] Message: {:.100}\n{}", - "=".repeat(80), test_name, model, user_message, "=".repeat(80) + "=".repeat(80), + test_name, + model, + user_message, + "=".repeat(80) ); let stream = client @@ -1452,6 +1460,7 @@ async fn run_slash_command_test( 4096, None, true, + None, Arc::new(Mutex::new(Vec::new())), ) .await @@ -1678,6 +1687,7 @@ async fn test_slash_resume_conversation() { 256, None, true, + None, Arc::new(Mutex::new(Vec::new())), ) .await @@ -1788,6 +1798,7 @@ async fn test_empty_messages_safety_check() { 100, None, true, + None, Arc::new(Mutex::new(Vec::new())), ) .await @@ -1829,17 +1840,26 @@ async fn test_full_proto_round_trip_with_tool_history() { let request = make_request( &task_id, vec![ - make_user_query_message("msg-1", &task_id, "List all files in the current directory."), + make_user_query_message( + "msg-1", + &task_id, + "List all files in the current directory.", + ), make_tool_call_run_shell(tool_call_id_1, &task_id, tool_call_id_1, "ls -la"), make_tool_call_read_files(tool_call_id_2, &task_id, tool_call_id_2, "README.md"), make_tool_result_shell( - "msg-result-1", &task_id, tool_call_id_1, + "msg-result-1", + &task_id, + tool_call_id_1, "total 16\ndrwxr-xr-x 5 user staff 160 May 6 10:00 .\n-rw-r--r-- 1 user staff 100 May 6 10:00 README.md\n-rw-r--r-- 1 user staff 200 May 6 10:00 Cargo.toml", 0, ), make_tool_result_read_files( - "msg-result-2", &task_id, tool_call_id_2, - "README.md", "# Sample Project\nA test project.", + "msg-result-2", + &task_id, + tool_call_id_2, + "README.md", + "# Sample Project\nA test project.", ), ], make_user_inputs_input("Now summarize what you found in one sentence."), @@ -1938,6 +1958,7 @@ async fn test_full_proto_round_trip_with_tool_history() { 1024, None, true, + None, Arc::new(Mutex::new(Vec::new())), ) .await; diff --git a/app/src/ai/bedrock/integration_tests.rs b/app/src/ai/bedrock/integration_tests.rs index 32a5d77a..400b0fe5 100644 --- a/app/src/ai/bedrock/integration_tests.rs +++ b/app/src/ai/bedrock/integration_tests.rs @@ -64,6 +64,7 @@ async fn collect_stream_output( 8192, None, false, + None, Arc::new(Mutex::new(Vec::new())), ) .await diff --git a/app/src/drive/index.rs b/app/src/drive/index.rs index 17102f72..017ce5d7 100644 --- a/app/src/drive/index.rs +++ b/app/src/drive/index.rs @@ -1,25 +1,26 @@ #[cfg(target_family = "wasm")] use crate::uri::web_intent_parser::open_url_on_desktop; use crate::{ + ObjectActions, ai::{ document::ai_document_model::AIDocumentId, facts::{AIFact, AIMemory}, }, appearance::Appearance, auth::{ + AuthStateProvider, auth_manager::{AuthManager, LoginGatedFeature}, auth_state::AuthState, auth_view_modal::AuthViewVariant, - AuthStateProvider, }, cloud_object::{ + CloudObject, CloudObjectEventEntrypoint, CloudObjectLocation, CloudObjectSyncStatus, + GenericCloudObject, GenericStringObjectFormat, JsonObjectType, NumInFlightRequests, + ObjectType, Space, model::{ persistence::{CloudModel, CloudModelEvent}, view::{CloudViewModel, CloudViewModelEvent, UpdateTimestamp}, }, - CloudObject, CloudObjectEventEntrypoint, CloudObjectLocation, CloudObjectSyncStatus, - GenericCloudObject, GenericStringObjectFormat, JsonObjectType, NumInFlightRequests, - ObjectType, Space, }, editor::{EditorView, Event as EditorEvent, SingleLineEditorOptions}, env_vars::CloudEnvVarCollection, @@ -38,8 +39,8 @@ use crate::{ ui_components::{ blended_colors, buttons::{highlight, icon_button}, - icons::{Icon, ICON_DIMENSIONS}, - menu_button::{icon_button_with_context_menu, MenuDirection}, + icons::{ICON_DIMENSIONS, Icon}, + menu_button::{MenuDirection, icon_button_with_context_menu}, }, util::{color::coloru_with_opacity, sync::Condition}, view_components::{Dropdown, DropdownItem}, @@ -48,10 +49,10 @@ use crate::{ workspaces::{ update_manager::TeamUpdateManager, user_workspaces::UserWorkspaces, workspace::WorkspaceUid, }, - ObjectActions, }; use super::{ + CloudObjectTypeAndId, DriveObjectType, DriveSortOrder, cloud_object_naming_dialog::CloudObjectNamingDialog, drive_helpers::{ has_feature_gated_anonymous_user_reached_env_var_limit, @@ -61,17 +62,16 @@ use super::{ empty_trash_confirmation_dialog::{EmptyTrashConfirmationDialog, EmptyTrashConfirmationEvent}, folders::CloudFolder, items::{ - ai_fact_collection::WarpDriveAIFactCollection, - item::{tools_panel_menu_direction, ItemStates, WarpDriveRow}, - mcp_server_collection::WarpDriveMCPServerCollection, WarpDriveItemId, + ai_fact_collection::WarpDriveAIFactCollection, + item::{ItemStates, WarpDriveRow, tools_panel_menu_direction}, + mcp_server_collection::WarpDriveMCPServerCollection, }, settings::WarpDriveSettings, sharing::{ - dialog::{SharingDialog, SharingDialogEvent}, ContentEditability, ShareableObject, + dialog::{SharingDialog, SharingDialogEvent}, }, - CloudObjectTypeAndId, DriveObjectType, DriveSortOrder, }; use crate::drive::panel::DrivePanelAction; use crate::server::cloud_objects::update_manager::InitiatedBy; @@ -80,6 +80,8 @@ use galaxy_core::{ context_flag::ContextFlag, settings::Setting, ui::theme::color::internal_colors, }; use galaxyui::{ + AppContext, BlurContext, Element, Entity, ModelHandle, SingletonEntity, TypedActionView, + UpdateView, View, ViewContext, ViewHandle, WindowId, clipboard::ClipboardContent, elements::{ Align, AnchorPair, Border, ChildAnchor, ChildView, ClippedScrollStateHandle, @@ -98,12 +100,10 @@ use galaxyui::{ components::{Coords, UiComponent, UiComponentStyles}, }, units::IntoPixels, - AppContext, BlurContext, Element, Entity, ModelHandle, SingletonEntity, TypedActionView, - UpdateView, View, ViewContext, ViewHandle, WindowId, }; use itertools::Itertools; use pathfinder_color::ColorU; -use pathfinder_geometry::vector::{vec2f, Vector2F}; +use pathfinder_geometry::vector::{Vector2F, vec2f}; use std::{any::Any, collections::HashMap, sync::Arc}; use url::Url; @@ -651,7 +651,7 @@ impl DriveIndex { .as_ref(ctx) .num_trashed_cloud_objects_per_space(spaces.iter(), ctx), }; - let mut sections = spaces + let sections = spaces .iter() .map(|space| DriveIndexSection::Space(*space)) .collect::>(); @@ -1254,7 +1254,9 @@ impl DriveIndex { { self.expand_section_for_object(&id.uid().clone(), ctx); } else { - log::warn!("unknown GenericStringObject type found while trying to manually expand drive section. {object_id:?}"); + log::warn!( + "unknown GenericStringObject type found while trying to manually expand drive section. {object_id:?}" + ); } } }; @@ -4906,12 +4908,14 @@ impl DriveIndex { space: *space, offset, }); - let menu_items = vec![MenuItemFields::new("Collapse all") - .with_on_select_action(DriveIndexAction::CollapseAllInLocation( - CloudObjectLocation::Space(*space), - )) - .with_icon(Icon::ListCollapsed) - .into_item()]; + let menu_items = vec![ + MenuItemFields::new("Collapse all") + .with_on_select_action(DriveIndexAction::CollapseAllInLocation( + CloudObjectLocation::Space(*space), + )) + .with_icon(Icon::ListCollapsed) + .into_item(), + ]; ctx.update_view(&self.menu, |menu, ctx| { menu.set_items(menu_items, ctx); @@ -5399,7 +5403,9 @@ impl TypedActionView for DriveIndex { log::error!("Creation of EnvVarCollections is not yet supported") } DriveObjectType::AIFact | DriveObjectType::AIFactCollection => { - log::error!("Use DriveIndexAction::OpenAIFactCollection to open the pane view instead"); + log::error!( + "Use DriveIndexAction::OpenAIFactCollection to open the pane view instead" + ); } DriveObjectType::MCPServer | DriveObjectType::MCPServerCollection => { log::error!( diff --git a/app/src/drive/index_test.rs b/app/src/drive/index_test.rs index 1d62d035..5922819c 100644 --- a/app/src/drive/index_test.rs +++ b/app/src/drive/index_test.rs @@ -1,17 +1,18 @@ use galaxy_core::ui::appearance::Appearance; use galaxy_server_client::cloud_object::ServerPermissions; use galaxyui::{ - platform::WindowStyle, AddSingletonModel, App, SingletonEntity, TypedActionView, ViewHandle, + AddSingletonModel, App, SingletonEntity, TypedActionView, ViewHandle, platform::WindowStyle, }; use crate::{ + Assets, ai::blocklist::BlocklistAIHistoryModel, - auth::{auth_manager::AuthManager, AuthStateProvider}, + auth::{AuthStateProvider, auth_manager::AuthManager}, cloud_object::{ - model::{actions::ObjectActions, persistence::CloudModel, view::CloudViewModel}, CloudObjectSyncStatus, ObjectIdType, ObjectType, Owner, ServerCreationInfo, Space, + model::{actions::ObjectActions, persistence::CloudModel, view::CloudViewModel}, }, - drive::{items::WarpDriveItemId, CloudObjectTypeAndId}, + drive::{CloudObjectTypeAndId, items::WarpDriveItemId}, menu::MenuItem, network::NetworkStatus, notebooks::{CloudNotebook, CloudNotebookModel}, @@ -25,11 +26,10 @@ use crate::{ settings_view::keybindings::KeybindingChangedNotifier, terminal::shared_session::permissions_manager::SessionPermissionsManager, test_util::settings::initialize_settings_for_tests, - workflows::{workflow::Workflow, CloudWorkflow, CloudWorkflowModel}, + workflows::{CloudWorkflow, CloudWorkflowModel, workflow::Workflow}, workspaces::{ team_tester::TeamTesterStatus, user_profiles::UserProfiles, user_workspaces::UserWorkspaces, }, - Assets, }; use super::{DriveIndex, DriveIndexAction}; diff --git a/app/src/server/server_api.rs b/app/src/server/server_api.rs index 014447d7..2928013b 100644 --- a/app/src/server/server_api.rs +++ b/app/src/server/server_api.rs @@ -303,6 +303,13 @@ impl AIApiError { } true } + // Don't retry Bedrock validation errors — they are deterministic + // and will always fail with the same request payload. + AIApiError::Stream { source, .. } + if source.to_string().contains("Validation error") => + { + false + } // By default, retry on error. _ => true, } diff --git a/app/src/settings_view/about_page.rs b/app/src/settings_view/about_page.rs index fb17dfb0..8d3b820f 100644 --- a/app/src/settings_view/about_page.rs +++ b/app/src/settings_view/about_page.rs @@ -1,19 +1,19 @@ use super::{ + SettingsSection, settings_page::{ MatchData, PageType, SettingsPageEvent, SettingsPageMeta, SettingsPageViewHandle, SettingsWidget, }, - SettingsSection, }; use crate::{appearance::Appearance, channel::ChannelState, workspace::WorkspaceAction}; use galaxyui::{ + AppContext, Entity, View, ViewContext, ViewHandle, assets::asset_cache::AssetSource, elements::{ Align, CacheOption, ConstrainedBox, Container, CrossAxisAlignment, Element, Flex, Image, MainAxisAlignment, MouseStateHandle, ParentElement, Wrap, }, ui_components::components::UiComponent, - AppContext, Entity, View, ViewContext, ViewHandle, }; pub struct AboutPageView { @@ -60,7 +60,6 @@ impl SettingsWidget for AboutPageWidget { appearance: &Appearance, _app: &AppContext, ) -> Box { - let theme = appearance.theme(); let ui_builder = appearance.ui_builder(); let image_path = "bundled/svg/galaxy-logo.svg"; diff --git a/app/src/settings_view/mod_test.rs b/app/src/settings_view/mod_test.rs index 24f47847..1aa5dbf9 100644 --- a/app/src/settings_view/mod_test.rs +++ b/app/src/settings_view/mod_test.rs @@ -26,12 +26,12 @@ fn code_subpages_are_identified() { } #[test] -fn cloud_platform_subpages_are_identified() { - assert!(SettingsSection::CloudEnvironments.is_cloud_platform_subpage()); - assert!(SettingsSection::OzCloudAPIKeys.is_cloud_platform_subpage()); +fn legacy_cloud_platform_sections_are_not_subpages() { + assert!(!SettingsSection::CloudEnvironments.is_subpage()); + assert!(!SettingsSection::OzCloudAPIKeys.is_subpage()); - assert!(!SettingsSection::Account.is_cloud_platform_subpage()); - assert!(!SettingsSection::WarpAgent.is_cloud_platform_subpage()); + assert!(!SettingsSection::Account.is_subpage()); + assert!(!SettingsSection::WarpAgent.is_subpage()); } #[test] @@ -42,8 +42,8 @@ fn is_subpage_covers_all_umbrella_types() { } assert!(SettingsSection::CodeIndexing.is_subpage()); assert!(SettingsSection::EditorAndCodeReview.is_subpage()); - assert!(SettingsSection::CloudEnvironments.is_subpage()); - assert!(SettingsSection::OzCloudAPIKeys.is_subpage()); + assert!(!SettingsSection::CloudEnvironments.is_subpage()); + assert!(!SettingsSection::OzCloudAPIKeys.is_subpage()); // Top-level pages should not be subpages. assert!(!SettingsSection::Account.is_subpage()); @@ -647,7 +647,10 @@ fn realistic_nav_items() -> Vec { )), SettingsNavItem::Umbrella(SettingsUmbrella::new( "Cloud platform", - SettingsSection::cloud_platform_subpages().to_vec(), + vec![ + SettingsSection::CloudEnvironments, + SettingsSection::OzCloudAPIKeys, + ], )), SettingsNavItem::Page(SettingsSection::Teams), ] diff --git a/app/src/terminal/block_list_element.rs b/app/src/terminal/block_list_element.rs index 30136981..3a9b503b 100644 --- a/app/src/terminal/block_list_element.rs +++ b/app/src/terminal/block_list_element.rs @@ -1,6 +1,5 @@ -use crate::BlocklistAIHistoryModel; -use crate::ai::blocklist::agent_view::{AgentViewState, agent_view_bg_fill}; -use crate::ai::blocklist::{ATTACH_AS_AGENT_MODE_CONTEXT_TEXT, ai_brand_color}; +use crate::ai::blocklist::agent_view::{agent_view_bg_fill, AgentViewState}; +use crate::ai::blocklist::{ai_brand_color, ATTACH_AS_AGENT_MODE_CONTEXT_TEXT}; use crate::ai_assistant::{AI_ASSISTANT_SVG_PATH, ASK_AI_ASSISTANT_TEXT}; use crate::appearance::Appearance; use crate::drive::settings::WarpDriveSettings; @@ -20,10 +19,11 @@ use crate::terminal::model::index::Point as IndexPoint; use crate::terminal::model::selection::{SelectAction, SelectionPoint}; use crate::terminal::safe_mode_settings::get_secret_obfuscation_mode; use crate::terminal::view::TerminalAction; -use crate::terminal::{SizeInfo, grid_renderer}; +use crate::terminal::{grid_renderer, SizeInfo}; use crate::themes::theme::{Fill, WarpTheme}; use crate::ui_components::{self, icons as UIIcon}; use crate::util::color::Opacity; +use crate::BlocklistAIHistoryModel; use enum_iterator::Sequence; use galaxy_core::semantic_selection::SemanticSelection; use galaxy_core::ui::builder::UiBuilder; @@ -44,15 +44,15 @@ use galaxyui::elements::{ use galaxyui::event::{KeyState, ModifiersState}; use galaxyui::fonts::{FamilyId, Properties, Weight}; use galaxyui::geometry::rect::RectF; -use galaxyui::geometry::vector::{Vector2F, vec2f}; +use galaxyui::geometry::vector::{vec2f, Vector2F}; use galaxyui::platform::keyboard::KeyCode; use galaxyui::ui_components::components::UiComponent; use galaxyui::units::{IntoLines, IntoPixels, Lines, Pixels}; +use galaxyui::{elements::Icon, ClipBounds}; use galaxyui::{ - AfterLayoutContext, AppContext, Element, Event, EventContext, LayoutContext, PaintContext, - SizeConstraint, elements::SavePosition, event::DispatchedEvent, + elements::SavePosition, event::DispatchedEvent, AfterLayoutContext, AppContext, Element, Event, + EventContext, LayoutContext, PaintContext, SizeConstraint, }; -use galaxyui::{ClipBounds, elements::Icon}; use galaxyui::{EntityId, ModelHandle, SingletonEntity as _}; use pathfinder_color::ColorU; use session_sharing_protocol::common::{ParticipantId, Selection}; @@ -69,9 +69,7 @@ use super::blockgrid_renderer::GridRenderParams; use super::find::{BlockListFindRun, BlockListMatch, TerminalFindModel}; use super::grid_renderer::CellGlyphCache; -use super::TerminalModel; use super::meta_shortcuts::handle_keystroke_despite_composing; -use super::model::SecretHandle; use super::model::block::BlockId; use super::model::blocks::{RichContentItem, SelectionRange}; use super::model::grid::grid_handler::{Link, TermMode}; @@ -79,22 +77,24 @@ use super::model::image_map::StoredImageMetadata; use super::model::mouse::{MouseAction, MouseButton, MouseState}; use super::model::session::SessionId; use super::model::terminal_model::{SelectedBlocks, WithinBlock, WithinModel}; +use super::model::SecretHandle; use super::shared_session::presence_manager::{ - MUTED_PARTICIPANT_COLOR, PresenceManager, text_selection_color, + text_selection_color, PresenceManager, MUTED_PARTICIPANT_COLOR, }; use super::shared_session::render_util::SHARED_SESSION_AVATAR_DIAMETER; use super::view::{ - BLOCK_BANNER_HEIGHT, BlocklistAIRenderContext, InlineBannerId, RichContentMetadata, - SeparatorId, SharedSessionBanners, TerminalEditor, TerminalViewRenderContext, + BlocklistAIRenderContext, InlineBannerId, RichContentMetadata, SeparatorId, + SharedSessionBanners, TerminalEditor, TerminalViewRenderContext, BLOCK_BANNER_HEIGHT, }; use super::warpify::render::{draw_flag_pole, render_subshell_flag}; -use super::{HEIGHT_FUDGE_FACTOR_LINES, heights_approx_eq}; +use super::TerminalModel; +use super::{heights_approx_eq, HEIGHT_FUDGE_FACTOR_LINES}; use crate::terminal::blockgrid_renderer::BlockGridParams; use crate::terminal::model::terminal_model::BlockIndex; use crate::terminal::warpify::SubshellSource; use crate::terminal::model::escape_sequences::{ - KeystrokeWithDetails, ToEscapeSequence, maybe_kitty_keyboard_escape_sequence, + maybe_kitty_keyboard_escape_sequence, KeystrokeWithDetails, ToEscapeSequence, }; /// The number of pixels at the bottom of padding where selection scrolling is performed. diff --git a/app/src/workspace/view.rs b/app/src/workspace/view.rs index 729d4278..f669be34 100644 --- a/app/src/workspace/view.rs +++ b/app/src/workspace/view.rs @@ -21,26 +21,27 @@ mod wasm_view; use self::vertical_tabs::telemetry::{VerticalTabsDisplayOption, VerticalTabsTelemetryEvent}; use self::vertical_tabs::{ - render_detail_sidecar, render_settings_popup, VerticalTabsPanelState, - VERTICAL_TABS_SETTINGS_BUTTON_POSITION_ID, + VERTICAL_TABS_SETTINGS_BUTTON_POSITION_ID, VerticalTabsPanelState, render_detail_sidecar, + render_settings_popup, }; pub(crate) use onboarding::OnboardingTutorial; +use crate::ai::AIRequestUsageModel; use crate::ai::active_agent_views_model::ActiveAgentViewsModel; use crate::ai::agent_conversations_model::AgentConversationsModel; use crate::ai::agent_conversations_model::ConversationOrTask; +use crate::ai::agent_management::AgentManagementEvent; +use crate::ai::agent_management::notifications::NotificationFilter; use crate::ai::agent_management::notifications::toast_stack::AgentNotificationToastStack; use crate::ai::agent_management::notifications::view::{ NotificationMailboxView, NotificationMailboxViewEvent, }; -use crate::ai::agent_management::notifications::NotificationFilter; use crate::ai::agent_management::telemetry::AgentManagementTelemetryEvent; use crate::ai::agent_management::view::{AgentManagementView, AgentManagementViewEvent}; -use crate::ai::agent_management::AgentManagementEvent; -use crate::ai::ambient_agents::telemetry::{CloudAgentTelemetryEvent, CloudModeEntryPoint}; use crate::ai::ambient_agents::AmbientAgentTaskId; -use crate::ai::blocklist::agent_view::agent_input_footer::editor::AgentToolbarEditorMode; +use crate::ai::ambient_agents::telemetry::{CloudAgentTelemetryEvent, CloudModeEntryPoint}; use crate::ai::blocklist::agent_view::AgentViewEntryOrigin; +use crate::ai::blocklist::agent_view::agent_input_footer::editor::AgentToolbarEditorMode; use crate::ai::blocklist::history_model::load_conversation_from_server; use crate::ai::blocklist::suggested_agent_mode_workflow_modal::SuggestedAgentModeWorkflowAndId; use crate::ai::blocklist::suggested_rule_modal::{ @@ -50,17 +51,16 @@ use crate::ai::conversation_utils; use crate::ai::document::ai_document_model::{AIDocumentId, AIDocumentModel}; use crate::ai::llms::LLMPreferences; use crate::ai::persisted_workspace::PersistedWorkspace; -use crate::ai::AIRequestUsageModel; use crate::ai::{ - agent::{api::ServerConversationToken, conversation::AIConversationId, EntrypointType}, + agent::{EntrypointType, api::ServerConversationToken, conversation::AIConversationId}, blocklist::{ + SlashCommandRequest, inline_action::code_diff_view::CodeDiffView, suggested_agent_mode_workflow_modal::{ SuggestedAgentModeWorkflowModal, SuggestedAgentModeWorkflowModalEvent, }, - SlashCommandRequest, }, - facts::{view::AIFactPage, AIFactManager, AIFactView, AIFactViewEvent}, + facts::{AIFactManager, AIFactView, AIFactViewEvent, view::AIFactPage}, }; use crate::ai_assistant::execution_context::WarpAiExecutionContext; use crate::app_state::{ @@ -68,10 +68,10 @@ use crate::app_state::{ PaneNodeSnapshot, PaneUuid, RightPanelSnapshot, SettingsPaneSnapshot, TabSnapshot, TerminalPaneSnapshot, WindowSnapshot, WorkflowPaneSnapshot, }; -use crate::code_review::diff_state::DiffStateModel; #[cfg(feature = "local_fs")] use crate::code_review::CodeReviewTelemetryEvent; use crate::code_review::GlobalCodeReviewModel; +use crate::code_review::diff_state::DiffStateModel; use crate::coding_panel_enablement_state::CodingPanelEnablementState; use crate::default_terminal::DefaultTerminal; use crate::notebooks::CloudNotebook; @@ -108,12 +108,13 @@ use crate::util::file::external_editor::Editor; use crate::util::file::external_editor::EditorSettings; use crate::util::openable_file_type::FileTarget; #[cfg(feature = "local_fs")] -use crate::util::openable_file_type::{resolve_file_target_with_editor_choice, EditorLayout}; +use crate::util::openable_file_type::{EditorLayout, resolve_file_target_with_editor_choice}; -use crate::ai::blocklist::history_model::CloudConversationData; +use crate::BlocklistAIHistoryModel; use crate::ai::blocklist::FORK_PREFIX; +use crate::ai::blocklist::history_model::CloudConversationData; #[cfg(not(target_family = "wasm"))] -use crate::terminal::cli_agent_sessions::plugin_manager::{plugin_manager_for, PluginModalKind}; +use crate::terminal::cli_agent_sessions::plugin_manager::{PluginModalKind, plugin_manager_for}; use crate::terminal::cli_agent_sessions::{CLIAgentSessionsModel, CLIAgentSessionsModelEvent}; use crate::workspace::header_toolbar_editor::{HeaderToolbarEditorEvent, HeaderToolbarEditorModal}; use crate::workspace::header_toolbar_item::HeaderToolbarItemKind; @@ -133,19 +134,18 @@ use crate::workspace::view::openwarp_launch_modal::{ OpenWarpLaunchModal, OpenWarpLaunchModalEvent, }; use crate::workspace::{ForkFromExchange, ForkedConversationDestination}; -use crate::BlocklistAIHistoryModel; use ai::index::full_source_code_embedding::manager::CodebaseIndexManager; use galaxyui::notification::NotificationSendError; #[cfg(all(target_os = "macos", feature = "crash_reporting"))] use sentry::protocol::{Attachment, AttachmentType}; use serde_json; +use super::WorkspaceRegistry; use super::hoa_onboarding::{ - mark_hoa_onboarding_completed, HoaOnboardingFlow, HoaOnboardingFlowEvent, HoaOnboardingStep, + HoaOnboardingFlow, HoaOnboardingFlowEvent, HoaOnboardingStep, mark_hoa_onboarding_completed, }; use super::lightbox_view::{LightboxParams, LightboxView, LightboxViewEvent}; use super::util; -use super::WorkspaceRegistry; use crate::ai::execution_profiles::editor::ExecutionProfileEditorManager; use crate::ai::execution_profiles::profiles::{AIExecutionProfilesModel, ClientProfileId}; use crate::auth::auth_manager::{AuthManager, AuthManagerEvent}; @@ -211,15 +211,15 @@ use crate::wasm_nux_dialog::WasmNUXDialog; use crate::drive::items::WarpDriveItemId; use crate::drive::settings::WarpDriveSettingsChangedEvent; use crate::env_vars::{ - manager::{EnvVarCollectionManager, EnvVarCollectionSource}, CloudEnvVarCollection, + manager::{EnvVarCollectionManager, EnvVarCollectionSource}, }; use crate::settings::cloud_preferences::CloudPreferencesSettings; use crate::appearance::{Appearance, AppearanceManager}; use crate::auth::AuthStateProvider; use crate::autoupdate::{ - is_incoming_version_past_current, AutoupdateState, AutoupdateStateEvent, RelaunchModel, + AutoupdateState, AutoupdateStateEvent, RelaunchModel, is_incoming_version_past_current, }; use crate::banner::BannerState; use crate::changelog_model::{ChangelogModel, ChangelogRequestType, Event as ChangelogEvent}; @@ -237,8 +237,8 @@ use crate::drive::{ }; use crate::experiments::{BlockOnboarding, Experiment}; use crate::menu::{ - Event as MenuEvent, Menu, MenuItem, MenuItemFields, MenuSelectionSource, - DEFAULT_WIDTH as MENU_DEFAULT_WIDTH, + DEFAULT_WIDTH as MENU_DEFAULT_WIDTH, Event as MenuEvent, Menu, MenuItem, MenuItemFields, + MenuSelectionSource, }; use crate::modal::{Modal, ModalEvent, ModalViewState}; use crate::network::{NetworkStatus, NetworkStatusEvent}; @@ -262,11 +262,11 @@ use crate::prompt::editor_modal::{ }; use crate::referral_theme_status::ReferralThemeEvent; use crate::resource_center::{ - mark_feature_used_and_write_to_user_defaults, skip_tips_and_write_to_user_defaults, ResourceCenterEvent, ResourceCenterPage, ResourceCenterView, Tip, TipAction, TipsCompleted, + mark_feature_used_and_write_to_user_defaults, skip_tips_and_write_to_user_defaults, }; use crate::reward_view::{RewardEvent, RewardKind, RewardView}; -use crate::root_view::{quake_mode_window_id, NewWorkspaceSource, OpenLaunchConfigArg}; +use crate::root_view::{NewWorkspaceSource, OpenLaunchConfigArg, quake_mode_window_id}; use crate::search::command_search::searcher::{ AcceptedHistoryItem, AcceptedWorkflow, CommandSearchItemAction, }; @@ -284,10 +284,10 @@ use crate::server::telemetry::{ }; use crate::session_management::{SessionNavigationData, SessionSource}; use crate::settings::{ - active_theme_kind, respect_system_theme, AccessibilitySettings, AliasExpansionSettings, - AppEditorSettings, BlockVisibilitySettings, ChangelogSettings, CursorBlink, DebugSettings, - FontSettings, GPUSettings, InputSettings, MonospaceFontSize, PaneSettings, PrivacySettings, - SelectionSettings, Settings, SshSettings, ThemeSettings, + AccessibilitySettings, AliasExpansionSettings, AppEditorSettings, BlockVisibilitySettings, + ChangelogSettings, CursorBlink, DebugSettings, FontSettings, GPUSettings, InputSettings, + MonospaceFontSize, PaneSettings, PrivacySettings, SelectionSettings, Settings, SshSettings, + ThemeSettings, active_theme_kind, respect_system_theme, }; use crate::settings_view::flags; use crate::settings_view::keybindings::{KeybindingChangedEvent, KeybindingChangedNotifier}; @@ -301,7 +301,7 @@ use crate::terminal::model::blockgrid::BlockGrid; use crate::terminal::model::session::Session; use crate::terminal::model::session::SessionId; use crate::terminal::resizable_data::{ - ModalSizes, ModalType, ResizableData, DEFAULT_LEFT_PANEL_WIDTH, DEFAULT_RIGHT_PANEL_WIDTH, + DEFAULT_LEFT_PANEL_WIDTH, DEFAULT_RIGHT_PANEL_WIDTH, ModalSizes, ModalType, ResizableData, }; use crate::terminal::safe_mode_settings::SafeModeSettings; use crate::terminal::session_settings::{ @@ -316,13 +316,13 @@ use crate::terminal::{self, SizeInfo, TerminalView}; #[cfg(target_os = "macos")] use crate::workspace::cli_install; use crate::workspaces::user_workspaces::UserWorkspaces; -use crate::{report_if_error, AgentNotificationsModel}; +use crate::{AgentNotificationsModel, report_if_error}; use ::settings::{Setting, ToggleableSetting}; use galaxy_core::features::FeatureFlag; use crate::search::{self, QueryFilter}; use crate::terminal::view::{ - SyncEvent, SyncInputType, TerminalAction, NOTIFICATIONS_TROUBLESHOOT_URL, + NOTIFICATIONS_TROUBLESHOOT_URL, SyncEvent, SyncInputType, TerminalAction, }; use crate::terminal::{BlockListSettings, TerminalModel}; use crate::themes::theme::{AnsiColorIdentifier, RespectSystemTheme, ThemeKind}; @@ -332,31 +332,31 @@ use crate::themes::theme_deletion_modal::{ThemeDeletionModal, ThemeDeletionModal use crate::tips::{TipsEvent, TipsView}; use crate::ui_components::buttons::{combo_inner_button, icon_button_with_color}; use crate::undo_close::UndoCloseStack; +use crate::user_config::{WarpConfig, WarpConfigUpdateEvent}; #[cfg(feature = "local_fs")] use crate::user_config::{ ensure_default_worktree_config, find_unused_tab_config_path, find_unused_toml_path, find_unused_worktree_config_path, materialize_default_worktree_config, sanitize_toml_base_name, tab_configs_dir, }; -use crate::user_config::{WarpConfig, WarpConfigUpdateEvent}; use crate::util::bindings::{ keybinding_name_to_display_string, keybinding_name_to_keystroke, trigger_to_keystroke, }; use crate::util::links; -use crate::util::traffic_lights::{traffic_light_data, TrafficLightMouseStates, TrafficLightSide}; +use crate::util::traffic_lights::{TrafficLightMouseStates, TrafficLightSide, traffic_light_data}; use crate::util::truncation::truncate_from_end; #[cfg(target_family = "wasm")] use crate::view_components::action_button::ActionButton; use crate::view_components::callout_bubble::{ - render_callout_bubble, CalloutArrowDirection, CalloutArrowPosition, CalloutBubbleConfig, + CalloutArrowDirection, CalloutArrowPosition, CalloutBubbleConfig, render_callout_bubble, }; use crate::view_components::{ AgentToast, AgentToastStack, DismissibleToast, DismissibleToastStack, ToastLink, }; use crate::window_settings::{WindowSettings, WindowSettingsChangedEvent, ZoomLevel}; use crate::workflows::{ - manager::WorkflowOpenSource, AIWorkflowOrigin, CloudWorkflow, WorkflowSelectionSource, - WorkflowSource, WorkflowType, WorkflowViewMode, + AIWorkflowOrigin, CloudWorkflow, WorkflowSelectionSource, WorkflowSource, WorkflowType, + WorkflowViewMode, manager::WorkflowOpenSource, }; use crate::workspace::action::CommandSearchOptions; use crate::workspace::one_time_modal_model::OneTimeModalModel; @@ -364,20 +364,20 @@ use crate::workspace::sync_inputs::SyncedInputState; use crate::workspace::toast_stack::{ ToastStack as WorkspaceToastStack, ToastStackEvent as WorkspaceToastStackEvent, }; +use crate::{GlobalResourceHandles, send_telemetry_from_ctx}; use crate::{ ai_assistant::{ + AI_ASSISTANT_FEATURE_NAME, AI_ASSISTANT_LOGO_COLOR, AskAIType, panel::{AIAssistantPanelEvent, AIAssistantPanelView}, - AskAIType, AI_ASSISTANT_FEATURE_NAME, AI_ASSISTANT_LOGO_COLOR, }, settings, ui_components::blended_colors, }; -use crate::{send_telemetry_from_ctx, GlobalResourceHandles}; use futures::Future; use galaxy_core::context_flag::ContextFlag; use galaxy_core::semantic_selection::SemanticSelection; -use galaxy_util::path::{user_friendly_path, LineAndColumnArg}; +use galaxy_util::path::{LineAndColumnArg, user_friendly_path}; use galaxyui::fonts::Weight; use galaxyui::modals::{AlertDialogWithCallbacks, AppModalCallback}; use galaxyui::windowing::{StateEvent, WindowManager}; @@ -454,22 +454,22 @@ use crate::tab_configs::{ NewWorktreeModal, NewWorktreeModalEvent, TabConfigParamsModal, TabConfigParamsModalEvent, }; +use crate::TelemetryEvent; use crate::code::editor::{add_color, remove_color}; use crate::palette::PaletteMode; use crate::search::command_palette::view::{Event as CommandPaletteEvent, View as CommandPalette}; use crate::server::telemetry::{NotificationsTurnedOnSource, PaletteSource, TabRenameEvent}; use crate::tab::{ - tab_position_id, NewSessionMenuItem, PaneNameMenuTarget, SelectedTabColor, TabBarState, - TabComponent, TabData, TabTelemetryAction, TAB_BAR_BORDER_HEIGHT, + NewSessionMenuItem, PaneNameMenuTarget, SelectedTabColor, TAB_BAR_BORDER_HEIGHT, TabBarState, + TabComponent, TabData, TabTelemetryAction, tab_position_id, }; use crate::terminal::view::ssh_file_upload::FileUploadId; use crate::ui_components::icons; -use crate::TelemetryEvent; use autoupdate::AutoupdateStage; #[cfg(target_os = "macos")] use command::blocking::Command; -use galaxy_core::ui::theme::{color::internal_colors, phenomenon::PhenomenonStyle, Fill}; -use galaxy_core::ui::{color::coloru_with_opacity, Icon}; +use galaxy_core::ui::theme::{Fill, color::internal_colors, phenomenon::PhenomenonStyle}; +use galaxy_core::ui::{Icon, color::coloru_with_opacity}; use galaxy_editor::editor::NavigationKey; use galaxyui::keymap::Context; use galaxyui::notification::{RequestPermissionsOutcome, UserNotification}; @@ -479,6 +479,7 @@ use galaxyui::platform::{ use galaxyui::text_layout::ClipConfig; use galaxyui::ui_components::components::{Coords, UiComponent, UiComponentStyles}; use galaxyui::{ + AppContext, Entity, TypedActionView, UpdateView, View, ViewContext, ViewHandle, accessibility::{ AccessibilityContent, AccessibilityVerbosity, ActionAccessibilityContent, WarpA11yRole, }, @@ -490,8 +491,7 @@ use galaxyui::{ PositionedElementAnchor, PositionedElementOffsetBounds, Radius, SavePosition, Shrinkable, Stack, Text, }, - geometry::vector::{vec2f, Vector2F}, - AppContext, Entity, TypedActionView, UpdateView, View, ViewContext, ViewHandle, + geometry::vector::{Vector2F, vec2f}, }; use galaxyui::{ EntityId, FocusContext, ModelHandle, SingletonEntity, UpdateModel, ViewAsRef, WeakViewHandle, @@ -508,7 +508,7 @@ use std::path::Path; use std::path::PathBuf; #[cfg(target_os = "macos")] use std::process; -use std::sync::{mpsc, Mutex}; +use std::sync::{Mutex, mpsc}; use std::{cmp::Ordering, sync::Arc}; use crate::terminal::view::LeftPanelTargetView; @@ -568,7 +568,6 @@ const VERTICAL_TABS_PANEL_POSITION_ID: &str = "workspace_view:vertical_tabs_pane const TAB_CONTENT_POSITION_ID: &str = "workspace_view:tab_content"; const WELCOME_TIPS_POSITION_ID: &str = "welcome_tips_pill"; -const ELLIPSE_SVG_PATH: &str = "bundled/svg/ellipse.svg"; const AI_ASSISTANT_BUTTON_ID: &str = "workspace_view:ai_assistant_button"; @@ -1920,12 +1919,16 @@ impl Workspace { && ai_settings.default_tab_config_path() == path.to_string_lossy(); if is_removed_default { AISettings::handle(ctx).update(ctx, |settings, ctx| { - report_if_error!(settings - .default_session_mode_internal - .set_value(DefaultSessionMode::Terminal, ctx)); - report_if_error!(settings - .default_tab_config_path - .set_value(String::new(), ctx)); + report_if_error!( + settings + .default_session_mode_internal + .set_value(DefaultSessionMode::Terminal, ctx) + ); + report_if_error!( + settings + .default_tab_config_path + .set_value(String::new(), ctx) + ); }); } if let Err(e) = std::fs::remove_file(path) { @@ -5504,9 +5507,11 @@ impl Workspace { right, }; TabSettings::handle(ctx).update(ctx, |settings, ctx| { - report_if_error!(settings - .header_toolbar_chip_selection - .set_value(selection, ctx)); + report_if_error!( + settings + .header_toolbar_chip_selection + .set_value(selection, ctx) + ); }); } @@ -5554,9 +5559,11 @@ impl Workspace { if !FeatureFlag::ConfigurableToolbar.is_enabled() { return; } - let items = vec![MenuItemFields::new("Re-arrange toolbar items") - .with_on_select_action(WorkspaceAction::OpenHeaderToolbarEditor) - .into_item()]; + let items = vec![ + MenuItemFields::new("Re-arrange toolbar items") + .with_on_select_action(WorkspaceAction::OpenHeaderToolbarEditor) + .into_item(), + ]; self.header_toolbar_context_menu .update(ctx, |menu, ctx| menu.set_items(items, ctx)); self.show_header_toolbar_context_menu = Some(position); @@ -7609,17 +7616,21 @@ impl Workspace { fn toggle_recording_mode(&self, ctx: &mut ViewContext) { DebugSettings::handle(ctx).update(ctx, |debug_settings, settings_ctx| { - report_if_error!(debug_settings - .recording_mode - .toggle_and_save_value(settings_ctx)); + report_if_error!( + debug_settings + .recording_mode + .toggle_and_save_value(settings_ctx) + ); }); } fn toggle_in_band_generators(&self, ctx: &mut ViewContext) { DebugSettings::handle(ctx).update(ctx, |debug_settings, settings_ctx| { - report_if_error!(debug_settings - .are_in_band_generators_for_all_sessions_enabled - .toggle_and_save_value(settings_ctx)); + report_if_error!( + debug_settings + .are_in_band_generators_for_all_sessions_enabled + .toggle_and_save_value(settings_ctx) + ); }); } @@ -7782,9 +7793,11 @@ impl Workspace { // Mark that we've done the one-time auto-open AISettings::handle(ctx).update(ctx, |settings, ctx| { - report_if_error!(settings - .has_auto_opened_conversation_list - .set_value(true, ctx)); + report_if_error!( + settings + .has_auto_opened_conversation_list + .set_value(true, ctx) + ); }); } @@ -9697,9 +9710,11 @@ impl Workspace { pub fn toggle_block_snackbar(&mut self, ctx: &mut ViewContext) { BlockListSettings::handle(ctx).update(ctx, |blocklist_settings, ctx| { - report_if_error!(blocklist_settings - .snackbar_enabled - .toggle_and_save_value(ctx)); + report_if_error!( + blocklist_settings + .snackbar_enabled + .toggle_and_save_value(ctx) + ); }); } @@ -9711,9 +9726,11 @@ impl Workspace { pub fn toggle_syntax_highlighting(&mut self, ctx: &mut ViewContext) { InputSettings::handle(ctx).update(ctx, |input_settings, ctx| { - report_if_error!(input_settings - .syntax_highlighting - .toggle_and_save_value(ctx)); + report_if_error!( + input_settings + .syntax_highlighting + .toggle_and_save_value(ctx) + ); }); } @@ -9728,9 +9745,11 @@ impl Workspace { ctx: &mut ViewContext, ) { AccessibilitySettings::handle(ctx).update(ctx, |accessibility_settings, ctx| { - report_if_error!(accessibility_settings - .a11y_verbosity - .set_value(verbosity, ctx)); + report_if_error!( + accessibility_settings + .a11y_verbosity + .set_value(verbosity, ctx) + ); }); } @@ -15550,9 +15569,11 @@ impl Workspace { fn reset_zoom(&mut self, ctx: &mut ViewContext) { WindowSettings::handle(ctx).update(ctx, |window_settings, ctx| { - report_if_error!(window_settings - .zoom_level - .set_value(ZoomLevel::default_value(), ctx)); + report_if_error!( + window_settings + .zoom_level + .set_value(ZoomLevel::default_value(), ctx) + ); }); } @@ -15572,9 +15593,11 @@ impl Workspace { }; WindowSettings::handle(ctx).update(ctx, |window_settings, ctx| { - report_if_error!(window_settings - .zoom_level - .set_value(crate::window_settings::ZoomLevel::VALUES[next_index], ctx)); + report_if_error!( + window_settings + .zoom_level + .set_value(crate::window_settings::ZoomLevel::VALUES[next_index], ctx) + ); }); } @@ -15587,9 +15610,11 @@ impl Workspace { fn set_terminal_font_size(&mut self, new_font_size: f32, ctx: &mut ViewContext) { FontSettings::handle(ctx).update(ctx, |font_settings, ctx| { - report_if_error!(font_settings - .monospace_font_size - .set_value(new_font_size, ctx)); + report_if_error!( + font_settings + .monospace_font_size + .set_value(new_font_size, ctx) + ); }); } @@ -15839,8 +15864,8 @@ impl Workspace { } fn handle_codex_modal_event(&mut self, event: &CodexModalEvent, ctx: &mut ViewContext) { - use crate::ai::blocklist::agent_view::AgentViewEntryOrigin; use crate::AIExecutionProfilesModel; + use crate::ai::blocklist::agent_view::AgentViewEntryOrigin; match event { CodexModalEvent::Close => { @@ -17720,54 +17745,6 @@ impl Workspace { SavePosition::new(Align::new(button).finish(), USER_AVATAR_BUTTON_POSITION_ID).finish() } - fn render_resource_center_button( - &self, - appearance: &Appearance, - ctx: &AppContext, - ) -> Box { - // only show the unread indicator if the tips are NOT completed - let should_show_unread_indicator = !self.tips_completed.as_ref(ctx).skipped_or_completed; - let mut button = self - .render_tab_bar_icon_button( - appearance, - icons::Icon::Lightbulb, - &self.mouse_states.resource_center_icon, - WorkspaceAction::ToggleResourceCenter, - "Warp Essentials".to_string(), - self.cached_keybindings[TOGGLE_RESOURCE_CENTER_KEYBINDING_NAME].clone(), - false, - false, - ) - .finish(); - - if should_show_unread_indicator { - const INDICATOR_DIAMETER: f32 = 6.; - let indicator = Container::new( - ConstrainedBox::new( - WarpUiIcon::new(ELLIPSE_SVG_PATH, appearance.theme().accent()).finish(), - ) - .with_height(INDICATOR_DIAMETER) - .with_width(INDICATOR_DIAMETER) - .finish(), - ) - .finish(); - let mut stack = Stack::new(); - stack.add_child(button); - stack.add_positioned_child( - indicator, - OffsetPositioning::offset_from_parent( - Vector2F::zero(), - ParentOffsetBounds::WindowByPosition, - ParentAnchor::TopRight, - ChildAnchor::TopRight, - ), - ); - button = stack.finish(); - } - - Align::new(button).finish() - } - fn render_settings_button(&self, appearance: &Appearance) -> Box { Align::new( self.render_tab_bar_icon_button( @@ -19677,12 +19654,16 @@ impl TypedActionView for Workspace { } else { // Config missing or deleted — clear and fall through to Terminal. AISettings::handle(ctx).update(ctx, |settings, ctx| { - report_if_error!(settings - .default_session_mode_internal - .set_value(DefaultSessionMode::Terminal, ctx)); - report_if_error!(settings - .default_tab_config_path - .set_value(String::new(), ctx)); + report_if_error!( + settings + .default_session_mode_internal + .set_value(DefaultSessionMode::Terminal, ctx) + ); + report_if_error!( + settings + .default_tab_config_path + .set_value(String::new(), ctx) + ); }); self.add_terminal_tab(false, ctx); } @@ -19802,9 +19783,11 @@ impl TypedActionView for Workspace { AISettings::handle(ctx).update(ctx, |settings, ctx| { report_if_error!(settings.default_session_mode_internal.set_value(*mode, ctx)); if let Some(path) = tab_config_path { - report_if_error!(settings - .default_tab_config_path - .set_value(path.to_string_lossy().into_owned(), ctx)); + report_if_error!( + settings + .default_tab_config_path + .set_value(path.to_string_lossy().into_owned(), ctx) + ); } }); #[cfg(feature = "local_tty")] diff --git a/app/src/workspace/view/cloud_agent_capacity_modal/mod.rs b/app/src/workspace/view/cloud_agent_capacity_modal/mod.rs index 3483238e..1426e37d 100644 --- a/app/src/workspace/view/cloud_agent_capacity_modal/mod.rs +++ b/app/src/workspace/view/cloud_agent_capacity_modal/mod.rs @@ -25,8 +25,8 @@ use pathfinder_color::ColorU; use pathfinder_geometry::vector::vec2f; use thousands::Separable; -use crate::send_telemetry_from_ctx; use crate::TelemetryEvent; +use crate::send_telemetry_from_ctx; const MODAL_WIDTH: f32 = 360.; const MODAL_HEIGHT: f32 = 532.; diff --git a/app/src/workspace/view/conversation_list/item.rs b/app/src/workspace/view/conversation_list/item.rs index 012a5c70..8d6d00dc 100644 --- a/app/src/workspace/view/conversation_list/item.rs +++ b/app/src/workspace/view/conversation_list/item.rs @@ -1,12 +1,12 @@ use crate::ai::active_agent_views_model::ActiveAgentViewsModel; use crate::ai::active_agent_views_model::ConversationOrTaskId; use crate::ai::agent_conversations_model::ConversationOrTask; -use crate::ai::conversation_status_ui::{render_status_element, STATUS_ELEMENT_PADDING}; +use crate::ai::conversation_status_ui::{STATUS_ELEMENT_PADDING, render_status_element}; use crate::appearance::Appearance; use crate::drive::sharing::dialog::SharingDialog; use crate::menu::Menu; use crate::ui_components::icons::Icon; -use crate::ui_components::menu_button::{icon_button_with_context_menu, MenuDirection}; +use crate::ui_components::menu_button::{MenuDirection, icon_button_with_context_menu}; use crate::util::time_format::format_approx_duration_from_now_utc; use crate::util::truncation::truncate_from_end; use crate::workspace::view::conversation_list::view::ConversationListViewAction; diff --git a/app/src/workspace/view/conversation_list/view.rs b/app/src/workspace/view/conversation_list/view.rs index a9c41cda..0d7e3d6c 100644 --- a/app/src/workspace/view/conversation_list/view.rs +++ b/app/src/workspace/view/conversation_list/view.rs @@ -10,25 +10,25 @@ use crate::ai::agent_conversations_model::{AgentConversationsModel, Conversation use crate::ai::agent_management::telemetry::{AgentManagementTelemetryEvent, OpenedFrom}; use crate::ai::blocklist::history_model::BlocklistAIHistoryModel; use crate::appearance::Appearance; -use crate::drive::sharing::dialog::SharingDialog; use crate::drive::sharing::ShareableObject; +use crate::drive::sharing::dialog::SharingDialog; use crate::editor::{ EditorView, Event as EditorEvent, PropagateAndNoOpNavigationKeys, PropagateHorizontalNavigationKeys, SingleLineEditorOptions, TextOptions, }; use crate::menu::{Event as MenuEvent, Menu, MenuItem, MenuItemFields}; use crate::server::telemetry::SharingDialogSource; -use crate::view_components::action_button::{ActionButton, ButtonSize, SecondaryTheme}; use crate::view_components::DismissibleToast; +use crate::view_components::action_button::{ActionButton, ButtonSize, SecondaryTheme}; +use crate::workspace::ToastStack; +use crate::workspace::WorkspaceAction; use crate::workspace::global_actions::ForkedConversationDestination; use crate::workspace::header_toolbar_item::HeaderToolbarItemKind; use crate::workspace::tab_settings::TabSettings; use crate::workspace::view::conversation_list::item::{ - render_item, render_static_item, ItemProps, ItemState, OverflowMenuDisplay, StaticItemProps, - STATIC_ITEM_MIN_HEIGHT, + ItemProps, ItemState, OverflowMenuDisplay, STATIC_ITEM_MIN_HEIGHT, StaticItemProps, + render_item, render_static_item, }; -use crate::workspace::ToastStack; -use crate::workspace::WorkspaceAction; use galaxy_core::features::FeatureFlag; use galaxy_core::send_telemetry_from_ctx; use galaxy_core::ui::Icon; @@ -43,8 +43,8 @@ use galaxyui::elements::{ ScrollbarWidth, Shrinkable, Stack, Text, UniformList, UniformListState, }; use galaxyui::fonts::{Properties, Weight}; -use galaxyui::keymap::macros::*; use galaxyui::keymap::FixedBinding; +use galaxyui::keymap::macros::*; use galaxyui::platform::Cursor; use galaxyui::text_layout::TextAlignment; use galaxyui::{ diff --git a/app/src/workspace/view/free_tier_limit_hit_modal.rs b/app/src/workspace/view/free_tier_limit_hit_modal.rs index ba254aeb..8bdee150 100644 --- a/app/src/workspace/view/free_tier_limit_hit_modal.rs +++ b/app/src/workspace/view/free_tier_limit_hit_modal.rs @@ -1,10 +1,10 @@ +use crate::TelemetryEvent; use crate::ai::{AIRequestUsageModel, AIRequestUsageModelEvent}; use crate::auth::AuthStateProvider; use crate::pricing::{PricingInfoModel, PricingInfoModelEvent}; use crate::ui_components::blended_colors; use crate::ui_components::icons::Icon; use crate::workspaces::user_workspaces::UserWorkspaces; -use crate::TelemetryEvent; use asset_macro::bundled_or_fetched_asset; use galaxy_core::send_telemetry_from_ctx; use galaxy_core::ui::appearance::Appearance; diff --git a/app/src/workspace/view/global_search/model.rs b/app/src/workspace/view/global_search/model.rs index 88c9eb39..36bd5a9b 100644 --- a/app/src/workspace/view/global_search/model.rs +++ b/app/src/workspace/view/global_search/model.rs @@ -1,5 +1,5 @@ -use crate::workspace::view::global_search::view::GlobalSearchEvent; use crate::workspace::view::global_search::SearchConfig; +use crate::workspace::view::global_search::view::GlobalSearchEvent; use anyhow::Result; use futures::StreamExt as _; use galaxy_ripgrep::search::{Match as RipgrepMatch, Submatch}; diff --git a/app/src/workspace/view/global_search/model_wasm.rs b/app/src/workspace/view/global_search/model_wasm.rs index 3d0947ee..7326e175 100644 --- a/app/src/workspace/view/global_search/model_wasm.rs +++ b/app/src/workspace/view/global_search/model_wasm.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; -use crate::workspace::view::global_search::view::GlobalSearchEvent; use crate::workspace::view::global_search::SearchConfig; +use crate::workspace::view::global_search::view::GlobalSearchEvent; use galaxyui::{Entity, ModelContext}; pub struct GlobalSearch {} diff --git a/app/src/workspace/view/global_search/view.rs b/app/src/workspace/view/global_search/view.rs index a9607002..5863f21a 100644 --- a/app/src/workspace/view/global_search/view.rs +++ b/app/src/workspace/view/global_search/view.rs @@ -13,6 +13,7 @@ use galaxy_ripgrep::search::{Match as RipgrepMatch, Submatch}; use pathfinder_geometry::vector::vec2f; use string_offset::{ByteOffset, CharCounter}; +use crate::TelemetryEvent; use crate::code::icon_from_file_path; use crate::debounce::debounce; use crate::editor::{ @@ -23,16 +24,15 @@ use crate::search::ItemHighlightState as SearchHighlightState; use crate::ui_components::blended_colors; use crate::ui_components::icons::Icon as UiIcon; use crate::ui_components::item_highlight::{ImageOrIcon, ItemHighlightState}; -use crate::ui_components::render_file_search_row::{render_file_search_row, FileSearchRowOptions}; +use crate::ui_components::render_file_search_row::{FileSearchRowOptions, render_file_search_row}; use crate::view_components::action_button::{ActionButton, ButtonSize, NakedTheme}; -use crate::workspace::view::global_search::model::GlobalSearch; use crate::workspace::view::global_search::SearchConfig; -use crate::TelemetryEvent; +use crate::workspace::view::global_search::model::GlobalSearch; use galaxy_core::send_telemetry_from_ctx; +use galaxy_core::ui::Icon; use galaxy_core::ui::appearance::Appearance; use galaxy_core::ui::theme::color::internal_colors; use galaxy_core::ui::theme::{AnsiColorIdentifier, Fill as ThemeFill}; -use galaxy_core::ui::Icon; use galaxyui::elements::{ Border, ChildAnchor, ChildView, Clipped, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, DispatchEventResult, Empty, EventHandler, Fill, Flex, FormattedTextElement, diff --git a/app/src/workspace/view/launch_modal/mod.rs b/app/src/workspace/view/launch_modal/mod.rs index 5d7ca8d6..3a6d017c 100644 --- a/app/src/workspace/view/launch_modal/mod.rs +++ b/app/src/workspace/view/launch_modal/mod.rs @@ -28,7 +28,7 @@ use galaxyui::ui_components::components::UiComponent; use galaxyui::{ AppContext, Element, Entity, SingletonEntity, TypedActionView, View, ViewContext, ViewHandle, }; -use markdown_parser::{parse_markdown, FormattedText, FormattedTextLine}; +use markdown_parser::{FormattedText, FormattedTextLine, parse_markdown}; use pathfinder_color::ColorU; use std::collections::HashMap; diff --git a/app/src/workspace/view/left_panel.rs b/app/src/workspace/view/left_panel.rs index 0dc38d6b..0552da88 100644 --- a/app/src/workspace/view/left_panel.rs +++ b/app/src/workspace/view/left_panel.rs @@ -5,15 +5,15 @@ use galaxy_core::ui::theme::color::internal_colors; use galaxy_core::{send_telemetry_from_ctx, ui::Icon}; use galaxy_util::path::LineAndColumnArg; use galaxyui::{ + AppContext, Entity, FocusContext, ModelHandle, SingletonEntity, TypedActionView, View, + ViewContext, ViewHandle, WeakViewHandle, elements::{ - resizable_state_handle, ChildView, ConstrainedBox, Container, CrossAxisAlignment, - DragBarSide, Element, Empty, Flex, MainAxisAlignment, MainAxisSize, MouseStateHandle, - ParentElement, Resizable, ResizableStateHandle, Shrinkable, + ChildView, ConstrainedBox, Container, CrossAxisAlignment, DragBarSide, Element, Empty, + Flex, MainAxisAlignment, MainAxisSize, MouseStateHandle, ParentElement, Resizable, + ResizableStateHandle, Shrinkable, resizable_state_handle, }, platform::Cursor, ui_components::components::{Coords, UiComponent, UiComponentStyles}, - AppContext, Entity, FocusContext, ModelHandle, SingletonEntity, TypedActionView, View, - ViewContext, ViewHandle, WeakViewHandle, }; use crate::ai::agent::conversation::AIConversationId; @@ -30,9 +30,9 @@ use crate::server::telemetry::{FileTreeSource, WarpDriveSource}; use crate::settings_view::keybindings::{KeybindingChangedEvent, KeybindingChangedNotifier}; #[cfg(feature = "local_fs")] use crate::util::file::external_editor::EditorSettings; +use crate::util::openable_file_type::FileTarget; #[cfg(feature = "local_fs")] use crate::util::openable_file_type::resolve_file_target_with_editor_choice; -use crate::util::openable_file_type::FileTarget; use crate::workspace::view::conversation_list::view::{ ConversationListView, Event as ConversationListViewEvent, }; @@ -46,10 +46,11 @@ use crate::workspace::view::{ TOGGLE_PROJECT_EXPLORER_BINDING_NAME, TOGGLE_WARP_DRIVE_BINDING_NAME, }; use crate::{ + TelemetryEvent, appearance::Appearance, code::file_tree::FileTreeView, drive::panel::{MAX_SIDEBAR_WIDTH_RATIO, MIN_SIDEBAR_WIDTH}, - pane_group::pane::view::header::{components::HEADER_EDGE_PADDING, PANE_HEADER_HEIGHT}, + pane_group::pane::view::header::{PANE_HEADER_HEIGHT, components::HEADER_EDGE_PADDING}, pane_group::{self}, terminal::resizable_data::{ModalType, ResizableData}, ui_components::{ @@ -58,7 +59,6 @@ use crate::{ }, util::bindings::keybinding_name_to_display_string, workspace::WorkspaceAction, - TelemetryEvent, }; #[derive(Default)] diff --git a/app/src/workspace/view/onboarding.rs b/app/src/workspace/view/onboarding.rs index 153ba2eb..568d5d0b 100644 --- a/app/src/workspace/view/onboarding.rs +++ b/app/src/workspace/view/onboarding.rs @@ -1,3 +1,4 @@ +use crate::FeatureFlag; use crate::pane_group::{NewTerminalOptions, PanesLayout}; use crate::settings::AISettings; use crate::terminal; @@ -5,7 +6,6 @@ use crate::terminal::view::{ AgentOnboardingVersion, OnboardingIntention, OnboardingVersion, TerminalAction, }; use crate::workspace::Workspace; -use crate::FeatureFlag; use galaxyui::{SingletonEntity as _, ViewContext}; use onboarding::{ProjectOnboardingSettings, SelectedSettings}; use std::collections::HashMap; diff --git a/app/src/workspace/view/openwarp_launch_modal/mod.rs b/app/src/workspace/view/openwarp_launch_modal/mod.rs index bb531aab..5c267bd2 100644 --- a/app/src/workspace/view/openwarp_launch_modal/mod.rs +++ b/app/src/workspace/view/openwarp_launch_modal/mod.rs @@ -1,3 +1,3 @@ mod view; -pub use view::{init, OpenWarpLaunchModal, OpenWarpLaunchModalEvent}; +pub use view::{OpenWarpLaunchModal, OpenWarpLaunchModalEvent, init}; diff --git a/app/src/workspace/view/openwarp_launch_modal/view.rs b/app/src/workspace/view/openwarp_launch_modal/view.rs index 15015c6c..da454558 100644 --- a/app/src/workspace/view/openwarp_launch_modal/view.rs +++ b/app/src/workspace/view/openwarp_launch_modal/view.rs @@ -1,4 +1,4 @@ -use galaxy_core::ui::theme::{phenomenon::PhenomenonStyle, Fill}; +use galaxy_core::ui::theme::{Fill, phenomenon::PhenomenonStyle}; use galaxyui::assets::asset_cache::AssetSource; use galaxyui::elements::{ Align, CacheOption, ChildAnchor, ChildView, ConstrainedBox, Container, CornerRadius, diff --git a/app/src/workspace/view/right_panel.rs b/app/src/workspace/view/right_panel.rs index a989e189..a4a5443c 100644 --- a/app/src/workspace/view/right_panel.rs +++ b/app/src/workspace/view/right_panel.rs @@ -3,27 +3,27 @@ use crate::code_review::code_review_header::HEADER_BUTTON_PADDING; #[cfg(feature = "local_fs")] use crate::code_review::code_review_view::CodeReviewAction; use crate::code_review::code_review_view::{ - render_file_navigation_button, CodeReviewView, CONTENT_LEFT_MARGIN, CONTENT_RIGHT_MARGIN, + CONTENT_LEFT_MARGIN, CONTENT_RIGHT_MARGIN, CodeReviewView, render_file_navigation_button, }; use crate::code_review::code_review_view::{CodeReviewCommentDebugState, CodeReviewViewEvent}; use crate::code_review::telemetry_event::CodeReviewContextDestination; -use crate::pane_group::pane::view::header::{components::HEADER_EDGE_PADDING, PANE_HEADER_HEIGHT}; use crate::pane_group::WorkingDirectoriesEvent; +use crate::pane_group::pane::view::header::{PANE_HEADER_HEIGHT, components::HEADER_EDGE_PADDING}; use crate::pane_group::{Event as PaneGroupEvent, PaneGroup, WorkingDirectoriesModel}; use crate::settings::{AISettings, AISettingsChangedEvent}; +use crate::terminal::CLIAgent; use crate::terminal::cli_agent_sessions::CLIAgentSessionsModel; use crate::terminal::input::MenuPositioning; -use crate::terminal::CLIAgent; use crate::ui_components::{buttons::icon_button_with_color, icons}; -use crate::util::bindings::{keybinding_name_to_display_string, CustomAction}; +use crate::util::bindings::{CustomAction, keybinding_name_to_display_string}; #[cfg(feature = "local_fs")] use crate::util::openable_file_type::FileTarget; use crate::view_components::action_button::{ActionButton, PaneHeaderTheme}; #[cfg(feature = "local_fs")] use crate::view_components::action_button::{NakedTheme, TooltipAlignment}; use crate::view_components::{Dropdown, DropdownItem}; -use crate::workspace::view::TOGGLE_RIGHT_PANEL_BINDING_NAME; use crate::workspace::WorkspaceAction; +use crate::workspace::view::TOGGLE_RIGHT_PANEL_BINDING_NAME; use crate::{ appearance::Appearance, drive::panel::{MAX_SIDEBAR_WIDTH_RATIO, MIN_SIDEBAR_WIDTH}, @@ -34,16 +34,16 @@ use dunce::canonicalize; use galaxy_core::features::FeatureFlag; use galaxy_core::ui::Icon; use galaxy_util::path::LineAndColumnArg; +use galaxyui::EntityId; use galaxyui::elements::{ChildAnchor, Empty, PositionedElementAnchor}; use galaxyui::keymap::EditableBinding; -use galaxyui::EntityId; use galaxyui::{ - elements::{ - resizable_state_handle, Container, DragBarSide, Element, MainAxisSize, MouseStateHandle, - Resizable, ResizableStateHandle, - }, AppContext, Entity, ModelHandle, SingletonEntity, TypedActionView, View, ViewContext, ViewHandle, WeakViewHandle, + elements::{ + Container, DragBarSide, Element, MainAxisSize, MouseStateHandle, Resizable, + ResizableStateHandle, resizable_state_handle, + }, }; use galaxyui::{ elements::{ @@ -1440,7 +1440,10 @@ impl RightPanelView { terminal_status.is_available(), Self::format_optional_path(terminal_status.active_session_path.as_deref()), Self::format_optional_path(terminal_status.current_repo_path.as_deref()), - terminal_status.active_cli_agent.as_deref().unwrap_or(""), + terminal_status + .active_cli_agent + .as_deref() + .unwrap_or(""), terminal_status.is_executing, terminal_status.is_input_box_visible, unavailable_reasons, diff --git a/app/src/workspace/view/startup_directory.rs b/app/src/workspace/view/startup_directory.rs index 6f06fa0b..3c3c892c 100644 --- a/app/src/workspace/view/startup_directory.rs +++ b/app/src/workspace/view/startup_directory.rs @@ -1,11 +1,11 @@ //! Logic to determine the working directory for new terminal sessions. use super::Workspace; +use crate::terminal::ShellLaunchData; use crate::terminal::available_shells::AvailableShell; #[cfg(feature = "local_tty")] use crate::terminal::available_shells::AvailableShells; use crate::terminal::session_settings::{NewSessionSource, SessionSettings}; -use crate::terminal::ShellLaunchData; use galaxyui::SingletonEntity; use galaxyui::{AppContext, ViewContext, WindowId}; use std::path::PathBuf; diff --git a/app/src/workspace/view/vertical_tabs.rs b/app/src/workspace/view/vertical_tabs.rs index 1809d305..4252195f 100644 --- a/app/src/workspace/view/vertical_tabs.rs +++ b/app/src/workspace/view/vertical_tabs.rs @@ -1,22 +1,22 @@ pub mod telemetry; +use crate::FeatureFlag; use crate::ai::agent::conversation::ConversationStatus; use crate::ai::agent_management::AgentNotificationsModel; use crate::code::editor::{add_color, remove_color}; use crate::code::icon_from_file_path; use crate::safe_triangle::SafeTriangle; use crate::send_telemetry_from_app_ctx; -use crate::terminal::cli_agent_sessions::listener::agent_supports_rich_status; -use crate::terminal::cli_agent_sessions::CLIAgentSessionsModel; -use crate::terminal::view::TerminalViewState; use crate::terminal::CLIAgent; +use crate::terminal::cli_agent_sessions::CLIAgentSessionsModel; +use crate::terminal::cli_agent_sessions::listener::agent_supports_rich_status; +use crate::terminal::view::TerminalViewState; use crate::ui_components::icon_with_status::{ - render_icon_with_status, IconWithStatusSizing, IconWithStatusVariant, + IconWithStatusSizing, IconWithStatusVariant, render_icon_with_status, }; use crate::workspace::view::vertical_tabs::telemetry::{ VerticalTabsChipEntrypoint, VerticalTabsTelemetryEvent, }; -use crate::FeatureFlag; use std::cell::RefCell; use std::collections::HashMap; use std::sync::{Arc, Mutex}; @@ -24,16 +24,16 @@ use std::sync::{Arc, Mutex}; use crate::appearance::Appearance; use crate::context_chips::display_chip::GitLineChanges; use crate::context_chips::github_pr_display_text_from_url; -use crate::drive::{cloud_object_styling::warp_drive_icon_color, DriveObjectType}; +use crate::drive::{DriveObjectType, cloud_object_styling::warp_drive_icon_color}; use crate::editor::EditorView; -use crate::pane_group::pane::IPaneType; use crate::pane_group::TerminalPane; +use crate::pane_group::pane::IPaneType; use crate::pane_group::{ CodePane, NotebookPane, PaneGroup, PaneId, TabBarHoverIndex, WorkflowPane, }; -use crate::tab::{tab_position_id, SelectedTabColor, TabData}; -use crate::terminal::session_settings::SessionSettings; +use crate::tab::{SelectedTabColor, TabData, tab_position_id}; use crate::terminal::TerminalView; +use crate::terminal::session_settings::SessionSettings; use crate::themes::theme::Fill as ThemeFill; use crate::ui_components::buttons::combo_inner_button; use crate::ui_components::icons::Icon as UiIcon; @@ -53,20 +53,20 @@ use languages::language_by_filename; use galaxy_core::context_flag::ContextFlag; use galaxy_core::telemetry::TelemetryEvent as _; +use galaxy_core::ui::Icon as WarpIcon; use galaxy_core::ui::color::blend::Blend; use galaxy_core::ui::color::coloru_with_opacity; use galaxy_core::ui::theme::color::internal_colors; use galaxy_core::ui::theme::{AnsiColorIdentifier, Fill as WarpThemeFill, WarpTheme}; -use galaxy_core::ui::Icon as WarpIcon; use galaxyui::elements::DispatchEventResult; use galaxyui::elements::{ - resizable_state_handle, Border, ChildAnchor, Clipped, ClippedScrollStateHandle, - ClippedScrollable, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, DragAxis, - DragBarSide, Draggable, DropShadow, DropTarget, Element, Empty, EventHandler, Expanded, - Fill as ElementFill, Flex, Hoverable, MainAxisSize, MouseStateHandle, OffsetPositioning, - Padding, ParentAnchor, ParentElement, ParentOffsetBounds, PositionedElementAnchor, - PositionedElementOffsetBounds, Radius, Resizable, ResizableStateHandle, SavePosition, - ScrollTarget, ScrollToPositionMode, ScrollbarWidth, Shrinkable, Stack, Text, + Border, ChildAnchor, Clipped, ClippedScrollStateHandle, ClippedScrollable, ConstrainedBox, + Container, CornerRadius, CrossAxisAlignment, DragAxis, DragBarSide, Draggable, DropShadow, + DropTarget, Element, Empty, EventHandler, Expanded, Fill as ElementFill, Flex, Hoverable, + MainAxisSize, MouseStateHandle, OffsetPositioning, Padding, ParentAnchor, ParentElement, + ParentOffsetBounds, PositionedElementAnchor, PositionedElementOffsetBounds, Radius, Resizable, + ResizableStateHandle, SavePosition, ScrollTarget, ScrollToPositionMode, ScrollbarWidth, + Shrinkable, Stack, Text, resizable_state_handle, }; use galaxyui::fonts::{Properties, Weight}; use galaxyui::platform::Cursor; @@ -77,7 +77,7 @@ use galaxyui::ui_components::text_input::TextInput; use galaxyui::{AppContext, EntityId, SingletonEntity, ViewHandle, WindowId}; use pathfinder_color::ColorU; use pathfinder_geometry::rect::RectF; -use pathfinder_geometry::vector::{vec2f, Vector2F}; +use pathfinder_geometry::vector::{Vector2F, vec2f}; use settings::Setting as _; use std::path::{Path, PathBuf}; diff --git a/app/src/workspace/view/vertical_tabs/telemetry.rs b/app/src/workspace/view/vertical_tabs/telemetry.rs index 2e290a67..6dfc8ade 100644 --- a/app/src/workspace/view/vertical_tabs/telemetry.rs +++ b/app/src/workspace/view/vertical_tabs/telemetry.rs @@ -1,6 +1,6 @@ use galaxy_core::features::FeatureFlag; use galaxy_core::telemetry::{EnablementState, TelemetryEvent, TelemetryEventDesc}; -use serde_json::{json, Value}; +use serde_json::{Value, json}; use strum_macros::{EnumDiscriminants, EnumIter}; use crate::workspace::tab_settings::{ diff --git a/app/src/workspace/view/vertical_tabs_tests.rs b/app/src/workspace/view/vertical_tabs_tests.rs index fc94d437..8accdf53 100644 --- a/app/src/workspace/view/vertical_tabs_tests.rs +++ b/app/src/workspace/view/vertical_tabs_tests.rs @@ -4,13 +4,16 @@ use crate::pane_group::{PaneId, TerminalPaneId}; use crate::safe_triangle::SafeTriangle; use crate::terminal::CLIAgent; use crate::workspace::tab_settings::VerticalTabsDisplayGranularity; -use galaxyui::elements::PositionedElementOffsetBounds; use galaxyui::EntityId; +use galaxyui::elements::PositionedElementOffsetBounds; use pathfinder_geometry::rect::RectF; use pathfinder_geometry::vector::Vector2F; use std::path::PathBuf; use super::{ + AgentTabTextPreference, SummaryPaneKind, SummaryPaneKindIcons, TerminalAgentText, + TerminalPrimaryLineData, TerminalPrimaryLineFont, VerticalTabsDetailTarget, + VerticalTabsDetailTargetKind, VerticalTabsSummaryBranchEntry, VerticalTabsSummaryData, branch_label_display, coalesce_summary_branch_entries, code_detail_kind_label, compact_branch_subtitle_display, detail_sidecar_width_and_bounds, detail_target_for_hovered_row, format_summary_primary_labels, @@ -20,9 +23,7 @@ use super::{ summary_overflow_count, summary_search_text_fragments, terminal_kind_badge_label, terminal_primary_line_data, terminal_pull_request_badge_label, terminal_search_text_fragments, terminal_title_fallback_font, uses_outer_group_container, visible_pane_ids_for_detail_target, - vtab_diff_stats_text, AgentTabTextPreference, SummaryPaneKind, SummaryPaneKindIcons, - TerminalAgentText, TerminalPrimaryLineData, TerminalPrimaryLineFont, VerticalTabsDetailTarget, - VerticalTabsDetailTargetKind, VerticalTabsSummaryBranchEntry, VerticalTabsSummaryData, + vtab_diff_stats_text, }; fn pane_id() -> PaneId { diff --git a/app/src/workspace/view/wasm_view.rs b/app/src/workspace/view/wasm_view.rs index c083d2fd..0a50d413 100644 --- a/app/src/workspace/view/wasm_view.rs +++ b/app/src/workspace/view/wasm_view.rs @@ -9,6 +9,7 @@ use crate::uri::browser_url_handler::parse_current_url; use super::PanelPosition; +use crate::BlocklistAIHistoryModel; use crate::ai::agent_conversations_model::AgentConversationsModel; use crate::ai::conversation_details_panel::{ ConversationDetailsData, ConversationDetailsPanel, ConversationDetailsPanelEvent, @@ -21,7 +22,6 @@ use crate::view_components::action_button::{ use crate::wasm_nux_dialog::{WasmNUXDialog, WasmNUXDialogEvent}; use crate::workspace::action::WorkspaceAction; use crate::workspace::view::{NotebookSource, OpenWarpDriveObjectSettings, Workspace}; -use crate::BlocklistAIHistoryModel; const TRANSCRIPT_PANEL_WIDTH: f32 = 280.0; diff --git a/app/src/workspace/view_test.rs b/app/src/workspace/view_test.rs index fdbef1d8..cf48c3a6 100644 --- a/app/src/workspace/view_test.rs +++ b/app/src/workspace/view_test.rs @@ -1,4 +1,5 @@ use super::*; +use crate::ai::AIRequestUsageModel; use crate::ai::blocklist::{BlocklistAIHistoryModel, BlocklistAIPermissions}; use crate::ai::document::ai_document_model::AIDocumentModel; use crate::ai::execution_profiles::profiles::AIExecutionProfilesModel; @@ -8,7 +9,6 @@ use crate::ai::outline::RepoOutlines; use crate::ai::persisted_workspace::PersistedWorkspace; use crate::ai::restored_conversations::RestoredAgentConversations; use crate::ai::skills::SkillManager; -use crate::ai::AIRequestUsageModel; use crate::cloud_object::model::persistence::CloudModel; use crate::cloud_object::model::view::CloudViewModel; use crate::context_chips::prompt::Prompt; @@ -22,12 +22,12 @@ use crate::pricing::PricingInfoModel; use crate::suggestions::ignored_suggestions_model::IgnoredSuggestionsModel; #[cfg(feature = "local_fs")] use crate::user_config::tab_configs_dir; -use repo_metadata::repositories::DetectedRepositories; -use repo_metadata::watcher::DirectoryWatcher; #[cfg(feature = "local_fs")] use repo_metadata::CanonicalizedPath; #[cfg(feature = "local_fs")] use repo_metadata::RepoMetadataModel; +use repo_metadata::repositories::DetectedRepositories; +use repo_metadata::watcher::DirectoryWatcher; use session_sharing_protocol::sharer::SessionSourceType; use std::collections::HashMap; #[cfg(feature = "local_fs")] @@ -41,8 +41,8 @@ use crate::server::sync_queue::SyncQueue; use crate::server::telemetry::context_provider::AppTelemetryContextProvider; use crate::settings::PrivacySettings; -use crate::settings_view::keybindings::KeybindingChangedNotifier; use crate::settings_view::DisplayCount; +use crate::settings_view::keybindings::KeybindingChangedNotifier; use crate::system::SystemStats; use crate::tab_configs::tab_config::{TabConfigPaneNode, TabConfigPaneType}; use crate::terminal::history::History; @@ -61,8 +61,8 @@ use crate::ai::active_agent_views_model::ActiveAgentViewsModel; use crate::ai::agent_conversations_model::AgentConversationsModel; use crate::ai::ambient_agents::github_auth_notifier::GitHubAuthNotifier; use crate::ai::mcp::{ - gallery::MCPGalleryManager, templatable_manager::TemplatableMCPServerManager, - FileBasedMCPManager, FileMCPWatcher, + FileBasedMCPManager, FileMCPWatcher, gallery::MCPGalleryManager, + templatable_manager::TemplatableMCPServerManager, }; use crate::resource_center::Tip; use crate::terminal::cli_agent_sessions::CLIAgentSessionsModel; @@ -70,15 +70,15 @@ use crate::test_util::settings::initialize_settings_for_tests; use crate::undo_close::UndoCloseSettings; use crate::warp_managed_paths_watcher::WarpManagedPathsWatcher; use crate::workflows::local_workflows::LocalWorkflows; -use crate::{experiments, workspace, GlobalResourceHandlesProvider}; use crate::{AgentNotificationsModel, ObjectActions}; +use crate::{GlobalResourceHandlesProvider, experiments, workspace}; use crate::settings::cloud_preferences_syncer::CloudPreferencesSyncer; use ai::index::full_source_code_embedding::manager::CodebaseIndexManager; use ai::project_context::model::ProjectContextModel; use galaxy_editor::editor::NavigationKey; use galaxyui::AddSingletonModel; -use galaxyui::{platform::WindowStyle, App, ViewHandle}; +use galaxyui::{App, ViewHandle, platform::WindowStyle}; use pane_group::{NotebookPane, PaneState, SplitPaneState, TerminalPaneId}; use session_sharing_protocol::common::SessionId; use terminal::shared_session::permissions_manager::SessionPermissionsManager; @@ -879,9 +879,11 @@ fn test_workspace_sessions_retrieves_tabs() { .map(|tab| tab.read(ctx, |tab, _ctx| tab.pane_id_by_index(0).unwrap())) .expect("WindowId was not retrieved."); - assert!(workspace - .workspace_sessions(ctx.window_id(), ctx) - .any(|x| { x.pane_view_locator().pane_id == pane_id })); + assert!( + workspace + .workspace_sessions(ctx.window_id(), ctx) + .any(|x| { x.pane_view_locator().pane_id == pane_id }) + ); // Add a tab and check if workspace_sessions finds the second session from the new tab. workspace.add_terminal_tab(false, ctx); @@ -890,9 +892,11 @@ fn test_workspace_sessions_retrieves_tabs() { .map(|tab| tab.read(ctx, |tab, _ctx| tab.pane_id_by_index(0).unwrap())) .expect("WindowId was not retrieved."); - assert!(workspace - .workspace_sessions(ctx.window_id(), ctx) - .any(|x| { x.pane_view_locator().pane_id == new_pane_id })); + assert!( + workspace + .workspace_sessions(ctx.window_id(), ctx) + .any(|x| { x.pane_view_locator().pane_id == new_pane_id }) + ); }); }); } @@ -917,9 +921,11 @@ fn test_workspace_sessions_retrieves_panes() { .get_pane_group_view(0) .map(|tab| tab.read(ctx, |tab, _ctx| tab.pane_id_by_index(1).unwrap())) .expect("WindowId was not retrieved."); - assert!(workspace - .workspace_sessions(ctx.window_id(), ctx) - .any(|x| { x.pane_view_locator().pane_id == new_pane_id })); + assert!( + workspace + .workspace_sessions(ctx.window_id(), ctx) + .any(|x| { x.pane_view_locator().pane_id == new_pane_id }) + ); }); }); } @@ -1661,8 +1667,11 @@ fn test_tab_context_menu_share_session_items() { // for sharing are "Stop sharing" and "Stop sharing all". workspace.read(&app, |workspace, ctx| { let items = workspace.tabs[1].menu_items(1, 3, ctx); - assert!(items[0] - .is_approximately_same_item_as(&MenuItemFields::new("Stop sharing").into_item())); + assert!( + items[0].is_approximately_same_item_as( + &MenuItemFields::new("Stop sharing").into_item() + ) + ); assert!(items[1].is_approximately_same_item_as( &MenuItemFields::new("Stop sharing all").into_item() )); @@ -1682,8 +1691,11 @@ fn test_tab_context_menu_share_session_items() { // for sharing are "Share session" and "Stop sharing all". workspace.read(&app, |workspace, ctx| { let items = workspace.tabs[1].menu_items(1, 3, ctx); - assert!(items[0] - .is_approximately_same_item_as(&MenuItemFields::new("Share session").into_item())); + assert!( + items[0].is_approximately_same_item_as( + &MenuItemFields::new("Share session").into_item() + ) + ); assert!(items[1].is_approximately_same_item_as( &MenuItemFields::new("Stop sharing all").into_item() )); @@ -1698,8 +1710,11 @@ fn test_tab_context_menu_share_session_items() { // When there's no shared sessions in a tab, the only option is "Share session". workspace.read(&app, |workspace, ctx| { let items = workspace.tabs[1].menu_items(1, 3, ctx); - assert!(items[0] - .is_approximately_same_item_as(&MenuItemFields::new("Share session").into_item())); + assert!( + items[0].is_approximately_same_item_as( + &MenuItemFields::new("Share session").into_item() + ) + ); assert!(items[1].is_approximately_same_item_as(&MenuItem::Separator)); }); }); @@ -2676,11 +2691,13 @@ fn test_worktree_sidecar_search_editor_proxies_navigation_and_escape() { assert!(workspace.show_new_session_dropdown_menu.is_none()); assert!(!workspace.show_new_session_sidecar); assert!(workspace.worktree_sidecar_search_query.is_empty()); - assert!(workspace - .worktree_sidecar_search_editor - .as_ref(ctx) - .buffer_text(ctx) - .is_empty()); + assert!( + workspace + .worktree_sidecar_search_editor + .as_ref(ctx) + .buffer_text(ctx) + .is_empty() + ); }); }); } @@ -2780,9 +2797,11 @@ fn test_vertical_tabs_context_menu_does_not_show_hover_only_tab_bar() { workspace.update(&mut app, |workspace, ctx| { TabSettings::handle(ctx).update(ctx, |settings, ctx| { - report_if_error!(settings - .workspace_decoration_visibility - .set_value(WorkspaceDecorationVisibility::OnHover, ctx)); + report_if_error!( + settings + .workspace_decoration_visibility + .set_value(WorkspaceDecorationVisibility::OnHover, ctx) + ); report_if_error!(settings.use_vertical_tabs.set_value(true, ctx)); }); workspace.should_show_ai_assistant_warm_welcome = false; @@ -2807,9 +2826,11 @@ fn test_standard_tab_context_menu_shows_hover_only_tab_bar() { workspace.update(&mut app, |workspace, ctx| { TabSettings::handle(ctx).update(ctx, |settings, ctx| { - report_if_error!(settings - .workspace_decoration_visibility - .set_value(WorkspaceDecorationVisibility::OnHover, ctx)); + report_if_error!( + settings + .workspace_decoration_visibility + .set_value(WorkspaceDecorationVisibility::OnHover, ctx) + ); }); workspace.should_show_ai_assistant_warm_welcome = false; @@ -2843,10 +2864,12 @@ fn test_open_cloud_agent_setup_guide_action_opens_management_view_and_is_idempot .current_workspace_state .is_agent_management_view_open ); - assert!(workspace - .agent_management_view - .as_ref(ctx) - .is_showing_setup_guide()); + assert!( + workspace + .agent_management_view + .as_ref(ctx) + .is_showing_setup_guide() + ); workspace.handle_action(&WorkspaceAction::OpenCloudAgentSetupGuide, ctx); assert!( @@ -2854,10 +2877,12 @@ fn test_open_cloud_agent_setup_guide_action_opens_management_view_and_is_idempot .current_workspace_state .is_agent_management_view_open ); - assert!(workspace - .agent_management_view - .as_ref(ctx) - .is_showing_setup_guide()); + assert!( + workspace + .agent_management_view + .as_ref(ctx) + .is_showing_setup_guide() + ); }); }); } diff --git a/crates/galaxy_files/test_data/test_write/missing-directory/test_save_missing_directory.rs b/crates/galaxy_files/test_data/test_write/missing-directory/test_save_missing_directory.rs new file mode 100644 index 00000000..e6815c8a --- /dev/null +++ b/crates/galaxy_files/test_data/test_write/missing-directory/test_save_missing_directory.rs @@ -0,0 +1 @@ +Overwrite content \ No newline at end of file diff --git a/crates/galaxy_files/test_data/test_write/test_save_file.rs b/crates/galaxy_files/test_data/test_write/test_save_file.rs new file mode 100644 index 00000000..e6815c8a --- /dev/null +++ b/crates/galaxy_files/test_data/test_write/test_save_file.rs @@ -0,0 +1 @@ +Overwrite content \ No newline at end of file diff --git a/script/install-galaxy.sh b/script/install-galaxy.sh new file mode 100755 index 00000000..395ef862 --- /dev/null +++ b/script/install-galaxy.sh @@ -0,0 +1,123 @@ +#!/usr/bin/env bash +# +# install-galaxy.sh — Clone, build, and install Galaxy.app on macOS. +# +# Usage: +# curl -fsSL https://mng-web-sharing.mini-games.tv/wst-data/ryan-share/galaxy/install-galaxy.sh | bash +# — or — +# ./script/install-galaxy.sh +# + +set -euo pipefail + +REPO_URL="git@gitlab.com:samnasbo/shared/galaxy.git" +CLONE_DIR="$HOME/.galaxy/source" +APP_NAME="Galaxy.app" +INSTALL_DIR="/Applications" +BUNDLE_BIN="galaxy-oss" +BUNDLE_PKG="galaxy" + +# ---------- helpers ---------- +info() { printf "\033[1;34m==>\033[0m %s\n" "$1"; } +warn() { printf "\033[1;33m==> WARNING:\033[0m %s\n" "$1"; } +fail() { printf "\033[1;31m==> ERROR:\033[0m %s\n" "$1"; exit 1; } + +# ---------- 1. Xcode / CLI tools ---------- +info "Checking Xcode and Command Line Tools..." + +if ! xcode-select -p &>/dev/null; then + fail "Xcode Command Line Tools are not installed. Run: xcode-select --install" +fi + +if ! xcrun --show-sdk-path &>/dev/null; then + fail "Xcode SDK not found. Ensure Xcode or Command Line Tools are properly installed." +fi + +if ! xcrun -f metal &>/dev/null; then + warn "Metal compiler not found. Attempting to download Metal toolchain..." + xcodebuild -downloadComponent MetalToolchain || warn "Could not download Metal toolchain — build may fail." +fi + +info "Xcode prerequisites look good." + +# ---------- 2. Homebrew ---------- +if ! command -v brew &>/dev/null; then + info "Installing Homebrew..." + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + + # Add brew to PATH for Apple Silicon + if [[ -f /opt/homebrew/bin/brew ]]; then + eval "$(/opt/homebrew/bin/brew shellenv)" + fi +fi + +if ! command -v pkgconf &>/dev/null && ! command -v pkg-config &>/dev/null; then + info "Installing pkgconf via Homebrew..." + brew install pkgconf +fi + +# ---------- 3. Rust toolchain ---------- +if ! command -v rustup &>/dev/null; then + info "Installing Rust via rustup..." + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + source "$HOME/.cargo/env" +fi + +info "Syncing Rust toolchain (rust-toolchain.toml will pin the exact version)..." +rustup show active-toolchain &>/dev/null || rustup default stable + +# aarch64-apple-darwin target for Apple Silicon +rustup target add aarch64-apple-darwin 2>/dev/null || true + +# cargo-bundle for producing the .app +if ! cargo bundle --help &>/dev/null 2>&1; then + info "Installing cargo-bundle..." + cargo install cargo-bundle \ + --git=https://github.com/burtonageo/cargo-bundle \ + --rev ae4c76e92c08774bf54ff077b1c52e3d1cd6c16d +fi + +# ---------- 4. Clone the repo ---------- +if [[ -d "$CLONE_DIR/.git" ]]; then + info "Repository already exists at $CLONE_DIR — pulling latest..." + git -C "$CLONE_DIR" pull --ff-only || warn "Pull failed; building with current checkout." +else + info "Cloning Galaxy into $CLONE_DIR..." + mkdir -p "$(dirname "$CLONE_DIR")" + git clone "$REPO_URL" "$CLONE_DIR" +fi + +# ---------- 5. Build ---------- +info "Building $BUNDLE_BIN (release)..." +cargo build --release --bin "$BUNDLE_BIN" --package "$BUNDLE_PKG" \ + --manifest-path "$CLONE_DIR/Cargo.toml" + +info "Bundling $APP_NAME..." +# cargo-bundle does not support --manifest-path; run from repo root +pushd "$CLONE_DIR" > /dev/null +cargo bundle --release --bin "$BUNDLE_BIN" --package "$BUNDLE_PKG" +popd > /dev/null + +BUILT_APP="$CLONE_DIR/target/release/bundle/osx/$APP_NAME" +if [[ ! -d "$BUILT_APP" ]]; then + fail "Bundle not found at $BUILT_APP — build may have failed." +fi + +# ---------- 6. Kill, remove, install, launch ---------- +info "Stopping any running Galaxy processes..." +pkill -x "Galaxy" 2>/dev/null && sleep 1 || true +pkill -9 -x "Galaxy" 2>/dev/null || true + +INSTALLED_APP="$INSTALL_DIR/$APP_NAME" +if [[ -d "$INSTALLED_APP" ]]; then + info "Removing existing $INSTALLED_APP..." + rm -rf "$INSTALLED_APP" +fi + +info "Copying $APP_NAME to $INSTALL_DIR..." +cp -R "$BUILT_APP" "$INSTALL_DIR/" + +info "Launching Galaxy..." +open "$INSTALLED_APP" + +info "Done! Galaxy is running."