first pass of merging in warp (doesn't build)
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use itertools::Itertools;
|
||||
use warpui::{AppContext, Entity, SingletonEntity};
|
||||
|
||||
use super::search_item::BlockSearchItem;
|
||||
use crate::search::ai_context_menu::mixer::AIContextMenuSearchableAction;
|
||||
use crate::search::data_source::{Query, QueryResult};
|
||||
@@ -6,9 +10,6 @@ use crate::search::mixer::{DataSourceRunErrorWrapper, SyncDataSource};
|
||||
use crate::terminal::model::block::Block;
|
||||
use crate::terminal::TerminalView;
|
||||
use crate::workspace::ActiveSession;
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use galaxyui::{AppContext, Entity, SingletonEntity};
|
||||
use itertools::Itertools;
|
||||
|
||||
const MAX_RESULTS: usize = 20;
|
||||
const ZERO_STATE_BASE_SCORE: i64 = 1000;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use chrono::{Duration, Local};
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use galaxy_core::command::ExitCode;
|
||||
use warpui::{App, SingletonEntity};
|
||||
|
||||
use crate::search::ai_context_menu::blocks::data_source::BlockDataSource;
|
||||
use crate::search::ai_context_menu::blocks::search_item::BlockSearchItem;
|
||||
@@ -11,10 +14,6 @@ use crate::test_util::terminal::{
|
||||
};
|
||||
use crate::workspace::ActiveSession;
|
||||
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use galaxy_core::command::ExitCode;
|
||||
use galaxyui::{App, SingletonEntity};
|
||||
|
||||
/// Helper to create a `BlockSearchItem` with the given parameters.
|
||||
fn make_block_search_item(
|
||||
command: &str,
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
use chrono::{DateTime, Local};
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use ordered_float::OrderedFloat;
|
||||
use galaxy_core::command::ExitCode;
|
||||
use warpui::elements::{
|
||||
ConstrainedBox, Container, CrossAxisAlignment, Flex, Highlight, Icon, ParentElement, Text,
|
||||
};
|
||||
use warpui::fonts::{Properties, Weight};
|
||||
use warpui::{AppContext, Element, SingletonEntity};
|
||||
|
||||
use crate::appearance::Appearance;
|
||||
use crate::search::ai_context_menu::mixer::AIContextMenuSearchableAction;
|
||||
use crate::search::ai_context_menu::styles;
|
||||
@@ -5,17 +15,6 @@ use crate::search::item::SearchItem;
|
||||
use crate::search::result_renderer::ItemHighlightState;
|
||||
use crate::terminal::model::block::BlockId;
|
||||
use crate::util::truncation::truncate_from_end;
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use galaxyui::elements::Highlight;
|
||||
use galaxyui::fonts::{Properties, Weight};
|
||||
use galaxyui::{
|
||||
elements::{ConstrainedBox, Container, CrossAxisAlignment, Flex, Icon, ParentElement, Text},
|
||||
AppContext, Element, SingletonEntity,
|
||||
};
|
||||
use ordered_float::OrderedFloat;
|
||||
|
||||
use chrono::{DateTime, Local};
|
||||
use galaxy_core::command::ExitCode;
|
||||
|
||||
/// Calculate how long ago a timestamp was
|
||||
fn time_ago_string(timestamp: Option<&DateTime<Local>>) -> String {
|
||||
|
||||
@@ -1,7 +1,33 @@
|
||||
#![cfg_attr(not(feature = "local_fs"), allow(dead_code))]
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use std::time::Duration;
|
||||
|
||||
use ai::index::Symbol;
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use instant::Instant;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use itertools::Itertools;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use repo_metadata::repositories::DetectedRepositories;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use warp_util::local_or_remote_path::LocalOrRemotePath;
|
||||
use warpui::AppContext;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use warpui::ModelSpawner;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use warpui::SingletonEntity;
|
||||
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use super::search_item::CodeSearchItem;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use crate::ai::outline::{OutlineStatus, RepoOutlines, RepoOutlinesEvent};
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use crate::search::ai_context_menu::mixer::AIContextMenuSearchableAction;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use crate::search::data_source::{Query, QueryResult};
|
||||
@@ -11,32 +37,8 @@ use crate::search::files::model::FileSearchModel;
|
||||
use crate::search::mixer::{
|
||||
AsyncDataSource, BoxFuture, DataSourceRunError, DataSourceRunErrorWrapper,
|
||||
};
|
||||
use ai::index::Symbol;
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use galaxyui::AppContext;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use galaxyui::ModelSpawner;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use instant::Instant;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use itertools::Itertools;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use std::time::Duration;
|
||||
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use crate::ai::outline::{OutlineStatus, RepoOutlines, RepoOutlinesEvent};
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use crate::workspace::ActiveSession;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use galaxyui::SingletonEntity;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use repo_metadata::repositories::DetectedRepositories;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use std::path::Path;
|
||||
|
||||
const MAX_RESULTS: usize = 200;
|
||||
|
||||
@@ -84,12 +86,15 @@ impl CodeSymbolCache {
|
||||
spawner,
|
||||
};
|
||||
|
||||
ctx.subscribe_to_model(&RepoOutlines::handle(ctx), |me, event, ctx| match event {
|
||||
RepoOutlinesEvent::OutlinesUpdated(repo_path) => {
|
||||
me.symbol_cache.get_mut().remove(repo_path);
|
||||
ctx.emit(());
|
||||
}
|
||||
});
|
||||
ctx.subscribe_to_model(
|
||||
&RepoOutlines::handle(ctx),
|
||||
|me, _, event, ctx| match event {
|
||||
RepoOutlinesEvent::OutlinesUpdated(repo_path) => {
|
||||
me.symbol_cache.get_mut().remove(repo_path);
|
||||
ctx.emit(());
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
cache
|
||||
}
|
||||
@@ -118,7 +123,11 @@ impl CodeSymbolCache {
|
||||
.active_window
|
||||
.and_then(|window_id| ActiveSession::as_ref(app).path_if_local(window_id))
|
||||
.and_then(|current_dir| {
|
||||
DetectedRepositories::as_ref(app).get_root_for_path(Path::new(current_dir))
|
||||
DetectedRepositories::as_ref(app)
|
||||
.get_root_for_path(&LocalOrRemotePath::Local(
|
||||
Path::new(current_dir).to_path_buf(),
|
||||
))
|
||||
.and_then(|r| PathBuf::try_from(r).ok())
|
||||
})?;
|
||||
|
||||
let (outline_status, _) = RepoOutlines::as_ref(app).get_outline(&git_repo_path)?;
|
||||
@@ -198,7 +207,11 @@ impl CodeSymbolCache {
|
||||
.active_window
|
||||
.and_then(|window_id| ActiveSession::as_ref(app).path_if_local(window_id))
|
||||
.and_then(|current_dir| {
|
||||
DetectedRepositories::as_ref(app).get_root_for_path(Path::new(current_dir))
|
||||
DetectedRepositories::as_ref(app)
|
||||
.get_root_for_path(&LocalOrRemotePath::Local(
|
||||
Path::new(current_dir).to_path_buf(),
|
||||
))
|
||||
.and_then(|r| PathBuf::try_from(r).ok())
|
||||
})
|
||||
else {
|
||||
return HashSet::new();
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#[cfg(test)]
|
||||
use super::*;
|
||||
use ai::index::Symbol;
|
||||
use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use ai::index::Symbol;
|
||||
|
||||
#[cfg(test)]
|
||||
use super::*;
|
||||
|
||||
fn create_test_symbol(name: &str, type_prefix: Option<&str>) -> CodeSymbol {
|
||||
CodeSymbol {
|
||||
file_path: PathBuf::from("test.rs"),
|
||||
|
||||
@@ -3,14 +3,16 @@ pub mod data_source;
|
||||
pub mod search_item;
|
||||
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use crate::ai::outline::{OutlineStatus, RepoOutlines};
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use crate::workspace::ActiveSession;
|
||||
use std::path::Path;
|
||||
|
||||
use galaxyui::AppContext;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use galaxyui::SingletonEntity;
|
||||
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use std::path::Path;
|
||||
use crate::ai::outline::{OutlineStatus, RepoOutlines};
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use crate::workspace::ActiveSession;
|
||||
|
||||
/// Checks if the code symbols (outline) are currently being indexed for the active directory.
|
||||
/// Returns true if the outline is in a pending state, false otherwise.
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
use crate::appearance::Appearance;
|
||||
use crate::search::ai_context_menu::styles;
|
||||
use crate::search::ai_context_menu::{mixer::AIContextMenuSearchableAction, safe_truncate};
|
||||
use crate::search::item::{IconLocation, SearchItem};
|
||||
use crate::search::result_renderer::ItemHighlightState;
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use galaxyui::elements::{
|
||||
ConstrainedBox, Container, CrossAxisAlignment, Flex, Highlight, Icon, ParentElement, Text,
|
||||
@@ -13,6 +8,11 @@ use ordered_float::OrderedFloat;
|
||||
|
||||
// Import CodeSymbol from the data_source module
|
||||
use super::data_source::CodeSymbol;
|
||||
use crate::appearance::Appearance;
|
||||
use crate::search::ai_context_menu::mixer::AIContextMenuSearchableAction;
|
||||
use crate::search::ai_context_menu::{safe_truncate, styles};
|
||||
use crate::search::item::{IconLocation, SearchItem};
|
||||
use crate::search::result_renderer::ItemHighlightState;
|
||||
|
||||
const MAX_COMBINED_LENGTH: usize = 55;
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use warpui::{AppContext, SingletonEntity};
|
||||
|
||||
use super::search_item::CommandSearchItem;
|
||||
use crate::search::ai_context_menu::mixer::AIContextMenuSearchableAction;
|
||||
use crate::search::data_source::{Query, QueryResult};
|
||||
use crate::search::mixer::{DataSourceRunErrorWrapper, SyncDataSource};
|
||||
use crate::terminal::History;
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use galaxyui::{AppContext, SingletonEntity};
|
||||
use std::collections::HashSet;
|
||||
|
||||
const MAX_RESULTS: usize = 50;
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use ordered_float::OrderedFloat;
|
||||
use warpui::elements::{ConstrainedBox, Container, Icon, Text};
|
||||
use warpui::{AppContext, Element, SingletonEntity};
|
||||
|
||||
use crate::appearance::Appearance;
|
||||
use crate::search::ai_context_menu::mixer::AIContextMenuSearchableAction;
|
||||
use crate::search::item::SearchItem;
|
||||
use crate::search::result_renderer::ItemHighlightState;
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use galaxyui::{
|
||||
elements::{ConstrainedBox, Container, Icon, Text},
|
||||
AppContext, Element, SingletonEntity,
|
||||
};
|
||||
use ordered_float::OrderedFloat;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CommandSearchItem {
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use warpui::{AppContext, Entity, SingletonEntity};
|
||||
|
||||
use super::search_item::ConversationSearchItem;
|
||||
use super::ConversationContextItem;
|
||||
use crate::ai::agent_conversations_model::AgentConversationsModel;
|
||||
@@ -5,9 +10,6 @@ use crate::ai::conversation_navigation::ConversationNavigationData;
|
||||
use crate::search::ai_context_menu::mixer::AIContextMenuSearchableAction;
|
||||
use crate::search::data_source::{Query, QueryResult};
|
||||
use crate::search::mixer::{DataSourceRunErrorWrapper, SyncDataSource};
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use galaxyui::{AppContext, Entity, SingletonEntity};
|
||||
use std::collections::HashSet;
|
||||
|
||||
const MAX_RESULTS: usize = 50;
|
||||
/// Minimum fuzzy match score to include a conversation in filtered results.
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use ordered_float::OrderedFloat;
|
||||
use warpui::elements::{
|
||||
ConstrainedBox, Container, CrossAxisAlignment, Flex, Highlight, Icon, ParentElement, Text,
|
||||
};
|
||||
use warpui::fonts::{Properties, Weight};
|
||||
use warpui::{AppContext, Element, SingletonEntity};
|
||||
|
||||
use super::ConversationContextItem;
|
||||
use crate::appearance::Appearance;
|
||||
@@ -9,11 +14,6 @@ use crate::search::item::SearchItem;
|
||||
use crate::search::result_renderer::ItemHighlightState;
|
||||
use crate::util::time_format::format_approx_duration_from_now_utc;
|
||||
use crate::util::truncation::truncate_from_end;
|
||||
use galaxyui::elements::{
|
||||
ConstrainedBox, Container, CrossAxisAlignment, Flex, Highlight, Icon, ParentElement, Text,
|
||||
};
|
||||
use galaxyui::fonts::{Properties, Weight};
|
||||
use galaxyui::{AppContext, Element, SingletonEntity};
|
||||
|
||||
const MAX_TITLE_LENGTH: usize = 45;
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use warpui::AppContext;
|
||||
|
||||
use super::search_item::DiffSetSearchItem;
|
||||
use crate::code_review::diff_state::DiffMode;
|
||||
|
||||
use crate::search::ai_context_menu::mixer::AIContextMenuSearchableAction;
|
||||
use crate::search::data_source::{Query, QueryResult};
|
||||
use crate::search::mixer::{DataSourceRunErrorWrapper, SyncDataSource};
|
||||
use galaxyui::AppContext;
|
||||
|
||||
const UNCOMMITTED_CHANGES_NAME: &str = "uncommitted changes";
|
||||
const MAIN_BRANCH_CHANGES_NAME: &str = "changes vs. main branch";
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
use crate::appearance::Appearance;
|
||||
use crate::code_review::diff_state::DiffMode;
|
||||
use crate::search::ai_context_menu::mixer::AIContextMenuSearchableAction;
|
||||
use crate::search::ai_context_menu::styles;
|
||||
use crate::search::item::SearchItem;
|
||||
use crate::search::result_renderer::ItemHighlightState;
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use galaxyui::elements::{
|
||||
ConstrainedBox, Container, CrossAxisAlignment, Flex, Icon, ParentElement, Text,
|
||||
@@ -11,6 +5,13 @@ use galaxyui::elements::{
|
||||
use galaxyui::{AppContext, Element, SingletonEntity};
|
||||
use ordered_float::OrderedFloat;
|
||||
|
||||
use crate::appearance::Appearance;
|
||||
use crate::code_review::diff_state::DiffMode;
|
||||
use crate::search::ai_context_menu::mixer::AIContextMenuSearchableAction;
|
||||
use crate::search::ai_context_menu::styles;
|
||||
use crate::search::item::SearchItem;
|
||||
use crate::search::result_renderer::ItemHighlightState;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DiffSetSearchItem {
|
||||
pub diff_mode: DiffMode,
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
#![cfg_attr(not(feature = "local_fs"), allow(dead_code))]
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use futures_lite::future::yield_now;
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use itertools::Itertools;
|
||||
#[cfg(feature = "local_fs")]
|
||||
use repo_metadata::repositories::DetectedRepositories;
|
||||
use warpui::{AppContext, SingletonEntity};
|
||||
|
||||
use super::search_item::FileSearchItem;
|
||||
#[cfg(feature = "local_fs")]
|
||||
use crate::code::opened_files::OpenedFilesModel;
|
||||
use crate::search::ai_context_menu::mixer::AIContextMenuSearchableAction;
|
||||
use crate::search::async_snapshot_data_source::AsyncSnapshotDataSource;
|
||||
@@ -7,16 +19,8 @@ use crate::search::data_source::{Query, QueryResult};
|
||||
use crate::search::files::model::FileSearchModel;
|
||||
use crate::search::files::search_item::FileSearchResult;
|
||||
use crate::search::mixer::{BoxFuture, DataSourceRunErrorWrapper};
|
||||
#[cfg(feature = "local_fs")]
|
||||
use crate::workspace::ActiveSession;
|
||||
use futures_lite::future::yield_now;
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use galaxyui::{AppContext, SingletonEntity};
|
||||
use itertools::Itertools;
|
||||
use repo_metadata::repositories::DetectedRepositories;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
|
||||
const MAX_RESULTS: usize = 200;
|
||||
|
||||
@@ -58,7 +62,7 @@ pub fn file_data_source_for_current_repo(
|
||||
last_opened,
|
||||
}
|
||||
} else {
|
||||
let contents = file_search_model.get_repo_contents(app);
|
||||
let contents = file_search_model.get_repo_contents(&query.text, app);
|
||||
FileSnapshot {
|
||||
contents,
|
||||
git_changed_files: HashSet::new(),
|
||||
@@ -104,33 +108,36 @@ pub fn file_data_source_for_pwd(
|
||||
|
||||
/// Captures last-opened timestamps from `OpenedFilesModel` for the active
|
||||
/// repo at snapshot time. Returns an empty map when no repo is active.
|
||||
#[cfg(feature = "local_fs")]
|
||||
fn snapshot_last_opened(app: &AppContext) -> HashMap<String, instant::Instant> {
|
||||
let git_repo_path = app
|
||||
let repo_root = app
|
||||
.windows()
|
||||
.state()
|
||||
.active_window
|
||||
.and_then(|window_id| ActiveSession::as_ref(app).path_if_local(window_id))
|
||||
.and_then(|current_dir| {
|
||||
DetectedRepositories::as_ref(app).get_root_for_path(Path::new(current_dir))
|
||||
});
|
||||
.and_then(|window_id| ActiveSession::as_ref(app).working_directory(window_id))
|
||||
.and_then(|working_dir| DetectedRepositories::as_ref(app).get_root_for_path(working_dir));
|
||||
|
||||
let Some(repo_path) = git_repo_path else {
|
||||
let Some(repo_root) = repo_root else {
|
||||
return HashMap::new();
|
||||
};
|
||||
|
||||
let opened_files_model = OpenedFilesModel::as_ref(app);
|
||||
let Some(opened_in_repo) = opened_files_model.opened_files_for_repo(&repo_path) else {
|
||||
let Some(opened_in_repo) = opened_files_model.opened_files_for_repo(&repo_root) else {
|
||||
return HashMap::new();
|
||||
};
|
||||
|
||||
// Convert PathBuf keys to String keys matching FileSearchResult.path
|
||||
// (relative paths from repo root).
|
||||
opened_in_repo
|
||||
.iter()
|
||||
.map(|(path, ts)| (path.to_string_lossy().to_string(), *ts))
|
||||
.map(|(path, ts)| (path.clone(), *ts))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// File-open recency is unavailable without a local filesystem.
|
||||
#[cfg(not(feature = "local_fs"))]
|
||||
fn snapshot_last_opened(_app: &AppContext) -> HashMap<String, instant::Instant> {
|
||||
HashMap::new()
|
||||
}
|
||||
|
||||
/// Routes file matching to zero-state ranking or query-based fuzzy scoring.
|
||||
pub(crate) fn fuzzy_match_files(
|
||||
snapshot: FileSnapshot,
|
||||
@@ -272,3 +279,7 @@ async fn fuzzy_match_files_query(
|
||||
.k_largest_relaxed_by_key(MAX_RESULTS, |item| item.score())
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "data_source_tests.rs"]
|
||||
mod tests;
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
use crate::search::{
|
||||
ai_context_menu::{
|
||||
files::data_source::{file_data_source_for_pwd, fuzzy_match_files, FileSnapshot},
|
||||
mixer::AIContextMenuSearchableAction,
|
||||
},
|
||||
data_source::Query,
|
||||
files::{model::FileSearchModel, search_item::FileSearchResult},
|
||||
item::SearchItem,
|
||||
mixer::AsyncDataSource,
|
||||
};
|
||||
use crate::{terminal::model::session::Session, workspace::ActiveSession};
|
||||
use galaxyui::platform::WindowStyle;
|
||||
use galaxyui::r#async::block_on;
|
||||
use galaxyui::windowing::WindowManager;
|
||||
use galaxyui::SingletonEntity;
|
||||
use galaxyui::{elements::Empty, App, AppContext, Element, Entity, TypedActionView, View};
|
||||
use repo_metadata::repositories::DetectedRepositories;
|
||||
use repo_metadata::RepoMetadataModel;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use repo_metadata::repositories::DetectedRepositories;
|
||||
use repo_metadata::RepoMetadataModel;
|
||||
use tempfile::tempdir;
|
||||
use galaxyui::elements::Empty;
|
||||
use galaxyui::platform::WindowStyle;
|
||||
use galaxyui::r#async::block_on;
|
||||
use galaxyui::windowing::WindowManager;
|
||||
use galaxyui::{App, AppContext, Element, Entity, SingletonEntity, TypedActionView, View};
|
||||
|
||||
use crate::search::ai_context_menu::files::data_source::{
|
||||
file_data_source_for_pwd, fuzzy_match_files, FileSnapshot,
|
||||
};
|
||||
use crate::search::ai_context_menu::mixer::AIContextMenuSearchableAction;
|
||||
use crate::search::data_source::Query;
|
||||
use crate::search::files::model::FileSearchModel;
|
||||
use crate::search::files::search_item::FileSearchResult;
|
||||
use crate::search::item::SearchItem;
|
||||
use crate::search::mixer::AsyncDataSource;
|
||||
use crate::terminal::model::session::Session;
|
||||
use crate::workspace::ActiveSession;
|
||||
struct TestView;
|
||||
|
||||
impl Entity for TestView {
|
||||
@@ -406,9 +407,10 @@ fn test_path_proximity_ranking() {
|
||||
|
||||
#[test]
|
||||
fn test_directory_search_support() {
|
||||
use crate::search::ai_context_menu::files::search_item::FileSearchItem;
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
|
||||
use crate::search::ai_context_menu::files::search_item::FileSearchItem;
|
||||
|
||||
// Test that directories can be created with is_directory flag
|
||||
let directory_item = FileSearchItem {
|
||||
path: PathBuf::from("src/components/"),
|
||||
@@ -434,10 +436,7 @@ fn test_directory_search_support() {
|
||||
|
||||
#[test]
|
||||
fn test_directory_action_type() {
|
||||
use crate::search::ai_context_menu::files::search_item::FileSearchItem;
|
||||
use crate::search::ai_context_menu::mixer::AIContextMenuSearchableAction;
|
||||
use crate::search::item::SearchItem;
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
|
||||
|
||||
let directory_item = FileSearchItem {
|
||||
path: PathBuf::from("src/components/"),
|
||||
@@ -520,7 +519,7 @@ fn test_mixed_file_directory_search() {
|
||||
("src/components/ui", true),
|
||||
("src/components/ui/modal.rs", false),
|
||||
("tests/components", true),
|
||||
("tests/components/button_test.rs", false),
|
||||
("tests/components/button_tests.rs", false),
|
||||
];
|
||||
|
||||
let query = "components";
|
||||
|
||||
@@ -1,5 +1,2 @@
|
||||
pub mod data_source;
|
||||
pub mod search_item;
|
||||
|
||||
#[cfg(test)]
|
||||
mod data_source_tests;
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use ordered_float::OrderedFloat;
|
||||
use std::fmt::Debug;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use ordered_float::OrderedFloat;
|
||||
use warpui::elements::{ConstrainedBox, Container, Icon};
|
||||
use warpui::{AppContext, Element};
|
||||
|
||||
use crate::appearance::Appearance;
|
||||
use crate::search::ai_context_menu::mixer::AIContextMenuSearchableAction;
|
||||
use crate::search::ai_context_menu::styles;
|
||||
use crate::search::files::icon::icon_from_file_path;
|
||||
use crate::search::item::SearchItem;
|
||||
use crate::search::result_renderer::ItemHighlightState;
|
||||
use galaxyui::elements::{ConstrainedBox, Container, Icon};
|
||||
use galaxyui::{AppContext, Element};
|
||||
|
||||
use crate::search::files::icon::icon_from_file_path;
|
||||
use crate::ui_components::render_file_search_row::{render_file_search_row, FileSearchRowOptions};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use warpui::{AppContext, SingletonEntity};
|
||||
|
||||
use super::search_item::NotebookSearchItem;
|
||||
use crate::cloud_object::model::persistence::CloudModel;
|
||||
use crate::cloud_object::CloudModelType;
|
||||
@@ -6,8 +9,6 @@ use crate::search::ai_context_menu::mixer::AIContextMenuSearchableAction;
|
||||
use crate::search::data_source::{Query, QueryResult};
|
||||
use crate::search::mixer::{DataSourceRunErrorWrapper, SyncDataSource};
|
||||
use crate::workspaces::user_workspaces::UserWorkspaces;
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use galaxyui::{AppContext, SingletonEntity};
|
||||
|
||||
const MAX_RESULTS: usize = 50;
|
||||
/// Base score for zero-state results. Each item gets an additional bonus based on
|
||||
@@ -155,3 +156,7 @@ impl SyncDataSource for NotebookDataSource {
|
||||
impl galaxyui::Entity for NotebookDataSource {
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "data_source_tests.rs"]
|
||||
mod tests;
|
||||
|
||||
@@ -0,0 +1,250 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use chrono::{Duration, Utc};
|
||||
use cloud_object_client::MockObjectClient;
|
||||
use settings::manager::SettingsManager;
|
||||
use warpui::{App, SingletonEntity};
|
||||
|
||||
use crate::auth::AuthStateProvider;
|
||||
use crate::cloud_object::model::persistence::CloudModel;
|
||||
use crate::cloud_object::model::view::CloudViewModel;
|
||||
use crate::cloud_object::{Owner, Revision, ServerMetadata, ServerNotebook, ServerPermissions};
|
||||
use crate::notebooks::manager::NotebookManager;
|
||||
use crate::notebooks::CloudNotebookModel;
|
||||
use crate::search::ai_context_menu::notebooks::data_source::NotebookDataSource;
|
||||
use crate::search::data_source::Query;
|
||||
use crate::search::mixer::SyncDataSource;
|
||||
use crate::server::cloud_objects::update_manager::UpdateManager;
|
||||
use crate::server::ids::{ServerId, SyncId};
|
||||
use crate::server::server_api::team::MockTeamClient;
|
||||
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::workspaces::team_tester::TeamTesterStatus;
|
||||
use crate::workspaces::user_profiles::UserProfiles;
|
||||
use crate::workspaces::user_workspaces::UserWorkspaces;
|
||||
use crate::NetworkStatus;
|
||||
|
||||
fn mock_server_notebook_with_revision(id: i64, title: &str, revision: Revision) -> ServerNotebook {
|
||||
ServerNotebook::new(
|
||||
SyncId::ServerId(id.into()),
|
||||
CloudNotebookModel {
|
||||
title: title.to_string(),
|
||||
data: format!("{title} content"),
|
||||
ai_document_id: None,
|
||||
conversation_id: None,
|
||||
},
|
||||
ServerMetadata {
|
||||
uid: ServerId::default(),
|
||||
revision,
|
||||
metadata_last_updated_ts: Utc::now().into(),
|
||||
trashed_ts: None,
|
||||
folder_id: None,
|
||||
is_welcome_object: false,
|
||||
creator_uid: None,
|
||||
last_editor_uid: None,
|
||||
current_editor_uid: None,
|
||||
},
|
||||
ServerPermissions {
|
||||
space: Owner::mock_current_user(),
|
||||
guests: Vec::new(),
|
||||
anyone_link_sharing: None,
|
||||
permissions_last_updated_ts: Utc::now().into(),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn initialize_app(app: &mut App) {
|
||||
app.add_singleton_model(|_| NetworkStatus::new());
|
||||
app.add_singleton_model(|_| SystemStats::new());
|
||||
let mock_team_client = Arc::new(MockTeamClient::new());
|
||||
let mock_workspace_client = Arc::new(MockWorkspaceClient::new());
|
||||
app.add_singleton_model(|ctx| {
|
||||
UserWorkspaces::mock(
|
||||
mock_team_client.clone(),
|
||||
mock_workspace_client.clone(),
|
||||
vec![],
|
||||
ctx,
|
||||
)
|
||||
});
|
||||
app.add_singleton_model(TeamTesterStatus::new);
|
||||
app.add_singleton_model(SyncQueue::mock);
|
||||
app.add_singleton_model(CloudModel::mock);
|
||||
app.add_singleton_model(|ctx| UpdateManager::new(None, Arc::new(MockObjectClient::new()), ctx));
|
||||
app.add_singleton_model(|_| UserProfiles::new(Vec::new()));
|
||||
app.add_singleton_model(CloudViewModel::new);
|
||||
app.add_singleton_model(NotebookManager::mock);
|
||||
app.add_singleton_model(|_| ServerApiProvider::new_for_test());
|
||||
app.add_singleton_model(|_| SettingsManager::default());
|
||||
app.add_singleton_model(|_| AuthStateProvider::new_for_test());
|
||||
app.update(crate::settings::init_and_register_user_preferences);
|
||||
app.update(AISettings::register_and_subscribe_to_events);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zero_state_scores_reflect_recency() {
|
||||
App::test((), |mut app| async move {
|
||||
initialize_app(&mut app);
|
||||
|
||||
let now = Utc::now();
|
||||
CloudModel::handle(&app).update(&mut app, |model, ctx| {
|
||||
model.upsert_from_server_notebook(
|
||||
mock_server_notebook_with_revision(
|
||||
1,
|
||||
"oldest",
|
||||
(now - Duration::minutes(3)).into(),
|
||||
),
|
||||
ctx,
|
||||
);
|
||||
model.upsert_from_server_notebook(
|
||||
mock_server_notebook_with_revision(
|
||||
2,
|
||||
"middle",
|
||||
(now - Duration::minutes(2)).into(),
|
||||
),
|
||||
ctx,
|
||||
);
|
||||
model.upsert_from_server_notebook(
|
||||
mock_server_notebook_with_revision(
|
||||
3,
|
||||
"newest",
|
||||
(now - Duration::minutes(1)).into(),
|
||||
),
|
||||
ctx,
|
||||
);
|
||||
});
|
||||
|
||||
let data_source = NotebookDataSource::new(false);
|
||||
let results = app.read(|app| data_source.run_query(&Query::from(""), app).unwrap());
|
||||
|
||||
assert_eq!(results.len(), 3);
|
||||
// run_query sorts descending by score, so first result should be newest
|
||||
let scores: Vec<_> = results.iter().map(|r| r.score()).collect();
|
||||
assert!(
|
||||
scores[0] > scores[1] && scores[1] > scores[2],
|
||||
"Expected scores in strictly descending order (newest first), got {scores:?}"
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filtered_state_adds_recency_bonus_to_equal_matches() {
|
||||
App::test((), |mut app| async move {
|
||||
initialize_app(&mut app);
|
||||
|
||||
let now = Utc::now();
|
||||
// All titles contain "plan" so fuzzy scores should be similar
|
||||
CloudModel::handle(&app).update(&mut app, |model, ctx| {
|
||||
model.upsert_from_server_notebook(
|
||||
mock_server_notebook_with_revision(
|
||||
1,
|
||||
"my first plan",
|
||||
(now - Duration::minutes(3)).into(),
|
||||
),
|
||||
ctx,
|
||||
);
|
||||
model.upsert_from_server_notebook(
|
||||
mock_server_notebook_with_revision(
|
||||
2,
|
||||
"my second plan",
|
||||
(now - Duration::minutes(2)).into(),
|
||||
),
|
||||
ctx,
|
||||
);
|
||||
model.upsert_from_server_notebook(
|
||||
mock_server_notebook_with_revision(
|
||||
3,
|
||||
"my third plan",
|
||||
(now - Duration::minutes(1)).into(),
|
||||
),
|
||||
ctx,
|
||||
);
|
||||
});
|
||||
|
||||
let data_source = NotebookDataSource::new(false);
|
||||
let results = app.read(|app| data_source.run_query(&Query::from("plan"), app).unwrap());
|
||||
|
||||
assert_eq!(results.len(), 3);
|
||||
// All match "plan" similarly; recency bonus should make newer items score higher
|
||||
let scores: Vec<_> = results.iter().map(|r| r.score()).collect();
|
||||
assert!(
|
||||
scores[0] > scores[1] && scores[1] > scores[2],
|
||||
"Expected scores in strictly descending order (newest first), got {scores:?}"
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multibyte_character_truncation() {
|
||||
// Test string with multibyte characters (emojis, accented chars)
|
||||
let test_content = "This is a test with emojis 🚀 and accented chars like café and naïve that should be truncated properly without panicking. This string is intentionally long to test the 200 character limit and ensure we don't slice in the middle of multibyte characters like 你好世界";
|
||||
|
||||
let truncated = if test_content.len() > 200 {
|
||||
let result = test_content
|
||||
.char_indices()
|
||||
.take_while(|(i, _)| *i <= 197)
|
||||
.last()
|
||||
.map(|(i, c)| &test_content[..i + c.len_utf8()])
|
||||
.unwrap_or("");
|
||||
format!("{result}...")
|
||||
} else {
|
||||
test_content.to_string()
|
||||
};
|
||||
|
||||
// Should not panic and should produce a valid string
|
||||
assert!(!truncated.is_empty());
|
||||
assert!(truncated.ends_with("..."));
|
||||
// The truncated string should be valid UTF-8
|
||||
assert!(std::str::from_utf8(truncated.as_bytes()).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_truncation_with_boundary_at_multibyte_char() {
|
||||
// Create a string where byte 197 falls exactly in the middle of a multibyte character
|
||||
let mut test_content = "a".repeat(195); // 195 single-byte chars
|
||||
test_content.push('🚀'); // 4-byte emoji at positions 195-198
|
||||
test_content.push_str("more text after emoji");
|
||||
|
||||
// This should not panic even though byte 197 is in the middle of the emoji
|
||||
let truncated = if test_content.len() > 200 {
|
||||
let result = test_content
|
||||
.char_indices()
|
||||
.take_while(|(i, _)| *i <= 197)
|
||||
.last()
|
||||
.map(|(i, c)| &test_content[..i + c.len_utf8()])
|
||||
.unwrap_or("");
|
||||
format!("{result}...")
|
||||
} else {
|
||||
test_content.to_string()
|
||||
};
|
||||
|
||||
// Should not panic and should produce a valid string
|
||||
assert!(!truncated.is_empty());
|
||||
// The truncated string should be valid UTF-8
|
||||
assert!(std::str::from_utf8(truncated.as_bytes()).is_ok());
|
||||
// Should either include the full emoji or stop before it
|
||||
assert!(!truncated.contains("🚀") || truncated.contains("🚀..."));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_short_content_not_truncated() {
|
||||
let short_content = "This is a short string with emoji 🚀";
|
||||
|
||||
let result = if short_content.len() > 200 {
|
||||
let truncated = short_content
|
||||
.char_indices()
|
||||
.take_while(|(i, _)| *i <= 197)
|
||||
.last()
|
||||
.map(|(i, c)| &short_content[..i + c.len_utf8()])
|
||||
.unwrap_or("");
|
||||
format!("{truncated}...")
|
||||
} else {
|
||||
short_content.to_string()
|
||||
};
|
||||
|
||||
// Short content should not be truncated
|
||||
assert_eq!(result, short_content);
|
||||
assert!(!result.ends_with("..."));
|
||||
}
|
||||
@@ -1,5 +1,2 @@
|
||||
pub mod data_source;
|
||||
pub mod search_item;
|
||||
|
||||
#[cfg(test)]
|
||||
mod data_source_test;
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use ordered_float::OrderedFloat;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::appearance::Appearance;
|
||||
use crate::cloud_object::ObjectType;
|
||||
use crate::search::ai_context_menu::styles;
|
||||
use crate::search::ai_context_menu::{mixer::AIContextMenuSearchableAction, safe_truncate};
|
||||
use crate::search::item::SearchItem;
|
||||
use crate::search::result_renderer::ItemHighlightState;
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use ordered_float::OrderedFloat;
|
||||
use galaxyui::elements::{
|
||||
ConstrainedBox, Container, CrossAxisAlignment, Flex, Highlight, Icon, ParentElement, Text,
|
||||
};
|
||||
use galaxyui::fonts::{Properties, Weight};
|
||||
use galaxyui::{AppContext, Element, SingletonEntity};
|
||||
|
||||
use crate::appearance::Appearance;
|
||||
use crate::cloud_object::ObjectType;
|
||||
use crate::search::ai_context_menu::mixer::AIContextMenuSearchableAction;
|
||||
use crate::search::ai_context_menu::{safe_truncate, styles};
|
||||
use crate::search::item::SearchItem;
|
||||
use crate::search::result_renderer::ItemHighlightState;
|
||||
|
||||
const MAX_COMBINED_LENGTH: usize = 55;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use warpui::{AppContext, Entity, SingletonEntity};
|
||||
|
||||
use super::search_item::RuleSearchItem;
|
||||
use crate::ai::facts::{AIFact, CloudAIFactModel};
|
||||
use crate::cloud_object::model::generic_string_model::GenericStringObjectId;
|
||||
@@ -6,8 +9,6 @@ use crate::cloud_object::CloudObject;
|
||||
use crate::search::ai_context_menu::mixer::AIContextMenuSearchableAction;
|
||||
use crate::search::data_source::{Query, QueryResult};
|
||||
use crate::search::mixer::{DataSourceRunErrorWrapper, SyncDataSource};
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use galaxyui::{AppContext, Entity, SingletonEntity};
|
||||
|
||||
const MAX_RESULTS: usize = 50;
|
||||
const ZERO_STATE_BASE_SCORE: i64 = 1000;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use chrono::{Duration, Utc};
|
||||
use galaxyui::{App, SingletonEntity};
|
||||
use cloud_object_client::MockObjectClient;
|
||||
use settings::manager::SettingsManager;
|
||||
|
||||
use crate::ai::facts::{AIFact, AIMemory, CloudAIFactModel};
|
||||
@@ -18,6 +18,8 @@ use crate::search::data_source::Query;
|
||||
use crate::search::mixer::SyncDataSource;
|
||||
use crate::server::cloud_objects::update_manager::UpdateManager;
|
||||
use crate::server::ids::{ServerId, SyncId};
|
||||
use crate::server::server_api::team::MockTeamClient;
|
||||
use crate::server::server_api::workspace::MockWorkspaceClient;
|
||||
use crate::server::server_api::ServerApiProvider;
|
||||
use crate::server::sync_queue::SyncQueue;
|
||||
use crate::settings::AISettings;
|
||||
@@ -27,19 +29,21 @@ use crate::workspaces::user_profiles::UserProfiles;
|
||||
use crate::workspaces::user_workspaces::UserWorkspaces;
|
||||
use crate::NetworkStatus;
|
||||
|
||||
use crate::server::server_api::object::MockObjectClient;
|
||||
use crate::server::server_api::team::MockTeamClient;
|
||||
use crate::server::server_api::workspace::MockWorkspaceClient;
|
||||
|
||||
type ServerAIFact = GenericServerObject<
|
||||
crate::cloud_object::model::generic_string_model::GenericStringObjectId,
|
||||
CloudAIFactModel,
|
||||
>;
|
||||
|
||||
fn mock_server_ai_fact(id: i64, name: &str, content: &str, revision: Revision) -> ServerAIFact {
|
||||
GenericServerObject {
|
||||
id: SyncId::ServerId(id.into()),
|
||||
metadata: ServerMetadata {
|
||||
GenericServerObject::new(
|
||||
SyncId::ServerId(id.into()),
|
||||
GenericStringModel::new(AIFact::Memory(AIMemory {
|
||||
name: Some(name.to_string()),
|
||||
content: content.to_string(),
|
||||
is_autogenerated: false,
|
||||
suggested_logging_id: None,
|
||||
})),
|
||||
ServerMetadata {
|
||||
uid: ServerId::default(),
|
||||
revision,
|
||||
metadata_last_updated_ts: Utc::now().into(),
|
||||
@@ -50,21 +54,13 @@ fn mock_server_ai_fact(id: i64, name: &str, content: &str, revision: Revision) -
|
||||
last_editor_uid: None,
|
||||
current_editor_uid: None,
|
||||
},
|
||||
permissions: ServerPermissions {
|
||||
ServerPermissions {
|
||||
space: Owner::mock_current_user(),
|
||||
guests: Vec::new(),
|
||||
anyone_link_sharing: None,
|
||||
permissions_last_updated_ts: Utc::now().into(),
|
||||
},
|
||||
model: GenericStringModel {
|
||||
string_model: AIFact::Memory(AIMemory {
|
||||
name: Some(name.to_string()),
|
||||
content: content.to_string(),
|
||||
is_autogenerated: false,
|
||||
suggested_logging_id: None,
|
||||
}),
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn initialize_app(app: &mut App) {
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use ordered_float::OrderedFloat;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::appearance::Appearance;
|
||||
use crate::cloud_object::{GenericStringObjectFormat, JsonObjectType, ObjectType};
|
||||
use crate::search::ai_context_menu::styles;
|
||||
use crate::search::ai_context_menu::{mixer::AIContextMenuSearchableAction, safe_truncate};
|
||||
use crate::search::item::SearchItem;
|
||||
use crate::search::result_renderer::ItemHighlightState;
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use ordered_float::OrderedFloat;
|
||||
use galaxyui::elements::{
|
||||
ConstrainedBox, Container, CrossAxisAlignment, Flex, Highlight, Icon, ParentElement, Text,
|
||||
};
|
||||
use galaxyui::fonts::{Properties, Weight};
|
||||
use galaxyui::{AppContext, Element, SingletonEntity};
|
||||
|
||||
use crate::appearance::Appearance;
|
||||
use crate::cloud_object::{GenericStringObjectFormat, JsonObjectType, ObjectType};
|
||||
use crate::search::ai_context_menu::mixer::AIContextMenuSearchableAction;
|
||||
use crate::search::ai_context_menu::{safe_truncate, styles};
|
||||
use crate::search::item::SearchItem;
|
||||
use crate::search::result_renderer::ItemHighlightState;
|
||||
|
||||
const MAX_COMBINED_LENGTH: usize = 55;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use warp_util::local_or_remote_path::LocalOrRemotePath;
|
||||
use warpui::{AppContext, Entity, SingletonEntity};
|
||||
|
||||
use super::search_item::SkillSearchItem;
|
||||
use crate::ai::skills::SkillManager;
|
||||
use crate::search::ai_context_menu::mixer::AIContextMenuSearchableAction;
|
||||
use crate::search::data_source::{Query, QueryResult};
|
||||
use crate::search::mixer::{DataSourceRunErrorWrapper, SyncDataSource};
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use galaxyui::{AppContext, Entity, SingletonEntity};
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use crate::workspace::ActiveSession;
|
||||
|
||||
@@ -31,23 +31,19 @@ impl SyncDataSource for SkillsDataSource {
|
||||
let query_text = &query.text;
|
||||
|
||||
// Resolve the current working directory from the active window's session.
|
||||
let cwd: Option<PathBuf> = {
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
{
|
||||
app.windows()
|
||||
.state()
|
||||
.active_window
|
||||
.and_then(|window_id| ActiveSession::as_ref(app).path_if_local(window_id))
|
||||
.map(PathBuf::from)
|
||||
}
|
||||
#[cfg(target_family = "wasm")]
|
||||
{
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let skills =
|
||||
SkillManager::as_ref(app).get_skills_for_working_directory(cwd.as_deref(), app);
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
let cwd: Option<LocalOrRemotePath> = app
|
||||
.windows()
|
||||
.state()
|
||||
.active_window
|
||||
.map(|window_id| {
|
||||
let active_session = ActiveSession::as_ref(app);
|
||||
active_session.working_directory(window_id).cloned()
|
||||
})
|
||||
.unwrap_or(None);
|
||||
#[cfg(target_family = "wasm")]
|
||||
let cwd: Option<LocalOrRemotePath> = None;
|
||||
let skills = SkillManager::as_ref(app).get_skills_for_working_directory(cwd.as_ref(), app);
|
||||
|
||||
let mut results: Vec<QueryResult<Self::Action>> = if query_text.is_empty() {
|
||||
// Zero state: show all skills with a uniform high score.
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
use ai::skills::SkillProvider;
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use ordered_float::OrderedFloat;
|
||||
|
||||
use crate::appearance::Appearance;
|
||||
use crate::search::ai_context_menu::mixer::AIContextMenuSearchableAction;
|
||||
use crate::search::ai_context_menu::styles;
|
||||
use crate::search::item::SearchItem;
|
||||
use crate::search::result_renderer::ItemHighlightState;
|
||||
use galaxy_core::ui::icons::Icon;
|
||||
use galaxyui::elements::{
|
||||
ConstrainedBox, Container, CrossAxisAlignment, Flex, Highlight, ParentElement, Shrinkable, Text,
|
||||
@@ -14,6 +8,12 @@ use galaxyui::elements::{
|
||||
use galaxyui::fonts::{Properties, Weight};
|
||||
use galaxyui::{AppContext, Element, SingletonEntity};
|
||||
|
||||
use crate::appearance::Appearance;
|
||||
use crate::search::ai_context_menu::mixer::AIContextMenuSearchableAction;
|
||||
use crate::search::ai_context_menu::styles;
|
||||
use crate::search::item::SearchItem;
|
||||
use crate::search::result_renderer::ItemHighlightState;
|
||||
|
||||
const MAX_DESCRIPTION_LEN: usize = 60;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -1,5 +1,30 @@
|
||||
use std::collections::HashSet;
|
||||
use std::ops::Range;
|
||||
use std::time::Duration;
|
||||
|
||||
use async_channel::Sender;
|
||||
use itertools::Itertools;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use repo_metadata::repositories::DetectedRepositories;
|
||||
use settings::Setting as _;
|
||||
use galaxy_core::features::FeatureFlag;
|
||||
use warpui::elements::{
|
||||
AnchorPair, Border, ChildView, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment,
|
||||
Dismiss, Empty, Fill, Flex, Hoverable, Icon, MouseStateHandle, OffsetPositioning, OffsetType,
|
||||
ParentElement, PositionedElementOffsetBounds, PositioningAxis, Radius, SavePosition,
|
||||
ScrollStateHandle, Scrollable, ScrollableElement, ScrollbarWidth, Shrinkable, Stack, Text,
|
||||
UniformList, UniformListState, XAxisAnchor, YAxisAnchor,
|
||||
};
|
||||
use warpui::platform::Cursor;
|
||||
use warpui::windowing::WindowManager;
|
||||
use warpui::{
|
||||
AppContext, Element, Entity, ModelHandle, SingletonEntity, TypedActionView, View, ViewContext,
|
||||
ViewHandle, WeakViewHandle,
|
||||
};
|
||||
|
||||
use super::styles;
|
||||
use crate::appearance::Appearance;
|
||||
use crate::debounce::debounce;
|
||||
use crate::debounce;
|
||||
use crate::drive::settings::WarpDriveSettings;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use crate::search::ai_context_menu::blocks::data_source::BlockDataSource;
|
||||
@@ -16,8 +41,7 @@ use crate::search::ai_context_menu::diffset::data_source::DiffSetDataSource;
|
||||
use crate::search::ai_context_menu::files::data_source::{
|
||||
file_data_source_for_current_repo, file_data_source_for_pwd,
|
||||
};
|
||||
use crate::search::ai_context_menu::mixer::AIContextMenuMixer;
|
||||
use crate::search::ai_context_menu::mixer::AIContextMenuSearchableAction;
|
||||
use crate::search::ai_context_menu::mixer::{AIContextMenuMixer, AIContextMenuSearchableAction};
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use crate::search::ai_context_menu::notebooks::data_source::NotebookDataSource;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
@@ -26,55 +50,14 @@ use crate::search::ai_context_menu::rules::data_source::RulesDataSource;
|
||||
use crate::search::ai_context_menu::skills::data_source::SkillsDataSource;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use crate::search::ai_context_menu::workflows::data_source::WorkflowDataSource;
|
||||
use crate::search::data_source::QueryResult;
|
||||
use crate::search::data_source::{Query, QueryFilter};
|
||||
use crate::search::data_source::{Query, QueryFilter, QueryResult};
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use crate::search::mixer::AddAsyncSourceOptions;
|
||||
use crate::search::result_renderer::{QueryResultRenderer, QueryResultRendererStyles};
|
||||
use crate::search::search_bar::{SearchBar, SearchBarEvent, SearchBarState, SearchResultOrdering};
|
||||
use crate::settings::InputSettings;
|
||||
use async_channel::Sender;
|
||||
use galaxy_core::features::FeatureFlag;
|
||||
use galaxyui::elements::ConstrainedBox;
|
||||
use galaxyui::elements::CrossAxisAlignment;
|
||||
use galaxyui::elements::Empty;
|
||||
use galaxyui::elements::Fill;
|
||||
use galaxyui::elements::Hoverable;
|
||||
use galaxyui::elements::MouseStateHandle;
|
||||
use galaxyui::elements::ScrollStateHandle;
|
||||
use galaxyui::elements::Scrollable;
|
||||
use galaxyui::elements::ScrollableElement;
|
||||
use galaxyui::elements::ScrollbarWidth;
|
||||
use galaxyui::elements::UniformList;
|
||||
use galaxyui::elements::UniformListState;
|
||||
use galaxyui::elements::{
|
||||
AnchorPair, Border, ChildView, Container, CornerRadius, Dismiss, Flex, Icon, OffsetPositioning,
|
||||
OffsetType, ParentElement, PositionedElementOffsetBounds, PositioningAxis, Radius,
|
||||
SavePosition, Shrinkable, Stack, Text, XAxisAnchor, YAxisAnchor,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use settings::Setting as _;
|
||||
use std::collections::HashSet;
|
||||
use std::ops::Range;
|
||||
use std::time::Duration;
|
||||
|
||||
use galaxyui::platform::Cursor;
|
||||
use galaxyui::windowing::WindowManager;
|
||||
use galaxyui::SingletonEntity;
|
||||
use galaxyui::View;
|
||||
use galaxyui::{
|
||||
AppContext, Element, Entity, ModelHandle, TypedActionView, ViewContext, ViewHandle,
|
||||
WeakViewHandle,
|
||||
};
|
||||
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use crate::workspace::ActiveSession;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use repo_metadata::repositories::DetectedRepositories;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use std::path::Path;
|
||||
|
||||
use super::styles;
|
||||
|
||||
const CORNER_RADIUS: f32 = 8.0;
|
||||
const DEFAULT_PALETTE_WIDTH: f32 = 320.0;
|
||||
@@ -402,13 +385,13 @@ impl AIContextMenu {
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
{
|
||||
let active_window_id = app.windows().state().active_window;
|
||||
let active_dir = active_window_id
|
||||
.and_then(|window_id| ActiveSession::as_ref(app).path_if_local(window_id));
|
||||
active_dir.is_some_and(|dir| {
|
||||
DetectedRepositories::as_ref(app)
|
||||
.get_root_for_path(Path::new(dir))
|
||||
.is_some()
|
||||
})
|
||||
active_window_id
|
||||
.and_then(|window_id| ActiveSession::as_ref(app).working_directory(window_id))
|
||||
.is_some_and(|dir| {
|
||||
DetectedRepositories::as_ref(app)
|
||||
.get_root_for_canonical_path(dir)
|
||||
.is_some()
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use warpui::{AppContext, SingletonEntity};
|
||||
|
||||
use super::search_item::WorkflowSearchItem;
|
||||
use crate::cloud_object::model::persistence::CloudModel;
|
||||
use crate::cloud_object::CloudModelType;
|
||||
@@ -5,8 +8,6 @@ use crate::search::ai_context_menu::mixer::AIContextMenuSearchableAction;
|
||||
use crate::search::data_source::{Query, QueryResult};
|
||||
use crate::search::mixer::{DataSourceRunErrorWrapper, SyncDataSource};
|
||||
use crate::workspaces::user_workspaces::UserWorkspaces;
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use galaxyui::{AppContext, SingletonEntity};
|
||||
|
||||
const MAX_RESULTS: usize = 50;
|
||||
/// Base score for zero-state results. Each item gets an additional bonus based on
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use ordered_float::OrderedFloat;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::appearance::Appearance;
|
||||
use crate::cloud_object::ObjectType;
|
||||
use crate::search::ai_context_menu::styles;
|
||||
use crate::search::ai_context_menu::{mixer::AIContextMenuSearchableAction, safe_truncate};
|
||||
use crate::search::item::SearchItem;
|
||||
use crate::search::result_renderer::ItemHighlightState;
|
||||
use fuzzy_match::FuzzyMatchResult;
|
||||
use ordered_float::OrderedFloat;
|
||||
use galaxyui::elements::{
|
||||
ConstrainedBox, Container, CrossAxisAlignment, Flex, Highlight, Icon, ParentElement, Text,
|
||||
};
|
||||
use galaxyui::fonts::{Properties, Weight};
|
||||
use galaxyui::{AppContext, Element, SingletonEntity};
|
||||
|
||||
use crate::appearance::Appearance;
|
||||
use crate::cloud_object::ObjectType;
|
||||
use crate::search::ai_context_menu::mixer::AIContextMenuSearchableAction;
|
||||
use crate::search::ai_context_menu::{safe_truncate, styles};
|
||||
use crate::search::item::SearchItem;
|
||||
use crate::search::result_renderer::ItemHighlightState;
|
||||
|
||||
const MAX_COMBINED_LENGTH: usize = 55;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
Reference in New Issue
Block a user