Initial public release of Warp.
Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
@@ -0,0 +1,386 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use warpui::{
|
||||
elements::MouseStateHandle, AppContext, EntityId, SingletonEntity, ViewContext, ViewHandle,
|
||||
WindowId,
|
||||
};
|
||||
|
||||
use super::OneTimeModalModel;
|
||||
use crate::window_settings::WindowSettings;
|
||||
use crate::{
|
||||
appearance::Appearance, pane_group::PaneId, terminal::TerminalView, workspace::Workspace,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
/// What composes a pane (i.e. the pane group and the pane itself).
|
||||
pub struct PaneViewLocator {
|
||||
pub pane_group_id: EntityId,
|
||||
pub pane_id: PaneId,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(super) struct WorkspaceMouseStates {
|
||||
pub(super) new_tab_button: MouseStateHandle,
|
||||
pub(super) new_tab_menu: MouseStateHandle,
|
||||
pub(super) new_tab: MouseStateHandle,
|
||||
pub(super) overflow_button: MouseStateHandle,
|
||||
pub(super) banner_button: MouseStateHandle,
|
||||
pub(super) banner_secondary_button: MouseStateHandle,
|
||||
pub(super) more_info_banner_button: MouseStateHandle,
|
||||
pub(super) resource_center_icon: MouseStateHandle,
|
||||
pub(super) ai_tab_bar_button: MouseStateHandle,
|
||||
pub(super) agent_management_view_button: MouseStateHandle,
|
||||
pub(super) left_panel_icon: MouseStateHandle,
|
||||
pub(super) settings_icon: MouseStateHandle,
|
||||
pub(super) dismiss_banner_button: MouseStateHandle,
|
||||
pub(super) sign_in_button: MouseStateHandle,
|
||||
pub(super) sign_up_button: MouseStateHandle,
|
||||
pub(super) offline_icon: MouseStateHandle,
|
||||
pub(super) avatar_icon: MouseStateHandle,
|
||||
pub(super) header_dimming: MouseStateHandle,
|
||||
pub(super) right_panel_icon: MouseStateHandle,
|
||||
pub(super) notifications_mailbox: MouseStateHandle,
|
||||
pub(super) session_config_tab_config_chip_close: MouseStateHandle,
|
||||
pub(super) tools_panel_icon: MouseStateHandle,
|
||||
pub(super) title_bar_search_bar: MouseStateHandle,
|
||||
#[cfg(target_family = "wasm")]
|
||||
pub(super) warp_logo: MouseStateHandle,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum WelcomeTipsViewState {
|
||||
Unavailable,
|
||||
Available { is_popup_open: bool },
|
||||
}
|
||||
|
||||
impl WelcomeTipsViewState {
|
||||
pub fn is_popup_open(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
WelcomeTipsViewState::Available {
|
||||
is_popup_open: true,
|
||||
..
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
pub fn close_popup(&mut self) {
|
||||
if let WelcomeTipsViewState::Available {
|
||||
ref mut is_popup_open,
|
||||
..
|
||||
} = self
|
||||
{
|
||||
*is_popup_open = false;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toggle_popup(&mut self) {
|
||||
if let WelcomeTipsViewState::Available {
|
||||
ref mut is_popup_open,
|
||||
..
|
||||
} = self
|
||||
{
|
||||
*is_popup_open = !*is_popup_open;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO change this struct to enum (as we can only have 1 of them set to true at a time)
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct WorkspaceState {
|
||||
pub is_palette_open: bool,
|
||||
pub is_ctrl_tab_palette_open: bool,
|
||||
pub is_theme_chooser_open: bool,
|
||||
pub is_theme_creator_modal_open: bool,
|
||||
pub is_theme_deletion_modal_open: bool,
|
||||
pub is_changelog_modal_open: bool,
|
||||
pub is_tab_being_dragged: bool,
|
||||
pub is_reward_modal_open: bool,
|
||||
pub is_launch_config_save_modal_open: bool,
|
||||
pub is_resource_center_open: bool,
|
||||
pub is_command_search_open: bool,
|
||||
pub is_warp_drive_open: bool,
|
||||
pub is_ai_assistant_panel_open: bool,
|
||||
pub is_agent_management_popup_open: bool,
|
||||
pub is_auth_override_modal_open: bool,
|
||||
pub is_require_login_modal_open: bool,
|
||||
pub is_workflow_modal_open: bool,
|
||||
pub is_prompt_editor_open: bool,
|
||||
pub is_agent_toolbar_editor_open: bool,
|
||||
pub is_header_toolbar_editor_open: bool,
|
||||
pub is_import_modal_open: bool,
|
||||
pub is_close_session_confirmation_dialog_open: bool,
|
||||
pub is_rewind_confirmation_dialog_open: bool,
|
||||
pub is_delete_conversation_confirmation_dialog_open: bool,
|
||||
pub is_native_quit_modal_open: bool,
|
||||
pub is_shared_objects_creation_denied_modal_open: bool,
|
||||
pub is_suggested_agent_mode_workflow_modal_open: bool,
|
||||
pub is_suggested_rule_modal_open: bool,
|
||||
pub is_enable_auto_reload_modal_open: bool,
|
||||
pub is_notification_mailbox_open: bool,
|
||||
pub is_agent_management_view_open: bool,
|
||||
pub is_codex_modal_open: bool,
|
||||
pub is_cloud_agent_capacity_modal_open: bool,
|
||||
pub is_free_tier_limit_hit_modal_open: bool,
|
||||
pub is_tab_config_params_modal_open: bool,
|
||||
pub is_session_config_modal_open: bool,
|
||||
pub is_new_worktree_modal_open: bool,
|
||||
pub is_remove_tab_config_dialog_open: bool,
|
||||
/// Whether the transcript details panel is open (WASM only, for conversation transcript viewing).
|
||||
pub is_transcript_details_panel_open: bool,
|
||||
tab_being_renamed: Option<usize>, // The index of the tab being renamed
|
||||
pane_being_renamed: Option<PaneViewLocator>,
|
||||
}
|
||||
|
||||
impl WorkspaceState {
|
||||
pub fn is_any_non_terminal_view_open(&self, app: &AppContext) -> bool {
|
||||
self.is_any_modal_open(app)
|
||||
|| self.is_theme_chooser_open
|
||||
|| self.is_ai_assistant_panel_open
|
||||
|| self.is_workflow_modal_open
|
||||
|| self.is_warp_drive_open
|
||||
}
|
||||
|
||||
pub fn is_any_non_palette_modal_open(&self, app: &AppContext) -> bool {
|
||||
self.is_theme_creator_modal_open
|
||||
|| self.is_theme_deletion_modal_open
|
||||
|| self.is_changelog_modal_open
|
||||
|| self.tab_being_renamed.is_some()
|
||||
|| self.pane_being_renamed.is_some()
|
||||
|| self.is_reward_modal_open
|
||||
|| self.is_launch_config_save_modal_open
|
||||
|| self.is_command_search_open
|
||||
|| self.is_prompt_editor_open
|
||||
|| self.is_agent_toolbar_editor_open
|
||||
|| self.is_header_toolbar_editor_open
|
||||
|| self.is_agent_management_popup_open
|
||||
|| self.is_import_modal_open
|
||||
|| self.is_shared_objects_creation_denied_modal_open
|
||||
|| self.is_suggested_rule_modal_open
|
||||
|| self.is_suggested_agent_mode_workflow_modal_open
|
||||
|| self.is_enable_auto_reload_modal_open
|
||||
|| self.is_codex_modal_open
|
||||
|| self.is_cloud_agent_capacity_modal_open
|
||||
|| self.is_free_tier_limit_hit_modal_open
|
||||
|| self.is_tab_config_params_modal_open
|
||||
|| self.is_session_config_modal_open
|
||||
|| self.is_new_worktree_modal_open
|
||||
|| self.is_remove_tab_config_dialog_open
|
||||
|| {
|
||||
let one_time_modal = OneTimeModalModel::as_ref(app);
|
||||
one_time_modal.is_oz_launch_modal_open()
|
||||
|| one_time_modal.is_build_plan_migration_modal_open()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether any modal (sitting over terminal views) is open.
|
||||
pub fn is_any_modal_open(&self, app: &AppContext) -> bool {
|
||||
self.is_any_non_palette_modal_open(app)
|
||||
|| self.is_palette_open
|
||||
|| self.is_ctrl_tab_palette_open
|
||||
}
|
||||
|
||||
pub fn close_all_modals(&mut self) {
|
||||
self.is_palette_open = false;
|
||||
self.is_ctrl_tab_palette_open = false;
|
||||
self.is_theme_creator_modal_open = false;
|
||||
self.is_theme_deletion_modal_open = false;
|
||||
self.is_changelog_modal_open = false;
|
||||
self.tab_being_renamed = None;
|
||||
self.pane_being_renamed = None;
|
||||
self.is_reward_modal_open = false;
|
||||
self.is_launch_config_save_modal_open = false;
|
||||
self.is_command_search_open = false;
|
||||
self.is_workflow_modal_open = false;
|
||||
self.is_prompt_editor_open = false;
|
||||
self.is_agent_toolbar_editor_open = false;
|
||||
self.is_header_toolbar_editor_open = false;
|
||||
self.is_import_modal_open = false;
|
||||
self.is_shared_objects_creation_denied_modal_open = false;
|
||||
self.is_auth_override_modal_open = false;
|
||||
self.is_require_login_modal_open = false;
|
||||
self.is_suggested_rule_modal_open = false;
|
||||
self.is_suggested_agent_mode_workflow_modal_open = false;
|
||||
self.is_enable_auto_reload_modal_open = false;
|
||||
self.is_codex_modal_open = false;
|
||||
self.is_cloud_agent_capacity_modal_open = false;
|
||||
self.is_free_tier_limit_hit_modal_open = false;
|
||||
self.is_tab_config_params_modal_open = false;
|
||||
self.is_session_config_modal_open = false;
|
||||
self.is_new_worktree_modal_open = false;
|
||||
self.is_remove_tab_config_dialog_open = false;
|
||||
}
|
||||
|
||||
pub fn is_right_panel_open(&self) -> bool {
|
||||
self.is_resource_center_open || self.is_ai_assistant_panel_open
|
||||
}
|
||||
|
||||
pub fn is_left_panel_open(&self) -> bool {
|
||||
self.is_theme_chooser_open
|
||||
}
|
||||
|
||||
pub fn close_all_left_panels(&mut self) {
|
||||
self.is_warp_drive_open = false;
|
||||
self.is_theme_chooser_open = false;
|
||||
}
|
||||
|
||||
pub fn is_tab_being_renamed(&self) -> bool {
|
||||
self.tab_being_renamed.is_some()
|
||||
}
|
||||
|
||||
pub fn set_tab_being_renamed(&mut self, index: usize) {
|
||||
self.tab_being_renamed = Some(index);
|
||||
self.pane_being_renamed = None;
|
||||
}
|
||||
|
||||
pub fn clear_tab_being_renamed(&mut self) {
|
||||
self.tab_being_renamed = None;
|
||||
}
|
||||
|
||||
pub fn tab_being_renamed(&self) -> Option<usize> {
|
||||
self.tab_being_renamed
|
||||
}
|
||||
|
||||
pub fn is_pane_being_renamed(&self, pane: PaneViewLocator) -> bool {
|
||||
self.pane_being_renamed == Some(pane)
|
||||
}
|
||||
|
||||
pub fn is_any_pane_being_renamed(&self) -> bool {
|
||||
self.pane_being_renamed.is_some()
|
||||
}
|
||||
|
||||
pub fn set_pane_being_renamed(&mut self, pane: PaneViewLocator) {
|
||||
self.pane_being_renamed = Some(pane);
|
||||
self.tab_being_renamed = None;
|
||||
}
|
||||
|
||||
pub fn clear_pane_being_renamed(&mut self) {
|
||||
self.pane_being_renamed = None;
|
||||
}
|
||||
|
||||
pub fn pane_being_renamed(&self) -> Option<PaneViewLocator> {
|
||||
self.pane_being_renamed
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to represent left and right movement for tabs in WorkspaceActions
|
||||
#[derive(PartialEq, Eq, Clone, Serialize, Deserialize, Copy)]
|
||||
pub enum TabMovement {
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
/// Fallback behavior for when a terminal input is needed, but none are available.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub enum TerminalSessionFallbackBehavior {
|
||||
/// Never open a new terminal session; fail if there is not an active session with no running
|
||||
/// command.
|
||||
RequireExisting,
|
||||
/// Open a new terminal session if and only if there is no active session. If the active
|
||||
/// session is busy, fail.
|
||||
#[default]
|
||||
OpenIfNone,
|
||||
/// Open a new terminal session if there is active session OR if the active session is busy.
|
||||
OpenIfNeeded,
|
||||
}
|
||||
|
||||
/// Given a [`WindowId`], see if its [`Workspace`] contains an active [`TerminalView`] and return
|
||||
/// that.
|
||||
///
|
||||
/// Note that "active" is not the same as "focused" in Warp's pane management.
|
||||
pub fn active_terminal_in_window<T, F>(
|
||||
window_id: WindowId,
|
||||
ctx: &mut AppContext,
|
||||
update: F,
|
||||
) -> Option<T>
|
||||
where
|
||||
F: FnOnce(&mut TerminalView, &mut ViewContext<TerminalView>) -> T,
|
||||
{
|
||||
ctx.views_of_type::<Workspace>(window_id)
|
||||
.as_ref()
|
||||
.and_then(|v| v.first())
|
||||
.and_then(|handle| {
|
||||
handle.update(ctx, |workspace, w_ctx| {
|
||||
workspace
|
||||
.active_tab_pane_group()
|
||||
.update(w_ctx, |active_group, a_ctx| {
|
||||
active_group
|
||||
.active_session_view(a_ctx)
|
||||
.map(|terminal| terminal.update(a_ctx, update))
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Check if two terminal views are in the same tab pane group.
|
||||
///
|
||||
/// Returns true if both terminal views are in the same tab, false otherwise.
|
||||
pub fn is_terminal_view_in_same_tab(
|
||||
terminal_view_id_1: &EntityId,
|
||||
terminal_view_id_2: &EntityId,
|
||||
app: &AppContext,
|
||||
) -> bool {
|
||||
if terminal_view_id_1 == terminal_view_id_2 {
|
||||
return true;
|
||||
}
|
||||
let Some(active_window) = app.windows().active_window() else {
|
||||
return false;
|
||||
};
|
||||
let Some(workspace) = app
|
||||
.views_of_type::<Workspace>(active_window)
|
||||
.and_then(|views| views.first().cloned())
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
let workspace = workspace.as_ref(app);
|
||||
workspace
|
||||
.list_tab_pane_groups(app)
|
||||
.into_iter()
|
||||
.any(|tab_pane_group| {
|
||||
tab_pane_group.terminal_ids.contains(terminal_view_id_1)
|
||||
&& tab_pane_group.terminal_ids.contains(terminal_view_id_2)
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the active terminal session view in the active tab. This is used as the target for any
|
||||
/// selections in the adjacent editor.
|
||||
pub fn get_context_target_terminal_view(
|
||||
window_id: WindowId,
|
||||
ctx: &AppContext,
|
||||
) -> Option<ViewHandle<TerminalView>> {
|
||||
ctx.views_of_type::<Workspace>(window_id)
|
||||
.as_ref()
|
||||
.and_then(|v| v.first())
|
||||
.and_then(|handle| {
|
||||
handle.read(ctx, |workspace, w_ctx| {
|
||||
workspace
|
||||
.active_tab_pane_group()
|
||||
.read(w_ctx, |active_group, a_ctx| {
|
||||
active_group.active_session_view(a_ctx)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_terminal_background_fill(
|
||||
window_id: WindowId,
|
||||
app: &AppContext,
|
||||
) -> warpui::elements::Fill {
|
||||
let theme = Appearance::as_ref(app).theme();
|
||||
let terminal_opacity = get_terminal_background_opacity(window_id, app);
|
||||
theme.background().with_opacity(terminal_opacity).into()
|
||||
}
|
||||
|
||||
fn get_terminal_background_opacity(window_id: WindowId, app: &AppContext) -> u8 {
|
||||
let theme = Appearance::as_ref(app).theme();
|
||||
let background_opacity = WindowSettings::as_ref(app)
|
||||
.background_opacity
|
||||
.effective_opacity(window_id, app);
|
||||
|
||||
if let Some(img) = theme.background_image() {
|
||||
let opacity_ratio = background_opacity as f32 / 100.;
|
||||
// Scale the overlay opacity with the background opacity ratio.
|
||||
(((100 - img.opacity) as f32) * opacity_ratio) as u8
|
||||
} else {
|
||||
background_opacity
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user