Port upstream UI polish and refresh dependencies
Bring over selected Warp UI improvements: selectable Thought text, message timestamps, animated theme backgrounds, editable New Window shortcuts, safer tab renaming and restoration, and text/font/discard crash fixes. Match upstream orchestration pill geometry and tooltips, and replace expanded subagent previews with compact links that retain live status and conversation navigation for launches and follow-up messages. Include the pending Cargo.lock dependency refresh. Adapted upstream PRs: #15948, #15884, #15691, #14618, #15772, #15705, #15605, #14895, #15996, #15771, #14893, and #14603.
This commit is contained in:
@@ -530,6 +530,7 @@ use crate::workspace::view::cloud_agent_capacity_modal::CloudAgentCapacityModalV
|
||||
use crate::workspace::{
|
||||
CommandSearchOptions, ForkAIConversationParams, ForkFromExchange,
|
||||
ForkedConversationDestination, OneTimeModalModel, ToastStack, WorkspaceAction,
|
||||
WorkspaceRegistry,
|
||||
};
|
||||
use crate::workspaces::user_workspaces::{UserWorkspaces, UserWorkspacesEvent};
|
||||
use crate::workspaces::workspace::CustomerType;
|
||||
@@ -1428,6 +1429,9 @@ pub enum ContextMenuAction {
|
||||
CopyAIBlockQuery {
|
||||
ai_block_view_id: EntityId,
|
||||
},
|
||||
CopyAIBlockTimestamp {
|
||||
ai_block_view_id: EntityId,
|
||||
},
|
||||
/// Copy the AI block output text
|
||||
CopyAIBlockOutput {
|
||||
ai_block_view_id: EntityId,
|
||||
@@ -1528,6 +1532,7 @@ impl fmt::Debug for ContextMenuAction {
|
||||
StopSharing => f.write_str("StopSharing"),
|
||||
CopyAIDebuggingLink { .. } => f.write_str("CopyAIDebuggingLink"),
|
||||
CopyAIBlockQuery { .. } => f.write_str("CopyAIBlockPrompt"),
|
||||
CopyAIBlockTimestamp { .. } => f.write_str("CopyAIBlockTimestamp"),
|
||||
CopyAIBlockOutput { .. } => f.write_str("CopyAIBlockOutput"),
|
||||
CopyAIBlock { .. } => f.write_str("CopyAIBlockBoth"),
|
||||
CopyAIBlockConversation { .. } => f.write_str("CopyAIBlockConversation"),
|
||||
@@ -12149,7 +12154,17 @@ impl TerminalView {
|
||||
// case, we want the block to be focused because otherwise,
|
||||
// users get stuck as they'd otherwise need to click into the
|
||||
// box to respond to whether or not they want to update oh my zsh.
|
||||
self.focus_terminal(ctx);
|
||||
//
|
||||
// Skipped while a tab or tab-group rename editor is focused. Taking focus
|
||||
// would end the rename and lose user inputs (#14241).
|
||||
let inline_rename_editor_is_focused = WorkspaceRegistry::as_ref(ctx)
|
||||
.get(self.window_id, ctx)
|
||||
.is_some_and(|workspace| {
|
||||
workspace.as_ref(ctx).is_inline_rename_editor_focused(ctx)
|
||||
});
|
||||
if !inline_rename_editor_is_focused {
|
||||
self.focus_terminal(ctx);
|
||||
}
|
||||
}
|
||||
ModelEvent::AfterBlockStarted {
|
||||
command,
|
||||
@@ -24998,6 +25013,19 @@ impl TerminalView {
|
||||
}
|
||||
}
|
||||
}
|
||||
CopyAIBlockTimestamp { ai_block_view_id } => {
|
||||
for rich_content in self.rich_content_views.iter() {
|
||||
if let Some(ai_metadata) = rich_content
|
||||
.ai_block_metadata()
|
||||
.filter(|metadata| metadata.ai_block_handle.id() == *ai_block_view_id)
|
||||
{
|
||||
ai_metadata.ai_block_handle.update(ctx, |block, ctx| {
|
||||
block.handle_action(&AIBlockAction::CopyTimestamp, ctx);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
CopyAIBlockOutput { ai_block_view_id } => {
|
||||
// Copy only the current AI block's output
|
||||
for rich_content in self.rich_content_views.iter() {
|
||||
|
||||
@@ -74,6 +74,8 @@ fn render_user_query(
|
||||
&user_display_name,
|
||||
profile_image_url.as_ref(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
&Default::default(),
|
||||
&Default::default(),
|
||||
0,
|
||||
|
||||
@@ -119,6 +119,27 @@ impl TerminalView {
|
||||
.into_item(),
|
||||
);
|
||||
}
|
||||
let has_query_timestamp = self.rich_content_views.iter().any(|rich_content| {
|
||||
rich_content
|
||||
.ai_block_metadata()
|
||||
.filter(|metadata| metadata.ai_block_handle.id() == ai_block_view_id)
|
||||
.is_some_and(|metadata| {
|
||||
metadata
|
||||
.ai_block_handle
|
||||
.as_ref(ctx)
|
||||
.query_sent_at(ctx)
|
||||
.is_some()
|
||||
})
|
||||
});
|
||||
if has_query_timestamp {
|
||||
items.push(
|
||||
MenuItemFields::new("Copy timestamp")
|
||||
.with_on_select_action(TerminalAction::ContextMenu(
|
||||
ContextMenuAction::CopyAIBlockTimestamp { ai_block_view_id },
|
||||
))
|
||||
.into_item(),
|
||||
);
|
||||
}
|
||||
items.push(MenuItem::Separator);
|
||||
items.push(
|
||||
MenuItemFields::new("Save as prompt")
|
||||
|
||||
@@ -51,6 +51,7 @@ use crate::server::ids::{ClientId, SyncId};
|
||||
use crate::server::server_api::ai::SpawnAgentRequest;
|
||||
use crate::settings::import::model::ImportedConfigModel;
|
||||
use crate::settings::{AISettings, AppEditorSettings, WarpPromptSeparator};
|
||||
use crate::tab::NewSessionMenuItem;
|
||||
use crate::terminal::alt_screen::should_intercept_mouse;
|
||||
use crate::terminal::block_list_element::{SnackbarPoint, SnackbarTranslationMode};
|
||||
use crate::terminal::block_list_viewport::{ClampingMode, ScrollLines};
|
||||
@@ -84,7 +85,8 @@ use crate::test_util::terminal::{
|
||||
};
|
||||
use crate::test_util::{add_window_with_terminal, assert_eventually};
|
||||
use crate::view_components::find::FindWithinBlockState;
|
||||
use crate::workspace::ToastStack;
|
||||
use crate::workspace::view::tests::{initialize_app as initialize_workspace_app, mock_workspace};
|
||||
use crate::workspace::{ToastStack, WorkspaceAction};
|
||||
use crate::ActiveAgentViewsModel;
|
||||
|
||||
fn add_window_with_cloud_mode_terminal(app: &mut App) -> ViewHandle<TerminalView> {
|
||||
@@ -8427,3 +8429,73 @@ fn hidden_tool_control_changes_preserve_pinned_and_unpinned_scroll() {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn visible_bootstrap_block_leaves_focus_on_tab_rename_editor() {
|
||||
App::test((), |mut app| async move {
|
||||
initialize_workspace_app(&mut app);
|
||||
let workspace = mock_workspace(&mut app);
|
||||
let (window, terminal) = workspace.update(&mut app, |workspace, ctx| {
|
||||
workspace.rename_tab(0, ctx);
|
||||
let terminal = workspace
|
||||
.active_tab_pane_group()
|
||||
.as_ref(ctx)
|
||||
.active_session_view(ctx)
|
||||
.expect("tab should contain a terminal");
|
||||
(ctx.window_id(), terminal)
|
||||
});
|
||||
assert!(workspace.read(&app, |workspace, ctx| {
|
||||
workspace.is_inline_rename_editor_focused(ctx)
|
||||
}));
|
||||
let focused_before = app.focused_view_id(window);
|
||||
|
||||
terminal.update(&mut app, |view, ctx| {
|
||||
view.handle_terminal_event(&ModelEvent::VisibleBootstrapBlock, ctx);
|
||||
});
|
||||
|
||||
assert_eq!(app.focused_view_id(window), focused_before);
|
||||
assert!(workspace.read(&app, |workspace, ctx| {
|
||||
workspace.is_inline_rename_editor_focused(ctx)
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn visible_bootstrap_block_leaves_focus_on_tab_group_rename_editor() {
|
||||
let _grouped_tabs_guard = FeatureFlag::GroupedTabs.override_enabled(true);
|
||||
App::test((), |mut app| async move {
|
||||
initialize_workspace_app(&mut app);
|
||||
let workspace = mock_workspace(&mut app);
|
||||
let (window, terminal, group_id) = workspace.update(&mut app, |workspace, ctx| {
|
||||
workspace.handle_action(
|
||||
&WorkspaceAction::SelectNewSessionMenuItem(NewSessionMenuItem::CreateNewTabGroup),
|
||||
ctx,
|
||||
);
|
||||
let group_id = workspace.tabs[0]
|
||||
.group_id
|
||||
.expect("active tab should be assigned to the new group");
|
||||
let terminal = workspace
|
||||
.active_tab_pane_group()
|
||||
.as_ref(ctx)
|
||||
.active_session_view(ctx)
|
||||
.expect("new tab group should contain a terminal");
|
||||
(ctx.window_id(), terminal, group_id)
|
||||
});
|
||||
workspace.update(&mut app, |workspace, ctx| {
|
||||
workspace.rename_tab_group(group_id, ctx);
|
||||
});
|
||||
assert!(workspace.read(&app, |workspace, ctx| {
|
||||
workspace.is_inline_rename_editor_focused(ctx)
|
||||
}));
|
||||
let focused_before = app.focused_view_id(window);
|
||||
|
||||
terminal.update(&mut app, |view, ctx| {
|
||||
view.handle_terminal_event(&ModelEvent::VisibleBootstrapBlock, ctx);
|
||||
});
|
||||
|
||||
assert_eq!(app.focused_view_id(window), focused_before);
|
||||
assert!(workspace.read(&app, |workspace, ctx| {
|
||||
workspace.is_inline_rename_editor_focused(ctx)
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user