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,22 +1,13 @@
#![allow(dead_code, unused_imports, unused_variables)]
use galaxyui::{
elements::{CornerRadius, Dismiss, MouseStateHandle, Radius},
fonts::Weight,
platform::Cursor,
ui_components::{
button::ButtonVariant,
components::{Coords, UiComponent, UiComponentStyles},
},
AppContext, Element, Entity, SingletonEntity, TypedActionView, View, ViewContext,
};
use galaxyui::elements::{Container, CornerRadius, Dismiss, MouseStateHandle, Radius};
use galaxyui::fonts::Weight;
use galaxyui::platform::Cursor;
use galaxyui::ui_components::button::ButtonVariant;
use galaxyui::ui_components::components::{Coords, UiComponent, UiComponentStyles};
use galaxyui::{AppContext, Element, Entity, SingletonEntity, TypedActionView, View, ViewContext};
use crate::{
appearance::Appearance,
ui_components::{
blended_colors,
dialog::{dialog_styles, Dialog},
},
};
use crate::appearance::Appearance;
use crate::ui_components::blended_colors;
use crate::ui_components::dialog::{dialog_styles, Dialog};
const BUTTON_PADDING: f32 = 12.;
const BUTTON_FONT_SIZE: f32 = 14.;
@@ -28,12 +19,17 @@ const CANCEL_TEXT: &str = "Cancel";
const DELETE_TEAM_TITLE_TEXT: &str = "Are you sure you want to delete this team?";
const LEAVE_TEAM_TITLE_TEXT: &str = "Are you sure you want to leave this team?";
const REMOVE_TEAM_MEMBER_TITLE_TEXT: &str = "Are you sure you want to remove this member?";
const DELETE_TEAM_BODY_TEXT: &str = "Deleting this team will permanently delete it and all of its related content, including billing information or credits. You will not be able to restore them.";
const LEAVE_TEAM_BODY_TEXT: &str = "You will need to be reinvited in order to rejoin.";
const LEAVE_TEAM_RELOAD_CREDITS_BODY_TEXT: &str = "If you leave this team, youll lose access to any remaining reload credits tied to it. Youll regain access to any unused, non-expired credits if you rejoin the same team later.";
const REMOVE_TEAM_MEMBER_RELOAD_CREDITS_BODY_TEXT: &str = "This member will lose access to any remaining reload credits tied to this team. If they rejoin later, theyll regain access to any unused, non-expired credits.";
const DELETE_TEAM_CONFIRM_TEXT: &str = "Yes, delete";
const LEAVE_TEAM_CONFIRM_TEXT: &str = "Yes, leave";
const LEAVE_TEAM_RELOAD_CREDITS_CONFIRM_TEXT: &str = "Leave Team";
const REMOVE_TEAM_MEMBER_RELOAD_CREDITS_CONFIRM_TEXT: &str = "Remove Member";
pub enum CloudActionConfirmationDialogEvent {
Cancel,
@@ -50,6 +46,8 @@ pub enum CloudActionConfirmationDialogAction {
pub enum CloudActionConfirmationDialogVariant {
LeaveTeam,
DeleteTeam,
LeaveTeamReloadCredits,
RemoveTeamMemberReloadCredits,
#[default]
None,
}
@@ -81,9 +79,15 @@ impl CloudActionConfirmationDialog {
fn title_text(&self) -> String {
match self.variant {
CloudActionConfirmationDialogVariant::LeaveTeam => LEAVE_TEAM_TITLE_TEXT.to_string(),
CloudActionConfirmationDialogVariant::LeaveTeam
| CloudActionConfirmationDialogVariant::LeaveTeamReloadCredits => {
LEAVE_TEAM_TITLE_TEXT.to_string()
}
CloudActionConfirmationDialogVariant::DeleteTeam => DELETE_TEAM_TITLE_TEXT.to_string(),
CloudActionConfirmationDialogVariant::None => "".to_string(),
CloudActionConfirmationDialogVariant::RemoveTeamMemberReloadCredits => {
REMOVE_TEAM_MEMBER_TITLE_TEXT.to_string()
}
CloudActionConfirmationDialogVariant::None => String::new(),
}
}
@@ -91,7 +95,13 @@ impl CloudActionConfirmationDialog {
match self.variant {
CloudActionConfirmationDialogVariant::LeaveTeam => LEAVE_TEAM_BODY_TEXT.to_string(),
CloudActionConfirmationDialogVariant::DeleteTeam => DELETE_TEAM_BODY_TEXT.to_string(),
CloudActionConfirmationDialogVariant::None => "".to_string(),
CloudActionConfirmationDialogVariant::LeaveTeamReloadCredits => {
LEAVE_TEAM_RELOAD_CREDITS_BODY_TEXT.to_string()
}
CloudActionConfirmationDialogVariant::RemoveTeamMemberReloadCredits => {
REMOVE_TEAM_MEMBER_RELOAD_CREDITS_BODY_TEXT.to_string()
}
CloudActionConfirmationDialogVariant::None => String::new(),
}
}
@@ -101,7 +111,13 @@ impl CloudActionConfirmationDialog {
CloudActionConfirmationDialogVariant::DeleteTeam => {
DELETE_TEAM_CONFIRM_TEXT.to_string()
}
CloudActionConfirmationDialogVariant::None => "".to_string(),
CloudActionConfirmationDialogVariant::LeaveTeamReloadCredits => {
LEAVE_TEAM_RELOAD_CREDITS_CONFIRM_TEXT.to_string()
}
CloudActionConfirmationDialogVariant::RemoveTeamMemberReloadCredits => {
REMOVE_TEAM_MEMBER_RELOAD_CREDITS_CONFIRM_TEXT.to_string()
}
CloudActionConfirmationDialogVariant::None => String::new(),
}
}
}
@@ -194,7 +210,11 @@ impl View for CloudActionConfirmationDialog {
dialog_styles(appearance),
)
.with_bottom_row_child(cancel_button)
.with_bottom_row_child(confirm_button)
.with_bottom_row_child(
Container::new(confirm_button)
.with_margin_left(12.)
.finish(),
)
.with_width(DIALOG_WIDTH)
.build()
.finish();
+16 -19
View File
@@ -1,24 +1,21 @@
use galaxyui::{
elements::{
Border, Clipped, Container, CornerRadius, Dismiss, Empty, Flex, MainAxisSize,
MouseStateHandle, ParentElement, Radius, Shrinkable, Text,
},
fonts::Weight,
platform::Cursor,
presenter::ChildView,
ui_components::{
button::ButtonVariant,
components::{Coords, UiComponent, UiComponentStyles},
},
AppContext, Element, ViewHandle,
use galaxyui::elements::{
Border, Clipped, Container, CornerRadius, Dismiss, Empty, Flex, MainAxisSize, MouseStateHandle,
ParentElement, Radius, Shrinkable, Text,
};
use warpui::fonts::Weight;
use warpui::platform::Cursor;
use warpui::presenter::ChildView;
use warpui::ui_components::button::ButtonVariant;
use warpui::ui_components::components::{Coords, UiComponent, UiComponentStyles};
use warpui::{AppContext, Element, ViewHandle};
use super::index::DriveIndexAction;
use super::DriveObjectType;
use crate::appearance::Appearance;
use crate::cloud_object::Space;
use crate::{
appearance::Appearance, editor::EditorView, server::ids::SyncId, ui_components::blended_colors,
};
use super::{index::DriveIndexAction, DriveObjectType};
use crate::editor::EditorView;
use crate::server::ids::SyncId;
use crate::ui_components::blended_colors;
const DIALOG_PADDING: f32 = 24.;
const INPUT_MARGIN_TOP: f32 = 16.;
@@ -39,7 +36,7 @@ const CREATE_BUTTON_TEXT: &str = "Create";
const CANCEL_BUTTON_TEXT: &str = "Cancel";
const RENAME_BUTTON_TEXT: &str = "Rename";
/// Struct holding necessary infromation and states for the dialog
/// Struct holding necessary information and states for the dialog
/// that opens when creating or updating a folder or notebook.
///
/// This dialog can be opened for a folder or a space. If open_for_folder_id = None, it's a space.
+4 -5
View File
@@ -1,9 +1,8 @@
use galaxy_core::ui::{
appearance::Appearance,
color::{contrast::MinimumAllowedContrast, ContrastingColor},
theme::Fill,
};
use pathfinder_color::ColorU;
use galaxy_core::ui::appearance::Appearance;
use galaxy_core::ui::color::contrast::MinimumAllowedContrast;
use galaxy_core::ui::color::ContrastingColor;
use galaxy_core::ui::theme::Fill;
use super::DriveObjectType;
use crate::ui_components::blended_colors;
+108 -60
View File
@@ -1,83 +1,131 @@
use galaxyui::{SingletonEntity, ViewContext};
use warp_server_auth::auth_state::AuthState;
use warp_server_auth::user::PersonalObjectLimits;
use warp_server_auth::AuthStateProvider;
use galaxyui::{AppContext, SingletonEntity};
use crate::{
auth::{auth_manager::AuthManager, AuthStateProvider},
cloud_object::{
model::persistence::CloudModel, GenericStringObjectFormat, JsonObjectType, ObjectType,
Space,
},
};
use crate::auth::auth_manager::AuthManager;
use crate::cloud_object::model::persistence::CloudModel;
use crate::cloud_object::Space;
pub fn has_feature_gated_anonymous_user_reached_notebook_limit<V: galaxyui::View>(
ctx: &mut ViewContext<V>,
#[derive(Clone, Copy)]
enum AnonymousUserObjectLimit {
Notebook,
Workflow,
EnvVarCollection,
}
impl AnonymousUserObjectLimit {
fn is_exceeded(self, limits: PersonalObjectLimits, num_objects: usize) -> bool {
match self {
Self::Notebook => num_objects > limits.notebook_limit,
Self::Workflow => num_objects > limits.workflow_limit,
Self::EnvVarCollection => num_objects > limits.env_var_limit,
}
}
}
fn is_feature_gated_anonymous_user_past_limit(
auth_state: &AuthState,
num_objects: usize,
object_limit: AnonymousUserObjectLimit,
) -> bool {
auth_state
.is_anonymous_user_feature_gated()
.unwrap_or_default()
&& auth_state
.personal_object_limits()
.is_some_and(|limits| object_limit.is_exceeded(limits, num_objects))
}
pub(crate) fn is_feature_gated_anonymous_user_past_notebook_limit(
auth_state: &AuthState,
num_objects: usize,
) -> bool {
is_feature_gated_anonymous_user_past_limit(
auth_state,
num_objects,
AnonymousUserObjectLimit::Notebook,
)
}
pub(crate) fn is_feature_gated_anonymous_user_past_workflow_limit(
auth_state: &AuthState,
num_objects: usize,
) -> bool {
is_feature_gated_anonymous_user_past_limit(
auth_state,
num_objects,
AnonymousUserObjectLimit::Workflow,
)
}
pub(crate) fn is_feature_gated_anonymous_user_past_env_var_limit(
auth_state: &AuthState,
num_objects: usize,
) -> bool {
is_feature_gated_anonymous_user_past_limit(
auth_state,
num_objects,
AnonymousUserObjectLimit::EnvVarCollection,
)
}
fn has_feature_gated_anonymous_user_reached_limit(
ctx: &mut AppContext,
num_objects: usize,
object_limit: AnonymousUserObjectLimit,
) -> bool {
if AuthStateProvider::handle(ctx).read(ctx, |auth_state_provider, _ctx| {
is_feature_gated_anonymous_user_past_limit(
auth_state_provider.get(),
num_objects,
object_limit,
)
}) {
AuthManager::handle(ctx).update(ctx, |auth_manager: &mut AuthManager, ctx| {
auth_manager.anonymous_user_hit_drive_object_limit(ctx);
});
return true;
};
false
}
pub fn has_feature_gated_anonymous_user_reached_notebook_limit(ctx: &mut AppContext) -> bool {
let count = CloudModel::handle(ctx).read(ctx, |model, ctx| {
model
.active_non_welcome_notebooks_in_space(Space::Personal, ctx)
.count()
});
if AuthStateProvider::handle(ctx).read(ctx, |auth_state_provider, _ctx| {
auth_state_provider
.get()
.is_anonymous_user_past_object_limit(ObjectType::Notebook, count + 1)
.unwrap_or_default()
}) {
AuthManager::handle(ctx).update(ctx, |auth_manager: &mut AuthManager, ctx| {
auth_manager.anonymous_user_hit_drive_object_limit(ctx);
});
return true;
};
false
has_feature_gated_anonymous_user_reached_limit(
ctx,
count + 1,
AnonymousUserObjectLimit::Notebook,
)
}
pub fn has_feature_gated_anonymous_user_reached_workflow_limit<V: galaxyui::View>(
ctx: &mut ViewContext<V>,
) -> bool {
pub fn has_feature_gated_anonymous_user_reached_workflow_limit(ctx: &mut AppContext) -> bool {
let count = CloudModel::handle(ctx).read(ctx, |model, ctx| {
model
.active_non_welcome_workflows_in_space(Space::Personal, ctx)
.count()
});
if AuthStateProvider::handle(ctx).read(ctx, |auth_state_provider, _ctx| {
auth_state_provider
.get()
.is_anonymous_user_past_object_limit(ObjectType::Workflow, count + 1)
.unwrap_or_default()
}) {
AuthManager::handle(ctx).update(ctx, |auth_manager: &mut AuthManager, ctx| {
auth_manager.anonymous_user_hit_drive_object_limit(ctx);
});
return true;
};
false
has_feature_gated_anonymous_user_reached_limit(
ctx,
count + 1,
AnonymousUserObjectLimit::Workflow,
)
}
pub fn has_feature_gated_anonymous_user_reached_env_var_limit<V: galaxyui::View>(
ctx: &mut ViewContext<V>,
) -> bool {
pub fn has_feature_gated_anonymous_user_reached_env_var_limit(ctx: &mut AppContext) -> bool {
let count = CloudModel::handle(ctx).read(ctx, |model, ctx| {
model
.active_non_welcome_env_var_collections_in_space(Space::Personal, ctx)
.count()
});
if AuthStateProvider::handle(ctx).read(ctx, |auth_state_provider, _ctx| {
auth_state_provider
.get()
.is_anonymous_user_past_object_limit(
ObjectType::GenericStringObject(GenericStringObjectFormat::Json(
JsonObjectType::EnvVarCollection,
)),
count + 1,
)
.unwrap_or_default()
}) {
AuthManager::handle(ctx).update(ctx, |auth_manager: &mut AuthManager, ctx| {
auth_manager.anonymous_user_hit_drive_object_limit(ctx);
});
return true;
};
false
has_feature_gated_anonymous_user_reached_limit(
ctx,
count + 1,
AnonymousUserObjectLimit::EnvVarCollection,
)
}
@@ -1,18 +1,12 @@
use galaxyui::{
elements::MouseStateHandle,
fonts::Weight,
platform::Cursor,
ui_components::{
button::ButtonVariant,
components::{Coords, UiComponent, UiComponentStyles},
},
AppContext, Element, Entity, SingletonEntity, TypedActionView, View, ViewContext,
};
use galaxyui::elements::MouseStateHandle;
use galaxyui::fonts::Weight;
use galaxyui::platform::Cursor;
use galaxyui::ui_components::button::ButtonVariant;
use galaxyui::ui_components::components::{Coords, UiComponent, UiComponentStyles};
use galaxyui::{AppContext, Element, Entity, SingletonEntity, TypedActionView, View, ViewContext};
use crate::{
appearance::Appearance,
ui_components::dialog::{dialog_styles, Dialog},
};
use crate::appearance::Appearance;
use crate::ui_components::dialog::{dialog_styles, Dialog};
const CANCEL_TEXT: &str = "Cancel";
+13 -20
View File
@@ -1,12 +1,8 @@
use std::collections::hash_map::{Entry, OccupiedEntry};
use std::collections::HashMap;
#[cfg(feature = "local_fs")]
use std::io::ErrorKind;
use std::{
collections::{
hash_map::{Entry, OccupiedEntry},
HashMap,
},
path::{Path, PathBuf},
};
use std::path::{Path, PathBuf};
#[cfg(feature = "local_fs")]
use aho_corasick::{AhoCorasick, MatchKind};
@@ -15,18 +11,17 @@ use anyhow::{anyhow, Context};
#[cfg(feature = "local_fs")]
use futures::AsyncWriteExt;
use galaxy_util::path::ShellFamily;
use galaxyui::{
platform::{file_picker::FilePickerError, FilePickerConfiguration, OperatingSystem},
r#async::SpawnedFutureHandle,
AppContext, Entity, ModelContext, SingletonEntity, WindowId,
};
use galaxyui::platform::file_picker::FilePickerError;
use galaxyui::platform::{FilePickerConfiguration, OperatingSystem};
use galaxyui::r#async::SpawnedFutureHandle;
use galaxyui::{AppContext, Entity, ModelContext, SingletonEntity, WindowId};
use crate::{
cloud_object::{model::persistence::CloudModel, Space},
safe_warn,
view_components::DismissibleToast,
workspace::{active_terminal_in_window, ToastStack},
};
use super::CloudObjectTypeAndId;
use crate::cloud_object::model::persistence::CloudModel;
use crate::cloud_object::Space;
use crate::safe_warn;
use crate::view_components::DismissibleToast;
use crate::workspace::{active_terminal_in_window, ToastStack};
#[cfg(feature = "local_fs")]
use crate::{
notebooks::export_notebook, server::cloud_objects::update_manager::get_duplicate_object_name,
@@ -34,8 +29,6 @@ use crate::{
workspace::WorkspaceAction,
};
use super::CloudObjectTypeAndId;
/// Singleton model for exporting from Warp Drive.
pub struct ExportManager {
exports: HashMap<ExportId, Export>,
+15 -19
View File
@@ -1,9 +1,7 @@
use std::{
collections::HashMap,
fs,
path::{Path, PathBuf},
sync::Arc,
};
use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use futures::channel::oneshot;
use galaxy_util::path::ShellFamily;
@@ -11,20 +9,18 @@ use galaxyui::{AddSingletonModel, App, SingletonEntity, WindowId};
use parking_lot::Mutex;
use tempfile::TempDir;
use crate::{
cloud_object::{
model::persistence::CloudModel, CloudObjectMetadata, CloudObjectPermissions, ObjectIdType,
ObjectType, Space,
},
drive::CloudObjectTypeAndId,
notebooks::{CloudNotebook, CloudNotebookModel, NotebookId},
server::ids::SyncId,
workflows::{workflow::Workflow, CloudWorkflow, CloudWorkflowModel, WorkflowId},
workspace::ToastStack,
workspaces::user_workspaces::UserWorkspaces,
};
use super::{safe_filename, ExportEvent, ExportId, ExportManager};
use crate::cloud_object::model::persistence::CloudModel;
use crate::cloud_object::{
CloudObjectMetadata, CloudObjectPermissions, ObjectIdType, ObjectType, Space,
};
use crate::drive::CloudObjectTypeAndId;
use crate::notebooks::{CloudNotebook, CloudNotebookModel, NotebookId};
use crate::server::ids::SyncId;
use crate::workflows::workflow::Workflow;
use crate::workflows::{CloudWorkflow, CloudWorkflowModel, WorkflowId};
use crate::workspace::ToastStack;
use crate::workspaces::user_workspaces::UserWorkspaces;
struct ExportTest {
target_dir: TempDir,
+19 -57
View File
@@ -1,52 +1,25 @@
use std::sync::Arc;
use super::items::folder::WarpDriveFolder;
use super::items::WarpDriveItem;
use super::CloudObjectTypeAndId;
use crate::server::cloud_objects::update_manager::InitiatedBy;
use crate::{
appearance::Appearance,
cloud_object::{
CloudModelType, CloudObjectEventEntrypoint, CreateCloudObjectResult, CreateObjectRequest,
GenericCloudObject, GenericServerObject, ObjectType, Revision, ServerCloudObject, Space,
UpdateCloudObjectResult,
},
persistence::ModelEvent,
server::{
ids::{ServerId, SyncId},
server_api::object::ObjectClient,
sync_queue::{QueueItem, SerializedModel},
},
};
use anyhow::Result;
use async_trait::async_trait;
pub use cloud_object_models::{CloudFolder, CloudFolderModel};
// Re-exported from galaxy_server_client.
pub use galaxy_server_client::ids::FolderId;
/// The model for a `CloudFolder`.
#[derive(Clone, Debug, PartialEq)]
pub struct CloudFolderModel {
pub name: String,
// TODO: since this is local only state, we should consider only surfacing it as part of the
// CloudViewModel. Right now, every server folder uses CloudFolderModel, which means it
// hardcodes a value of `false` for this property since it can't know what the local state is.
pub is_open: bool,
pub is_warp_pack: bool,
}
impl CloudFolderModel {
pub fn new(name: &str, is_warp_pack: bool) -> Self {
Self {
name: name.to_owned(),
is_open: false,
is_warp_pack,
}
}
}
/// `CloudFolder` is a folder retrieved from the server.
pub type CloudFolder = GenericCloudObject<FolderId, CloudFolderModel>;
// Re-exported from warp_server_client.
use super::items::folder::WarpDriveFolder;
use super::items::WarpDriveItem;
use super::CloudObjectTypeAndId;
use crate::appearance::Appearance;
use crate::cloud_object::{
CloudModelType, CloudObjectEventEntrypoint, CloudObjectUpsertParams, CreateCloudObjectResult,
CreateObjectRequest, GenericServerObject, ObjectType, Revision, Space, UpdateCloudObjectResult,
};
use crate::persistence::ModelEvent;
use crate::server::cloud_objects::update_manager::InitiatedBy;
use crate::server::ids::{ServerId, SyncId};
use crate::server::server_api::object::ObjectClient;
use crate::server::sync_queue::{QueueItem, SerializedModel};
#[cfg_attr(not(target_family = "wasm"), async_trait)]
#[cfg_attr(target_family = "wasm", async_trait(?Send))]
@@ -70,14 +43,14 @@ impl CloudModelType for CloudFolderModel {
self.name.clone()
}
fn upsert_event(&self, folder: &CloudFolder) -> ModelEvent {
fn upsert_event(params: CloudObjectUpsertParams<Self>) -> ModelEvent {
ModelEvent::UpsertFolder {
folder: folder.clone(),
folder: CloudFolder::from(params),
}
}
fn bulk_upsert_event(objects: &[CloudFolder]) -> ModelEvent {
ModelEvent::UpsertFolders(objects.to_vec())
fn bulk_upsert_event(objects: Vec<CloudObjectUpsertParams<Self>>) -> ModelEvent {
ModelEvent::UpsertFolders(objects.into_iter().map(CloudFolder::from).collect())
}
fn create_object_queue_item(
@@ -120,17 +93,6 @@ impl CloudModelType for CloudFolderModel {
SerializedModel::new(self.name.to_owned())
}
fn new_from_server_update(&self, server_cloud_object: &ServerCloudObject) -> Option<Self> {
if let ServerCloudObject::Folder(server_folder) = server_cloud_object {
return Some(CloudFolderModel {
name: server_folder.model.name.clone(),
is_open: self.is_open,
is_warp_pack: server_folder.model.is_warp_pack,
});
}
None
}
fn can_move_to_space(&self, current_space: Space, new_space: Space) -> bool {
// We don't currently support moving folders across spaces.
current_space == new_space
+7 -8
View File
@@ -3,15 +3,14 @@ use std::env::current_dir;
use galaxy_core::ui::appearance::Appearance;
use galaxyui::App;
use crate::{
cloud_object::model::persistence::CloudModel,
network::NetworkStatus,
server::{cloud_objects::update_manager::UpdateManager, sync_queue::SyncQueue},
workspaces::{team_tester::TeamTesterStatus, user_workspaces::UserWorkspaces},
GlobalResourceHandles, GlobalResourceHandlesProvider,
};
use super::expand_dirs;
use crate::cloud_object::model::persistence::CloudModel;
use crate::network::NetworkStatus;
use crate::server::cloud_objects::update_manager::UpdateManager;
use crate::server::sync_queue::SyncQueue;
use crate::workspaces::team_tester::TeamTesterStatus;
use crate::workspaces::user_workspaces::UserWorkspaces;
use crate::{GlobalResourceHandles, GlobalResourceHandlesProvider};
#[test]
fn test_expand_directories() {
+18 -20
View File
@@ -1,29 +1,27 @@
use crate::{
appearance::Appearance,
cloud_object::{model::persistence::CloudModel, CloudObject, Owner},
server::{ids::SyncId, sync_queue::SyncQueue},
themes::theme::GalaxyTheme,
workspaces::user_workspaces::UserWorkspaces,
};
use pathfinder_geometry::vector::vec2f;
use galaxy_core::ui::theme::Fill;
use galaxyui::elements::{
Align, Border, ChildAnchor, ClippedScrollStateHandle, ClippedScrollable, ConstrainedBox,
Container, CornerRadius, CrossAxisAlignment, Flex, Highlight, MouseStateHandle,
OffsetPositioning, ParentAnchor, ParentElement, ParentOffsetBounds, Radius, ScrollbarWidth,
Shrinkable, Stack, Text,
};
use galaxyui::platform::{FilePickerConfiguration, FileType};
use galaxyui::presenter::ChildView;
use galaxyui::ui_components::button::ButtonVariant;
use galaxyui::ui_components::components::{UiComponent, UiComponentStyles};
use galaxyui::{
elements::{
Align, Border, ChildAnchor, ClippedScrollStateHandle, ClippedScrollable, ConstrainedBox,
Container, CornerRadius, CrossAxisAlignment, Flex, Highlight, MouseStateHandle,
OffsetPositioning, ParentAnchor, ParentElement, ParentOffsetBounds, Radius, ScrollbarWidth,
Shrinkable, Stack, Text,
},
platform::{FilePickerConfiguration, FileType},
presenter::ChildView,
ui_components::{
button::ButtonVariant,
components::{UiComponent, UiComponentStyles},
},
AppContext, Element, Entity, SingletonEntity, TypedActionView, View, ViewContext, ViewHandle,
};
use pathfinder_geometry::vector::vec2f;
use super::modal_body::{ImportModalBody, ImportModalBodyAction, ImportModalBodyEvent};
use crate::appearance::Appearance;
use crate::cloud_object::model::persistence::CloudModel;
use crate::cloud_object::{CloudObject, Owner};
use crate::server::ids::SyncId;
use crate::server::sync_queue::SyncQueue;
use crate::themes::theme::WarpTheme;
use crate::workspaces::user_workspaces::UserWorkspaces;
const CLOSE_BUTTON_SIZE: f32 = 24.;
const HEADER_FONT_SIZE: f32 = 16.;
+23 -30
View File
@@ -1,38 +1,31 @@
use futures_util::stream::AbortHandle;
use galaxyui::{
elements::{
Align, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, Flex,
MainAxisAlignment, MainAxisSize, MouseStateHandle, ParentElement, Radius,
},
platform::{file_picker::FilePickerError, Cursor},
ui_components::{
button::{ButtonVariant, TextAndIcon, TextAndIconAlignment},
components::{Coords, UiComponent, UiComponentStyles},
},
AppContext, Element, Entity, ModelHandle, SingletonEntity, TypedActionView, View, ViewContext,
};
use pathfinder_geometry::vector::vec2f;
use std::path::PathBuf;
use crate::{
appearance::Appearance,
cloud_object::Owner,
server::{
ids::{ClientId, SyncId},
sync_queue::SyncQueue,
},
ui_components::icons::Icon,
view_components::DismissibleToast,
workspace::ToastStack,
use futures_util::stream::AbortHandle;
use pathfinder_geometry::vector::vec2f;
use galaxyui::elements::{
Align, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, Flex, MainAxisAlignment,
MainAxisSize, MouseStateHandle, ParentElement, Radius,
};
use galaxyui::platform::file_picker::FilePickerError;
use galaxyui::platform::Cursor;
use galaxyui::ui_components::button::{ButtonVariant, TextAndIcon, TextAndIconAlignment};
use galaxyui::ui_components::components::{Coords, UiComponent, UiComponentStyles};
use galaxyui::{
AppContext, Element, Entity, ModelHandle, SingletonEntity, TypedActionView, View, ViewContext,
};
use super::{
modal::BODY_HEIGHT,
nodes::{
expand_dirs, parse_file, FileContent, FileId, FileUploadState, FolderId, UploadResult,
},
queue::{ImportQueue, ImportQueueArgs, ImportQueueEvent, ParentId, RequestContent},
use super::modal::BODY_HEIGHT;
use super::nodes::{
expand_dirs, parse_file, FileContent, FileId, FileUploadState, FolderId, UploadResult,
};
use super::queue::{ImportQueue, ImportQueueArgs, ImportQueueEvent, ParentId, RequestContent};
use crate::appearance::Appearance;
use crate::cloud_object::Owner;
use crate::server::ids::{ClientId, SyncId};
use crate::server::sync_queue::SyncQueue;
use crate::ui_components::icons::Icon;
use crate::view_components::DismissibleToast;
use crate::workspace::ToastStack;
const FILE_PICKER_BUTTON_WIDTH: f32 = 250.;
const BUTTON_FONT_SIZE: f32 = 14.;
+3 -3
View File
@@ -1,8 +1,8 @@
use std::{collections::HashMap, path::PathBuf};
use crate::drive::import::nodes::{UploadResult, UploadStatus};
use std::collections::HashMap;
use std::path::PathBuf;
use super::{FileId, FileNode, FileType, FileUploadState, FolderId, FolderNode, ImportedNode};
use crate::drive::import::nodes::{UploadResult, UploadStatus};
fn mock_tree() -> FileUploadState {
let mut folder_id_to_node = HashMap::new();
+23 -30
View File
@@ -1,36 +1,31 @@
use crate::{
drive::{cloud_object_styling::warp_drive_icon_color, DriveObjectType},
notebooks::post_process_notebook,
workflows::{
export_workflow::export_deserialize, workflow::Workflow, workflow_enum::WorkflowEnum,
},
};
use std::collections::HashMap;
use std::ffi::OsStr;
use std::ops::{Add, AddAssign, SubAssign};
use std::path::{Path, PathBuf};
use anyhow::Result;
use async_recursion::async_recursion;
use futures_lite::StreamExt;
use galaxyui::{
elements::{
Align, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, Flex, Hoverable,
MouseStateHandle, ParentElement, Radius, Shrinkable,
},
platform::Cursor,
ui_components::components::{UiComponent, UiComponentStyles},
Element,
};
use pathfinder_color::ColorU;
use std::{
collections::HashMap,
ffi::OsStr,
ops::{Add, AddAssign, SubAssign},
path::{Path, PathBuf},
};
use crate::{
appearance::Appearance, notebooks::file::is_markdown_file, server::ids::ClientId,
themes::theme::Fill, ui_components::icons::Icon,
use galaxyui::elements::{
Align, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, Flex, Hoverable,
MouseStateHandle, ParentElement, Radius, Shrinkable,
};
use warpui::platform::Cursor;
use warpui::ui_components::components::{UiComponent, UiComponentStyles};
use warpui::Element;
use super::modal_body::{ImportModalBodyAction, BASE_INDENT, IMPORT_FONT_SIZE, INDENT_MARGIN};
use crate::appearance::Appearance;
use crate::drive::cloud_object_styling::warp_drive_icon_color;
use crate::drive::DriveObjectType;
use crate::notebooks::file::is_markdown_file;
use crate::server::ids::ClientId;
use crate::themes::theme::Fill;
use crate::ui_components::icons::Icon;
use crate::workflows::export_workflow::export_deserialize;
use crate::workflows::workflow::Workflow;
use crate::workflows::workflow_enum::WorkflowEnum;
#[cfg(test)]
#[path = "node_tests.rs"]
@@ -756,7 +751,7 @@ impl FileUploadState {
folder.server_id = Some(server_id.clone());
// If a folder has no children or all of its children complete syncing,
// we need to bubble the state up in the folder hierachy tree.
// we need to bubble the state up in the folder hierarchy tree.
if folder.children().is_empty() || folder.all_children_synced {
folder.status = UploadStatus::Loaded(server_id);
true
@@ -898,9 +893,7 @@ impl FileUploadState {
pub(super) async fn parse_file(path: PathBuf, file_type: FileType) -> Result<FileContent> {
match file_type {
FileType::Notebook => Ok(FileContent::Notebook(post_process_notebook(
&async_fs::read_to_string(path).await?,
))),
FileType::Notebook => Ok(FileContent::Notebook(async_fs::read_to_string(path).await?)),
FileType::Workflow => {
let file = async_fs::read(path).await?;
let mut workflow_enums: HashMap<ClientId, WorkflowEnum> = HashMap::new();
+12 -15
View File
@@ -2,20 +2,17 @@ use std::collections::HashMap;
use galaxyui::{Entity, ModelContext, SingletonEntity};
use crate::{
cloud_object::{model::persistence::CloudModel, CloudObjectEventEntrypoint, Owner},
drive::folders::FolderId,
notebooks::CloudNotebookModel,
server::{
cloud_objects::update_manager::{
InitiatedBy, ObjectOperation, OperationSuccessType, UpdateManager, UpdateManagerEvent,
},
ids::{ClientId, SyncId},
},
workflows::{workflow::Workflow, workflow_enum::WorkflowEnum},
};
use super::nodes::{self, FileId};
use crate::cloud_object::model::persistence::CloudModel;
use crate::cloud_object::{CloudObjectEventEntrypoint, Owner};
use crate::drive::folders::FolderId;
use crate::notebooks::CloudNotebookModel;
use crate::server::cloud_objects::update_manager::{
InitiatedBy, ObjectOperation, OperationSuccessType, UpdateManager, UpdateManagerEvent,
};
use crate::server::ids::{ClientId, SyncId};
use crate::workflows::workflow::Workflow;
use crate::workflows::workflow_enum::WorkflowEnum;
pub(super) enum ImportQueueEvent {
FileCompleted {
@@ -105,7 +102,7 @@ pub(super) struct ImportQueue {
impl ImportQueue {
pub fn new(ctx: &mut ModelContext<Self>) -> Self {
let update_manager = UpdateManager::handle(ctx);
ctx.subscribe_to_model(&update_manager, |me, event, ctx| {
ctx.subscribe_to_model(&update_manager, |me, _, event, ctx| {
me.handle_update_manager_event(event, ctx);
});
@@ -117,7 +114,7 @@ impl ImportQueue {
}
}
// Whether all dependcies of an item has been sync-ed.
// Whether all dependencies of an item has been sync-ed.
fn dependency_synced(&self, item: &ImportQueueArgs) -> bool {
match &item.parent_id {
ParentId::FolderToUpload(id) => self
+95 -105
View File
@@ -1,111 +1,102 @@
#[cfg(target_family = "wasm")]
use crate::uri::web_intent_parser::open_url_on_desktop;
use crate::{
ai::{
document::ai_document_model::AIDocumentId,
facts::{AIFact, AIMemory},
},
appearance::Appearance,
auth::{
auth_manager::{AuthManager, LoginGatedFeature},
auth_state::AuthState,
auth_view_modal::AuthViewVariant,
AuthStateProvider,
},
cloud_object::{
model::{
persistence::{CloudModel, CloudModelEvent},
view::{CloudViewModel, CloudViewModelEvent, UpdateTimestamp},
},
CloudObject, CloudObjectEventEntrypoint, CloudObjectLocation, CloudObjectSyncStatus,
GenericCloudObject, GenericStringObjectFormat, JsonObjectType, NumInFlightRequests,
ObjectType, Space,
},
editor::{EditorView, Event as EditorEvent, SingleLineEditorOptions},
env_vars::CloudEnvVarCollection,
features::FeatureFlag,
menu::{Event, Menu, MenuItem, MenuItemFields},
network::NetworkStatus,
notebooks::CloudNotebookModel,
report_if_error, send_telemetry_from_ctx,
server::{
cloud_objects::update_manager::{FetchSingleObjectOption, UpdateManager},
ids::{ClientId, ObjectUid, ServerId, SyncId},
sync_queue::SyncQueue,
telemetry::{AnonymousUserSignupEntrypoint, SharingDialogSource, TelemetryEvent},
},
settings::app_installation_detection::{UserAppInstallDetectionSettings, UserAppInstallStatus},
ui_components::{
blended_colors,
buttons::{highlight, icon_button},
icons::{Icon, ICON_DIMENSIONS},
menu_button::{icon_button_with_context_menu, MenuDirection},
},
util::{color::coloru_with_opacity, sync::Condition},
view_components::{Dropdown, DropdownItem},
workflows::{CloudWorkflow, WorkflowViewMode},
workspace::active_terminal_in_window,
workspaces::{
update_manager::TeamUpdateManager, user_workspaces::UserWorkspaces, workspace::WorkspaceUid,
},
ObjectActions,
};
use std::any::Any;
use std::collections::HashMap;
use std::sync::Arc;
use super::{
cloud_object_naming_dialog::CloudObjectNamingDialog,
drive_helpers::{
has_feature_gated_anonymous_user_reached_env_var_limit,
has_feature_gated_anonymous_user_reached_notebook_limit,
has_feature_gated_anonymous_user_reached_workflow_limit,
},
empty_trash_confirmation_dialog::{EmptyTrashConfirmationDialog, EmptyTrashConfirmationEvent},
folders::CloudFolder,
items::{
ai_fact_collection::WarpDriveAIFactCollection,
item::{tools_panel_menu_direction, ItemStates, WarpDriveRow},
mcp_server_collection::WarpDriveMCPServerCollection,
WarpDriveItemId,
},
settings::WarpDriveSettings,
sharing::{
dialog::{SharingDialog, SharingDialogEvent},
ContentEditability, ShareableObject,
},
CloudObjectTypeAndId, DriveObjectType, DriveSortOrder,
};
use crate::drive::panel::DrivePanelAction;
use crate::server::cloud_objects::update_manager::InitiatedBy;
use futures::Future;
use galaxy_core::{
context_flag::ContextFlag, settings::Setting, ui::theme::color::internal_colors,
};
use galaxyui::{
clipboard::ClipboardContent,
elements::{
Align, AnchorPair, Border, ChildAnchor, ChildView, ClippedScrollStateHandle,
ClippedScrollable, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, Dash,
DropTarget, DropTargetData, Empty, Flex, Highlight, Hoverable, MainAxisAlignment,
MainAxisSize, MouseStateHandle, OffsetPositioning, OffsetType, ParentAnchor, ParentElement,
ParentOffsetBounds, PositionedElementAnchor, PositionedElementOffsetBounds,
PositioningAxis, Radius, SavePosition, ScrollTarget, ScrollToPositionMode, ScrollbarWidth,
Shrinkable, Stack, Text, XAxisAnchor, YAxisAnchor,
},
fonts::{Properties, Weight},
keymap::FixedBinding,
platform::{Cursor, OperatingSystem},
ui_components::{
button::ButtonVariant,
components::{Coords, UiComponent, UiComponentStyles},
},
units::IntoPixels,
AppContext, BlurContext, Element, Entity, ModelHandle, SingletonEntity, TypedActionView,
UpdateView, View, ViewContext, ViewHandle, WindowId,
};
use itertools::Itertools;
use pathfinder_color::ColorU;
use pathfinder_geometry::vector::{vec2f, Vector2F};
use std::{any::Any, collections::HashMap, sync::Arc};
use url::Url;
use galaxy_core::context_flag::ContextFlag;
use galaxy_core::settings::Setting;
use galaxy_core::ui::theme::color::internal_colors;
use galaxy_util::sync::Condition;
use galaxyui::clipboard::ClipboardContent;
use galaxyui::elements::{
Align, AnchorPair, Border, ChildAnchor, ChildView, ClippedScrollStateHandle, ClippedScrollable,
ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, Dash, DropTarget, DropTargetData,
Empty, Flex, Highlight, Hoverable, MainAxisAlignment, MainAxisSize, MouseStateHandle,
OffsetPositioning, OffsetType, ParentAnchor, ParentElement, ParentOffsetBounds,
PositionedElementAnchor, PositionedElementOffsetBounds, PositioningAxis, Radius, SavePosition,
ScrollTarget, ScrollToPositionMode, ScrollbarWidth, Shrinkable, Stack, Text, XAxisAnchor,
YAxisAnchor,
};
use galaxyui::fonts::{Properties, Weight};
use galaxyui::keymap::FixedBinding;
use galaxyui::platform::{Cursor, OperatingSystem};
use galaxyui::ui_components::button::ButtonVariant;
use galaxyui::ui_components::components::{Coords, UiComponent, UiComponentStyles};
use galaxyui::units::IntoPixels;
use galaxyui::{
AppContext, BlurContext, Element, Entity, ModelHandle, SingletonEntity, TypedActionView,
UpdateView, View, ViewContext, ViewHandle, WindowId,
};
use std::{any::Any, collections::HashMap, sync::Arc};
use super::cloud_object_naming_dialog::CloudObjectNamingDialog;
use super::drive_helpers::{
has_feature_gated_anonymous_user_reached_env_var_limit,
has_feature_gated_anonymous_user_reached_notebook_limit,
has_feature_gated_anonymous_user_reached_workflow_limit,
};
use super::empty_trash_confirmation_dialog::{
EmptyTrashConfirmationDialog, EmptyTrashConfirmationEvent,
};
use super::folders::CloudFolder;
use super::items::ai_fact_collection::WarpDriveAIFactCollection;
use super::items::item::{tools_panel_menu_direction, ItemStates, WarpDriveRow};
use super::items::mcp_server_collection::WarpDriveMCPServerCollection;
use super::items::WarpDriveItemId;
use super::settings::WarpDriveSettings;
use super::sharing::dialog::{SharingDialog, SharingDialogEvent};
use super::sharing::{ContentEditability, ShareableObject};
use super::{CloudObjectTypeAndId, DriveObjectType, DriveSortOrder};
use crate::ai::document::ai_document_model::AIDocumentId;
use crate::ai::facts::{AIFact, AIMemory};
use crate::appearance::Appearance;
use crate::auth::auth_manager::{AuthManager, LoginGatedFeature};
use crate::auth::auth_state::AuthState;
use crate::auth::auth_view_modal::AuthViewVariant;
use crate::auth::AuthStateProvider;
use crate::cloud_object::model::persistence::{CloudModel, CloudModelEvent};
use crate::cloud_object::model::view::{CloudViewModel, CloudViewModelEvent, UpdateTimestamp};
use crate::cloud_object::{
CloudObject, CloudObjectEventEntrypoint, CloudObjectLocation, CloudObjectSyncStatus,
GenericCloudObject, GenericStringObjectFormat, JsonObjectType, NumInFlightRequests, ObjectType,
Space,
};
use crate::drive::panel::DrivePanelAction;
use crate::editor::{EditorView, Event as EditorEvent, SingleLineEditorOptions};
use crate::env_vars::CloudEnvVarCollection;
use crate::features::FeatureFlag;
use crate::menu::{Event, Menu, MenuItem, MenuItemFields};
use crate::network::NetworkStatus;
use crate::notebooks::CloudNotebookModel;
use crate::server::cloud_objects::update_manager::{
FetchSingleObjectOption, InitiatedBy, UpdateManager,
};
use crate::server::ids::{ClientId, ObjectUid, ServerId, SyncId};
use crate::server::sync_queue::SyncQueue;
use crate::server::telemetry::{
AnonymousUserSignupEntrypoint, SharingDialogSource, TelemetryEvent,
};
use crate::settings::app_installation_detection::{
UserAppInstallDetectionSettings, UserAppInstallStatus,
};
use crate::ui_components::blended_colors;
use crate::ui_components::buttons::{highlight, icon_button};
use crate::ui_components::icons::{Icon, ICON_DIMENSIONS};
use crate::ui_components::menu_button::{icon_button_with_context_menu, MenuDirection};
#[cfg(target_family = "wasm")]
use crate::uri::web_intent_parser::open_url_on_desktop;
use crate::util::color::coloru_with_opacity;
use crate::view_components::{Dropdown, DropdownItem};
use crate::workflows::{CloudWorkflow, WorkflowViewMode};
use crate::workspace::active_terminal_in_window;
use crate::workspaces::update_manager::TeamUpdateManager;
use crate::workspaces::user_workspaces::UserWorkspaces;
use crate::workspaces::workspace::WorkspaceUid;
use crate::{report_if_error, send_telemetry_from_ctx, ObjectActions};
const WARP_DRIVE_TITLE: &str = "Galaxy Drive";
@@ -242,7 +233,7 @@ pub enum DriveIndexSection {
JoinTeam,
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub enum DriveIndexAction {
OpenObject(CloudObjectTypeAndId),
OpenWorkflowInPane {
@@ -415,7 +406,6 @@ impl DriveIndexAction {
impl From<&DriveIndexAction> for LoginGatedFeature {
fn from(val: &DriveIndexAction) -> LoginGatedFeature {
use DriveIndexAction::*;
match val {
OpenTeamSettingsPage => "Open Team Settings",
ViewPlans { .. } => "View Plans",
@@ -5659,5 +5649,5 @@ impl TypedActionView for DriveIndex {
}
#[cfg(test)]
#[path = "index_test.rs"]
#[path = "index_tests.rs"]
mod tests;
@@ -1,38 +1,37 @@
use cloud_objects::cloud_object::ServerPermissions;
use galaxy_core::ui::appearance::Appearance;
use galaxy_server_client::cloud_object::ServerPermissions;
use galaxyui::{
platform::WindowStyle, AddSingletonModel, App, SingletonEntity, TypedActionView, ViewHandle,
};
use crate::{
ai::blocklist::BlocklistAIHistoryModel,
auth::{auth_manager::AuthManager, AuthStateProvider},
cloud_object::{
model::{actions::ObjectActions, persistence::CloudModel, view::CloudViewModel},
CloudObjectSyncStatus, ObjectIdType, ObjectType, Owner, ServerCreationInfo, Space,
},
drive::{items::WarpDriveItemId, CloudObjectTypeAndId},
menu::MenuItem,
network::NetworkStatus,
notebooks::{CloudNotebook, CloudNotebookModel},
server::{
cloud_objects::update_manager::UpdateManager,
ids::{ClientId, ServerIdAndType, SyncId},
server_api::ServerApiProvider,
sync_queue::{QueueItem, SyncQueue},
telemetry::context_provider::AppTelemetryContextProvider,
},
settings_view::keybindings::KeybindingChangedNotifier,
terminal::shared_session::permissions_manager::SessionPermissionsManager,
test_util::settings::initialize_settings_for_tests,
workflows::{workflow::Workflow, CloudWorkflow, CloudWorkflowModel},
workspaces::{
team_tester::TeamTesterStatus, user_profiles::UserProfiles, user_workspaces::UserWorkspaces,
},
Assets,
};
use galaxyui::platform::WindowStyle;
use galaxyui::{AddSingletonModel, App, SingletonEntity, TypedActionView, ViewHandle};
use super::{DriveIndex, DriveIndexAction};
use crate::ai::blocklist::BlocklistAIHistoryModel;
use crate::auth::auth_manager::AuthManager;
use crate::auth::AuthStateProvider;
use crate::cloud_object::model::actions::ObjectActions;
use crate::cloud_object::model::persistence::CloudModel;
use crate::cloud_object::model::view::CloudViewModel;
use crate::cloud_object::{
CloudObjectSyncStatus, ObjectIdType, ObjectType, Owner, ServerCreationInfo, Space,
};
use crate::drive::items::WarpDriveItemId;
use crate::drive::CloudObjectTypeAndId;
use crate::menu::MenuItem;
use crate::network::NetworkStatus;
use crate::notebooks::{CloudNotebook, CloudNotebookModel};
use crate::server::cloud_objects::update_manager::UpdateManager;
use crate::server::ids::{ClientId, ServerIdAndType, SyncId};
use crate::server::server_api::ServerApiProvider;
use crate::server::sync_queue::{QueueItem, SyncQueue};
use crate::server::telemetry::context_provider::AppTelemetryContextProvider;
use crate::settings_view::keybindings::KeybindingChangedNotifier;
use crate::terminal::shared_session::permissions_manager::SessionPermissionsManager;
use crate::test_util::settings::initialize_settings_for_tests;
use crate::workflows::workflow::Workflow;
use crate::workflows::{CloudWorkflow, CloudWorkflowModel};
use crate::workspaces::team_tester::TeamTesterStatus;
use crate::workspaces::user_profiles::UserProfiles;
use crate::workspaces::user_workspaces::UserWorkspaces;
use crate::ASSETS;
fn initialize_app(app: &mut App) {
initialize_settings_for_tests(app);
@@ -121,7 +120,7 @@ fn label_for_menu_item(item: &MenuItem<DriveIndexAction>) -> &str {
#[test]
fn test_retry_menu_item_visibility() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let index = create_index(&mut app);
let sync_id = create_workflow(&mut app);
@@ -171,7 +170,7 @@ fn test_retry_menu_item_visibility() {
#[test]
fn test_retry_menu_item_logic() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
let index = create_index(&mut app);
let sync_id = create_workflow(&mut app);
+10 -14
View File
@@ -1,19 +1,15 @@
use galaxyui::{
elements::{Container, Flex, MouseStateHandle, ParentElement},
fonts::Weight,
ui_components::components::{UiComponent, UiComponentStyles},
AppContext, Element,
};
use crate::{
ai::facts::{AIFact, AIMemory, CloudAIFact},
appearance::Appearance,
cloud_object::CloudObjectMetadata,
drive::{index::DriveIndexAction, CloudObjectTypeAndId, DriveObjectType},
themes::theme::Fill,
};
use galaxyui::elements::{Container, Flex, MouseStateHandle, ParentElement};
use galaxyui::fonts::Weight;
use galaxyui::ui_components::components::{UiComponent, UiComponentStyles};
use galaxyui::{AppContext, Element};
use super::{WarpDriveItem, WarpDriveItemId};
use crate::ai::facts::{AIFact, AIMemory, CloudAIFact};
use crate::appearance::Appearance;
use crate::cloud_object::CloudObjectMetadata;
use crate::drive::index::DriveIndexAction;
use crate::drive::{CloudObjectTypeAndId, DriveObjectType};
use crate::themes::theme::Fill;
#[derive(Clone)]
pub struct WarpDriveAIFact {
+8 -9
View File
@@ -1,14 +1,13 @@
use galaxyui::{elements::MouseStateHandle, AppContext, Element};
use crate::{
appearance::Appearance,
cloud_object::CloudObjectMetadata,
drive::{index::DriveIndexAction, DriveObjectType},
server::ids::ClientId,
themes::theme::Fill,
};
use galaxyui::elements::MouseStateHandle;
use galaxyui::{AppContext, Element};
use super::{WarpDriveItem, WarpDriveItemId};
use crate::appearance::Appearance;
use crate::cloud_object::CloudObjectMetadata;
use crate::drive::index::DriveIndexAction;
use crate::drive::DriveObjectType;
use crate::server::ids::ClientId;
use crate::themes::theme::Fill;
#[derive(Clone)]
pub struct WarpDriveAIFactCollection {
+12 -18
View File
@@ -1,24 +1,18 @@
use galaxy_core::context_flag::ContextFlag;
use galaxyui::{
elements::{Clipped, Container, Flex, MouseStateHandle, ParentElement},
fonts::Weight,
ui_components::components::{UiComponent, UiComponentStyles},
AppContext, Element, SingletonEntity,
};
use itertools::Itertools;
use crate::{
appearance::Appearance,
cloud_object::{
model::actions::{ObjectActionType, ObjectActions},
CloudObjectMetadata,
},
drive::{index::DriveIndexAction, CloudObjectTypeAndId, DriveObjectType},
env_vars::{CloudEnvVarCollection, EnvVarValue},
themes::theme::Fill,
};
use galaxy_core::context_flag::ContextFlag;
use galaxyui::elements::{Clipped, Container, Flex, MouseStateHandle, ParentElement};
use galaxyui::fonts::Weight;
use galaxyui::ui_components::components::{UiComponent, UiComponentStyles};
use galaxyui::{AppContext, Element, SingletonEntity};
use super::{WarpDriveItem, WarpDriveItemId};
use crate::appearance::Appearance;
use crate::cloud_object::model::actions::{ObjectActionType, ObjectActions};
use crate::cloud_object::CloudObjectMetadata;
use crate::drive::index::DriveIndexAction;
use crate::drive::{CloudObjectTypeAndId, DriveObjectType};
use crate::env_vars::{CloudEnvVarCollection, EnvVarValue};
use crate::themes::theme::Fill;
#[derive(Clone)]
pub struct WarpDriveEnvVarCollection {
+10 -12
View File
@@ -1,18 +1,16 @@
use galaxy_core::features::FeatureFlag;
use galaxyui::{elements::MouseStateHandle, AppContext, Element};
use crate::{
appearance::Appearance,
cloud_object::CloudObjectMetadata,
drive::{
cloud_object_styling::warp_drive_icon_color, folders::CloudFolder, index::DriveIndexAction,
CloudObjectTypeAndId, DriveObjectType,
},
themes::theme::Fill,
ui_components::icons::Icon,
};
use galaxyui::elements::MouseStateHandle;
use galaxyui::{AppContext, Element};
use super::{WarpDriveItem, WarpDriveItemId};
use crate::appearance::Appearance;
use crate::cloud_object::CloudObjectMetadata;
use crate::drive::cloud_object_styling::warp_drive_icon_color;
use crate::drive::folders::CloudFolder;
use crate::drive::index::DriveIndexAction;
use crate::drive::{CloudObjectTypeAndId, DriveObjectType};
use crate::themes::theme::Fill;
use crate::ui_components::icons::Icon;
#[derive(Clone)]
pub struct WarpDriveFolder {
+39 -53
View File
@@ -1,57 +1,43 @@
use galaxyui::{
elements::{
AcceptedByDropTarget, Border, ChildAnchor, ConstrainedBox, Container, CornerRadius,
CrossAxisAlignment, Draggable, DraggableState, DropShadow, Empty, Flex, Hoverable,
MainAxisSize, MouseStateHandle, OffsetPositioning, ParentAnchor, ParentElement,
ParentOffsetBounds, Radius, SavePosition, Shrinkable, SizeConstraintCondition,
SizeConstraintSwitch, Stack,
},
fonts::Weight,
platform::Cursor,
presenter::PositionCache,
ui_components::{
components::{UiComponent, UiComponentStyles},
text::Span,
},
AppContext, Element, SingletonEntity, ViewHandle,
use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::vector::Vector2F;
use galaxyui::elements::{
AcceptedByDropTarget, Border, ChildAnchor, ConstrainedBox, Container, CornerRadius,
CrossAxisAlignment, Draggable, DraggableState, DropShadow, Empty, Flex, Hoverable,
MainAxisSize, MouseStateHandle, OffsetPositioning, ParentAnchor, ParentElement,
ParentOffsetBounds, Radius, SavePosition, Shrinkable, SizeConstraintCondition,
SizeConstraintSwitch, Stack,
};
use pathfinder_geometry::{rect::RectF, vector::Vector2F};
use crate::{
cloud_object::{
model::{persistence::CloudModel, view::CloudViewModel},
CloudObject, CloudObjectMetadataExt, Owner,
},
drive::CloudObjectTypeAndId,
workspaces::{user_profiles::UserProfiles, user_workspaces::UserWorkspaces},
};
use crate::workspace::header_toolbar_item::HeaderToolbarItemKind;
use crate::workspace::tab_settings::TabSettings;
use crate::{
appearance::Appearance,
cloud_object::Space,
drive::{
index::{
DriveIndexAction, AUTOSCROLL_DETECTION_DISTANCE, AUTOSCROLL_SPEED_MULTIPLIER,
DRIVE_INDEX_VIEW_POSITION_ID, FOLDER_DEPTH_INDENT, INDEX_CONTENT_MARGIN_LEFT,
ITEM_FONT_SIZE, ITEM_MARGIN_BOTTOM, ITEM_PADDING_HORIZONTAL, ITEM_PADDING_VERTICAL,
},
panel::WARP_DRIVE_POSITION_ID,
},
menu::Menu,
ui_components::{
blended_colors,
icons::{Icon, ICON_DIMENSIONS},
menu_button::{
highlight_icon_button_with_context_menu_drive, icon_button_with_context_menu_drive,
MenuDirection,
},
},
};
use crate::{cloud_object::CloudObjectLocation, drive::items::WarpDriveItem};
use galaxyui::fonts::Weight;
use galaxyui::platform::Cursor;
use galaxyui::presenter::PositionCache;
use galaxyui::ui_components::components::{UiComponent, UiComponentStyles};
use galaxyui::ui_components::text::Span;
use galaxyui::{AppContext, Element, SingletonEntity, ViewHandle};
use super::WarpDriveItemId;
use crate::appearance::Appearance;
use crate::cloud_object::model::persistence::CloudModel;
use crate::cloud_object::model::view::CloudViewModel;
use crate::cloud_object::{CloudObject, CloudObjectLocation, CloudObjectMetadataExt, Owner, Space};
use crate::drive::index::{
DriveIndexAction, AUTOSCROLL_DETECTION_DISTANCE, AUTOSCROLL_SPEED_MULTIPLIER,
DRIVE_INDEX_VIEW_POSITION_ID, FOLDER_DEPTH_INDENT, INDEX_CONTENT_MARGIN_LEFT, ITEM_FONT_SIZE,
ITEM_MARGIN_BOTTOM, ITEM_PADDING_HORIZONTAL, ITEM_PADDING_VERTICAL,
};
use crate::drive::items::WarpDriveItem;
use crate::drive::panel::WARP_DRIVE_POSITION_ID;
use crate::drive::CloudObjectTypeAndId;
use crate::menu::Menu;
use crate::ui_components::blended_colors;
use crate::ui_components::icons::{Icon, ICON_DIMENSIONS};
use crate::ui_components::menu_button::{
highlight_icon_button_with_context_menu_drive, icon_button_with_context_menu_drive,
MenuDirection,
};
use crate::workspace::header_toolbar_item::HeaderToolbarItemKind;
use crate::workspace::tab_settings::TabSettings;
use crate::workspaces::user_profiles::UserProfiles;
use crate::workspaces::user_workspaces::UserWorkspaces;
pub(crate) fn tools_panel_menu_direction(app: &AppContext) -> MenuDirection {
let config = TabSettings::as_ref(app)
@@ -134,7 +120,7 @@ pub struct WarpDriveRow<'a> {
space: Space,
item_states: ItemStates,
overflow_button: Box<dyn Element>,
/// how many levels into a folder hierachy the row is.
/// how many levels into a folder hierarchy the row is.
/// 0 means the object is in the root directory.
folder_depth: usize,
sync_icon: Option<Box<dyn Element>>,
@@ -898,7 +884,7 @@ impl UiComponent for WarpDriveRow<'_> {
return;
}
// Otherwize, check to see if we should scroll downwards (revealing more content at the
// Otherwise, check to see if we should scroll downwards (revealing more content at the
// bottom).
// This computes the distance between the bottom of the *currently-dragging* item
// and the bottom of the drive index view. If distance < 10, emit a scroll event.
+9 -8
View File
@@ -1,12 +1,13 @@
use warpui::elements::MouseStateHandle;
use warpui::{AppContext, Element};
use super::{WarpDriveItem, WarpDriveItemId};
use crate::{
ai::mcp::CloudMCPServer,
appearance::Appearance,
cloud_object::CloudObjectMetadata,
drive::{index::DriveIndexAction, CloudObjectTypeAndId, DriveObjectType},
themes::theme::Fill,
};
use galaxyui::{elements::MouseStateHandle, AppContext, Element};
use crate::ai::mcp::CloudMCPServer;
use crate::appearance::Appearance;
use crate::cloud_object::CloudObjectMetadata;
use crate::drive::index::DriveIndexAction;
use crate::drive::{CloudObjectTypeAndId, DriveObjectType};
use crate::themes::theme::Fill;
#[derive(Clone)]
pub struct WarpDriveMCPServer {
+8 -9
View File
@@ -1,14 +1,13 @@
use galaxyui::{elements::MouseStateHandle, AppContext, Element};
use crate::{
appearance::Appearance,
cloud_object::CloudObjectMetadata,
drive::{index::DriveIndexAction, DriveObjectType},
server::ids::ClientId,
themes::theme::Fill,
};
use galaxyui::elements::MouseStateHandle;
use galaxyui::{AppContext, Element};
use super::{WarpDriveItem, WarpDriveItemId};
use crate::appearance::Appearance;
use crate::cloud_object::CloudObjectMetadata;
use crate::drive::index::DriveIndexAction;
use crate::drive::DriveObjectType;
use crate::server::ids::ClientId;
use crate::themes::theme::Fill;
#[derive(Clone)]
pub struct WarpDriveMCPServerCollection {
+9 -13
View File
@@ -1,17 +1,13 @@
use galaxyui::{elements::MouseStateHandle, AppContext, Element};
use galaxyui::elements::MouseStateHandle;
use galaxyui::{AppContext, Element};
use crate::{
appearance::Appearance,
cloud_object::{CloudObjectMetadata, Space},
themes::theme::Fill,
ui_components::icons::Icon,
};
use super::{
cloud_object_styling::warp_drive_icon_color,
index::{warp_drive_section_header_position_id, DriveIndexAction, DriveIndexSection},
CloudObjectTypeAndId, DriveObjectType,
};
use super::cloud_object_styling::warp_drive_icon_color;
use super::index::{warp_drive_section_header_position_id, DriveIndexAction, DriveIndexSection};
use super::{CloudObjectTypeAndId, DriveObjectType};
use crate::appearance::Appearance;
use crate::cloud_object::{CloudObjectMetadata, Space};
use crate::themes::theme::Fill;
use crate::ui_components::icons::Icon;
pub mod ai_fact;
pub mod ai_fact_collection;
+10 -14
View File
@@ -1,19 +1,15 @@
use galaxyui::{
elements::{Flex, MouseStateHandle, ParentElement},
fonts::Weight,
ui_components::components::{UiComponent, UiComponentStyles},
AppContext, Element,
};
use crate::{
appearance::Appearance,
cloud_object::CloudObjectMetadata,
drive::{index::DriveIndexAction, CloudObjectTypeAndId, DriveObjectType},
notebooks::CloudNotebook,
themes::theme::Fill,
};
use galaxyui::elements::{Flex, MouseStateHandle, ParentElement};
use galaxyui::fonts::Weight;
use galaxyui::ui_components::components::{UiComponent, UiComponentStyles};
use galaxyui::{AppContext, Element};
use super::{WarpDriveItem, WarpDriveItemId};
use crate::appearance::Appearance;
use crate::cloud_object::CloudObjectMetadata;
use crate::drive::index::DriveIndexAction;
use crate::drive::{CloudObjectTypeAndId, DriveObjectType};
use crate::notebooks::CloudNotebook;
use crate::themes::theme::Fill;
#[derive(Clone)]
pub struct WarpDriveNotebook {
+7 -8
View File
@@ -1,13 +1,12 @@
use galaxyui::{elements::MouseStateHandle, Element};
use crate::{
appearance::Appearance,
cloud_object::{CloudObjectMetadata, Space},
drive::{index::DriveIndexAction, DriveObjectType},
themes::theme::Fill,
};
use galaxyui::elements::MouseStateHandle;
use galaxyui::Element;
use super::{WarpDriveItem, WarpDriveItemId};
use crate::appearance::Appearance;
use crate::cloud_object::{CloudObjectMetadata, Space};
use crate::drive::index::DriveIndexAction;
use crate::drive::DriveObjectType;
use crate::themes::theme::Fill;
#[derive(Clone)]
pub struct WarpDriveSpace {
+11 -17
View File
@@ -1,23 +1,17 @@
use galaxy_core::context_flag::ContextFlag;
use galaxyui::{
elements::{Container, Flex, MouseStateHandle, ParentElement},
fonts::Weight,
ui_components::components::{UiComponent, UiComponentStyles},
AppContext, Element, SingletonEntity,
};
use crate::{
appearance::Appearance,
cloud_object::{
model::actions::{ObjectActionType, ObjectActions},
CloudObjectMetadata,
},
drive::{index::DriveIndexAction, CloudObjectTypeAndId, DriveObjectType},
themes::theme::Fill,
workflows::{CloudWorkflow, WorkflowViewMode},
};
use galaxyui::elements::{Container, Flex, MouseStateHandle, ParentElement};
use galaxyui::fonts::Weight;
use galaxyui::ui_components::components::{UiComponent, UiComponentStyles};
use galaxyui::{AppContext, Element, SingletonEntity};
use super::{WarpDriveItem, WarpDriveItemId};
use crate::appearance::Appearance;
use crate::cloud_object::model::actions::{ObjectActionType, ObjectActions};
use crate::cloud_object::CloudObjectMetadata;
use crate::drive::index::DriveIndexAction;
use crate::drive::{CloudObjectTypeAndId, DriveObjectType};
use crate::themes::theme::Fill;
use crate::workflows::{CloudWorkflow, WorkflowViewMode};
#[derive(Clone)]
pub struct WarpDriveWorkflow {
+24 -161
View File
@@ -13,27 +13,38 @@ pub mod settings;
pub mod sharing;
pub mod workflows;
use std::{cmp::Ordering, fmt};
use std::cmp::Ordering;
use std::fmt;
use galaxy_core::user_preferences::GetUserPreferences as _;
use galaxyui::AppContext;
pub use cloud_objects::drive::CloudObjectTypeAndId;
pub use index::DriveIndexVariant;
pub use panel::{DrivePanel, DrivePanelEvent};
use serde::{Deserialize, Serialize};
use crate::{
cloud_object::{
model::view::{CloudViewModel, UpdateTimestamp},
CloudObject, GenericStringObjectFormat, ObjectIdType, ObjectType,
},
server::ids::{HashedSqliteId, ObjectUid, ServerId, SyncId},
ui_components::icons::Icon,
workflows::CloudWorkflow,
};
use crate::cloud_object::model::view::{CloudViewModel, UpdateTimestamp};
use crate::cloud_object::{CloudObject, ObjectType};
use crate::server::ids::ServerId;
use crate::ui_components::icons::Icon;
use crate::workflows::CloudWorkflow;
type SortByComparator<'a> = dyn FnMut(&&dyn CloudObject, &&dyn CloudObject) -> Ordering + 'a;
#[derive(Copy, Clone, Debug)]
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct OpenWarpDriveObjectSettings {
/// The folder that should be focused in the Warp Drive when the object is opened.
pub focused_folder_id: Option<ServerId>,
/// The email of the user to invite to the object, if the object is being opened via the request access flow.
pub invitee_email: Option<String>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct OpenWarpDriveObjectArgs {
pub object_type: ObjectType,
pub server_id: ServerId,
pub settings: OpenWarpDriveObjectSettings,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum DriveObjectType {
Workflow,
AgentModeWorkflow,
@@ -87,154 +98,6 @@ impl fmt::Display for DriveObjectType {
}
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct OpenGalaxyDriveObjectSettings {
/// The folder that should be focused in the Warp Drive when the object is opened.
pub focused_folder_id: Option<ServerId>,
/// The email of the user to invite to the object, if the object is being opened via the request access flow.
pub invitee_email: Option<String>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct OpenGalaxyDriveObjectArgs {
pub object_type: ObjectType,
pub server_id: ServerId,
pub settings: OpenGalaxyDriveObjectSettings,
}
/// Enum to use to pass down type and id between actions to avoid multiplying actions whenever we
/// need to pass the object id etc.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum CloudObjectTypeAndId {
Notebook(SyncId),
Workflow(SyncId),
Folder(SyncId),
GenericStringObject {
object_type: GenericStringObjectFormat,
id: SyncId,
},
}
impl CloudObjectTypeAndId {
pub fn from_id_and_type(id: SyncId, object_type: ObjectType) -> Self {
match object_type {
ObjectType::Notebook => Self::Notebook(id),
ObjectType::Workflow => Self::Workflow(id),
ObjectType::Folder => Self::Folder(id),
ObjectType::GenericStringObject(format) => Self::GenericStringObject {
object_type: format,
id,
},
}
}
pub fn uid(self) -> ObjectUid {
match self {
Self::Notebook(id) => id.uid(),
Self::Workflow(id) => id.uid(),
Self::Folder(id) => id.uid(),
Self::GenericStringObject { id, .. } => id.uid(),
}
}
pub fn sync_id(self) -> SyncId {
match self {
Self::Notebook(id)
| Self::Workflow(id)
| Self::Folder(id)
| Self::GenericStringObject { id, .. } => id,
}
}
pub fn sqlite_uid_hash(self) -> HashedSqliteId {
match self {
CloudObjectTypeAndId::Notebook(id) => id.sqlite_uid_hash(ObjectIdType::Notebook),
CloudObjectTypeAndId::Workflow(id) => id.sqlite_uid_hash(ObjectIdType::Workflow),
CloudObjectTypeAndId::Folder(id) => id.sqlite_uid_hash(ObjectIdType::Folder),
CloudObjectTypeAndId::GenericStringObject { object_type: _, id } => {
id.sqlite_uid_hash(ObjectIdType::GenericStringObject)
}
}
}
pub fn object_id_type(&self) -> ObjectIdType {
match self {
CloudObjectTypeAndId::Notebook(_) => ObjectIdType::Notebook,
CloudObjectTypeAndId::Workflow(_) => ObjectIdType::Workflow,
CloudObjectTypeAndId::GenericStringObject { .. } => ObjectIdType::GenericStringObject,
CloudObjectTypeAndId::Folder(_) => ObjectIdType::Folder,
}
}
pub fn object_type(&self) -> ObjectType {
match self {
CloudObjectTypeAndId::Notebook(_) => ObjectType::Notebook,
CloudObjectTypeAndId::Workflow(_) => ObjectType::Workflow,
CloudObjectTypeAndId::Folder(_) => ObjectType::Folder,
CloudObjectTypeAndId::GenericStringObject { object_type, .. } => {
ObjectType::GenericStringObject(*object_type)
}
}
}
pub fn as_folder_id(self) -> Option<SyncId> {
match self {
CloudObjectTypeAndId::Notebook(_) => None,
CloudObjectTypeAndId::Workflow(_) => None,
CloudObjectTypeAndId::GenericStringObject { .. } => None,
CloudObjectTypeAndId::Folder(f) => Some(f),
}
}
pub fn as_notebook_id(self) -> Option<SyncId> {
match self {
CloudObjectTypeAndId::Notebook(id) => Some(id),
_ => None,
}
}
pub fn as_generic_string_object_id(self) -> Option<SyncId> {
match self {
CloudObjectTypeAndId::GenericStringObject { object_type: _, id } => Some(id),
_ => None,
}
}
pub fn has_server_id(self) -> bool {
matches!(
self,
CloudObjectTypeAndId::Notebook(SyncId::ServerId(_))
| CloudObjectTypeAndId::Workflow(SyncId::ServerId(_))
| CloudObjectTypeAndId::Folder(SyncId::ServerId(_))
| CloudObjectTypeAndId::GenericStringObject {
id: SyncId::ServerId(_),
..
}
)
}
pub fn server_id(self) -> Option<ServerId> {
match self {
CloudObjectTypeAndId::Notebook(SyncId::ServerId(notebook_id)) => Some(notebook_id),
CloudObjectTypeAndId::Workflow(SyncId::ServerId(workflow_id)) => Some(workflow_id),
CloudObjectTypeAndId::Folder(SyncId::ServerId(folder_id)) => Some(folder_id),
CloudObjectTypeAndId::GenericStringObject {
id: SyncId::ServerId(json_object_id),
..
} => Some(json_object_id),
_ => None,
}
}
pub fn drive_row_position_id(self) -> String {
format!("WarpDriveRow_{}", self.uid())
}
pub fn from_generic_string_object(object_type: GenericStringObjectFormat, id: SyncId) -> Self {
Self::GenericStringObject { object_type, id }
}
}
pub fn should_auto_open_welcome_folder(app: &mut AppContext) -> bool {
app.private_user_preferences()
.read_value(settings::HAS_AUTO_OPENED_WELCOME_FOLDER)
+30 -30
View File
@@ -1,39 +1,39 @@
use futures::Future;
use galaxyui::elements::{
Align, Flex, Hoverable, MouseStateHandle, ParentElement, SavePosition, Shrinkable,
};
use galaxyui::presenter::ChildView;
use galaxyui::windowing::{StateEvent, WindowManager};
use galaxyui::{
elements::{Align, Flex, Hoverable, MouseStateHandle, ParentElement, SavePosition, Shrinkable},
presenter::ChildView,
windowing::{StateEvent, WindowManager},
AppContext, Element, Entity, FocusContext, SingletonEntity, TypedActionView, View, ViewContext,
ViewHandle,
};
use crate::{
ai::{document::ai_document_model::AIDocumentId, facts::CloudAIFactModel},
cloud_object::{
model::{persistence::CloudModel, view::CloudViewModel},
CloudObjectEventEntrypoint, GenericStringObjectFormat, JsonObjectType, Owner, Space,
},
env_vars::{manager::EnvVarCollectionSource, CloudEnvVarCollection},
notebooks::{manager::NotebookSource, CloudNotebook},
server::{
cloud_objects::update_manager::{InitiatedBy, UpdateManager},
ids::{ClientId, ServerId, SyncId},
telemetry::SharingDialogSource,
},
workflows::{manager::WorkflowOpenSource, CloudWorkflow, WorkflowViewMode},
workspaces::user_workspaces::UserWorkspaces,
use super::drive_helpers::{
has_feature_gated_anonymous_user_reached_env_var_limit,
has_feature_gated_anonymous_user_reached_notebook_limit,
has_feature_gated_anonymous_user_reached_workflow_limit,
};
use super::{
drive_helpers::{
has_feature_gated_anonymous_user_reached_env_var_limit,
has_feature_gated_anonymous_user_reached_notebook_limit,
has_feature_gated_anonymous_user_reached_workflow_limit,
},
index::{DriveIndex, DriveIndexAction, DriveIndexEvent},
items::WarpDriveItemId,
CloudObjectTypeAndId, DriveObjectType,
use super::index::{DriveIndex, DriveIndexAction, DriveIndexEvent};
use super::items::WarpDriveItemId;
use super::{CloudObjectTypeAndId, DriveObjectType};
use crate::ai::document::ai_document_model::AIDocumentId;
use crate::ai::facts::CloudAIFactModel;
use crate::cloud_object::model::persistence::CloudModel;
use crate::cloud_object::model::view::CloudViewModel;
use crate::cloud_object::{
CloudObjectEventEntrypoint, GenericStringObjectFormat, JsonObjectType, Owner, Space,
};
use crate::env_vars::manager::EnvVarCollectionSource;
use crate::env_vars::CloudEnvVarCollection;
use crate::notebooks::manager::NotebookSource;
use crate::notebooks::CloudNotebook;
use crate::server::cloud_objects::update_manager::{InitiatedBy, UpdateManager};
use crate::server::ids::{ClientId, ServerId, SyncId};
use crate::server::telemetry::SharingDialogSource;
use crate::workflows::manager::WorkflowOpenSource;
use crate::workflows::{CloudWorkflow, WorkflowViewMode};
use crate::workspaces::user_workspaces::UserWorkspaces;
pub const MIN_SIDEBAR_WIDTH: f32 = 250.;
pub const MAX_SIDEBAR_WIDTH_RATIO: f32 = 0.75;
@@ -583,7 +583,7 @@ impl DrivePanel {
ctx.emit(DrivePanelEvent::OpenMCPServerCollection);
}
/// Recomputes and intializes the section states for the WD Index. This is needed after
/// Recomputes and initializes the section states for the WD Index. This is needed after
/// we directly change anything about the state of the index (such as folders being open/closed).
///
/// This should only be called if we immeidiately need to update and rely on the updated state.
@@ -735,5 +735,5 @@ pub(crate) mod styles {
}
#[cfg(test)]
#[path = "panel_test.rs"]
#[path = "panel_tests.rs"]
mod tests;
@@ -1,30 +1,27 @@
use galaxy_core::ui::appearance::Appearance;
use galaxyui::{platform::WindowStyle, App};
use crate::{
ai::blocklist::BlocklistAIHistoryModel,
auth::{auth_manager::AuthManager, AuthStateProvider},
cloud_object::{
model::{persistence::CloudModel, view::CloudViewModel},
Space,
},
drive::index::DriveIndexSection,
network::NetworkStatus,
server::{
cloud_objects::update_manager::UpdateManager, server_api::ServerApiProvider,
sync_queue::SyncQueue, telemetry::context_provider::AppTelemetryContextProvider,
},
settings_view::keybindings::KeybindingChangedNotifier,
terminal::{
resizable_data::ResizableData,
shared_session::permissions_manager::SessionPermissionsManager,
},
test_util::settings::initialize_settings_for_tests,
workspaces::{team_tester::TeamTesterStatus, user_workspaces::UserWorkspaces},
Assets, ObjectActions,
};
use galaxyui::platform::WindowStyle;
use galaxyui::App;
use super::DrivePanel;
use crate::ai::blocklist::BlocklistAIHistoryModel;
use crate::auth::auth_manager::AuthManager;
use crate::auth::AuthStateProvider;
use crate::cloud_object::model::persistence::CloudModel;
use crate::cloud_object::model::view::CloudViewModel;
use crate::cloud_object::Space;
use crate::drive::index::DriveIndexSection;
use crate::network::NetworkStatus;
use crate::server::cloud_objects::update_manager::UpdateManager;
use crate::server::server_api::ServerApiProvider;
use crate::server::sync_queue::SyncQueue;
use crate::server::telemetry::context_provider::AppTelemetryContextProvider;
use crate::settings_view::keybindings::KeybindingChangedNotifier;
use crate::terminal::resizable_data::ResizableData;
use crate::terminal::shared_session::permissions_manager::SessionPermissionsManager;
use crate::test_util::settings::initialize_settings_for_tests;
use crate::workspaces::team_tester::TeamTesterStatus;
use crate::workspaces::user_workspaces::UserWorkspaces;
use crate::{ObjectActions, ASSETS};
fn initialize_app(app: &mut App) {
initialize_settings_for_tests(app);
@@ -52,7 +49,7 @@ fn initialize_app(app: &mut App) {
#[test]
fn test_warp_drive_sections_with_no_team() {
App::test(Assets, |mut app| async move {
App::test(ASSETS, |mut app| async move {
initialize_app(&mut app);
// Instead of being in the panel module and depending on DrivePanel, this test should be in the index module.
+2 -1
View File
@@ -1,5 +1,6 @@
use settings::macros::define_settings_group;
use settings::{RespectUserSyncSetting, SupportedPlatforms, SyncToCloud};
use galaxy_core::features::FeatureFlag;
use settings::{macros::define_settings_group, SupportedPlatforms, SyncToCloud};
use super::DriveSortOrder;
+9 -11
View File
@@ -1,19 +1,17 @@
//! Support for displaying inherited ACLs.
use galaxy_core::ui::appearance::Appearance;
use galaxyui::{
elements::{CrossAxisAlignment, Flex, MouseStateHandle, ParentElement as _},
ui_components::components::UiComponent as _,
AppContext, Element, SingletonEntity as _,
};
use galaxyui::elements::{CrossAxisAlignment, Flex, MouseStateHandle, ParentElement as _};
use galaxyui::ui_components::components::UiComponent as _;
use galaxyui::{AppContext, Element, SingletonEntity as _};
use super::style;
use crate::{
cloud_object::{model::persistence::CloudModel, ServerObjectContainer},
drive::CloudObjectTypeAndId,
server::{ids::SyncId, telemetry::SharingDialogSource},
workspace::WorkspaceAction,
};
use crate::cloud_object::model::persistence::CloudModel;
use crate::cloud_object::ServerObjectContainer;
use crate::drive::CloudObjectTypeAndId;
use crate::server::ids::SyncId;
use crate::server::telemetry::SharingDialogSource;
use crate::workspace::WorkspaceAction;
/// UI state for inherited permissions.
pub struct InheritanceState {
+449 -87
View File
@@ -1,70 +1,66 @@
use std::borrow::Cow;
use email_address::EmailAddress;
use inheritance::{InheritanceDetails, InheritanceState};
use itertools::Itertools;
use pathfinder_color::ColorU;
use pathfinder_geometry::vector::vec2f;
use session_sharing_protocol::common::{Guest, PendingGuest, SessionId, TeamAclData};
use galaxy_core::ui::appearance::Appearance;
use galaxy_core::ui::theme::Fill as ThemeFill;
use warp_editor::editor::NavigationKey;
use warpui::clipboard::ClipboardContent;
use warpui::elements::{
Align, Border, ChildAnchor, ChildView, ConstrainedBox, Container, CornerRadius,
CrossAxisAlignment, Dismiss, Empty, Fill, Flex, Highlight, MainAxisAlignment, MainAxisSize,
MouseStateHandle, OffsetPositioning, ParentAnchor, ParentElement, PositionedElementAnchor,
PositionedElementOffsetBounds, Radius, SavePosition, ScrollStateHandle, Scrollable,
ScrollableElement, ScrollbarWidth, Shrinkable, Stack, UniformList, UniformListState,
};
use warpui::fonts::{Properties, Weight};
use warpui::keymap::FixedBinding;
use warpui::platform::{Cursor, SaveFilePickerConfiguration};
use warpui::ui_components::button::{ButtonVariant, TextAndIcon, TextAndIconAlignment};
use warpui::ui_components::components::{Coords, UiComponent, UiComponentStyles};
use warpui::{
AppContext, Element, Entity, FocusContext, SingletonEntity, TypedActionView, View, ViewContext,
ViewHandle, WeakViewHandle,
};
use super::qr_code::{qr_matrix_for_url, qr_png_for_url, QrMatrix, QUIET_ZONE_MODULES};
use super::{
style, ContentEditability, LinkSharingSubjectType, ShareableObject, SharingAccessLevel,
Subject, SubjectExt, TeamKind, UserKind,
};
use crate::ai::blocklist::{BlocklistAIHistoryEvent, BlocklistAIHistoryModel};
use crate::auth::AuthStateProvider;
use crate::cloud_object::model::persistence::CloudModel;
use crate::cloud_object::model::persistence::CloudModelEvent;
use crate::cloud_object::model::persistence::{CloudModel, CloudModelEvent};
use crate::cloud_object::model::view::CloudViewModel;
use crate::cloud_object::Owner;
use crate::cloud_object::{CloudObject, ServerGuestSubject};
use crate::cloud_object::{CloudObject, Owner, ServerGuestSubject};
use crate::editor::PropagateAndNoOpNavigationKeys;
use crate::menu::{self, Menu, MenuItem, MenuItemFields};
use crate::send_telemetry_from_ctx;
use crate::server::cloud_objects::update_manager::{
ObjectOperation, UpdateManager, UpdateManagerEvent,
};
use crate::server::ids::ServerId;
use crate::server::telemetry::CloudObjectTelemetryMetadata;
use crate::server::telemetry::OpenedSharingDialogEvent;
use crate::server::telemetry::SharingDialogSource;
use crate::server::telemetry::{
CloudObjectTelemetryMetadata, OpenedSharingDialogEvent, SharingDialogSource,
};
use crate::terminal::shared_session::permissions_manager::{
SessionPermissionsEvent, SessionPermissionsManager,
};
use crate::terminal::shared_session::SharedSessionActionSource;
use crate::terminal::TerminalView;
use crate::ui_components::buttons::icon_button_with_color;
use crate::ui_components::icons::Icon;
use crate::view_components::DismissibleToast;
use crate::word_block_editor::{
WordBlockEditorStyles, WordBlockEditorView, WordBlockEditorViewEvent, WordBlockLayout,
WordBlockStyles,
};
use crate::workspace::ToastStack;
use crate::workspace::{ToastStack, WorkspaceAction};
use crate::workspaces::user_workspaces::UserWorkspaces;
use crate::TelemetryEvent;
use email_address::EmailAddress;
use galaxy_core::ui::appearance::Appearance;
use galaxy_editor::editor::NavigationKey;
use galaxyui::elements::{
Align, ChildAnchor, ChildView, Fill, Highlight, MainAxisSize, MouseStateHandle,
OffsetPositioning, ParentAnchor, PositionedElementAnchor, PositionedElementOffsetBounds,
SavePosition, ScrollStateHandle, Scrollable, ScrollableElement, ScrollbarWidth, Shrinkable,
Stack, UniformList, UniformListState,
};
use galaxyui::fonts::{Properties, Weight};
use galaxyui::platform::Cursor;
use galaxyui::ui_components::button::{ButtonVariant, TextAndIcon, TextAndIconAlignment};
use galaxyui::ui_components::components::Coords;
use galaxyui::FocusContext;
use galaxyui::WeakViewHandle;
use galaxyui::{
clipboard::ClipboardContent,
elements::{
Border, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, Dismiss, Empty, Flex,
MainAxisAlignment, ParentElement, Radius,
},
keymap::FixedBinding,
ui_components::components::{UiComponent, UiComponentStyles},
AppContext, Element, Entity, SingletonEntity, TypedActionView, View, ViewContext, ViewHandle,
};
use inheritance::{InheritanceDetails, InheritanceState};
use itertools::Itertools;
use pathfinder_geometry::vector::vec2f;
use session_sharing_protocol::common::{Guest, PendingGuest, SessionId, TeamAclData};
use super::{
style, ContentEditability, LinkSharingSubjectType, ShareableObject, SharingAccessLevel,
Subject, SubjectExt, TeamKind, UserKind,
};
use crate::{send_telemetry_from_ctx, TelemetryEvent};
mod inheritance;
@@ -76,6 +72,11 @@ const EMAIL_CHIP_WIDTH: f32 = 100.;
const EMAIL_EDITOR_WIDTH: f32 = 100.;
const SHARING_DIALOG_WIDTH: f32 = 425.;
const QR_DIALOG_WIDTH: f32 = 400.;
const QR_CARD_SIZE: f32 = 192.;
const QR_VISUAL_SIZE: f32 = 160.;
const QR_ICON_BUTTON_SIZE: f32 = 32.;
const QR_EXPORT_SIZE: u32 = 1024;
const NO_ACCESS_LABEL: &str = "No access";
@@ -87,10 +88,22 @@ struct UiStateHandles {
link_sharing_menu_button: MouseStateHandle,
team_sharing_menu_button: MouseStateHandle,
copy_link_button: MouseStateHandle,
qr_code_button: MouseStateHandle,
qr_back_button: MouseStateHandle,
qr_copy_button: MouseStateHandle,
qr_download_button: MouseStateHandle,
qr_close_button: MouseStateHandle,
guest_list_state: UniformListState,
guest_scroll_state: ScrollStateHandle,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
enum SharingDialogMode {
#[default]
Access,
QrCode,
}
/// State for which menu is currently open.
///
/// This helps with two things:
@@ -173,6 +186,7 @@ pub struct SharingDialog {
ui_state_handles: UiStateHandles,
open_menu_state: OpenMenuState,
mode: SharingDialogMode,
}
#[derive(Debug, Clone)]
@@ -184,6 +198,9 @@ pub enum SharingDialogEvent {
pub enum SharingDialogAction {
Close,
CopyLink,
ShowQrCode,
BackToAccessDialog,
DownloadQrCode,
#[allow(dead_code)]
SetLinkPermissions(Option<SharingAccessLevel>),
ToggleLinkSharingMenu,
@@ -280,6 +297,7 @@ impl SharingDialog {
team_sharing_menu,
ui_state_handles: Default::default(),
open_menu_state: Default::default(),
mode: Default::default(),
}
}
@@ -367,6 +385,7 @@ impl SharingDialog {
/// Sets the target object whose ACLs are shown.
pub fn set_target(&mut self, target: Option<ShareableObject>, ctx: &mut ViewContext<Self>) {
self.target = target;
self.mode = SharingDialogMode::Access;
self.reset_invite_form(ctx);
self.refresh_object_permission_states(ctx);
ctx.notify();
@@ -382,6 +401,15 @@ impl SharingDialog {
.is_some_and(|target| matches!(target, ShareableObject::Session { .. }))
}
pub fn show_qr_code(&mut self, ctx: &mut ViewContext<Self>) {
if matches!(self.target, Some(ShareableObject::Session { .. })) {
self.set_open_menu(OpenMenuState::None, ctx);
self.mode = SharingDialogMode::QrCode;
ctx.focus_self();
ctx.notify();
}
}
/// Returns `true` if the target is an AI conversation that cannot be shared.
/// This happens when the conversation hasn't been synced to the cloud.
pub fn is_unsharable_conversation(&self, app: &AppContext) -> bool {
@@ -615,6 +643,7 @@ impl SharingDialog {
}
fn reset_editable_state(&mut self, ctx: &mut ViewContext<Self>) {
self.mode = SharingDialogMode::Access;
self.reset_invite_form(ctx);
ctx.notify();
}
@@ -675,7 +704,7 @@ impl SharingDialog {
guests: &[Guest],
pending_guests: &[PendingGuest],
) {
// We should only update the guests if the dialog is targetting the
// We should only update the guests if the dialog is targeting the
// correct session.
match self.target.as_ref() {
Some(ShareableObject::Session {
@@ -723,7 +752,7 @@ impl SharingDialog {
access_level: Option<SharingAccessLevel>,
ctx: &mut ViewContext<Self>,
) {
// Ensure we're targetting the correct session.
// Ensure we're targeting the correct session.
let Some(ShareableObject::Session {
session_id: target_session_id,
..
@@ -2386,9 +2415,317 @@ impl SharingDialog {
.finish()
}
fn target_link(&self, app: &AppContext) -> Option<String> {
self.target.as_ref().and_then(|target| target.link(app))
}
fn target_session_id(&self) -> Option<SessionId> {
match self.target {
Some(ShareableObject::Session { session_id, .. }) => Some(session_id),
_ => None,
}
}
fn qr_filename(&self) -> String {
match self.target_session_id() {
Some(session_id) => format!("warp-session-qr-code-{session_id}.png"),
None => "warp-session-qr-code.png".to_string(),
}
}
fn show_ephemeral_toast(
&self,
toast: DismissibleToast<WorkspaceAction>,
ctx: &mut ViewContext<Self>,
) {
let window_id = ctx.window_id();
ToastStack::handle(ctx).update(ctx, |toast_stack, ctx| {
toast_stack.add_ephemeral_toast(toast, window_id, ctx);
});
}
fn download_qr_code(&self, ctx: &mut ViewContext<Self>) {
let Some(url) = self.target_link(ctx) else {
self.show_ephemeral_toast(
DismissibleToast::error("Unable to download QR code.".to_string()),
ctx,
);
return;
};
let png = match qr_png_for_url(&url, QR_EXPORT_SIZE) {
Ok(png) => png,
Err(_) => {
self.show_ephemeral_toast(
DismissibleToast::error("Unable to download QR code.".to_string()),
ctx,
);
return;
}
};
ctx.open_save_file_picker(
move |path, me, ctx| {
let Some(path) = path else {
return;
};
me.write_qr_png(path, png, ctx);
},
SaveFilePickerConfiguration::new().with_default_filename(self.qr_filename()),
);
}
fn write_qr_png(&self, path: String, png: Vec<u8>, ctx: &mut ViewContext<Self>) {
ctx.spawn(
async move { async_fs::write(path, png).await },
|me, result, ctx| me.handle_qr_write_result(result, ctx),
);
}
fn handle_qr_write_result(&self, result: std::io::Result<()>, ctx: &mut ViewContext<Self>) {
match result {
Ok(()) => self.show_ephemeral_toast(
DismissibleToast::success("QR code downloaded.".to_string()),
ctx,
),
Err(_) => self.show_ephemeral_toast(
DismissibleToast::error("Unable to download QR code.".to_string()),
ctx,
),
}
}
fn render_footer_icon_button(
&self,
icon: Icon,
action: SharingDialogAction,
tooltip: &'static str,
mouse_state: MouseStateHandle,
appearance: &Appearance,
) -> Box<dyn Element> {
let button_background = appearance.theme().surface_2();
let button_foreground = appearance.theme().main_text_color(button_background);
let ui_builder = appearance.ui_builder().clone();
icon_button_with_color(
appearance,
icon,
false,
mouse_state,
ThemeFill::Solid(button_foreground.into()),
)
.with_tooltip(move || ui_builder.tool_tip(tooltip.to_string()).build().finish())
.with_style(UiComponentStyles {
width: Some(style::ACL_ITEM_HEIGHT),
height: Some(style::ACL_ITEM_HEIGHT),
padding: Some(Coords::uniform(4.)),
font_color: Some(button_foreground.into()),
background: Some(button_background.into()),
border_color: Some(style::form_border_color(appearance).into()),
border_radius: Some(CornerRadius::with_all(Radius::Pixels(4.))),
..Default::default()
})
.with_clicked_styles(UiComponentStyles {
background: Some(appearance.theme().surface_2().into()),
..Default::default()
})
.with_hovered_styles(UiComponentStyles {
background: Some(appearance.theme().surface_3().into()),
..Default::default()
})
.build()
.on_click(move |ctx, _, _| ctx.dispatch_typed_action(action.clone()))
.finish()
}
fn render_qr_matrix(&self, matrix: &QrMatrix) -> Box<dyn Element> {
let modules_with_quiet_zone = matrix.width().saturating_add(QUIET_ZONE_MODULES * 2);
let module_size = QR_VISUAL_SIZE / modules_with_quiet_zone as f32;
let mut column = Flex::column().with_main_axis_size(MainAxisSize::Max);
for y in 0..modules_with_quiet_zone {
let mut row = Flex::row().with_main_axis_size(MainAxisSize::Max);
for x in 0..modules_with_quiet_zone {
let matrix_x = x.saturating_sub(QUIET_ZONE_MODULES);
let matrix_y = y.saturating_sub(QUIET_ZONE_MODULES);
let is_dark = x >= QUIET_ZONE_MODULES
&& y >= QUIET_ZONE_MODULES
&& matrix_x < matrix.width()
&& matrix_y < matrix.width()
&& matrix.is_dark(matrix_x, matrix_y);
let color = if is_dark {
ColorU::black()
} else {
ColorU::white()
};
row.add_child(
ConstrainedBox::new(
Container::new(Empty::new().finish())
.with_background(color)
.finish(),
)
.with_width(module_size)
.with_height(module_size)
.finish(),
);
}
column.add_child(row.finish());
}
ConstrainedBox::new(column.finish())
.with_width(QR_VISUAL_SIZE)
.with_height(QR_VISUAL_SIZE)
.finish()
}
fn render_qr_header(&self, appearance: &Appearance) -> Box<dyn Element> {
let foreground = style::acl_primary_text_color(appearance);
let icon_button_styles = UiComponentStyles {
width: Some(QR_ICON_BUTTON_SIZE),
height: Some(QR_ICON_BUTTON_SIZE),
font_color: Some(foreground),
border_radius: Some(CornerRadius::with_all(Radius::Pixels(4.))),
..Default::default()
};
let icon_hover_styles = UiComponentStyles {
background: Some(appearance.theme().surface_2().into()),
..Default::default()
};
let back_button = icon_button_with_color(
appearance,
Icon::ArrowLeft,
false,
self.ui_state_handles.qr_back_button.clone(),
ThemeFill::Solid(foreground),
)
.with_style(icon_button_styles)
.with_hovered_styles(icon_hover_styles)
.build()
.on_click(|ctx, _, _| ctx.dispatch_typed_action(SharingDialogAction::BackToAccessDialog))
.finish();
let title = appearance
.ui_builder()
.span("Share session QR code")
.with_style(UiComponentStyles {
font_color: Some(foreground),
font_size: Some(style::HEADER_TEXT_SIZE),
..Default::default()
})
.build()
.finish();
let esc_hint = appearance
.ui_builder()
.span("ESC")
.with_style(UiComponentStyles {
font_color: Some(style::acl_secondary_text_color(appearance)),
font_size: Some(12.),
background: Some(appearance.theme().surface_2().into()),
border_radius: Some(CornerRadius::with_all(Radius::Pixels(4.))),
padding: Some(Coords::default().left(6.).right(6.).top(2.).bottom(2.)),
..Default::default()
})
.build()
.finish();
let close_button = icon_button_with_color(
appearance,
Icon::X,
false,
self.ui_state_handles.qr_close_button.clone(),
ThemeFill::Solid(foreground),
)
.with_style(icon_button_styles)
.with_hovered_styles(icon_hover_styles)
.build()
.on_click(|ctx, _, _| ctx.dispatch_typed_action(SharingDialogAction::Close))
.finish();
Container::new(
Flex::row()
.with_main_axis_size(MainAxisSize::Max)
.with_main_axis_alignment(MainAxisAlignment::SpaceBetween)
.with_cross_axis_alignment(CrossAxisAlignment::Center)
.with_children([
back_button,
Shrinkable::new(1., title).finish(),
esc_hint,
close_button,
])
.finish(),
)
.with_horizontal_padding(style::ACL_ITEM_PADDING)
.with_padding_top(8.)
.with_padding_bottom(8.)
.finish()
}
fn render_qr_dialog(&self, appearance: &Appearance, app: &AppContext) -> Box<dyn Element> {
let qr_contents = self
.target_link(app)
.and_then(|url| qr_matrix_for_url(&url).ok())
.map(|matrix| {
let card = Container::new(Align::new(self.render_qr_matrix(&matrix)).finish())
.with_background(ColorU::white())
.with_corner_radius(CornerRadius::with_all(Radius::Pixels(8.)))
.finish();
let action_row = Flex::row()
.with_main_axis_alignment(MainAxisAlignment::Center)
.with_cross_axis_alignment(CrossAxisAlignment::Center)
.with_children([
self.render_footer_icon_button(
Icon::Copy,
SharingDialogAction::CopyLink,
"Copy link",
self.ui_state_handles.qr_copy_button.clone(),
appearance,
),
Container::new(self.render_footer_icon_button(
Icon::Download,
SharingDialogAction::DownloadQrCode,
"Download QR code",
self.ui_state_handles.qr_download_button.clone(),
appearance,
))
.with_margin_left(8.)
.finish(),
])
.finish();
Flex::column()
.with_main_axis_alignment(MainAxisAlignment::Center)
.with_cross_axis_alignment(CrossAxisAlignment::Center)
.with_children([
ConstrainedBox::new(card)
.with_width(QR_CARD_SIZE)
.with_height(QR_CARD_SIZE)
.finish(),
Container::new(action_row).with_margin_top(12.).finish(),
])
.finish()
})
.unwrap_or_else(|| {
appearance
.ui_builder()
.paragraph("Unable to create QR code for this session link.")
.with_style(UiComponentStyles {
font_color: Some(style::acl_secondary_text_color(appearance)),
..Default::default()
})
.build()
.finish()
});
Flex::column()
.with_main_axis_size(MainAxisSize::Min)
.with_children([
self.render_qr_header(appearance),
Container::new(Align::new(qr_contents).finish())
.with_vertical_padding(32.)
.finish(),
])
.finish()
}
/// Renders a link to the shared object, with a CTA to copy the URL.
fn render_object_link(&self, appearance: &Appearance, app: &AppContext) -> Box<dyn Element> {
let url = match self.target.as_ref().and_then(|target| target.link(app)) {
let url = match self.target_link(app) {
Some(url) => url,
None => return Empty::new().finish(),
};
@@ -2404,12 +2741,8 @@ impl SharingDialog {
.finish();
let link = Container::new(Align::new(link_text).left().finish())
.with_border(
Border::new(1.)
.with_sides(true, true, true, false)
.with_border_color(style::form_border_color(appearance)),
)
.with_corner_radius(CornerRadius::with_left(Radius::Pixels(4.)))
.with_border(Border::all(1.).with_border_color(style::form_border_color(appearance)))
.with_corner_radius(CornerRadius::with_all(Radius::Pixels(4.)))
.with_uniform_padding(8.)
.finish();
@@ -2436,7 +2769,7 @@ impl SharingDialog {
font_color: Some(copy_button_foreground.into()),
background: Some(copy_button_background.into()),
border_color: Some(style::form_border_color(appearance).into()),
border_radius: Some(CornerRadius::with_right(Radius::Pixels(4.))),
border_radius: Some(CornerRadius::with_all(Radius::Pixels(4.))),
..Default::default()
})
.with_clicked_styles(UiComponentStyles {
@@ -2451,16 +2784,29 @@ impl SharingDialog {
.on_click(|ctx, _, _| ctx.dispatch_typed_action(SharingDialogAction::CopyLink))
.finish();
let link_form = ConstrainedBox::new(
Flex::row()
.with_children([Shrinkable::new(1., link).finish(), copy_button])
.with_main_axis_size(MainAxisSize::Max)
.with_main_axis_alignment(MainAxisAlignment::SpaceBetween)
.with_cross_axis_alignment(CrossAxisAlignment::Center)
.finish(),
)
.with_min_height(style::ACL_ITEM_HEIGHT)
.finish();
let qr_button = matches!(self.target, Some(ShareableObject::Session { .. })).then(|| {
self.render_footer_icon_button(
Icon::QrCode,
SharingDialogAction::ShowQrCode,
"Show QR code",
self.ui_state_handles.qr_code_button.clone(),
appearance,
)
});
let mut link_row = Flex::row()
.with_main_axis_size(MainAxisSize::Max)
.with_main_axis_alignment(MainAxisAlignment::SpaceBetween)
.with_cross_axis_alignment(CrossAxisAlignment::Center)
.with_child(Shrinkable::new(1., link).finish());
if let Some(qr_button) = qr_button {
link_row.add_child(Container::new(qr_button).with_margin_left(8.).finish());
}
link_row.add_child(Container::new(copy_button).with_margin_left(8.).finish());
let link_form = ConstrainedBox::new(link_row.finish())
.with_min_height(style::ACL_ITEM_HEIGHT)
.finish();
Container::new(link_form)
.with_horizontal_padding(style::ACL_ITEM_PADDING)
@@ -2500,41 +2846,51 @@ impl View for SharingDialog {
let appearance = Appearance::as_ref(app);
let theme = appearance.theme();
let mut contents = Flex::column();
let (contents, width) = if self.mode == SharingDialogMode::QrCode
&& matches!(self.target, Some(ShareableObject::Session { .. }))
{
(self.render_qr_dialog(appearance, app), QR_DIALOG_WIDTH)
} else {
let mut contents = Flex::column();
contents.extend(self.render_session_header(appearance, app));
contents.extend(self.render_session_header(appearance, app));
if self.can_edit_access(app) && self.can_direct_link_share(app) {
contents.add_child(self.render_invite_form(appearance, app));
}
contents.add_child(self.render_access_header(appearance));
contents.extend(self.render_restricted_access_label(appearance, app));
if self.can_edit_access(app) && self.can_direct_link_share(app) {
contents.add_child(self.render_invite_form(appearance, app));
}
contents.add_child(self.render_access_header(appearance));
contents.extend(self.render_restricted_access_label(appearance, app));
if self.can_anyone_with_link_share(app) {
contents.extend(self.render_link_sharing_subject(appearance, app));
}
contents.extend(self.render_team_sharing_subject(appearance, app));
contents.extend(self.render_owner(appearance, app));
if self.can_anyone_with_link_share(app) {
contents.extend(self.render_link_sharing_subject(appearance, app));
}
contents.extend(self.render_team_sharing_subject(appearance, app));
contents.extend(self.render_owner(appearance, app));
if let Some(guest_list) = self.render_guests(appearance) {
contents.add_child(guest_list);
}
if let Some(guest_list) = self.render_guests(appearance) {
contents.add_child(guest_list);
}
contents.add_child(self.render_object_link(appearance, app));
contents.add_child(self.render_object_link(appearance, app));
let mut stack = Stack::new();
stack.add_child(contents.finish());
self.render_menu(&mut stack, app);
let mut stack = Stack::new();
stack.add_child(contents.finish());
self.render_menu(&mut stack, app);
let dialog = Container::new(stack.finish())
let contents = Container::new(stack.finish())
.with_vertical_padding(style::ACL_ITEM_PADDING)
.finish();
(contents, SHARING_DIALOG_WIDTH)
};
let dialog = Container::new(contents)
.with_corner_radius(CornerRadius::with_all(Radius::Pixels(8.)))
.with_border(Border::all(1.).with_border_color(theme.surface_3().into()))
.with_vertical_padding(style::ACL_ITEM_PADDING)
.with_background(style::dialog_background(appearance));
Dismiss::new(
ConstrainedBox::new(dialog.finish())
.with_width(SHARING_DIALOG_WIDTH)
.with_width(width)
.finish(),
)
.on_dismiss(|ctx, _app| ctx.dispatch_typed_action(SharingDialogAction::Close))
@@ -2543,7 +2899,7 @@ impl View for SharingDialog {
}
fn on_focus(&mut self, focus_ctx: &FocusContext, ctx: &mut ViewContext<Self>) {
if focus_ctx.is_self_focused() {
if focus_ctx.is_self_focused() && self.mode == SharingDialogMode::Access {
ctx.focus(&self.invite_form.email_editor);
}
}
@@ -2615,6 +2971,12 @@ impl TypedActionView for SharingDialog {
ctx.notify();
}
SharingDialogAction::CopyLink => self.copy_link(ctx),
SharingDialogAction::ShowQrCode => self.show_qr_code(ctx),
SharingDialogAction::BackToAccessDialog => {
self.mode = SharingDialogMode::Access;
ctx.notify();
}
SharingDialogAction::DownloadQrCode => self.download_qr_code(ctx),
SharingDialogAction::ToggleLinkSharingMenu => {
self.toggle_menu(OpenMenuState::LinkSharing, ctx);
}
+19 -19
View File
@@ -1,31 +1,31 @@
use std::borrow::Cow;
use chrono::{DateTime, Local};
use galaxy_core::{channel::ChannelState, ui::appearance::Appearance};
use galaxyui::{
color::ColorU,
ui_components::components::{UiComponent, UiComponentStyles},
AppContext, SingletonEntity, WeakViewHandle,
};
use session_sharing_protocol::common::SessionId;
use galaxy_core::channel::ChannelState;
use galaxy_core::ui::appearance::Appearance;
use galaxyui::color::ColorU;
use galaxyui::ui_components::components::{UiComponent, UiComponentStyles};
use galaxyui::{AppContext, SingletonEntity, WeakViewHandle};
use crate::{
ai::{agent::conversation::AIConversationId, blocklist::BlocklistAIHistoryModel},
cloud_object::model::persistence::CloudModel,
server::{ids::ServerId, server_api::object::GuestIdentifier},
terminal::{shared_session::join_link, TerminalView},
ui_components::{
avatar::{Avatar, AvatarContent},
icons::Icon,
},
workspaces::{user_profiles::UserProfiles, user_workspaces::UserWorkspaces},
};
use crate::ai::agent::conversation::AIConversationId;
use crate::ai::blocklist::BlocklistAIHistoryModel;
use crate::cloud_object::model::persistence::CloudModel;
use crate::server::ids::ServerId;
use crate::server::server_api::object::GuestIdentifier;
use crate::terminal::shared_session::join_link;
use crate::terminal::TerminalView;
use crate::ui_components::avatar::{Avatar, AvatarContent};
use crate::ui_components::icons::Icon;
use crate::workspaces::user_profiles::UserProfiles;
use crate::workspaces::user_workspaces::UserWorkspaces;
pub mod dialog;
mod qr_code;
mod style;
// Re-export types from galaxy_server_client.
pub use galaxy_server_client::drive::sharing::{
// Re-export types from cloud_objects.
pub use cloud_objects::drive::sharing::{
LinkSharingSubjectType, SharingAccessLevel, Subject, TeamKind, UserKind,
};
+74
View File
@@ -0,0 +1,74 @@
use std::io::Cursor;
use image::{ImageBuffer, ImageFormat, Luma};
use qrcode::{Color, QrCode};
pub const QUIET_ZONE_MODULES: usize = 4;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct QrMatrix {
width: usize,
modules: Vec<bool>,
}
#[derive(Debug, thiserror::Error)]
pub enum QrCodeError {
#[error("Unable to encode QR code")]
Encode,
#[error("Unable to encode QR code image")]
Image(#[from] image::ImageError),
}
impl QrMatrix {
pub fn width(&self) -> usize {
self.width
}
pub fn is_dark(&self, x: usize, y: usize) -> bool {
self.modules
.get(y.saturating_mul(self.width).saturating_add(x))
.copied()
.unwrap_or(false)
}
}
pub fn qr_matrix_for_url(url: &str) -> Result<QrMatrix, QrCodeError> {
let code = QrCode::new(url.as_bytes()).map_err(|_| QrCodeError::Encode)?;
let width = code.width();
let modules = code
.to_colors()
.into_iter()
.map(|color| matches!(color, Color::Dark))
.collect();
Ok(QrMatrix { width, modules })
}
pub fn qr_png_for_url(url: &str, pixel_size: u32) -> Result<Vec<u8>, QrCodeError> {
let matrix = qr_matrix_for_url(url)?;
let modules_with_quiet_zone = matrix.width().saturating_add(QUIET_ZONE_MODULES * 2);
let module_size = (pixel_size as usize / modules_with_quiet_zone).max(1);
let image_size = modules_with_quiet_zone.saturating_mul(module_size) as u32;
let mut image = ImageBuffer::from_pixel(image_size, image_size, Luma([255u8]));
for y in 0..matrix.width() {
for x in 0..matrix.width() {
if matrix.is_dark(x, y) {
let start_x = (x + QUIET_ZONE_MODULES) * module_size;
let start_y = (y + QUIET_ZONE_MODULES) * module_size;
for pixel_y in start_y..start_y + module_size {
for pixel_x in start_x..start_x + module_size {
image.put_pixel(pixel_x as u32, pixel_y as u32, Luma([0u8]));
}
}
}
}
}
let mut png = Cursor::new(Vec::new());
image.write_to(&mut png, ImageFormat::Png)?;
Ok(png.into_inner())
}
#[cfg(test)]
#[path = "qr_code_tests.rs"]
mod tests;
+13
View File
@@ -0,0 +1,13 @@
use super::*;
#[test]
fn qr_matrix_for_url_returns_square_matrix_with_dark_modules() {
let matrix = match qr_matrix_for_url("https://app.warp.dev/session/test-session") {
Ok(matrix) => matrix,
Err(error) => panic!("failed to generate QR matrix: {error}"),
};
assert!(matrix.width() > 0);
assert_eq!(matrix.modules.len(), matrix.width() * matrix.width());
assert!(matrix.modules.iter().any(|is_dark| *is_dark));
}
+8 -13
View File
@@ -1,18 +1,13 @@
use std::borrow::Cow;
use galaxy_core::ui::{
appearance::Appearance,
theme::{color::internal_colors, Fill},
};
use galaxyui::{
color::ColorU,
elements::{CornerRadius, Radius},
fonts::Weight,
ui_components::{
components::{UiComponent as _, UiComponentStyles},
text::Span,
},
};
use galaxy_core::ui::appearance::Appearance;
use galaxy_core::ui::theme::color::internal_colors;
use galaxy_core::ui::theme::Fill;
use galaxyui::color::ColorU;
use galaxyui::elements::{CornerRadius, Radius};
use galaxyui::fonts::Weight;
use galaxyui::ui_components::components::{UiComponent as _, UiComponentStyles};
use galaxyui::ui_components::text::Span;
/// The padding around ACL items in the sharing dialog.
pub const ACL_ITEM_PADDING: f32 = 16.;
+8 -13
View File
@@ -5,19 +5,14 @@ use galaxyui::{SingletonEntity, ViewContext};
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use crate::{
ai::AIRequestUsageModel,
auth::AuthStateProvider,
send_telemetry_from_ctx,
server::telemetry::TelemetryEvent,
workflows::workflow::{Argument, Workflow},
workspaces::user_workspaces::UserWorkspaces,
};
use super::{
arguments::ArgumentsState,
modal::{AiAssistState, WorkflowModal, WorkflowModalEvent},
};
use super::arguments::ArgumentsState;
use super::modal::{AiAssistState, WorkflowModal, WorkflowModalEvent};
use crate::ai::AIRequestUsageModel;
use crate::auth::AuthStateProvider;
use crate::send_telemetry_from_ctx;
use crate::server::telemetry::TelemetryEvent;
use crate::workflows::workflow::{Argument, Workflow};
use crate::workspaces::user_workspaces::UserWorkspaces;
/// Generated command metadata from server.
#[derive(Debug)]
+5 -6
View File
@@ -1,10 +1,9 @@
use std::{
collections::{HashMap, HashSet},
ops::Range,
};
use std::collections::{HashMap, HashSet};
use std::ops::Range;
use handlebars::parser::{ParsedArgumentResult, ParsedArgumentsIterator};
use crate::workflows::workflow::Argument;
use handlebars::parser::{ParsedArgumentResult, ParsedArgumentsIterator};
/// Represents arguments for workflow to be viewed and edited in ArgumentsEditorView.
///
@@ -201,5 +200,5 @@ impl ArgumentsState {
}
#[cfg(test)]
#[path = "arguments_test.rs"]
#[path = "arguments_tests.rs"]
mod tests;
@@ -1,9 +1,10 @@
use std::{fmt::Debug, iter::zip};
use std::fmt::Debug;
use std::iter::zip;
use crate::workflows::workflow::Argument;
use galaxyui::App;
use super::ArgumentsState;
use crate::workflows::workflow::Argument;
fn assert_vector_eq<T>(vector: Vec<T>, expected_vector: &Vec<T>)
where
+21 -22
View File
@@ -1,34 +1,33 @@
use std::rc::Rc;
use galaxy_core::{features::FeatureFlag, ui::appearance::Appearance};
use strum::IntoEnumIterator;
use strum_macros::{EnumIter, IntoStaticStr};
use galaxy_core::features::FeatureFlag;
use galaxy_core::ui::appearance::Appearance;
use galaxy_editor::editor::NavigationKey;
use galaxyui::elements::{
Border, ClippedScrollStateHandle, ClippedScrollable, ConstrainedBox, Container, CornerRadius,
CrossAxisAlignment, Empty, Fill, Flex, MainAxisAlignment, MainAxisSize, MouseStateHandle,
ParentElement, Radius, ScrollbarWidth, Shrinkable,
};
use galaxyui::ui_components::button::ButtonVariant;
use galaxyui::ui_components::components::{UiComponent, UiComponentStyles};
use galaxyui::ui_components::toggle_menu::{ToggleMenuItem, ToggleMenuStateHandle};
use galaxyui::{
elements::{
Border, ClippedScrollStateHandle, ClippedScrollable, ConstrainedBox, Container,
CornerRadius, CrossAxisAlignment, Empty, Fill, Flex, MainAxisAlignment, MainAxisSize,
MouseStateHandle, ParentElement, Radius, ScrollbarWidth, Shrinkable,
},
ui_components::{
button::ButtonVariant,
components::{UiComponent, UiComponentStyles},
toggle_menu::{ToggleMenuItem, ToggleMenuStateHandle},
},
AppContext, Element, Entity, FocusContext, SingletonEntity, TypedActionView, View, ViewContext,
ViewHandle,
};
use strum::IntoEnumIterator;
use strum_macros::{EnumIter, IntoStaticStr};
use crate::{
cloud_object::{model::persistence::CloudModel, Revision},
editor::{
EditorOptions, EditorView, Event, PropagateAndNoOpNavigationKeys, SingleLineEditorOptions,
TextOptions,
},
server::ids::{ClientId, SyncId},
ui_components::{buttons::icon_button, icons::Icon},
workflows::workflow_enum::EnumVariants,
use crate::cloud_object::model::persistence::CloudModel;
use crate::cloud_object::Revision;
use crate::editor::{
EditorOptions, EditorView, Event, PropagateAndNoOpNavigationKeys, SingleLineEditorOptions,
TextOptions,
};
use crate::server::ids::{ClientId, SyncId};
use crate::ui_components::buttons::icon_button;
use crate::ui_components::icons::Icon;
use crate::workflows::workflow_enum::EnumVariants;
const CONTAINER_PADDING: f32 = 16.;
const CORE_WIDTH: f32 = 400.;
+48 -64
View File
@@ -1,70 +1,27 @@
use std::collections::HashMap;
use std::collections::HashSet;
use std::{cmp::Ordering, sync::Arc};
use std::cmp::Ordering;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use galaxy_core::ui::theme::Fill;
use galaxy_editor::editor::NavigationKey;
use galaxyui::elements::Clipped;
use galaxyui::FocusContext;
use galaxyui::{
clipboard::ClipboardContent,
elements::{
Align, Border, ChildAnchor, ClippedScrollStateHandle, ClippedScrollable, ConstrainedBox,
Container, CornerRadius, CrossAxisAlignment, Flex, MainAxisAlignment, MainAxisSize,
MouseStateHandle, OffsetPositioning, ParentAnchor, ParentElement, ParentOffsetBounds,
Radius, ScrollbarWidth, Shrinkable, Stack,
},
fonts::{FamilyId, Weight},
platform::Cursor,
presenter::ChildView,
ui_components::{
button::{ButtonVariant, TextAndIcon, TextAndIconAlignment},
components::{Coords, UiComponent, UiComponentStyles},
},
AppContext, Element, Entity, SingletonEntity, TypedActionView, UpdateView, View, ViewContext,
ViewHandle,
};
use itertools::Itertools;
use pathfinder_geometry::vector::vec2f;
use string_offset::CharOffset;
use crate::auth::UserUid;
use crate::{
appearance::Appearance,
cloud_object::{
breadcrumbs::{ContainingObject, ContainingObjectKind},
model::persistence::{CloudModel, CloudModelEvent},
CloudObject, CloudObjectEventEntrypoint, ObjectType, Owner, Revision,
},
drive::{
cloud_object_styling::warp_drive_icon_color, items::WarpDriveItemId, CloudObjectTypeAndId,
DriveObjectType,
},
editor::{
EditorOptions, EditorView, EnterAction, EnterSettings, Event as EditorEvent,
InteractionState, PlainTextEditorViewAction as EditorAction,
PropagateAndNoOpNavigationKeys, TextOptions, TextStyleOperation,
},
menu::{Event, Menu, MenuItem, MenuItemFields},
network::NetworkStatus,
server::{
cloud_objects::update_manager::UpdateManager,
ids::{ClientId, ServerId, SyncId},
server_api::ai::AIClient,
},
themes::theme::AnsiColorIdentifier,
ui_components::{
blended_colors,
breadcrumb::{self, BreadcrumbState},
buttons::icon_button,
dialog::{dialog_styles, Dialog},
icons::{self, Icon, ICON_DIMENSIONS},
menu_button::{icon_button_with_context_menu, MenuDirection},
},
workflows::{
workflow::{Argument, Workflow},
CloudWorkflow,
},
use galaxy_core::ui::theme::Fill;
use galaxy_editor::editor::NavigationKey;
use galaxyui::clipboard::ClipboardContent;
use galaxyui::elements::{
Align, Border, ChildAnchor, Clipped, ClippedScrollStateHandle, ClippedScrollable,
ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, Flex, MainAxisAlignment,
MainAxisSize, MouseStateHandle, OffsetPositioning, ParentAnchor, ParentElement,
ParentOffsetBounds, Radius, ScrollbarWidth, Shrinkable, Stack,
};
use galaxyui::fonts::{FamilyId, Weight};
use galaxyui::platform::Cursor;
use galaxyui::presenter::ChildView;
use galaxyui::ui_components::button::{ButtonVariant, TextAndIcon, TextAndIconAlignment};
use galaxyui::ui_components::components::{Coords, UiComponent, UiComponentStyles};
use galaxyui::{
AppContext, Element, Entity, FocusContext, SingletonEntity, TypedActionView, UpdateView, View,
ViewContext, ViewHandle,
};
use super::arguments::ArgumentsState;
@@ -73,6 +30,33 @@ use super::workflow_arg_selector::{
WorkflowArgSelector, WorkflowArgSelectorEvent, WorkflowArgSelectorStyles,
};
use super::workflow_arg_type_helpers::{self, ArgumentEditorRowIndex};
use crate::appearance::Appearance;
use crate::auth::UserUid;
use crate::cloud_object::breadcrumbs::{ContainingObject, ContainingObjectKind};
use crate::cloud_object::model::persistence::{CloudModel, CloudModelEvent};
use crate::cloud_object::{CloudObject, CloudObjectEventEntrypoint, ObjectType, Owner, Revision};
use crate::drive::cloud_object_styling::warp_drive_icon_color;
use crate::drive::items::WarpDriveItemId;
use crate::drive::{CloudObjectTypeAndId, DriveObjectType};
use crate::editor::{
EditorOptions, EditorView, EnterAction, EnterSettings, Event as EditorEvent, InteractionState,
PlainTextEditorViewAction as EditorAction, PropagateAndNoOpNavigationKeys, TextOptions,
TextStyleOperation,
};
use crate::menu::{Event, Menu, MenuItem, MenuItemFields};
use crate::network::NetworkStatus;
use crate::server::cloud_objects::update_manager::UpdateManager;
use crate::server::ids::{ClientId, ServerId, SyncId};
use crate::server::server_api::ai::AIClient;
use crate::themes::theme::AnsiColorIdentifier;
use crate::ui_components::blended_colors;
use crate::ui_components::breadcrumb::{self, BreadcrumbState};
use crate::ui_components::buttons::icon_button;
use crate::ui_components::dialog::{dialog_styles, Dialog};
use crate::ui_components::icons::{self, Icon, ICON_DIMENSIONS};
use crate::ui_components::menu_button::{icon_button_with_context_menu, MenuDirection};
use crate::workflows::workflow::{Argument, Workflow};
use crate::workflows::CloudWorkflow;
const BREADCRUMBS_VERTICAL_MARGIN: f32 = 6.;
const MODAL_WIDTH: f32 = 900.;
@@ -1947,5 +1931,5 @@ impl TypedActionView for WorkflowModal {
}
#[cfg(test)]
#[path = "modal_test.rs"]
#[path = "modal_tests.rs"]
mod tests;
@@ -1,21 +1,20 @@
use galaxy_core::ui::appearance::Appearance;
use galaxyui::{platform::WindowStyle, App, SingletonEntity, ViewHandle};
use std::sync::Arc;
use galaxy_core::ui::appearance::Appearance;
use warpui::platform::WindowStyle;
use warpui::{App, SingletonEntity, ViewHandle};
use super::WorkflowModal;
use crate::auth::AuthStateProvider;
use crate::{
cloud_object::model::persistence::CloudModel,
editor::PlainTextEditorViewAction as EditorAction,
server::server_api::team::MockTeamClient,
server::server_api::workspace::MockWorkspaceClient,
server::server_api::ServerApiProvider,
settings_view::keybindings::KeybindingChangedNotifier,
test_util::settings::initialize_settings_for_tests,
workflows::workflow::{Argument, Workflow},
UserWorkspaces,
};
use crate::cloud_object::model::persistence::CloudModel;
use crate::editor::PlainTextEditorViewAction as EditorAction;
use crate::server::server_api::team::MockTeamClient;
use crate::server::server_api::workspace::MockWorkspaceClient;
use crate::server::server_api::ServerApiProvider;
use crate::settings_view::keybindings::KeybindingChangedNotifier;
use crate::test_util::settings::initialize_settings_for_tests;
use crate::workflows::workflow::{Argument, Workflow};
use crate::UserWorkspaces;
fn initialize_app(app: &mut App) {
initialize_settings_for_tests(app);
@@ -1,49 +1,39 @@
use galaxy_core::ui::{appearance::Appearance, theme::Fill};
use std::collections::HashMap;
use std::rc::Rc;
use itertools::Itertools;
use strum::IntoEnumIterator;
use strum_macros::{EnumIter, IntoStaticStr};
use galaxy_core::ui::appearance::Appearance;
use galaxy_core::ui::theme::Fill;
use galaxy_editor::editor::NavigationKey;
use galaxyui::elements::{
Align, Border, ChildAnchor, ChildView, ClippedScrollStateHandle, ClippedScrollable,
ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, Dismiss, Empty, EventHandler,
Flex, Hoverable, MainAxisSize, MouseStateHandle, OffsetPositioning, ParentAnchor,
ParentElement, ParentOffsetBounds, Radius, ScrollbarWidth, Shrinkable, Stack, Text,
};
use galaxyui::fonts::FamilyId;
use galaxyui::geometry::vector::vec2f;
use galaxyui::platform::Cursor;
use galaxyui::ui_components::components::{Coords, UiComponent, UiComponentStyles};
use galaxyui::ui_components::text::Span;
use galaxyui::ui_components::toggle_menu::{ToggleMenuItem, ToggleMenuStateHandle};
use galaxyui::{
elements::{
Align, Border, ChildAnchor, ChildView, ClippedScrollStateHandle, ClippedScrollable,
ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, Dismiss, Empty, EventHandler,
Flex, Hoverable, MainAxisSize, MouseStateHandle, OffsetPositioning, ParentElement, Radius,
ScrollbarWidth, Shrinkable, Stack, Text,
},
geometry::vector::vec2f,
ui_components::{
components::{Coords, UiComponent, UiComponentStyles},
text::Span,
toggle_menu::{ToggleMenuItem, ToggleMenuStateHandle},
},
AppContext, Element, Entity, FocusContext, SingletonEntity, TypedActionView, View, ViewContext,
ViewHandle,
};
use itertools::Itertools;
use std::{collections::HashMap, rc::Rc};
use strum::IntoEnumIterator;
use crate::{
editor::{
EditorOptions, EditorView, EnterSettings, Event as EditorEvent, InteractionState,
PropagateAndNoOpNavigationKeys, TextOptions,
},
server::ids::SyncId,
ui_components::{
buttons::{highlight, icon_button},
icons::{self, Icon},
},
workflows::workflow::ArgumentType,
};
use galaxyui::platform::Cursor;
use galaxyui::{
elements::{ParentAnchor, ParentOffsetBounds},
fonts::FamilyId,
};
use crate::editor::EnterAction;
use strum_macros::{EnumIter, IntoStaticStr};
use super::enum_creation_dialog::WorkflowEnumData;
use crate::editor::{
EditorOptions, EditorView, EnterAction, EnterSettings, Event as EditorEvent, InteractionState,
PropagateAndNoOpNavigationKeys, TextOptions,
};
use crate::server::ids::SyncId;
use crate::ui_components::buttons::{highlight, icon_button};
use crate::ui_components::icons::{self, Icon};
use crate::workflows::workflow::ArgumentType;
const ARGUMENT_DEFAULT_VALUE_PLACEHOLDER_TEXT: &str = "Default value (optional)";
const ARGUMENT_EDITOR_FONT_SIZE: f32 = 14.;
@@ -2,20 +2,15 @@ use std::collections::HashMap;
use galaxyui::{AppContext, SingletonEntity, ViewHandle};
use crate::{
cloud_object::{model::persistence::CloudModel, CloudObjectEventEntrypoint, Owner},
editor::EditorView,
server::{cloud_objects::update_manager::UpdateManager, ids::SyncId},
workflows::{
workflow::{Argument, ArgumentType},
workflow_enum::WorkflowEnum,
},
};
use super::{
enum_creation_dialog::{EnumCreationDialog, WorkflowEnumData},
workflow_arg_selector::WorkflowArgSelector,
};
use super::enum_creation_dialog::{EnumCreationDialog, WorkflowEnumData};
use super::workflow_arg_selector::WorkflowArgSelector;
use crate::cloud_object::model::persistence::CloudModel;
use crate::cloud_object::{CloudObjectEventEntrypoint, Owner};
use crate::editor::EditorView;
use crate::server::cloud_objects::update_manager::UpdateManager;
use crate::server::ids::SyncId;
use crate::workflows::workflow::{Argument, ArgumentType};
use crate::workflows::workflow_enum::WorkflowEnum;
#[derive(Debug, Clone)]
pub struct ArgumentEditorRowIndex(pub usize);