first pass of merging in warp (doesn't build)

This commit is contained in:
Ryan Ward
2026-07-01 16:08:58 -05:00
parent 2f64909469
commit 4770ac06b5
3662 changed files with 414574 additions and 89772 deletions
@@ -1,3 +1,8 @@
use std::collections::HashMap;
use itertools::Itertools;
use warpui::{AppContext, Entity};
use crate::ai::agent::conversation::{AIConversation, AIConversationId};
use crate::ai::conversation_navigation::ConversationNavigationData;
use crate::search::command_palette::conversations::search::{
@@ -12,9 +17,6 @@ use crate::search::data_source::{DataSourceSearchError, Query, QueryResult};
use crate::search::mixer::DataSourceRunErrorWrapper;
use crate::search::SyncDataSource;
use crate::workspace::Workspace;
use galaxyui::{AppContext, Entity};
use itertools::Itertools;
use std::collections::HashMap;
/// Sections for grouping conversations in the command palette.
#[derive(Debug, PartialEq, Eq, Hash)]
@@ -75,13 +77,6 @@ impl DataSource {
}
}
pub fn historical() -> Self {
Self {
searcher: FuzzyConversationSearcher::historical(),
add_conversation_actions: false,
}
}
/// Returns a [`QueryResult`] for a conversation identified by `conversation_id`. `None` if no result was
/// found with the given ID.
pub fn query_result(
@@ -200,9 +195,7 @@ impl SyncDataSource for DataSource {
self.searcher
.search(&query.text.trim().to_lowercase(), app)
.map_err(|err| {
let search_error = DataSourceSearchError {
message: err.to_string(),
};
let search_error = DataSourceSearchError::new(err.to_string());
Box::new(search_error) as DataSourceRunErrorWrapper
})
};
@@ -2,8 +2,6 @@ mod data_source;
mod search;
mod search_item;
#[cfg(test)]
mod search_test;
pub use data_source::DataSource;
pub use crate::ai::conversation_navigation::ConversationNavigationData;
pub use data_source::DataSource;
@@ -1,11 +1,13 @@
use fuzzy_match::match_indices_case_insensitive;
use warpui::AppContext;
use crate::ai::conversation_navigation::ConversationNavigationData;
use crate::search::command_palette::conversations::search_item::ConversationAction;
use crate::search::command_palette::conversations::search_item::ConversationSearchItem;
use crate::search::command_palette::conversations::search_item::{
ConversationAction, ConversationSearchItem,
};
use crate::search::command_palette::conversations::DataSource;
use crate::search::data_source::QueryResult;
use crate::search::SyncDataSource;
use fuzzy_match::match_indices_case_insensitive;
use galaxyui::AppContext;
/// A conversation that was fuzzy matched against a search term.
#[derive(Debug)]
@@ -178,36 +180,15 @@ pub trait ConversationSearcher {
) -> anyhow::Result<Vec<QueryResult<SearcherAction>>>;
}
#[derive(PartialEq)]
pub enum ConversationType {
All,
Historical,
}
pub struct FuzzyConversationSearcher {
filter: ConversationType,
}
pub struct FuzzyConversationSearcher;
impl FuzzyConversationSearcher {
pub fn new() -> Self {
Self {
filter: ConversationType::All,
}
}
pub fn historical() -> Self {
Self {
filter: ConversationType::Historical,
}
Self
}
pub fn searchable_conversations(&self, app: &AppContext) -> Vec<ConversationNavigationData> {
match self.filter {
ConversationType::Historical => {
ConversationNavigationData::historical_conversations(app)
}
ConversationType::All => ConversationNavigationData::all_conversations(app),
}
ConversationNavigationData::all_conversations(app)
}
}
@@ -228,3 +209,7 @@ impl ConversationSearcher for FuzzyConversationSearcher {
.collect())
}
}
#[cfg(test)]
#[path = "search_tests.rs"]
mod tests;
@@ -1,3 +1,20 @@
use ordered_float::OrderedFloat;
use pathfinder_color::ColorU;
use pathfinder_geometry::vector::vec2f;
use galaxy_core::ui::color::blend::Blend;
use galaxy_core::ui::color::coloru_with_opacity;
use galaxy_core::ui::icons::Icon;
use galaxy_core::ui::theme::color::internal_colors;
use warpui::elements::{
AnchorPair, Container, CrossAxisAlignment, Expanded, Fill, Flex, Highlight, MainAxisSize,
MouseStateHandle, OffsetPositioning, OffsetType, ParentElement, ParentOffsetBounds,
PositioningAxis, Stack, Text, XAxisAnchor, YAxisAnchor,
};
use warpui::fonts::{Properties, Weight};
use warpui::ui_components::button::ButtonTooltipPosition;
use warpui::ui_components::components::{UiComponent, UiComponentStyles};
use warpui::{AppContext, Element, Gradient, SingletonEntity};
use crate::ai::agent::conversation::AIConversationId;
use crate::ai::blocklist::history_model::BlocklistAIHistoryModel;
use crate::appearance::Appearance;
@@ -10,21 +27,6 @@ use crate::search::result_renderer::ItemHighlightState;
use crate::search::SearchItem;
use crate::ui_components::buttons::icon_button;
use crate::util::time_format::format_approx_duration_from_now;
use galaxy_core::ui::color::{blend::Blend, coloru_with_opacity};
use galaxy_core::ui::icons::Icon;
use galaxy_core::ui::theme::color::internal_colors;
use galaxyui::elements::{
AnchorPair, Container, CrossAxisAlignment, Expanded, Fill, Flex, Highlight, MainAxisSize,
MouseStateHandle, OffsetPositioning, OffsetType, ParentElement, ParentOffsetBounds,
PositioningAxis, Stack, Text, XAxisAnchor, YAxisAnchor,
};
use galaxyui::fonts::{Properties, Weight};
use galaxyui::ui_components::button::ButtonTooltipPosition;
use galaxyui::ui_components::components::{UiComponent, UiComponentStyles};
use galaxyui::{AppContext, Element, Gradient, SingletonEntity};
use ordered_float::OrderedFloat;
use pathfinder_color::ColorU;
use pathfinder_geometry::vector::vec2f;
/// Information about which action to take once the conversation item is accepted.
#[derive(Debug)]
@@ -202,7 +204,8 @@ impl ConversationSearchItem {
.finish();
// We only want to show the fork button if the conversation is completed
// (i.e. the agent has finished responding and there are no blocked commands).
// (i.e. the agent has finished responding and there are no blocked commands
// and is not yielded waiting for events).
let conversation_is_done = BlocklistAIHistoryModel::as_ref(app)
.conversation(&conversation.id())
.map(|c| c.status().is_done())
@@ -1,8 +1,8 @@
use crate::ai::{
agent::conversation::AIConversationId, conversation_navigation::ConversationNavigationData,
};
use galaxyui::{EntityId, WindowId};
use crate::ai::agent::conversation::AIConversationId;
use crate::ai::conversation_navigation::ConversationNavigationData;
#[test]
fn test_conversation_navigation_data_ordering() {
// Create test data with different active states and timestamps
+58 -26
View File
@@ -1,28 +1,25 @@
use std::collections::HashSet;
use std::path::PathBuf;
use galaxy_core::context_flag::ContextFlag;
use galaxy_core::features::FeatureFlag;
use warpui::keymap::BindingId;
use warpui::{AppContext, Entity, ModelContext, ModelHandle, SingletonEntity};
use super::{conversations, warp_drive};
use crate::drive::settings::WarpDriveSettings;
use crate::search::action::CommandBindingDataSource;
use crate::search::binding_source::BindingSource;
use crate::search::command_palette::files;
use crate::search::command_palette::launch_config;
use crate::search::command_palette::mixer::{CommandPaletteItemAction, ItemSummary};
use crate::search::command_palette::new_session::NewSessionDataSource;
use crate::search::command_palette::repos::RepoDataSource;
use crate::search::command_palette::{navigation, CommandPaletteMixer};
use crate::search::command_palette::{files, launch_config, navigation, tabs, CommandPaletteMixer};
use crate::search::data_source::QueryResult;
use crate::search::files::model::FileSearchModel;
use crate::search::mixer::AddAsyncSourceOptions;
use crate::search::QueryFilter;
use crate::session_management::SessionSource;
use crate::settings::AISettings;
use galaxy_core::context_flag::ContextFlag;
use galaxy_core::features::FeatureFlag;
use galaxyui::keymap::BindingId;
use galaxyui::{AppContext, Entity, ModelContext, ModelHandle, SingletonEntity};
use super::conversations;
use super::warp_drive;
/// Store of all of the [`crate::search::DataSource`]s for the command palette.
pub struct DataSourceStore {
@@ -31,9 +28,9 @@ pub struct DataSourceStore {
warp_drive_data_source: ModelHandle<warp_drive::DataSource>,
launch_config_data_source: ModelHandle<launch_config::DataSource>,
new_session_data_source: Option<ModelHandle<NewSessionDataSource>>,
historical_conversation_data_source: ModelHandle<conversations::DataSource>,
all_conversation_data_source: ModelHandle<conversations::DataSource>,
repo_data_source: ModelHandle<RepoDataSource>,
tabs_data_source: Option<ModelHandle<tabs::DataSource>>,
}
impl DataSourceStore {
@@ -56,9 +53,6 @@ impl DataSourceStore {
&& cfg!(feature = "local_tty"))
.then_some(ctx.add_model(|ctx| NewSessionDataSource::new(binding_source, ctx)));
let historical_conversation_data_source: ModelHandle<conversations::DataSource> =
ctx.add_model(|_| conversations::DataSource::historical());
let all_conversation_data_source: ModelHandle<conversations::DataSource> =
ctx.add_model(|_| conversations::DataSource::new());
@@ -70,9 +64,9 @@ impl DataSourceStore {
warp_drive_data_source,
launch_config_data_source,
new_session_data_source,
historical_conversation_data_source,
all_conversation_data_source,
repo_data_source,
tabs_data_source: None,
}
}
@@ -128,8 +122,7 @@ impl DataSourceStore {
if FeatureFlag::CommandPaletteFileSearch.is_enabled() && !is_shared_session_viewer {
let file_search_model = FileSearchModel::as_ref(ctx);
let repo_root = file_search_model.repo_root(ctx);
let is_in_git_repo = repo_root.is_some();
let is_in_git_repo = file_search_model.repo_root_location(ctx).is_some();
let files_data_source = if is_in_git_repo {
ctx.add_model(|_| files::data_source::FileDataSource::new())
@@ -154,11 +147,6 @@ impl DataSourceStore {
self.all_conversation_data_source.clone(),
HashSet::from([QueryFilter::Conversations]),
);
mixer.add_sync_source(
self.historical_conversation_data_source.clone(),
HashSet::from([QueryFilter::HistoricalConversations]),
);
}
mixer.add_sync_source(
@@ -170,6 +158,45 @@ impl DataSourceStore {
});
}
/// Resets the [`CommandPaletteMixer`] to the set of data sources relevant for the Ctrl+Tab
/// palette, which shows tabs sorted by MRU order.
pub fn reset_ctrl_tab_mixer(
&mut self,
mixer: ModelHandle<CommandPaletteMixer>,
tabs: Vec<crate::session_management::TabNavigationData>,
ctx: &mut ModelContext<Self>,
) {
if self.tabs_data_source.is_none() {
self.tabs_data_source = Some(ctx.add_model(|_| tabs::DataSource::new()));
}
if let Some(tabs_data_source) = &self.tabs_data_source {
tabs_data_source.update(ctx, |ds, _| ds.set_tabs(tabs));
mixer.update(ctx, |mixer, ctx| {
mixer.reset(ctx);
mixer.add_sync_source(tabs_data_source.clone(), HashSet::from([QueryFilter::Tabs]));
ctx.notify();
});
}
}
/// Restores the [`CommandPaletteMixer`] to the sessions-only source for Ctrl+Tab,
/// undoing any previous `reset_ctrl_tab_mixer` call.
pub fn restore_ctrl_tab_session_mixer(
&self,
mixer: ModelHandle<CommandPaletteMixer>,
ctx: &mut ModelContext<Self>,
) {
mixer.update(ctx, |mixer, ctx| {
mixer.reset(ctx);
mixer.add_sync_source(
self.sessions_data_source.clone(),
HashSet::from([QueryFilter::Sessions]),
);
ctx.notify();
});
}
/// Returns a [`QueryResult`] from the data sources identified by the `summary`. `None` if none
/// of the data sources contained an item with given summary.
pub fn query_result_from_summary(
@@ -219,9 +246,10 @@ impl DataSourceStore {
line_and_column_arg,
} => {
// Create a file search item from the summary
use crate::search::command_palette::files::search_item::FileSearchItem;
use fuzzy_match::FuzzyMatchResult;
use crate::search::command_palette::files::search_item::FileSearchItem;
let search_item = FileSearchItem {
path: PathBuf::from(path),
project_directory: project_directory.clone(),
@@ -236,8 +264,7 @@ impl DataSourceStore {
project_directory,
} => {
// Create a directory search item from the summary
use crate::search::command_palette::files::search_item::FileSearchItem;
use fuzzy_match::FuzzyMatchResult;
let search_item = FileSearchItem {
path: PathBuf::from(path),
@@ -272,6 +299,11 @@ impl DataSourceStore {
// No-op action (used for non-interactable separator items that don't do anything on click).
None
}
ItemSummary::Tab { .. } => {
// Tabs are only shown in the ctrl_tab palette, not in recent commands.
None
}
}
}
@@ -291,5 +323,5 @@ impl Entity for DataSourceStore {
}
#[cfg(test)]
#[path = "data_sources_test.rs"]
#[path = "data_sources_tests.rs"]
mod tests;
@@ -1,44 +1,34 @@
use std::sync::Arc;
use chrono::Utc;
use galaxyui::{App, SingletonEntity};
use cloud_object_client::MockObjectClient;
use settings::manager::SettingsManager;
use super::*;
use crate::auth::AuthStateProvider;
use crate::cloud_object::Owner;
use crate::cloud_object::model::persistence::CloudModel;
use crate::cloud_object::model::view::CloudViewModel;
use crate::cloud_object::{
Owner, Revision, ServerMetadata, ServerNotebook, ServerPermissions, ServerWorkflow,
};
use crate::network::NetworkStatus;
use crate::notebooks::manager::NotebookManager;
use crate::notebooks::CloudNotebookModel;
use crate::notebooks::{CloudNotebookModel, NotebookId};
use crate::search::data_source::Query;
use crate::server::cloud_objects::update_manager::UpdateManager;
use crate::server::ids::ServerId;
use crate::server::ids::SyncId::{self};
use crate::settings::AISettings;
use crate::workflows::workflow::Workflow;
use crate::workflows::CloudWorkflowModel;
use crate::{
cloud_object::{
model::{persistence::CloudModel, view::CloudViewModel},
Revision, ServerMetadata, ServerNotebook, ServerPermissions, ServerWorkflow,
},
network::NetworkStatus,
notebooks::NotebookId,
search::data_source::Query,
server::{
cloud_objects::update_manager::UpdateManager, server_api::ServerApiProvider,
sync_queue::SyncQueue,
},
system::SystemStats,
workflows::WorkflowId,
workspaces::{
team_tester::TeamTesterStatus, user_profiles::UserProfiles, user_workspaces::UserWorkspaces,
},
};
#[cfg(test)]
use crate::server::server_api::object::MockObjectClient;
#[cfg(test)]
use crate::server::server_api::team::MockTeamClient;
#[cfg(test)]
use crate::server::server_api::workspace::MockWorkspaceClient;
use crate::server::server_api::ServerApiProvider;
use crate::server::sync_queue::SyncQueue;
use crate::settings::AISettings;
use crate::system::SystemStats;
use crate::workflows::workflow::Workflow;
use crate::workflows::{CloudWorkflowModel, WorkflowId};
use crate::workspaces::team_tester::TeamTesterStatus;
use crate::workspaces::user_profiles::UserProfiles;
use crate::workspaces::user_workspaces::UserWorkspaces;
fn mock_server_metadata() -> ServerMetadata {
ServerMetadata {
@@ -64,26 +54,26 @@ fn mock_server_permissions(owner: Owner) -> ServerPermissions {
}
fn mock_server_workflow(id: WorkflowId, owner: Owner) -> ServerWorkflow {
ServerWorkflow {
id: SyncId::ServerId(id.into()),
metadata: mock_server_metadata(),
permissions: mock_server_permissions(owner),
model: CloudWorkflowModel::new(Workflow::new(format!("foo{id}"), format!("bar{id}"))),
}
ServerWorkflow::new(
SyncId::ServerId(id.into()),
CloudWorkflowModel::new(Workflow::new(format!("foo{id}"), format!("bar{id}"))),
mock_server_metadata(),
mock_server_permissions(owner),
)
}
fn mock_server_notebook(id: NotebookId, owner: Owner) -> ServerNotebook {
ServerNotebook {
id: SyncId::ServerId(id.into()),
metadata: mock_server_metadata(),
permissions: mock_server_permissions(owner),
model: CloudNotebookModel {
ServerNotebook::new(
SyncId::ServerId(id.into()),
CloudNotebookModel {
title: format!("foo{id}"),
data: format!("bar{id}"),
ai_document_id: None,
conversation_id: None,
},
}
mock_server_metadata(),
mock_server_permissions(owner),
)
}
fn initialize_app(app: &mut App) {
@@ -1,23 +1,25 @@
#![cfg_attr(not(feature = "local_fs"), allow(dead_code))]
use super::search_item::{CreateFileSearchItem, FileSearchItem};
use crate::code::opened_files::OpenedFilesModel;
use crate::search::command_palette::mixer::CommandPaletteItemAction;
use crate::search::data_source::{Query, QueryResult};
use crate::search::files::model::FileSearchModel;
use crate::search::files::search_item::FileSearchResult;
use crate::search::mixer::{AsyncDataSource, BoxFuture, DataSourceRunErrorWrapper};
use futures_lite::FutureExt;
use fuzzy_match::FuzzyMatchResult;
use galaxy_util::path::CleanPathResult;
use galaxyui::{AppContext, Entity, SingletonEntity};
use instant::Instant;
use itertools::Itertools;
use std::collections::HashSet;
#[cfg(feature = "local_fs")]
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
use futures_lite::FutureExt;
use fuzzy_match::FuzzyMatchResult;
use instant::Instant;
use itertools::Itertools;
use galaxy_util::path::CleanPathResult;
use galaxyui::{AppContext, Entity, SingletonEntity};
use super::search_item::{CreateFileSearchItem, FileSearchItem};
use crate::code::opened_files::{OpenedFilesInRepo, OpenedFilesModel};
use crate::search::command_palette::mixer::CommandPaletteItemAction;
use crate::search::data_source::{Query, QueryFilter, QueryResult};
use crate::search::files::model::FileSearchModel;
use crate::search::files::search_item::FileSearchResult;
use crate::search::mixer::{AsyncDataSource, BoxFuture, DataSourceRunErrorWrapper};
const MAX_RESULTS: usize = 100;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
@@ -82,7 +84,11 @@ impl AsyncDataSource for FileDataSource {
self.run_zero_state_query(app)
} else {
// Non-empty query: use fuzzy matching
self.run_fuzzy_search_query(app, query_text)
self.run_fuzzy_search_query(
app,
query_text,
query.filters.contains(&QueryFilter::Files),
)
}
}
}
@@ -103,11 +109,11 @@ impl FileDataSource {
}
}
fn contents(&self, app: &AppContext) -> Arc<Vec<FileSearchResult>> {
fn contents(&self, query: &str, app: &AppContext) -> Arc<Vec<FileSearchResult>> {
match &self.mode {
FileDataSourceMode::Repo => {
let file_search_model = FileSearchModel::as_ref(app);
file_search_model.get_repo_contents(app)
file_search_model.get_repo_contents(query, app)
}
FileDataSourceMode::CurrentFolder { cached_contents } => {
Arc::new(cached_contents.clone())
@@ -127,47 +133,54 @@ impl FileDataSource {
let (contents, git_changed_files) = self.contents_with_git_changes(app);
let mut results = Vec::new();
let opened_files = OpenedFilesModel::as_ref(app);
let repo_root = file_search_model.repo_root(app);
let opened_files =
repo_root.and_then(|repo_root| opened_files.opened_files_for_repo(&repo_root));
let repo_root = file_search_model.repo_root_location(app);
let opened_files = repo_root
.and_then(|repo_root| opened_files.opened_files_for_repo(&repo_root))
.cloned();
for item in contents.iter() {
let mut file_ranking = if git_changed_files.contains(&item.path) {
FileRanking::ChangedInGit
} else {
FileRanking::None
};
Box::pin(async move {
let mut results = Vec::new();
if let Some(last_opened_timestamp) =
opened_files.and_then(|opened_files| opened_files.get(&PathBuf::from(&item.path)))
{
file_ranking = FileRanking::OpenedInWarp {
timestamp: *last_opened_timestamp,
};
for chunk in contents.chunks(50) {
for item in chunk {
let mut file_ranking = if git_changed_files.contains(&item.path) {
FileRanking::ChangedInGit
} else {
FileRanking::None
};
if let Some(last_opened_timestamp) = opened_files
.as_ref()
.and_then(|of: &OpenedFilesInRepo| of.get(&item.path))
{
file_ranking = FileRanking::OpenedInWarp {
timestamp: *last_opened_timestamp,
};
}
let match_result = FuzzyMatchResult {
score: 0,
matched_indices: vec![], // No highlighting needed for zero state
};
let search_item = FileSearchItem {
path: PathBuf::from(&item.path),
project_directory: item.project_directory.clone(),
match_result,
line_and_column_arg: None,
is_directory: item.is_directory,
};
results.push((file_ranking, QueryResult::from(search_item)));
}
futures_lite::future::yield_now().await;
}
let match_result = FuzzyMatchResult {
score: 0,
matched_indices: vec![], // No highlighting needed for zero state
};
results.sort_by_key(|(ranking, _)| *ranking);
let search_item = FileSearchItem {
path: PathBuf::from(&item.path),
project_directory: item.project_directory.clone(),
match_result,
line_and_column_arg: None,
is_directory: item.is_directory,
};
results.push((file_ranking, QueryResult::from(search_item)));
}
results.sort_by_key(|(ranking, _)| *ranking);
Box::pin(async move { Ok(results.into_iter().map(|(_, ranking)| ranking).collect()) })
Ok(results.into_iter().map(|(_, ranking)| ranking).collect())
})
}
/// Handle non-empty query with fuzzy matching (no git status needed)
@@ -175,14 +188,13 @@ impl FileDataSource {
&self,
app: &AppContext,
query_text: &str,
allow_create_file: bool,
) -> BoxFuture<
'static,
Result<Vec<QueryResult<CommandPaletteItemAction>>, DataSourceRunErrorWrapper>,
> {
let file_search_model = FileSearchModel::as_ref(app);
let contents = self.contents(app);
// Strip any trailing : in case user is in the middle of typing a line / column arg.
let query_text = query_text.strip_suffix(':').unwrap_or(query_text);
@@ -191,7 +203,9 @@ impl FileDataSource {
let opened_files = OpenedFilesModel::as_ref(app);
#[cfg(feature = "local_fs")]
let repo_root = file_search_model.repo_root(app);
let repo_root_location = file_search_model.repo_root_location(app);
// For the "Create file" fallback, use the expanded (but not repo-root-stripped)
// path so that absolute paths work correctly with Path::join.
@@ -223,10 +237,15 @@ impl FileDataSource {
)
.unwrap_or(query_file_content);
let opened_files = repo_root
let opened_files = repo_root_location
.and_then(|repo_root| opened_files.opened_files_for_repo(&repo_root))
.cloned();
// Fetch contents using the finalized query so it is pushed down into
// the repo-metadata traversal as a filter (matching files are not
// truncated away before fuzzy matching).
let contents = self.contents(&query_file_content, app);
const CHUNK_SIZE: usize = 50;
Box::pin(async move {
@@ -249,7 +268,7 @@ impl FileDataSource {
if opened_files
.as_ref()
.and_then(|opened_files| opened_files.get(&PathBuf::from(&item.path)))
.and_then(|of: &OpenedFilesInRepo| of.get(&item.path))
.is_some()
{
// Apply a boost to opened files to rank them above non-opened files.
@@ -275,8 +294,8 @@ impl FileDataSource {
.collect();
// If no files matched and we have a valid query and current directory,
// add a "Create <filename>..." option
if results.is_empty() && !query_file_name.trim().is_empty() {
// add a "Create a file named <filename>..." option
if allow_create_file && results.is_empty() && !query_file_name.trim().is_empty() {
if let Some(current_dir) = current_directory {
let create_item = CreateFileSearchItem {
file_name: query_file_name,
@@ -1,19 +1,19 @@
use fuzzy_match::FuzzyMatchResult;
use galaxy_util::path::LineAndColumnArg;
use ordered_float::OrderedFloat;
use std::fmt::Debug;
use std::path::PathBuf;
use crate::appearance::Appearance;
use crate::search::command_palette::mixer::CommandPaletteItemAction;
use crate::search::command_palette::styles;
use crate::search::item::{IconLocation, SearchItem};
use crate::search::result_renderer::ItemHighlightState;
use fuzzy_match::FuzzyMatchResult;
use ordered_float::OrderedFloat;
use galaxy_util::path::LineAndColumnArg;
use galaxyui::elements::{Align, ConstrainedBox, Container, Flex, Icon, ParentElement, Text};
use galaxyui::fonts::{Properties, Weight};
use galaxyui::{AppContext, Element, SingletonEntity};
use crate::appearance::Appearance;
use crate::search::command_palette::mixer::CommandPaletteItemAction;
use crate::search::command_palette::styles;
use crate::search::files::icon::icon_from_file_path;
use crate::search::item::{IconLocation, SearchItem};
use crate::search::result_renderer::ItemHighlightState;
use crate::ui_components::render_file_search_row::{render_file_search_row, FileSearchRowOptions};
#[derive(Debug)]
@@ -65,6 +65,7 @@ impl SearchItem for FileSearchItem {
FileSearchRowOptions {
match_result: Some(&self.match_result),
highlight_state,
max_combined_length: None,
..Default::default()
},
app,
@@ -160,7 +161,7 @@ impl SearchItem for CreateFileSearchItem {
let text_color = highlight_state.sub_text_fill(appearance).into_solid();
let label = Text::new_inline(
format!("Create {}", &self.file_name),
format!("Create a file named {}", &self.file_name),
appearance.ui_font_family(),
appearance.monospace_font_size(),
)
@@ -1,16 +1,16 @@
use crate::appearance::Appearance;
use crate::drive::cloud_object_styling::warp_drive_icon_color;
use crate::drive::DriveObjectType;
use crate::search::FilterChipRenderer as CommonFilterChipRenderer;
use crate::search::QueryFilter;
use crate::util::color::{ContrastingColor, MinimumAllowedContrast};
use pathfinder_color::ColorU;
use galaxyui::elements::{
ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, Flex, Hoverable, Icon,
MouseStateHandle, ParentElement, Radius, Text,
};
use galaxyui::platform::Cursor;
use galaxyui::{Element, EventContext};
use pathfinder_color::ColorU;
use crate::appearance::Appearance;
use crate::drive::cloud_object_styling::warp_drive_icon_color;
use crate::drive::DriveObjectType;
use crate::search::{FilterChipRenderer as CommonFilterChipRenderer, QueryFilter};
use crate::util::color::{ContrastingColor, MinimumAllowedContrast};
/// Trait to render filter chips for the command palette.
pub trait FilterChipRenderer: crate::search::FilterChipRenderer {
@@ -100,6 +100,7 @@ impl FilterChipRenderer for QueryFilter {
| QueryFilter::NaturalLanguage
| QueryFilter::Actions
| QueryFilter::Sessions
| QueryFilter::Tabs
| QueryFilter::Drive
| QueryFilter::LaunchConfigurations
| QueryFilter::PromptHistory
@@ -118,7 +119,7 @@ impl FilterChipRenderer for QueryFilter {
.theme()
.main_text_color(appearance.theme().surface_2())
.into_solid(),
QueryFilter::Conversations | QueryFilter::HistoricalConversations => appearance
QueryFilter::Conversations => appearance
.theme()
.main_text_color(appearance.theme().surface_2())
.into_solid(),
@@ -146,9 +147,10 @@ impl FilterChipRenderer for QueryFilter {
}
mod styles {
use crate::themes::theme::{Blend, Fill, GalaxyTheme};
use galaxyui::elements::{Border, MouseState};
use crate::themes::theme::{Blend, Fill, WarpTheme};
/// Size of the border when the query filter is hovered.
const HOVERED_BORDER_SIZE: f32 = 2.;
/// Size of the border when the query filter is _not_ hovered.
@@ -1,13 +1,15 @@
use std::collections::HashMap;
use std::sync::Arc;
use fuzzy_match::match_indices_case_insensitive;
use warpui::{AppContext, Entity, ModelContext, ModelHandle, SingletonEntity};
use crate::launch_configs::launch_config::LaunchConfig;
use crate::search::command_palette::launch_config::search_item::SearchItem;
use crate::search::command_palette::mixer::CommandPaletteItemAction;
use crate::search::data_source::{DataSourceSearchError, Query, QueryResult};
use crate::search::mixer::{DataSourceRunErrorWrapper, SyncDataSource};
use crate::user_config::{GalaxyConfig, GalaxyConfigUpdateEvent};
use fuzzy_match::match_indices_case_insensitive;
use galaxyui::{AppContext, Entity, ModelContext, SingletonEntity};
use std::collections::HashMap;
use std::sync::Arc;
use crate::user_config::{WarpConfig, WarpConfigUpdateEvent};
/// Datasource that searches against `LaunchConfig`s.
pub struct DataSource {
@@ -48,10 +50,11 @@ impl DataSource {
fn handle_config_event(
&mut self,
event: &GalaxyConfigUpdateEvent,
_: ModelHandle<WarpConfig>,
event: &WarpConfigUpdateEvent,
ctx: &mut ModelContext<Self>,
) {
if matches!(event, GalaxyConfigUpdateEvent::LaunchConfigs) {
if matches!(event, WarpConfigUpdateEvent::LaunchConfigs) {
self.searcher.refresh_search_index(ctx);
}
}
@@ -68,9 +71,7 @@ impl SyncDataSource for DataSource {
.searcher
.search(&query.text.trim().to_lowercase())
.map_err(|err| {
Box::new(DataSourceSearchError {
message: err.to_string(),
}) as DataSourceRunErrorWrapper
Box::new(DataSourceSearchError::new(err.to_string())) as DataSourceRunErrorWrapper
})?
.into_iter()
.map(QueryResult::from)
@@ -121,17 +122,15 @@ impl LaunchConfigSearcher for FuzzyLaunchConfigSearcher {
#[cfg(not(target_family = "wasm"))]
mod full_text_searcher {
use crate::define_search_schema;
use crate::launch_configs::launch_config::LaunchConfig;
use crate::search::command_palette::launch_config::data_source::LaunchConfigSearcher;
use crate::search::command_palette::launch_config::search_item::SearchItem;
use crate::search::searcher::{AsyncSearcher, DEFAULT_MEMORY_BUDGET, SCORE_CONVERSION_FACTOR};
use crate::user_config::GalaxyConfig;
use fuzzy_match::FuzzyMatchResult;
use galaxyui::r#async::executor::Background;
use galaxyui::{AppContext, SingletonEntity};
use std::collections::HashMap;
use std::sync::Arc;
use warp_search_core::define_search_schema;
use warpui::r#async::executor::Background;
use warpui::{AppContext, SingletonEntity};
use crate::search::command_palette::launch_config::data_source::LaunchConfigSearcher;
use crate::search::searcher::{AsyncSearcher, DEFAULT_MEMORY_BUDGET, SCORE_CONVERSION_FACTOR};
use crate::user_config::WarpConfig;
// The name of the launch configs are duplicated to ensure that the searcher
// hashes the name to uniquely identify the launch config.
@@ -1,15 +1,11 @@
use galaxyui::{
elements::{
Align, Border, ConstrainedBox, Container, CornerRadius, Flex, Highlight, ParentElement,
Radius, Shrinkable, Text,
},
fonts::{Properties, Weight},
ui_components::{
components::{Coords, UiComponent, UiComponentStyles},
text::Span,
},
Element,
use galaxyui::elements::{
Align, Border, ConstrainedBox, Container, CornerRadius, Flex, Highlight, ParentElement, Radius,
Shrinkable, Text,
};
use warpui::fonts::{Properties, Weight};
use warpui::ui_components::components::{Coords, UiComponent, UiComponentStyles};
use warpui::ui_components::text::Span;
use warpui::Element;
use crate::appearance::Appearance;
use crate::launch_configs::launch_config::LaunchConfig;
@@ -1,14 +1,15 @@
use crate::launch_configs::launch_config::LaunchConfig;
use crate::{appearance::Appearance, ui_components::icons::Icon};
use fuzzy_match::FuzzyMatchResult;
use std::sync::Arc;
use fuzzy_match::FuzzyMatchResult;
use ordered_float::OrderedFloat;
use warpui::{AppContext, Element, SingletonEntity};
use crate::appearance::Appearance;
use crate::launch_configs::launch_config::LaunchConfig;
use crate::search::command_palette::mixer::CommandPaletteItemAction;
use crate::search::command_palette::render_util::render_search_item_icon;
use crate::search::result_renderer::ItemHighlightState;
use galaxyui::{AppContext, Element, SingletonEntity};
use ordered_float::OrderedFloat;
use std::sync::Arc;
use crate::ui_components::icons::Icon;
/// SearchItem for a matching [`LaunchConfig`].
#[derive(Debug)]
+18 -5
View File
@@ -1,3 +1,10 @@
use std::sync::Arc;
use strum_macros::IntoStaticStr;
use warp_util::path::LineAndColumnArg;
use warpui::keymap::BindingId;
use warpui::{EntityId, WindowId};
use crate::ai::agent::conversation::AIConversationId;
use crate::drive::CloudObjectTypeAndId;
use crate::launch_configs::launch_config::LaunchConfig;
@@ -6,11 +13,6 @@ use crate::search::mixer::SearchMixer;
use crate::server::ids::SyncId;
use crate::util::bindings::CommandBinding;
use crate::workspace::PaneViewLocator;
use galaxy_util::path::LineAndColumnArg;
use galaxyui::keymap::BindingId;
use galaxyui::{EntityId, WindowId};
use std::sync::Arc;
use strum_macros::IntoStaticStr;
pub type CommandPaletteMixer = SearchMixer<CommandPaletteItemAction>;
@@ -37,6 +39,11 @@ pub enum CommandPaletteItemAction {
pane_view_locator: PaneViewLocator,
window_id: WindowId,
},
/// Navigate to a specific tab identified by its pane_group EntityId.
NavigateToTab {
pane_group_id: EntityId,
window_id: WindowId,
},
/// Navigate to a specific conversation.
NavigateToConversation {
pane_view_locator: Option<PaneViewLocator>,
@@ -94,6 +101,9 @@ impl CommandPaletteItemAction {
} => ItemSummary::Session {
pane_view_locator: *pane_view_locator,
},
CommandPaletteItemAction::NavigateToTab { pane_group_id, .. } => ItemSummary::Tab {
pane_group_id: *pane_group_id,
},
CommandPaletteItemAction::NavigateToConversation {
conversation_id, ..
} => ItemSummary::Conversation {
@@ -169,6 +179,9 @@ pub enum ItemSummary {
Session {
pane_view_locator: PaneViewLocator,
},
Tab {
pane_group_id: EntityId,
},
NewSession {
id: NewSessionOptionId,
},
+1
View File
@@ -11,6 +11,7 @@ pub mod render_util;
pub mod repos;
mod selected_items;
pub mod separator_search_item;
pub mod tabs;
pub mod view;
pub mod warp_drive;
mod zero_state;
@@ -1,3 +1,5 @@
use warpui::{AppContext, Entity, ModelHandle};
use crate::search::command_palette::mixer::CommandPaletteItemAction;
use crate::search::command_palette::navigation::search::{
FuzzySessionSearcher, MatchedSession, SessionMatchResult, SessionSearcher,
@@ -8,7 +10,6 @@ use crate::search::mixer::DataSourceRunErrorWrapper;
use crate::search::SyncDataSource;
use crate::session_management::{SessionNavigationData, SessionSource};
use crate::workspace::PaneViewLocator;
use galaxyui::{AppContext, Entity, ModelHandle};
/// Data source that produces possible running sessions a user could navigate to.
pub struct DataSource {
@@ -56,9 +57,7 @@ impl SyncDataSource for DataSource {
self.searcher
.search(&query.text.trim().to_lowercase(), app)
.map_err(|err| {
let search_error = DataSourceSearchError {
message: err.to_string(),
};
let search_error = DataSourceSearchError::new(err.to_string());
Box::new(search_error) as DataSourceRunErrorWrapper
})
}
@@ -1,3 +1,13 @@
use pathfinder_geometry::vector::vec2f;
use warpui::elements::{
Align, Border, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, Flex, Highlight,
ParentElement, Radius, Shrinkable, Wrap,
};
use warpui::fonts::{Properties, Weight};
use warpui::ui_components::components::{UiComponent, UiComponentStyles};
use warpui::units::IntoPixels;
use warpui::{AppContext, Element, SingletonEntity};
use crate::appearance::Appearance;
use crate::context_chips::display_chip::{
chip_container, render_git_diff_stats_content, render_udi_chip, udi_font_size, GitLineChanges,
@@ -16,15 +26,6 @@ use crate::terminal::model::blockgrid::BlockGrid;
use crate::terminal::model::grid::Dimensions;
use crate::terminal::safe_mode_settings::get_secret_obfuscation_mode;
use crate::terminal::SizeInfo;
use galaxyui::elements::{
Align, Border, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, Flex, Highlight,
ParentElement, Radius, Shrinkable, Wrap,
};
use galaxyui::fonts::{Properties, Weight};
use galaxyui::ui_components::components::{UiComponent, UiComponentStyles};
use galaxyui::units::IntoPixels;
use galaxyui::{AppContext, Element, SingletonEntity};
use pathfinder_geometry::vector::vec2f;
/// Renders a navigation session.
pub fn render_navigation_session(
@@ -163,6 +164,7 @@ fn render_prompt_udi(snapshot: &PromptSnapshot, appearance: &Appearance) -> Box<
};
parsed
}
ChipValue::GitBranchStatus(_) => continue,
};
let font_size = udi_font_size(appearance);
let content = render_git_diff_stats_content(
@@ -1,3 +1,9 @@
use std::ops::Range;
use fuzzy_match::match_indices_case_insensitive;
use itertools::Itertools;
use warpui::{AppContext, ModelHandle};
use crate::pane_group::PaneId;
use crate::search::command_palette::navigation::render::CommandRenderInfo;
use crate::search::command_palette::navigation::search_item::SearchItem;
@@ -5,10 +11,6 @@ use crate::search::command_palette::navigation::DataSource;
use crate::search::data_source::QueryResult;
use crate::search::SyncDataSource;
use crate::session_management::{CommandContext, SessionNavigationData, SessionSource};
use fuzzy_match::match_indices_case_insensitive;
use galaxyui::{AppContext, ModelHandle};
use itertools::Itertools;
use std::ops::Range;
/// A session that was fuzzy matched against a search term.
pub struct MatchedSession {
@@ -242,19 +244,16 @@ impl SessionSearcher for FuzzySessionSearcher {
pub use full_text_searcher::FullTextSessionSearcher;
#[cfg(not(target_family = "wasm"))]
mod full_text_searcher {
use crate::define_search_schema;
use crate::pane_group::PaneId;
use std::collections::HashMap;
use warp_search_core::define_search_schema;
use crate::search::command_palette::navigation::search::{
searchable_session_string_and_ranges, MatchedSession, SearcherAction,
SessionHighlightIndices, SessionMatchResult, SessionSearcher,
};
use crate::search::command_palette::navigation::search_item::SearchItem;
use crate::search::data_source::QueryResult;
use crate::search::searcher::{DEFAULT_MEMORY_BUDGET, SCORE_CONVERSION_FACTOR};
use crate::session_management::{SessionNavigationData, SessionSource};
use galaxyui::{AppContext, ModelHandle};
use itertools::Itertools;
use std::collections::HashMap;
define_search_schema!(
schema_name: SESSION_SEARCH_SCHEMA,
@@ -1,3 +1,7 @@
use ordered_float::OrderedFloat;
use warpui::elements::Container;
use warpui::{AppContext, Element, SingletonEntity};
use crate::appearance::Appearance;
use crate::pane_group::PaneId;
use crate::search::command_palette::mixer::CommandPaletteItemAction;
@@ -8,9 +12,6 @@ use crate::search::item::IconLocation;
use crate::search::result_renderer::ItemHighlightState;
use crate::session_management::SessionNavigationData;
use crate::ui_components::icons::Icon;
use galaxyui::elements::Container;
use galaxyui::{AppContext, Element, SingletonEntity};
use ordered_float::OrderedFloat;
/// Search item to render a session within the command palette.
pub struct SearchItem {
@@ -1,19 +1,19 @@
use super::new_session_option::{Direction, NewSessionConfig};
use super::new_session_option::{NewSessionOption, NewSessionOptionId};
use super::search_item::SearchItem;
use crate::search::data_source::DataSourceSearchError;
use crate::search::{
binding_source::BindingSource,
command_palette::mixer::CommandPaletteItemAction,
data_source::{Query, QueryResult},
mixer::{DataSourceRunErrorWrapper, SyncDataSource},
};
use crate::terminal::available_shells::AvailableShells;
use std::collections::HashMap;
use std::sync::Arc;
use fuzzy_match::{match_indices_case_insensitive, FuzzyMatchResult};
use galaxy_core::features::FeatureFlag;
use galaxyui::{AppContext, Entity, ModelContext, ModelHandle, SingletonEntity};
use std::collections::HashMap;
use std::sync::Arc;
use super::new_session_option::{
Direction, NewSessionConfig, NewSessionOption, NewSessionOptionId,
};
use super::search_item::SearchItem;
use crate::search::binding_source::BindingSource;
use crate::search::command_palette::mixer::CommandPaletteItemAction;
use crate::search::data_source::{DataSourceSearchError, Query, QueryResult};
use crate::search::mixer::{DataSourceRunErrorWrapper, SyncDataSource};
use crate::terminal::available_shells::AvailableShells;
/// Controls which kinds of new sessions the data source should surface.
#[derive(Copy, Clone, Debug)]
@@ -47,7 +47,7 @@ impl AllowedSessionKinds {
/// Gathers this data by:
/// - Listening for any binding source changes
/// - Comparing the options in binding sources (open new tab, open new window, etc.)
/// to the list of available shells, and creates an interesction of those items.
/// to the list of available shells, and creates an intersection of those items.
pub struct NewSessionDataSource {
searcher: Box<dyn NewSessionSearcher>,
allowed: AllowedSessionKinds,
@@ -189,9 +189,7 @@ impl SyncDataSource for NewSessionDataSource {
) -> Result<Vec<QueryResult<Self::Action>>, DataSourceRunErrorWrapper> {
let search_term = query.text.as_str();
self.searcher.search(search_term).map_err(|err| {
let search_error = DataSourceSearchError {
message: err.to_string(),
};
let search_error = DataSourceSearchError::new(err.to_string());
Box::new(search_error) as DataSourceRunErrorWrapper
})
}
@@ -301,7 +299,11 @@ impl NewSessionSearcher for FuzzyNewSessionSearcher {
#[cfg(not(target_family = "wasm"))]
mod full_text_searcher {
use crate::define_search_schema;
use fuzzy_match::FuzzyMatchResult;
use warp_search_core::define_search_schema;
use warpui::r#async::executor::Background;
use crate::search::command_palette::new_session::data_source::{
NewSessionSearcher, SearcherAction, SEARCHER_BASE_STRINGS,
};
@@ -311,10 +313,6 @@ mod full_text_searcher {
use crate::search::searcher::{
AsyncSearcher, DEFAULT_MEMORY_BUDGET, MIN_MEMORY_BUDGET, SCORE_CONVERSION_FACTOR,
};
use fuzzy_match::FuzzyMatchResult;
use galaxyui::r#async::executor::Background;
use std::collections::HashMap;
use std::sync::Arc;
define_search_schema!(
schema_name: NEW_SESSION_SEARCH_SCHEMA,
@@ -1,10 +1,12 @@
use std::borrow::Cow;
use std::fmt;
use warpui::Action;
use crate::server::telemetry::AddTabWithShellSource;
use crate::terminal::available_shells::AvailableShell;
use crate::terminal::view::TerminalAction;
use crate::WorkspaceAction;
use galaxyui::Action;
use std::borrow::Cow;
use std::fmt;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct NewSessionOptionId(pub(crate) String);
@@ -1,13 +1,10 @@
use warpui::elements::{Container, Flex, Highlight, ParentElement, Text};
use warpui::fonts::{Properties, Weight};
use warpui::Element;
use super::new_session_option::NewSessionOption;
use crate::search::command_palette::styles::SEARCH_ITEM_TEXT_PADDING;
use galaxyui::{
elements::{Container, Flex, Highlight, ParentElement, Text},
fonts::{Properties, Weight},
Element,
};
use crate::appearance::Appearance;
use crate::search::command_palette::styles::SEARCH_ITEM_TEXT_PADDING;
use crate::search::result_renderer::ItemHighlightState;
impl NewSessionOption {
@@ -1,17 +1,16 @@
use super::new_session_option::NewSessionOption;
use crate::{
appearance::Appearance, search::command_palette::render_util::render_search_item_icon,
ui_components::icons::Icon,
};
use fuzzy_match::FuzzyMatchResult;
use crate::search::command_palette::mixer::CommandPaletteItemAction;
use crate::search::result_renderer::ItemHighlightState;
use galaxyui::{AppContext, Element, SingletonEntity};
use ordered_float::OrderedFloat;
use std::sync::Arc;
use fuzzy_match::FuzzyMatchResult;
use ordered_float::OrderedFloat;
use galaxyui::{AppContext, Element, SingletonEntity};
use super::new_session_option::NewSessionOption;
use crate::appearance::Appearance;
use crate::search::command_palette::mixer::CommandPaletteItemAction;
use crate::search::command_palette::render_util::render_search_item_icon;
use crate::search::result_renderer::ItemHighlightState;
use crate::ui_components::icons::Icon;
#[derive(Debug)]
pub struct SearchItem {
match_result: FuzzyMatchResult,
@@ -1,12 +1,13 @@
use pathfinder_color::ColorU;
use galaxy_core::ui::theme::Fill;
use warpui::elements::{Align, ConstrainedBox, Container, Empty};
use warpui::Element;
use crate::appearance::Appearance;
use crate::search::result_renderer::ItemHighlightState;
use crate::themes::theme::Blend;
use crate::ui_components::icons::Icon;
use crate::util::color::{ContrastingColor, MinimumAllowedContrast};
use galaxy_core::ui::theme::Fill;
use galaxyui::elements::{Align, ConstrainedBox, Container, Empty};
use galaxyui::Element;
use pathfinder_color::ColorU;
/// Helper function to render an icon for any search item within the command palette with consistent
/// styling.
@@ -1,13 +1,12 @@
use std::path::Path;
use ai::workspace::WorkspaceMetadata;
use fuzzy_match::FuzzyMatchResult;
use galaxy_core::ui::theme::Fill;
use galaxyui::{
elements::{Align, ConstrainedBox, Flex, Highlight, ParentElement, Shrinkable, Text},
fonts::{Properties, Weight},
AppContext, Element, SingletonEntity,
};
use ordered_float::OrderedFloat;
use std::path::Path;
use galaxy_core::ui::theme::Fill;
use galaxyui::elements::{Align, ConstrainedBox, Flex, Highlight, ParentElement, Shrinkable, Text};
use galaxyui::fonts::{Properties, Weight};
use galaxyui::{AppContext, Element, SingletonEntity};
use crate::appearance::Appearance;
use crate::search::action::search_item::styles;
@@ -1,7 +1,8 @@
use crate::search::command_palette::mixer::ItemSummary;
use bounded_vec_deque::BoundedVecDeque;
use galaxyui::{Entity, SingletonEntity};
use crate::search::command_palette::mixer::ItemSummary;
/// Maximum number of elements to store. Per the [`BoundedVecDeque`] docs, it is recommended that
/// this is one less than the power of two to avoid unnecessary allocations.
///
@@ -1,7 +1,7 @@
use super::*;
use galaxyui::keymap::BindingId;
use itertools::Itertools;
use super::*;
#[test]
fn test_enqueue_new_item() {
let mut selected_items = SelectedItems::new();
@@ -1,11 +1,11 @@
use ordered_float::OrderedFloat;
use warpui::elements::{Empty, Text};
use warpui::{AppContext, Element, SingletonEntity};
use crate::appearance::Appearance;
use crate::search::command_palette::mixer::CommandPaletteItemAction;
use crate::search::item::SearchItem;
use crate::search::result_renderer::ItemHighlightState;
use crate::{appearance::Appearance, search::command_palette::mixer::CommandPaletteItemAction};
use galaxyui::{
elements::{Empty, Text},
AppContext, Element, SingletonEntity,
};
use ordered_float::OrderedFloat;
/// A simple separator item that displays a title to visually separate sections in search results.
#[derive(Debug)]
@@ -0,0 +1,65 @@
use warpui::{AppContext, Entity};
use crate::search::command_palette::mixer::CommandPaletteItemAction;
use crate::search::command_palette::tabs::SearchItem;
use crate::search::data_source::{Query, QueryResult};
use crate::search::mixer::{DataSourceRunErrorWrapper, SyncDataSource};
use crate::session_management::TabNavigationData;
/// Data source that produces tabs sorted by MRU order for the Ctrl+Tab palette.
///
/// Holds a pre-computed snapshot of tab data rather than a workspace handle,
/// because the synchronous query runs while the workspace view is borrowed
/// and a `WeakViewHandle::upgrade()` would fail.
pub struct DataSource {
tabs: Vec<TabNavigationData>,
}
impl Default for DataSource {
fn default() -> Self {
Self::new()
}
}
impl DataSource {
pub fn new() -> Self {
Self { tabs: vec![] }
}
pub fn set_tabs(&mut self, tabs: Vec<TabNavigationData>) {
self.tabs = tabs;
}
}
impl SyncDataSource for DataSource {
type Action = CommandPaletteItemAction;
fn run_query(
&self,
query: &Query,
_ctx: &AppContext,
) -> Result<Vec<QueryResult<Self::Action>>, DataSourceRunErrorWrapper> {
let query_text = query.text.trim().to_lowercase();
let results = self
.tabs
.iter()
.enumerate()
.filter(|(_, tab)| {
query_text.is_empty()
|| tab.title.to_lowercase().contains(&query_text)
|| tab
.subtitle
.as_deref()
.is_some_and(|s| s.to_lowercase().contains(&query_text))
})
.map(|(i, tab)| QueryResult::from(SearchItem::new(tab.clone(), i)))
.collect();
Ok(results)
}
}
impl Entity for DataSource {
type Event = ();
}
@@ -0,0 +1,5 @@
pub mod data_source;
pub mod search_item;
pub use data_source::DataSource;
pub use search_item::SearchItem;
@@ -0,0 +1,117 @@
use ordered_float::OrderedFloat;
use warpui::elements::{ConstrainedBox, Container, Flex, ParentElement, Text};
use warpui::fonts::{Properties, Weight};
use warpui::{AppContext, Element, SingletonEntity};
use crate::appearance::Appearance;
use crate::search::command_palette::mixer::CommandPaletteItemAction;
use crate::search::command_palette::render_util::render_search_item_icon;
use crate::search::item::{IconLocation, SearchItem as SearchItemTrait};
use crate::search::result_renderer::ItemHighlightState;
use crate::session_management::TabNavigationData;
use crate::ui_components::icons::Icon;
/// These items appear in the ctrl-tab palette only, not the main command palette.
/// Scoring matches against queries is not supported since only ranking by recency is needed.
pub struct SearchItem {
tab: TabNavigationData,
mru_rank: usize,
}
impl SearchItem {
pub fn new(tab: TabNavigationData, mru_rank: usize) -> Self {
Self { tab, mru_rank }
}
}
impl SearchItemTrait for SearchItem {
type Action = CommandPaletteItemAction;
fn is_multiline(&self) -> bool {
self.tab.subtitle.is_some()
}
fn render_icon(
&self,
highlight_state: ItemHighlightState,
appearance: &Appearance,
) -> Box<dyn Element> {
let color = if let Some(tab_color) = self.tab.color {
tab_color
.to_ansi_color(&appearance.theme().terminal_colors().normal)
.into()
} else {
highlight_state.icon_fill(appearance).into_solid()
};
render_search_item_icon(appearance, Icon::Navigation, color, highlight_state)
}
fn icon_location(&self, appearance: &Appearance) -> IconLocation {
let margin_top = (appearance.line_height_ratio() * appearance.monospace_font_size())
- appearance.monospace_font_size();
IconLocation::Top { margin_top }
}
fn render_item(
&self,
highlight_state: ItemHighlightState,
app: &AppContext,
) -> Box<dyn Element> {
let appearance = Appearance::as_ref(app);
let title_text = Text::new_inline(
format!("{} · Tab {}", self.tab.title, self.tab.tab_index),
appearance.ui_font_family(),
appearance.monospace_font_size(),
)
.with_color(highlight_state.main_text_fill(appearance).into_solid())
.with_style(Properties::default().weight(Weight::Bold))
.finish();
if let Some(subtitle) = &self.tab.subtitle {
let subtitle_text = Text::new_inline(
subtitle.clone(),
appearance.ui_font_family(),
appearance.monospace_font_size() - 2.,
)
.with_color(highlight_state.sub_text_fill(appearance).into_solid())
.finish();
let contents = Flex::column()
.with_child(title_text)
.with_child(Container::new(subtitle_text).with_padding_top(4.).finish())
.finish();
ConstrainedBox::new(contents).with_height(50.).finish()
} else {
ConstrainedBox::new(Flex::column().with_child(title_text).finish())
.with_height(50.)
.finish()
}
}
fn score(&self) -> OrderedFloat<f64> {
OrderedFloat::from(1000.0 - self.mru_rank as f64)
}
fn accept_result(&self) -> Self::Action {
CommandPaletteItemAction::NavigateToTab {
pane_group_id: self.tab.pane_group_id,
window_id: self.tab.window_id,
}
}
fn execute_result(&self) -> Self::Action {
self.accept_result()
}
fn accessibility_label(&self) -> String {
format!("Selected tab: {}.", self.tab.title)
}
fn accessibility_help_message(&self) -> Option<String> {
Some(format!(
"Press enter to navigate to tab: {}.",
self.tab.title
))
}
}
+69 -68
View File
@@ -1,60 +1,53 @@
use crate::appearance::Appearance;
use crate::drive::CloudObjectTypeAndId;
use crate::search::binding_source::{BindingFilterFn, BindingSource};
use crate::search::command_palette::mixer::CommandPaletteItemAction;
use crate::search::command_palette::SelectedItems;
use crate::search::result_renderer::QueryResultRenderer;
use crate::search::search_bar::SelectionUpdate;
use crate::search::search_bar::{SearchBar, SearchBarEvent, SearchBarState, SearchResultOrdering};
use crate::search::QueryFilter;
use crate::send_telemetry_from_ctx;
use crate::server::telemetry::LaunchConfigUiLocation;
use crate::server::telemetry::TelemetryEvent;
use crate::settings::CtrlTabBehavior;
use crate::terminal::keys_settings::KeysSettings;
use crate::themes::theme::GalaxyTheme;
use crate::view_components::DismissibleToast;
use crate::ToastStack;
use galaxy_core::send_telemetry_from_app_ctx;
use galaxy_util::path::LineAndColumnArg;
use lazy_static::lazy_static;
use crate::search::action::search_item::MatchedBinding;
use galaxyui::elements::DispatchEventResult;
use galaxyui::elements::EventHandler;
use galaxyui::event::KeyState;
use galaxyui::platform::keyboard::KeyCode;
use galaxyui::FocusContext;
use itertools::Itertools;
use crate::search::command_palette::zero_state::{self, Event as ZeroStateEvent, ZeroState};
use crate::search::data_source::QueryResult;
use std::collections::HashSet;
use std::ops::Deref;
use std::sync::Arc;
use crate::features::FeatureFlag;
use crate::palette::PaletteMode;
use crate::root_view::OpenLaunchConfigArg;
use crate::search::command_palette::data_sources::DataSourceStore;
use crate::server::ids::SyncId;
use crate::session_management::SessionSource;
use crate::workspace::{active_terminal_in_window, ForkedConversationDestination, WorkspaceAction};
use itertools::Itertools;
use lazy_static::lazy_static;
use galaxy_core::send_telemetry_from_app_ctx;
use galaxy_util::path::LineAndColumnArg;
use galaxyui::elements::{
Align, Border, ChildView, Clipped, ClippedScrollStateHandle, ClippedScrollable, ConstrainedBox,
Container, CornerRadius, Dismiss, Empty, Fill, Flex, ParentElement, Radius, SavePosition,
Shrinkable,
Container, CornerRadius, Dismiss, DispatchEventResult, Empty, EventHandler, Fill, Flex,
ParentElement, Radius, SavePosition, Shrinkable,
};
use galaxyui::event::KeyState;
use galaxyui::keymap::BindingId;
use galaxyui::platform::keyboard::KeyCode;
use galaxyui::units::{IntoPixels, Pixels};
use galaxyui::{
AppContext, Element, Entity, EntityId, ModelHandle, SingletonEntity, TypedActionView,
ViewContext, ViewHandle, WindowId,
AppContext, Element, Entity, EntityId, FocusContext, ModelHandle, SingletonEntity,
TypedActionView, ViewContext, ViewHandle, WindowId,
};
use super::super::palette_styles as styles;
use super::CommandPaletteMixer;
use crate::appearance::Appearance;
use crate::drive::CloudObjectTypeAndId;
use crate::features::FeatureFlag;
use crate::palette::PaletteMode;
use crate::root_view::OpenLaunchConfigArg;
use crate::search::action::search_item::MatchedBinding;
use crate::search::binding_source::{BindingFilterFn, BindingSource};
use crate::search::command_palette::data_sources::DataSourceStore;
use crate::search::command_palette::mixer::CommandPaletteItemAction;
use crate::search::command_palette::zero_state::{self, Event as ZeroStateEvent, ZeroState};
use crate::search::command_palette::SelectedItems;
use crate::search::data_source::QueryResult;
use crate::search::result_renderer::QueryResultRenderer;
use crate::search::search_bar::{
SearchBar, SearchBarEvent, SearchBarState, SearchResultOrdering, SelectionUpdate,
};
use crate::search::QueryFilter;
use crate::server::ids::SyncId;
use crate::server::telemetry::{LaunchConfigUiLocation, TelemetryEvent};
use crate::session_management::SessionSource;
use crate::settings::CtrlTabBehavior;
use crate::terminal::keys_settings::KeysSettings;
use crate::themes::theme::WarpTheme;
use crate::view_components::DismissibleToast;
use crate::workspace::{active_terminal_in_window, ForkedConversationDestination, WorkspaceAction};
use crate::{send_telemetry_from_ctx, ToastStack};
lazy_static! {
/// Set of hardcoded action names that we want to show in the command palette zero state.
@@ -179,7 +172,7 @@ impl galaxyui::View for View {
let appearance = Appearance::as_ref(app);
let theme = appearance.theme();
let body = if self.search_bar_state.as_ref(app).should_show_zero_state() {
let body = if self.search_bar.as_ref(app).should_show_zero_state(app) {
ChildView::new(&self.zero_state_handle).finish()
} else {
self.render_palette_list(theme, app)
@@ -337,22 +330,10 @@ impl View {
.map(|item| &item.search_result)
}
pub fn set_fixed_query_filters(
&mut self,
title: String,
filters: Vec<QueryFilter>,
ctx: &mut ViewContext<Self>,
) {
self.search_bar.update(ctx, |search_bar, ctx| {
search_bar.set_fixed_filters(title, filters, ctx);
});
ctx.notify();
}
/// Set the active query filter in the search bar to be `filter`.
pub fn set_active_query_filter(&mut self, filter: QueryFilter, ctx: &mut ViewContext<Self>) {
self.search_bar.update(ctx, |view, ctx| {
view.set_visible_query_filter(Some((filter, filter.filter_atom().primary_text)), ctx)
view.set_query_filter(Some((filter, filter.filter_atom().primary_text)), ctx)
});
ctx.notify();
}
@@ -364,15 +345,15 @@ impl View {
}
pub fn select_next_item(&mut self, ctx: &mut ViewContext<Self>) {
self.search_bar_state.update(ctx, |state, ctx| {
state.handle_selection_update(SelectionUpdate::Down, ctx);
self.search_bar.update(ctx, |search_bar, ctx| {
search_bar.handle_selection_update(SelectionUpdate::Down, ctx);
});
ctx.notify();
}
pub fn select_prev_item(&mut self, ctx: &mut ViewContext<Self>) {
self.search_bar_state.update(ctx, |state, ctx| {
state.handle_selection_update(SelectionUpdate::Up, ctx);
self.search_bar.update(ctx, |search_bar, ctx| {
search_bar.handle_selection_update(SelectionUpdate::Up, ctx);
});
ctx.notify();
}
@@ -385,9 +366,7 @@ impl View {
/// Returns the active query filters
pub fn active_query_filter(&self, app: &AppContext) -> Option<QueryFilter> {
self.search_bar_state
.as_ref(app)
.active_visible_query_filter()
self.search_bar_state.as_ref(app).active_query_filter()
}
pub fn is_mode_enabled(&self, mode: PaletteMode, app: &AppContext) -> bool {
@@ -739,10 +718,17 @@ impl View {
result_action: CommandPaletteItemAction,
ctx: &mut ViewContext<Self>,
) {
let selected_items_handle = SelectedItems::handle(ctx);
selected_items_handle.update(ctx, |selected_items, _ctx| {
selected_items.enqueue(result_action.to_summary())
});
// Tab navigations don't appear in the main command palette to avoid confusion with session
// navigations, so they can't evict real recent items from SelectedItems.
if !matches!(
result_action,
CommandPaletteItemAction::NavigateToTab { .. }
) {
let selected_items_handle = SelectedItems::handle(ctx);
selected_items_handle.update(ctx, |selected_items, _ctx| {
selected_items.enqueue(result_action.to_summary())
});
}
if let CommandPaletteItemAction::AcceptBinding { binding } = &result_action {
if let Some(action) = &binding.action {
@@ -812,6 +798,20 @@ impl View {
send_telemetry_from_ctx!(TelemetryEvent::SelectNavigationPaletteItem, ctx);
}
CommandPaletteItemAction::NavigateToTab {
pane_group_id,
window_id,
} => {
if let Some(root_view_id) = ctx.root_view_id(window_id) {
ctx.dispatch_action_for_view(
window_id,
root_view_id,
"root_view:activate_tab_by_pane_group_id",
&pane_group_id,
);
}
send_telemetry_from_ctx!(TelemetryEvent::SelectNavigationPaletteItem, ctx);
}
CommandPaletteItemAction::NavigateToConversation {
pane_view_locator,
window_id,
@@ -863,6 +863,7 @@ impl View {
summarize_after_fork: false,
summarization_prompt: None,
initial_prompt: None,
initial_attachments: vec![],
destination: ForkedConversationDestination::SplitPane,
});
}
@@ -1,3 +1,7 @@
use std::collections::HashMap;
use warpui::{AppContext, Entity, ModelContext, ModelHandle, SingletonEntity};
use super::env_var_collection_search_item::EnvVarCollectionSearchItem;
use super::notebook_search_item::NotebookSearchItem;
use super::workflow_search_item::WorkflowSearchItem;
@@ -18,8 +22,6 @@ use crate::search::QueryFilter;
use crate::server::ids::{ObjectUid, SyncId};
use crate::settings::AISettings;
use crate::workflows::CloudWorkflow;
use galaxyui::{AppContext, Entity, ModelContext, SingletonEntity};
use std::collections::HashMap;
/// Datasource that searches against all Warp Drive objects
pub struct DataSource {
@@ -64,6 +66,7 @@ impl DataSource {
fn handle_cloud_object_updated(
&mut self,
_: ModelHandle<CloudModel>,
event: &CloudModelEvent,
ctx: &mut ModelContext<Self>,
) {
@@ -162,9 +165,8 @@ impl crate::search::mixer::SyncDataSource for DataSource {
self.searcher
.search_notebook(&query.text.to_lowercase(), app)
.map_err(|err| {
Box::new(DataSourceSearchError {
message: err.to_string(),
}) as DataSourceRunErrorWrapper
Box::new(DataSourceSearchError::new(err.to_string()))
as DataSourceRunErrorWrapper
})?
.into_iter()
.map(QueryResult::from),
@@ -176,9 +178,8 @@ impl crate::search::mixer::SyncDataSource for DataSource {
self.searcher
.search_plans(&query.text.to_lowercase(), app)
.map_err(|err| {
Box::new(DataSourceSearchError {
message: err.to_string(),
}) as DataSourceRunErrorWrapper
Box::new(DataSourceSearchError::new(err.to_string()))
as DataSourceRunErrorWrapper
})?
.into_iter()
.map(QueryResult::from),
@@ -201,9 +202,8 @@ impl crate::search::mixer::SyncDataSource for DataSource {
app,
)
.map_err(|err| {
Box::new(DataSourceSearchError {
message: err.to_string(),
}) as DataSourceRunErrorWrapper
Box::new(DataSourceSearchError::new(err.to_string()))
as DataSourceRunErrorWrapper
})?
.into_iter()
.map(QueryResult::from),
@@ -217,9 +217,8 @@ impl crate::search::mixer::SyncDataSource for DataSource {
self.searcher
.search_env_var(&query.text.to_lowercase(), app)
.map_err(|err| {
Box::new(DataSourceSearchError {
message: err.to_string(),
}) as DataSourceRunErrorWrapper
Box::new(DataSourceSearchError::new(err.to_string()))
as DataSourceRunErrorWrapper
})?
.into_iter()
.map(QueryResult::from),
@@ -541,31 +540,25 @@ impl WarpDriveSearcher for FuzzyWarpDriveSearcher {
mod full_text_searcher {
use std::sync::Arc;
use fuzzy_match::FuzzyMatchResult;
use itertools::Itertools;
use warp_search_core::define_search_schema;
use warpui::r#async::executor::Background;
use warpui::{AppContext, SingletonEntity};
use crate::cloud_object::model::persistence::CloudModel;
use crate::cloud_object::{
CloudObject, CloudObjectLocation, GenericStringObjectFormat, JsonObjectType, ObjectType,
};
use crate::define_search_schema;
use crate::drive::folders::CloudFolder;
use crate::env_vars::CloudEnvVarCollection;
use crate::notebooks::manager::NotebookManager;
use crate::notebooks::CloudNotebook;
use crate::search::command_palette::warp_drive::data_source::WarpDriveSearcher;
use crate::search::command_palette::warp_drive::env_var_collection_search_item::{
EnvVarCollectionSearchItem, ENV_VAR_NAME_SEPARATOR,
};
use crate::search::command_palette::warp_drive::notebook_search_item::NotebookSearchItem;
use crate::search::command_palette::warp_drive::workflow_search_item::WorkflowSearchItem;
use crate::search::env_var_collections::fuzzy_match::FuzzyMatchEnvVarCollectionResult;
use crate::search::notebooks::fuzzy_match::FuzzyMatchNotebookResult;
use crate::search::searcher::{AsyncSearcher, DEFAULT_MEMORY_BUDGET, SCORE_CONVERSION_FACTOR};
use crate::search::workflows::fuzzy_match::FuzzyMatchWorkflowResult;
use crate::server::ids::ObjectUid;
use crate::workflows::CloudWorkflow;
use fuzzy_match::FuzzyMatchResult;
use galaxyui::r#async::executor::Background;
use galaxyui::{AppContext, SingletonEntity};
use itertools::Itertools;
/// Memory budget for the search index of warp drive.
/// Warp could potentially have a lot of objects, so we increase it from the default of 50MB to 100MB
@@ -1,3 +1,9 @@
use itertools::Itertools;
use ordered_float::OrderedFloat;
use warpui::elements::{Container, Flex, Highlight, ParentElement, Text};
use warpui::fonts::{Properties, Weight};
use warpui::{AppContext, Element, SingletonEntity};
use crate::appearance::Appearance;
use crate::cloud_object::CloudObject;
use crate::drive::cloud_object_styling::warp_drive_icon_color;
@@ -10,11 +16,6 @@ use crate::search::env_var_collections::fuzzy_match::FuzzyMatchEnvVarCollectionR
use crate::search::item::{IconLocation, SearchItem};
use crate::search::result_renderer::ItemHighlightState;
use crate::ui_components::icons::Icon;
use galaxyui::elements::{Container, Flex, Highlight, ParentElement, Text};
use galaxyui::fonts::{Properties, Weight};
use galaxyui::{AppContext, Element, SingletonEntity};
use itertools::Itertools;
use ordered_float::OrderedFloat;
pub const ENV_VAR_NAME_SEPARATOR: &str = ", ";
@@ -1,3 +1,8 @@
use ordered_float::OrderedFloat;
use warpui::elements::{Container, Flex, Highlight, ParentElement, Text};
use warpui::fonts::{Properties, Weight};
use warpui::{AppContext, Element, SingletonEntity};
use crate::appearance::Appearance;
use crate::cloud_object::CloudObject;
use crate::drive::cloud_object_styling::warp_drive_icon_color;
@@ -12,10 +17,6 @@ use crate::search::notebooks::fuzzy_match::{
};
use crate::search::result_renderer::ItemHighlightState;
use crate::ui_components::icons::Icon;
use galaxyui::elements::{Container, Flex, Highlight, ParentElement, Text};
use galaxyui::fonts::{Properties, Weight};
use galaxyui::{AppContext, Element, SingletonEntity};
use ordered_float::OrderedFloat;
/// Search item result for a cloud notebook.
#[derive(Debug)]
@@ -1,3 +1,8 @@
use ordered_float::OrderedFloat;
use warpui::elements::{Clipped, Container, Flex, Highlight, ParentElement, Shrinkable, Text};
use warpui::fonts::{Properties, Weight};
use warpui::{AppContext, Element, SingletonEntity};
use crate::appearance::Appearance;
use crate::cloud_object::CloudObject;
use crate::drive::cloud_object_styling::warp_drive_icon_color;
@@ -10,10 +15,6 @@ use crate::search::result_renderer::ItemHighlightState;
use crate::search::workflows::fuzzy_match::FuzzyMatchWorkflowResult;
use crate::ui_components::icons::Icon;
use crate::workflows::CloudWorkflow;
use galaxyui::elements::{Clipped, Container, Flex, Highlight, ParentElement, Shrinkable, Text};
use galaxyui::fonts::{Properties, Weight};
use galaxyui::{AppContext, Element, SingletonEntity};
use ordered_float::OrderedFloat;
/// Search item result for a cloud workflow.
#[derive(Debug)]
+10 -10
View File
@@ -1,21 +1,21 @@
mod items;
use std::collections::HashMap;
pub use items::Items;
use galaxy_core::context_flag::ContextFlag;
use galaxy_core::features::FeatureFlag;
pub use items::Items;
use crate::appearance::Appearance;
use crate::search::command_palette::FilterChipRenderer;
use crate::drive::settings::WarpDriveSettings;
use crate::search::QueryFilter;
use crate::settings::AISettings;
use crate::workspace::Workspace;
use galaxyui::elements::{Container, Flex, MouseStateHandle, ParentElement, Shrinkable, Wrap};
use galaxyui::{
AppContext, Element, Entity, ModelHandle, SingletonEntity, TypedActionView, View, ViewContext,
WindowId,
};
use std::collections::HashMap;
use crate::appearance::Appearance;
use crate::drive::settings::WarpDriveSettings;
use crate::search::command_palette::FilterChipRenderer;
use crate::search::QueryFilter;
use crate::settings::AISettings;
use crate::workspace::Workspace;
/// A zero-state view for the command palette.
pub struct ZeroState {
@@ -1,13 +1,13 @@
use crate::appearance::Appearance;
use crate::search::command_palette::mixer::CommandPaletteItemAction;
use crate::search::result_renderer::QueryResultRenderer;
use crate::search::search_bar::SelectionUpdate;
use galaxyui::elements::{Container, Flex, ParentElement};
use galaxyui::ui_components::components::{UiComponent, UiComponentStyles};
use galaxyui::ui_components::text::WrappableText;
use galaxyui::{AppContext, Element, Entity, ModelContext, SingletonEntity};
use crate::appearance::Appearance;
use crate::search::command_palette::mixer::CommandPaletteItemAction;
use crate::search::result_renderer::QueryResultRenderer;
use crate::search::search_bar::SelectionUpdate;
/// List of items shown within the zero state. "Recent" items are shown first followed by
/// "Suggested" items.
pub struct Items {