Initial public release of Warp.
Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
@@ -0,0 +1,248 @@
|
||||
use warp_util::path::user_friendly_path;
|
||||
use warpui::{
|
||||
elements::{
|
||||
Border, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, Flex, MainAxisSize,
|
||||
MouseStateHandle, ParentElement, Radius, Text,
|
||||
},
|
||||
platform::Cursor,
|
||||
ui_components::{
|
||||
button::{ButtonTooltipPosition, ButtonVariant},
|
||||
components::{UiComponent, UiComponentStyles},
|
||||
},
|
||||
AppContext, Element, SingletonEntity,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
appearance::Appearance, settings::ai::DefaultSessionMode, tab_configs::TabConfig,
|
||||
terminal::available_shells::AvailableShell, workspace::WorkspaceAction,
|
||||
};
|
||||
|
||||
pub(crate) const SIDECAR_WIDTH: f32 = 260.;
|
||||
const SIDECAR_PADDING: f32 = 12.;
|
||||
|
||||
/// Describes what the sidecar is showing, which determines which buttons appear.
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) enum SidecarItemKind {
|
||||
/// A built-in item (Terminal, a specific shell, Agent, Cloud Oz).
|
||||
BuiltIn {
|
||||
name: String,
|
||||
default_mode: DefaultSessionMode,
|
||||
shell: Option<AvailableShell>,
|
||||
},
|
||||
/// A user-created tab config loaded from disk.
|
||||
UserTabConfig { config: TabConfig },
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct SidecarMouseStates {
|
||||
pub(crate) make_default: MouseStateHandle,
|
||||
pub(crate) edit_config: MouseStateHandle,
|
||||
pub(crate) remove_config: MouseStateHandle,
|
||||
}
|
||||
|
||||
/// Renders the action sidecar panel as a raw element tree.
|
||||
/// Called directly from the Workspace render method (not via ChildView).
|
||||
pub(crate) fn render_action_sidecar(
|
||||
item: &SidecarItemKind,
|
||||
mouse_states: &SidecarMouseStates,
|
||||
is_already_default: bool,
|
||||
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.ui_font_size();
|
||||
|
||||
let mut column = Flex::column()
|
||||
.with_cross_axis_alignment(CrossAxisAlignment::Start)
|
||||
.with_main_axis_size(MainAxisSize::Min);
|
||||
|
||||
// Title
|
||||
let title = match item {
|
||||
SidecarItemKind::BuiltIn { name, .. } => name.clone(),
|
||||
SidecarItemKind::UserTabConfig { config } => config.name.clone(),
|
||||
};
|
||||
column.add_child(
|
||||
Container::new(
|
||||
Text::new_inline(title, font_family, font_size + 1.)
|
||||
.with_color(theme.main_text_color(theme.surface_2()).into())
|
||||
.finish(),
|
||||
)
|
||||
.with_margin_bottom(4.)
|
||||
.finish(),
|
||||
);
|
||||
|
||||
// Subtitle (file path for user configs)
|
||||
if let SidecarItemKind::UserTabConfig { config } = item {
|
||||
if let Some(path) = &config.source_path {
|
||||
let raw_path = path.to_string_lossy().into_owned();
|
||||
let home_dir = dirs::home_dir();
|
||||
let path_str =
|
||||
user_friendly_path(&raw_path, home_dir.as_ref().and_then(|h| h.to_str()))
|
||||
.into_owned();
|
||||
column.add_child(
|
||||
Container::new(
|
||||
Text::new_inline(path_str, font_family, font_size - 1.)
|
||||
.with_color(theme.sub_text_color(theme.surface_2()).into())
|
||||
.finish(),
|
||||
)
|
||||
.with_margin_bottom(8.)
|
||||
.finish(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let primary_text_color = theme.main_text_color(theme.surface_2());
|
||||
let button_style = UiComponentStyles {
|
||||
font_size: Some(12.),
|
||||
font_weight: Some(warpui::fonts::Weight::Bold),
|
||||
font_color: Some(primary_text_color.into()),
|
||||
padding: Some(warpui::ui_components::components::Coords {
|
||||
top: 4.,
|
||||
bottom: 4.,
|
||||
left: 8.,
|
||||
right: 8.,
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let make_default_action = match item {
|
||||
SidecarItemKind::BuiltIn {
|
||||
default_mode,
|
||||
shell,
|
||||
..
|
||||
} => WorkspaceAction::TabConfigSidecarMakeDefault {
|
||||
mode: *default_mode,
|
||||
tab_config_path: None,
|
||||
shell: shell.clone(),
|
||||
},
|
||||
SidecarItemKind::UserTabConfig { config } => WorkspaceAction::TabConfigSidecarMakeDefault {
|
||||
mode: DefaultSessionMode::TabConfig,
|
||||
tab_config_path: config.source_path.clone(),
|
||||
shell: None,
|
||||
},
|
||||
};
|
||||
|
||||
// "Make default" button (always shown; visually disabled with tooltip when already the default)
|
||||
let make_default_button = if is_already_default {
|
||||
let disabled_style = UiComponentStyles {
|
||||
font_color: Some(theme.disabled_text_color(theme.surface_2()).into()),
|
||||
border_color: Some(theme.outline().into()),
|
||||
..button_style
|
||||
};
|
||||
appearance
|
||||
.ui_builder()
|
||||
.button(ButtonVariant::Outlined, mouse_states.make_default.clone())
|
||||
.with_centered_text_label("Make default".into())
|
||||
.with_style(disabled_style)
|
||||
.with_tooltip({
|
||||
let ui_builder = appearance.ui_builder().clone();
|
||||
move || {
|
||||
ui_builder
|
||||
.tool_tip("Already the default".into())
|
||||
.build()
|
||||
.finish()
|
||||
}
|
||||
})
|
||||
.with_tooltip_position(ButtonTooltipPosition::Above)
|
||||
.set_clicked_styles(None)
|
||||
.build()
|
||||
.finish()
|
||||
} else {
|
||||
appearance
|
||||
.ui_builder()
|
||||
.button(ButtonVariant::Outlined, mouse_states.make_default.clone())
|
||||
.with_centered_text_label("Make default".into())
|
||||
.with_style(button_style)
|
||||
.build()
|
||||
.with_cursor(Cursor::PointingHand)
|
||||
.on_click(move |ctx: &mut warpui::elements::EventContext, _, _| {
|
||||
ctx.dispatch_typed_action(make_default_action.clone())
|
||||
})
|
||||
.finish()
|
||||
};
|
||||
column.add_child(
|
||||
ConstrainedBox::new(make_default_button)
|
||||
.with_max_width(SIDECAR_WIDTH - SIDECAR_PADDING * 2.)
|
||||
.finish(),
|
||||
);
|
||||
|
||||
// "Edit config" and "Remove" buttons (only for user tab configs)
|
||||
if let SidecarItemKind::UserTabConfig { config } = item {
|
||||
if let Some(config_path) = &config.source_path {
|
||||
let edit_path = config_path.clone();
|
||||
let edit_button = appearance
|
||||
.ui_builder()
|
||||
.button(ButtonVariant::Outlined, mouse_states.edit_config.clone())
|
||||
.with_centered_text_label("Edit config".into())
|
||||
.with_style(button_style)
|
||||
.build()
|
||||
.with_cursor(Cursor::PointingHand)
|
||||
.on_click(move |ctx: &mut warpui::elements::EventContext, _, _| {
|
||||
ctx.dispatch_typed_action(WorkspaceAction::TabConfigSidecarEditConfig {
|
||||
path: edit_path.clone(),
|
||||
})
|
||||
})
|
||||
.finish();
|
||||
column.add_child(
|
||||
Container::new(
|
||||
ConstrainedBox::new(edit_button)
|
||||
.with_max_width(SIDECAR_WIDTH - SIDECAR_PADDING * 2.)
|
||||
.finish(),
|
||||
)
|
||||
.with_margin_top(4.)
|
||||
.finish(),
|
||||
);
|
||||
|
||||
let remove_name = config.name.clone();
|
||||
let remove_path = config_path.clone();
|
||||
let red_color = theme.ansi_fg_red();
|
||||
let remove_style = UiComponentStyles {
|
||||
font_color: Some(red_color),
|
||||
border_color: Some(red_color.into()),
|
||||
..button_style
|
||||
};
|
||||
let remove_button = appearance
|
||||
.ui_builder()
|
||||
.button(ButtonVariant::Outlined, mouse_states.remove_config.clone())
|
||||
.with_centered_text_label("Remove".into())
|
||||
.with_style(remove_style)
|
||||
.with_hovered_styles(UiComponentStyles {
|
||||
border_color: Some(theme.accent().into()),
|
||||
..Default::default()
|
||||
})
|
||||
.build()
|
||||
.with_cursor(Cursor::PointingHand)
|
||||
.on_click(move |ctx: &mut warpui::elements::EventContext, _, _| {
|
||||
ctx.dispatch_typed_action(WorkspaceAction::TabConfigSidecarRemoveConfig {
|
||||
name: remove_name.clone(),
|
||||
path: remove_path.clone(),
|
||||
})
|
||||
})
|
||||
.finish();
|
||||
column.add_child(
|
||||
Container::new(
|
||||
ConstrainedBox::new(remove_button)
|
||||
.with_max_width(SIDECAR_WIDTH - SIDECAR_PADDING * 2.)
|
||||
.finish(),
|
||||
)
|
||||
.with_margin_top(4.)
|
||||
.finish(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ConstrainedBox::new(
|
||||
Container::new(column.finish())
|
||||
.with_padding_left(SIDECAR_PADDING)
|
||||
.with_padding_right(SIDECAR_PADDING)
|
||||
.with_padding_top(SIDECAR_PADDING)
|
||||
.with_padding_bottom(SIDECAR_PADDING)
|
||||
.with_background(theme.surface_2())
|
||||
.with_corner_radius(CornerRadius::with_all(Radius::Pixels(8.)))
|
||||
.with_border(Border::all(1.).with_border_fill(theme.outline()))
|
||||
.finish(),
|
||||
)
|
||||
.with_width(SIDECAR_WIDTH)
|
||||
.finish()
|
||||
}
|
||||
Reference in New Issue
Block a user