Initial public release of Warp.

Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
David Stern
2026-04-28 08:43:33 -05:00
commit 0dbd3d567a
4982 changed files with 1431549 additions and 0 deletions
+386
View File
@@ -0,0 +1,386 @@
use pathfinder_geometry::rect::RectF;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Arc;
use warpui::platform::FullscreenState;
use warpui::AppContext;
use crate::ai::agent::conversation::AIConversationId;
use crate::ai::agent_conversations_model::AgentManagementFilters;
use crate::ai::ambient_agents::AmbientAgentTaskId;
use crate::ai::blocklist::InputConfig;
use crate::ai::blocklist::SerializedBlockListItem;
use crate::code::editor_management::CodeSource;
use crate::drive::OpenWarpDriveObjectSettings;
use crate::root_view::quake_mode_window_id;
use crate::server::ids::SyncId;
use crate::settings_view::{environments_page::EnvironmentsPage, SettingsSection};
use crate::tab::SelectedTabColor;
use crate::terminal::ShellLaunchData;
use crate::themes::theme::AnsiColorIdentifier;
use crate::workspace::view::left_panel::ToolPanelView;
use crate::workspace::Workspace;
#[derive(Debug, Clone, PartialEq)]
pub struct AppState {
pub windows: Vec<WindowSnapshot>,
pub active_window_index: Option<usize>,
pub block_lists: Arc<HashMap<PaneUuid, Vec<SerializedBlockListItem>>>,
pub running_mcp_servers: Vec<uuid::Uuid>,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PaneUuid(pub Vec<u8>);
/// Wrapper for persisting agent management filters to restore.
#[derive(Default, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct PersistedAgentManagementFilters {
pub filters: AgentManagementFilters,
}
#[derive(Clone, Debug, PartialEq)]
pub struct WindowSnapshot {
pub tabs: Vec<TabSnapshot>,
pub active_tab_index: usize,
pub bounds: Option<RectF>,
pub fullscreen_state: FullscreenState,
pub quake_mode: bool,
pub universal_search_width: Option<f32>,
pub warp_ai_width: Option<f32>,
pub voltron_width: Option<f32>,
pub warp_drive_index_width: Option<f32>,
pub left_panel_open: bool,
pub vertical_tabs_panel_open: bool,
pub left_panel_width: Option<f32>,
pub right_panel_width: Option<f32>,
pub agent_management_filters: Option<PersistedAgentManagementFilters>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct TabSnapshot {
pub custom_title: Option<String>,
pub root: PaneNodeSnapshot,
pub default_directory_color: Option<AnsiColorIdentifier>,
pub selected_color: SelectedTabColor,
pub left_panel: Option<LeftPanelSnapshot>,
pub right_panel: Option<RightPanelSnapshot>,
}
impl TabSnapshot {
pub(crate) fn color(&self) -> Option<AnsiColorIdentifier> {
self.selected_color.resolve(self.default_directory_color)
}
}
#[derive(Clone, Debug, PartialEq)]
#[allow(
clippy::large_enum_variant,
reason = "LeafSnapshot is significantly larger than BranchSnapshot due to nested snapshot types."
)]
pub enum PaneNodeSnapshot {
Branch(BranchSnapshot),
Leaf(LeafSnapshot),
}
impl PaneNodeSnapshot {
pub fn has_horizontal_split(&self) -> bool {
match self {
PaneNodeSnapshot::Leaf(_) => false,
PaneNodeSnapshot::Branch(BranchSnapshot {
direction,
children,
}) => {
let self_has_split = *direction == SplitDirection::Horizontal && children.len() > 1;
self_has_split
|| children
.iter()
.any(|(_, child)| child.has_horizontal_split())
}
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct BranchSnapshot {
pub direction: SplitDirection,
pub children: Vec<(PaneFlex, PaneNodeSnapshot)>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct LeafSnapshot {
pub is_focused: bool,
pub custom_vertical_tabs_title: Option<String>,
pub contents: LeafContents,
}
#[derive(Clone, Debug, PartialEq)]
pub enum LeafContents {
Terminal(TerminalPaneSnapshot),
Notebook(NotebookPaneSnapshot),
AIDocument(AIDocumentPaneSnapshot),
Code(CodePaneSnapShot),
EnvVarCollection(EnvVarCollectionPaneSnapshot),
EnvironmentManagement(EnvironmentManagementPaneSnapshot),
Workflow(WorkflowPaneSnapshot),
Settings(SettingsPaneSnapshot),
AIFact(AIFactPaneSnapshot),
ExecutionProfileEditor,
CodeReview(CodeReviewPaneSnapshot),
AmbientAgent(AmbientAgentPaneSnapshot),
/// The in-app network log pane. Not persisted across restarts because the
/// backing log is an in-memory ring buffer that starts empty on launch.
NetworkLog,
/// An entrypoint pane type to launch other pane types from a search palette. The default view
/// when creating a tab.
Welcome {
startup_directory: Option<PathBuf>,
},
/// A new first-time user experience which prioritizes choosing a coding repository.
GetStarted,
}
#[cfg(feature = "local_fs")]
impl LeafContents {
/// Whether this pane content should be written to (and later restored
/// from) the SQLite app-state database.
///
/// Non-persisted pane types are skipped entirely during the pane tree
/// traversal in `save_app_state`, so no `pane_nodes` row is inserted for
/// them. This is important: inserting a `pane_nodes` row with
/// `is_leaf = true` but no matching `pane_leaves` row leaves an orphan
/// that `read_node` cannot resolve, which causes the surrounding tab's
/// restoration to fail and the whole tab to disappear on restart.
pub(crate) fn is_persisted(&self) -> bool {
match self {
// Network log: the backing log is an in-memory ring buffer that
// starts empty on launch; persisting would also regress back to
// an on-disk log via the app-state database.
LeafContents::NetworkLog
// Environment management panes are opened on-demand via workspace
// actions and have no persistable state.
| LeafContents::EnvironmentManagement(_) => false,
LeafContents::Terminal(_)
| LeafContents::Notebook(_)
| LeafContents::AIDocument(_)
| LeafContents::Code(_)
| LeafContents::EnvVarCollection(_)
| LeafContents::Workflow(_)
| LeafContents::Settings(_)
| LeafContents::AIFact(_)
| LeafContents::ExecutionProfileEditor
| LeafContents::CodeReview(_)
| LeafContents::AmbientAgent(_)
| LeafContents::Welcome { .. }
| LeafContents::GetStarted => true,
}
}
}
/// Snapshot of an ambient agent pane.
#[derive(Clone, Debug, PartialEq)]
pub struct AmbientAgentPaneSnapshot {
pub uuid: Vec<u8>,
// `task_id` is purposefully optional,
// as you can have a valid state (i.e. an empty cloud mode pane) where it is None.
pub task_id: Option<AmbientAgentTaskId>,
}
/// Snapshot of the contents of a terminal pane.
#[derive(Clone, Debug, PartialEq)]
pub struct TerminalPaneSnapshot {
pub uuid: Vec<u8>,
pub cwd: Option<String>,
pub shell_launch_data: Option<ShellLaunchData>,
pub is_active: bool,
pub is_read_only: bool,
pub input_config: Option<InputConfig>,
pub llm_model_override: Option<String>,
pub active_profile_id: Option<SyncId>,
pub conversation_ids_to_restore: Vec<AIConversationId>,
/// The active conversation ID if the agent view was open in fullscreen mode.
/// When `Some`, the agent view should be restored to fullscreen for this conversation.
pub active_conversation_id: Option<AIConversationId>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum NotebookPaneSnapshot {
CloudNotebook {
/// The ID of the notebook that was open in this pane. There are 3 possibilities:
/// 1. The pane contains a newly-created notebook that has not been edited yet. It might not
/// have an ID yet (client or server), so this will be `None`.
/// 2. The pane contains a notebook that hasn't been synced to the server yet, so this will
/// contain a client ID that should exist in SQLite.
/// 3. The pane contains a notebook that's known to the server, so this will contain the
/// server ID.
notebook_id: Option<SyncId>,
// Settings for the notebook pane when it's opened (such as a folder to focus upon opening)
settings: OpenWarpDriveObjectSettings,
},
LocalFileNotebook {
/// The path to the local file that was open in this pane. This may be `None` if
/// the pane contained an unreadable file.
path: Option<PathBuf>,
},
}
#[derive(Clone, Debug, PartialEq)]
pub enum AIDocumentPaneSnapshot {
Local {
document_id: String,
version: i32,
content: Option<String>,
title: Option<String>,
},
}
#[derive(Clone, Debug, PartialEq)]
pub struct CodePaneTabSnapshot {
pub path: Option<PathBuf>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum CodePaneSnapShot {
Local {
tabs: Vec<CodePaneTabSnapshot>,
active_tab_index: usize,
/// The full `CodeSource` for this pane, serialized as JSON in the DB.
source: Option<CodeSource>,
},
}
#[derive(Clone, Debug, PartialEq)]
pub enum WorkflowPaneSnapshot {
CloudWorkflow {
workflow_id: Option<SyncId>,
// Settings for the workflow pane when it's opened (such as a folder to focus upon opening)
settings: OpenWarpDriveObjectSettings,
},
}
#[derive(Clone, Debug, PartialEq)]
pub enum EnvVarCollectionPaneSnapshot {
// CloudEnvVarCollection snapshots operate under the same heuristics
// as NotebookPaneSnapshot::CloudNotebook
CloudEnvVarCollection {
env_var_collection_id: Option<SyncId>,
},
}
#[derive(Clone, Debug, PartialEq)]
pub struct EnvironmentManagementPaneSnapshot {
pub mode: EnvironmentsPage,
}
#[derive(Clone, Debug, PartialEq)]
pub enum SettingsPaneSnapshot {
Local {
current_page: SettingsSection,
search_query: Option<String>,
},
}
#[derive(Clone, Debug, PartialEq)]
pub enum AIFactPaneSnapshot {
Personal,
}
#[derive(Clone, Debug, PartialEq)]
pub enum CodeReviewPaneSnapshot {
Local {
terminal_uuid: Vec<u8>,
repo_path: PathBuf,
},
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum LeftPanelDisplayedTab {
FileTree,
GlobalSearch,
WarpDrive,
ConversationListView,
}
impl From<ToolPanelView> for LeftPanelDisplayedTab {
fn from(view: ToolPanelView) -> Self {
match view {
ToolPanelView::ProjectExplorer => LeftPanelDisplayedTab::FileTree,
ToolPanelView::GlobalSearch { .. } => LeftPanelDisplayedTab::GlobalSearch,
ToolPanelView::WarpDrive => LeftPanelDisplayedTab::WarpDrive,
ToolPanelView::ConversationListView => LeftPanelDisplayedTab::ConversationListView,
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct LeftPanelSnapshot {
pub left_panel_displayed_tab: LeftPanelDisplayedTab,
pub pane_group_id: String,
pub width: usize,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct RightPanelSnapshot {
pub pane_group_id: String,
pub width: usize,
pub is_maximized: bool,
}
/// Copied from pane group model, which should be private to pane group.
#[derive(Clone, Debug, PartialEq)]
pub enum SplitDirection {
Horizontal,
Vertical,
}
#[derive(Clone, Debug, PartialEq)]
pub struct PaneFlex(pub f32);
pub fn get_app_state(app: &AppContext) -> AppState {
let active_window_id = app.windows().active_window();
let quake_mode_id = quake_mode_window_id();
let mut active_window_index = None;
let mut windows = vec![];
for (index, window_id) in app.window_ids().enumerate() {
// Determine index of active window
if let Some(active_window_id) = active_window_id {
if active_window_id == window_id {
active_window_index = Some(index);
}
}
if let Some(first_workspace) = app
.views_of_type::<Workspace>(window_id)
.as_ref()
.and_then(|workspaces| workspaces.first())
{
let ws = first_workspace.as_ref(app);
if ws.is_drag_preview_workspace() {
continue;
}
let snapshot = ws.snapshot(
window_id,
quake_mode_id.map(|id| id == window_id).unwrap_or(false),
app,
);
if !snapshot.tabs.is_empty() {
windows.push(snapshot);
}
}
}
AppState {
windows,
active_window_index,
block_lists: Default::default(),
running_mcp_servers: Vec::new(),
}
}
#[cfg(test)]
#[path = "app_state_tests.rs"]
mod tests;