feat: expand Galaxy agent and remote tooling
Add Wormhole remote helpers, provider and agent improvements, filesystem diagnostics, model metadata support, and schema-aware settings IntelliSense.
This commit is contained in:
@@ -67,7 +67,7 @@ pub enum OnboardingVersion {
|
||||
/// This represents whether entering a subshell for a particular command should become automatic in
|
||||
/// the future, or to ask again.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum RememberForWarpification {
|
||||
pub enum RememberForWormholing {
|
||||
/// If yes, need to transmit the command itself so it can be persisted to user-defaults
|
||||
RememberSubshellCommand(String),
|
||||
RememberSSHHost(String),
|
||||
@@ -75,22 +75,22 @@ pub enum RememberForWarpification {
|
||||
DoNotRememberSSHHost,
|
||||
}
|
||||
|
||||
impl RememberForWarpification {
|
||||
impl RememberForWormholing {
|
||||
pub fn as_bool(&self) -> bool {
|
||||
match self {
|
||||
RememberForWarpification::RememberSubshellCommand(_) => true,
|
||||
RememberForWarpification::RememberSSHHost(_) => true,
|
||||
RememberForWarpification::DoNotRememberSubshellCommand => false,
|
||||
RememberForWarpification::DoNotRememberSSHHost => false,
|
||||
RememberForWormholing::RememberSubshellCommand(_) => true,
|
||||
RememberForWormholing::RememberSSHHost(_) => true,
|
||||
RememberForWormholing::DoNotRememberSubshellCommand => false,
|
||||
RememberForWormholing::DoNotRememberSSHHost => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_ssh(&self) -> bool {
|
||||
match self {
|
||||
RememberForWarpification::RememberSSHHost(_) => true,
|
||||
RememberForWarpification::DoNotRememberSSHHost => true,
|
||||
RememberForWarpification::RememberSubshellCommand(_) => false,
|
||||
RememberForWarpification::DoNotRememberSubshellCommand => false,
|
||||
RememberForWormholing::RememberSSHHost(_) => true,
|
||||
RememberForWormholing::DoNotRememberSSHHost => true,
|
||||
RememberForWormholing::RememberSubshellCommand(_) => false,
|
||||
RememberForWormholing::DoNotRememberSubshellCommand => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -284,8 +284,8 @@ pub enum TerminalAction {
|
||||
},
|
||||
/// Starts a subshell in the active session.
|
||||
TriggerSubshellBootstrap,
|
||||
/// If the user says "no" to Warpification, possibly requesting not to be asked again
|
||||
DismissWarpifyBanner(RememberForWarpification),
|
||||
/// If the user says "no" to Wormholing, possibly requesting not to be asked again
|
||||
DismissWormholeBanner(RememberForWormholing),
|
||||
/// Triggers the banner asking to turn the running block into a subshell. The String is the
|
||||
/// command that the user entered.
|
||||
ShowSubshellBanner(String),
|
||||
@@ -342,7 +342,7 @@ pub enum TerminalAction {
|
||||
GenerateCodebaseIndex,
|
||||
/// This is for debugging, dev only for now
|
||||
LoadAgentModeConversation,
|
||||
ShowWarpifySettings,
|
||||
ShowWormholeSettings,
|
||||
/// Removes a pending attachment (image or file) by index in the unified list.
|
||||
DeleteAttachment {
|
||||
index: usize,
|
||||
@@ -622,7 +622,7 @@ impl fmt::Debug for TerminalAction {
|
||||
OpenBlockListContextMenu => f.write_str("OpenBlockListContextMenu"),
|
||||
AskAIAssistant { block_index } => write!(f, "AskAIAssistant({block_index:?})"),
|
||||
TriggerSubshellBootstrap => f.write_str("TriggerSubshellBootstrap"),
|
||||
DismissWarpifyBanner(remember) => write!(f, "DismissWarpifyBanner({remember:?})"),
|
||||
DismissWormholeBanner(remember) => write!(f, "DismissWormholeBanner({remember:?})"),
|
||||
ShowSubshellBanner(_) => f.write_str("ShowSubshellBanner"),
|
||||
InsertMostRecentCommandCorrection => f.write_str("InsertMostRecentCommandCorrection"),
|
||||
AliasExpansionBanner(action) => write!(f, "AliasExpansionBanner({action:?}"),
|
||||
@@ -682,7 +682,7 @@ impl fmt::Debug for TerminalAction {
|
||||
ShowInitializationBlock => write!(f, "ShowInitializationBlock"),
|
||||
GenerateCodebaseIndex => write!(f, "GenerateIndexForRepo"),
|
||||
LoadAgentModeConversation => write!(f, "LoadAgentModeConversation"),
|
||||
ShowWarpifySettings => write!(f, "ShowWarpifySettings"),
|
||||
ShowWormholeSettings => write!(f, "ShowWormholeSettings"),
|
||||
DeleteAttachment { index } => write!(f, "DeleteAttachment({index:?})"),
|
||||
OpenAttachmentLightbox { index } => {
|
||||
write!(f, "OpenAttachmentLightbox({index:?})")
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
//! without a LayoutContext. Use the exported BLOCK_BANNER_HEIGHT const when the banner height
|
||||
//! needs to be taken into account.
|
||||
|
||||
mod warpify;
|
||||
mod wormhole;
|
||||
|
||||
use galaxyui::elements::{
|
||||
ConstrainedBox, Container, CornerRadius, Hoverable, MouseState, MouseStateHandle,
|
||||
ParentElement, Radius, Stack,
|
||||
};
|
||||
use galaxyui::Element;
|
||||
pub use warpify::*;
|
||||
pub use wormhole::*;
|
||||
|
||||
use crate::themes::theme::GalaxyTheme;
|
||||
|
||||
@@ -25,13 +25,13 @@ const BANNER_H_PADDING: f32 = 8.;
|
||||
pub const BLOCK_BANNER_HEIGHT: f32 = CONSTRAINED_BANNER_HEIGHT + BANNER_TOP_MARGIN;
|
||||
|
||||
pub enum WithinBlockBanner {
|
||||
WarpifyBanner(WarpifyBannerState),
|
||||
WormholeBanner(WormholeBannerState),
|
||||
}
|
||||
|
||||
impl WithinBlockBanner {
|
||||
pub fn banner_height(&self) -> f32 {
|
||||
match self {
|
||||
WithinBlockBanner::WarpifyBanner(_) => BLOCK_BANNER_HEIGHT,
|
||||
WithinBlockBanner::WormholeBanner(_) => BLOCK_BANNER_HEIGHT,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+21
-21
@@ -10,14 +10,14 @@ use pathfinder_color::ColorU;
|
||||
|
||||
use super::render_block_banner;
|
||||
use crate::appearance::Appearance;
|
||||
use crate::terminal::view::{RememberForWarpification, TerminalAction};
|
||||
use crate::terminal::view::{RememberForWormholing, TerminalAction};
|
||||
use crate::themes::theme::Fill;
|
||||
use crate::ui_components::blended_colors;
|
||||
|
||||
const CLOSE_BUTTON_DIAMETER: f32 = 20.0;
|
||||
const STANDARD_PADDING: f32 = 8.0;
|
||||
|
||||
pub struct WarpifyBannerState {
|
||||
pub struct WormholeBannerState {
|
||||
/// The subshell command that triggered the banner.
|
||||
pub command: String,
|
||||
pub height: f32,
|
||||
@@ -25,19 +25,19 @@ pub struct WarpifyBannerState {
|
||||
pub dont_ask_button_mouse_state: MouseStateHandle,
|
||||
pub dismiss_button_mouse_state: MouseStateHandle,
|
||||
|
||||
/// This keybinding gets rendered in the Warpification banner, but we can't look it up
|
||||
/// This keybinding gets rendered in the Wormholing banner, but we can't look it up
|
||||
/// during render as a &mut AppContext is not available then. This needs to get
|
||||
/// looked up during action handling and cached here.
|
||||
pub initialize_warpify_keybinding: Option<Keystroke>,
|
||||
pub initialize_wormhole_keybinding: Option<Keystroke>,
|
||||
pub hover_state: MouseStateHandle,
|
||||
}
|
||||
|
||||
impl WarpifyBannerState {
|
||||
pub fn new(command: String, initialize_warpify_keybinding: Option<Keystroke>) -> Self {
|
||||
impl WormholeBannerState {
|
||||
pub fn new(command: String, initialize_wormhole_keybinding: Option<Keystroke>) -> Self {
|
||||
Self {
|
||||
command,
|
||||
height: 0.0,
|
||||
initialize_warpify_keybinding,
|
||||
initialize_wormhole_keybinding,
|
||||
accept_button_mouse_state: Default::default(),
|
||||
dont_ask_button_mouse_state: Default::default(),
|
||||
dismiss_button_mouse_state: Default::default(),
|
||||
@@ -46,18 +46,18 @@ impl WarpifyBannerState {
|
||||
}
|
||||
|
||||
pub fn title(&self) -> &str {
|
||||
"Warpify subshell"
|
||||
"Wormhole subshell"
|
||||
}
|
||||
|
||||
pub fn action(&self) -> TerminalAction {
|
||||
TerminalAction::TriggerSubshellBootstrap
|
||||
}
|
||||
|
||||
fn remember_for_warpification(&self, should_remember: bool) -> RememberForWarpification {
|
||||
fn remember_for_wormholing(&self, should_remember: bool) -> RememberForWormholing {
|
||||
if should_remember {
|
||||
RememberForWarpification::RememberSubshellCommand(self.command.to_owned())
|
||||
RememberForWormholing::RememberSubshellCommand(self.command.to_owned())
|
||||
} else {
|
||||
RememberForWarpification::DoNotRememberSubshellCommand
|
||||
RememberForWormholing::DoNotRememberSubshellCommand
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,18 +65,18 @@ impl WarpifyBannerState {
|
||||
/// This banner is shown when the user runs a command which is recognized as a subshell-compatible
|
||||
/// command. It asks if they want to bootstrap a subshell and, if so, whether we should ask again
|
||||
/// next time they run the same command.
|
||||
pub fn render_warpification_banner(
|
||||
state: &WarpifyBannerState,
|
||||
pub fn render_wormholing_banner(
|
||||
state: &WormholeBannerState,
|
||||
appearance: &Appearance,
|
||||
) -> Box<dyn Element> {
|
||||
let yes_button = render_yes_button(
|
||||
state,
|
||||
&state.initialize_warpify_keybinding,
|
||||
&state.initialize_wormhole_keybinding,
|
||||
&state.accept_button_mouse_state,
|
||||
appearance,
|
||||
);
|
||||
|
||||
let remember = state.remember_for_warpification(true);
|
||||
let remember = state.remember_for_wormholing(true);
|
||||
let dont_ask_button = Container::new(
|
||||
appearance
|
||||
.ui_builder()
|
||||
@@ -87,7 +87,7 @@ pub fn render_warpification_banner(
|
||||
.with_text_label("Do not show again".to_owned())
|
||||
.build()
|
||||
.on_click(move |ctx, _, _| {
|
||||
ctx.dispatch_typed_action(TerminalAction::DismissWarpifyBanner(
|
||||
ctx.dispatch_typed_action(TerminalAction::DismissWormholeBanner(
|
||||
remember.to_owned(),
|
||||
));
|
||||
})
|
||||
@@ -96,7 +96,7 @@ pub fn render_warpification_banner(
|
||||
.with_margin_right(16.)
|
||||
.finish();
|
||||
|
||||
let do_not_remember = state.remember_for_warpification(false);
|
||||
let do_not_remember = state.remember_for_wormholing(false);
|
||||
let close_button = appearance
|
||||
.ui_builder()
|
||||
.close_button(
|
||||
@@ -105,7 +105,7 @@ pub fn render_warpification_banner(
|
||||
)
|
||||
.build()
|
||||
.on_click(move |ctx, _, _| {
|
||||
ctx.dispatch_typed_action(TerminalAction::DismissWarpifyBanner(
|
||||
ctx.dispatch_typed_action(TerminalAction::DismissWormholeBanner(
|
||||
do_not_remember.to_owned(),
|
||||
));
|
||||
})
|
||||
@@ -132,12 +132,12 @@ pub fn render_warpification_banner(
|
||||
}
|
||||
|
||||
fn render_yes_button(
|
||||
state: &WarpifyBannerState,
|
||||
initialize_warpification_keybinding: &Option<Keystroke>,
|
||||
state: &WormholeBannerState,
|
||||
initialize_wormholing_keybinding: &Option<Keystroke>,
|
||||
mouse_state: &MouseStateHandle,
|
||||
appearance: &Appearance,
|
||||
) -> Box<dyn Element> {
|
||||
let yes_button = match initialize_warpification_keybinding {
|
||||
let yes_button = match initialize_wormholing_keybinding {
|
||||
Some(keystroke) => appearance
|
||||
.ui_builder()
|
||||
.keyboard_shortcut_button(state.title().to_owned(), keystroke, mouse_state.clone())
|
||||
@@ -81,8 +81,8 @@ pub fn init(app: &mut AppContext) {
|
||||
app.register_binding_validator::<TerminalView>(is_binding_pty_compliant);
|
||||
|
||||
init_overlapping_keybindings(app);
|
||||
// Register input mode bindings before warpify bindings so ctrl-i warpifies
|
||||
// instead of opening inline agent when a warpify banner is visible.
|
||||
// Register input mode bindings before wormhole bindings so ctrl-i wormholes
|
||||
// instead of opening inline agent when a wormhole banner is visible.
|
||||
register_input_mode_bindings(app);
|
||||
|
||||
app.register_fixed_bindings([
|
||||
@@ -320,7 +320,7 @@ pub fn init(app: &mut AppContext) {
|
||||
| (id!("Terminal") & !id!("IMEOpen") & id!(flags::CLI_AGENT_RICH_INPUT_OPEN)),
|
||||
),
|
||||
EditableBinding::new(
|
||||
"terminal:warpify_subshell",
|
||||
"terminal:wormhole_subshell",
|
||||
"Wormhole subshell",
|
||||
TerminalAction::TriggerSubshellBootstrap,
|
||||
)
|
||||
|
||||
@@ -18,7 +18,7 @@ use crate::terminal::view::init_environment::InitEnvironmentBlock;
|
||||
use crate::terminal::view::ssh_remote_server_choice_view::SshRemoteServerChoiceView;
|
||||
use crate::terminal::view::ssh_remote_server_failed_banner::SshRemoteServerFailedBanner;
|
||||
use crate::terminal::view::ssh_tmux_deprecation_banner::SshTmuxDeprecationBanner;
|
||||
use crate::terminal::warpify::success_block::WarpifySuccessBlock;
|
||||
use crate::terminal::wormhole::success_block::WormholeSuccessBlock;
|
||||
use crate::terminal::TerminalView;
|
||||
|
||||
/// Specifies where to insert rich content in the blocklist.
|
||||
@@ -249,8 +249,8 @@ pub enum RichContentMetadata {
|
||||
SshTmuxDeprecationBanner {
|
||||
handle: ViewHandle<SshTmuxDeprecationBanner>,
|
||||
},
|
||||
WarpifySuccessBlock {
|
||||
bootstrap_success_block_handle: ViewHandle<WarpifySuccessBlock>,
|
||||
WormholeSuccessBlock {
|
||||
bootstrap_success_block_handle: ViewHandle<WormholeSuccessBlock>,
|
||||
},
|
||||
TelemetryBanner {
|
||||
telemetry_banner_handle: ViewHandle<TelemetryBanner>,
|
||||
|
||||
@@ -187,7 +187,7 @@ impl FileUpload {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an sftp command that copies a given local file into the PWD of the warpified ssh session, if any.
|
||||
/// Creates an sftp command that copies a given local file into the PWD of the wormholed ssh session, if any.
|
||||
fn transfer_file_sftp_command(&self, file_upload: &FileUploadInfo) -> String {
|
||||
// "sftp "
|
||||
let mut command = String::from("sftp ");
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
//! Inline block view that asks the user whether they want to install
|
||||
//! Warp's SSH extension on the remote host the shell just connected to,
|
||||
//! Wormhole's remote helper on the host the shell just connected to,
|
||||
//! or continue without installing (falling back to the existing
|
||||
//! ControlMaster warpification path).
|
||||
//! ControlMaster wormholing path).
|
||||
//!
|
||||
//! Designed from frame 6050:2448 of the Figma file
|
||||
//! [Remote session initialization](https://www.figma.com/design/r0BO9cTZCK6pDE6qerg2K0/Remote-session-initialization).
|
||||
//!
|
||||
//! The view owns:
|
||||
//! - a child [`KeyboardNavigableButtons`] handle for the two selectable
|
||||
//! cards ("Install Warp's SSH extension" / "Continue without installing"),
|
||||
//! cards ("Install Wormhole helper" / "Continue without installing"),
|
||||
//! - the [`SessionId`] this prompt is scoped to (used for event forwarding),
|
||||
//! - the current "Don't ask me this again" checked state (purely local to
|
||||
//! this prompt instance; persisted to `ssh_extension_install_mode` only
|
||||
@@ -37,7 +37,7 @@ use crate::ai::blocklist::inline_action::inline_action_header::{
|
||||
};
|
||||
use crate::server::telemetry::TelemetryEvent;
|
||||
use crate::terminal::model::session::SessionId;
|
||||
use crate::terminal::warpify::settings::{SshExtensionInstallMode, WarpifySettings};
|
||||
use crate::terminal::wormhole::settings::{SshExtensionInstallMode, WormholeSettings};
|
||||
use crate::ui_components::blended_colors;
|
||||
use crate::{send_telemetry_from_ctx, Appearance};
|
||||
|
||||
@@ -48,14 +48,14 @@ pub enum SshRemoteServerChoiceViewAction {
|
||||
Install,
|
||||
Skip,
|
||||
ToggleDoNotAskAgain,
|
||||
OpenWarpifySettings,
|
||||
OpenWormholeSettings,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum SshRemoteServerChoiceViewEvent {
|
||||
Install,
|
||||
Skip,
|
||||
OpenWarpifySettings,
|
||||
OpenWormholeSettings,
|
||||
}
|
||||
|
||||
/// Choice block prompting the user to install the remote-server binary on the remote host or skip.
|
||||
@@ -74,7 +74,7 @@ impl SshRemoteServerChoiceView {
|
||||
let buttons = ctx.add_typed_action_view(|_| {
|
||||
KeyboardNavigableButtons::new(vec![
|
||||
rich_navigation_button(
|
||||
"Install Galaxy's SSH extension".to_string(),
|
||||
"Install Wormhole helper".to_string(),
|
||||
Some(
|
||||
"Install Galaxy's extension to enable agent features like file browsing, \
|
||||
code review, and intelligent command completions in this session."
|
||||
@@ -171,14 +171,16 @@ impl SshRemoteServerChoiceView {
|
||||
.with_child(Container::new(checkbox_label).with_margin_left(4.).finish())
|
||||
.finish();
|
||||
|
||||
// Right: "Manage Warpify settings" link.
|
||||
// Right: "Manage Wormhole settings" link.
|
||||
let manage_settings_link = appearance
|
||||
.ui_builder()
|
||||
.link(
|
||||
"Manage Wormhole settings".into(),
|
||||
None,
|
||||
Some(Box::new(|ctx| {
|
||||
ctx.dispatch_typed_action(SshRemoteServerChoiceViewAction::OpenWarpifySettings);
|
||||
ctx.dispatch_typed_action(
|
||||
SshRemoteServerChoiceViewAction::OpenWormholeSettings,
|
||||
);
|
||||
})),
|
||||
self.manage_settings_mouse_state.clone(),
|
||||
)
|
||||
@@ -264,7 +266,7 @@ impl TypedActionView for SshRemoteServerChoiceView {
|
||||
SshRemoteServerChoiceViewAction::Install => {
|
||||
if self.do_not_ask_again {
|
||||
let mode = SshExtensionInstallMode::AlwaysInstall;
|
||||
WarpifySettings::handle(ctx).update(ctx, |settings, ctx| {
|
||||
WormholeSettings::handle(ctx).update(ctx, |settings, ctx| {
|
||||
if let Err(e) = settings.ssh_extension_install_mode.set_value(mode, ctx) {
|
||||
log::error!("Failed to persist ssh_extension_install_mode: {e}");
|
||||
}
|
||||
@@ -281,7 +283,7 @@ impl TypedActionView for SshRemoteServerChoiceView {
|
||||
SshRemoteServerChoiceViewAction::Skip => {
|
||||
if self.do_not_ask_again {
|
||||
let mode = SshExtensionInstallMode::NeverInstall;
|
||||
WarpifySettings::handle(ctx).update(ctx, |settings, ctx| {
|
||||
WormholeSettings::handle(ctx).update(ctx, |settings, ctx| {
|
||||
if let Err(e) = settings.ssh_extension_install_mode.set_value(mode, ctx) {
|
||||
log::error!("Failed to persist ssh_extension_install_mode: {e}");
|
||||
}
|
||||
@@ -305,8 +307,8 @@ impl TypedActionView for SshRemoteServerChoiceView {
|
||||
);
|
||||
ctx.notify();
|
||||
}
|
||||
SshRemoteServerChoiceViewAction::OpenWarpifySettings => {
|
||||
ctx.emit(SshRemoteServerChoiceViewEvent::OpenWarpifySettings);
|
||||
SshRemoteServerChoiceViewAction::OpenWormholeSettings => {
|
||||
ctx.emit(SshRemoteServerChoiceViewEvent::OpenWormholeSettings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//! Banner shown when the remote-server binary check, installation, or connection fails on the remote host.
|
||||
//! We fall back to the existing Warpification behavior and display this banner so the user knows why advanced features are unavailable.
|
||||
//! We fall back to the existing Wormholing behavior and display this banner so the user knows why advanced features are unavailable.
|
||||
|
||||
use galaxy_core::ui::theme::color::internal_colors;
|
||||
use galaxy_core::ui::theme::AnsiColorIdentifier;
|
||||
@@ -15,11 +15,11 @@ use crate::terminal::model::session::SessionId;
|
||||
use crate::ui_components::icons::Icon;
|
||||
use crate::Appearance;
|
||||
|
||||
const BANNER_TITLE: &str = "Couldn't connect to the Warp SSH extension";
|
||||
const BANNER_TITLE: &str = "Couldn't connect to the Wormhole helper";
|
||||
|
||||
const BANNER_BODY: &str =
|
||||
"While advanced features like file browsing and code review are currently \
|
||||
disabled, the rest of your Warpified experience is fully available.";
|
||||
disabled, the rest of your Wormholed experience is fully available.";
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum SshRemoteServerFailedBannerAction {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! One-time inline banner shown to users who had previously opted into the now-deprecated
|
||||
//! tmux-based SSH warpification flow. It explains that tmux SSH warpification has been turned
|
||||
//! off in favor of Warp's SSH extension (remote server) and links to the docs.
|
||||
//! tmux-based SSH wormholing flow. It explains that tmux SSH wormholing has been turned
|
||||
//! off in favor of Galaxy's SSH extension (remote server).
|
||||
//!
|
||||
//! The banner is shown at most once per affected user: it is gated on the
|
||||
//! `ssh_tmux_deprecation_notice_pending` setting, which is set by a one-time migration and
|
||||
@@ -8,29 +8,25 @@
|
||||
|
||||
use galaxy_core::ui::theme::color::internal_colors;
|
||||
use warpui::elements::{
|
||||
Align, ConstrainedBox, Container, CrossAxisAlignment, Flex, Hoverable, MainAxisAlignment,
|
||||
ConstrainedBox, Container, CrossAxisAlignment, Flex, Hoverable, MainAxisAlignment,
|
||||
MainAxisSize, MouseStateHandle, ParentElement, Shrinkable, Text,
|
||||
};
|
||||
use warpui::platform::Cursor;
|
||||
use warpui::ui_components::components::{UiComponent, UiComponentStyles};
|
||||
use warpui::{AppContext, Element, Entity, SingletonEntity, TypedActionView, View, ViewContext};
|
||||
|
||||
use crate::terminal::model::session::SessionId;
|
||||
use crate::terminal::warpify::render::SSH_DOCS_URL;
|
||||
use crate::ui_components::icons::Icon;
|
||||
use crate::Appearance;
|
||||
|
||||
const BANNER_TITLE: &str = "Tmux SSH warpification has been deprecated";
|
||||
const BANNER_TITLE: &str = "Legacy tmux SSH wormholing has been retired";
|
||||
|
||||
const BANNER_BODY: &str = "Warp now connects to remote sessions using the SSH extension, which is \
|
||||
const BANNER_BODY: &str =
|
||||
"Galaxy now connects to remote sessions using the SSH extension, which is \
|
||||
more robust than the tmux-based flow. The tmux option has been removed.";
|
||||
|
||||
const LEARN_MORE_LABEL: &str = "Learn more";
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum SshTmuxDeprecationBannerAction {
|
||||
Dismiss,
|
||||
LearnMore,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -40,7 +36,6 @@ pub enum SshTmuxDeprecationBannerEvent {
|
||||
|
||||
pub struct SshTmuxDeprecationBanner {
|
||||
session_id: SessionId,
|
||||
learn_more_mouse_state: MouseStateHandle,
|
||||
close_mouse_state: MouseStateHandle,
|
||||
}
|
||||
|
||||
@@ -48,7 +43,6 @@ impl SshTmuxDeprecationBanner {
|
||||
pub fn new(session_id: SessionId) -> Self {
|
||||
Self {
|
||||
session_id,
|
||||
learn_more_mouse_state: MouseStateHandle::default(),
|
||||
close_mouse_state: MouseStateHandle::default(),
|
||||
}
|
||||
}
|
||||
@@ -72,13 +66,12 @@ impl View for SshTmuxDeprecationBanner {
|
||||
let theme = appearance.theme();
|
||||
let fg_color = theme.foreground().into_solid();
|
||||
let muted_color = internal_colors::neutral_5(theme);
|
||||
let accent_color = theme.accent().into_solid();
|
||||
let font_size = appearance.monospace_font_size();
|
||||
let small_font_size = font_size - 2.;
|
||||
|
||||
// Warp icon to match the other warpification blocks.
|
||||
// Galaxy icon to match the other wormholing blocks.
|
||||
let icon = Container::new(
|
||||
ConstrainedBox::new(Icon::Warp.to_warpui_icon(fg_color.into()).finish())
|
||||
ConstrainedBox::new(Icon::GalaxyLogo.to_warpui_icon(fg_color.into()).finish())
|
||||
.with_width(16.)
|
||||
.with_height(16.)
|
||||
.finish(),
|
||||
@@ -103,26 +96,6 @@ impl View for SshTmuxDeprecationBanner {
|
||||
.with_color(muted_color)
|
||||
.finish();
|
||||
|
||||
let learn_more = appearance
|
||||
.ui_builder()
|
||||
.link(
|
||||
LEARN_MORE_LABEL.into(),
|
||||
None,
|
||||
Some(Box::new(|ctx| {
|
||||
ctx.dispatch_typed_action(SshTmuxDeprecationBannerAction::LearnMore);
|
||||
})),
|
||||
self.learn_more_mouse_state.clone(),
|
||||
)
|
||||
.soft_wrap(false)
|
||||
.with_style(UiComponentStyles {
|
||||
font_size: Some(small_font_size),
|
||||
font_family_id: Some(appearance.ui_font_family()),
|
||||
font_color: Some(accent_color),
|
||||
..Default::default()
|
||||
})
|
||||
.build()
|
||||
.finish();
|
||||
|
||||
// Close (X) button
|
||||
let close_icon_color = muted_color;
|
||||
let close = Hoverable::new(self.close_mouse_state.clone(), move |_| {
|
||||
@@ -158,26 +131,17 @@ impl View for SshTmuxDeprecationBanner {
|
||||
.with_child(close_container)
|
||||
.finish();
|
||||
|
||||
// Body text + learn more link, indented past the icon to align with the title.
|
||||
// Body text, indented past the icon to align with the title.
|
||||
let body_container = Container::new(body)
|
||||
.with_margin_top(2.)
|
||||
.with_margin_left(24.)
|
||||
.finish();
|
||||
|
||||
// Wrap the link in a left-aligned `Align` so its hover/underline region hugs the
|
||||
// link text instead of stretching to the full banner width (the parent column uses
|
||||
// `CrossAxisAlignment::Stretch`).
|
||||
let learn_more_container = Container::new(Align::new(learn_more).left().finish())
|
||||
.with_margin_top(4.)
|
||||
.with_margin_left(24.)
|
||||
.finish();
|
||||
|
||||
let content = Flex::column()
|
||||
.with_main_axis_size(MainAxisSize::Min)
|
||||
.with_cross_axis_alignment(CrossAxisAlignment::Stretch)
|
||||
.with_child(header_row)
|
||||
.with_child(body_container)
|
||||
.with_child(learn_more_container)
|
||||
.finish();
|
||||
|
||||
Container::new(content)
|
||||
@@ -195,10 +159,6 @@ impl TypedActionView for SshTmuxDeprecationBanner {
|
||||
SshTmuxDeprecationBannerAction::Dismiss => {
|
||||
ctx.emit(SshTmuxDeprecationBannerEvent::Dismissed);
|
||||
}
|
||||
SshTmuxDeprecationBannerAction::LearnMore => {
|
||||
ctx.open_url(SSH_DOCS_URL);
|
||||
ctx.emit(SshTmuxDeprecationBannerEvent::Dismissed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ use crate::terminal::shared_session::{
|
||||
SharedSessionActionSource, SharedSessionScrollbackType, SharedSessionSource,
|
||||
};
|
||||
use crate::util::image::{infer_mime_type, MAX_IMAGE_SIZE_BYTES_FOR_CLI_AGENT, MIME_SNIFF_BYTES};
|
||||
mod warpify_footer;
|
||||
mod wormhole_footer;
|
||||
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, LazyLock};
|
||||
@@ -44,7 +44,7 @@ use galaxyui::{
|
||||
};
|
||||
use parking_lot::FairMutex;
|
||||
use pathfinder_color::ColorU;
|
||||
use warpify_footer::{WarpifyFooterView, WarpifyFooterViewEvent};
|
||||
use wormhole_footer::{WormholeFooterView, WormholeFooterViewEvent};
|
||||
|
||||
use super::{RichContentInsertionPosition, TerminalAction, TerminalView};
|
||||
use crate::ai::blocklist::agent_view::agent_view_bg_fill;
|
||||
@@ -267,11 +267,11 @@ impl TerminalView {
|
||||
UseAgentToolbarEvent::HideRichInput => {
|
||||
self.close_cli_agent_rich_input_and_disable_auto_toggle(ctx);
|
||||
}
|
||||
UseAgentToolbarEvent::Warpify => {
|
||||
UseAgentToolbarEvent::Wormhole => {
|
||||
self.hide_use_agent_footer_in_blocklist(ctx);
|
||||
self.handle_action(&TerminalAction::TriggerSubshellBootstrap, ctx);
|
||||
send_telemetry_from_ctx!(
|
||||
TelemetryEvent::WarpifyFooterAcceptedWarpify { is_ssh: false },
|
||||
TelemetryEvent::WormholeFooterAcceptedWormhole { is_ssh: false },
|
||||
ctx
|
||||
);
|
||||
}
|
||||
@@ -295,8 +295,8 @@ impl TerminalView {
|
||||
) -> bool {
|
||||
let ai_settings = AISettings::as_ref(app);
|
||||
|
||||
// If the warpify footer is active, a subshell was detected and we should show the footer.
|
||||
if self.use_agent_footer.as_ref(app).is_warpify_active(app) {
|
||||
// If the wormhole footer is active, a subshell was detected and we should show the footer.
|
||||
if self.use_agent_footer.as_ref(app).is_wormhole_active(app) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -421,7 +421,7 @@ impl TerminalView {
|
||||
|
||||
if !self.model.lock().is_alt_screen_active() {
|
||||
self.use_agent_footer.update(ctx, |footer, ctx| {
|
||||
footer.clear_warpify(ctx);
|
||||
footer.clear_wormhole(ctx);
|
||||
});
|
||||
self.hide_use_agent_footer_in_blocklist(ctx);
|
||||
}
|
||||
@@ -1046,8 +1046,8 @@ pub struct UseAgentToolbar {
|
||||
// Shared agent input footer (renders CLI agent mode when a CLI session is active).
|
||||
agent_input_footer: ViewHandle<AgentInputFooter>,
|
||||
|
||||
// Warpify footer UI (shown when a subshell/SSH command is detected).
|
||||
warpify_footer_view: ViewHandle<WarpifyFooterView>,
|
||||
// Wormhole footer UI (shown when a subshell/SSH command is detected).
|
||||
wormhole_footer_view: ViewHandle<WormholeFooterView>,
|
||||
|
||||
// `true` if the user has dismissed the footer.
|
||||
//
|
||||
@@ -1120,11 +1120,11 @@ impl UseAgentToolbar {
|
||||
me.handle_agent_input_footer_event(event, ctx);
|
||||
});
|
||||
|
||||
let warpify_footer_view =
|
||||
ctx.add_typed_action_view(|ctx| WarpifyFooterView::new(terminal_model.clone(), ctx));
|
||||
let wormhole_footer_view =
|
||||
ctx.add_typed_action_view(|ctx| WormholeFooterView::new(terminal_model.clone(), ctx));
|
||||
|
||||
ctx.subscribe_to_view(&warpify_footer_view, |me, _, event, ctx| {
|
||||
me.handle_warpify_footer_event(event, ctx);
|
||||
ctx.subscribe_to_view(&wormhole_footer_view, |me, _, event, ctx| {
|
||||
me.handle_wormhole_footer_event(event, ctx);
|
||||
});
|
||||
|
||||
ctx.subscribe_to_model(model_event_dispatcher, |me, _, event, ctx| {
|
||||
@@ -1150,7 +1150,7 @@ impl UseAgentToolbar {
|
||||
dismiss_button,
|
||||
dont_show_again_button,
|
||||
agent_input_footer,
|
||||
warpify_footer_view,
|
||||
wormhole_footer_view,
|
||||
terminal_model,
|
||||
did_user_dismiss: false,
|
||||
}
|
||||
@@ -1186,19 +1186,19 @@ impl UseAgentToolbar {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_warpify_footer_event(
|
||||
fn handle_wormhole_footer_event(
|
||||
&mut self,
|
||||
event: &WarpifyFooterViewEvent,
|
||||
event: &WormholeFooterViewEvent,
|
||||
ctx: &mut ViewContext<Self>,
|
||||
) {
|
||||
match event {
|
||||
WarpifyFooterViewEvent::Warpify => {
|
||||
ctx.emit(UseAgentToolbarEvent::Warpify);
|
||||
WormholeFooterViewEvent::Wormhole => {
|
||||
ctx.emit(UseAgentToolbarEvent::Wormhole);
|
||||
}
|
||||
WarpifyFooterViewEvent::UseAgent => {
|
||||
WormholeFooterViewEvent::UseAgent => {
|
||||
ctx.emit(UseAgentToolbarEvent::UseAgent);
|
||||
}
|
||||
WarpifyFooterViewEvent::Dismiss => {
|
||||
WormholeFooterViewEvent::Dismiss => {
|
||||
ctx.emit(UseAgentToolbarEvent::Dismiss);
|
||||
}
|
||||
}
|
||||
@@ -1207,7 +1207,7 @@ impl UseAgentToolbar {
|
||||
pub(in crate::terminal) fn notify_and_notify_children(&mut self, ctx: &mut ViewContext<Self>) {
|
||||
ctx.notify();
|
||||
self.agent_input_footer.update(ctx, |_, ctx| ctx.notify());
|
||||
self.warpify_footer_view.update(ctx, |_, ctx| ctx.notify());
|
||||
self.wormhole_footer_view.update(ctx, |_, ctx| ctx.notify());
|
||||
self.button.update(ctx, |_, ctx| ctx.notify());
|
||||
self.give_control_back_button
|
||||
.update(ctx, |_, ctx| ctx.notify());
|
||||
@@ -1227,26 +1227,26 @@ impl UseAgentToolbar {
|
||||
.map(|session| session.agent)
|
||||
}
|
||||
|
||||
/// Activates the warpify footer. When active, the footer shows the
|
||||
/// warpify view instead of the CLI agent or regular "Use agent" views.
|
||||
pub(in crate::terminal) fn show_warpify(&mut self, ctx: &mut ViewContext<Self>) {
|
||||
self.warpify_footer_view.update(ctx, |view, ctx| {
|
||||
/// Activates the wormhole footer. When active, the footer shows the
|
||||
/// wormhole view instead of the CLI agent or regular "Use agent" views.
|
||||
pub(in crate::terminal) fn show_wormhole(&mut self, ctx: &mut ViewContext<Self>) {
|
||||
self.wormhole_footer_view.update(ctx, |view, ctx| {
|
||||
view.show(ctx);
|
||||
});
|
||||
ctx.notify();
|
||||
}
|
||||
|
||||
/// Deactivates the warpify footer so it reverts to its default behavior.
|
||||
pub(in crate::terminal) fn clear_warpify(&mut self, ctx: &mut ViewContext<Self>) {
|
||||
self.warpify_footer_view.update(ctx, |view, ctx| {
|
||||
/// Deactivates the wormhole footer so it reverts to its default behavior.
|
||||
pub(in crate::terminal) fn clear_wormhole(&mut self, ctx: &mut ViewContext<Self>) {
|
||||
self.wormhole_footer_view.update(ctx, |view, ctx| {
|
||||
view.clear(ctx);
|
||||
});
|
||||
ctx.notify();
|
||||
}
|
||||
|
||||
/// Returns whether the warpify footer is currently active.
|
||||
pub(in crate::terminal) fn is_warpify_active(&self, app: &AppContext) -> bool {
|
||||
self.warpify_footer_view.as_ref(app).is_active()
|
||||
/// Returns whether the wormhole footer is currently active.
|
||||
pub(in crate::terminal) fn is_wormhole_active(&self, app: &AppContext) -> bool {
|
||||
self.wormhole_footer_view.as_ref(app).is_active()
|
||||
}
|
||||
|
||||
/// Returns whether there's a current CLI agent (like Claude Code).
|
||||
@@ -1272,8 +1272,8 @@ pub enum UseAgentToolbarEvent {
|
||||
OpenRichInput,
|
||||
/// Hide the rich input editor (same as Escape).
|
||||
HideRichInput,
|
||||
/// User chose to warpify the subshell.
|
||||
Warpify,
|
||||
/// User chose to wormhole the subshell.
|
||||
Wormhole,
|
||||
/// User chose to use the agent.
|
||||
UseAgent,
|
||||
StartRemoteControl {
|
||||
@@ -1292,9 +1292,9 @@ impl View for UseAgentToolbar {
|
||||
}
|
||||
|
||||
fn render(&self, app: &AppContext) -> Box<dyn Element> {
|
||||
// If the warpify footer is active, delegate rendering to the warpify footer view.
|
||||
if self.warpify_footer_view.as_ref(app).is_active() {
|
||||
return ChildView::new(&self.warpify_footer_view).finish();
|
||||
// If the wormhole footer is active, delegate rendering to the wormhole footer view.
|
||||
if self.wormhole_footer_view.as_ref(app).is_active() {
|
||||
return ChildView::new(&self.wormhole_footer_view).finish();
|
||||
}
|
||||
|
||||
// Hide the toolbar entirely when CLI rich input is open,
|
||||
|
||||
+33
-33
@@ -15,28 +15,28 @@ use crate::view_components::action_button::{
|
||||
};
|
||||
|
||||
/// Footer view rendered for detected subshell commands, offering both
|
||||
/// "Warpify" and "Use agent" buttons in a horizontal row.
|
||||
pub(super) struct WarpifyFooterView {
|
||||
/// "Wormhole" and "Use agent" buttons in a horizontal row.
|
||||
pub(super) struct WormholeFooterView {
|
||||
terminal_model: Arc<FairMutex<TerminalModel>>,
|
||||
warpify_button: ViewHandle<ActionButton>,
|
||||
wormhole_button: ViewHandle<ActionButton>,
|
||||
use_agent_button: ViewHandle<ActionButton>,
|
||||
dismiss_button: ViewHandle<ActionButton>,
|
||||
/// Whether the footer is currently offering subshell warpification.
|
||||
/// Whether the footer is currently offering subshell wormholing.
|
||||
is_active: bool,
|
||||
}
|
||||
|
||||
impl WarpifyFooterView {
|
||||
impl WormholeFooterView {
|
||||
pub fn new(terminal_model: Arc<FairMutex<TerminalModel>>, ctx: &mut ViewContext<Self>) -> Self {
|
||||
let button_size = ButtonSize::XSmall;
|
||||
|
||||
let warpify_button = ctx.add_typed_action_view(|_ctx| {
|
||||
let wormhole_button = ctx.add_typed_action_view(|_ctx| {
|
||||
ActionButton::new("Wormhole subshell", AgentFooterButtonTheme::new(None))
|
||||
.with_icon(Icon::Warp)
|
||||
.with_icon(Icon::GalaxyLogo)
|
||||
.with_size(button_size)
|
||||
.with_tooltip("Enable Galaxy shell integration in this session")
|
||||
.with_tooltip_alignment(TooltipAlignment::Left)
|
||||
.on_click(|ctx| {
|
||||
ctx.dispatch_typed_action(WarpifyFooterViewAction::Warpify);
|
||||
ctx.dispatch_typed_action(WormholeFooterViewAction::Wormhole);
|
||||
})
|
||||
});
|
||||
|
||||
@@ -48,7 +48,7 @@ impl WarpifyFooterView {
|
||||
.with_tooltip("Ask the Galaxy agent to assist")
|
||||
.with_tooltip_alignment(TooltipAlignment::Left)
|
||||
.on_click(|ctx| {
|
||||
ctx.dispatch_typed_action(WarpifyFooterViewAction::UseAgent);
|
||||
ctx.dispatch_typed_action(WormholeFooterViewAction::UseAgent);
|
||||
})
|
||||
});
|
||||
|
||||
@@ -56,24 +56,24 @@ impl WarpifyFooterView {
|
||||
ActionButton::new("Dismiss", AgentFooterButtonTheme::new(None))
|
||||
.with_size(button_size)
|
||||
.on_click(|ctx| {
|
||||
ctx.dispatch_typed_action(WarpifyFooterViewAction::Dismiss);
|
||||
ctx.dispatch_typed_action(WormholeFooterViewAction::Dismiss);
|
||||
})
|
||||
});
|
||||
|
||||
Self {
|
||||
terminal_model,
|
||||
warpify_button,
|
||||
wormhole_button,
|
||||
use_agent_button,
|
||||
dismiss_button,
|
||||
is_active: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Activates the footer so it offers subshell warpification.
|
||||
/// Activates the footer so it offers subshell wormholing.
|
||||
pub fn show(&mut self, ctx: &mut ViewContext<Self>) {
|
||||
self.warpify_button.update(ctx, |button, ctx| {
|
||||
self.wormhole_button.update(ctx, |button, ctx| {
|
||||
button.set_keybinding(
|
||||
Some(KeystrokeSource::Binding("terminal:warpify_subshell")),
|
||||
Some(KeystrokeSource::Binding("terminal:wormhole_subshell")),
|
||||
ctx,
|
||||
);
|
||||
});
|
||||
@@ -81,7 +81,7 @@ impl WarpifyFooterView {
|
||||
ctx.notify();
|
||||
}
|
||||
|
||||
/// Returns whether the footer is currently offering subshell warpification.
|
||||
/// Returns whether the footer is currently offering subshell wormholing.
|
||||
pub fn is_active(&self) -> bool {
|
||||
self.is_active
|
||||
}
|
||||
@@ -89,7 +89,7 @@ impl WarpifyFooterView {
|
||||
/// Deactivates the footer.
|
||||
pub fn clear(&mut self, ctx: &mut ViewContext<Self>) {
|
||||
self.is_active = false;
|
||||
self.warpify_button.update(ctx, |button, ctx| {
|
||||
self.wormhole_button.update(ctx, |button, ctx| {
|
||||
button.set_keybinding(None, ctx);
|
||||
});
|
||||
ctx.notify();
|
||||
@@ -97,25 +97,25 @@ impl WarpifyFooterView {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum WarpifyFooterViewAction {
|
||||
Warpify,
|
||||
pub enum WormholeFooterViewAction {
|
||||
Wormhole,
|
||||
UseAgent,
|
||||
Dismiss,
|
||||
}
|
||||
|
||||
pub enum WarpifyFooterViewEvent {
|
||||
Warpify,
|
||||
pub enum WormholeFooterViewEvent {
|
||||
Wormhole,
|
||||
UseAgent,
|
||||
Dismiss,
|
||||
}
|
||||
|
||||
impl Entity for WarpifyFooterView {
|
||||
type Event = WarpifyFooterViewEvent;
|
||||
impl Entity for WormholeFooterView {
|
||||
type Event = WormholeFooterViewEvent;
|
||||
}
|
||||
|
||||
impl View for WarpifyFooterView {
|
||||
impl View for WormholeFooterView {
|
||||
fn ui_name() -> &'static str {
|
||||
"WarpifyFooterView"
|
||||
"WormholeFooterView"
|
||||
}
|
||||
|
||||
fn render(&self, _app: &AppContext) -> Box<dyn Element> {
|
||||
@@ -125,7 +125,7 @@ impl View for WarpifyFooterView {
|
||||
.with_spacing(4.)
|
||||
.with_main_axis_size(MainAxisSize::Max)
|
||||
.with_cross_axis_alignment(CrossAxisAlignment::Center)
|
||||
.with_child(ChildView::new(&self.warpify_button).finish())
|
||||
.with_child(ChildView::new(&self.wormhole_button).finish())
|
||||
.with_child(ChildView::new(&self.use_agent_button).finish())
|
||||
.with_child(Expanded::new(1., Empty::new().finish()).finish())
|
||||
.with_child(ChildView::new(&self.dismiss_button).finish());
|
||||
@@ -144,24 +144,24 @@ impl View for WarpifyFooterView {
|
||||
}
|
||||
}
|
||||
|
||||
impl TypedActionView for WarpifyFooterView {
|
||||
type Action = WarpifyFooterViewAction;
|
||||
impl TypedActionView for WormholeFooterView {
|
||||
type Action = WormholeFooterViewAction;
|
||||
|
||||
fn handle_action(&mut self, action: &Self::Action, ctx: &mut ViewContext<Self>) {
|
||||
match action {
|
||||
WarpifyFooterViewAction::Warpify => {
|
||||
WormholeFooterViewAction::Wormhole => {
|
||||
if self.is_active {
|
||||
self.clear(ctx);
|
||||
ctx.emit(WarpifyFooterViewEvent::Warpify);
|
||||
ctx.emit(WormholeFooterViewEvent::Wormhole);
|
||||
}
|
||||
}
|
||||
WarpifyFooterViewAction::UseAgent => {
|
||||
WormholeFooterViewAction::UseAgent => {
|
||||
self.clear(ctx);
|
||||
ctx.emit(WarpifyFooterViewEvent::UseAgent);
|
||||
ctx.emit(WormholeFooterViewEvent::UseAgent);
|
||||
}
|
||||
WarpifyFooterViewAction::Dismiss => {
|
||||
WormholeFooterViewAction::Dismiss => {
|
||||
self.clear(ctx);
|
||||
ctx.emit(WarpifyFooterViewEvent::Dismiss);
|
||||
ctx.emit(WormholeFooterViewEvent::Dismiss);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user