v1.4.0: Auto-compact streaming, Bedrock summarization support, subagent orchestration, and Galaxy rebrand continuation

Major features:
- Auto-compact: triggers conversation summarization when context window >= 85%,
  compacts Bedrock message history to a summary pair, and tracks live context tokens
- Bedrock summarization: plumbs `is_summarization` flag through translator/client/response
  pipeline, handles SummarizeConversation input type, and marks `summarized` in metadata
- Session restore: rebuilds bedrock_message_history from persisted task messages via
  newly-public `convert_proto_message`, preventing empty history on reconnect
- Subagent orchestration: adds SubagentQuestion/Answer/CompletionSummary event types,
  parent-child question routing with depth limits, retry counting, and drain methods
- Summarization UI: inline SummarizationView in AI blocks with progress/finished states

Refactors:
- Rename WarpTheme → GalaxyTheme across ~100 files (rebrand continuation)
- Rename warp_home_config_dir → galaxy_home_config_dir and related path functions
- Predefined rules: replace "System Defined Rule #N" with descriptive names
  (e.g. "Correctness Over Speed", "Never Guess") and add lookup helpers
- Usage view: replace cumulative input/output token display with live context tokens,
  cache hit rate calculation, and separate cache read/write stats
- Telemetry: remove verbose doc comments, simplify trait definitions
- Facts view: simplify delete permission check (always allow local deletion)
- Remove warp_managed_paths_watcher.rs (dead code)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ryan Ward
2026-05-21 11:59:37 -05:00
co-authored by Claude Opus 4.6
parent eaa2ddc75e
commit 6f54e2cb30
229 changed files with 2506 additions and 2634 deletions
Generated
+1 -1
View File
@@ -5191,7 +5191,7 @@ dependencies = [
[[package]] [[package]]
name = "galaxy" name = "galaxy"
version = "1.2.1" version = "1.3.0"
dependencies = [ dependencies = [
"addr", "addr",
"aho-corasick", "aho-corasick",
+1 -1
View File
@@ -394,7 +394,7 @@ fn embed_resource_file(target_dir: &Path) {
use std::io::Write; use std::io::Write;
let version = env::var("GIT_RELEASE_TAG").unwrap_or("v0".to_owned()); let version = env::var("GIT_RELEASE_TAG").unwrap_or("v0".to_owned());
let app_name = env::var("WARP_APP_NAME").unwrap_or("Warp".to_owned()); let app_name = env::var("GALAXY_APP_NAME").unwrap_or("Galaxy".to_owned());
let bin_name = env::var("CARGO_BIN_NAME").unwrap_or("local".to_owned()); let bin_name = env::var("CARGO_BIN_NAME").unwrap_or("local".to_owned());
let icon_path = Path::new("channels") let icon_path = Path::new("channels")
+3
View File
@@ -139,6 +139,8 @@ pub struct RequestParams {
/// can store them back into the conversation for the next request cycle. /// can store them back into the conversation for the next request cycle.
pub bedrock_messages_sent: pub bedrock_messages_sent:
std::sync::Arc<std::sync::Mutex<Vec<crate::ai::bedrock::convert::ConversationMessage>>>, std::sync::Arc<std::sync::Mutex<Vec<crate::ai::bedrock::convert::ConversationMessage>>>,
/// Whether this request is a conversation summarization/compaction.
pub is_summarization: bool,
} }
pub type Event = Result<warp_multi_agent_api::ResponseEvent, Arc<AIApiError>>; pub type Event = Result<warp_multi_agent_api::ResponseEvent, Arc<AIApiError>>;
@@ -327,6 +329,7 @@ impl RequestParams {
agent_name: None, agent_name: None,
bedrock_message_history: Vec::new(), bedrock_message_history: Vec::new(),
bedrock_messages_sent: std::sync::Arc::new(std::sync::Mutex::new(Vec::new())), bedrock_messages_sent: std::sync::Arc::new(std::sync::Mutex::new(Vec::new())),
is_summarization: false,
} }
} }
} }
+1
View File
@@ -154,6 +154,7 @@ pub async fn generate_multi_agent_output(
root_task_id: params.root_task_id.clone(), root_task_id: params.root_task_id.clone(),
bedrock_message_history: params.bedrock_message_history.clone(), bedrock_message_history: params.bedrock_message_history.clone(),
bedrock_messages_sent: params.bedrock_messages_sent.clone(), bedrock_messages_sent: params.bedrock_messages_sent.clone(),
is_summarization: params.is_summarization,
}; };
match translator::execute(translator_request, &mut request).await { match translator::execute(translator_request, &mut request).await {
+1
View File
@@ -42,6 +42,7 @@ fn request_params_with_ask_user_question_enabled(ask_user_question_enabled: bool
agent_name: None, agent_name: None,
bedrock_message_history: Vec::new(), bedrock_message_history: Vec::new(),
bedrock_messages_sent: std::sync::Arc::new(std::sync::Mutex::new(Vec::new())), bedrock_messages_sent: std::sync::Arc::new(std::sync::Mutex::new(Vec::new())),
is_summarization: false,
} }
} }
+76 -3
View File
@@ -30,7 +30,7 @@ use galaxy_core::features::FeatureFlag;
use galaxy_core::send_telemetry_from_ctx; use galaxy_core::send_telemetry_from_ctx;
use galaxy_core::ui::appearance::Appearance; use galaxy_core::ui::appearance::Appearance;
use galaxy_core::ui::theme::color::internal_colors; use galaxy_core::ui::theme::color::internal_colors;
use galaxy_core::ui::theme::WarpTheme; use galaxy_core::ui::theme::GalaxyTheme;
use galaxyui::color::ColorU; use galaxyui::color::ColorU;
use galaxyui::{EntityId, ModelContext, SingletonEntity}; use galaxyui::{EntityId, ModelContext, SingletonEntity};
use uuid::Uuid; use uuid::Uuid;
@@ -235,6 +235,19 @@ pub struct AIConversation {
/// tool calls, and tool results sent to/received from Bedrock across all /// tool calls, and tool results sent to/received from Bedrock across all
/// request cycles. This is the source of truth for what Bedrock sees. /// request cycles. This is the source of truth for what Bedrock sees.
bedrock_message_history: Vec<crate::ai::bedrock::convert::ConversationMessage>, bedrock_message_history: Vec<crate::ai::bedrock::convert::ConversationMessage>,
/// Live context token count from the most recent Bedrock response.
/// This is the actual input_tokens reported by Bedrock — represents the current
/// context window size, NOT a cumulative total.
current_context_tokens: u32,
/// Guards against repeated auto-compact triggers within the same high-usage window.
/// Set to true when auto-compact fires; reset when summarization completes.
has_pending_auto_compact: bool,
/// Number of times this child agent conversation has been automatically
/// restarted after a transient error. Capped at MAX_SUBAGENT_RETRIES.
subagent_retry_count: u8,
} }
pub(crate) fn artifact_from_fork_proto( pub(crate) fn artifact_from_fork_proto(
@@ -287,6 +300,9 @@ impl AIConversation {
is_remote_child: false, is_remote_child: false,
last_event_sequence: None, last_event_sequence: None,
bedrock_message_history: Vec::new(), bedrock_message_history: Vec::new(),
current_context_tokens: 0,
has_pending_auto_compact: false,
subagent_retry_count: 0,
} }
} }
@@ -314,6 +330,20 @@ impl AIConversation {
.cmp(depths.get(a.as_str()).unwrap_or(&0)) .cmp(depths.get(a.as_str()).unwrap_or(&0))
}); });
// Collect all task messages for rebuilding bedrock_message_history on restore.
// We must do this before consuming the tasks into exchanges.
let all_task_messages: Vec<&api::Message> = api_tasks_by_id
.values()
.flat_map(|task| task.messages.iter())
.collect();
let bedrock_message_history: Vec<crate::ai::bedrock::convert::ConversationMessage> =
all_task_messages
.iter()
.filter_map(|msg| {
crate::ai::bedrock::request_translator::convert_proto_message(msg)
})
.collect();
let mut api_tasks_and_exchanges_by_id: HashMap<_, _> = api_tasks_by_id let mut api_tasks_and_exchanges_by_id: HashMap<_, _> = api_tasks_by_id
.into_iter() .into_iter()
.map(|(id, task)| { .map(|(id, task)| {
@@ -469,7 +499,10 @@ impl AIConversation {
parent_conversation_id, parent_conversation_id,
is_remote_child: false, is_remote_child: false,
last_event_sequence, last_event_sequence,
bedrock_message_history: Vec::new(), bedrock_message_history,
current_context_tokens: 0,
has_pending_auto_compact: false,
subagent_retry_count: 0,
}) })
} }
@@ -521,6 +554,26 @@ impl AIConversation {
self.conversation_usage_metadata.context_window_usage self.conversation_usage_metadata.context_window_usage
} }
pub fn set_context_window_usage(&mut self, value: f32) {
self.conversation_usage_metadata.context_window_usage = value;
}
pub fn current_context_tokens(&self) -> u32 {
self.current_context_tokens
}
pub fn set_current_context_tokens(&mut self, tokens: u32) {
self.current_context_tokens = tokens;
}
pub fn has_pending_auto_compact(&self) -> bool {
self.has_pending_auto_compact
}
pub fn set_has_pending_auto_compact(&mut self, value: bool) {
self.has_pending_auto_compact = value;
}
pub fn credits_spent(&self) -> f32 { pub fn credits_spent(&self) -> f32 {
(self.conversation_usage_metadata.credits_spent * 10.0).round() / 10.0 (self.conversation_usage_metadata.credits_spent * 10.0).round() / 10.0
} }
@@ -859,6 +912,14 @@ impl AIConversation {
self.is_remote_child = true; self.is_remote_child = true;
} }
pub fn subagent_retry_count(&self) -> u8 {
self.subagent_retry_count
}
pub fn increment_subagent_retry_count(&mut self) {
self.subagent_retry_count = self.subagent_retry_count.saturating_add(1);
}
/// Returns a flat list of linearized messages across all tasks, interpolating subtask messages /// Returns a flat list of linearized messages across all tasks, interpolating subtask messages
/// in between subagent tool calls and results, effectively corresponding to the order in which /// in between subagent tool calls and results, effectively corresponding to the order in which
/// the messages were created and added to the conversation. /// the messages were created and added to the conversation.
@@ -1566,6 +1627,17 @@ impl AIConversation {
if was_user_initiated_request { if was_user_initiated_request {
self.last_block_token_usage_by_model.clear(); self.last_block_token_usage_by_model.clear();
} }
// Update live context token count from this response's input tokens.
// This represents the actual current context window size (not cumulative).
let live_input: u32 = token_usage
.iter()
.map(|u| u.total_input + u.input_cache_read + u.input_cache_write)
.sum();
if live_input > 0 {
self.current_context_tokens = live_input;
}
for usage in token_usage.into_iter() { for usage in token_usage.into_iter() {
let entry = self let entry = self
.total_token_usage_by_model .total_token_usage_by_model
@@ -1666,6 +1738,7 @@ impl AIConversation {
// so we only update the summarized flag if it's going from false to true. // so we only update the summarized flag if it's going from false to true.
if usage_metadata.summarized && !self.conversation_usage_metadata.was_summarized { if usage_metadata.summarized && !self.conversation_usage_metadata.was_summarized {
self.conversation_usage_metadata.was_summarized = usage_metadata.summarized; self.conversation_usage_metadata.was_summarized = usage_metadata.summarized;
self.has_pending_auto_compact = false;
} }
} }
Ok(()) Ok(())
@@ -3844,7 +3917,7 @@ impl ConversationStatus {
} }
} }
pub fn status_icon_and_color(&self, theme: &WarpTheme) -> (Icon, ColorU) { pub fn status_icon_and_color(&self, theme: &GalaxyTheme) -> (Icon, ColorU) {
match self { match self {
ConversationStatus::InProgress => (Icon::ClockLoader, theme.ansi_fg_magenta()), ConversationStatus::InProgress => (Icon::ClockLoader, theme.ansi_fg_magenta()),
ConversationStatus::Success => (Icon::Check, theme.ansi_fg_green()), ConversationStatus::Success => (Icon::Check, theme.ansi_fg_green()),
+2 -2
View File
@@ -29,7 +29,7 @@ use galaxy_cli::agent::Harness;
use galaxy_core::execution_mode::AppExecutionMode; use galaxy_core::execution_mode::AppExecutionMode;
use galaxy_core::features::FeatureFlag; use galaxy_core::features::FeatureFlag;
use galaxy_core::report_error; use galaxy_core::report_error;
use galaxy_core::ui::theme::{color::internal_colors, WarpTheme}; use galaxy_core::ui::theme::{color::internal_colors, GalaxyTheme};
use galaxyui::color::ColorU; use galaxyui::color::ColorU;
use galaxyui::r#async::Timer; use galaxyui::r#async::Timer;
use galaxyui::windowing::{StateEvent, WindowManager}; use galaxyui::windowing::{StateEvent, WindowManager};
@@ -334,7 +334,7 @@ impl AgentRunDisplayStatus {
) )
} }
pub fn status_icon_and_color(&self, theme: &WarpTheme) -> (Icon, ColorU) { pub fn status_icon_and_color(&self, theme: &GalaxyTheme) -> (Icon, ColorU) {
match self { match self {
AgentRunDisplayStatus::TaskQueued AgentRunDisplayStatus::TaskQueued
| AgentRunDisplayStatus::TaskPending | AgentRunDisplayStatus::TaskPending
@@ -1,7 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use galaxy_core::ui::icons::Icon; use galaxy_core::ui::icons::Icon;
use galaxy_core::ui::theme::{Fill, WarpTheme}; use galaxy_core::ui::theme::{Fill, GalaxyTheme};
use galaxyui::clipboard::ClipboardContent; use galaxyui::clipboard::ClipboardContent;
use galaxyui::elements::{ use galaxyui::elements::{
ChildView, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, DispatchEventResult, ChildView, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, DispatchEventResult,
@@ -331,7 +331,7 @@ fn render_timestamp_with_dot(item: &NotificationItem, appearance: &Appearance) -
fn render_expand_chevron( fn render_expand_chevron(
expanded: bool, expanded: bool,
on_click: OnExpandClick, on_click: OnExpandClick,
theme: &WarpTheme, theme: &GalaxyTheme,
) -> Box<dyn Element> { ) -> Box<dyn Element> {
let icon = if expanded { let icon = if expanded {
Icon::ChevronDown Icon::ChevronDown
@@ -411,7 +411,7 @@ const NOTIFICATION_AVATAR_SIZING: IconWithStatusSizing = IconWithStatusSizing {
fn render_agent_avatar( fn render_agent_avatar(
agent: NotificationSourceAgent, agent: NotificationSourceAgent,
category: NotificationCategory, category: NotificationCategory,
theme: &WarpTheme, theme: &GalaxyTheme,
) -> Box<dyn Element> { ) -> Box<dyn Element> {
let status = notification_category_to_conversation_status(category); let status = notification_category_to_conversation_status(category);
let variant = match agent { let variant = match agent {
+4 -4
View File
@@ -134,14 +134,14 @@ where
} }
} }
/// Refresh Warp Drive before executing an operation. /// Refresh Galaxy Drive before executing an operation.
pub fn refresh_warp_drive( pub fn refresh_warp_drive(
ctx: &AppContext, ctx: &AppContext,
) -> impl Future<Output = anyhow::Result<()>> + Send + 'static { ) -> impl Future<Output = anyhow::Result<()>> + Send + 'static {
UpdateManager::as_ref(ctx) UpdateManager::as_ref(ctx)
.initial_load_complete() .initial_load_complete()
.with_timeout(WARP_DRIVE_SYNC_TIMEOUT) .with_timeout(WARP_DRIVE_SYNC_TIMEOUT)
.map_err(|_| anyhow::anyhow!("Timed out waiting for Warp Drive to sync")) .map_err(|_| anyhow::anyhow!("Timed out waiting for Galaxy Drive to sync"))
} }
/// Fetch the conversation's server metadata and validate that its harness matches the caller's /// Fetch the conversation's server metadata and validate that its harness matches the caller's
@@ -213,7 +213,7 @@ pub enum EnvironmentChoice {
impl EnvironmentChoice { impl EnvironmentChoice {
/// Resolve the environment to use when creating an agent integration. /// Resolve the environment to use when creating an agent integration.
/// Warp Drive *must* have been synced first. /// Galaxy Drive *must* have been synced first.
pub fn resolve_for_create( pub fn resolve_for_create(
args: EnvironmentCreateArgs, args: EnvironmentCreateArgs,
ctx: &AppContext, ctx: &AppContext,
@@ -279,7 +279,7 @@ Without an environment, the agent will not be able to access private repositorie
/// Resolve the environment to use when updating an agent integration. If the user did not /// Resolve the environment to use when updating an agent integration. If the user did not
/// request any changes to the environment, this returns `Ok(None)`. /// request any changes to the environment, this returns `Ok(None)`.
/// Warp Drive *must* have been synced first. /// Galaxy Drive *must* have been synced first.
pub fn resolve_for_update( pub fn resolve_for_update(
args: EnvironmentUpdateArgs, args: EnvironmentUpdateArgs,
ctx: &AppContext, ctx: &AppContext,
+2 -2
View File
@@ -357,7 +357,7 @@ pub enum AgentDriverError {
#[error("Agent profile \"{0}\" not found")] #[error("Agent profile \"{0}\" not found")]
ProfileError(String), ProfileError(String),
#[error( #[error(
"Failed to authenticate with server - please log in via 'oz login', provide an API key via '--api-key <key>', or set the WARP_API_KEY environment variable" "Failed to authenticate with server - please log in via 'oz login', provide an API key via '--api-key <key>', or set the GALAXY_API_KEY environment variable"
)] )]
NotLoggedIn, NotLoggedIn,
#[error("Saved prompt not found for id {0}")] #[error("Saved prompt not found for id {0}")]
@@ -369,7 +369,7 @@ pub enum AgentDriverError {
#[source] #[source]
error: terminal::ShareSessionError, error: terminal::ShareSessionError,
}, },
#[error("Error syncing Warp Drive")] #[error("Error syncing Galaxy Drive")]
WarpDriveSyncFailed, WarpDriveSyncFailed,
#[error("Requested environment not found: {0}")] #[error("Requested environment not found: {0}")]
EnvironmentNotFound(String), EnvironmentNotFound(String),
@@ -1,3 +1,4 @@
use crate::ai::agent::RenderableAIError;
use crate::ai::blocklist::task_status_sync_model::classify_renderable_error; use crate::ai::blocklist::task_status_sync_model::classify_renderable_error;
use crate::server::server_api::ai::TaskStatusUpdate; use crate::server::server_api::ai::TaskStatusUpdate;
use galaxy_graphql::ai::{AgentTaskState, PlatformErrorCode}; use galaxy_graphql::ai::{AgentTaskState, PlatformErrorCode};
@@ -66,7 +67,7 @@ pub fn classify_driver_error(error: &AgentDriverError) -> (AgentTaskState, TaskS
AgentDriverError::WarpDriveSyncFailed => ( AgentDriverError::WarpDriveSyncFailed => (
AgentTaskState::Error, AgentTaskState::Error,
TaskStatusUpdate::with_error_code( TaskStatusUpdate::with_error_code(
"Warp Drive failed to sync. Please check your network connection and try again.", "Galaxy Drive failed to sync. Please check your network connection and try again.",
PlatformErrorCode::InternalError, PlatformErrorCode::InternalError,
), ),
), ),
@@ -76,7 +77,7 @@ pub fn classify_driver_error(error: &AgentDriverError) -> (AgentTaskState, TaskS
AgentTaskState::Error, AgentTaskState::Error,
TaskStatusUpdate::with_error_code( TaskStatusUpdate::with_error_code(
format!( format!(
"Authentication required. Log in via '{bin} login', provide an API key via '--api-key', or set the WARP_API_KEY environment variable." "Authentication required. Log in via '{bin} login', provide an API key via '--api-key', or set the GALAXY_API_KEY environment variable."
), ),
PlatformErrorCode::AuthenticationRequired, PlatformErrorCode::AuthenticationRequired,
), ),
@@ -95,7 +96,7 @@ pub fn classify_driver_error(error: &AgentDriverError) -> (AgentTaskState, TaskS
AgentTaskState::Failed, AgentTaskState::Failed,
TaskStatusUpdate::with_error_code( TaskStatusUpdate::with_error_code(
format!( format!(
"MCP server {uuid} was not found. Verify the server exists in your Warp Drive and the UUID is correct." "MCP server {uuid} was not found. Verify the server exists in your Galaxy Drive and the UUID is correct."
), ),
PlatformErrorCode::EnvironmentSetupFailed, PlatformErrorCode::EnvironmentSetupFailed,
), ),
@@ -125,7 +126,7 @@ pub fn classify_driver_error(error: &AgentDriverError) -> (AgentTaskState, TaskS
AgentTaskState::Failed, AgentTaskState::Failed,
TaskStatusUpdate::with_error_code( TaskStatusUpdate::with_error_code(
format!( format!(
"Agent profile \"{name}\" not found. Check the profile ID and ensure it exists in your team's Warp Drive." "Agent profile \"{name}\" not found. Check the profile ID and ensure it exists in your team's Galaxy Drive."
), ),
PlatformErrorCode::ResourceNotFound, PlatformErrorCode::ResourceNotFound,
), ),
@@ -134,7 +135,7 @@ pub fn classify_driver_error(error: &AgentDriverError) -> (AgentTaskState, TaskS
AgentTaskState::Failed, AgentTaskState::Failed,
TaskStatusUpdate::with_error_code( TaskStatusUpdate::with_error_code(
format!( format!(
"Saved prompt not found for ID {id}. Verify the prompt exists in your Warp Drive." "Saved prompt not found for ID {id}. Verify the prompt exists in your Galaxy Drive."
), ),
PlatformErrorCode::ResourceNotFound, PlatformErrorCode::ResourceNotFound,
), ),
@@ -302,6 +303,31 @@ pub fn classify_driver_error(error: &AgentDriverError) -> (AgentTaskState, TaskS
} }
} }
/// Returns true if an `AgentDriverError` represents a transient condition that
/// a subagent can recover from by retrying (e.g., network issues, rate limits,
/// server overload). Permanent errors (auth, config, cancelled) return false.
pub fn is_self_recoverable(error: &AgentDriverError) -> bool {
match error {
AgentDriverError::ConversationError { error: renderable } => {
matches!(
renderable,
RenderableAIError::ServerOverloaded
| RenderableAIError::Other {
will_attempt_resume: true,
..
}
)
}
AgentDriverError::WarpDriveSyncFailed
| AgentDriverError::TeamMetadataRefreshTimeout
| AgentDriverError::ShareSessionFailed {
error: ShareSessionError::Timeout,
..
} => true,
_ => false,
}
}
#[cfg(test)] #[cfg(test)]
#[path = "error_classification_tests.rs"] #[path = "error_classification_tests.rs"]
mod tests; mod tests;
+5 -5
View File
@@ -194,7 +194,7 @@ impl EnvironmentCommandRunner {
ctx.spawn(initial_sync, move |_, result, ctx| { ctx.spawn(initial_sync, move |_, result, ctx| {
if result.is_err() { if result.is_err() {
super::report_fatal_error( super::report_fatal_error(
anyhow::anyhow!("Timed out waiting for Warp Drive to sync"), anyhow::anyhow!("Timed out waiting for Galaxy Drive to sync"),
ctx, ctx,
); );
return; return;
@@ -265,7 +265,7 @@ impl EnvironmentCommandRunner {
ctx.spawn(initial_sync, move |_, result, ctx| { ctx.spawn(initial_sync, move |_, result, ctx| {
if result.is_err() { if result.is_err() {
super::report_fatal_error( super::report_fatal_error(
anyhow::anyhow!("Timed out waiting for Warp Drive to sync"), anyhow::anyhow!("Timed out waiting for Galaxy Drive to sync"),
ctx, ctx,
); );
return; return;
@@ -474,7 +474,7 @@ impl EnvironmentCommandRunner {
ctx.spawn(initial_sync, move |_, result, ctx| { ctx.spawn(initial_sync, move |_, result, ctx| {
if result.is_err() { if result.is_err() {
super::report_fatal_error( super::report_fatal_error(
anyhow::anyhow!("Timed out waiting for Warp Drive to sync"), anyhow::anyhow!("Timed out waiting for Galaxy Drive to sync"),
ctx, ctx,
); );
return; return;
@@ -856,7 +856,7 @@ impl EnvironmentCommandRunner {
ctx.spawn(initial_sync, move |_, result, ctx| { ctx.spawn(initial_sync, move |_, result, ctx| {
if result.is_err() { if result.is_err() {
super::report_fatal_error( super::report_fatal_error(
anyhow::anyhow!("Timed out waiting for Warp Drive to sync"), anyhow::anyhow!("Timed out waiting for Galaxy Drive to sync"),
ctx, ctx,
); );
return; return;
@@ -1035,7 +1035,7 @@ impl EnvironmentCommandRunner {
ctx.spawn(initial_sync, move |_, result, ctx| { ctx.spawn(initial_sync, move |_, result, ctx| {
if result.is_err() { if result.is_err() {
super::report_fatal_error( super::report_fatal_error(
anyhow::anyhow!("Timed out waiting for Warp Drive to sync"), anyhow::anyhow!("Timed out waiting for Galaxy Drive to sync"),
ctx, ctx,
); );
return; return;
+3 -3
View File
@@ -175,7 +175,7 @@ fn dispatch_command(
schedule::run(ctx, global_options, schedule_cmd) schedule::run(ctx, global_options, schedule_cmd)
} }
CliCommand::Secret(secret_cmd) => { CliCommand::Secret(secret_cmd) => {
if !FeatureFlag::WarpManagedSecrets.is_enabled() { if !FeatureFlag::GalaxyManagedSecrets.is_enabled() {
return Err(anyhow::anyhow!("invalid value 'secret'")); return Err(anyhow::anyhow!("invalid value 'secret'"));
} }
secret::run(ctx, global_options, secret_cmd) secret::run(ctx, global_options, secret_cmd)
@@ -547,7 +547,7 @@ impl AgentDriverRunner {
// Ensure we've synced team state before starting the driver. // Ensure we've synced team state before starting the driver.
Self::refresh_team_metadata(&foreground).await?; Self::refresh_team_metadata(&foreground).await?;
// Wait for Warp Drive to sync before building the task config, since // Wait for Galaxy Drive to sync before building the task config, since
// prompt resolution (SavedPrompt -> workflow lookup) and environment // prompt resolution (SavedPrompt -> workflow lookup) and environment
// resolution (CloudAmbientAgentEnvironment lookup) depend on it. // resolution (CloudAmbientAgentEnvironment lookup) depend on it.
if foreground if foreground
@@ -1328,7 +1328,7 @@ fn launch_command(
dispatched = true; dispatched = true;
let auth_state = AuthStateProvider::handle(ctx).as_ref(ctx).get(); let auth_state = AuthStateProvider::handle(ctx).as_ref(ctx).get();
let message = if auth_state.is_api_key_authenticated() { let message = if auth_state.is_api_key_authenticated() {
"Your API key is invalid. Please provide a valid key via '--api-key' or the WARP_API_KEY environment variable.".to_string() "Your API key is invalid. Please provide a valid key via '--api-key' or the GALAXY_API_KEY environment variable.".to_string()
} else { } else {
format!("Your credentials are invalid. Please log in again with `{cli_name} login`.") format!("Your credentials are invalid. Please log in again with `{cli_name} login`.")
}; };
+1 -1
View File
@@ -72,7 +72,7 @@ pub fn run(
global_options: GlobalOptions, global_options: GlobalOptions,
command: SecretCommand, command: SecretCommand,
) -> Result<()> { ) -> Result<()> {
if !FeatureFlag::WarpManagedSecrets.is_enabled() { if !FeatureFlag::GalaxyManagedSecrets.is_enabled() {
return Err(anyhow::anyhow!("This feature is not enabled")); return Err(anyhow::anyhow!("This feature is not enabled"));
} }
+4 -4
View File
@@ -118,7 +118,7 @@ static DEFAULT_TIPS: LazyLock<Vec<AgentTip>> = LazyLock::new(|| {
description: "Store reusable workflows, notebooks, and prompts in your".to_string(), description: "Store reusable workflows, notebooks, and prompts in your".to_string(),
link: Some("https://docs.warp.dev/knowledge-and-collaboration/warp-drive".to_string()), link: Some("https://docs.warp.dev/knowledge-and-collaboration/warp-drive".to_string()),
binding_name: None, binding_name: None,
action: Some(WorkspaceAction::OpenWarpDrive), action: Some(WorkspaceAction::OpenGalaxyDrive),
kind: AgentTipKind::WarpDrive, kind: AgentTipKind::WarpDrive,
}, },
AgentTip { AgentTip {
@@ -129,7 +129,7 @@ static DEFAULT_TIPS: LazyLock<Vec<AgentTip>> = LazyLock::new(|| {
kind: AgentTipKind::General, kind: AgentTipKind::General,
}, },
AgentTip { AgentTip {
description: "`@` to add context from files, blocks, or Warp Drive objects to your prompt.".to_string(), description: "`@` to add context from files, blocks, or Galaxy Drive objects to your prompt.".to_string(),
link: Some("https://docs.warp.dev/agent-platform/local-agents/agent-context/using-to-add-context".to_string()), link: Some("https://docs.warp.dev/agent-platform/local-agents/agent-context/using-to-add-context".to_string()),
binding_name: None, binding_name: None,
action: None, action: None,
@@ -297,7 +297,7 @@ static DEFAULT_TIPS: LazyLock<Vec<AgentTip>> = LazyLock::new(|| {
kind: AgentTipKind::General, kind: AgentTipKind::General,
}, },
AgentTip { AgentTip {
description: "`/init` to generate a `WARP.md` file and define project rules for the agent.".to_string(), description: "`/init` to generate a `GALAXY.md` file and define project rules for the agent.".to_string(),
link: Some("https://docs.warp.dev/agent-platform/capabilities/rules".to_string()), link: Some("https://docs.warp.dev/agent-platform/capabilities/rules".to_string()),
binding_name: None, binding_name: None,
action: None, action: None,
@@ -411,7 +411,7 @@ impl WorkspaceAction {
pub fn display_text(&self) -> Option<String> { pub fn display_text(&self) -> Option<String> {
match self { match self {
WorkspaceAction::OpenPalette { .. } => Some("Open palette".to_string()), WorkspaceAction::OpenPalette { .. } => Some("Open palette".to_string()),
WorkspaceAction::OpenWarpDrive => Some("Warp Drive.".to_string()), WorkspaceAction::OpenGalaxyDrive => Some("Galaxy Drive.".to_string()),
WorkspaceAction::ToggleRightPanel => Some("Show diff view".to_string()), WorkspaceAction::ToggleRightPanel => Some("Show diff view".to_string()),
_ => None, _ => None,
} }
+3 -3
View File
@@ -610,7 +610,7 @@ impl AIDocumentView {
let appearance = Appearance::as_ref(app); let appearance = Appearance::as_ref(app);
let ui_builder = appearance.ui_builder().clone(); let ui_builder = appearance.ui_builder().clone();
let tooltip = ui_builder let tooltip = ui_builder
.tool_tip("Save and auto-sync this plan to your Warp Drive".to_string()) .tool_tip("Save and auto-sync this plan to your Galaxy Drive".to_string())
.build() .build()
.finish(); .finish();
let sync_button_mouse_state = self.sync_button_mouse_state.clone(); let sync_button_mouse_state = self.sync_button_mouse_state.clone();
@@ -663,7 +663,7 @@ impl AIDocumentView {
let color = theme.nonactive_ui_detail().into_solid(); let color = theme.nonactive_ui_detail().into_solid();
let ui_builder = appearance.ui_builder().clone(); let ui_builder = appearance.ui_builder().clone();
let tooltip_text = let tooltip_text =
"This plan is synced to your Warp Drive and will auto save any edits you make." "This plan is synced to your Galaxy Drive and will auto save any edits you make."
.to_string(); .to_string();
let synced_status_mouse_state = self.synced_status_mouse_state.clone(); let synced_status_mouse_state = self.synced_status_mouse_state.clone();
Container::new( Container::new(
@@ -1233,7 +1233,7 @@ impl BackingView for AIDocumentView {
.into_item(), .into_item(),
); );
menu_items.push( menu_items.push(
MenuItemFields::new("Show in Warp Drive") MenuItemFields::new("Show in Galaxy Drive")
.with_on_select_action(AIDocumentAction::ShowInWarpDrive) .with_on_select_action(AIDocumentAction::ShowInWarpDrive)
.with_icon(Icon::WarpDrive) .with_icon(Icon::WarpDrive)
.into_item(), .into_item(),
+2 -2
View File
@@ -4,7 +4,7 @@ use anyhow::anyhow;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use galaxy_cli::agent::Harness; use galaxy_cli::agent::Harness;
use galaxy_core::report_error; use galaxy_core::report_error;
use galaxy_core::ui::theme::WarpTheme; use galaxy_core::ui::theme::GalaxyTheme;
use galaxyui::color::ColorU; use galaxyui::color::ColorU;
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
@@ -406,7 +406,7 @@ impl AmbientAgentTaskState {
} }
} }
pub fn status_icon_and_color(&self, theme: &WarpTheme) -> (Icon, ColorU) { pub fn status_icon_and_color(&self, theme: &GalaxyTheme) -> (Icon, ColorU) {
match self { match self {
AmbientAgentTaskState::Queued AmbientAgentTaskState::Queued
| AmbientAgentTaskState::Pending | AmbientAgentTaskState::Pending
+2
View File
@@ -140,6 +140,7 @@ impl BedrockClient {
user_query: Option<String>, user_query: Option<String>,
diagnostic_logger: Option<Arc<BedrockDiagnosticLogger>>, diagnostic_logger: Option<Arc<BedrockDiagnosticLogger>>,
messages_sent: Arc<Mutex<Vec<ConversationMessage>>>, messages_sent: Arc<Mutex<Vec<ConversationMessage>>>,
is_summarization: bool,
) -> Result<ResponseStream, BedrockError> { ) -> Result<ResponseStream, BedrockError> {
let effective_model_id = if cross_region_inference { let effective_model_id = if cross_region_inference {
apply_cross_region_prefix(model_id, &self.region) apply_cross_region_prefix(model_id, &self.region)
@@ -228,6 +229,7 @@ impl BedrockClient {
diagnostic_logger, diagnostic_logger,
messages_sent, messages_sent,
effective_model_id, effective_model_id,
is_summarization,
))) )))
} }
+49 -7
View File
@@ -175,6 +175,20 @@ pub fn extract_new_input_messages(request: &api::Request) -> Vec<ConversationMes
), ),
}); });
} }
api::request::input::Type::SummarizeConversation(summarize) => {
let prompt = if summarize.prompt.is_empty() {
"Please summarize this conversation so far, preserving key decisions, \
code changes, and important context. Be concise but retain all \
information needed to continue the work."
.to_string()
} else {
summarize.prompt.clone()
};
results.push(ConversationMessage {
role: MessageRole::User,
content: MessageContent::Text(prompt),
});
}
_ => {} _ => {}
} }
@@ -378,6 +392,30 @@ fn extract_input_messages(request: &api::Request) -> Vec<api::Message> {
}); });
} }
} }
api::request::input::Type::SummarizeConversation(summarize) => {
let prompt = if summarize.prompt.is_empty() {
"Please summarize this conversation so far, preserving key decisions, \
code changes, and important context. Be concise but retain all \
information needed to continue the work."
.to_string()
} else {
summarize.prompt.clone()
};
results.push(api::Message {
id: uuid::Uuid::new_v4().to_string(),
task_id: task_id.clone(),
request_id: String::new(),
timestamp: None,
server_message_data: String::new(),
citations: vec![],
message: Some(api::message::Message::UserQuery(
api::message::UserQuery {
query: prompt,
..Default::default()
},
)),
});
}
_ => {} _ => {}
} }
@@ -1171,8 +1209,9 @@ pub fn extract_messages_from_request(request: &api::Request) -> Vec<Conversation
messages messages
} }
#[cfg(test)] /// Converts a proto `api::Message` into a `ConversationMessage` for the Bedrock message history.
fn convert_proto_message_for_test(msg: &api::Message) -> Option<ConversationMessage> { /// Used to rebuild the message history from persisted task messages on session restore.
pub fn convert_proto_message(msg: &api::Message) -> Option<ConversationMessage> {
let message_content = msg.message.as_ref()?; let message_content = msg.message.as_ref()?;
match message_content { match message_content {
api::message::Message::UserQuery(query) => Some(ConversationMessage { api::message::Message::UserQuery(query) => Some(ConversationMessage {
@@ -1184,7 +1223,7 @@ fn convert_proto_message_for_test(msg: &api::Message) -> Option<ConversationMess
content: MessageContent::Text(output.text.clone()), content: MessageContent::Text(output.text.clone()),
}), }),
api::message::Message::ToolCall(tool_call) => { api::message::Message::ToolCall(tool_call) => {
let (name, input) = extract_tool_call_info_for_test(tool_call); let (name, input) = extract_tool_call_info(tool_call);
Some(ConversationMessage { Some(ConversationMessage {
role: MessageRole::Assistant, role: MessageRole::Assistant,
content: MessageContent::ToolUse { content: MessageContent::ToolUse {
@@ -1214,11 +1253,8 @@ fn convert_proto_message_for_test(msg: &api::Message) -> Option<ConversationMess
} }
} }
#[cfg(test)]
#[allow(deprecated)] #[allow(deprecated)]
fn extract_tool_call_info_for_test( fn extract_tool_call_info(tool_call: &api::message::ToolCall) -> (String, serde_json::Value) {
tool_call: &api::message::ToolCall,
) -> (String, serde_json::Value) {
if let Some(tool) = &tool_call.tool { if let Some(tool) = &tool_call.tool {
match tool { match tool {
api::message::tool_call::Tool::RunShellCommand(cmd) => ( api::message::tool_call::Tool::RunShellCommand(cmd) => (
@@ -1254,6 +1290,12 @@ fn extract_tool_call_info_for_test(
} }
} }
#[cfg(test)]
fn convert_proto_message_for_test(msg: &api::Message) -> Option<ConversationMessage> {
convert_proto_message(msg)
}
#[cfg(test)] #[cfg(test)]
#[path = "request_translator_tests.rs"] #[path = "request_translator_tests.rs"]
mod tests; mod tests;
+4 -1
View File
@@ -65,6 +65,7 @@ pub fn bedrock_stream_to_response_events(
diagnostic_logger: Option<Arc<BedrockDiagnosticLogger>>, diagnostic_logger: Option<Arc<BedrockDiagnosticLogger>>,
messages_sent: Arc<Mutex<Vec<ConversationMessage>>>, messages_sent: Arc<Mutex<Vec<ConversationMessage>>>,
model_id: String, model_id: String,
is_summarization: bool,
) -> BoxStream<'static, Event> { ) -> BoxStream<'static, Event> {
let request_id = Uuid::new_v4().to_string(); let request_id = Uuid::new_v4().to_string();
let conversation_id = Uuid::new_v4().to_string(); let conversation_id = Uuid::new_v4().to_string();
@@ -470,6 +471,7 @@ pub fn bedrock_stream_to_response_events(
cache_read_input_tokens, cache_read_input_tokens,
cache_write_input_tokens, cache_write_input_tokens,
&model_id, &model_id,
is_summarization,
); );
yield Ok(finished_event); yield Ok(finished_event);
}; };
@@ -555,6 +557,7 @@ pub(super) fn build_stream_finished(
cache_read_input_tokens: i32, cache_read_input_tokens: i32,
cache_write_input_tokens: i32, cache_write_input_tokens: i32,
model_id: &str, model_id: &str,
is_summarization: bool,
) -> ResponseEvent { ) -> ResponseEvent {
let total_tokens = let total_tokens =
(input_tokens + output_tokens + cache_read_input_tokens + cache_write_input_tokens) as u32; (input_tokens + output_tokens + cache_read_input_tokens + cache_write_input_tokens) as u32;
@@ -598,7 +601,7 @@ pub(super) fn build_stream_finished(
#[allow(deprecated)] #[allow(deprecated)]
let conversation_usage_metadata = Some(stream_finished::ConversationUsageMetadata { let conversation_usage_metadata = Some(stream_finished::ConversationUsageMetadata {
context_window_usage: context_usage, context_window_usage: context_usage,
summarized: false, summarized: is_summarization,
credits_spent: 0.0, credits_spent: 0.0,
token_usage: vec![], token_usage: vec![],
tool_usage_metadata: None, tool_usage_metadata: None,
+2
View File
@@ -14,6 +14,7 @@ pub struct TranslatorRequest {
pub root_task_id: Option<String>, pub root_task_id: Option<String>,
pub bedrock_message_history: Vec<ConversationMessage>, pub bedrock_message_history: Vec<ConversationMessage>,
pub bedrock_messages_sent: Arc<Mutex<Vec<ConversationMessage>>>, pub bedrock_messages_sent: Arc<Mutex<Vec<ConversationMessage>>>,
pub is_summarization: bool,
} }
pub async fn execute( pub async fn execute(
@@ -100,6 +101,7 @@ pub async fn execute(
user_query_text, user_query_text,
diagnostic_logger, diagnostic_logger,
params.bedrock_messages_sent.clone(), params.bedrock_messages_sent.clone(),
params.is_summarization,
) )
.await?; .await?;
@@ -6,7 +6,7 @@ use crate::ai::agent::ReadSkillResult;
use crate::ai::agent::{AIAgentAction, AIAgentActionId, AIAgentActionType}; use crate::ai::agent::{AIAgentAction, AIAgentActionId, AIAgentActionType};
use crate::ai::blocklist::action_model::AIConversationId; use crate::ai::blocklist::action_model::AIConversationId;
use crate::ai::skills::SkillManager; use crate::ai::skills::SkillManager;
use crate::warp_managed_paths_watcher::WarpManagedPathsWatcher; use crate::galaxy_managed_paths_watcher::GalaxyManagedPathsWatcher;
use ai::skills::{parse_skill, SkillReference}; use ai::skills::{parse_skill, SkillReference};
use galaxyui::App; use galaxyui::App;
use repo_metadata::{ use repo_metadata::{
@@ -22,7 +22,7 @@ fn initialize_app(app: &mut App) {
app.add_singleton_model(|_| DetectedRepositories::default()); app.add_singleton_model(|_| DetectedRepositories::default());
app.add_singleton_model(RepoMetadataModel::new); app.add_singleton_model(RepoMetadataModel::new);
app.add_singleton_model(HomeDirectoryWatcher::new_for_test); app.add_singleton_model(HomeDirectoryWatcher::new_for_test);
app.add_singleton_model(WarpManagedPathsWatcher::new_for_testing); app.add_singleton_model(GalaxyManagedPathsWatcher::new_for_testing);
app.add_singleton_model(SkillManager::new); app.add_singleton_model(SkillManager::new);
} }
+1
View File
@@ -8,6 +8,7 @@ mod inline_agent_view_header;
// TODO: Move orchestration_conversation_links module import elsewhere. // TODO: Move orchestration_conversation_links module import elsewhere.
pub(crate) mod orchestration_conversation_links; pub(crate) mod orchestration_conversation_links;
pub mod shortcuts; pub mod shortcuts;
pub(crate) mod subagent_inline_panel;
mod zero_state_block; mod zero_state_block;
pub use agent_input_footer::*; pub use agent_input_footer::*;
@@ -0,0 +1,343 @@
//! Inline subagent panel rendered within the parent agent's chat flow.
//!
//! Shows a collapsible panel with the subagent's status, a mini-transcript of
//! recent messages, and controls to expand to full view or cancel.
use galaxy_core::ui::theme::Fill;
use galaxyui::elements::{
ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, Element, Empty, Flex,
MainAxisAlignment, MainAxisSize, MouseStateHandle, ParentElement, Radius, Shrinkable, Text,
};
use galaxyui::{AppContext, SingletonEntity};
use pathfinder_color::ColorU;
use warp_multi_agent_api as api;
use crate::ai::agent::conversation::{AIConversationId, ConversationStatus};
use crate::ai::blocklist::inline_action::inline_action_header::{
ICON_MARGIN, INLINE_ACTION_HEADER_VERTICAL_PADDING, INLINE_ACTION_HORIZONTAL_PADDING,
};
use crate::ai::blocklist::inline_action::inline_action_icons::icon_size;
use crate::ai::blocklist::BlocklistAIHistoryModel;
use crate::appearance::Appearance;
use crate::ui_components::blended_colors;
use crate::ui_components::icons::Icon;
const MINI_TRANSCRIPT_MAX_LINES: usize = 8;
const PANEL_MAX_HEIGHT: f32 = 200.;
const PANEL_CORNER_RADIUS: f32 = 8.;
/// State for a single subagent inline panel instance.
#[derive(Debug, Clone)]
pub struct SubagentPanelState {
pub conversation_id: AIConversationId,
pub is_expanded: bool,
pub header_mouse_state: MouseStateHandle,
pub expand_button_mouse_state: MouseStateHandle,
pub cancel_button_mouse_state: MouseStateHandle,
}
impl SubagentPanelState {
pub fn new(conversation_id: AIConversationId) -> Self {
Self {
conversation_id,
is_expanded: false,
header_mouse_state: MouseStateHandle::default(),
expand_button_mouse_state: MouseStateHandle::default(),
cancel_button_mouse_state: MouseStateHandle::default(),
}
}
}
/// Renders the inline subagent panel for a child conversation.
pub fn render_subagent_inline_panel(
state: &SubagentPanelState,
app: &AppContext,
) -> Box<dyn Element> {
let appearance = Appearance::as_ref(app);
let theme = appearance.theme();
let history_model = BlocklistAIHistoryModel::as_ref(app);
let Some(conversation) = history_model.conversation(&state.conversation_id) else {
return Empty::new().finish();
};
let status = conversation.status().clone();
let agent_name = conversation
.agent_name()
.unwrap_or("Subagent")
.to_string();
let panel_bg = blended_colors::neutral_2(theme);
let mut column = Flex::column().with_cross_axis_alignment(CrossAxisAlignment::Stretch);
// Header — always visible
column.add_child(render_panel_header(
&agent_name,
&status,
state,
panel_bg,
app,
));
// Body (mini-transcript) — only when expanded
if state.is_expanded {
let transcript_lines = collect_mini_transcript(&state.conversation_id, app);
if !transcript_lines.is_empty() {
column.add_child(render_mini_transcript(&transcript_lines, panel_bg, app));
}
}
// Footer — show summary when complete
if status.is_done() {
if let Some(summary) = get_completion_summary(&state.conversation_id, app) {
column.add_child(render_summary_footer(&summary, panel_bg, app));
}
}
Container::new(column.finish())
.with_background_color(panel_bg)
.with_corner_radius(CornerRadius::with_all(Radius::Pixels(PANEL_CORNER_RADIUS)))
.with_margin_top(4.)
.with_margin_bottom(4.)
.finish()
}
fn render_panel_header(
agent_name: &str,
status: &ConversationStatus,
state: &SubagentPanelState,
_background: ColorU,
app: &AppContext,
) -> Box<dyn Element> {
let appearance = Appearance::as_ref(app);
let theme = appearance.theme();
let font_family = appearance.ui_font_family();
let font_size = appearance.monospace_font_size();
let surface = theme.surface_2();
let mut header_row = Flex::row()
.with_main_axis_alignment(MainAxisAlignment::SpaceBetween)
.with_main_axis_size(MainAxisSize::Max)
.with_cross_axis_alignment(CrossAxisAlignment::Center);
// Left: status icon + agent name + status text
let mut left_side = Flex::row().with_cross_axis_alignment(CrossAxisAlignment::Center);
let (icon, icon_color) = status.status_icon_and_color(theme);
let status_icon_element = ConstrainedBox::new(
galaxyui::elements::Icon::new(icon.into(), icon_color).finish(),
)
.with_width(icon_size(app))
.with_height(icon_size(app))
.finish();
left_side.add_child(
Container::new(status_icon_element)
.with_margin_right(ICON_MARGIN)
.finish(),
);
let name_color: ColorU = theme.main_text_color(surface).into();
left_side.add_child(
Text::new_inline(agent_name.to_string(), font_family, font_size)
.with_color(name_color)
.finish(),
);
let status_text = match status {
ConversationStatus::InProgress => "Working...",
ConversationStatus::Success => "Complete",
ConversationStatus::Error => "Error",
ConversationStatus::Cancelled => "Cancelled",
ConversationStatus::Blocked { .. } => "Blocked",
};
let status_text_color = blended_colors::text_disabled(theme, surface);
left_side.add_child(
Container::new(
Text::new_inline(status_text.to_string(), font_family, font_size)
.with_color(status_text_color)
.finish(),
)
.with_margin_left(8.)
.finish(),
);
header_row.add_child(Shrinkable::new(1.0, left_side.finish()).finish());
// Right: collapse/expand chevron
let mut right_side = Flex::row().with_cross_axis_alignment(CrossAxisAlignment::Center);
let chevron_icon = if state.is_expanded {
Icon::ChevronDown
} else {
Icon::ChevronRight
};
let chevron_color = blended_colors::text_disabled(theme, surface);
let chevron = ConstrainedBox::new(
galaxyui::elements::Icon::new(chevron_icon.into(), chevron_color).finish(),
)
.with_width(icon_size(app))
.with_height(icon_size(app))
.finish();
right_side.add_child(
Container::new(chevron)
.with_margin_right(4.)
.finish(),
);
header_row.add_child(right_side.finish());
Container::new(header_row.finish())
.with_padding_left(INLINE_ACTION_HORIZONTAL_PADDING)
.with_padding_right(INLINE_ACTION_HORIZONTAL_PADDING)
.with_padding_top(INLINE_ACTION_HEADER_VERTICAL_PADDING)
.with_padding_bottom(INLINE_ACTION_HEADER_VERTICAL_PADDING)
.finish()
}
fn collect_mini_transcript(
conversation_id: &AIConversationId,
app: &AppContext,
) -> Vec<String> {
let history_model = BlocklistAIHistoryModel::as_ref(app);
let Some(conversation) = history_model.conversation(conversation_id) else {
return vec![];
};
let mut lines = Vec::new();
let messages = conversation.all_linearized_messages();
for msg in messages.iter().rev().take(MINI_TRANSCRIPT_MAX_LINES * 2) {
if let Some(text) = extract_message_text(msg) {
let truncated = if text.len() > 120 {
format!("{}...", &text[..117])
} else {
text
};
lines.push(truncated);
if lines.len() >= MINI_TRANSCRIPT_MAX_LINES {
break;
}
}
}
lines.reverse();
lines
}
fn extract_message_text(msg: &api::Message) -> Option<String> {
let message_content = msg.message.as_ref()?;
match message_content {
api::message::Message::AgentOutput(output) => {
if output.text.is_empty() {
None
} else {
Some(output.text.clone())
}
}
api::message::Message::UserQuery(query) => {
if query.query.is_empty() {
None
} else {
Some(query.query.clone())
}
}
_ => None,
}
}
fn render_mini_transcript(
lines: &[String],
background: ColorU,
app: &AppContext,
) -> Box<dyn Element> {
let appearance = Appearance::as_ref(app);
let theme = appearance.theme();
let text_color = blended_colors::text_disabled(theme, background);
let font_family = appearance.ui_font_family();
let font_size = appearance.monospace_font_size() - 1.;
let mut column = Flex::column();
for line in lines {
let prefixed = format!("> {line}");
column.add_child(
Text::new_inline(prefixed, font_family, font_size)
.with_color(text_color)
.finish(),
);
}
ConstrainedBox::new(
Container::new(column.finish())
.with_padding_left(INLINE_ACTION_HORIZONTAL_PADDING)
.with_padding_right(INLINE_ACTION_HORIZONTAL_PADDING)
.with_padding_top(4.)
.with_padding_bottom(4.)
.finish(),
)
.with_max_height(PANEL_MAX_HEIGHT)
.finish()
}
fn get_completion_summary(
conversation_id: &AIConversationId,
app: &AppContext,
) -> Option<String> {
let history_model = BlocklistAIHistoryModel::as_ref(app);
let conversation = history_model.conversation(conversation_id)?;
if !conversation.status().is_done() {
return None;
}
let messages = conversation.all_linearized_messages();
for msg in messages.iter().rev() {
if let Some(text) = extract_message_text(msg) {
if !text.is_empty() {
let truncated = if text.len() > 300 {
format!("{}...", &text[..297])
} else {
text
};
return Some(truncated);
}
}
}
None
}
fn render_summary_footer(
summary: &str,
_background: ColorU,
app: &AppContext,
) -> Box<dyn Element> {
let appearance = Appearance::as_ref(app);
let theme = appearance.theme();
let surface = theme.surface_2();
let text_color: ColorU = theme.main_text_color(surface).into();
let label_color = blended_colors::text_disabled(theme, surface);
let font_family = appearance.ui_font_family();
let font_size = appearance.monospace_font_size();
let mut row = Flex::row().with_cross_axis_alignment(CrossAxisAlignment::Start);
row.add_child(
Text::new_inline("Summary: ".to_string(), font_family, font_size)
.with_color(label_color)
.finish(),
);
row.add_child(
Shrinkable::new(
1.0,
Text::new_inline(summary.to_string(), font_family, font_size)
.with_color(text_color)
.finish(),
)
.finish(),
);
Container::new(row.finish())
.with_padding_left(INLINE_ACTION_HORIZONTAL_PADDING)
.with_padding_right(INLINE_ACTION_HORIZONTAL_PADDING)
.with_padding_top(6.)
.with_padding_bottom(6.)
.finish()
}
+86
View File
@@ -90,6 +90,7 @@ use crate::ai::blocklist::inline_action::aws_bedrock_credentials_error::{
use crate::ai::blocklist::inline_action::search_codebase::{ use crate::ai::blocklist::inline_action::search_codebase::{
SearchCodebaseView, SearchCodebaseViewEvent, SearchCodebaseView, SearchCodebaseViewEvent,
}; };
use crate::ai::blocklist::inline_action::summarization::SummarizationView;
use crate::ai::blocklist::inline_action::web_fetch::WebFetchView; use crate::ai::blocklist::inline_action::web_fetch::WebFetchView;
use crate::ai::blocklist::inline_action::web_search::WebSearchView; use crate::ai::blocklist::inline_action::web_search::WebSearchView;
use crate::ai::facts::{AIFact, AIMemory, CloudAIFactModel}; use crate::ai::facts::{AIFact, AIMemory, CloudAIFactModel};
@@ -830,6 +831,9 @@ pub struct AIBlock {
/// Map from web fetch message IDs to their view handles. /// Map from web fetch message IDs to their view handles.
web_fetch_views: HashMap<MessageId, ViewHandle<WebFetchView>>, web_fetch_views: HashMap<MessageId, ViewHandle<WebFetchView>>,
/// Map from summarization message IDs to their view handles.
summarization_views: HashMap<MessageId, ViewHandle<SummarizationView>>,
/// Map from todo list IDs to their states. /// Map from todo list IDs to their states.
todo_list_states: HashMap<MessageId, TodoListElementState>, todo_list_states: HashMap<MessageId, TodoListElementState>,
@@ -1340,6 +1344,7 @@ impl AIBlock {
search_codebase_view: Default::default(), search_codebase_view: Default::default(),
web_search_views: Default::default(), web_search_views: Default::default(),
web_fetch_views: Default::default(), web_fetch_views: Default::default(),
summarization_views: Default::default(),
requested_commands_to_auto_collapse: Default::default(), requested_commands_to_auto_collapse: Default::default(),
review_changes_button, review_changes_button,
open_all_comments_button, open_all_comments_button,
@@ -1801,6 +1806,9 @@ impl AIBlock {
self.handle_web_fetch_messages(&output.messages, ctx); self.handle_web_fetch_messages(&output.messages, ctx);
} }
self.handle_summarization_messages(&output.messages, ctx);
self.maybe_create_summarization_view_from_input(ctx);
for action in output.actions() { for action in output.actions() {
let new_action_ids: HashSet<AIAgentActionId> = let new_action_ids: HashSet<AIAgentActionId> =
output.actions().map(|action| action.id.clone()).collect(); output.actions().map(|action| action.id.clone()).collect();
@@ -3489,6 +3497,84 @@ impl AIBlock {
} }
} }
fn handle_summarization_messages(
&mut self,
messages: &[AIAgentOutputMessage],
ctx: &mut ViewContext<Self>,
) {
use crate::ai::agent::SummarizationType;
for message in messages {
let AIAgentOutputMessageType::Summarization {
finished_duration,
summarization_type,
..
} = &message.message
else {
continue;
};
if !matches!(summarization_type, SummarizationType::ConversationSummary) {
continue;
}
if let Some(view) = self.summarization_views.get(&message.id) {
if finished_duration.is_some() {
view.update(ctx, |view, ctx| {
view.mark_finished();
ctx.notify();
});
}
} else {
let is_finished = finished_duration.is_some();
let view = ctx.add_view(|ctx| {
let mut v = SummarizationView::new(ctx);
if is_finished {
v.mark_finished();
}
v
});
self.summarization_views.insert(message.id.clone(), view);
ctx.notify();
}
}
}
/// Creates a SummarizationView when the exchange input is a SummarizeConversation.
/// This handles the Bedrock path where no Summarization output message is emitted.
fn maybe_create_summarization_view_from_input(&mut self, ctx: &mut ViewContext<Self>) {
let is_summarize_input = self
.model
.inputs_to_render(ctx)
.iter()
.any(|i| matches!(i, AIAgentInput::SummarizeConversation { .. }));
if !is_summarize_input {
return;
}
let key = MessageId::new("__summarization_inline_view__".to_string());
if self.summarization_views.contains_key(&key) {
// Already created — check if we should mark it finished
let is_complete = !self.model.status(ctx).is_streaming();
if is_complete {
if let Some(view) = self.summarization_views.get(&key) {
view.update(ctx, |view, ctx| {
if !view.is_finished {
view.mark_finished();
ctx.notify();
}
});
}
}
return;
}
let view = ctx.add_view(|ctx| SummarizationView::new(ctx));
self.summarization_views.insert(key, view);
ctx.notify();
}
/// Note this is called when the search codebase tool call definition finishes streaming, not when the search actually completes. /// Note this is called when the search codebase tool call definition finishes streaming, not when the search actually completes.
fn handle_search_codebase_complete( fn handle_search_codebase_complete(
&mut self, &mut self,
+3 -2
View File
@@ -86,7 +86,7 @@ use galaxy_core::ui::color::contrast::{
foreground_color_with_minimum_contrast, MinimumAllowedContrast, foreground_color_with_minimum_contrast, MinimumAllowedContrast,
}; };
use galaxy_core::ui::color::Rgb; use galaxy_core::ui::color::Rgb;
use galaxy_core::ui::theme::{Fill, WarpTheme}; use galaxy_core::ui::theme::{Fill, GalaxyTheme};
use galaxyui::elements::{Highlight, HighlightedRange, Text}; use galaxyui::elements::{Highlight, HighlightedRange, Text};
use galaxyui::fonts::Properties; use galaxyui::fonts::Properties;
use galaxyui::platform::Cursor; use galaxyui::platform::Cursor;
@@ -433,7 +433,7 @@ pub(crate) fn add_highlights_to_rich_text(
find_context: Option<FindContext<'_>>, find_context: Option<FindContext<'_>>,
location_index: usize, location_index: usize,
line_count: usize, line_count: usize,
theme: &WarpTheme, theme: &GalaxyTheme,
is_selecting: bool, is_selecting: bool,
is_action: bool, is_action: bool,
app: &AppContext, app: &AppContext,
@@ -1080,6 +1080,7 @@ impl View for AIBlock {
search_codebase_view: &self.search_codebase_view, search_codebase_view: &self.search_codebase_view,
web_search_views: &self.web_search_views, web_search_views: &self.web_search_views,
web_fetch_views: &self.web_fetch_views, web_fetch_views: &self.web_fetch_views,
summarization_views: &self.summarization_views,
review_changes_button: &self.review_changes_button, review_changes_button: &self.review_changes_button,
open_all_comments_button: &self.open_all_comments_button, open_all_comments_button: &self.open_all_comments_button,
dismiss_suggestion_button: &self.dismiss_suggestion_button, dismiss_suggestion_button: &self.dismiss_suggestion_button,
+32 -8
View File
@@ -88,6 +88,7 @@ use crate::{
}, },
requested_command::RequestedCommand, requested_command::RequestedCommand,
search_codebase::SearchCodebaseView, search_codebase::SearchCodebaseView,
summarization::SummarizationView,
suggested_unit_tests::SuggestedUnitTestsView, suggested_unit_tests::SuggestedUnitTestsView,
web_fetch::WebFetchView, web_fetch::WebFetchView,
web_search::WebSearchView, web_search::WebSearchView,
@@ -177,6 +178,7 @@ pub(crate) struct Props<'a> {
pub(super) search_codebase_view: &'a HashMap<AIAgentActionId, ViewHandle<SearchCodebaseView>>, pub(super) search_codebase_view: &'a HashMap<AIAgentActionId, ViewHandle<SearchCodebaseView>>,
pub(super) web_search_views: &'a HashMap<MessageId, ViewHandle<WebSearchView>>, pub(super) web_search_views: &'a HashMap<MessageId, ViewHandle<WebSearchView>>,
pub(super) web_fetch_views: &'a HashMap<MessageId, ViewHandle<WebFetchView>>, pub(super) web_fetch_views: &'a HashMap<MessageId, ViewHandle<WebFetchView>>,
pub(super) summarization_views: &'a HashMap<MessageId, ViewHandle<SummarizationView>>,
pub(super) review_changes_button: &'a ViewHandle<ActionButton>, pub(super) review_changes_button: &'a ViewHandle<ActionButton>,
pub(super) open_all_comments_button: &'a ViewHandle<ActionButton>, pub(super) open_all_comments_button: &'a ViewHandle<ActionButton>,
pub(super) dismiss_suggestion_button: &'a ViewHandle<ActionButton>, pub(super) dismiss_suggestion_button: &'a ViewHandle<ActionButton>,
@@ -210,6 +212,23 @@ pub(super) fn render(props: Props, app: &AppContext) -> Box<dyn Element> {
let conversation_status = props.model.conversation(app).map(|c| c.status()); let conversation_status = props.model.conversation(app).map(|c| c.status());
let is_conversation_in_progress = conversation_status.is_some_and(|s| s.is_in_progress()); let is_conversation_in_progress = conversation_status.is_some_and(|s| s.is_in_progress());
// If this is a summarization request, render the inline SummarizationView at the top
// regardless of output status. This handles the Bedrock path where no Summarization
// output message type is emitted.
let is_summarize_input = props
.model
.inputs_to_render(app)
.iter()
.any(|i| matches!(i, AIAgentInput::SummarizeConversation { .. }));
if is_summarize_input {
let key = crate::ai::agent::MessageId::new(
"__summarization_inline_view__".to_string(),
);
if let Some(summarization_view) = props.summarization_views.get(&key) {
output_items.add_child(ChildView::new(summarization_view).finish());
}
}
let status = props.model.status(app); let status = props.model.status(app);
match status { match status {
// Ignore errors if the response is not yet complete-- it could be a deserialization // Ignore errors if the response is not yet complete-- it could be a deserialization
@@ -807,8 +826,14 @@ pub(super) fn render(props: Props, app: &AppContext) -> Box<dyn Element> {
} if matches!( } if matches!(
summarization_type, summarization_type,
SummarizationType::ConversationSummary SummarizationType::ConversationSummary
) && !are_all_text_sections_empty(&text.sections) => ) =>
{ {
if let Some(summarization_view) =
props.summarization_views.get(&output_message.id)
{
output_items
.add_child(ChildView::new(summarization_view).finish());
} else if !are_all_text_sections_empty(&text.sections) {
let header_text = "Conversation summarized".to_string(); let header_text = "Conversation summarized".to_string();
if let Some(element) = render_collapsible_block( if let Some(element) = render_collapsible_block(
output_message, output_message,
@@ -824,6 +849,7 @@ pub(super) fn render(props: Props, app: &AppContext) -> Box<dyn Element> {
output_items.add_child(element); output_items.add_child(element);
} }
} }
}
AIAgentOutputMessageType::WebSearch(web_search_status) => { AIAgentOutputMessageType::WebSearch(web_search_status) => {
if !FeatureFlag::WebSearchUI.is_enabled() { if !FeatureFlag::WebSearchUI.is_enabled() {
continue; continue;
@@ -3210,19 +3236,18 @@ fn render_usage_button(props: Props, app: &AppContext) -> Box<dyn Element> {
}; };
let context_usage = conversation.context_window_usage(); let context_usage = conversation.context_window_usage();
let total_input = conversation.total_input_tokens(); let current_context = conversation.current_context_tokens();
let cache_read = conversation.total_cache_read_tokens(); let cache_read = conversation.total_cache_read_tokens();
let cache_write = conversation.total_cache_write_tokens(); let cache_write = conversation.total_cache_write_tokens();
let cache_miss = conversation.cache_miss_tokens();
let cost_cents = conversation.total_cost_cents(); let cost_cents = conversation.total_cost_cents();
let max_context: u32 = if context_usage > 0.0 { let max_context: u32 = if context_usage > 0.0 {
(total_input as f32 / context_usage).round() as u32 (current_context as f32 / context_usage).round() as u32
} else { } else {
200_000 200_000
}; };
let context_pct = context_usage * 100.0; let context_pct = context_usage * 100.0;
let cache_total = cache_read + cache_write + cache_miss; let cache_total = cache_read + cache_write;
let cache_hit_pct = if cache_total > 0 { let cache_hit_pct = if cache_total > 0 {
(cache_read as f64 / cache_total as f64) * 100.0 (cache_read as f64 / cache_total as f64) * 100.0
} else { } else {
@@ -3230,14 +3255,13 @@ fn render_usage_button(props: Props, app: &AppContext) -> Box<dyn Element> {
}; };
let usage_text = format!( let usage_text = format!(
"Context: {:.1}% ({} / {}) | Cache: {:.1}% (R: {}, W: {}, M: {}) | Cost: ${:.2}", "Context: {:.1}% ({} / {}) | Cache: {:.1}% (R: {}, W: {}) | Cost: ${:.2}",
context_pct, context_pct,
format_token_count(total_input), format_token_count(current_context),
format_token_count(max_context), format_token_count(max_context),
cache_hit_pct, cache_hit_pct,
format_token_count(cache_read), format_token_count(cache_read),
format_token_count(cache_write), format_token_count(cache_write),
format_token_count(cache_miss),
cost_cents / 100.0, cost_cents / 100.0,
); );
+1 -1
View File
@@ -254,7 +254,7 @@ fn render_linked_code_block_internal(
let open_button = render_button( let open_button = render_button(
appearance, appearance,
Icon::LinkExternal, Icon::LinkExternal,
"Open in Warp", "Open in Galaxy",
mouse_handles.open_button, mouse_handles.open_button,
code_clone.clone(), code_clone.clone(),
on_open, on_open,
+122 -1
View File
@@ -1995,6 +1995,9 @@ impl BlocklistAIController {
request_params.parent_agent_id = parent_agent_id; request_params.parent_agent_id = parent_agent_id;
request_params.agent_name = agent_name; request_params.agent_name = agent_name;
request_params.bedrock_message_history = bedrock_history; request_params.bedrock_message_history = bedrock_history;
request_params.is_summarization = request_input
.all_inputs()
.any(|input| matches!(input, AIAgentInput::SummarizeConversation { .. }));
let server_conversation_token_for_identifiers = let server_conversation_token_for_identifiers =
conversation_data.server_conversation_token.clone(); conversation_data.server_conversation_token.clone();
@@ -2020,9 +2023,13 @@ impl BlocklistAIController {
let input_contains_user_query = request_input let input_contains_user_query = request_input
.all_inputs() .all_inputs()
.any(|input| input.is_user_query()); .any(|input| input.is_user_query());
let input_is_summarization = request_input
.all_inputs()
.any(|input| matches!(input, AIAgentInput::SummarizeConversation { .. }));
ctx.subscribe_to_model(&response_stream, move |me, event, ctx| { ctx.subscribe_to_model(&response_stream, move |me, event, ctx| {
me.handle_response_stream_event( me.handle_response_stream_event(
input_contains_user_query, input_contains_user_query,
input_is_summarization,
event, event,
&response_stream_clone, &response_stream_clone,
ctx, ctx,
@@ -2212,6 +2219,7 @@ impl BlocklistAIController {
fn handle_response_stream_event( fn handle_response_stream_event(
&mut self, &mut self,
did_input_contain_user_query: bool, did_input_contain_user_query: bool,
is_summarization_request: bool,
event: &ResponseStreamEvent, event: &ResponseStreamEvent,
response_stream: &ModelHandle<ResponseStream>, response_stream: &ModelHandle<ResponseStream>,
ctx: &mut ModelContext<Self>, ctx: &mut ModelContext<Self>,
@@ -2319,12 +2327,89 @@ impl BlocklistAIController {
let history_model = BlocklistAIHistoryModel::handle(ctx); let history_model = BlocklistAIHistoryModel::handle(ctx);
history_model.update(ctx, |history_model, _| { history_model.update(ctx, |history_model, _| {
if let Some(conversation) = history_model.conversation_mut(&conversation_id) { if let Some(conversation) = history_model.conversation_mut(&conversation_id) {
*conversation.bedrock_message_history_mut() = new_history; // If this was a summarization request, compact the
// history to just the summary instead of keeping
// the full message list. This is what actually
// frees up context window space.
let is_summarization = is_summarization_request;
if is_summarization {
// Extract the assistant's summary from the last
// message in the history (the response).
let summary_text = new_history
.iter()
.rev()
.find_map(|msg| {
use crate::ai::bedrock::convert::{
MessageContent, MessageRole,
};
if msg.role == MessageRole::Assistant {
if let MessageContent::Text(text) =
&msg.content
{
Some(text.clone())
} else {
None
}
} else {
None
}
});
if let Some(summary) = summary_text {
use crate::ai::bedrock::convert::{
ConversationMessage, MessageContent,
MessageRole,
};
let assistant_reply = "Understood. I have the context from our previous conversation. How can I help you next?";
let user_msg = format!(
"Here is a summary of our conversation so far:\n\n{summary}"
);
let compacted = vec![
ConversationMessage {
role: MessageRole::User,
content: MessageContent::Text(user_msg.clone()),
},
ConversationMessage {
role: MessageRole::Assistant,
content: MessageContent::Text(
assistant_reply.to_string()
),
},
];
log::info!(
"[bedrock] Compacted conversation history from {} messages to {} (summary)",
new_history.len(),
compacted.len()
);
*conversation.bedrock_message_history_mut() =
compacted;
// Estimate new context size from the compacted content.
// ~4 chars per token is a reasonable approximation.
let estimated_tokens = ((user_msg.len() + assistant_reply.len()) / 4) as u32;
let max_context = crate::ai::bedrock::response_translator::context_window_for_model("claude-opus-4-6-20250514[1m]");
let new_usage = estimated_tokens as f32 / max_context as f32;
conversation.set_context_window_usage(new_usage);
conversation.set_current_context_tokens(estimated_tokens);
log::info!(
"[bedrock] Post-compact context estimate: ~{} tokens ({:.1}% of context window)",
estimated_tokens,
new_usage * 100.0
);
} else {
*conversation.bedrock_message_history_mut() =
new_history;
}
} else {
*conversation.bedrock_message_history_mut() =
new_history;
log::info!( log::info!(
"[bedrock] Updated conversation bedrock history: {} messages", "[bedrock] Updated conversation bedrock history: {} messages",
conversation.bedrock_message_history().len() conversation.bedrock_message_history().len()
); );
} }
}
}); });
} }
} }
@@ -2800,6 +2885,42 @@ impl BlocklistAIController {
}); });
ctx.emit(BlocklistAIControllerEvent::FreeTierLimitCheckTriggered); ctx.emit(BlocklistAIControllerEvent::FreeTierLimitCheckTriggered);
} }
// Auto-compact: trigger summarization when context window usage >= 85%.
let should_auto_compact = {
let history_model = BlocklistAIHistoryModel::as_ref(ctx);
history_model
.conversation(&conversation_id)
.is_some_and(|conversation| {
let is_summarization_request = conversation
.latest_exchange()
.is_some_and(|exchange| {
exchange
.input
.iter()
.any(|i| matches!(i, AIAgentInput::SummarizeConversation { .. }))
});
conversation.context_window_usage() >= 0.85
&& !conversation.has_pending_auto_compact()
&& !is_summarization_request
})
};
if should_auto_compact {
log::info!(
"[auto-compact] Context window usage >= 85% for conversation {:?}, triggering summarization",
conversation_id
);
BlocklistAIHistoryModel::handle(ctx).update(ctx, |history_model, _| {
if let Some(conversation) = history_model.conversation_mut(&conversation_id) {
conversation.set_has_pending_auto_compact(true);
}
});
self.send_slash_command_request(
SlashCommandRequest::Summarize { prompt: None },
ctx,
);
}
} }
} }
@@ -4,7 +4,7 @@ use ai::agent::{
action::{AskUserQuestionItem, AskUserQuestionOption, AskUserQuestionType}, action::{AskUserQuestionItem, AskUserQuestionOption, AskUserQuestionType},
action_result::{AskUserQuestionAnswerItem, AskUserQuestionResult}, action_result::{AskUserQuestionAnswerItem, AskUserQuestionResult},
}; };
use galaxy_core::ui::theme::{color::internal_colors, WarpTheme}; use galaxy_core::ui::theme::{color::internal_colors, GalaxyTheme};
use galaxyui::{ use galaxyui::{
elements::{ elements::{
new_scrollable::SingleAxisConfig, Border, ChildView, Clipped, ClippedScrollStateHandle, new_scrollable::SingleAxisConfig, Border, ChildView, Clipped, ClippedScrollStateHandle,
@@ -1333,7 +1333,7 @@ impl AskUserQuestionView {
fn render_question_text( fn render_question_text(
question_text: &str, question_text: &str,
appearance: &Appearance, appearance: &Appearance,
theme: &WarpTheme, theme: &GalaxyTheme,
) -> Box<dyn Element> { ) -> Box<dyn Element> {
let text_color = theme.foreground().into(); let text_color = theme.foreground().into();
Container::new(render_text_with_markdown_support( Container::new(render_text_with_markdown_support(
@@ -1357,7 +1357,7 @@ impl AskUserQuestionView {
&self, &self,
question_text: &str, question_text: &str,
appearance: &Appearance, appearance: &Appearance,
theme: &WarpTheme, theme: &GalaxyTheme,
) -> Box<dyn Element> { ) -> Box<dyn Element> {
let body = Flex::column() let body = Flex::column()
.with_cross_axis_alignment(CrossAxisAlignment::Stretch) .with_cross_axis_alignment(CrossAxisAlignment::Stretch)
@@ -1385,7 +1385,7 @@ impl AskUserQuestionView {
fn render_nav_footer( fn render_nav_footer(
&self, &self,
appearance: &Appearance, appearance: &Appearance,
theme: &WarpTheme, theme: &GalaxyTheme,
app: &AppContext, app: &AppContext,
) -> Box<dyn Element> { ) -> Box<dyn Element> {
let counter = format!( let counter = format!(
@@ -11,6 +11,7 @@ pub(crate) mod requested_command_attribution;
pub(crate) mod requested_script; pub(crate) mod requested_script;
pub(super) mod search_codebase; pub(super) mod search_codebase;
pub(crate) mod search_results_common; pub(crate) mod search_results_common;
pub(super) mod summarization;
pub(crate) mod suggested_unit_tests; pub(crate) mod suggested_unit_tests;
pub(super) mod web_fetch; pub(super) mod web_fetch;
pub(super) mod web_search; pub(super) mod web_search;
@@ -0,0 +1,197 @@
use galaxy_core::ui::appearance::Appearance;
use galaxyui::elements::shimmering_text::{ShimmerConfig, ShimmeringTextElement, ShimmeringTextStateHandle};
use galaxyui::elements::{
ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, Element, Flex,
MainAxisAlignment, ParentElement, Radius, Shrinkable, Text,
};
use galaxyui::r#async::{SpawnedFutureHandle, Timer};
use galaxyui::{AppContext, Entity, SingletonEntity, View, ViewContext};
use instant::Instant;
use std::time::Duration;
use super::inline_action_header::{
INLINE_ACTION_HEADER_VERTICAL_PADDING, INLINE_ACTION_HORIZONTAL_PADDING,
};
use super::inline_action_icons::icon_size;
use crate::ai::blocklist::block::view_impl::WithContentItemSpacing;
use crate::ui_components::icons::Icon;
pub enum SummarizationViewEvent {}
pub struct SummarizationView {
pub is_finished: bool,
shimmering_text_handle: ShimmeringTextStateHandle,
start_time: Instant,
timer_handle: Option<SpawnedFutureHandle>,
}
impl SummarizationView {
pub fn new(ctx: &mut ViewContext<Self>) -> Self {
let mut view = Self {
is_finished: false,
shimmering_text_handle: ShimmeringTextStateHandle::default(),
start_time: Instant::now(),
timer_handle: None,
};
view.start_timer(ctx);
view
}
pub fn mark_finished(&mut self) {
self.is_finished = true;
if let Some(handle) = self.timer_handle.take() {
handle.abort();
}
}
fn start_timer(&mut self, ctx: &mut ViewContext<Self>) {
if self.timer_handle.is_some() {
return;
}
let handle = ctx.spawn(
async move {
Timer::after(Duration::from_secs(1)).await;
},
|me, _unit, ctx| {
me.timer_handle = None;
if !me.is_finished {
ctx.notify();
me.start_timer(ctx);
}
},
);
self.timer_handle = Some(handle);
}
fn render_in_progress(&self, app: &AppContext) -> Box<dyn Element> {
let appearance = Appearance::as_ref(app);
let theme = appearance.theme();
let header_background = theme.surface_2();
let mut header_row = Flex::row()
.with_main_axis_alignment(MainAxisAlignment::Start)
.with_cross_axis_alignment(CrossAxisAlignment::Center);
// Clock loader icon (magenta, matches InProgress convention)
let icon_element = galaxyui::elements::Icon::new(
Icon::ClockLoader.into(),
theme.ansi_fg_magenta(),
)
.finish();
let icon_box = ConstrainedBox::new(icon_element)
.with_width(icon_size(app))
.with_height(icon_size(app))
.finish();
header_row.add_child(
Container::new(icon_box)
.with_margin_right(8.)
.finish(),
);
// Shimmering "Summarizing conversation..." text
let base_color = theme.disabled_text_color(header_background).into_solid();
let shimmer_color = theme.main_text_color(header_background).into_solid();
let shimmer_element = ShimmeringTextElement::new(
"Summarizing conversation...".to_string(),
appearance.ui_font_family(),
appearance.monospace_font_size(),
base_color,
shimmer_color,
ShimmerConfig::default(),
self.shimmering_text_handle.clone(),
)
.finish();
header_row.add_child(Shrinkable::new(1.0, shimmer_element).finish());
// Elapsed time suffix
let elapsed = self.start_time.elapsed();
let elapsed_text = format_elapsed(elapsed);
let suffix = Text::new_inline(
format!(" \u{2022} {elapsed_text}"),
appearance.ui_font_family(),
appearance.monospace_font_size(),
)
.with_color(theme.disabled_text_color(header_background).into())
.finish();
header_row.add_child(suffix);
Container::new(header_row.finish())
.with_horizontal_padding(INLINE_ACTION_HORIZONTAL_PADDING)
.with_vertical_padding(INLINE_ACTION_HEADER_VERTICAL_PADDING)
.with_background(header_background)
.with_corner_radius(CornerRadius::with_all(Radius::Pixels(8.)))
.finish()
}
fn render_finished(&self, app: &AppContext) -> Box<dyn Element> {
let appearance = Appearance::as_ref(app);
let theme = appearance.theme();
let header_background = theme.surface_2();
let mut header_row = Flex::row()
.with_main_axis_alignment(MainAxisAlignment::Start)
.with_cross_axis_alignment(CrossAxisAlignment::Center);
// Checkmark-style icon for completed
let icon_element = galaxyui::elements::Icon::new(
Icon::Check.into(),
theme.ansi_fg_green(),
)
.finish();
let icon_box = ConstrainedBox::new(icon_element)
.with_width(icon_size(app))
.with_height(icon_size(app))
.finish();
header_row.add_child(
Container::new(icon_box)
.with_margin_right(8.)
.finish(),
);
let elapsed = self.start_time.elapsed();
let elapsed_text = format_elapsed(elapsed);
let title = Text::new_inline(
format!("Conversation summarized \u{2022} {elapsed_text}"),
appearance.ui_font_family(),
appearance.monospace_font_size(),
)
.with_color(theme.main_text_color(header_background).into())
.finish();
header_row.add_child(Shrinkable::new(1.0, title).finish());
Container::new(header_row.finish())
.with_horizontal_padding(INLINE_ACTION_HORIZONTAL_PADDING)
.with_vertical_padding(INLINE_ACTION_HEADER_VERTICAL_PADDING)
.with_background(header_background)
.with_corner_radius(CornerRadius::with_all(Radius::Pixels(8.)))
.finish()
}
}
impl Entity for SummarizationView {
type Event = SummarizationViewEvent;
}
impl View for SummarizationView {
fn ui_name() -> &'static str {
"SummarizationView"
}
fn render(&self, app: &AppContext) -> Box<dyn Element> {
let element = if self.is_finished {
self.render_finished(app)
} else {
self.render_in_progress(app)
};
element.with_agent_output_item_spacing(app).finish()
}
}
fn format_elapsed(duration: Duration) -> String {
let secs = duration.as_secs();
if secs < 60 {
format!("{secs}s")
} else {
format!("{}m {}s", secs / 60, secs % 60)
}
}
+172 -6
View File
@@ -19,6 +19,8 @@ use warp_multi_agent_api as api;
const MAX_RETRY_ATTEMPTS: i32 = 3; const MAX_RETRY_ATTEMPTS: i32 = 3;
const MAX_PENDING_LIFECYCLE_EVENTS_PER_TARGET: usize = 200; const MAX_PENDING_LIFECYCLE_EVENTS_PER_TARGET: usize = 200;
pub const MAX_SUBAGENT_RETRIES: u8 = 3;
const MAX_SUBAGENT_QUESTION_DEPTH: u8 = 3;
/// Stage associated with a lifecycle error detail. /// Stage associated with a lifecycle error detail.
/// This keeps persisted/runtime metadata consistent across API payloads and DB rows. /// This keeps persisted/runtime metadata consistent across API payloads and DB rows.
@@ -64,6 +66,23 @@ pub enum PendingEventDetail {
Lifecycle { Lifecycle {
event: api::AgentEvent, event: api::AgentEvent,
}, },
/// A subagent is asking its parent a question (routed from AskUserQuestion).
SubagentQuestion {
source_conversation_id: AIConversationId,
question_text: String,
options: Vec<String>,
depth: u8,
},
/// The parent's answer to a subagent's question.
SubagentAnswer {
target_conversation_id: AIConversationId,
answer_text: String,
},
/// A subagent reporting its completion summary to the parent.
SubagentCompletionSummary {
source_conversation_id: AIConversationId,
summary_text: String,
},
} }
/// A queued event consumed by the controller. /// A queued event consumed by the controller.
@@ -930,29 +949,49 @@ impl OrchestrationEventService {
let mut messages = Vec::new(); let mut messages = Vec::new();
let mut lifecycle_events = Vec::new(); let mut lifecycle_events = Vec::new();
for event in &deliverable { let mut server_bound_events = Vec::new();
for event in deliverable {
match &event.detail { match &event.detail {
PendingEventDetail::Message { PendingEventDetail::Message {
message_id, message_id,
addresses, addresses,
subject, subject,
message_body, message_body,
} => messages.push(ReceivedMessageInput { } => {
messages.push(ReceivedMessageInput {
message_id: message_id.clone(), message_id: message_id.clone(),
sender_agent_id: event.source_agent_id.clone(), sender_agent_id: event.source_agent_id.clone(),
addresses: addresses.clone(), addresses: addresses.clone(),
subject: subject.clone(), subject: subject.clone(),
message_body: message_body.clone(), message_body: message_body.clone(),
}), });
PendingEventDetail::Lifecycle { event } => lifecycle_events.push(event.clone()), server_bound_events.push(event);
}
PendingEventDetail::Lifecycle { event: _ } => {
lifecycle_events.push(
if let PendingEventDetail::Lifecycle { event: e } = &event.detail {
e.clone()
} else {
unreachable!()
},
);
server_bound_events.push(event);
}
// Local-only subagent events are consumed directly by the controller,
// not converted to AIAgentInput or awaited for server echo.
PendingEventDetail::SubagentQuestion { .. }
| PendingEventDetail::SubagentAnswer { .. }
| PendingEventDetail::SubagentCompletionSummary { .. } => {}
} }
} }
// Move to awaiting echo for delivery confirmation. // Only server-bound events need echo confirmation.
if !server_bound_events.is_empty() {
self.awaiting_server_echo_events self.awaiting_server_echo_events
.entry(conversation_id) .entry(conversation_id)
.or_default() .or_default()
.extend(deliverable); .extend(server_bound_events);
}
let mut inputs = Vec::new(); let mut inputs = Vec::new();
if !messages.is_empty() { if !messages.is_empty() {
@@ -966,6 +1005,34 @@ impl OrchestrationEventService {
inputs inputs
} }
/// Drain only the local subagent events (Question/Answer/Summary) for a conversation.
/// These are not sent to the server and are consumed directly by the controller.
pub fn drain_subagent_events(
&mut self,
conversation_id: &AIConversationId,
) -> Vec<PendingEvent> {
let Some(pending) = self.pending_events.get_mut(conversation_id) else {
return vec![];
};
let mut subagent_events = Vec::new();
pending.retain(|event| match &event.detail {
PendingEventDetail::SubagentQuestion { .. }
| PendingEventDetail::SubagentAnswer { .. }
| PendingEventDetail::SubagentCompletionSummary { .. } => {
subagent_events.push(event.clone());
false
}
_ => true,
});
if pending.is_empty() {
self.pending_events.remove(conversation_id);
}
subagent_events
}
/// Moves all awaiting events back to pending for retry after a failed /// Moves all awaiting events back to pending for retry after a failed
/// send attempt. Increments attempt counts and drops events that have /// send attempt. Increments attempt counts and drops events that have
/// exhausted their retry limit. /// exhausted their retry limit.
@@ -1109,6 +1176,101 @@ impl OrchestrationEventService {
self.awaiting_server_echo_events.remove(&conversation_id); self.awaiting_server_echo_events.remove(&conversation_id);
} }
} }
/// Route a subagent's AskUserQuestion to the parent conversation for silent auto-answer.
pub fn route_subagent_question_to_parent(
&mut self,
child_conversation_id: AIConversationId,
parent_conversation_id: AIConversationId,
question_text: String,
options: Vec<String>,
depth: u8,
ctx: &mut ModelContext<Self>,
) {
if depth >= MAX_SUBAGENT_QUESTION_DEPTH {
log::warn!(
"Subagent question depth limit reached for conversation {:?}",
child_conversation_id
);
return;
}
let event = PendingEvent {
event_id: Uuid::new_v4().to_string(),
source_agent_id: child_conversation_id.to_string(),
attempt_count: 0,
detail: PendingEventDetail::SubagentQuestion {
source_conversation_id: child_conversation_id,
question_text,
options,
depth,
},
};
self.pending_events
.entry(parent_conversation_id)
.or_default()
.push(event);
ctx.emit(OrchestrationEventServiceEvent::EventsReady {
conversation_id: parent_conversation_id,
});
}
/// Route the parent's answer back to the child subagent.
pub fn route_answer_to_subagent(
&mut self,
child_conversation_id: AIConversationId,
answer_text: String,
ctx: &mut ModelContext<Self>,
) {
let event = PendingEvent {
event_id: Uuid::new_v4().to_string(),
source_agent_id: "parent".to_string(),
attempt_count: 0,
detail: PendingEventDetail::SubagentAnswer {
target_conversation_id: child_conversation_id,
answer_text,
},
};
self.pending_events
.entry(child_conversation_id)
.or_default()
.push(event);
ctx.emit(OrchestrationEventServiceEvent::EventsReady {
conversation_id: child_conversation_id,
});
}
/// Route a subagent's completion summary to the parent conversation.
pub fn route_subagent_completion_summary(
&mut self,
child_conversation_id: AIConversationId,
parent_conversation_id: AIConversationId,
summary_text: String,
ctx: &mut ModelContext<Self>,
) {
let event = PendingEvent {
event_id: Uuid::new_v4().to_string(),
source_agent_id: child_conversation_id.to_string(),
attempt_count: 0,
detail: PendingEventDetail::SubagentCompletionSummary {
source_conversation_id: child_conversation_id,
summary_text,
},
};
self.pending_events
.entry(parent_conversation_id)
.or_default()
.push(event);
ctx.emit(OrchestrationEventServiceEvent::EventsReady {
conversation_id: parent_conversation_id,
});
}
} }
/// `None` means \"subscribe to all lifecycle types\" (input omitted). /// `None` means \"subscribe to all lifecycle types\" (input omitted).
@@ -1135,6 +1297,10 @@ fn did_event_round_trip_through_server(
PendingEventDetail::Lifecycle { event } => { PendingEventDetail::Lifecycle { event } => {
echoed_lifecycle_event_ids.contains(event.event_id.as_str()) echoed_lifecycle_event_ids.contains(event.event_id.as_str())
} }
// Local-only events never round-trip through the server.
PendingEventDetail::SubagentQuestion { .. }
| PendingEventDetail::SubagentAnswer { .. }
| PendingEventDetail::SubagentCompletionSummary { .. } => false,
} }
} }
@@ -33,11 +33,14 @@ pub struct ConversationUsageInfo {
pub lines_added: i32, pub lines_added: i32,
pub lines_removed: i32, pub lines_removed: i32,
pub commands_executed: i32, pub commands_executed: i32,
pub total_input_tokens: u32, /// Live context window token count (from most recent Bedrock response).
pub total_output_tokens: u32, pub current_context_tokens: u32,
pub total_cache_read_tokens: u32, /// Cumulative cost across all requests.
pub total_cache_write_tokens: u32,
pub estimated_cost_cents: f32, pub estimated_cost_cents: f32,
/// Cumulative cache read tokens (session total).
pub total_cache_read_tokens: u32,
/// Cumulative cache write tokens (session total).
pub total_cache_write_tokens: u32,
} }
/// Timing information for the last set of agent responses /// Timing information for the last set of agent responses
@@ -246,30 +249,19 @@ impl ConversationUsageView {
); );
} }
// Token usage section // Context tokens (live state — current context window size)
let total_tokens = self.usage_info.total_input_tokens if self.usage_info.current_context_tokens > 0 {
+ self.usage_info.total_output_tokens labels.push(render_label_text("Context tokens", appearance));
+ self.usage_info.total_cache_read_tokens values.push(render_value_text(
format_token_count(self.usage_info.current_context_tokens),
appearance,
));
}
// Cache usage (cumulative session totals)
let total_cache = self.usage_info.total_cache_read_tokens
+ self.usage_info.total_cache_write_tokens; + self.usage_info.total_cache_write_tokens;
if total_tokens > 0 { if total_cache > 0 {
labels.push(render_label_text("Total tokens", appearance));
values.push(render_value_text(
format_token_count(total_tokens),
appearance,
));
labels.push(render_label_text(" Input", appearance));
values.push(render_value_text(
format_token_count(self.usage_info.total_input_tokens),
appearance,
));
labels.push(render_label_text(" Output", appearance));
values.push(render_value_text(
format_token_count(self.usage_info.total_output_tokens),
appearance,
));
if self.usage_info.total_cache_read_tokens > 0 { if self.usage_info.total_cache_read_tokens > 0 {
labels.push(render_label_text("Cache read", appearance)); labels.push(render_label_text("Cache read", appearance));
values.push(render_value_text( values.push(render_value_text(
@@ -285,6 +277,19 @@ impl ConversationUsageView {
appearance, appearance,
)); ));
} }
// Cache hit rate
let cache_miss = self.usage_info.current_context_tokens
.saturating_sub(self.usage_info.total_cache_read_tokens);
let total_input = self.usage_info.total_cache_read_tokens + cache_miss;
if total_input > 0 {
let hit_rate = (self.usage_info.total_cache_read_tokens as f32 / total_input as f32) * 100.0;
labels.push(render_label_text("Cache hit rate", appearance));
values.push(render_value_text(
format!("{:.0}%", hit_rate),
appearance,
));
}
} }
labels.push(render_label_text("Context window used", appearance)); labels.push(render_label_text("Context window used", appearance));
+2 -2
View File
@@ -1,4 +1,4 @@
use galaxy_core::ui::theme::{Fill, WarpTheme}; use galaxy_core::ui::theme::{Fill, GalaxyTheme};
use galaxy_core::ui::Icon; use galaxy_core::ui::Icon;
use galaxyui::Element; use galaxyui::Element;
@@ -33,7 +33,7 @@ pub fn icon_for_context_window_usage(context_window_usage: f32) -> Icon {
pub fn render_context_window_usage_icon( pub fn render_context_window_usage_icon(
context_window_usage: f32, context_window_usage: f32,
theme: &WarpTheme, theme: &GalaxyTheme,
color_override: Option<Fill>, color_override: Option<Fill>,
) -> Box<dyn Element> { ) -> Box<dyn Element> {
let icon = icon_for_context_window_usage(context_window_usage); let icon = icon_for_context_window_usage(context_window_usage);
+3 -3
View File
@@ -19,7 +19,7 @@ use pathfinder_color::ColorU;
use pathfinder_geometry::vector::vec2f; use pathfinder_geometry::vector::vec2f;
use crate::{ use crate::{
themes::theme::{AnsiColorIdentifier, Fill, WarpTheme}, themes::theme::{AnsiColorIdentifier, Fill, GalaxyTheme},
ui_components::icons::Icon, ui_components::icons::Icon,
}; };
@@ -52,7 +52,7 @@ pub const CLAUDE_ORANGE: ColorU = ColorU {
/// Returns the color to be used for various AI signifiers /// Returns the color to be used for various AI signifiers
/// input with AI mode). /// input with AI mode).
pub fn ai_brand_color(theme: &WarpTheme) -> ColorU { pub fn ai_brand_color(theme: &GalaxyTheme) -> ColorU {
AnsiColorIdentifier::Magenta AnsiColorIdentifier::Magenta
.to_ansi_color(&theme.terminal_colors().normal) .to_ansi_color(&theme.terminal_colors().normal)
.into() .into()
@@ -60,7 +60,7 @@ pub fn ai_brand_color(theme: &WarpTheme) -> ColorU {
/// Returns the color to be used for error UI throughout Agent Mode (like the "request limit /// Returns the color to be used for error UI throughout Agent Mode (like the "request limit
/// exceeded" chip). /// exceeded" chip).
pub fn error_color(theme: &WarpTheme) -> ColorU { pub fn error_color(theme: &GalaxyTheme) -> ColorU {
AnsiColorIdentifier::Red AnsiColorIdentifier::Red
.to_ansi_color(&theme.terminal_colors().normal) .to_ansi_color(&theme.terminal_colors().normal)
.into() .into()
+4 -4
View File
@@ -1,6 +1,6 @@
use galaxy_core::ui::appearance::Appearance; use galaxy_core::ui::appearance::Appearance;
use galaxy_core::ui::color::coloru_with_opacity; use galaxy_core::ui::color::coloru_with_opacity;
use galaxy_core::ui::theme::{Fill, WarpTheme}; use galaxy_core::ui::theme::{Fill, GalaxyTheme};
use galaxyui::color::ColorU; use galaxyui::color::ColorU;
use galaxyui::elements::{ConstrainedBox, Container, CornerRadius, Radius}; use galaxyui::elements::{ConstrainedBox, Container, CornerRadius, Radius};
use galaxyui::Element; use galaxyui::Element;
@@ -13,17 +13,17 @@ use crate::ui_components::icons::Icon;
pub const STATUS_ELEMENT_PADDING: f32 = 2.; pub const STATUS_ELEMENT_PADDING: f32 = 2.;
pub trait StatusElementStyle { pub trait StatusElementStyle {
fn status_icon_and_color(&self, theme: &WarpTheme) -> (Icon, ColorU); fn status_icon_and_color(&self, theme: &GalaxyTheme) -> (Icon, ColorU);
} }
impl StatusElementStyle for ConversationStatus { impl StatusElementStyle for ConversationStatus {
fn status_icon_and_color(&self, theme: &WarpTheme) -> (Icon, ColorU) { fn status_icon_and_color(&self, theme: &GalaxyTheme) -> (Icon, ColorU) {
ConversationStatus::status_icon_and_color(self, theme) ConversationStatus::status_icon_and_color(self, theme)
} }
} }
impl StatusElementStyle for AgentRunDisplayStatus { impl StatusElementStyle for AgentRunDisplayStatus {
fn status_icon_and_color(&self, theme: &WarpTheme) -> (Icon, ColorU) { fn status_icon_and_color(&self, theme: &GalaxyTheme) -> (Icon, ColorU) {
AgentRunDisplayStatus::status_icon_and_color(self, theme) AgentRunDisplayStatus::status_icon_and_color(self, theme)
} }
} }
@@ -664,7 +664,7 @@ pub fn render_plan_auto_sync_toggle(
.finish(); .finish();
let desc_elem = Text::new( let desc_elem = Text::new(
"The plans this agent creates will be automatically added and synced to Warp Drive." "The plans this agent creates will be automatically added and synced to Galaxy Drive."
.to_string(), .to_string(),
appearance.ui_font_family(), appearance.ui_font_family(),
11., 11.,
+20 -11
View File
@@ -5,49 +5,58 @@ pub struct PredefinedRule {
pub const SYSTEM_DEFINED_RULE_PREFIX: &str = "System Defined Rule"; pub const SYSTEM_DEFINED_RULE_PREFIX: &str = "System Defined Rule";
pub fn is_predefined_rule(name: &str) -> bool {
PREDEFINED_RULES.iter().any(|r| r.name == name)
|| name.starts_with(SYSTEM_DEFINED_RULE_PREFIX)
}
pub fn predefined_rule_index(name: &str) -> Option<usize> {
PREDEFINED_RULES.iter().position(|r| r.name == name)
}
pub const PREDEFINED_RULES: &[PredefinedRule] = &[ pub const PREDEFINED_RULES: &[PredefinedRule] = &[
PredefinedRule { PredefinedRule {
name: "System Defined Rule #1", name: "Correctness Over Speed",
content: "Prioritize correctness, completeness, and reliability over speed.", content: "Prioritize correctness, completeness, and reliability over speed.",
}, },
PredefinedRule { PredefinedRule {
name: "System Defined Rule #2", name: "Never Guess",
content: "Never guess. If uncertain, explicitly say so and verify before finalizing.", content: "Never guess. If uncertain, explicitly say so and verify before finalizing.",
}, },
PredefinedRule { PredefinedRule {
name: "System Defined Rule #3", name: "Evidence-Based Claims",
content: "Ground non-trivial claims in evidence (repo files, command output, tests, official documentation).", content: "Ground non-trivial claims in evidence (repo files, command output, tests, official documentation).",
}, },
PredefinedRule { PredefinedRule {
name: "System Defined Rule #4", name: "Verify When Uncertain",
content: "If confidence is not high, or if a claim depends on external/current behavior, perform web verification before answering; prioritize official docs and cross-check with at least one additional reliable source.", content: "If confidence is not high, or if a claim depends on external/current behavior, perform web verification before answering; prioritize official docs and cross-check with at least one additional reliable source.",
}, },
PredefinedRule { PredefinedRule {
name: "System Defined Rule #5", name: "Separate Facts From Assumptions",
content: "Clearly separate facts, assumptions, and hypotheses.", content: "Clearly separate facts, assumptions, and hypotheses.",
}, },
PredefinedRule { PredefinedRule {
name: "System Defined Rule #6", name: "Ask When Ambiguous",
content: "Ask clarifying questions when ambiguity could change the solution or implementation.", content: "Ask clarifying questions when ambiguity could change the solution or implementation.",
}, },
PredefinedRule { PredefinedRule {
name: "System Defined Rule #7", name: "Validate Code Changes",
content: "For code changes, run relevant validations when available (tests, lint, typecheck, build) and report what was run, what passed/failed, and what was not run.", content: "For code changes, run relevant validations when available (tests, lint, typecheck, build) and report what was run, what passed/failed, and what was not run.",
}, },
PredefinedRule { PredefinedRule {
name: "System Defined Rule #8", name: "Disclose Unvalidated Risks",
content: "If validation cannot be run, state that explicitly and describe residual risk and recommended manual checks.", content: "If validation cannot be run, state that explicitly and describe residual risk and recommended manual checks.",
}, },
PredefinedRule { PredefinedRule {
name: "System Defined Rule #9", name: "Admit Unknowns",
content: "Prefer \"I don't know yet\" over plausible speculation.", content: "Prefer \"I don't know yet\" over plausible speculation.",
}, },
PredefinedRule { PredefinedRule {
name: "System Defined Rule #10", name: "Challenge Bad Ideas",
content: "If the user's idea is wrong, incomplete, risky, or non-optimal, say so directly and respectfully; explain why it may fail and provide a better alternative that still achieves the user's goal.", content: "If the user's idea is wrong, incomplete, risky, or non-optimal, say so directly and respectfully; explain why it may fail and provide a better alternative that still achieves the user's goal.",
}, },
PredefinedRule { PredefinedRule {
name: "System Defined Rule #11", name: "Surface Disagreement",
content: "Do not hide uncertainty, and do not avoid technical disagreement when correctness is at stake.", content: "Do not hide uncertainty, and do not avoid technical disagreement when correctness is at stake.",
}, },
]; ];
+5 -18
View File
@@ -1,7 +1,4 @@
use crate::cloud_object::{ use crate::cloud_object::{CloudObject, CloudObjectSyncStatus};
CloudObject, CloudObjectSyncStatus, GenericStringObjectFormat, JsonObjectType,
};
use crate::drive::CloudObjectTypeAndId;
use crate::network::NetworkStatus; use crate::network::NetworkStatus;
use crate::pane_group::focus_state::PaneFocusHandle; use crate::pane_group::focus_state::PaneFocusHandle;
use crate::pane_group::{pane::view, BackingView, PaneConfiguration, PaneEvent}; use crate::pane_group::{pane::view, BackingView, PaneConfiguration, PaneEvent};
@@ -337,22 +334,12 @@ pub fn is_online(app: &AppContext) -> bool {
NetworkStatus::as_ref(app).is_online() NetworkStatus::as_ref(app).is_online()
} }
pub fn is_delete_allowed(ai_fact: CloudAIFact, app: &AppContext) -> bool { pub fn is_delete_allowed(_ai_fact: CloudAIFact, _app: &AppContext) -> bool {
let cloud_object_type_and_id = CloudObjectTypeAndId::GenericStringObject { true
object_type: GenericStringObjectFormat::Json(JsonObjectType::AIFact),
id: ai_fact.sync_id(),
};
is_online(app)
&& cloud_object_type_and_id.has_server_id()
&& !ai_fact.metadata().has_pending_online_only_change()
} }
pub fn is_edit_allowed(ai_fact: CloudAIFact, app: &AppContext) -> bool { pub fn is_edit_allowed(_ai_fact: CloudAIFact, _app: &AppContext) -> bool {
let cloud_object_type_and_id = CloudObjectTypeAndId::GenericStringObject { true
object_type: GenericStringObjectFormat::Json(JsonObjectType::AIFact),
id: ai_fact.sync_id(),
};
is_online(app) || !cloud_object_type_and_id.has_server_id()
} }
pub fn is_syncing(ai_fact: CloudAIFact, app: &AppContext) -> bool { pub fn is_syncing(ai_fact: CloudAIFact, app: &AppContext) -> bool {
+80 -6
View File
@@ -46,8 +46,12 @@ use markdown_parser::{
use std::fmt::Debug; use std::fmt::Debug;
use std::path::PathBuf; use std::path::PathBuf;
use super::{is_edit_allowed, is_syncing, style, AIFact, CloudAIFact, CloudAIFactModel}; use super::{
use crate::ai::facts::predefined_rules::{PREDEFINED_RULES, SYSTEM_DEFINED_RULE_PREFIX}; is_delete_allowed, is_edit_allowed, is_syncing, style, AIFact, CloudAIFact, CloudAIFactModel,
};
use crate::ai::facts::predefined_rules::{
is_predefined_rule, predefined_rule_index, PREDEFINED_RULES,
};
use crate::ai::facts::AIMemory; use crate::ai::facts::AIMemory;
pub const HEADER_TEXT: &str = "Rules"; pub const HEADER_TEXT: &str = "Rules";
@@ -56,7 +60,7 @@ const DESCRIPTION_TEXT: &str = "Rules enhance the agent by providing structured
const SEARCH_PLACEHOLDER_TEXT: &str = "Search rules"; const SEARCH_PLACEHOLDER_TEXT: &str = "Search rules";
const ZERO_STATE_TEXT: &str = "Once you add a rule, it will be shown here."; const ZERO_STATE_TEXT: &str = "Once you add a rule, it will be shown here.";
const ZERO_STATE_TEXT_PROJECT: &str = const ZERO_STATE_TEXT_PROJECT: &str =
"Once you generate a WARP.md rules file for a project, it will appear here."; "Once you generate a GALAXY.md rules file for a project, it will appear here.";
const DISABLED_BANNER_TEXT: &str = const DISABLED_BANNER_TEXT: &str =
"Your rules are disabled and won't be used as context in sessions. You can "; "Your rules are disabled and won't be used as context in sessions. You can ";
@@ -84,6 +88,7 @@ pub enum RuleViewAction {
AddPredefinedRules, AddPredefinedRules,
InitializeProject, InitializeProject,
Edit(SyncId), Edit(SyncId),
Delete(SyncId),
OpenSettings, OpenSettings,
SelectScope(RuleScope), SelectScope(RuleScope),
OpenFile(PathBuf), OpenFile(PathBuf),
@@ -94,6 +99,7 @@ pub struct MouseStateHandles {
pub hover: MouseStateHandle, pub hover: MouseStateHandle,
pub sync_status_hover: MouseStateHandle, pub sync_status_hover: MouseStateHandle,
pub sync_status_icon: MouseStateHandle, pub sync_status_icon: MouseStateHandle,
pub delete_hover: MouseStateHandle,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -358,13 +364,37 @@ impl RuleView {
.cloned() .cloned()
.collect() .collect()
}; };
self.global_rules = ai_rules let mut rows: Vec<CloudRuleRow> = ai_rules
.into_iter() .into_iter()
.map(|ai_fact| CloudRuleRow { .map(|ai_fact| CloudRuleRow {
fact: ai_fact, fact: ai_fact,
mouse_states: Default::default(), mouse_states: Default::default(),
}) })
.collect(); .collect();
rows.sort_by(|a, b| {
let name_a = match &a.fact.model().string_model {
AIFact::Memory(AIMemory { name, .. }) => name.clone().unwrap_or_default(),
};
let name_b = match &b.fact.model().string_model {
AIFact::Memory(AIMemory { name, .. }) => name.clone().unwrap_or_default(),
};
let is_predefined_a = is_predefined_rule(&name_a);
let is_predefined_b = is_predefined_rule(&name_b);
match (is_predefined_a, is_predefined_b) {
(true, true) => {
let idx_a = predefined_rule_index(&name_a).unwrap_or(usize::MAX);
let idx_b = predefined_rule_index(&name_b).unwrap_or(usize::MAX);
idx_a.cmp(&idx_b)
}
(true, false) => std::cmp::Ordering::Less,
(false, true) => std::cmp::Ordering::Greater,
(false, false) => std::cmp::Ordering::Equal,
}
});
self.global_rules = rows;
ctx.notify(); ctx.notify();
} }
@@ -466,7 +496,7 @@ impl RuleView {
.filter_map(|row| { .filter_map(|row| {
let AIFact::Memory(AIMemory { ref name, .. }) = row.fact.model().string_model; let AIFact::Memory(AIMemory { ref name, .. }) = row.fact.model().string_model;
let name = name.as_deref().unwrap_or_default(); let name = name.as_deref().unwrap_or_default();
if name.starts_with(SYSTEM_DEFINED_RULE_PREFIX) { if is_predefined_rule(name) {
Some(( Some((
name.to_string(), name.to_string(),
(row.fact.sync_id(), row.fact.metadata().revision.clone()), (row.fact.sync_id(), row.fact.metadata().revision.clone()),
@@ -870,7 +900,8 @@ impl RuleView {
let mut row = Flex::row() let mut row = Flex::row()
.with_main_axis_size(MainAxisSize::Max) .with_main_axis_size(MainAxisSize::Max)
.with_main_axis_alignment(MainAxisAlignment::SpaceBetween); .with_main_axis_alignment(MainAxisAlignment::SpaceBetween)
.with_cross_axis_alignment(CrossAxisAlignment::Center);
if let Some(sync_status_icon) = if let Some(sync_status_icon) =
self.render_sync_status_icon(ai_row.clone(), appearance, app) self.render_sync_status_icon(ai_row.clone(), appearance, app)
@@ -880,6 +911,45 @@ impl RuleView {
row.add_child(Expanded::new(1., fact_text).finish()); row.add_child(Expanded::new(1., fact_text).finish());
if is_delete_allowed(ai_row.fact.clone(), app) {
let delete_sync_id = ai_row.fact.sync_id();
let delete_button = Hoverable::new(
ai_row.mouse_states.delete_hover.clone(),
|state| {
let mut container = Container::new(
ConstrainedBox::new(
Icon::Trash
.to_galaxyui_icon(
appearance
.theme()
.sub_text_color(appearance.theme().background()),
)
.finish(),
)
.with_width(16.)
.with_height(16.)
.finish(),
)
.with_uniform_padding(4.)
.with_corner_radius(CornerRadius::with_all(
galaxyui::elements::Radius::Pixels(4.),
));
if state.is_hovered() {
container =
container.with_background(appearance.theme().surface_2());
}
container.finish()
},
)
.with_cursor(Cursor::PointingHand)
.on_click(move |ctx, _, _| {
ctx.dispatch_typed_action(RuleViewAction::Delete(delete_sync_id));
})
.finish();
row.add_child(delete_button);
}
let mut hoverable = Hoverable::new(ai_row.mouse_states.hover.clone(), |state| { let mut hoverable = Hoverable::new(ai_row.mouse_states.hover.clone(), |state| {
let mut bg_color = internal_colors::neutral_1(appearance.theme()); let mut bg_color = internal_colors::neutral_1(appearance.theme());
if state.is_hovered() { if state.is_hovered() {
@@ -904,6 +974,7 @@ impl RuleView {
if is_edit_allowed(ai_row.fact.clone(), app) { if is_edit_allowed(ai_row.fact.clone(), app) {
hoverable = hoverable hoverable = hoverable
.with_cursor(Cursor::PointingHand) .with_cursor(Cursor::PointingHand)
.with_defer_events_to_children()
.on_click(move |ctx, _, _| { .on_click(move |ctx, _, _| {
ctx.dispatch_typed_action(RuleViewAction::Edit(ai_row.fact.sync_id())); ctx.dispatch_typed_action(RuleViewAction::Edit(ai_row.fact.sync_id()));
}); });
@@ -1050,6 +1121,9 @@ impl TypedActionView for RuleView {
RuleViewAction::Edit(sync_id) => { RuleViewAction::Edit(sync_id) => {
ctx.emit(RuleViewEvent::Edit(*sync_id)); ctx.emit(RuleViewEvent::Edit(*sync_id));
} }
RuleViewAction::Delete(sync_id) => {
self.delete_ai_rule(*sync_id, ctx);
}
RuleViewAction::OpenSettings => { RuleViewAction::OpenSettings => {
ctx.emit(RuleViewEvent::OpenSettings); ctx.emit(RuleViewEvent::OpenSettings);
} }
+5 -5
View File
@@ -14,7 +14,7 @@ use crate::{
ParsedTemplatableMCPServerResult, ParsedTemplatableMCPServerResult,
}, },
settings::{ai::AISettings, AISettingsChangedEvent}, settings::{ai::AISettings, AISettingsChangedEvent},
warp_managed_paths_watcher::warp_data_dir, galaxy_managed_paths_watcher::galaxy_data_dir,
}; };
/// Singleton model to manage file-based MCP servers. /// Singleton model to manage file-based MCP servers.
@@ -235,7 +235,7 @@ impl FileBasedMCPManager {
/// config location. /// config location.
/// ///
/// "Global" means the installation was detected outside of a user repository: /// "Global" means the installation was detected outside of a user repository:
/// - For `MCPProvider::Warp`: `warp_data_dir()` (i.e. `~/.warp-core/.mcp.json`). /// - For `MCPProvider::Warp`: `galaxy_data_dir()` (i.e. `~/.warp-core/.mcp.json`).
/// - For any other provider: the user's home directory (e.g. `~/.claude.json`). /// - For any other provider: the user's home directory (e.g. `~/.claude.json`).
/// ///
/// Project-scoped installations (those detected inside a repo) are not considered /// Project-scoped installations (those detected inside a repo) are not considered
@@ -243,7 +243,7 @@ impl FileBasedMCPManager {
/// case this returns `true` due to the global reference). /// case this returns `true` due to the global reference).
fn is_global_server(&self, hash: u64) -> bool { fn is_global_server(&self, hash: u64) -> bool {
let home_dir = dirs::home_dir(); let home_dir = dirs::home_dir();
let warp_root = warp_data_dir(); let warp_root = galaxy_data_dir();
self.file_based_servers_by_root self.file_based_servers_by_root
.iter() .iter()
.any(|(root_path, provider_map)| { .any(|(root_path, provider_map)| {
@@ -264,7 +264,7 @@ impl FileBasedMCPManager {
/// Returns `true` if the server identified by `hash` is referenced from the global /// Returns `true` if the server identified by `hash` is referenced from the global
/// Warp config (`~/.warp/.mcp.json`). Global Warp servers always auto-spawn. /// Warp config (`~/.warp/.mcp.json`). Global Warp servers always auto-spawn.
fn is_global_warp_server(&self, hash: u64) -> bool { fn is_global_warp_server(&self, hash: u64) -> bool {
let warp_root = warp_data_dir(); let warp_root = galaxy_data_dir();
self.file_based_servers_by_root self.file_based_servers_by_root
.get(&warp_root) .get(&warp_root)
.and_then(|provider_map| provider_map.get(&MCPProvider::Warp)) .and_then(|provider_map| provider_map.get(&MCPProvider::Warp))
@@ -432,7 +432,7 @@ impl FileBasedMCPManager {
// Global Warp installs live under `~/.warp-core/`, which is internal Warp state // Global Warp installs live under `~/.warp-core/`, which is internal Warp state
// rather than a meaningful working directory. Map them to the home dir so // rather than a meaningful working directory. Map them to the home dir so
// all global installs (Warp and third-party) share a consistent cwd. // all global installs (Warp and third-party) share a consistent cwd.
if discovery_root == warp_data_dir() { if discovery_root == galaxy_data_dir() {
return dirs::home_dir().or(Some(discovery_root)); return dirs::home_dir().or(Some(discovery_root));
} }
Some(discovery_root) Some(discovery_root)
+3 -3
View File
@@ -3,7 +3,7 @@ use crate::ai::mcp::FileMCPWatcher;
use crate::ai::mcp::ParsedTemplatableMCPServerResult; use crate::ai::mcp::ParsedTemplatableMCPServerResult;
use crate::auth::AuthStateProvider; use crate::auth::AuthStateProvider;
use crate::settings::{AISettings, FocusedTerminalInfo}; use crate::settings::{AISettings, FocusedTerminalInfo};
use crate::warp_managed_paths_watcher::{warp_data_dir, WarpManagedPathsWatcher}; use crate::galaxy_managed_paths_watcher::{galaxy_data_dir, GalaxyManagedPathsWatcher};
use crate::workspaces::user_workspaces::UserWorkspaces; use crate::workspaces::user_workspaces::UserWorkspaces;
use galaxy_core::features::FeatureFlag; use galaxy_core::features::FeatureFlag;
use galaxyui::{App, Entity, ModelHandle, SingletonEntity as _}; use galaxyui::{App, Entity, ModelHandle, SingletonEntity as _};
@@ -22,7 +22,7 @@ fn setup_app(app: &mut App) -> galaxyui::ModelHandle<FileBasedMCPManager> {
app.add_singleton_model(|_| DetectedRepositories::default()); app.add_singleton_model(|_| DetectedRepositories::default());
app.add_singleton_model(RepoMetadataModel::new); app.add_singleton_model(RepoMetadataModel::new);
app.add_singleton_model(HomeDirectoryWatcher::new_for_test); app.add_singleton_model(HomeDirectoryWatcher::new_for_test);
app.add_singleton_model(WarpManagedPathsWatcher::new_for_testing); app.add_singleton_model(GalaxyManagedPathsWatcher::new_for_testing);
app.add_singleton_model(FileMCPWatcher::new); app.add_singleton_model(FileMCPWatcher::new);
app.add_singleton_model(AISettings::new_with_defaults); app.add_singleton_model(AISettings::new_with_defaults);
app.add_singleton_model(|_| AuthStateProvider::new_for_test()); app.add_singleton_model(|_| AuthStateProvider::new_for_test());
@@ -260,7 +260,7 @@ fn test_update_file_based_servers_removes_unreferenced_servers() {
#[test] #[test]
fn test_global_warp_server_always_spawns() { fn test_global_warp_server_always_spawns() {
let _flag_guard = FeatureFlag::FileBasedMcp.override_enabled(true); let _flag_guard = FeatureFlag::FileBasedMcp.override_enabled(true);
let warp_root = warp_data_dir(); let warp_root = galaxy_data_dir();
let parsed = parse_mcp_json(r#"{"global-warp": {"command": "npx", "args": ["warp"]}}"#); let parsed = parse_mcp_json(r#"{"global-warp": {"command": "npx", "args": ["warp"]}}"#);
App::test((), |mut app| async move { App::test((), |mut app| async move {
+7 -7
View File
@@ -19,8 +19,8 @@ use crate::ai::mcp::{
home_config_file_path, parsing::normalize_codex_toml_to_json, MCPProvider, home_config_file_path, parsing::normalize_codex_toml_to_json, MCPProvider,
ParsedTemplatableMCPServerResult, ParsedTemplatableMCPServerResult,
}; };
use crate::warp_managed_paths_watcher::{ use crate::galaxy_managed_paths_watcher::{
warp_managed_mcp_config_path, WarpManagedPathsWatcher, WarpManagedPathsWatcherEvent, galaxy_managed_mcp_config_path, GalaxyManagedPathsWatcher, GalaxyManagedPathsWatcherEvent,
}; };
use crate::HomeDirectoryWatcher; use crate::HomeDirectoryWatcher;
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
@@ -173,12 +173,12 @@ impl FileMCPWatcher {
ctx.subscribe_to_model(&HomeDirectoryWatcher::handle(ctx), |me, event, ctx| { ctx.subscribe_to_model(&HomeDirectoryWatcher::handle(ctx), |me, event, ctx| {
me.handle_home_directory_watcher_event(event, ctx); me.handle_home_directory_watcher_event(event, ctx);
}); });
ctx.subscribe_to_model(&WarpManagedPathsWatcher::handle(ctx), |me, event, ctx| { ctx.subscribe_to_model(&GalaxyManagedPathsWatcher::handle(ctx), |me, event, ctx| {
me.handle_warp_managed_paths_event(event, ctx); me.handle_warp_managed_paths_event(event, ctx);
}); });
let mut home_provider_watchers = HashMap::new(); let mut home_provider_watchers = HashMap::new();
if let Some(mcp_config_path) = warp_managed_mcp_config_path() { if let Some(mcp_config_path) = galaxy_managed_mcp_config_path() {
Self::spawn_config_parse( Self::spawn_config_parse(
mcp_config_path.config_path, mcp_config_path.config_path,
mcp_config_path.root_path, mcp_config_path.root_path,
@@ -418,11 +418,11 @@ impl FileMCPWatcher {
fn handle_warp_managed_paths_event( fn handle_warp_managed_paths_event(
&mut self, &mut self,
event: &WarpManagedPathsWatcherEvent, event: &GalaxyManagedPathsWatcherEvent,
ctx: &mut ModelContext<Self>, ctx: &mut ModelContext<Self>,
) { ) {
let WarpManagedPathsWatcherEvent::FilesChanged(update) = event; let GalaxyManagedPathsWatcherEvent::FilesChanged(update) = event;
let Some(mcp_config_path) = warp_managed_mcp_config_path() else { let Some(mcp_config_path) = galaxy_managed_mcp_config_path() else {
return; return;
}; };
let config_path = mcp_config_path.config_path; let config_path = mcp_config_path.config_path;
+4 -4
View File
@@ -48,7 +48,7 @@ cfg_if::cfg_if! {
pub(crate) fn home_config_file_path(provider: MCPProvider) -> Option<PathBuf> { pub(crate) fn home_config_file_path(provider: MCPProvider) -> Option<PathBuf> {
match provider { match provider {
MCPProvider::Warp => galaxy_core::paths::warp_home_mcp_config_file_path(), MCPProvider::Warp => galaxy_core::paths::galaxy_home_mcp_config_file_path(),
_ => dirs::home_dir().map(|home_dir| home_dir.join(provider.home_config_path())), _ => dirs::home_dir().map(|home_dir| home_dir.join(provider.home_config_path())),
} }
} }
@@ -146,11 +146,11 @@ mod tests {
#[test] #[test]
fn mcp_provider_from_file_path_recognizes_warp_home_path() { fn mcp_provider_from_file_path_recognizes_warp_home_path() {
if let Some(warp_home_mcp_config_file_path) = if let Some(galaxy_home_mcp_config_file_path) =
galaxy_core::paths::warp_home_mcp_config_file_path() galaxy_core::paths::galaxy_home_mcp_config_file_path()
{ {
assert_eq!( assert_eq!(
mcp_provider_from_file_path(&warp_home_mcp_config_file_path), mcp_provider_from_file_path(&galaxy_home_mcp_config_file_path),
Some(MCPProvider::Warp) Some(MCPProvider::Warp)
); );
} }
@@ -15,9 +15,9 @@ use super::{
use watcher::{BulkFilesystemWatcherEvent, HomeDirectoryWatcher, HomeDirectoryWatcherEvent}; use watcher::{BulkFilesystemWatcherEvent, HomeDirectoryWatcher, HomeDirectoryWatcherEvent};
use crate::server::datetime_ext::DateTimeExt; use crate::server::datetime_ext::DateTimeExt;
use crate::warp_managed_paths_watcher::{ use crate::galaxy_managed_paths_watcher::{
filter_repository_update_by_prefix, warp_managed_skill_dirs, WarpManagedPathsWatcher, filter_repository_update_by_prefix, galaxy_managed_skill_dirs, GalaxyManagedPathsWatcher,
WarpManagedPathsWatcherEvent, GalaxyManagedPathsWatcherEvent,
}; };
use ai::skills::{ use ai::skills::{
home_skills_path, parse_skill, ParsedSkill, SkillProvider, SKILL_PROVIDER_DEFINITIONS, home_skills_path, parse_skill, ParsedSkill, SkillProvider, SKILL_PROVIDER_DEFINITIONS,
@@ -124,7 +124,7 @@ impl SkillWatcher {
} }
}, },
); );
ctx.subscribe_to_model(&WarpManagedPathsWatcher::handle(ctx), |me, event, ctx| { ctx.subscribe_to_model(&GalaxyManagedPathsWatcher::handle(ctx), |me, event, ctx| {
me.handle_warp_managed_paths_event(event, ctx); me.handle_warp_managed_paths_event(event, ctx);
}); });
} }
@@ -138,7 +138,7 @@ impl SkillWatcher {
// We use a separate HomeDirectoryWatcher to detect when those are created and start watching them after they are created. // We use a separate HomeDirectoryWatcher to detect when those are created and start watching them after they are created.
let mut home_provider_watchers = HashMap::new(); let mut home_provider_watchers = HashMap::new();
if let Some(home_path) = home_dir { if let Some(home_path) = home_dir {
Self::spawn_read_skills_from_directories(warp_managed_skill_dirs(), ctx); Self::spawn_read_skills_from_directories(galaxy_managed_skill_dirs(), ctx);
let skills_parent_paths: HashSet<PathBuf> = SKILL_PROVIDER_DEFINITIONS let skills_parent_paths: HashSet<PathBuf> = SKILL_PROVIDER_DEFINITIONS
.iter() .iter()
.filter(|provider| provider.provider != SkillProvider::Warp) .filter(|provider| provider.provider != SkillProvider::Warp)
@@ -806,11 +806,11 @@ impl SkillWatcher {
fn handle_warp_managed_paths_event( fn handle_warp_managed_paths_event(
&mut self, &mut self,
event: &WarpManagedPathsWatcherEvent, event: &GalaxyManagedPathsWatcherEvent,
ctx: &mut ModelContext<Self>, ctx: &mut ModelContext<Self>,
) { ) {
let WarpManagedPathsWatcherEvent::FilesChanged(update) = event; let GalaxyManagedPathsWatcherEvent::FilesChanged(update) = event;
for skill_dir in warp_managed_skill_dirs() { for skill_dir in galaxy_managed_skill_dirs() {
if let Some(filtered_update) = filter_repository_update_by_prefix(update, &skill_dir) { if let Some(filtered_update) = filter_repository_update_by_prefix(update, &skill_dir) {
self.handle_repository_update(&filtered_update, ctx); self.handle_repository_update(&filtered_update, ctx);
} }
+3 -3
View File
@@ -12,7 +12,7 @@ use galaxyui::AppContext;
use regex::Regex; use regex::Regex;
use repo_metadata::{local_model::GetContentsArgs, RepoContent, RepoMetadataModel}; use repo_metadata::{local_model::GetContentsArgs, RepoContent, RepoMetadataModel};
use crate::warp_managed_paths_watcher::warp_managed_skill_dirs; use crate::galaxy_managed_paths_watcher::galaxy_managed_skill_dirs;
/// Finds all skill directories in a repository by querying the RepoMetadataModel tree. /// Finds all skill directories in a repository by querying the RepoMetadataModel tree.
/// ///
@@ -100,7 +100,7 @@ pub fn extract_skill_parent_directory(path: &Path) -> Result<PathBuf, Error> {
&& path && path
.parent() .parent()
.and_then(Path::parent) .and_then(Path::parent)
.is_some_and(|parent| warp_managed_skill_dirs().iter().any(|dir| parent == dir)); .is_some_and(|parent| galaxy_managed_skill_dirs().iter().any(|dir| parent == dir));
if is_warp_home_skill { if is_warp_home_skill {
return dirs::home_dir() return dirs::home_dir()
.ok_or_else(|| anyhow::anyhow!("Home directory not available for {}", path.display())); .ok_or_else(|| anyhow::anyhow!("Home directory not available for {}", path.display()));
@@ -136,7 +136,7 @@ pub fn is_home_skill_directory(path: &Path) -> bool {
pub fn is_home_provider_path(path: &Path) -> bool { pub fn is_home_provider_path(path: &Path) -> bool {
SKILL_PROVIDER_DEFINITIONS.iter().any(|provider| { SKILL_PROVIDER_DEFINITIONS.iter().any(|provider| {
if provider.provider == SkillProvider::Warp { if provider.provider == SkillProvider::Warp {
return warp_managed_skill_dirs().iter().any(|dir| path == dir); return galaxy_managed_skill_dirs().iter().any(|dir| path == dir);
} }
home_skills_path(provider.provider) home_skills_path(provider.provider)
.as_ref() .as_ref()
@@ -327,7 +327,7 @@ fn is_home_provider_path_true_for_known_providers() {
let path = home_dir.join(".agents").join("skills"); let path = home_dir.join(".agents").join("skills");
assert!(is_home_provider_path(&path)); assert!(is_home_provider_path(&path));
if let Some(path) = galaxy_core::paths::warp_home_skills_dir() { if let Some(path) = galaxy_core::paths::galaxy_home_skills_dir() {
assert!(is_home_provider_path(&path)); assert!(is_home_provider_path(&path));
} }
@@ -350,12 +350,12 @@ fn extract_skill_parent_directory_returns_home_dir_for_warp_home_skill() {
eprintln!("Skipping test: home directory not available"); eprintln!("Skipping test: home directory not available");
return; return;
}; };
let Some(warp_home_skills_dir) = galaxy_core::paths::warp_home_skills_dir() else { let Some(galaxy_home_skills_dir) = galaxy_core::paths::galaxy_home_skills_dir() else {
eprintln!("Skipping test: Warp home skills directory not available"); eprintln!("Skipping test: Warp home skills directory not available");
return; return;
}; };
let skill_path = warp_home_skills_dir.join("test-skill").join("SKILL.md"); let skill_path = galaxy_home_skills_dir.join("test-skill").join("SKILL.md");
let result = extract_skill_parent_directory(&skill_path); let result = extract_skill_parent_directory(&skill_path);
assert_eq!(result.ok(), Some(home_dir)); assert_eq!(result.ok(), Some(home_dir));
} }
+2 -2
View File
@@ -23,7 +23,7 @@ use galaxyui::AppContext;
use galaxyui::SingletonEntity as _; use galaxyui::SingletonEntity as _;
use super::SkillManager; use super::SkillManager;
use crate::warp_managed_paths_watcher::warp_managed_skill_dirs; use crate::galaxy_managed_paths_watcher::galaxy_managed_skill_dirs;
const SKILL_FILE_NAME: &str = "SKILL.md"; const SKILL_FILE_NAME: &str = "SKILL.md";
@@ -64,7 +64,7 @@ fn home_skill_dirs_for_resolution() -> Vec<PathBuf> {
let mut skill_dirs = Vec::new(); let mut skill_dirs = Vec::new();
for provider in SKILL_PROVIDER_DEFINITIONS.iter() { for provider in SKILL_PROVIDER_DEFINITIONS.iter() {
if provider.provider == SkillProvider::Warp { if provider.provider == SkillProvider::Warp {
for dir in warp_managed_skill_dirs() { for dir in galaxy_managed_skill_dirs() {
push_unique_path(&mut skill_dirs, dir); push_unique_path(&mut skill_dirs, dir);
} }
} else if let Some(dir) = home_skills_path(provider.provider) { } else if let Some(dir) = home_skills_path(provider.provider) {
+7 -7
View File
@@ -1,5 +1,5 @@
use super::*; use super::*;
use crate::warp_managed_paths_watcher::WarpManagedPathsWatcher; use crate::galaxy_managed_paths_watcher::GalaxyManagedPathsWatcher;
use ai::skills::{ParsedSkill, SkillProvider, SkillScope}; use ai::skills::{ParsedSkill, SkillProvider, SkillScope};
use galaxy_core::channel::ChannelState; use galaxy_core::channel::ChannelState;
use galaxyui::App; use galaxyui::App;
@@ -74,7 +74,7 @@ fn get_skills_for_working_directory_scopes_subdirectory_skills() {
let repo_handle = app.add_singleton_model(|_| DetectedRepositories::default()); let repo_handle = app.add_singleton_model(|_| DetectedRepositories::default());
app.add_singleton_model(RepoMetadataModel::new); app.add_singleton_model(RepoMetadataModel::new);
app.add_singleton_model(HomeDirectoryWatcher::new_for_test); app.add_singleton_model(HomeDirectoryWatcher::new_for_test);
app.add_singleton_model(WarpManagedPathsWatcher::new_for_testing); app.add_singleton_model(GalaxyManagedPathsWatcher::new_for_testing);
let skill_manager_handle = app.add_singleton_model(SkillManager::new); let skill_manager_handle = app.add_singleton_model(SkillManager::new);
// Register the repo root so get_root_for_path returns Some. // Register the repo root so get_root_for_path returns Some.
@@ -196,7 +196,7 @@ fn get_skills_for_working_directory_name_collision_returns_both() {
let repo_handle = app.add_singleton_model(|_| DetectedRepositories::default()); let repo_handle = app.add_singleton_model(|_| DetectedRepositories::default());
app.add_singleton_model(RepoMetadataModel::new); app.add_singleton_model(RepoMetadataModel::new);
app.add_singleton_model(HomeDirectoryWatcher::new_for_test); app.add_singleton_model(HomeDirectoryWatcher::new_for_test);
app.add_singleton_model(WarpManagedPathsWatcher::new_for_testing); app.add_singleton_model(GalaxyManagedPathsWatcher::new_for_testing);
let skill_manager_handle = app.add_singleton_model(SkillManager::new); let skill_manager_handle = app.add_singleton_model(SkillManager::new);
// Register the repo root so get_root_for_path returns Some. // Register the repo root so get_root_for_path returns Some.
@@ -292,7 +292,7 @@ fn cloud_environment_skills_always_included() {
let repo_handle = app.add_singleton_model(|_| DetectedRepositories::default()); let repo_handle = app.add_singleton_model(|_| DetectedRepositories::default());
app.add_singleton_model(RepoMetadataModel::new); app.add_singleton_model(RepoMetadataModel::new);
app.add_singleton_model(HomeDirectoryWatcher::new_for_test); app.add_singleton_model(HomeDirectoryWatcher::new_for_test);
app.add_singleton_model(WarpManagedPathsWatcher::new_for_testing); app.add_singleton_model(GalaxyManagedPathsWatcher::new_for_testing);
let skill_manager_handle = app.add_singleton_model(SkillManager::new); let skill_manager_handle = app.add_singleton_model(SkillManager::new);
let canonical_repo_a = let canonical_repo_a =
@@ -493,7 +493,7 @@ fn best_supported_provider_fast_path_returns_deduped_provider() {
app.add_singleton_model(|_| DetectedRepositories::default()); app.add_singleton_model(|_| DetectedRepositories::default());
app.add_singleton_model(RepoMetadataModel::new); app.add_singleton_model(RepoMetadataModel::new);
app.add_singleton_model(HomeDirectoryWatcher::new_for_test); app.add_singleton_model(HomeDirectoryWatcher::new_for_test);
app.add_singleton_model(WarpManagedPathsWatcher::new_for_testing); app.add_singleton_model(GalaxyManagedPathsWatcher::new_for_testing);
let handle = app.add_singleton_model(SkillManager::new); let handle = app.add_singleton_model(SkillManager::new);
let claude_skill = make_skill("deploy", ".claude"); let claude_skill = make_skill("deploy", ".claude");
@@ -518,7 +518,7 @@ fn best_supported_provider_remaps_to_supported_provider() {
app.add_singleton_model(|_| DetectedRepositories::default()); app.add_singleton_model(|_| DetectedRepositories::default());
app.add_singleton_model(RepoMetadataModel::new); app.add_singleton_model(RepoMetadataModel::new);
app.add_singleton_model(HomeDirectoryWatcher::new_for_test); app.add_singleton_model(HomeDirectoryWatcher::new_for_test);
app.add_singleton_model(WarpManagedPathsWatcher::new_for_testing); app.add_singleton_model(GalaxyManagedPathsWatcher::new_for_testing);
let handle = app.add_singleton_model(SkillManager::new); let handle = app.add_singleton_model(SkillManager::new);
let agents_skill = make_skill("deploy", ".agents"); let agents_skill = make_skill("deploy", ".agents");
@@ -548,7 +548,7 @@ fn best_supported_provider_falls_back_when_no_match() {
app.add_singleton_model(|_| DetectedRepositories::default()); app.add_singleton_model(|_| DetectedRepositories::default());
app.add_singleton_model(RepoMetadataModel::new); app.add_singleton_model(RepoMetadataModel::new);
app.add_singleton_model(HomeDirectoryWatcher::new_for_test); app.add_singleton_model(HomeDirectoryWatcher::new_for_test);
app.add_singleton_model(WarpManagedPathsWatcher::new_for_testing); app.add_singleton_model(GalaxyManagedPathsWatcher::new_for_testing);
let handle = app.add_singleton_model(SkillManager::new); let handle = app.add_singleton_model(SkillManager::new);
let agents_skill = make_skill("deploy", ".agents"); let agents_skill = make_skill("deploy", ".agents");
+2 -2
View File
@@ -21,7 +21,7 @@ use std::hash::{Hash, Hasher};
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use crate::warp_managed_paths_watcher::warp_managed_skill_dirs; use crate::galaxy_managed_paths_watcher::galaxy_managed_skill_dirs;
lazy_static! { lazy_static! {
static ref CONTENT_HASHER: SipHasher = SipHasher::new_with_keys(0, 0); static ref CONTENT_HASHER: SipHasher = SipHasher::new_with_keys(0, 0);
@@ -167,7 +167,7 @@ pub fn icon_override_for_skill_name(name: &str) -> Option<Icon> {
pub fn skill_path_from_file_path(file_path: &Path) -> Option<PathBuf> { pub fn skill_path_from_file_path(file_path: &Path) -> Option<PathBuf> {
for definition in SKILL_PROVIDER_DEFINITIONS.iter() { for definition in SKILL_PROVIDER_DEFINITIONS.iter() {
let home_skill_dirs = if definition.provider == SkillProvider::Warp { let home_skill_dirs = if definition.provider == SkillProvider::Warp {
warp_managed_skill_dirs() galaxy_managed_skill_dirs()
} else { } else {
home_skills_path(definition.provider).into_iter().collect() home_skills_path(definition.provider).into_iter().collect()
}; };
+3 -3
View File
@@ -14,18 +14,18 @@ fn test_skill_path_from_file_path_skill_md() {
#[test] #[test]
fn test_skill_path_from_file_path_warp_home_skill() { fn test_skill_path_from_file_path_warp_home_skill() {
let Some(warp_home_skills_dir) = galaxy_core::paths::warp_home_skills_dir() else { let Some(galaxy_home_skills_dir) = galaxy_core::paths::galaxy_home_skills_dir() else {
eprintln!("Skipping test: Warp home skills directory not available"); eprintln!("Skipping test: Warp home skills directory not available");
return; return;
}; };
let warp_home_skill = warp_home_skills_dir let warp_home_skill = galaxy_home_skills_dir
.join("my-skill") .join("my-skill")
.join("assets") .join("assets")
.join("image.png"); .join("image.png");
let result = skill_path_from_file_path(&warp_home_skill); let result = skill_path_from_file_path(&warp_home_skill);
assert_eq!( assert_eq!(
result, result,
Some(warp_home_skills_dir.join("my-skill").join("SKILL.md")) Some(galaxy_home_skills_dir.join("my-skill").join("SKILL.md"))
); );
} }
+5 -5
View File
@@ -15,7 +15,7 @@ use crate::terminal::alt_screen_reporting::AltScreenReporting;
use crate::terminal::session_settings::SessionSettings; use crate::terminal::session_settings::SessionSettings;
use crate::terminal::settings::{SpacingMode, TerminalSettings}; use crate::terminal::settings::{SpacingMode, TerminalSettings};
use crate::undo_close::UndoCloseStack; use crate::undo_close::UndoCloseStack;
use crate::user_config::WarpConfig; use crate::user_config::GalaxyConfig;
use crate::util::bindings::{self, trigger_to_keystroke, CustomAction}; use crate::util::bindings::{self, trigger_to_keystroke, CustomAction};
use crate::util::links; use crate::util::links;
use crate::workspace::sync_inputs::SyncedInputState; use crate::workspace::sync_inputs::SyncedInputState;
@@ -138,7 +138,7 @@ fn updateable_custom_item_without_checkmark(action: CustomAction, ctx: &AppConte
fn make_new_app_menu(ctx: &AppContext) -> Menu { fn make_new_app_menu(ctx: &AppContext) -> Menu {
let mut menu_items = vec![updateable_custom_item_without_checkmark( let mut menu_items = vec![updateable_custom_item_without_checkmark(
CustomAction::ShowAboutWarp, CustomAction::ShowAboutGalaxy,
ctx, ctx,
)]; )];
@@ -375,7 +375,7 @@ fn make_new_edit_menu(ctx: &AppContext) -> Menu {
fn make_new_view_menu(ctx: &AppContext) -> Menu { fn make_new_view_menu(ctx: &AppContext) -> Menu {
let mut items = vec![ let mut items = vec![
updateable_custom_item_without_checkmark(CustomAction::ToggleWarpDrive, ctx), updateable_custom_item_without_checkmark(CustomAction::ToggleGalaxyDrive, ctx),
MenuItem::Separator, MenuItem::Separator,
updateable_custom_item_without_checkmark(CustomAction::CommandPalette, ctx), updateable_custom_item_without_checkmark(CustomAction::CommandPalette, ctx),
updateable_custom_item_without_checkmark(CustomAction::NavigationPalette, ctx), updateable_custom_item_without_checkmark(CustomAction::NavigationPalette, ctx),
@@ -605,7 +605,7 @@ fn make_new_drive_menu(ctx: &AppContext) -> Menu {
)); ));
items.extend([ items.extend([
MenuItem::Separator, MenuItem::Separator,
updateable_custom_item_without_checkmark(CustomAction::ToggleWarpDrive, ctx), updateable_custom_item_without_checkmark(CustomAction::ToggleGalaxyDrive, ctx),
updateable_custom_item_without_checkmark(CustomAction::SearchDrive, ctx), updateable_custom_item_without_checkmark(CustomAction::SearchDrive, ctx),
updateable_custom_item_without_checkmark(CustomAction::OpenTeamSettings, ctx), updateable_custom_item_without_checkmark(CustomAction::OpenTeamSettings, ctx),
updateable_custom_item_without_checkmark(CustomAction::OpenAIFactCollection, ctx), updateable_custom_item_without_checkmark(CustomAction::OpenAIFactCollection, ctx),
@@ -934,7 +934,7 @@ fn make_new_help_menu() -> Menu {
fn make_launch_config_menu_items(ctx: &mut AppContext) -> Vec<MenuItem> { fn make_launch_config_menu_items(ctx: &mut AppContext) -> Vec<MenuItem> {
let mut launch_config_menu_items = vec![]; let mut launch_config_menu_items = vec![];
let launch_configs = WarpConfig::handle(ctx).as_ref(ctx).launch_configs(); let launch_configs = GalaxyConfig::handle(ctx).as_ref(ctx).launch_configs();
for config in launch_configs { for config in launch_configs {
launch_config_menu_items.push(MenuItem::Custom(CustomMenuItem::new( launch_config_menu_items.push(MenuItem::Custom(CustomMenuItem::new(
&config.name, &config.name,
+3 -3
View File
@@ -13,7 +13,7 @@ use crate::ai::ambient_agents::AmbientAgentTaskId;
use crate::ai::blocklist::InputConfig; use crate::ai::blocklist::InputConfig;
use crate::ai::blocklist::SerializedBlockListItem; use crate::ai::blocklist::SerializedBlockListItem;
use crate::code::editor_management::CodeSource; use crate::code::editor_management::CodeSource;
use crate::drive::OpenWarpDriveObjectSettings; use crate::drive::OpenGalaxyDriveObjectSettings;
use crate::root_view::quake_mode_window_id; use crate::root_view::quake_mode_window_id;
use crate::server::ids::SyncId; use crate::server::ids::SyncId;
use crate::settings_view::{environments_page::EnvironmentsPage, SettingsSection}; use crate::settings_view::{environments_page::EnvironmentsPage, SettingsSection};
@@ -216,7 +216,7 @@ pub enum NotebookPaneSnapshot {
/// server ID. /// server ID.
notebook_id: Option<SyncId>, notebook_id: Option<SyncId>,
// Settings for the notebook pane when it's opened (such as a folder to focus upon opening) // Settings for the notebook pane when it's opened (such as a folder to focus upon opening)
settings: OpenWarpDriveObjectSettings, settings: OpenGalaxyDriveObjectSettings,
}, },
LocalFileNotebook { LocalFileNotebook {
/// The path to the local file that was open in this pane. This may be `None` if /// The path to the local file that was open in this pane. This may be `None` if
@@ -255,7 +255,7 @@ pub enum WorkflowPaneSnapshot {
CloudWorkflow { CloudWorkflow {
workflow_id: Option<SyncId>, workflow_id: Option<SyncId>,
// Settings for the workflow pane when it's opened (such as a folder to focus upon opening) // Settings for the workflow pane when it's opened (such as a folder to focus upon opening)
settings: OpenWarpDriveObjectSettings, settings: OpenGalaxyDriveObjectSettings,
}, },
} }
+4 -4
View File
@@ -25,7 +25,7 @@ use crate::{
active_theme_kind, FontSettings, FontSettingsChangedEvent, MonospaceFontSize, Settings, active_theme_kind, FontSettings, FontSettingsChangedEvent, MonospaceFontSize, Settings,
ThemeSettings, ThemeSettings,
}, },
themes::theme::{ThemeKind, WarpTheme}, themes::theme::{ThemeKind, GalaxyTheme},
ASSETS, ASSETS,
}; };
@@ -40,7 +40,7 @@ pub use galaxy_core::ui::appearance::{Appearance, AppearanceEvent};
pub struct AppearanceManager { pub struct AppearanceManager {
// The transient theme is a theme that is set by the user but not saved // The transient theme is a theme that is set by the user but not saved
// as a setting. It is used when the user is actively choosing a theme. // as a setting. It is used when the user is actively choosing a theme.
transient_theme: Option<WarpTheme>, transient_theme: Option<GalaxyTheme>,
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
app_icon_at_startup: AppIcon, app_icon_at_startup: AppIcon,
@@ -438,10 +438,10 @@ fn build_appearance(ctx: &mut AppContext) -> Appearance {
} }
#[cfg(target_family = "wasm")] #[cfg(target_family = "wasm")]
fn emit_theme_background_event(theme: &WarpTheme) { fn emit_theme_background_event(theme: &GalaxyTheme) {
let bg = theme.background().into_solid(); let bg = theme.background().into_solid();
let color = format!("#{:02x}{:02x}{:02x}", bg.r, bg.g, bg.b); let color = format!("#{:02x}{:02x}{:02x}", bg.r, bg.g, bg.b);
crate::platform::wasm::emit_event(crate::platform::wasm::WarpEvent::ThemeBackgroundChanged { crate::platform::wasm::emit_event(crate::platform::wasm::GalaxyEvent::ThemeBackgroundChanged {
color, color,
}); });
} }
+1 -1
View File
@@ -268,7 +268,7 @@ pub fn log_out(app: &mut AppContext) {
} }
#[cfg(target_family = "wasm")] #[cfg(target_family = "wasm")]
crate::platform::wasm::emit_event(crate::platform::wasm::WarpEvent::LoggedOut); crate::platform::wasm::emit_event(crate::platform::wasm::GalaxyEvent::LoggedOut);
} }
// Remove the cloud persisted settings from user defaults. // Remove the cloud persisted settings from user defaults.
+3 -3
View File
@@ -26,7 +26,7 @@ use galaxyui::r#async::Timer;
use galaxyui::windowing::state::ApplicationStage; use galaxyui::windowing::state::ApplicationStage;
use galaxyui::windowing::{self, WindowManager}; use galaxyui::windowing::{self, WindowManager};
use galaxyui::{ use galaxyui::{
accessibility::{AccessibilityContent, WarpA11yRole}, accessibility::{AccessibilityContent, GalaxyA11yRole},
AppContext, AppContext,
}; };
use galaxyui::{Entity, ModelContext, SingletonEntity, ViewContext}; use galaxyui::{Entity, ModelContext, SingletonEntity, ViewContext};
@@ -679,12 +679,12 @@ pub fn accessibility_content(
(RequestType::ManualCheck, Ok(UpdateReady::Yes { .. })) => Some(AccessibilityContent::new( (RequestType::ManualCheck, Ok(UpdateReady::Yes { .. })) => Some(AccessibilityContent::new(
"Update available.", "Update available.",
"Use the command palette to install and relaunch Galaxy", "Use the command palette to install and relaunch Galaxy",
WarpA11yRole::HelpRole, GalaxyA11yRole::HelpRole,
)), )),
// Any non-successful autoupdate check // Any non-successful autoupdate check
(RequestType::ManualCheck, _) => Some(AccessibilityContent::new_without_help( (RequestType::ManualCheck, _) => Some(AccessibilityContent::new_without_help(
"No updates available", "No updates available",
WarpA11yRole::HelpRole, GalaxyA11yRole::HelpRole,
)), )),
_ => None, _ => None,
} }
+4 -4
View File
@@ -25,7 +25,7 @@ use crate::{
drive::{ drive::{
folders::{CloudFolderModel, FolderId}, folders::{CloudFolderModel, FolderId},
items::WarpDriveItem, items::WarpDriveItem,
CloudObjectTypeAndId, OpenWarpDriveObjectArgs, OpenWarpDriveObjectSettings, CloudObjectTypeAndId, OpenGalaxyDriveObjectArgs, OpenGalaxyDriveObjectSettings,
}, },
env_vars::CloudEnvVarCollectionModel, env_vars::CloudEnvVarCollectionModel,
notebooks::{CloudNotebookModel, NotebookId}, notebooks::{CloudNotebookModel, NotebookId},
@@ -935,7 +935,7 @@ where
/// can be opened natively in Warp with no web interaction. /// can be opened natively in Warp with no web interaction.
pub fn extract_server_id_and_object_type_from_warp_drive_link( pub fn extract_server_id_and_object_type_from_warp_drive_link(
url: &Url, url: &Url,
) -> Option<OpenWarpDriveObjectArgs> { ) -> Option<OpenGalaxyDriveObjectArgs> {
let server_id = url let server_id = url
.path_segments() .path_segments()
.and_then(|mut segments| segments.next_back()) .and_then(|mut segments| segments.next_back())
@@ -958,13 +958,13 @@ pub fn extract_server_id_and_object_type_from_warp_drive_link(
let invitee_email: Option<String> = query_string.get("invitee_email").map(|s| s.to_string()); let invitee_email: Option<String> = query_string.get("invitee_email").map(|s| s.to_string());
Some(OpenWarpDriveObjectArgs { Some(OpenGalaxyDriveObjectArgs {
object_type, object_type,
server_id: match server_id { server_id: match server_id {
Some(server_id) => server_id.try_into().ok()?, Some(server_id) => server_id.try_into().ok()?,
_ => return None, _ => return None,
}, },
settings: OpenWarpDriveObjectSettings { settings: OpenGalaxyDriveObjectSettings {
focused_folder_id, focused_folder_id,
invitee_email, invitee_email,
}, },
+6 -6
View File
@@ -20,7 +20,7 @@ use galaxyui::elements::{ChildAnchor, OffsetPositioning, Radius, SavePosition, S
use galaxyui::keymap::EditableBinding; use galaxyui::keymap::EditableBinding;
use galaxyui::ui_components::components::UiComponent; use galaxyui::ui_components::components::UiComponent;
pub use galaxyui::{ pub use galaxyui::{
accessibility::{AccessibilityContent, WarpA11yRole}, accessibility::{AccessibilityContent, GalaxyA11yRole},
elements::{ParentElement as _, Stack}, elements::{ParentElement as _, Stack},
geometry::vector::vec2f, geometry::vector::vec2f,
AppContext, AppContext,
@@ -378,10 +378,10 @@ impl CodeEditorFind {
self.searcher.as_ref(ctx).match_count() self.searcher.as_ref(ctx).match_count()
), ),
"Use enter and shift-enter to navigate between matches. Escape to quit.", "Use enter and shift-enter to navigate between matches. Escape to quit.",
WarpA11yRole::UserAction, GalaxyA11yRole::UserAction,
) )
} else { } else {
AccessibilityContent::new_without_help("No results.", WarpA11yRole::UserAction) AccessibilityContent::new_without_help("No results.", GalaxyA11yRole::UserAction)
}; };
ctx.emit_a11y_content(content); ctx.emit_a11y_content(content);
} }
@@ -396,12 +396,12 @@ impl CodeEditorFind {
"Successfully replaced match. Selected match is {match_index} of {remaining_matches}" "Successfully replaced match. Selected match is {match_index} of {remaining_matches}"
), ),
"Continue pressing Enter to replace more matches, or use up/down arrows to navigate.", "Continue pressing Enter to replace more matches, or use up/down arrows to navigate.",
WarpA11yRole::UserAction, GalaxyA11yRole::UserAction,
) )
} else { } else {
AccessibilityContent::new_without_help( AccessibilityContent::new_without_help(
"Successfully replaced the last match.", "Successfully replaced the last match.",
WarpA11yRole::UserAction, GalaxyA11yRole::UserAction,
) )
}; };
ctx.emit_a11y_content(content); ctx.emit_a11y_content(content);
@@ -946,7 +946,7 @@ impl View for CodeEditorFind {
Some(AccessibilityContent::new( Some(AccessibilityContent::new(
description, description,
help_text, help_text,
WarpA11yRole::TextareaRole, GalaxyA11yRole::TextareaRole,
)) ))
} }
+1 -1
View File
@@ -116,7 +116,7 @@ pub enum CodeSource {
}, },
/// Opened from an active AI agent conversation. /// Opened from an active AI agent conversation.
AIAction { id: AIAgentActionId }, AIAction { id: AIAgentActionId },
/// Opened from project rules (WARP.md) file. /// Opened from project rules (GALAXY.md) file.
ProjectRules { path: PathBuf }, ProjectRules { path: PathBuf },
/// Opened from file tree. /// Opened from file tree.
FileTree { path: PathBuf }, FileTree { path: PathBuf },
+2 -2
View File
@@ -6,7 +6,7 @@
use std::{collections::HashMap, path::PathBuf}; use std::{collections::HashMap, path::PathBuf};
use galaxy_core::ui::{ use galaxy_core::ui::{
appearance::Appearance, icons::Icon as WarpIcon, theme::color::internal_colors, appearance::Appearance, icons::Icon as GalaxyIcon, theme::color::internal_colors,
}; };
use galaxy_files::FileModel; use galaxy_files::FileModel;
use galaxyui::{ use galaxyui::{
@@ -520,7 +520,7 @@ fn render_header(
let icon_color = theme.sub_text_color(theme.background()); let icon_color = theme.sub_text_color(theme.background());
let close_button = Hoverable::new(back_mouse_state, move |state| { let close_button = Hoverable::new(back_mouse_state, move |state| {
let close_icon = ConstrainedBox::new( let close_icon = ConstrainedBox::new(
galaxyui::elements::Icon::new(WarpIcon::X.into(), icon_color).finish(), galaxyui::elements::Icon::new(GalaxyIcon::X.into(), icon_color).finish(),
) )
.with_width(16.) .with_width(16.)
.with_height(16.) .with_height(16.)
+5 -5
View File
@@ -10,7 +10,7 @@ use lsp::{
use crate::code::lsp_telemetry::{LspControlActionType, LspEnablementSource, LspTelemetryEvent}; use crate::code::lsp_telemetry::{LspControlActionType, LspEnablementSource, LspTelemetryEvent};
use galaxy_core::ui::theme::color::internal_colors; use galaxy_core::ui::theme::color::internal_colors;
use galaxy_core::ui::theme::{Fill as ThemeFill, WarpTheme}; use galaxy_core::ui::theme::{Fill as ThemeFill, GalaxyTheme};
use galaxy_core::ui::{appearance::Appearance, Icon}; use galaxy_core::ui::{appearance::Appearance, Icon};
use galaxyui::elements::{ use galaxyui::elements::{
ChildAnchor, ChildView, Dismiss, Empty, Hoverable, MainAxisSize, MouseStateHandle, ChildAnchor, ChildView, Dismiss, Empty, Hoverable, MainAxisSize, MouseStateHandle,
@@ -167,7 +167,7 @@ enum LSPServerRenderStatus {
} }
impl LSPServerRenderStatus { impl LSPServerRenderStatus {
fn to_icon_color(&self, theme: &WarpTheme) -> ColorU { fn to_icon_color(&self, theme: &GalaxyTheme) -> ColorU {
match self { match self {
LSPServerRenderStatus::Available => AnsiColorIdentifier::Green LSPServerRenderStatus::Available => AnsiColorIdentifier::Green
.to_ansi_color(&theme.terminal_colors().normal) .to_ansi_color(&theme.terminal_colors().normal)
@@ -285,7 +285,7 @@ impl CodeFooterView {
}) })
} }
fn render_tab_config_info_icon(theme: &WarpTheme) -> Box<dyn Element> { fn render_tab_config_info_icon(theme: &GalaxyTheme) -> Box<dyn Element> {
Container::new( Container::new(
ConstrainedBox::new( ConstrainedBox::new(
Icon::Info Icon::Info
@@ -1376,7 +1376,7 @@ impl CodeFooterView {
/// Computes the aggregate indicator color across all tracked servers. /// Computes the aggregate indicator color across all tracked servers.
/// Priority: Failed > Busy > Stopped > Available. /// Priority: Failed > Busy > Stopped > Available.
fn aggregate_indicator_color(&self, theme: &WarpTheme, app: &AppContext) -> ColorU { fn aggregate_indicator_color(&self, theme: &GalaxyTheme, app: &AppContext) -> ColorU {
if self.lsp_servers.is_empty() { if self.lsp_servers.is_empty() {
return LSPServerRenderStatus::Stopped.to_icon_color(theme); return LSPServerRenderStatus::Stopped.to_icon_color(theme);
} }
@@ -1449,7 +1449,7 @@ impl CodeFooterView {
} }
fn render_status_text( fn render_status_text(
theme: &WarpTheme, theme: &GalaxyTheme,
appearance: &Appearance, appearance: &Appearance,
message: String, message: String,
) -> Box<dyn Element> { ) -> Box<dyn Element> {
+2 -2
View File
@@ -1,6 +1,6 @@
use galaxy_core::ui::{ use galaxy_core::ui::{
appearance::Appearance, appearance::Appearance,
theme::{color::internal_colors, WarpTheme}, theme::{color::internal_colors, GalaxyTheme},
}; };
use galaxy_editor::{ use galaxy_editor::{
content::buffer::InitialBufferState, content::buffer::InitialBufferState,
@@ -575,7 +575,7 @@ impl LocalCodeEditorView {
} }
/// Render a separator line between hover card sections. /// Render a separator line between hover card sections.
fn render_separator(theme: &WarpTheme) -> Box<dyn Element> { fn render_separator(theme: &GalaxyTheme) -> Box<dyn Element> {
Container::new( Container::new(
ConstrainedBox::new( ConstrainedBox::new(
Rect::new() Rect::new()
+3 -3
View File
@@ -172,7 +172,7 @@ use crate::{
editor::InteractionState, editor::InteractionState,
pane_group::pane::{view, BackingView, PaneEvent}, pane_group::pane::{view, BackingView, PaneEvent},
send_telemetry_from_ctx, send_telemetry_from_ctx,
themes::theme::WarpTheme, themes::theme::GalaxyTheme,
}; };
use vec1::Vec1; use vec1::Vec1;
@@ -1383,7 +1383,7 @@ impl CodeReviewView {
let init_project_button = ctx.add_typed_action_view(|_ctx| { let init_project_button = ctx.add_typed_action_view(|_ctx| {
ActionButton::new("Initialize codebase", NakedTheme) ActionButton::new("Initialize codebase", NakedTheme)
.with_size(ButtonSize::Small) .with_size(ButtonSize::Small)
.with_tooltip("Enables codebase indexing and WARP.md") .with_tooltip("Enables codebase indexing and GALAXY.md")
.with_tooltip_alignment(TooltipAlignment::Center) .with_tooltip_alignment(TooltipAlignment::Center)
.on_click(|ctx| { .on_click(|ctx| {
ctx.dispatch_typed_action(CodeReviewAction::InitProjectForCurrentDirectory) ctx.dispatch_typed_action(CodeReviewAction::InitProjectForCurrentDirectory)
@@ -5528,7 +5528,7 @@ impl CodeReviewView {
fn styled_file_content_container( fn styled_file_content_container(
content: Box<dyn Element>, content: Box<dyn Element>,
theme: &WarpTheme, theme: &GalaxyTheme,
) -> Box<dyn Element> { ) -> Box<dyn Element> {
Container::new( Container::new(
Flex::row() Flex::row()
+1 -1
View File
@@ -43,7 +43,7 @@ pub(crate) struct HeaderClickHandler {
/// (rounded corners, neutral background, outline border). /// (rounded corners, neutral background, outline border).
fn comment_card_container( fn comment_card_container(
content: Box<dyn Element>, content: Box<dyn Element>,
theme: &galaxy_core::ui::theme::WarpTheme, theme: &galaxy_core::ui::theme::GalaxyTheme,
) -> Box<dyn Element> { ) -> Box<dyn Element> {
Container::new(content) Container::new(content)
.with_corner_radius(CornerRadius::with_all(Radius::Pixels(8.))) .with_corner_radius(CornerRadius::with_all(Radius::Pixels(8.)))
+1 -1
View File
@@ -9,7 +9,7 @@ use galaxy_core::channel::ChannelState;
use galaxyui::windowing; use galaxyui::windowing;
pub(crate) fn run() -> anyhow::Result<()> { pub(crate) fn run() -> anyhow::Result<()> {
println!("Warp version: {:?}", ChannelState::app_version()); println!("Galaxy version: {:?}", ChannelState::app_version());
#[cfg(not(windows))] #[cfg(not(windows))]
{ {
+4 -4
View File
@@ -2,7 +2,7 @@ use crate::{
appearance::Appearance, appearance::Appearance,
cloud_object::{model::persistence::CloudModel, CloudObject, Owner}, cloud_object::{model::persistence::CloudModel, CloudObject, Owner},
server::{ids::SyncId, sync_queue::SyncQueue}, server::{ids::SyncId, sync_queue::SyncQueue},
themes::theme::WarpTheme, themes::theme::GalaxyTheme,
workspaces::user_workspaces::UserWorkspaces, workspaces::user_workspaces::UserWorkspaces,
}; };
use galaxy_core::ui::theme::Fill; use galaxy_core::ui::theme::Fill;
@@ -184,7 +184,7 @@ impl ImportModal {
fn render_breadcrumbs( fn render_breadcrumbs(
&self, &self,
appearance: &Appearance, appearance: &Appearance,
theme: &WarpTheme, theme: &GalaxyTheme,
app: &AppContext, app: &AppContext,
) -> Box<dyn Element> { ) -> Box<dyn Element> {
let (breadcrumb_text, highlight_indices) = self.breadcrumb(app); let (breadcrumb_text, highlight_indices) = self.breadcrumb(app);
@@ -222,7 +222,7 @@ impl ImportModal {
fn render_header( fn render_header(
&self, &self,
appearance: &Appearance, appearance: &Appearance,
theme: &WarpTheme, theme: &GalaxyTheme,
app: &AppContext, app: &AppContext,
) -> Box<dyn Element> { ) -> Box<dyn Element> {
let top_row = Flex::row() let top_row = Flex::row()
@@ -258,7 +258,7 @@ impl ImportModal {
.finish() .finish()
} }
fn render_body(&self, theme: &WarpTheme) -> Box<dyn Element> { fn render_body(&self, theme: &GalaxyTheme) -> Box<dyn Element> {
Container::new( Container::new(
ConstrainedBox::new( ConstrainedBox::new(
ClippedScrollable::vertical( ClippedScrollable::vertical(
+1 -1
View File
@@ -46,7 +46,7 @@ impl WarpDriveItem for WarpDriveFolder {
fn icon(&self, appearance: &Appearance, color: Option<Fill>) -> Option<Box<dyn Element>> { fn icon(&self, appearance: &Appearance, color: Option<Fill>) -> Option<Box<dyn Element>> {
let icon_fill = let icon_fill =
color.unwrap_or(warp_drive_icon_color(appearance, DriveObjectType::Folder).into()); color.unwrap_or(warp_drive_icon_color(appearance, DriveObjectType::Folder).into());
let icon = if FeatureFlag::WarpPacks.is_enabled() && self.folder.model().is_warp_pack { let icon = if FeatureFlag::GalaxyPacks.is_enabled() && self.folder.model().is_warp_pack {
Icon::PackageCheck Icon::PackageCheck
} else { } else {
Icon::from(DriveObjectType::Folder) Icon::from(DriveObjectType::Folder)
+3 -3
View File
@@ -88,7 +88,7 @@ impl fmt::Display for DriveObjectType {
} }
#[derive(Debug, Clone, Eq, PartialEq, Default)] #[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct OpenWarpDriveObjectSettings { pub struct OpenGalaxyDriveObjectSettings {
/// The folder that should be focused in the Warp Drive when the object is opened. /// The folder that should be focused in the Warp Drive when the object is opened.
pub focused_folder_id: Option<ServerId>, pub focused_folder_id: Option<ServerId>,
/// The email of the user to invite to the object, if the object is being opened via the request access flow. /// The email of the user to invite to the object, if the object is being opened via the request access flow.
@@ -96,10 +96,10 @@ pub struct OpenWarpDriveObjectSettings {
} }
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
pub struct OpenWarpDriveObjectArgs { pub struct OpenGalaxyDriveObjectArgs {
pub object_type: ObjectType, pub object_type: ObjectType,
pub server_id: ServerId, pub server_id: ServerId,
pub settings: OpenWarpDriveObjectSettings, pub settings: OpenGalaxyDriveObjectSettings,
} }
/// Enum to use to pass down type and id between actions to avoid multiplying actions whenever we /// Enum to use to pass down type and id between actions to avoid multiplying actions whenever we
+3 -3
View File
@@ -122,7 +122,7 @@ use galaxyui::text::TextBuffer;
use galaxyui::text_layout::TextStyle; use galaxyui::text_layout::TextStyle;
use galaxyui::windowing::WindowManager; use galaxyui::windowing::WindowManager;
use galaxyui::{ use galaxyui::{
accessibility::{AccessibilityContent, ActionAccessibilityContent, WarpA11yRole}, accessibility::{AccessibilityContent, ActionAccessibilityContent, GalaxyA11yRole},
fonts::Cache as FontCache, fonts::Cache as FontCache,
keymap::{EditableBinding, FixedBinding}, keymap::{EditableBinding, FixedBinding},
AppContext, Element, Entity, ModelAsRef, ModelHandle, View, ViewContext, WindowId, AppContext, Element, Entity, ModelAsRef, ModelHandle, View, ViewContext, WindowId,
@@ -8368,7 +8368,7 @@ impl TypedActionView for EditorView {
) -> ActionAccessibilityContent { ) -> ActionAccessibilityContent {
match action { match action {
EditorAction::UserInsert(text) => ActionAccessibilityContent::Custom( EditorAction::UserInsert(text) => ActionAccessibilityContent::Custom(
AccessibilityContent::new_without_help(text.to_string(), WarpA11yRole::UserAction), AccessibilityContent::new_without_help(text.to_string(), GalaxyA11yRole::UserAction),
), ),
EditorAction::SelectLeft EditorAction::SelectLeft
| EditorAction::SelectToLineEnd | EditorAction::SelectToLineEnd
@@ -8403,7 +8403,7 @@ impl TypedActionView for EditorView {
EditorAction::Paste => { EditorAction::Paste => {
ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help( ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help(
format!("Pasting: {}", self.clipboard_content(ctx)), format!("Pasting: {}", self.clipboard_content(ctx)),
WarpA11yRole::UserAction, GalaxyA11yRole::UserAction,
)) ))
} }
_ => ActionAccessibilityContent::from_debug(), _ => ActionAccessibilityContent::from_debug(),
+6 -6
View File
@@ -27,7 +27,7 @@ use std::{
}; };
use galaxyui::{ use galaxyui::{
accessibility::{AccessibilityContent, WarpA11yRole}, accessibility::{AccessibilityContent, GalaxyA11yRole},
text_layout::TextStyle, text_layout::TextStyle,
AppContext, Entity, ModelAsRef, ModelContext, ModelHandle, AppContext, Entity, ModelAsRef, ModelContext, ModelHandle,
}; };
@@ -505,7 +505,7 @@ impl EditorModel {
let delta = &text[start.as_usize()..end.as_usize()]; let delta = &text[start.as_usize()..end.as_usize()];
match (was_selecting, is_selecting) { match (was_selecting, is_selecting) {
(false, false) => { (false, false) => {
AccessibilityContent::new_without_help(delta, WarpA11yRole::UserAction) AccessibilityContent::new_without_help(delta, GalaxyA11yRole::UserAction)
} }
(_, true) => { (_, true) => {
// Note that Range is start <= x < end, and in our case, when deciding what was the action // Note that Range is start <= x < end, and in our case, when deciding what was the action
@@ -529,10 +529,10 @@ impl EditorModel {
} else { } else {
"unselected" "unselected"
}; };
AccessibilityContent::new(delta, format!(", {action}"), WarpA11yRole::UserAction) AccessibilityContent::new(delta, format!(", {action}"), GalaxyA11yRole::UserAction)
} }
(true, false) => { (true, false) => {
AccessibilityContent::new_without_help("Unselected", WarpA11yRole::UserAction) AccessibilityContent::new_without_help("Unselected", GalaxyA11yRole::UserAction)
} }
} }
} }
@@ -2230,7 +2230,7 @@ impl EditorModel {
ctx.emit_a11y_content(AccessibilityContent::new( ctx.emit_a11y_content(AccessibilityContent::new(
self.selected_text(ctx), self.selected_text(ctx),
", deleted", ", deleted",
WarpA11yRole::UserAction, GalaxyA11yRole::UserAction,
)); ));
self.change_selections(new_selections, ctx); self.change_selections(new_selections, ctx);
self.insert("", None, ctx); self.insert("", None, ctx);
@@ -2254,7 +2254,7 @@ impl EditorModel {
ctx.emit_a11y_content(AccessibilityContent::new( ctx.emit_a11y_content(AccessibilityContent::new(
self.selected_text(ctx), self.selected_text(ctx),
", deleted", ", deleted",
WarpA11yRole::UserAction, GalaxyA11yRole::UserAction,
)); ));
self.change_selections(new_selections, ctx); self.change_selections(new_selections, ctx);
self.insert("", None, ctx); self.insert("", None, ctx);
@@ -16,21 +16,21 @@ use watcher::{BulkFilesystemWatcher, BulkFilesystemWatcherEvent};
/// Duration between filesystem watch events for the Warp managed paths watcher, in milliseconds. /// Duration between filesystem watch events for the Warp managed paths watcher, in milliseconds.
#[cfg(not(target_family = "wasm"))] #[cfg(not(target_family = "wasm"))]
const WARP_MANAGED_PATHS_WATCHER_DEBOUNCE_MILLI_SECS: u64 = 500; const GALAXY_MANAGED_PATHS_WATCHER_DEBOUNCE_MILLI_SECS: u64 = 500;
pub(crate) fn warp_data_dir() -> PathBuf { pub(crate) fn galaxy_data_dir() -> PathBuf {
galaxy_core::paths::data_dir() galaxy_core::paths::data_dir()
} }
#[cfg(target_family = "wasm")] #[cfg(target_family = "wasm")]
pub(crate) fn ensure_warp_watch_roots_exist() {} pub(crate) fn ensure_galaxy_watch_roots_exist() {}
#[cfg(not(target_family = "wasm"))] #[cfg(not(target_family = "wasm"))]
pub(crate) fn ensure_warp_watch_roots_exist() { pub(crate) fn ensure_galaxy_watch_roots_exist() {
let data_dir = warp_data_dir(); let data_dir = galaxy_data_dir();
if let Err(err) = fs::create_dir_all(&data_dir) { if let Err(err) = fs::create_dir_all(&data_dir) {
log::warn!( log::warn!(
"Failed to create Warp data directory {}: {err}", "Failed to create Galaxy data directory {}: {err}",
data_dir.display() data_dir.display()
); );
} }
@@ -39,7 +39,7 @@ pub(crate) fn ensure_warp_watch_roots_exist() {
if config_local_dir != data_dir { if config_local_dir != data_dir {
if let Err(err) = fs::create_dir_all(&config_local_dir) { if let Err(err) = fs::create_dir_all(&config_local_dir) {
log::warn!( log::warn!(
"Failed to create Warp config directory {}: {err}", "Failed to create Galaxy config directory {}: {err}",
config_local_dir.display() config_local_dir.display()
); );
} }
@@ -47,35 +47,35 @@ pub(crate) fn ensure_warp_watch_roots_exist() {
} }
#[cfg_attr(target_family = "wasm", allow(dead_code))] #[cfg_attr(target_family = "wasm", allow(dead_code))]
pub(crate) fn warp_home_config_dir() -> Option<PathBuf> { pub(crate) fn galaxy_home_config_dir() -> Option<PathBuf> {
galaxy_core::paths::warp_home_config_dir() galaxy_core::paths::galaxy_home_config_dir()
} }
pub(crate) fn warp_home_skills_dir() -> Option<PathBuf> { pub(crate) fn galaxy_home_skills_dir() -> Option<PathBuf> {
galaxy_core::paths::warp_home_skills_dir() galaxy_core::paths::galaxy_home_skills_dir()
} }
#[cfg_attr(target_family = "wasm", allow(dead_code))] #[cfg_attr(target_family = "wasm", allow(dead_code))]
pub(crate) fn warp_home_mcp_config_file_path() -> Option<PathBuf> { pub(crate) fn galaxy_home_mcp_config_file_path() -> Option<PathBuf> {
galaxy_core::paths::warp_home_mcp_config_file_path() galaxy_core::paths::galaxy_home_mcp_config_file_path()
} }
#[cfg_attr(target_family = "wasm", allow(dead_code))] #[cfg_attr(target_family = "wasm", allow(dead_code))]
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct WarpMcpConfigPath { pub(crate) struct GalaxyMcpConfigPath {
pub(crate) root_path: PathBuf, pub(crate) root_path: PathBuf,
pub(crate) config_path: PathBuf, pub(crate) config_path: PathBuf,
} }
pub(crate) fn warp_managed_skill_dirs() -> Vec<PathBuf> { pub(crate) fn galaxy_managed_skill_dirs() -> Vec<PathBuf> {
warp_home_skills_dir().into_iter().collect() galaxy_home_skills_dir().into_iter().collect()
} }
#[cfg_attr(target_family = "wasm", allow(dead_code))] #[cfg_attr(target_family = "wasm", allow(dead_code))]
pub(crate) fn warp_managed_mcp_config_path() -> Option<WarpMcpConfigPath> { pub(crate) fn galaxy_managed_mcp_config_path() -> Option<GalaxyMcpConfigPath> {
Some(WarpMcpConfigPath { Some(GalaxyMcpConfigPath {
root_path: home_dir()?, root_path: home_dir()?,
config_path: warp_home_mcp_config_file_path()?, config_path: galaxy_home_mcp_config_file_path()?,
}) })
} }
@@ -200,23 +200,23 @@ fn filesystem_event_to_repository_update(event: &BulkFilesystemWatcherEvent) ->
#[cfg(target_family = "wasm")] #[cfg(target_family = "wasm")]
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) enum WarpManagedPathsWatcherEvent {} pub(crate) enum GalaxyManagedPathsWatcherEvent {}
#[cfg(not(target_family = "wasm"))] #[cfg(not(target_family = "wasm"))]
pub(crate) enum WarpManagedPathsWatcherEvent { pub(crate) enum GalaxyManagedPathsWatcherEvent {
FilesChanged(RepositoryUpdate), FilesChanged(RepositoryUpdate),
} }
#[cfg(not(target_family = "wasm"))] #[cfg(not(target_family = "wasm"))]
pub(crate) struct WarpManagedPathsWatcher { pub(crate) struct GalaxyManagedPathsWatcher {
_watcher: ModelHandle<BulkFilesystemWatcher>, _watcher: ModelHandle<BulkFilesystemWatcher>,
} }
#[cfg(target_family = "wasm")] #[cfg(target_family = "wasm")]
pub(crate) struct WarpManagedPathsWatcher; pub(crate) struct GalaxyManagedPathsWatcher;
#[cfg(not(target_family = "wasm"))] #[cfg(not(target_family = "wasm"))]
impl WarpManagedPathsWatcher { impl GalaxyManagedPathsWatcher {
pub(crate) fn new(ctx: &mut ModelContext<Self>) -> Self { pub(crate) fn new(ctx: &mut ModelContext<Self>) -> Self {
Self::new_internal(ctx, true) Self::new_internal(ctx, true)
} }
@@ -230,7 +230,7 @@ impl WarpManagedPathsWatcher {
let watcher = if should_register_watcher { let watcher = if should_register_watcher {
ctx.add_model(|ctx| { ctx.add_model(|ctx| {
BulkFilesystemWatcher::new( BulkFilesystemWatcher::new(
Duration::from_millis(WARP_MANAGED_PATHS_WATCHER_DEBOUNCE_MILLI_SECS), Duration::from_millis(GALAXY_MANAGED_PATHS_WATCHER_DEBOUNCE_MILLI_SECS),
ctx, ctx,
) )
}) })
@@ -240,7 +240,7 @@ impl WarpManagedPathsWatcher {
ctx.subscribe_to_model(&watcher, Self::handle_fs_event); ctx.subscribe_to_model(&watcher, Self::handle_fs_event);
if should_register_watcher { if should_register_watcher {
let data_dir = warp_data_dir(); let data_dir = galaxy_data_dir();
let config_local_dir = galaxy_core::paths::config_local_dir(); let config_local_dir = galaxy_core::paths::config_local_dir();
let should_register_config_local_dir = config_local_dir != data_dir; let should_register_config_local_dir = config_local_dir != data_dir;
let worktrees_dir = data_dir.join("worktrees"); let worktrees_dir = data_dir.join("worktrees");
@@ -250,7 +250,7 @@ impl WarpManagedPathsWatcher {
data_dir.clone(), data_dir.clone(),
WatchFilter::with_filter(Arc::new(move |path| !path.starts_with(&worktrees_dir))), WatchFilter::with_filter(Arc::new(move |path| !path.starts_with(&worktrees_dir))),
RecursiveMode::Recursive, RecursiveMode::Recursive,
"Warp data directory", "Galaxy data directory",
); );
if should_register_config_local_dir { if should_register_config_local_dir {
Self::register_path( Self::register_path(
@@ -259,42 +259,42 @@ impl WarpManagedPathsWatcher {
config_local_dir.clone(), config_local_dir.clone(),
WatchFilter::accept_all(), WatchFilter::accept_all(),
RecursiveMode::Recursive, RecursiveMode::Recursive,
"Warp config directory", "Galaxy config directory",
); );
} }
if let Some(warp_home_skills_dir) = warp_home_skills_dir() { if let Some(galaxy_home_skills_dir) = galaxy_home_skills_dir() {
if warp_home_skills_dir.exists() if galaxy_home_skills_dir.exists()
&& !warp_home_skills_dir.starts_with(&data_dir) && !galaxy_home_skills_dir.starts_with(&data_dir)
&& (!should_register_config_local_dir && (!should_register_config_local_dir
|| !warp_home_skills_dir.starts_with(&config_local_dir)) || !galaxy_home_skills_dir.starts_with(&config_local_dir))
{ {
Self::register_path( Self::register_path(
ctx, ctx,
&watcher, &watcher,
warp_home_skills_dir, galaxy_home_skills_dir,
WatchFilter::accept_all(), WatchFilter::accept_all(),
RecursiveMode::Recursive, RecursiveMode::Recursive,
"Warp home skills directory", "Galaxy home skills directory",
); );
} }
} }
if let (Some(warp_home_config_dir), Some(warp_home_mcp_config_path)) = if let (Some(galaxy_home_config_dir), Some(warp_home_mcp_config_path)) =
(warp_home_config_dir(), warp_home_mcp_config_file_path()) (galaxy_home_config_dir(), galaxy_home_mcp_config_file_path())
{ {
if warp_home_config_dir.exists() if galaxy_home_config_dir.exists()
&& !warp_home_config_dir.starts_with(&data_dir) && !galaxy_home_config_dir.starts_with(&data_dir)
&& (!should_register_config_local_dir && (!should_register_config_local_dir
|| !warp_home_config_dir.starts_with(&config_local_dir)) || !galaxy_home_config_dir.starts_with(&config_local_dir))
{ {
Self::register_path( Self::register_path(
ctx, ctx,
&watcher, &watcher,
warp_home_config_dir, galaxy_home_config_dir,
WatchFilter::with_filter(Arc::new(move |path| { WatchFilter::with_filter(Arc::new(move |path| {
path == warp_home_mcp_config_path path == warp_home_mcp_config_path
})), })),
RecursiveMode::NonRecursive, RecursiveMode::NonRecursive,
"Warp home MCP config directory", "Galaxy home MCP config directory",
); );
} }
} }
@@ -333,13 +333,13 @@ impl WarpManagedPathsWatcher {
) { ) {
let update = filesystem_event_to_repository_update(event); let update = filesystem_event_to_repository_update(event);
if !update.is_empty() { if !update.is_empty() {
ctx.emit(WarpManagedPathsWatcherEvent::FilesChanged(update)); ctx.emit(GalaxyManagedPathsWatcherEvent::FilesChanged(update));
} }
} }
} }
#[cfg(target_family = "wasm")] #[cfg(target_family = "wasm")]
impl WarpManagedPathsWatcher { impl GalaxyManagedPathsWatcher {
pub(crate) fn new(_ctx: &mut ModelContext<Self>) -> Self { pub(crate) fn new(_ctx: &mut ModelContext<Self>) -> Self {
Self Self
} }
@@ -350,11 +350,11 @@ impl WarpManagedPathsWatcher {
} }
} }
impl Entity for WarpManagedPathsWatcher { impl Entity for GalaxyManagedPathsWatcher {
type Event = WarpManagedPathsWatcherEvent; type Event = GalaxyManagedPathsWatcherEvent;
} }
impl SingletonEntity for WarpManagedPathsWatcher {} impl SingletonEntity for GalaxyManagedPathsWatcher {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@@ -365,25 +365,25 @@ mod tests {
use repo_metadata::{RepositoryUpdate, TargetFile}; use repo_metadata::{RepositoryUpdate, TargetFile};
use super::{ use super::{
filter_repository_update_by_prefix, warp_home_mcp_config_file_path, warp_home_skills_dir, filter_repository_update_by_prefix, galaxy_home_mcp_config_file_path, galaxy_home_skills_dir,
warp_managed_mcp_config_path, warp_managed_skill_dirs, galaxy_managed_mcp_config_path, galaxy_managed_skill_dirs,
}; };
#[test] #[test]
fn warp_managed_skill_dirs_contains_only_warp_home_path() { fn galaxy_managed_skill_dirs_contains_only_warp_home_path() {
let dirs = warp_managed_skill_dirs(); let dirs = galaxy_managed_skill_dirs();
match warp_home_skills_dir() { match galaxy_home_skills_dir() {
Some(warp_home_skills_dir) => assert_eq!(dirs, vec![warp_home_skills_dir]), Some(galaxy_home_skills_dir) => assert_eq!(dirs, vec![galaxy_home_skills_dir]),
None => assert!(dirs.is_empty()), None => assert!(dirs.is_empty()),
} }
} }
#[test] #[test]
fn warp_managed_mcp_config_path_contains_only_warp_home_path() { fn galaxy_managed_mcp_config_path_contains_only_warp_home_path() {
match ( match (
home_dir(), home_dir(),
warp_home_mcp_config_file_path(), galaxy_home_mcp_config_file_path(),
warp_managed_mcp_config_path(), galaxy_managed_mcp_config_path(),
) { ) {
(Some(home_dir), Some(warp_home_mcp_config_path), Some(path)) => { (Some(home_dir), Some(warp_home_mcp_config_path), Some(path)) => {
assert_eq!(path.root_path, home_dir); assert_eq!(path.root_path, home_dir);
+6 -6
View File
@@ -15,7 +15,7 @@ use galaxyui::elements::{
}; };
use galaxyui::ui_components::components::{Coords, UiComponent, UiComponentStyles}; use galaxyui::ui_components::components::{Coords, UiComponent, UiComponentStyles};
use galaxyui::{ use galaxyui::{
accessibility::{AccessibilityContent, WarpA11yRole}, accessibility::{AccessibilityContent, GalaxyA11yRole},
elements::{ elements::{
Align, AnchorPair, Border, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, Align, AnchorPair, Border, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment,
DropShadow, Element, Empty, EventHandler, Flex, Highlight, Icon, OffsetPositioning, DropShadow, Element, Empty, EventHandler, Flex, Highlight, Icon, OffsetPositioning,
@@ -591,13 +591,13 @@ impl InputSuggestions {
ctx.emit_a11y_content(AccessibilityContent::new( ctx.emit_a11y_content(AccessibilityContent::new(
format!("Suggestion: {text}.\n"), format!("Suggestion: {text}.\n"),
desc, desc,
WarpA11yRole::MenuItemRole, GalaxyA11yRole::MenuItemRole,
)); ));
} }
(Some(text), None) => { (Some(text), None) => {
ctx.emit_a11y_content(AccessibilityContent::new_without_help( ctx.emit_a11y_content(AccessibilityContent::new_without_help(
format!("Suggestion: {text}.\n"), format!("Suggestion: {text}.\n"),
WarpA11yRole::MenuItemRole, GalaxyA11yRole::MenuItemRole,
)); ));
} }
_ => {} _ => {}
@@ -621,7 +621,7 @@ impl InputSuggestions {
if let Some(text) = self.get_selected_item_text() { if let Some(text) = self.get_selected_item_text() {
ctx.emit_a11y_content(AccessibilityContent::new_without_help( ctx.emit_a11y_content(AccessibilityContent::new_without_help(
format!("Selected: {text}"), format!("Selected: {text}"),
WarpA11yRole::MenuItemRole, GalaxyA11yRole::MenuItemRole,
)); ));
} }
} }
@@ -646,7 +646,7 @@ impl InputSuggestions {
) { ) {
ctx.emit_a11y_content(AccessibilityContent::new_without_help( ctx.emit_a11y_content(AccessibilityContent::new_without_help(
"Closed suggestions.", "Closed suggestions.",
WarpA11yRole::UserAction, GalaxyA11yRole::UserAction,
)); ));
ctx.emit(Event::CloseSuggestion { ctx.emit(Event::CloseSuggestion {
should_restore_buffer_before_history_up, should_restore_buffer_before_history_up,
@@ -1089,7 +1089,7 @@ impl View for InputSuggestions {
// TODO use bindings from user settings // TODO use bindings from user settings
"Navigate with tab and shift-tab, and confirm with enter. Execute selected command \ "Navigate with tab and shift-tab, and confirm with enter. Execute selected command \
with command + enter. Esc leaves the suggestions menu.", with command + enter. Esc leaves the suggestions menu.",
WarpA11yRole::MenuRole, GalaxyA11yRole::MenuRole,
)) ))
} }
} }
+2 -2
View File
@@ -9,7 +9,7 @@ use string_offset::CharOffset;
use crate::{ use crate::{
cloud_object::{model::persistence::CloudModel, CloudObjectEventEntrypoint, Space}, cloud_object::{model::persistence::CloudModel, CloudObjectEventEntrypoint, Space},
drive::OpenWarpDriveObjectSettings, drive::OpenGalaxyDriveObjectSettings,
integration_testing::view_getters::{notebook_view, workspace_view}, integration_testing::view_getters::{notebook_view, workspace_view},
notebooks::manager::NotebookSource, notebooks::manager::NotebookSource,
server::{ server::{
@@ -83,7 +83,7 @@ pub fn open_notebook(window_key: impl Into<String>, notebook_key: impl Into<Stri
WindowManager::as_ref(ctx).show_window_and_focus_app(*window_id); WindowManager::as_ref(ctx).show_window_and_focus_app(*window_id);
workspace.open_notebook( workspace.open_notebook(
&NotebookSource::Existing(*notebook_id), &NotebookSource::Existing(*notebook_id),
&OpenWarpDriveObjectSettings::default(), &OpenGalaxyDriveObjectSettings::default(),
ctx, ctx,
true, true,
); );
+2 -2
View File
@@ -4,7 +4,7 @@ use galaxyui::{
use crate::{ use crate::{
cloud_object::{model::persistence::CloudModel, CloudObjectEventEntrypoint, Space}, cloud_object::{model::persistence::CloudModel, CloudObjectEventEntrypoint, Space},
drive::OpenWarpDriveObjectSettings, drive::OpenGalaxyDriveObjectSettings,
integration_testing::view_getters::workspace_view, integration_testing::view_getters::workspace_view,
server::{ server::{
cloud_objects::update_manager::UpdateManager, cloud_objects::update_manager::UpdateManager,
@@ -70,7 +70,7 @@ pub fn open_workflow(window_key: impl Into<String>, workflow_key: impl Into<Stri
WindowManager::as_ref(ctx).show_window_and_focus_app(*window_id); WindowManager::as_ref(ctx).show_window_and_focus_app(*window_id);
workspace.open_workflow_in_pane( workspace.open_workflow_in_pane(
&WorkflowOpenSource::Existing(*workflow_id), &WorkflowOpenSource::Existing(*workflow_id),
&OpenWarpDriveObjectSettings::default(), &OpenGalaxyDriveObjectSettings::default(),
WorkflowViewMode::View, WorkflowViewMode::View,
ctx, ctx,
); );
@@ -5,7 +5,7 @@ use crate::{
AppState, BranchSnapshot, LeafContents, LeafSnapshot, NotebookPaneSnapshot, PaneFlex, AppState, BranchSnapshot, LeafContents, LeafSnapshot, NotebookPaneSnapshot, PaneFlex,
PaneNodeSnapshot, SplitDirection, TabSnapshot, TerminalPaneSnapshot, WindowSnapshot, PaneNodeSnapshot, SplitDirection, TabSnapshot, TerminalPaneSnapshot, WindowSnapshot,
}, },
drive::OpenWarpDriveObjectSettings, drive::OpenGalaxyDriveObjectSettings,
tab::SelectedTabColor, tab::SelectedTabColor,
}; };
@@ -81,7 +81,7 @@ fn test_config_from_snapshot_flattens_single_pane() {
custom_vertical_tabs_title: None, custom_vertical_tabs_title: None,
contents: LeafContents::Notebook(NotebookPaneSnapshot::CloudNotebook { contents: LeafContents::Notebook(NotebookPaneSnapshot::CloudNotebook {
notebook_id: None, notebook_id: None,
settings: OpenWarpDriveObjectSettings::default(), settings: OpenGalaxyDriveObjectSettings::default(),
}), }),
}), }),
), ),
@@ -151,7 +151,7 @@ fn test_config_from_snapshot_filters_panes() {
custom_vertical_tabs_title: None, custom_vertical_tabs_title: None,
contents: LeafContents::Notebook(NotebookPaneSnapshot::CloudNotebook { contents: LeafContents::Notebook(NotebookPaneSnapshot::CloudNotebook {
notebook_id: None, notebook_id: None,
settings: OpenWarpDriveObjectSettings::default(), settings: OpenGalaxyDriveObjectSettings::default(),
}), }),
}), }),
), ),
@@ -215,7 +215,7 @@ fn test_config_from_snapshot_filters_tabs() {
custom_vertical_tabs_title: None, custom_vertical_tabs_title: None,
contents: LeafContents::Notebook(NotebookPaneSnapshot::CloudNotebook { contents: LeafContents::Notebook(NotebookPaneSnapshot::CloudNotebook {
notebook_id: None, notebook_id: None,
settings: OpenWarpDriveObjectSettings::default(), settings: OpenGalaxyDriveObjectSettings::default(),
}), }),
}), }),
)], )],
@@ -285,7 +285,7 @@ fn test_config_with_active_tab_index_and_filtered_tabs() {
custom_vertical_tabs_title: None, custom_vertical_tabs_title: None,
contents: LeafContents::Notebook(NotebookPaneSnapshot::CloudNotebook { contents: LeafContents::Notebook(NotebookPaneSnapshot::CloudNotebook {
notebook_id: None, notebook_id: None,
settings: OpenWarpDriveObjectSettings::default(), settings: OpenGalaxyDriveObjectSettings::default(),
}), }),
}), }),
)], )],
@@ -376,7 +376,7 @@ fn test_config_with_active_tab_being_filtered() {
custom_vertical_tabs_title: None, custom_vertical_tabs_title: None,
contents: LeafContents::Notebook(NotebookPaneSnapshot::CloudNotebook { contents: LeafContents::Notebook(NotebookPaneSnapshot::CloudNotebook {
notebook_id: None, notebook_id: None,
settings: OpenWarpDriveObjectSettings::default(), settings: OpenGalaxyDriveObjectSettings::default(),
}), }),
}), }),
)], )],
+5 -5
View File
@@ -8,13 +8,13 @@ use crate::send_telemetry_from_ctx;
use crate::server::telemetry::TelemetryEvent; use crate::server::telemetry::TelemetryEvent;
use crate::user_config::launch_configs_dir; use crate::user_config::launch_configs_dir;
#[cfg(feature = "local_fs")] #[cfg(feature = "local_fs")]
use crate::user_config::{util::file_name_to_human_readable_name, WarpConfig}; use crate::user_config::{util::file_name_to_human_readable_name, GalaxyConfig};
use crate::util::bindings::keybinding_name_to_display_string; use crate::util::bindings::keybinding_name_to_display_string;
#[cfg(feature = "local_fs")] #[cfg(feature = "local_fs")]
use crate::util::openable_file_type::FileTarget; use crate::util::openable_file_type::FileTarget;
use galaxy_core::paths::home_relative_path; use galaxy_core::paths::home_relative_path;
use galaxy_core::ui::theme::Fill; use galaxy_core::ui::theme::Fill;
use galaxyui::accessibility::{AccessibilityContent, WarpA11yRole}; use galaxyui::accessibility::{AccessibilityContent, GalaxyA11yRole};
use galaxyui::elements::{ use galaxyui::elements::{
Align, Border, ChildAnchor, ChildView, Clipped, ConstrainedBox, Container, CornerRadius, Align, Border, ChildAnchor, ChildView, Clipped, ConstrainedBox, Container, CornerRadius,
Element, Empty, Flex, FormattedTextElement, MouseStateHandle, OffsetPositioning, ParentAnchor, Element, Empty, Flex, FormattedTextElement, MouseStateHandle, OffsetPositioning, ParentAnchor,
@@ -153,7 +153,7 @@ pub enum LaunchConfigModalEvent {
/// It's called when the new config was just saved. Note that when we save the configuration, /// It's called when the new config was just saved. Note that when we save the configuration,
/// it take a moment for the file system to register the change, and us to receive it (as there /// it take a moment for the file system to register the change, and us to receive it (as there
/// is a delay in our watcher). But because we actually have the LaunchConfig in our hands /// is a delay in our watcher). But because we actually have the LaunchConfig in our hands
/// already, we may as well save it "manually" to the WarpConfig, while waiting for the update /// already, we may as well save it "manually" to the GalaxyConfig, while waiting for the update
/// from the file system. This event passes a saved config to the handler to let us do that. /// from the file system. This event passes a saved config to the handler to let us do that.
SuccessfullySavedConfig(LaunchConfig), SuccessfullySavedConfig(LaunchConfig),
#[cfg(feature = "local_fs")] #[cfg(feature = "local_fs")]
@@ -263,7 +263,7 @@ impl LaunchConfigSaveModal {
let launch_config_name = file_name_to_human_readable_name(&file_name_candidate); let launch_config_name = file_name_to_human_readable_name(&file_name_candidate);
if let Some(app_state) = &self.current_app_state { if let Some(app_state) = &self.current_app_state {
let launch_config = LaunchConfig::from_snapshot(launch_config_name, app_state); let launch_config = LaunchConfig::from_snapshot(launch_config_name, app_state);
match WarpConfig::save_new_launch_config(file_name_candidate, launch_config.clone()) { match GalaxyConfig::save_new_launch_config(file_name_candidate, launch_config.clone()) {
Ok(file_name) => { Ok(file_name) => {
self.saved_successfully(file_name, ctx); self.saved_successfully(file_name, ctx);
ctx.emit(LaunchConfigModalEvent::SuccessfullySavedConfig( ctx.emit(LaunchConfigModalEvent::SuccessfullySavedConfig(
@@ -661,7 +661,7 @@ impl View for LaunchConfigSaveModal {
"Type the name of the file to which you want to save your "Type the name of the file to which you want to save your
current configuration of windows, tabs, and panes. Use enter to save the current configuration of windows, tabs, and panes. Use enter to save the
launch configuration, esc to quit the save configuration modal.", launch configuration, esc to quit the save configuration modal.",
WarpA11yRole::PopoverRole, GalaxyA11yRole::PopoverRole,
)) ))
} }
} }
+10 -10
View File
@@ -93,7 +93,7 @@ mod view_components;
mod vim_registers; mod vim_registers;
mod voice; mod voice;
mod voltron; mod voltron;
mod warp_managed_paths_watcher; mod galaxy_managed_paths_watcher;
#[cfg(target_family = "wasm")] #[cfg(target_family = "wasm")]
mod wasm_nux_dialog; mod wasm_nux_dialog;
mod window_settings; mod window_settings;
@@ -241,9 +241,9 @@ use crate::terminal::resizable_data::ResizableData;
use crate::terminal::view::inline_banner::ByoLlmAuthBannerSessionState; use crate::terminal::view::inline_banner::ByoLlmAuthBannerSessionState;
use crate::terminal::{AudibleBell, History}; use crate::terminal::{AudibleBell, History};
use crate::undo_close::UndoCloseStack; use crate::undo_close::UndoCloseStack;
use crate::user_config::WarpConfig; use crate::user_config::GalaxyConfig;
use crate::vim_registers::VimRegisters; use crate::vim_registers::VimRegisters;
use crate::warp_managed_paths_watcher::{ensure_warp_watch_roots_exist, WarpManagedPathsWatcher}; use crate::galaxy_managed_paths_watcher::{ensure_galaxy_watch_roots_exist, GalaxyManagedPathsWatcher};
use crate::workflows::aliases::WorkflowAliases; use crate::workflows::aliases::WorkflowAliases;
use crate::workflows::local_workflows::LocalWorkflows; use crate::workflows::local_workflows::LocalWorkflows;
use crate::workspace::{ActiveSession, OneTimeModalModel, ToastStack}; use crate::workspace::{ActiveSession, OneTimeModalModel, ToastStack};
@@ -347,7 +347,7 @@ pub enum LaunchMode {
/// Run the regular GUI application. /// Run the regular GUI application.
App { App {
args: galaxy_cli::AppArgs, args: galaxy_cli::AppArgs,
/// API key for server authentication, if provided via `--api-key` or `WARP_API_KEY`. /// API key for server authentication, if provided via `--api-key` or `GALAXY_API_KEY`.
/// Only used on dogfood channels. /// Only used on dogfood channels.
api_key: Option<String>, api_key: Option<String>,
}, },
@@ -996,14 +996,14 @@ fn initialize_app(
// One-time migration: give Preview its own config directory by // One-time migration: give Preview its own config directory by
// symlinking contents from the shared ~/.warp location. Must run // symlinking contents from the shared ~/.warp location. Must run
// before ensure_warp_watch_roots_exist() creates the new directory. // before ensure_galaxy_watch_roots_exist() creates the new directory.
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
preview_config_migration::migrate_preview_config_dir_if_needed(); preview_config_migration::migrate_preview_config_dir_if_needed();
ensure_warp_watch_roots_exist(); ensure_galaxy_watch_roots_exist();
ctx.add_singleton_model(WarpManagedPathsWatcher::new); ctx.add_singleton_model(GalaxyManagedPathsWatcher::new);
ctx.add_singleton_model(WarpConfig::new); ctx.add_singleton_model(GalaxyConfig::new);
ctx.add_singleton_model(|_ctx| SettingsManager::default()); ctx.add_singleton_model(|_ctx| SettingsManager::default());
let user_defaults_on_startup = settings::init(startup_toml_parse_error, ctx); let user_defaults_on_startup = settings::init(startup_toml_parse_error, ctx);
@@ -2443,7 +2443,7 @@ pub fn enabled_features() -> HashSet<FeatureFlag> {
#[cfg(all(not(windows), feature = "kitty_images"))] #[cfg(all(not(windows), feature = "kitty_images"))]
FeatureFlag::KittyImages, FeatureFlag::KittyImages,
#[cfg(feature = "warp_packs")] #[cfg(feature = "warp_packs")]
FeatureFlag::WarpPacks, FeatureFlag::GalaxyPacks,
#[cfg(feature = "global_ai_analytics_banner")] #[cfg(feature = "global_ai_analytics_banner")]
FeatureFlag::GlobalAIAnalyticsBanner, FeatureFlag::GlobalAIAnalyticsBanner,
#[cfg(feature = "global_ai_analytics_collection")] #[cfg(feature = "global_ai_analytics_collection")]
@@ -2657,7 +2657,7 @@ pub fn enabled_features() -> HashSet<FeatureFlag> {
#[cfg(feature = "agent_view_block_context")] #[cfg(feature = "agent_view_block_context")]
FeatureFlag::AgentViewBlockContext, FeatureFlag::AgentViewBlockContext,
#[cfg(feature = "galaxy_managed_secrets")] #[cfg(feature = "galaxy_managed_secrets")]
FeatureFlag::WarpManagedSecrets, FeatureFlag::GalaxyManagedSecrets,
#[cfg(feature = "v4a_file_diffs")] #[cfg(feature = "v4a_file_diffs")]
FeatureFlag::V4AFileDiffs, FeatureFlag::V4AFileDiffs,
#[cfg(feature = "interactive_conversation_management_view")] #[cfg(feature = "interactive_conversation_management_view")]
+6 -6
View File
@@ -15,7 +15,7 @@ use galaxyui::elements::{
}; };
use galaxyui::WindowId; use galaxyui::WindowId;
use galaxyui::{ use galaxyui::{
accessibility::{AccessibilityContent, ActionAccessibilityContent, WarpA11yRole}, accessibility::{AccessibilityContent, ActionAccessibilityContent, GalaxyA11yRole},
elements::{ elements::{
Align, Border, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, Dismiss, Align, Border, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, Dismiss,
DispatchEventResult, Element, EventHandler, Flex, Hoverable, Icon, MainAxisAlignment, DispatchEventResult, Element, EventHandler, Flex, Hoverable, Icon, MainAxisAlignment,
@@ -2342,28 +2342,28 @@ impl<A: Action + Clone> SubMenu<A> {
Custom(AccessibilityContent::new( Custom(AccessibilityContent::new(
menu_item, menu_item,
instructions, instructions,
WarpA11yRole::TextRole, GalaxyA11yRole::TextRole,
)) ))
} }
OpenSubmenu => Custom(AccessibilityContent::new( OpenSubmenu => Custom(AccessibilityContent::new(
String::from("Submenu Expanded"), String::from("Submenu Expanded"),
"Press the right key to open the selected submenu", "Press the right key to open the selected submenu",
WarpA11yRole::TextRole, GalaxyA11yRole::TextRole,
)), )),
CloseSubmenu(_) => Custom(AccessibilityContent::new( CloseSubmenu(_) => Custom(AccessibilityContent::new(
String::from("Submenu Closed"), String::from("Submenu Closed"),
"Removing focus from a submenu will close the submenu", "Removing focus from a submenu will close the submenu",
WarpA11yRole::TextRole, GalaxyA11yRole::TextRole,
)), )),
Close(_) => Custom(AccessibilityContent::new( Close(_) => Custom(AccessibilityContent::new(
String::from("Menu Closed"), String::from("Menu Closed"),
"Press the escape key to close the menu", "Press the escape key to close the menu",
WarpA11yRole::TextRole, GalaxyA11yRole::TextRole,
)), )),
Enter => Custom(AccessibilityContent::new( Enter => Custom(AccessibilityContent::new(
String::from("Action Selected"), String::from("Action Selected"),
"Press the enter key to execute the selected menu item action", "Press the enter key to execute the selected menu item action",
WarpA11yRole::TextRole, GalaxyA11yRole::TextRole,
)), )),
HoverSubmenuLeafNode { .. } HoverSubmenuLeafNode { .. }
| UnhoverSubmenuParent(_) | UnhoverSubmenuParent(_)
+2 -2
View File
@@ -6,7 +6,7 @@ use galaxy_editor::{
search::{SearchEvent, Searcher}, search::{SearchEvent, Searcher},
}; };
use galaxyui::{ use galaxyui::{
accessibility::{AccessibilityContent, ActionAccessibilityContent, WarpA11yRole}, accessibility::{AccessibilityContent, ActionAccessibilityContent, GalaxyA11yRole},
elements::{ elements::{
Border, ChildAnchor, Clipped, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, Border, ChildAnchor, Clipped, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment,
Empty, Flex, MouseStateHandle, OffsetPositioning, ParentElement, PositionedElementAnchor, Empty, Flex, MouseStateHandle, OffsetPositioning, ParentElement, PositionedElementAnchor,
@@ -565,7 +565,7 @@ impl TypedActionView for FindBar {
}; };
Some(AccessibilityContent::new_without_help( Some(AccessibilityContent::new_without_help(
text, text,
WarpA11yRole::UserAction, GalaxyA11yRole::UserAction,
)) ))
.into() .into()
} }
+3 -3
View File
@@ -2,7 +2,7 @@ use base64::{prelude::BASE64_STANDARD, Engine as _};
use std::{any::Any, borrow::Cow, collections::HashMap, ops::Range, time::Duration}; use std::{any::Any, borrow::Cow, collections::HashMap, ops::Range, time::Duration};
use galaxyui::{ use galaxyui::{
accessibility::{AccessibilityContent, ActionAccessibilityContent, WarpA11yRole}, accessibility::{AccessibilityContent, ActionAccessibilityContent, GalaxyA11yRole},
clipboard::ClipboardContent, clipboard::ClipboardContent,
AppContext, Entity, ModelAsRef, ModelContext, ModelHandle, SingletonEntity, WindowId, AppContext, Entity, ModelAsRef, ModelContext, ModelHandle, SingletonEntity, WindowId,
}; };
@@ -1289,7 +1289,7 @@ impl NotebooksEditorModel {
if let Some(command) = child_model.executable_command(ctx) { if let Some(command) = child_model.executable_command(ctx) {
ctx.emit_a11y_content(AccessibilityContent::new_without_help( ctx.emit_a11y_content(AccessibilityContent::new_without_help(
format!("Selected workflow: {command}"), format!("Selected workflow: {command}"),
WarpA11yRole::TextareaRole, GalaxyA11yRole::TextareaRole,
)); ));
} }
@@ -1650,7 +1650,7 @@ impl NotebooksEditorModel {
let text = format!("{style:?} {action}"); let text = format!("{style:?} {action}");
ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help( ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help(
text, text,
WarpA11yRole::UserAction, GalaxyA11yRole::UserAction,
)) ))
} }
+3 -3
View File
@@ -10,7 +10,7 @@ use galaxy_editor::{
render::model::RenderState, render::model::RenderState,
}; };
use galaxyui::{ use galaxyui::{
accessibility::{AccessibilityContent, ActionAccessibilityContent, WarpA11yRole}, accessibility::{AccessibilityContent, ActionAccessibilityContent, GalaxyA11yRole},
elements::{ elements::{
AnchorPair, Border, ConstrainedBox, Container, CornerRadius, DropShadow, Flex, AnchorPair, Border, ConstrainedBox, Container, CornerRadius, DropShadow, Flex,
MainAxisSize, MouseStateHandle, OffsetPositioning, OffsetType, ParentElement, Point, MainAxisSize, MouseStateHandle, OffsetPositioning, OffsetType, ParentElement, Point,
@@ -447,12 +447,12 @@ impl TypedActionView for Omnibar {
OmnibarAction::ConvertBlock(style) => { OmnibarAction::ConvertBlock(style) => {
ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help( ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help(
format!("Convert to {}", BlockType::from(style).label()), format!("Convert to {}", BlockType::from(style).label()),
WarpA11yRole::UserAction, GalaxyA11yRole::UserAction,
)) ))
} }
OmnibarAction::OpenLinkEditor => ActionAccessibilityContent::from_debug(), OmnibarAction::OpenLinkEditor => ActionAccessibilityContent::from_debug(),
OmnibarAction::UnstyleLink => ActionAccessibilityContent::Custom( OmnibarAction::UnstyleLink => ActionAccessibilityContent::Custom(
AccessibilityContent::new_without_help("Remove link", WarpA11yRole::UserAction), AccessibilityContent::new_without_help("Remove link", GalaxyA11yRole::UserAction),
), ),
} }
} }
+26 -26
View File
@@ -28,7 +28,7 @@ use string_offset::CharOffset;
use galaxy_util::{path::LineAndColumnArg, user_input::UserInput}; use galaxy_util::{path::LineAndColumnArg, user_input::UserInput};
use galaxyui::{ use galaxyui::{
accessibility::{AccessibilityContent, ActionAccessibilityContent, WarpA11yRole}, accessibility::{AccessibilityContent, ActionAccessibilityContent, GalaxyA11yRole},
assets::asset_cache::{AssetCache, AssetHandle, AssetState}, assets::asset_cache::{AssetCache, AssetHandle, AssetState},
clipboard::ClipboardContent, clipboard::ClipboardContent,
elements::{ elements::{
@@ -2440,7 +2440,7 @@ impl RichTextEditorView {
if show_open_in_warp { if show_open_in_warp {
let path_for_warp = path.clone(); let path_for_warp = path.clone();
links.push(TooltipLink { links.push(TooltipLink {
text: "Open in Warp".to_string(), text: "Open in Galaxy".to_string(),
on_click: Box::new(move |ctx: &mut EventContext| { on_click: Box::new(move |ctx: &mut EventContext| {
ctx.dispatch_typed_action(EditorViewAction::OpenFile { ctx.dispatch_typed_action(EditorViewAction::OpenFile {
path: path_for_warp.clone(), path: path_for_warp.clone(),
@@ -3015,13 +3015,13 @@ impl TypedActionView for RichTextEditorView {
EditorViewAction::UserTyped(text) => { EditorViewAction::UserTyped(text) => {
ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help( ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help(
text.clone().into_inner(), text.clone().into_inner(),
WarpA11yRole::UserAction, GalaxyA11yRole::UserAction,
)) ))
} }
EditorViewAction::Paste | EditorViewAction::MiddleClickPaste => { EditorViewAction::Paste | EditorViewAction::MiddleClickPaste => {
ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help( ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help(
format!("Pasting: {}", ctx.clipboard().read().plain_text), format!("Pasting: {}", ctx.clipboard().read().plain_text),
WarpA11yRole::UserAction, GalaxyA11yRole::UserAction,
)) ))
} }
EditorViewAction::Enter EditorViewAction::Enter
@@ -3034,21 +3034,21 @@ impl TypedActionView for RichTextEditorView {
| EditorViewAction::Unindent | EditorViewAction::Unindent
| EditorViewAction::Tab => ActionAccessibilityContent::from_debug(), | EditorViewAction::Tab => ActionAccessibilityContent::from_debug(),
EditorViewAction::ShiftTab => ActionAccessibilityContent::Custom( EditorViewAction::ShiftTab => ActionAccessibilityContent::Custom(
AccessibilityContent::new_without_help("Shift-tab", WarpA11yRole::UserAction), AccessibilityContent::new_without_help("Shift-tab", GalaxyA11yRole::UserAction),
), ),
EditorViewAction::EditLink | EditorViewAction::CreateOrEditLink => { EditorViewAction::EditLink | EditorViewAction::CreateOrEditLink => {
ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help( ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help(
"Edit Link", "Edit Link",
WarpA11yRole::UserAction, GalaxyA11yRole::UserAction,
)) ))
} }
EditorViewAction::CopyLink => ActionAccessibilityContent::Custom( EditorViewAction::CopyLink => ActionAccessibilityContent::Custom(
AccessibilityContent::new_without_help("Copy Link", WarpA11yRole::UserAction), AccessibilityContent::new_without_help("Copy Link", GalaxyA11yRole::UserAction),
), ),
EditorViewAction::OpenTooltipLink(link) => { EditorViewAction::OpenTooltipLink(link) => {
ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help( ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help(
format!("Open link: {}", **link), format!("Open link: {}", **link),
WarpA11yRole::UserAction, GalaxyA11yRole::UserAction,
)) ))
} }
EditorViewAction::SecondaryLinkAction(link) => { EditorViewAction::SecondaryLinkAction(link) => {
@@ -3058,72 +3058,72 @@ impl TypedActionView for RichTextEditorView {
); );
ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help( ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help(
content, content,
WarpA11yRole::UserAction, GalaxyA11yRole::UserAction,
)) ))
} }
EditorViewAction::DeleteLineLeft => { EditorViewAction::DeleteLineLeft => {
ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help( ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help(
"Delete line left", "Delete line left",
WarpA11yRole::UserAction, GalaxyA11yRole::UserAction,
)) ))
} }
EditorViewAction::DeleteLineRight => { EditorViewAction::DeleteLineRight => {
ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help( ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help(
"Delete line right", "Delete line right",
WarpA11yRole::UserAction, GalaxyA11yRole::UserAction,
)) ))
} }
EditorViewAction::DeleteWordLeft => { EditorViewAction::DeleteWordLeft => {
ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help( ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help(
"Delete word left", "Delete word left",
WarpA11yRole::UserAction, GalaxyA11yRole::UserAction,
)) ))
} }
EditorViewAction::DeleteWordRight => { EditorViewAction::DeleteWordRight => {
ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help( ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help(
"Delete word right", "Delete word right",
WarpA11yRole::UserAction, GalaxyA11yRole::UserAction,
)) ))
} }
EditorViewAction::CutLineLeft => ActionAccessibilityContent::Custom( EditorViewAction::CutLineLeft => ActionAccessibilityContent::Custom(
AccessibilityContent::new_without_help("Cut line left", WarpA11yRole::UserAction), AccessibilityContent::new_without_help("Cut line left", GalaxyA11yRole::UserAction),
), ),
EditorViewAction::CutLineRight => ActionAccessibilityContent::Custom( EditorViewAction::CutLineRight => ActionAccessibilityContent::Custom(
AccessibilityContent::new_without_help("Cut line right", WarpA11yRole::UserAction), AccessibilityContent::new_without_help("Cut line right", GalaxyA11yRole::UserAction),
), ),
EditorViewAction::CutWordLeft => ActionAccessibilityContent::Custom( EditorViewAction::CutWordLeft => ActionAccessibilityContent::Custom(
AccessibilityContent::new_without_help("Cut word left", WarpA11yRole::UserAction), AccessibilityContent::new_without_help("Cut word left", GalaxyA11yRole::UserAction),
), ),
EditorViewAction::CutWordRight => ActionAccessibilityContent::Custom( EditorViewAction::CutWordRight => ActionAccessibilityContent::Custom(
AccessibilityContent::new_without_help("Cut word right", WarpA11yRole::UserAction), AccessibilityContent::new_without_help("Cut word right", GalaxyA11yRole::UserAction),
), ),
EditorViewAction::ShowCharacterPalette => { EditorViewAction::ShowCharacterPalette => {
ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help( ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help(
"Show character palette", "Show character palette",
WarpA11yRole::UserAction, GalaxyA11yRole::UserAction,
)) ))
} }
EditorViewAction::ShowFindBar => ActionAccessibilityContent::Custom( EditorViewAction::ShowFindBar => ActionAccessibilityContent::Custom(
AccessibilityContent::new_without_help("Show find bar", WarpA11yRole::UserAction), AccessibilityContent::new_without_help("Show find bar", GalaxyA11yRole::UserAction),
), ),
EditorViewAction::OpenBlockInsertionMenu => { EditorViewAction::OpenBlockInsertionMenu => {
ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help( ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help(
"Open block-insertion menu", "Open block-insertion menu",
WarpA11yRole::UserAction, GalaxyA11yRole::UserAction,
)) ))
} }
EditorViewAction::OpenEmbeddedObjectSearch => { EditorViewAction::OpenEmbeddedObjectSearch => {
ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help( ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help(
"Open embedded object search menu", "Open embedded object search menu",
WarpA11yRole::UserAction, GalaxyA11yRole::UserAction,
)) ))
} }
EditorViewAction::InsertBlock(block_type) => { EditorViewAction::InsertBlock(block_type) => {
ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help( ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help(
format!("Insert {} block", BlockType::from(block_type).label()), format!("Insert {} block", BlockType::from(block_type).label()),
WarpA11yRole::UserAction, GalaxyA11yRole::UserAction,
)) ))
} }
EditorViewAction::Bold => self EditorViewAction::Bold => self
@@ -3150,23 +3150,23 @@ impl TypedActionView for RichTextEditorView {
ActionAccessibilityContent::Custom(AccessibilityContent::new( ActionAccessibilityContent::Custom(AccessibilityContent::new(
"De-select command", "De-select command",
"Switch from selecting commands to selecting text", "Switch from selecting commands to selecting text",
WarpA11yRole::UserAction, GalaxyA11yRole::UserAction,
)) ))
} }
EditorViewAction::CodeBlockTypeSelectedAtOffset { EditorViewAction::CodeBlockTypeSelectedAtOffset {
code_block_type, .. code_block_type, ..
} => ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help( } => ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help(
format!("Change code block language to {code_block_type}"), format!("Change code block language to {code_block_type}"),
WarpA11yRole::UserAction, GalaxyA11yRole::UserAction,
)), )),
EditorViewAction::CopyTextToClipboard { .. } => ActionAccessibilityContent::Custom( EditorViewAction::CopyTextToClipboard { .. } => ActionAccessibilityContent::Custom(
AccessibilityContent::new_without_help("Copy code block", WarpA11yRole::UserAction), AccessibilityContent::new_without_help("Copy code block", GalaxyA11yRole::UserAction),
), ),
EditorViewAction::ToggleTaskList(_) => { EditorViewAction::ToggleTaskList(_) => {
// TODO(ben): Is it useful to include the text and/or on/off state here? // TODO(ben): Is it useful to include the text and/or on/off state here?
ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help( ActionAccessibilityContent::Custom(AccessibilityContent::new_without_help(
"Toggle task list", "Toggle task list",
WarpA11yRole::UserAction, GalaxyA11yRole::UserAction,
)) ))
} }
EditorViewAction::Delete EditorViewAction::Delete
+2 -2
View File
@@ -8,7 +8,7 @@ use galaxy_util::path::user_friendly_path;
#[cfg(feature = "local_fs")] #[cfg(feature = "local_fs")]
use galaxyui::clipboard::ClipboardContent; use galaxyui::clipboard::ClipboardContent;
use galaxyui::{ use galaxyui::{
accessibility::{AccessibilityContent, WarpA11yRole}, accessibility::{AccessibilityContent, GalaxyA11yRole},
elements::{ elements::{
Align, Container, CrossAxisAlignment, DispatchEventResult, Empty, EventHandler, Flex, Align, Container, CrossAxisAlignment, DispatchEventResult, Empty, EventHandler, Flex,
MainAxisAlignment, MainAxisSize, MouseStateHandle, ParentElement, SavePosition, Shrinkable, MainAxisAlignment, MainAxisSize, MouseStateHandle, ParentElement, SavePosition, Shrinkable,
@@ -810,7 +810,7 @@ impl View for FileNotebookView {
fn accessibility_contents(&self, _ctx: &AppContext) -> Option<AccessibilityContent> { fn accessibility_contents(&self, _ctx: &AppContext) -> Option<AccessibilityContent> {
Some(AccessibilityContent::new_without_help( Some(AccessibilityContent::new_without_help(
format!("{} notebook", self.title()), format!("{} notebook", self.title()),
WarpA11yRole::TextRole, GalaxyA11yRole::TextRole,
)) ))
} }
+4 -4
View File
@@ -21,7 +21,7 @@ use crate::util::file::external_editor::EditorSettings;
#[cfg(feature = "local_fs")] #[cfg(feature = "local_fs")]
use crate::util::openable_file_type::{is_supported_image_file, resolve_file_target, FileTarget}; use crate::util::openable_file_type::{is_supported_image_file, resolve_file_target, FileTarget};
use crate::{ use crate::{
drive::OpenWarpDriveObjectArgs, drive::OpenGalaxyDriveObjectArgs,
terminal::model::session::Session, terminal::model::session::Session,
uri::parse_url_paths::{get_item_data_from_warp_link, WarpWebLink}, uri::parse_url_paths::{get_item_data_from_warp_link, WarpWebLink},
workspace::ActiveSession, workspace::ActiveSession,
@@ -266,7 +266,7 @@ impl NotebookLinks {
match link { match link {
LinkTarget::Url(url) => { LinkTarget::Url(url) => {
if let Some(WarpWebLink::DriveObject(args)) = get_item_data_from_warp_link(&url) { if let Some(WarpWebLink::DriveObject(args)) = get_item_data_from_warp_link(&url) {
return ctx.emit(LinkEvent::OpenWarpDriveLink { return ctx.emit(LinkEvent::OpenGalaxyDriveLink {
open_warp_drive_args: *args, open_warp_drive_args: *args,
}); });
} }
@@ -409,8 +409,8 @@ pub enum LinkEvent {
path: PathBuf, path: PathBuf,
session: Arc<Session>, session: Arc<Session>,
}, },
OpenWarpDriveLink { OpenGalaxyDriveLink {
open_warp_drive_args: OpenWarpDriveObjectArgs, open_warp_drive_args: OpenGalaxyDriveObjectArgs,
}, },
/// This event tells the parent pane group to open a new terminal session in the given /// This event tells the parent pane group to open a new terminal session in the given
/// directory. /// directory.
+2 -2
View File
@@ -13,7 +13,7 @@ use crate::{
model::persistence::{CloudModel, CloudModelEvent}, model::persistence::{CloudModel, CloudModelEvent},
Owner, Owner,
}, },
drive::OpenWarpDriveObjectSettings, drive::OpenGalaxyDriveObjectSettings,
pane_group::{NotebookPane, PaneContent}, pane_group::{NotebookPane, PaneContent},
safe_debug, safe_warn, safe_debug, safe_warn,
server::{ server::{
@@ -182,7 +182,7 @@ impl NotebookManager {
pub fn create_pane( pub fn create_pane(
&mut self, &mut self,
source: &NotebookSource, source: &NotebookSource,
settings: &OpenWarpDriveObjectSettings, settings: &OpenGalaxyDriveObjectSettings,
window_id: WindowId, window_id: WindowId,
ctx: &mut ModelContext<Self>, ctx: &mut ModelContext<Self>,
) -> NotebookPane { ) -> NotebookPane {
+7 -7
View File
@@ -16,7 +16,7 @@ use galaxy_editor::{
model::{CoreEditorModel, RichTextEditorModel}, model::{CoreEditorModel, RichTextEditorModel},
}; };
use galaxyui::{ use galaxyui::{
accessibility::{AccessibilityContent, WarpA11yRole}, accessibility::{AccessibilityContent, GalaxyA11yRole},
clipboard::ClipboardContent, clipboard::ClipboardContent,
elements::{ elements::{
Align, Clipped, ConstrainedBox, Container, CrossAxisAlignment, DispatchEventResult, Empty, Align, Clipped, ConstrainedBox, Container, CrossAxisAlignment, DispatchEventResult, Empty,
@@ -52,7 +52,7 @@ use crate::{
drive::{ drive::{
drive_helpers::has_feature_gated_anonymous_user_reached_notebook_limit, drive_helpers::has_feature_gated_anonymous_user_reached_notebook_limit,
export::ExportManager, items::WarpDriveItemId, sharing::ShareableObject, export::ExportManager, items::WarpDriveItemId, sharing::ShareableObject,
CloudObjectTypeAndId, OpenWarpDriveObjectSettings, CloudObjectTypeAndId, OpenGalaxyDriveObjectSettings,
}, },
editor::{ editor::{
EditOrigin, EditorView, Event as EditorEvent, InteractionState, EditOrigin, EditorView, Event as EditorEvent, InteractionState,
@@ -1522,7 +1522,7 @@ impl NotebookView {
pub fn wait_for_initial_load_then_load( pub fn wait_for_initial_load_then_load(
&mut self, &mut self,
notebook_id: SyncId, notebook_id: SyncId,
settings: &OpenWarpDriveObjectSettings, settings: &OpenGalaxyDriveObjectSettings,
window_id: WindowId, window_id: WindowId,
ctx: &mut ViewContext<Self>, ctx: &mut ViewContext<Self>,
) { ) {
@@ -1561,7 +1561,7 @@ impl NotebookView {
fn fetch_and_load_notebook( fn fetch_and_load_notebook(
&mut self, &mut self,
notebook_id: ServerId, notebook_id: ServerId,
settings: &OpenWarpDriveObjectSettings, settings: &OpenGalaxyDriveObjectSettings,
window_id: WindowId, window_id: WindowId,
ctx: &mut ViewContext<Self>, ctx: &mut ViewContext<Self>,
) { ) {
@@ -1605,7 +1605,7 @@ impl NotebookView {
pub fn load( pub fn load(
&mut self, &mut self,
notebook: CloudNotebook, notebook: CloudNotebook,
settings: &OpenWarpDriveObjectSettings, settings: &OpenGalaxyDriveObjectSettings,
ctx: &mut ViewContext<Self>, ctx: &mut ViewContext<Self>,
) -> SpawnedFutureHandle { ) -> SpawnedFutureHandle {
self.set_title(&notebook.model().title, ctx); self.set_title(&notebook.model().title, ctx);
@@ -1868,7 +1868,7 @@ impl NotebookView {
if let Some(notebook) = CloudModel::as_ref(ctx).get_notebook(&id) { if let Some(notebook) = CloudModel::as_ref(ctx).get_notebook(&id) {
self.load( self.load(
notebook.clone(), notebook.clone(),
&OpenWarpDriveObjectSettings::default(), &OpenGalaxyDriveObjectSettings::default(),
ctx, ctx,
); );
} }
@@ -2185,7 +2185,7 @@ impl View for NotebookView {
fn accessibility_contents(&self, ctx: &AppContext) -> Option<AccessibilityContent> { fn accessibility_contents(&self, ctx: &AppContext) -> Option<AccessibilityContent> {
Some(AccessibilityContent::new_without_help( Some(AccessibilityContent::new_without_help(
format!("{} notebook", self.title(ctx)), format!("{} notebook", self.title(ctx)),
WarpA11yRole::TextRole, GalaxyA11yRole::TextRole,
)) ))
} }
+2 -2
View File
@@ -25,7 +25,7 @@ use crate::{
}, },
Owner, Revision, ServerCloudObject, ServerMetadata, ServerNotebook, ServerPermissions, Owner, Revision, ServerCloudObject, ServerMetadata, ServerNotebook, ServerPermissions,
}, },
drive::OpenWarpDriveObjectSettings, drive::OpenGalaxyDriveObjectSettings,
editor::{DisplayPoint, EditorAction, InteractionState, SelectAction}, editor::{DisplayPoint, EditorAction, InteractionState, SelectAction},
network::NetworkStatus, network::NetworkStatus,
notebooks::{ notebooks::{
@@ -140,7 +140,7 @@ fn open_notebook(
notebook: CloudNotebook, notebook: CloudNotebook,
) -> BoxFuture<'static, ()> { ) -> BoxFuture<'static, ()> {
let load_future = handle.update(app, |view, ctx| { let load_future = handle.update(app, |view, ctx| {
view.load(notebook, &OpenWarpDriveObjectSettings::default(), ctx) view.load(notebook, &OpenGalaxyDriveObjectSettings::default(), ctx)
}); });
app.update(|ctx| ctx.await_spawned_future(load_future.future_id())) app.update(|ctx| ctx.await_spawned_future(load_future.future_id()))
} }
+4 -4
View File
@@ -113,7 +113,7 @@ use crate::banner::{Banner, BannerEvent, BannerState, BannerTextContent, Dismiss
use crate::channel::{Channel, ChannelState}; use crate::channel::{Channel, ChannelState};
use crate::code::view::CodeView; use crate::code::view::CodeView;
use crate::drive::items::WarpDriveItemId; use crate::drive::items::WarpDriveItemId;
use crate::drive::{CloudObjectTypeAndId, OpenWarpDriveObjectArgs}; use crate::drive::{CloudObjectTypeAndId, OpenGalaxyDriveObjectArgs};
use crate::features::FeatureFlag; use crate::features::FeatureFlag;
use crate::launch_configs::launch_config::{self, PaneMode, PaneTemplateType}; use crate::launch_configs::launch_config::{self, PaneMode, PaneTemplateType};
use crate::persistence::ModelEvent; use crate::persistence::ModelEvent;
@@ -522,8 +522,8 @@ pub enum Event {
/// The session that the path was opened from. /// The session that the path was opened from.
session: Arc<Session>, session: Arc<Session>,
}, },
OpenWarpDriveLink { OpenGalaxyDriveLink {
open_warp_drive_args: OpenWarpDriveObjectArgs, open_warp_drive_args: OpenGalaxyDriveObjectArgs,
}, },
#[cfg(feature = "local_fs")] #[cfg(feature = "local_fs")]
OpenCodeInWarp { OpenCodeInWarp {
@@ -598,7 +598,7 @@ pub enum Event {
}, },
/// Clears the hovered tab index so it no longer appears as highlighted drop target /// Clears the hovered tab index so it no longer appears as highlighted drop target
ClearHoveredTabIndex, ClearHoveredTabIndex,
OpenWarpDriveObjectInPane(ObjectUid), OpenGalaxyDriveObjectInPane(ObjectUid),
OpenSuggestedAgentModeWorkflowModal { OpenSuggestedAgentModeWorkflowModal {
workflow_and_id: SuggestedAgentModeWorkflowAndId, workflow_and_id: SuggestedAgentModeWorkflowAndId,
}, },
+2 -2
View File
@@ -49,7 +49,7 @@ use crate::{
}, },
test_util::settings::initialize_settings_for_tests, test_util::settings::initialize_settings_for_tests,
undo_close::UndoCloseStack, undo_close::UndoCloseStack,
warp_managed_paths_watcher::WarpManagedPathsWatcher, galaxy_managed_paths_watcher::GalaxyManagedPathsWatcher,
workflows::local_workflows::LocalWorkflows, workflows::local_workflows::LocalWorkflows,
workspace::{ workspace::{
sync_inputs::SyncedInputState, ActiveSession, OneTimeModalModel, WorkspaceRegistry, sync_inputs::SyncedInputState, ActiveSession, OneTimeModalModel, WorkspaceRegistry,
@@ -102,7 +102,7 @@ fn initialize_app(app: &mut App) {
app.add_singleton_model(|_| DetectedRepositories::default()); app.add_singleton_model(|_| DetectedRepositories::default());
app.add_singleton_model(HomeDirectoryWatcher::new_for_test); app.add_singleton_model(HomeDirectoryWatcher::new_for_test);
app.add_singleton_model(DirectoryWatcher::new); app.add_singleton_model(DirectoryWatcher::new);
app.add_singleton_model(WarpManagedPathsWatcher::new_for_testing); app.add_singleton_model(GalaxyManagedPathsWatcher::new_for_testing);
app.add_singleton_model(FileMCPWatcher::new); app.add_singleton_model(FileMCPWatcher::new);
app.add_singleton_model(|_| FileBasedMCPManager::default()); app.add_singleton_model(|_| FileBasedMCPManager::default());
+5 -5
View File
@@ -7,7 +7,7 @@ use galaxyui::{AppContext, ModelHandle, SingletonEntity, ViewContext, ViewHandle
use crate::{ use crate::{
app_state::{LeafContents, NotebookPaneSnapshot}, app_state::{LeafContents, NotebookPaneSnapshot},
cloud_object::Space, cloud_object::Space,
drive::{items::WarpDriveItemId, CloudObjectTypeAndId, OpenWarpDriveObjectSettings}, drive::{items::WarpDriveItemId, CloudObjectTypeAndId, OpenGalaxyDriveObjectSettings},
notebooks::{ notebooks::{
link::{LinkEvent, NotebookLinks}, link::{LinkEvent, NotebookLinks},
manager::{NotebookManager, NotebookSource}, manager::{NotebookManager, NotebookSource},
@@ -47,7 +47,7 @@ impl NotebookPane {
/// Restore a notebook pane given its cloud notebook ID. /// Restore a notebook pane given its cloud notebook ID.
pub fn restore( pub fn restore(
notebook_id: Option<SyncId>, notebook_id: Option<SyncId>,
settings: &OpenWarpDriveObjectSettings, settings: &OpenGalaxyDriveObjectSettings,
ctx: &mut ViewContext<PaneGroup>, ctx: &mut ViewContext<PaneGroup>,
) -> anyhow::Result<Self> { ) -> anyhow::Result<Self> {
let window_id = ctx.window_id(); let window_id = ctx.window_id();
@@ -81,7 +81,7 @@ impl PaneContent for NotebookPane {
let notebook_id = self.notebook_view(app).as_ref(app).notebook_id(app); let notebook_id = self.notebook_view(app).as_ref(app).notebook_id(app);
LeafContents::Notebook(NotebookPaneSnapshot::CloudNotebook { LeafContents::Notebook(NotebookPaneSnapshot::CloudNotebook {
notebook_id, notebook_id,
settings: OpenWarpDriveObjectSettings::default(), settings: OpenGalaxyDriveObjectSettings::default(),
}) })
} }
@@ -183,9 +183,9 @@ pub(super) fn subscribe_to_link_model(
session: session.clone(), session: session.clone(),
}) })
} }
LinkEvent::OpenWarpDriveLink { LinkEvent::OpenGalaxyDriveLink {
open_warp_drive_args, open_warp_drive_args,
} => ctx.emit(crate::pane_group::Event::OpenWarpDriveLink { } => ctx.emit(crate::pane_group::Event::OpenGalaxyDriveLink {
open_warp_drive_args: open_warp_drive_args.clone(), open_warp_drive_args: open_warp_drive_args.clone(),
}), }),
LinkEvent::StartLocalSession { path } => { LinkEvent::StartLocalSession { path } => {
+2 -2
View File
@@ -902,8 +902,8 @@ fn handle_terminal_view_event(
Event::RoleRequestCancelled(role_request_id) => { Event::RoleRequestCancelled(role_request_id) => {
group.remove_shared_session_role_request(role_request_id.clone(), ctx); group.remove_shared_session_role_request(role_request_id.clone(), ctx);
} }
Event::OpenWarpDriveObjectInPane(uid) => { Event::OpenGalaxyDriveObjectInPane(uid) => {
ctx.emit(pane_group::Event::OpenWarpDriveObjectInPane(uid.clone())); ctx.emit(pane_group::Event::OpenGalaxyDriveObjectInPane(uid.clone()));
} }
Event::OpenSuggestedAgentModeWorkflowModal { workflow_and_id } => { Event::OpenSuggestedAgentModeWorkflowModal { workflow_and_id } => {
ctx.emit(pane_group::Event::OpenSuggestedAgentModeWorkflowModal { ctx.emit(pane_group::Event::OpenSuggestedAgentModeWorkflowModal {
+3 -3
View File
@@ -4,7 +4,7 @@ use super::{
}; };
use crate::{ use crate::{
app_state::{LeafContents, WorkflowPaneSnapshot}, app_state::{LeafContents, WorkflowPaneSnapshot},
drive::{items::WarpDriveItemId, OpenWarpDriveObjectSettings}, drive::{items::WarpDriveItemId, OpenGalaxyDriveObjectSettings},
server::ids::SyncId, server::ids::SyncId,
workflows::{ workflows::{
manager::{WorkflowManager, WorkflowOpenSource}, manager::{WorkflowManager, WorkflowOpenSource},
@@ -39,7 +39,7 @@ impl WorkflowPane {
pub fn restore( pub fn restore(
workflow_id: Option<SyncId>, workflow_id: Option<SyncId>,
settings: OpenWarpDriveObjectSettings, settings: OpenGalaxyDriveObjectSettings,
ctx: &mut ViewContext<PaneGroup>, ctx: &mut ViewContext<PaneGroup>,
) -> anyhow::Result<Self> { ) -> anyhow::Result<Self> {
let window_id = ctx.window_id(); let window_id = ctx.window_id();
@@ -132,7 +132,7 @@ impl PaneContent for WorkflowPane {
let workflow_id = self.get_view(app).as_ref(app).workflow_id(); let workflow_id = self.get_view(app).as_ref(app).workflow_id();
LeafContents::Workflow(WorkflowPaneSnapshot::CloudWorkflow { LeafContents::Workflow(WorkflowPaneSnapshot::CloudWorkflow {
workflow_id: Some(workflow_id), workflow_id: Some(workflow_id),
settings: OpenWarpDriveObjectSettings::default(), settings: OpenGalaxyDriveObjectSettings::default(),
}) })
} }
+6 -6
View File
@@ -20,7 +20,7 @@ use std::{fmt, iter, mem};
use super::{ActivationReason, PaneGroup, PaneId}; use super::{ActivationReason, PaneGroup, PaneId};
use crate::pane_group::{get_minimum_pane_size, DraggedBorder, PaneGroupAction}; use crate::pane_group::{get_minimum_pane_size, DraggedBorder, PaneGroupAction};
use crate::themes::theme::WarpTheme; use crate::themes::theme::GalaxyTheme;
use galaxy_core::features::FeatureFlag; use galaxy_core::features::FeatureFlag;
#[cfg(test)] #[cfg(test)]
@@ -492,7 +492,7 @@ impl PaneData {
self.len == 0 self.len == 0
} }
pub fn render(&self, theme: &WarpTheme, app: &AppContext) -> Box<dyn Element> { pub fn render(&self, theme: &GalaxyTheme, app: &AppContext) -> Box<dyn Element> {
match &self.root { match &self.root {
PaneNode::Leaf(pane) => pane.render(app), PaneNode::Leaf(pane) => pane.render(app),
PaneNode::Branch(node) => node.render(theme, &self.hidden_panes, app), PaneNode::Branch(node) => node.render(theme, &self.hidden_panes, app),
@@ -694,7 +694,7 @@ impl PaneNode {
fn render( fn render(
&self, &self,
theme: &WarpTheme, theme: &GalaxyTheme,
hidden_panes: &Vec<HiddenPane>, hidden_panes: &Vec<HiddenPane>,
app: &AppContext, app: &AppContext,
) -> Box<dyn Element> { ) -> Box<dyn Element> {
@@ -1023,7 +1023,7 @@ impl PaneBranch {
fn render( fn render(
&self, &self,
theme: &WarpTheme, theme: &GalaxyTheme,
hidden_panes: &Vec<HiddenPane>, hidden_panes: &Vec<HiddenPane>,
app: &AppContext, app: &AppContext,
) -> Box<dyn Element> { ) -> Box<dyn Element> {
@@ -1355,7 +1355,7 @@ fn create_divider_placeholder(direction: SplitDirection, position_id: &str) -> B
fn create_divider( fn create_divider(
direction: SplitDirection, direction: SplitDirection,
item: &Divider, item: &Divider,
theme: &WarpTheme, theme: &GalaxyTheme,
) -> Box<dyn Element> { ) -> Box<dyn Element> {
let divider = ConstrainedBox::new( let divider = ConstrainedBox::new(
Rect::new() Rect::new()
@@ -1393,7 +1393,7 @@ fn create_divider(
fn create_minimalist_divider( fn create_minimalist_divider(
direction: SplitDirection, direction: SplitDirection,
item: &Divider, item: &Divider,
theme: &WarpTheme, theme: &GalaxyTheme,
) -> Box<dyn Element> { ) -> Box<dyn Element> {
let divider = ConstrainedBox::new( let divider = ConstrainedBox::new(
Rect::new() Rect::new()
+3 -3
View File
@@ -86,7 +86,7 @@ use crate::cloud_object::{
}; };
use crate::code::editor_management::CodeSource; use crate::code::editor_management::CodeSource;
use crate::drive::folders::{CloudFolder, CloudFolderModel, FolderId}; use crate::drive::folders::{CloudFolder, CloudFolderModel, FolderId};
use crate::drive::OpenWarpDriveObjectSettings; use crate::drive::OpenGalaxyDriveObjectSettings;
use crate::env_vars::{CloudEnvVarCollection, CloudEnvVarCollectionModel}; use crate::env_vars::{CloudEnvVarCollection, CloudEnvVarCollectionModel};
use crate::features::FeatureFlag; use crate::features::FeatureFlag;
use crate::notebooks::{CloudNotebook, NotebookId}; use crate::notebooks::{CloudNotebook, NotebookId};
@@ -2519,7 +2519,7 @@ fn read_node(conn: &mut SqliteConnection, node: model::PaneNode) -> Result<PaneN
Some(path) => NotebookPaneSnapshot::LocalFileNotebook { path: Some(path) }, Some(path) => NotebookPaneSnapshot::LocalFileNotebook { path: Some(path) },
None => NotebookPaneSnapshot::CloudNotebook { None => NotebookPaneSnapshot::CloudNotebook {
notebook_id, notebook_id,
settings: OpenWarpDriveObjectSettings::default(), settings: OpenGalaxyDriveObjectSettings::default(),
}, },
}) })
} }
@@ -2537,7 +2537,7 @@ fn read_node(conn: &mut SqliteConnection, node: model::PaneNode) -> Result<PaneN
LeafContents::Workflow(WorkflowPaneSnapshot::CloudWorkflow { LeafContents::Workflow(WorkflowPaneSnapshot::CloudWorkflow {
workflow_id, workflow_id,
settings: OpenWarpDriveObjectSettings::default(), settings: OpenGalaxyDriveObjectSettings::default(),
}) })
} }
CODE_PANE_KIND => { CODE_PANE_KIND => {
+1 -1
View File
@@ -2,7 +2,7 @@ use js_sys::ReferenceError;
use thiserror::Error; use thiserror::Error;
use wasm_bindgen::{JsCast, JsValue}; use wasm_bindgen::{JsCast, JsValue};
pub use galaxy_web_event_bus::{emit_event, WarpEvent}; pub use galaxy_web_event_bus::{emit_event, GalaxyEvent};
/// This function should be called early in application initialization to ensure that /// This function should be called early in application initialization to ensure that
/// static variables are initialized. /// static variables are initialized.
+1 -1
View File
@@ -56,7 +56,7 @@ pub(crate) fn migrate_config_dir_via_symlinks(old_dir: &Path, new_dir: &Path) {
// The existence of new_dir is the migration marker — no separate marker // The existence of new_dir is the migration marker — no separate marker
// file is needed. Once this directory exists (whether created by the // file is needed. Once this directory exists (whether created by the
// migration itself or by ensure_warp_watch_roots_exist on a subsequent // migration itself or by ensure_galaxy_watch_roots_exist on a subsequent
// launch), this function is a no-op. // launch), this function is a no-op.
if new_dir.exists() || !old_dir.exists() { if new_dir.exists() || !old_dir.exists() {
return; return;
+2 -2
View File
@@ -92,7 +92,7 @@ pub enum TipAction {
WarpAI, WarpAI,
// This toggles Warp Drive rather than opening it. This enum can't directly be // This toggles Warp Drive rather than opening it. This enum can't directly be
// renamed because we serialize it into the welcome tips. // renamed because we serialize it into the welcome tips.
OpenWarpDrive, OpenGalaxyDrive,
Changelog, Changelog,
// Note that this item has been deprecated from the UI and is not in any section. // Note that this item has been deprecated from the UI and is not in any section.
// We are leaving it in this enum to ensure that we don't re-use `Workflows` as a // We are leaving it in this enum to ensure that we don't re-use `Workflows` as a
@@ -112,7 +112,7 @@ impl TipAction {
TipAction::ThemePicker => "workspace:show_theme_chooser", TipAction::ThemePicker => "workspace:show_theme_chooser",
TipAction::SaveNewLaunchConfig => "workspace:open_launch_config_save_modal", TipAction::SaveNewLaunchConfig => "workspace:open_launch_config_save_modal",
TipAction::WarpAI => "workspace:toggle_ai_assistant", TipAction::WarpAI => "workspace:toggle_ai_assistant",
TipAction::OpenWarpDrive => "workspace:toggle_left_panel", TipAction::OpenGalaxyDrive => "workspace:toggle_left_panel",
// Slash commands are also registered as editable bindings, so callers can look them up here // Slash commands are also registered as editable bindings, so callers can look them up here
// the same way they do regular app actions. // the same way they do regular app actions.
TipAction::Changelog => "/changelog", TipAction::Changelog => "/changelog",
+2 -2
View File
@@ -1,6 +1,6 @@
use galaxy_core::ui::builder::UiBuilder; use galaxy_core::ui::builder::UiBuilder;
use galaxyui::{ use galaxyui::{
accessibility::{AccessibilityContent, WarpA11yRole}, accessibility::{AccessibilityContent, GalaxyA11yRole},
elements::{Align, Container, Element, Flex, MouseStateHandle, ParentElement}, elements::{Align, Container, Element, Flex, MouseStateHandle, ParentElement},
keymap::FixedBinding, keymap::FixedBinding,
ui_components::button::ButtonVariant, ui_components::button::ButtonVariant,
@@ -186,7 +186,7 @@ impl View for RewardView {
Some(AccessibilityContent::new( Some(AccessibilityContent::new(
format!("{} {}", TITLE, self.subtitle()), format!("{} {}", TITLE, self.subtitle()),
ACCESSIBILITY_HELP, ACCESSIBILITY_HELP,
WarpA11yRole::WindowRole, GalaxyA11yRole::WindowRole,
)) ))
} }
+10 -10
View File
@@ -14,7 +14,7 @@ use crate::cloud_object::model::persistence::CloudModel;
use crate::cloud_object::{GenericStringObjectFormat, JsonObjectType, ObjectType}; use crate::cloud_object::{GenericStringObjectFormat, JsonObjectType, ObjectType};
use crate::drive::export::ExportManager; use crate::drive::export::ExportManager;
use crate::drive::items::WarpDriveItemId; use crate::drive::items::WarpDriveItemId;
use crate::drive::{CloudObjectTypeAndId, OpenWarpDriveObjectArgs, OpenWarpDriveObjectSettings}; use crate::drive::{CloudObjectTypeAndId, OpenGalaxyDriveObjectArgs, OpenGalaxyDriveObjectSettings};
use crate::experiments::{BlockOnboarding, Experiment}; use crate::experiments::{BlockOnboarding, Experiment};
use crate::interval_timer::IntervalTimer; use crate::interval_timer::IntervalTimer;
use crate::launch_configs::launch_config; use crate::launch_configs::launch_config;
@@ -49,7 +49,7 @@ use crate::terminal::keys_settings::KeysSettings;
use crate::terminal::shell::ShellType; use crate::terminal::shell::ShellType;
use crate::terminal::view::{cell_size_and_padding, TerminalAction}; use crate::terminal::view::{cell_size_and_padding, TerminalAction};
use crate::themes::onboarding_theme_picker_themes; use crate::themes::onboarding_theme_picker_themes;
use crate::themes::theme::{AnsiColorIdentifier, Blend, Fill, ThemeKind, WarpThemeConfig}; use crate::themes::theme::{AnsiColorIdentifier, Blend, Fill, ThemeKind, GalaxyThemeConfig};
use crate::uri::OpenMCPSettingsArgs; use crate::uri::OpenMCPSettingsArgs;
use crate::util::bindings::{self, is_binding_pty_compliant}; use crate::util::bindings::{self, is_binding_pty_compliant};
use crate::util::traffic_lights::{traffic_light_data, TrafficLightData, TrafficLightMouseStates}; use crate::util::traffic_lights::{traffic_light_data, TrafficLightData, TrafficLightMouseStates};
@@ -1133,7 +1133,7 @@ fn open_linear_issue_work_in_new_window(args: &LinearIssueWork, ctx: &mut AppCon
}); });
} }
fn open_warp_drive_object(arg: &OpenWarpDriveObjectArgs, ctx: &mut AppContext) { fn open_warp_drive_object(arg: &OpenGalaxyDriveObjectArgs, ctx: &mut AppContext) {
match arg.object_type { match arg.object_type {
ObjectType::Notebook => open_new_workspace_with_notebook_open( ObjectType::Notebook => open_new_workspace_with_notebook_open(
SyncId::ServerId(arg.server_id), SyncId::ServerId(arg.server_id),
@@ -1158,7 +1158,7 @@ fn display_object_missing_error_in_window(window_id: WindowId, ctx: &mut AppCont
fn open_new_workspace_with_notebook_open( fn open_new_workspace_with_notebook_open(
notebook_id: SyncId, notebook_id: SyncId,
settings: OpenWarpDriveObjectSettings, settings: OpenGalaxyDriveObjectSettings,
ctx: &mut AppContext, ctx: &mut AppContext,
) { ) {
open_new_with_workspace_source( open_new_with_workspace_source(
@@ -1172,7 +1172,7 @@ fn open_new_workspace_with_notebook_open(
fn open_new_workspace_with_workflow_open( fn open_new_workspace_with_workflow_open(
workflow_id: SyncId, workflow_id: SyncId,
settings: OpenWarpDriveObjectSettings, settings: OpenGalaxyDriveObjectSettings,
ctx: &mut AppContext, ctx: &mut AppContext,
) { ) {
open_new_with_workspace_source( open_new_with_workspace_source(
@@ -1583,11 +1583,11 @@ pub enum NewWorkspaceSource {
}, },
NotebookById { NotebookById {
id: SyncId, id: SyncId,
settings: OpenWarpDriveObjectSettings, settings: OpenGalaxyDriveObjectSettings,
}, },
WorkflowById { WorkflowById {
id: SyncId, id: SyncId,
settings: OpenWarpDriveObjectSettings, settings: OpenGalaxyDriveObjectSettings,
}, },
AgentSession { AgentSession {
options: Box<NewTerminalOptions>, options: Box<NewTerminalOptions>,
@@ -2153,7 +2153,7 @@ impl RootView {
} }
fn onboarding_theme_kind(theme_name: &str) -> Option<ThemeKind> { fn onboarding_theme_kind(theme_name: &str) -> Option<ThemeKind> {
WarpThemeConfig::new() GalaxyThemeConfig::new()
.theme_items() .theme_items()
.find_map(|(kind, theme)| { .find_map(|(kind, theme)| {
(theme.name().as_deref() == Some(theme_name)).then(|| kind.clone()) (theme.name().as_deref() == Some(theme_name)).then(|| kind.clone())
@@ -2593,7 +2593,7 @@ impl RootView {
pub fn open_warp_drive_object_in_existing_window( pub fn open_warp_drive_object_in_existing_window(
&mut self, &mut self,
arg: &OpenWarpDriveObjectArgs, arg: &OpenGalaxyDriveObjectArgs,
ctx: &mut ViewContext<Self>, ctx: &mut ViewContext<Self>,
) -> bool { ) -> bool {
if let AuthOnboardingState::Terminal(handle) = &self.auth_onboarding_state { if let AuthOnboardingState::Terminal(handle) = &self.auth_onboarding_state {
@@ -2856,7 +2856,7 @@ impl RootView {
ctx.dispatch_typed_action_for_view( ctx.dispatch_typed_action_for_view(
window_id, window_id,
handle.id(), handle.id(),
&WorkspaceAction::OpenWarpDrive, &WorkspaceAction::OpenGalaxyDrive,
); );
ctx.windows().show_window_and_focus_app(window_id); ctx.windows().show_window_and_focus_app(window_id);
} else { } else {

Some files were not shown because too many files have changed in this diff Show More