first pass of merging in warp (doesn't build)
This commit is contained in:
+179
-72
@@ -2,74 +2,63 @@
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::ai::agent::conversation::AIConversationId;
|
||||
use crate::ai::blocklist::agent_view::AgentViewEntryOrigin;
|
||||
use crate::ai::document::ai_document_model::{AIDocumentSaveStatus, AIDocumentUserEditStatus};
|
||||
use crate::appearance::Appearance;
|
||||
use crate::drive::{items::WarpDriveItemId, sharing::ShareableObject, CloudObjectTypeAndId};
|
||||
use crate::notebooks::editor::view::RichTextEditorConfig;
|
||||
use crate::pane_group::focus_state::PaneFocusHandle;
|
||||
use crate::pane_group::pane::view::header::components::{
|
||||
render_pane_header_buttons, render_pane_header_title_text, render_three_column_header,
|
||||
CenteredHeaderEdgeWidth,
|
||||
};
|
||||
use crate::pane_group::pane::view::header::toolbelt_button_position_id;
|
||||
use crate::pane_group::pane::view::header::PaneHeaderAction;
|
||||
use crate::send_telemetry_from_ctx;
|
||||
use crate::settings::FontSettings;
|
||||
use crate::terminal::input::MenuPositioning;
|
||||
use crate::terminal::view::TerminalView;
|
||||
use crate::util::bindings::keybinding_name_to_keystroke;
|
||||
use crate::view_components::action_button::{
|
||||
ButtonSize, NakedTheme, SecondaryTheme, TooltipAlignment,
|
||||
};
|
||||
use crate::view_components::DismissibleToast;
|
||||
use crate::workspace::ToastStack;
|
||||
use crate::BlocklistAIHistoryModel;
|
||||
use crate::{
|
||||
ai::document::ai_document_model::{
|
||||
AIDocumentId, AIDocumentInstance, AIDocumentModel, AIDocumentModelEvent,
|
||||
AIDocumentUpdateSource, AIDocumentVersion,
|
||||
},
|
||||
editor::InteractionState,
|
||||
menu::{Menu, MenuItem, MenuItemFields},
|
||||
notebooks::{
|
||||
editor::{
|
||||
model::NotebooksEditorModel,
|
||||
rich_text_styles,
|
||||
view::{EditorViewEvent, RichTextEditorView},
|
||||
},
|
||||
link::{NotebookLinks, SessionSource},
|
||||
},
|
||||
pane_group::{pane::view, BackingView, PaneConfiguration, PaneEvent},
|
||||
server::telemetry::TelemetryEvent,
|
||||
ui_components::buttons::icon_button,
|
||||
ui_components::icons::Icon,
|
||||
view_components::action_button::{ActionButton, PrimaryTheme},
|
||||
};
|
||||
use pathfinder_geometry::vector::vec2f;
|
||||
use galaxy_core::ui::icons;
|
||||
use galaxy_core::ui::icons::ICON_DIMENSIONS;
|
||||
use galaxy_core::ui::theme::Fill as ThemeFill;
|
||||
use galaxyui::clipboard::ClipboardContent;
|
||||
use galaxyui::elements::CrossAxisAlignment;
|
||||
use galaxyui::elements::MainAxisAlignment;
|
||||
use galaxyui::elements::MainAxisSize;
|
||||
use galaxyui::elements::{ChildAnchor, PositionedElementAnchor, PositionedElementOffsetBounds};
|
||||
use galaxyui::keymap::EditableBinding;
|
||||
use galaxyui::keymap::FixedBinding;
|
||||
use galaxyui::elements::{
|
||||
ChildAnchor, ChildView, ConstrainedBox, Container, CrossAxisAlignment, Flex, Hoverable,
|
||||
MainAxisAlignment, MainAxisSize, MouseStateHandle, OffsetPositioning, ParentElement,
|
||||
PositionedElementAnchor, PositionedElementOffsetBounds, SavePosition, Stack,
|
||||
};
|
||||
use galaxyui::keymap::{EditableBinding, FixedBinding};
|
||||
use galaxyui::text_layout::ClipConfig;
|
||||
use galaxyui::ui_components::button::ButtonTooltipPosition;
|
||||
use galaxyui::ui_components::components::UiComponent;
|
||||
use galaxyui::{
|
||||
elements::{
|
||||
ChildView, ConstrainedBox, Container, Flex, Hoverable, MouseStateHandle, OffsetPositioning,
|
||||
ParentElement, SavePosition, Stack,
|
||||
},
|
||||
AppContext, Element, Entity, ModelHandle, SingletonEntity, TypedActionView, View, ViewContext,
|
||||
ViewHandle,
|
||||
id, AppContext, Element, Entity, EntityId, ModelHandle, SingletonEntity, TypedActionView, View,
|
||||
ViewContext, ViewHandle,
|
||||
};
|
||||
use galaxyui::{id, EntityId};
|
||||
use pathfinder_geometry::vector::vec2f;
|
||||
|
||||
use crate::ai::agent::conversation::AIConversationId;
|
||||
use crate::ai::blocklist::agent_view::AgentViewEntryOrigin;
|
||||
use crate::ai::document::ai_document_model::{
|
||||
AIDocumentId, AIDocumentInstance, AIDocumentModel, AIDocumentModelEvent, AIDocumentSaveStatus,
|
||||
AIDocumentUpdateSource, AIDocumentUserEditStatus, AIDocumentVersion,
|
||||
};
|
||||
use crate::ai::document::orchestration_config_block::OrchestrationConfigBlockView;
|
||||
use crate::appearance::Appearance;
|
||||
use crate::drive::items::WarpDriveItemId;
|
||||
use crate::drive::sharing::ShareableObject;
|
||||
use crate::drive::CloudObjectTypeAndId;
|
||||
use crate::editor::InteractionState;
|
||||
use crate::menu::{Menu, MenuItem, MenuItemFields};
|
||||
use crate::notebooks::editor::model::NotebooksEditorModel;
|
||||
use crate::notebooks::editor::rich_text_styles;
|
||||
use crate::notebooks::editor::view::{EditorViewEvent, RichTextEditorConfig, RichTextEditorView};
|
||||
use crate::notebooks::link::{NotebookLinks, SessionSource};
|
||||
use crate::pane_group::focus_state::PaneFocusHandle;
|
||||
use crate::pane_group::pane::view;
|
||||
use crate::pane_group::pane::view::header::components::{
|
||||
render_pane_header_buttons, render_pane_header_title_text, render_three_column_header,
|
||||
CenteredHeaderEdgeWidth,
|
||||
};
|
||||
use crate::pane_group::pane::view::header::{toolbelt_button_position_id, PaneHeaderAction};
|
||||
use crate::pane_group::{BackingView, PaneConfiguration, PaneEvent};
|
||||
use crate::server::telemetry::TelemetryEvent;
|
||||
use crate::settings::FontSettings;
|
||||
use crate::terminal::input::MenuPositioning;
|
||||
use crate::terminal::view::TerminalView;
|
||||
use crate::ui_components::buttons::icon_button;
|
||||
use crate::ui_components::icons::Icon;
|
||||
use crate::util::bindings::keybinding_name_to_keystroke;
|
||||
use crate::view_components::action_button::{
|
||||
ActionButton, ButtonSize, NakedTheme, PrimaryTheme, SecondaryTheme, TooltipAlignment,
|
||||
};
|
||||
use crate::view_components::DismissibleToast;
|
||||
use crate::workspace::ToastStack;
|
||||
use crate::{send_telemetry_from_ctx, BlocklistAIHistoryModel};
|
||||
|
||||
pub fn init(app: &mut AppContext) {
|
||||
app.register_editable_bindings([EditableBinding::new(
|
||||
@@ -90,23 +79,25 @@ pub fn init(app: &mut AppContext) {
|
||||
)]);
|
||||
}
|
||||
|
||||
#[cfg(feature = "local_fs")]
|
||||
use warp_util::path::LineAndColumnArg;
|
||||
|
||||
#[cfg(feature = "local_fs")]
|
||||
use crate::code::editor_management::CodeSource;
|
||||
// Import keybinding constants from code view to ensure consistency
|
||||
use crate::code::view::{SAVE_FILE_BINDING_DESCRIPTION, SAVE_FILE_BINDING_NAME};
|
||||
use crate::notebooks::file::MarkdownDisplayMode;
|
||||
#[cfg(feature = "local_fs")]
|
||||
use crate::util::file::external_editor::settings::EditorLayout;
|
||||
#[cfg(feature = "local_fs")]
|
||||
use crate::util::openable_file_type::FileTarget;
|
||||
#[cfg(feature = "local_fs")]
|
||||
use galaxy_util::path::LineAndColumnArg;
|
||||
|
||||
// Import keybinding constants from code view to ensure consistency
|
||||
use crate::code::view::{SAVE_FILE_BINDING_DESCRIPTION, SAVE_FILE_BINDING_NAME};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum AIDocumentAction {
|
||||
Close,
|
||||
SelectVersion(AIDocumentVersion),
|
||||
Export,
|
||||
CopyAsMarkdown,
|
||||
OpenVersionMenu,
|
||||
CreateWarpDriveNotebook,
|
||||
RevertToDocumentVersion,
|
||||
@@ -170,6 +161,7 @@ pub struct AIDocumentView {
|
||||
synced_status_mouse_state: MouseStateHandle,
|
||||
view_position_id: String,
|
||||
version_button: ViewHandle<ActionButton>,
|
||||
orchestration_config_block: Option<ViewHandle<OrchestrationConfigBlockView>>,
|
||||
}
|
||||
|
||||
impl AIDocumentView {
|
||||
@@ -271,21 +263,61 @@ impl AIDocumentView {
|
||||
use crate::ai::blocklist::BlocklistAIHistoryEvent;
|
||||
match event {
|
||||
BlocklistAIHistoryEvent::UpdatedConversationStatus {
|
||||
terminal_view_id, ..
|
||||
terminal_surface_id,
|
||||
..
|
||||
} => {
|
||||
// Check if this is our terminal view
|
||||
if let Some(tv) = &me.original_terminal_view {
|
||||
if tv.id() == *terminal_view_id {
|
||||
if tv.id() == *terminal_surface_id {
|
||||
me.update_header_buttons(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
BlocklistAIHistoryEvent::RestoredConversations {
|
||||
terminal_view_id,
|
||||
terminal_surface_id,
|
||||
conversation_ids,
|
||||
} => {
|
||||
// Try to populate terminal view if conversations were restored
|
||||
me.maybe_populate_terminal_view(*terminal_view_id, conversation_ids, ctx);
|
||||
me.maybe_populate_terminal_view(
|
||||
*terminal_surface_id,
|
||||
conversation_ids,
|
||||
ctx,
|
||||
);
|
||||
}
|
||||
BlocklistAIHistoryEvent::OrchestrationConfigUpdated {
|
||||
conversation_id: cid,
|
||||
from_restore,
|
||||
} => {
|
||||
let our_conv = AIDocumentModel::as_ref(ctx)
|
||||
.get_conversation_id_for_document_id(&document_id);
|
||||
if our_conv.as_ref() == Some(cid) {
|
||||
// Lazily create the config block view if the
|
||||
// plan sidebar opened before the orchestration
|
||||
// config arrived.
|
||||
let was_freshly_created = if me.orchestration_config_block.is_none() {
|
||||
let conv_id = *cid;
|
||||
// TODO: introduce DocumentId / PlanId newtypes to make this
|
||||
// conversion type-safe.
|
||||
let plan_id = document_id.to_string();
|
||||
me.orchestration_config_block =
|
||||
Some(ctx.add_typed_action_view(move |ctx| {
|
||||
OrchestrationConfigBlockView::new(conv_id, plan_id, ctx)
|
||||
}));
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
// Arm auto-pop for live agent dispatches but
|
||||
// not for restore-hydrated events.
|
||||
if was_freshly_created && !*from_restore {
|
||||
if let Some(block) = &me.orchestration_config_block {
|
||||
block.update(ctx, |block, ctx| {
|
||||
block.arm_for_fresh_dispatch(ctx);
|
||||
});
|
||||
}
|
||||
}
|
||||
ctx.notify();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -304,7 +336,9 @@ impl AIDocumentView {
|
||||
let appearance = Appearance::as_ref(ctx);
|
||||
let font_settings = FontSettings::as_ref(ctx);
|
||||
let styles = rich_text_styles(appearance, font_settings);
|
||||
NotebooksEditorModel::new_unbound(styles, ctx)
|
||||
let mut model = NotebooksEditorModel::new_unbound(styles, ctx);
|
||||
model.set_default_mermaid_display_mode(MarkdownDisplayMode::Rendered, ctx);
|
||||
model
|
||||
})
|
||||
});
|
||||
|
||||
@@ -403,6 +437,27 @@ impl AIDocumentView {
|
||||
})
|
||||
});
|
||||
|
||||
// Create the orchestration config block if there's an active config
|
||||
// for this document's conversation.
|
||||
let doc_conversation_id =
|
||||
AIDocumentModel::as_ref(ctx).get_conversation_id_for_document_id(&document_id);
|
||||
let has_orchestration_config = doc_conversation_id.and_then(|cid| {
|
||||
BlocklistAIHistoryModel::as_ref(ctx)
|
||||
.conversation(&cid)
|
||||
.and_then(|conv| {
|
||||
let plan_id_str = document_id.to_string();
|
||||
conv.orchestration_config_for_plan(&plan_id_str)
|
||||
.map(|_| cid)
|
||||
})
|
||||
});
|
||||
let doc_id_for_block = document_id;
|
||||
let orchestration_config_block = has_orchestration_config.map(|conv_id| {
|
||||
let plan_id = doc_id_for_block.to_string();
|
||||
ctx.add_typed_action_view(move |ctx| {
|
||||
OrchestrationConfigBlockView::new(conv_id, plan_id, ctx)
|
||||
})
|
||||
});
|
||||
|
||||
let mut me = Self {
|
||||
document_id,
|
||||
document_version,
|
||||
@@ -420,6 +475,7 @@ impl AIDocumentView {
|
||||
synced_status_mouse_state: MouseStateHandle::default(),
|
||||
view_position_id,
|
||||
version_button,
|
||||
orchestration_config_block,
|
||||
};
|
||||
// Force update the editor view based on the initial document version
|
||||
me.refresh(ctx);
|
||||
@@ -958,8 +1014,9 @@ impl AIDocumentView {
|
||||
/// Export the current content as a markdown file.
|
||||
#[cfg(feature = "local_fs")]
|
||||
fn export(&self, ctx: &mut ViewContext<Self>) {
|
||||
use crate::drive::export::safe_filename;
|
||||
use galaxyui::platform::SaveFilePickerConfiguration;
|
||||
|
||||
use crate::drive::export::safe_filename;
|
||||
let markdown = self.editor.as_ref(ctx).markdown_unescaped(ctx);
|
||||
|
||||
// Get the document title from the model
|
||||
@@ -1015,12 +1072,41 @@ impl View for AIDocumentView {
|
||||
"AIDocumentView"
|
||||
}
|
||||
|
||||
fn render(&self, _app: &AppContext) -> Box<dyn galaxyui::Element> {
|
||||
fn render(&self, app: &AppContext) -> Box<dyn galaxyui::Element> {
|
||||
let has_orchestration_config = AIDocumentModel::as_ref(app)
|
||||
.get_conversation_id_for_document_id(&self.document_id)
|
||||
.and_then(|cid| {
|
||||
let plan_id_str = self.document_id.to_string();
|
||||
BlocklistAIHistoryModel::as_ref(app)
|
||||
.conversation(&cid)
|
||||
.and_then(|conv| conv.orchestration_config_for_plan(&plan_id_str).map(|_| ()))
|
||||
})
|
||||
.is_some();
|
||||
|
||||
let mut content_column =
|
||||
Flex::column().with_cross_axis_alignment(CrossAxisAlignment::Stretch);
|
||||
|
||||
// Orchestration config block — shown above the editor when the
|
||||
// conversation has an active OrchestrationConfigSnapshot.
|
||||
if has_orchestration_config {
|
||||
if let Some(config_block) = &self.orchestration_config_block {
|
||||
content_column.add_child(
|
||||
Container::new(ChildView::new(config_block).finish())
|
||||
.with_horizontal_padding(16.)
|
||||
.with_padding_bottom(12.)
|
||||
.with_padding_top(8.)
|
||||
.finish(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let editor = Container::new(ChildView::new(&self.editor).finish())
|
||||
.with_padding_left(8.)
|
||||
.with_padding_right(8.)
|
||||
.finish();
|
||||
let mut stack = Stack::new().with_child(editor);
|
||||
content_column.add_child(warpui::elements::Expanded::new(1.0, editor).finish());
|
||||
|
||||
let mut stack = Stack::new().with_child(content_column.finish());
|
||||
|
||||
if self.is_version_menu_open {
|
||||
stack.add_positioned_overlay_child(
|
||||
@@ -1052,6 +1138,20 @@ impl TypedActionView for AIDocumentView {
|
||||
self.refresh(ctx);
|
||||
}
|
||||
AIDocumentAction::Export => self.export(ctx),
|
||||
AIDocumentAction::CopyAsMarkdown => {
|
||||
let markdown = self.editor.as_ref(ctx).markdown_unescaped(ctx);
|
||||
ctx.clipboard()
|
||||
.write(ClipboardContent::plain_text(markdown));
|
||||
|
||||
let window_id = ctx.window_id();
|
||||
ToastStack::handle(ctx).update(ctx, |toast_stack, ctx| {
|
||||
toast_stack.add_ephemeral_toast(
|
||||
DismissibleToast::success("Copied to clipboard as Markdown".to_string()),
|
||||
window_id,
|
||||
ctx,
|
||||
);
|
||||
});
|
||||
}
|
||||
AIDocumentAction::CreateWarpDriveNotebook => self.create_warp_drive_notebook(ctx),
|
||||
AIDocumentAction::CopyLink(link) => {
|
||||
send_telemetry_from_ctx!(
|
||||
@@ -1240,6 +1340,13 @@ impl BackingView for AIDocumentView {
|
||||
);
|
||||
}
|
||||
|
||||
menu_items.push(
|
||||
MenuItemFields::new("Copy as Markdown")
|
||||
.with_on_select_action(AIDocumentAction::CopyAsMarkdown)
|
||||
.with_icon(Icon::Copy)
|
||||
.into_item(),
|
||||
);
|
||||
|
||||
#[cfg(feature = "local_fs")]
|
||||
{
|
||||
menu_items.push(
|
||||
|
||||
Reference in New Issue
Block a user