chore(editor): remove the inert palette button from the chat footer
The affordance has no feature behind it yet; ship it when it does.
This commit is contained in:
parent
a963502d1a
commit
6e41b68474
|
|
@ -32,8 +32,6 @@ pub enum ChatFooterButton {
|
|||
AgentTeam,
|
||||
/// Paperclip attachment button.
|
||||
AddAttachment,
|
||||
/// Palette button — currently inert (#27 future affordance).
|
||||
Palette,
|
||||
/// Send button in its normal idle state.
|
||||
Send,
|
||||
/// Stop button shown while a response streams.
|
||||
|
|
|
|||
|
|
@ -359,8 +359,6 @@ pub(crate) struct FooterLayout {
|
|||
pub(crate) agent_team: Rect,
|
||||
/// Paperclip attach button — bare icon, muted.
|
||||
pub(crate) attach: Rect,
|
||||
/// Palette button — bare icon, muted, currently inert (#27 spec).
|
||||
pub(crate) palette: Rect,
|
||||
/// Stop circle — shown only while a turn streams.
|
||||
pub(crate) stop: Rect,
|
||||
/// Send/stop circle — the primary action button.
|
||||
|
|
@ -633,7 +631,7 @@ impl<'a> Widget for AIChatPlaceholder<'a> {
|
|||
}
|
||||
|
||||
// Bottom toolbar (#27) — single row:
|
||||
// model pill | ⚡ speed chip | 📎 attach | 🎨 palette | [gap] | ◻ stop | ↑ send
|
||||
// model pill | ⚡ speed chip | 📎 attach | [gap] | ◻ stop | ↑ send
|
||||
let toolbar_y = input_rect.origin.y + input_area_h + attach_h;
|
||||
let toolbar_center_y = toolbar_y + INPUT_TOOLBAR_HEIGHT / 2.0;
|
||||
let footer = self.footer_layout(rect, self.input_rect(rect), toolbar_y);
|
||||
|
|
|
|||
|
|
@ -636,10 +636,10 @@ fn bottom_toolbar_layout_model_pill_is_leftmost() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn bottom_toolbar_layout_order_is_model_speed_attach_palette_send() {
|
||||
// #38: ⚡/📎/🎨 moved right; #42: stop shares the send slot. Full
|
||||
fn bottom_toolbar_layout_order_is_model_speed_attach_send() {
|
||||
// #38: ⚡/📎 moved right; #42: stop shares the send slot. Full
|
||||
// left-to-right order is:
|
||||
// model (LEFT) | [gap] | speed | attach | palette | send (RIGHT)
|
||||
// model (LEFT) | [gap] | speed | attach | send (RIGHT)
|
||||
let s = EditorState::new();
|
||||
let panel = AIChatPlaceholder::from_editor(&s);
|
||||
let rect = Rect::xywh(0.0, 0.0, AI_CHAT_WIDTH, AI_CHAT_HEIGHT);
|
||||
|
|
@ -647,7 +647,7 @@ fn bottom_toolbar_layout_order_is_model_speed_attach_palette_send() {
|
|||
let toolbar_top = input.origin.y + INPUT_AREA_HEIGHT;
|
||||
let footer = panel.footer_layout(rect, input, toolbar_top);
|
||||
|
||||
// Left-to-right order: model < speed < attach < palette < send
|
||||
// Left-to-right order: model < speed < attach < send
|
||||
assert!(
|
||||
footer.model.origin.x < footer.speed.origin.x,
|
||||
"model left of speed"
|
||||
|
|
@ -657,19 +657,15 @@ fn bottom_toolbar_layout_order_is_model_speed_attach_palette_send() {
|
|||
"speed left of attach"
|
||||
);
|
||||
assert!(
|
||||
footer.attach.origin.x < footer.palette.origin.x,
|
||||
"attach left of palette"
|
||||
);
|
||||
assert!(
|
||||
footer.palette.origin.x < footer.send.origin.x,
|
||||
"palette left of send"
|
||||
footer.attach.origin.x < footer.send.origin.x,
|
||||
"attach left of send"
|
||||
);
|
||||
// #42: stop shares the send slot (toggle in place), not a separate button.
|
||||
assert!(
|
||||
(footer.stop.origin.x - footer.send.origin.x).abs() < 0.01,
|
||||
"stop shares the send slot"
|
||||
);
|
||||
// #38 specific: speed/attach/palette must all be RIGHT of the model pill.
|
||||
// #38 specific: speed/attach must all be RIGHT of the model pill.
|
||||
let model_right = footer.model.origin.x + footer.model.size.x;
|
||||
assert!(
|
||||
footer.speed.origin.x > model_right + 4.0,
|
||||
|
|
@ -710,28 +706,6 @@ fn hit_test_stop_circle_only_active_while_streaming() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hit_test_palette_button_is_inert() {
|
||||
// Palette is a no-op in #27; clicking it returns Inside (consumed).
|
||||
let mut s = EditorState::new();
|
||||
seed_available_model(&mut s);
|
||||
let panel = AIChatPlaceholder::from_editor(&s);
|
||||
let rect = Rect::xywh(0.0, 0.0, AI_CHAT_WIDTH, AI_CHAT_HEIGHT);
|
||||
let input = panel.input_rect(rect);
|
||||
let toolbar_top = input.origin.y + INPUT_AREA_HEIGHT;
|
||||
let footer = panel.footer_layout(rect, input, toolbar_top);
|
||||
let palette_center = Point2D::new(
|
||||
footer.palette.origin.x + footer.palette.size.x / 2.0,
|
||||
footer.palette.origin.y + footer.palette.size.y / 2.0,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
panel.hit_test(rect, palette_center),
|
||||
Some(AIChatHit::Inside),
|
||||
"palette button should be consumed (inert) in #27 layout"
|
||||
);
|
||||
}
|
||||
|
||||
// ── Task 5.6 Parallel Agents picker tests ────────────────────────────────────
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -296,7 +296,7 @@ fn from_editor_picks_up_chat_button_press_targets() {
|
|||
#[test]
|
||||
fn paint_footer_neutral_hovers_use_visible_feedback() {
|
||||
// #27 restyle: model pill and speed/attach have hover washes; send
|
||||
// is now a filled circle (bg always present); palette is bare icon.
|
||||
// is now a filled circle (bg always present).
|
||||
// Test the subset that emit a neutral wash rect on hover.
|
||||
let cases = [
|
||||
op_editor_core::ChatFooterButton::ModelPicker,
|
||||
|
|
@ -320,7 +320,6 @@ fn paint_footer_neutral_hovers_use_visible_feedback() {
|
|||
op_editor_core::ChatFooterButton::SpeedChip => footer.speed,
|
||||
op_editor_core::ChatFooterButton::AgentTeam => footer.agent_team,
|
||||
op_editor_core::ChatFooterButton::AddAttachment => footer.attach,
|
||||
op_editor_core::ChatFooterButton::Palette => footer.palette,
|
||||
op_editor_core::ChatFooterButton::Send => footer.send,
|
||||
op_editor_core::ChatFooterButton::Stop => unreachable!(),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//! Footer toolbar geometry and paint for the AI chat panel.
|
||||
//!
|
||||
//! Computes the bottom single-row toolbar rects:
|
||||
//! model pill (LEFT) | [gap] | ⚡ parallel-agents chip | attach | palette | send (RIGHT)
|
||||
//! model pill (LEFT) | [gap] | ⚡ parallel-agents chip | attach | send (RIGHT)
|
||||
//!
|
||||
//! As of #38 the ⚡/📎/🎨 cluster moved from the LEFT (between model and gap) to
|
||||
//! the RIGHT. As of #42 the cluster sits snug against the single send/stop
|
||||
|
|
@ -46,7 +46,7 @@ const PARALLEL_AGENTS_ROW_H: f32 = PARALLEL_AGENTS_ROW_H_PUB;
|
|||
/// Width of the Parallel Agents picker overlay.
|
||||
const PARALLEL_AGENTS_PICKER_W: f32 = 130.0;
|
||||
|
||||
/// Width of the bare-icon buttons (attach, palette) in the toolbar.
|
||||
/// Width of the bare-icon buttons (attach) in the toolbar.
|
||||
pub(crate) const FOOTER_ICON_W: f32 = 24.0;
|
||||
|
||||
/// Diameter of the circular send/stop buttons.
|
||||
|
|
@ -73,7 +73,7 @@ impl<'a> AIChatPlaceholder<'a> {
|
|||
// Agent-team chip — zero-width logical rect for schema compat; contains() = false.
|
||||
let agent_team = Rect::xywh(model_x + FOOTER_MODEL_PILL_W, cy - 11.0, 0.0, 22.0);
|
||||
|
||||
// Right cluster (#38/#42 layout) — ⚡ chip | 📎 attach | 🎨 palette | send,
|
||||
// Right cluster (#38/#42 layout) — ⚡ chip | 📎 attach | send,
|
||||
// laid out right-to-left from right_edge (stop shares the send slot).
|
||||
let right_edge = rect.origin.x + rect.size.x - PAD;
|
||||
|
||||
|
|
@ -90,17 +90,9 @@ impl<'a> AIChatPlaceholder<'a> {
|
|||
// against the single circle with no reserved gap between 🎨 and send (#42).
|
||||
let stop = send;
|
||||
|
||||
// Palette icon — immediately left of the send/stop circle.
|
||||
let palette_x = send.origin.x - FOOTER_GAP - FOOTER_ICON_W;
|
||||
let palette = Rect::xywh(
|
||||
palette_x,
|
||||
cy - FOOTER_ICON_W / 2.0,
|
||||
FOOTER_ICON_W,
|
||||
FOOTER_ICON_W,
|
||||
);
|
||||
|
||||
// Attach icon — immediately left of palette.
|
||||
let attach_x = palette_x - FOOTER_GAP - FOOTER_ICON_W;
|
||||
// Attach icon — immediately left of the send/stop circle. (The
|
||||
// palette slot is removed until the feature behind it exists.)
|
||||
let attach_x = send.origin.x - FOOTER_GAP - FOOTER_ICON_W;
|
||||
let attach = Rect::xywh(
|
||||
attach_x,
|
||||
cy - FOOTER_ICON_W / 2.0,
|
||||
|
|
@ -118,7 +110,6 @@ impl<'a> AIChatPlaceholder<'a> {
|
|||
speed,
|
||||
agent_team,
|
||||
attach,
|
||||
palette,
|
||||
stop,
|
||||
send,
|
||||
}
|
||||
|
|
@ -276,7 +267,7 @@ pub(crate) fn paint_parallel_agents_picker(
|
|||
|
||||
/// Paint the bottom-toolbar row of the AI chat panel (#27 / #32 layout).
|
||||
///
|
||||
/// Draws: model pill | [gap] | ⚡ parallel-agents chip | 📎 attach | 🎨 palette | ↑ send (◻ stop while streaming)
|
||||
/// Draws: model pill | [gap] | ⚡ parallel-agents chip | 📎 attach | ↑ send (◻ stop while streaming)
|
||||
///
|
||||
/// The ⚡ chip shows "{N}x" in gold (N = `agent_team_size`) and opens the
|
||||
/// Parallel Agents picker on click.
|
||||
|
|
@ -444,32 +435,6 @@ pub(crate) fn paint_bottom_toolbar(
|
|||
1.4,
|
||||
);
|
||||
|
||||
// --- Palette button — bare icon, inert (#27 future affordance) ---
|
||||
let palette_rect = footer.palette;
|
||||
if widget.footer_hover == Some(ChatFooterButton::Palette)
|
||||
|| widget.footer_pressed == Some(ChatFooterButton::Palette)
|
||||
{
|
||||
cx.backend.fill_round_rect(
|
||||
palette_rect,
|
||||
6.0,
|
||||
chat_neutral_feedback_color(
|
||||
&widget.theme,
|
||||
widget.footer_pressed == Some(ChatFooterButton::Palette),
|
||||
),
|
||||
);
|
||||
}
|
||||
draw_icon(
|
||||
cx.backend,
|
||||
Icon::Palette,
|
||||
Point2D::new(
|
||||
palette_rect.origin.x + attach_icon_offset,
|
||||
palette_rect.origin.y + attach_icon_offset,
|
||||
),
|
||||
12.0,
|
||||
widget.theme.muted_foreground,
|
||||
1.4,
|
||||
);
|
||||
|
||||
// --- Stop circle — shown only while a turn streams ---
|
||||
if streaming {
|
||||
let stop_rect = footer.stop;
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ impl<'a> AIChatPlaceholder<'a> {
|
|||
return Some(AIChatHit::FocusInput);
|
||||
}
|
||||
// Bottom toolbar strip (#27 layout):
|
||||
// model pill | ⚡ speed chip | 📎 attach | 🎨 palette | [gap] | ◻ stop | ↑ send
|
||||
// model pill | ⚡ speed chip | 📎 attach | [gap] | ◻ stop | ↑ send
|
||||
if point.y >= toolbar_top {
|
||||
let footer = self.footer_layout(rect, input_rect, toolbar_top);
|
||||
let streaming = self.is_streaming();
|
||||
|
|
@ -220,10 +220,6 @@ impl<'a> AIChatPlaceholder<'a> {
|
|||
AIChatHit::AddAttachment
|
||||
});
|
||||
}
|
||||
// Palette is inert in #27 — consume the click so canvas is not affected.
|
||||
if (footer.palette).contains(point) {
|
||||
return Some(AIChatHit::Inside);
|
||||
}
|
||||
// Stop circle — only a live target while streaming.
|
||||
if streaming && (footer.stop).contains(point) {
|
||||
return Some(AIChatHit::Stop);
|
||||
|
|
@ -451,9 +447,6 @@ impl<'a> AIChatPlaceholder<'a> {
|
|||
if !streaming && (footer.attach).contains(point) {
|
||||
return Some(op_editor_core::ChatFooterButton::AddAttachment);
|
||||
}
|
||||
if (footer.palette).contains(point) {
|
||||
return Some(op_editor_core::ChatFooterButton::Palette);
|
||||
}
|
||||
if streaming && (footer.stop).contains(point) {
|
||||
return Some(op_editor_core::ChatFooterButton::Stop);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue