fix(ai): maximize rust web chat panel
This commit is contained in:
parent
26e812dba1
commit
a0787a579e
|
|
@ -35,10 +35,8 @@
|
|||
//! Functions that pull in `op_editor_ui::widgets::*` MUST live
|
||||
//! in this file (per spec §1.4). Phase B4 boundary check enforces.
|
||||
|
||||
use op_editor_core::ChatAnchor;
|
||||
use op_editor_ui::widgets::{
|
||||
LayoutCx, LocalePicker, Toolbar, TopBar, Widget, AI_CHAT_COLLAPSED_HEIGHT,
|
||||
AI_CHAT_COLLAPSED_WIDTH, AI_CHAT_HEIGHT, AI_CHAT_WIDTH, LOCALE_PICKER_WIDTH, TOOLBAR_WIDTH,
|
||||
LayoutCx, LocalePicker, Toolbar, TopBar, Widget, LOCALE_PICKER_WIDTH, TOOLBAR_WIDTH,
|
||||
TOP_BAR_HEIGHT,
|
||||
};
|
||||
use op_editor_ui::{Point2D, Rect, Theme};
|
||||
|
|
@ -46,6 +44,9 @@ use op_editor_ui::{Point2D, Rect, Theme};
|
|||
mod agent_settings_press;
|
||||
#[cfg(test)]
|
||||
mod agent_settings_press_tests;
|
||||
mod ai_chat_geometry;
|
||||
#[cfg(test)]
|
||||
mod ai_chat_geometry_tests;
|
||||
mod boolean_ops;
|
||||
#[cfg(test)]
|
||||
mod boolean_toolbar_tests;
|
||||
|
|
@ -719,14 +720,6 @@ impl WidgetHost {
|
|||
// `widget_host/keyboard.rs` — split out to keep this spine
|
||||
// file under the 800-line ceiling.
|
||||
|
||||
fn ai_chat_size(&self) -> (f32, f32) {
|
||||
if self.editor_state.chat.collapsed {
|
||||
(AI_CHAT_COLLAPSED_WIDTH, AI_CHAT_COLLAPSED_HEIGHT)
|
||||
} else {
|
||||
(AI_CHAT_WIDTH, AI_CHAT_HEIGHT)
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::widget_host) fn locale_picker_rect(&self, viewport_w: f32) -> Rect {
|
||||
let top_bar_rect = Rect {
|
||||
origin: Point2D::new(0.0, 0.0),
|
||||
|
|
@ -744,49 +737,6 @@ impl WidgetHost {
|
|||
}
|
||||
}
|
||||
|
||||
pub(in crate::widget_host) fn ai_chat_rect(
|
||||
&self,
|
||||
viewport_w: f32,
|
||||
viewport_h: f32,
|
||||
) -> Option<Rect> {
|
||||
let (cx0, cy0, cw, ch) = self.canvas_region(viewport_w, viewport_h);
|
||||
let (panel_w, panel_h) = self.ai_chat_size();
|
||||
if cw <= panel_w + AICHAT_INSET_LEFT + 16.0 || ch <= panel_h + 16.0 {
|
||||
return None;
|
||||
}
|
||||
if let Some(d) = self.chat_drag {
|
||||
return Some(Rect {
|
||||
origin: Point2D::new(d.pos_x, d.pos_y),
|
||||
size: Point2D::new(panel_w, panel_h),
|
||||
});
|
||||
}
|
||||
// `editor_state.chat.anchor` is op-editor-core's `ChatAnchor`;
|
||||
// shell-core's is a structurally identical four-variant enum.
|
||||
let (x, y) = match self.editor_state.chat.anchor {
|
||||
op_editor_core::ChatAnchor::TopLeft => {
|
||||
(cx0 + AICHAT_INSET_LEFT, cy0 + AICHAT_INSET_BOTTOM)
|
||||
}
|
||||
op_editor_core::ChatAnchor::TopRight => (
|
||||
cx0 + cw - panel_w - AICHAT_INSET_BOTTOM,
|
||||
cy0 + AICHAT_INSET_BOTTOM,
|
||||
),
|
||||
op_editor_core::ChatAnchor::BottomLeft => (
|
||||
cx0 + AICHAT_INSET_LEFT,
|
||||
cy0 + ch - panel_h - AICHAT_INSET_BOTTOM,
|
||||
),
|
||||
op_editor_core::ChatAnchor::BottomRight => (
|
||||
cx0 + cw - panel_w - AICHAT_INSET_BOTTOM,
|
||||
cy0 + ch - panel_h - AICHAT_INSET_BOTTOM,
|
||||
),
|
||||
};
|
||||
// `ChatAnchor` import kept for the `nearest` call in release.
|
||||
let _ = ChatAnchor::TopLeft;
|
||||
Some(Rect {
|
||||
origin: Point2D::new(x, y),
|
||||
size: Point2D::new(panel_w, panel_h),
|
||||
})
|
||||
}
|
||||
|
||||
pub(in crate::widget_host) fn layer_panel_rect(&self, viewport_h: f32) -> Rect {
|
||||
Rect {
|
||||
origin: Point2D::new(0.0, TOP_BAR_HEIGHT),
|
||||
|
|
|
|||
66
crates/op-host-web/src/widget_host/ai_chat_geometry.rs
Normal file
66
crates/op-host-web/src/widget_host/ai_chat_geometry.rs
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
//! AI chat floating-panel geometry for the web host.
|
||||
|
||||
use super::{WidgetHost, AICHAT_INSET_BOTTOM, AICHAT_INSET_LEFT};
|
||||
use op_editor_ui::widgets::{
|
||||
AI_CHAT_COLLAPSED_HEIGHT, AI_CHAT_COLLAPSED_WIDTH, AI_CHAT_HEIGHT, AI_CHAT_WIDTH,
|
||||
};
|
||||
use op_editor_ui::{Point2D, Rect};
|
||||
|
||||
impl WidgetHost {
|
||||
pub(in crate::widget_host) fn ai_chat_size(&self) -> (f32, f32) {
|
||||
if self.editor_state.chat.collapsed {
|
||||
(AI_CHAT_COLLAPSED_WIDTH, AI_CHAT_COLLAPSED_HEIGHT)
|
||||
} else {
|
||||
(AI_CHAT_WIDTH, AI_CHAT_HEIGHT)
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::widget_host) fn ai_chat_rect(
|
||||
&self,
|
||||
viewport_w: f32,
|
||||
viewport_h: f32,
|
||||
) -> Option<Rect> {
|
||||
let (cx0, cy0, cw, ch) = self.canvas_region(viewport_w, viewport_h);
|
||||
if self.editor_state.chat.maximized && !self.editor_state.chat.collapsed {
|
||||
let inset = 12.0;
|
||||
if cw <= inset * 2.0 + 16.0 || ch <= inset * 2.0 + 16.0 {
|
||||
return None;
|
||||
}
|
||||
return Some(Rect {
|
||||
origin: Point2D::new(cx0 + inset, cy0 + inset),
|
||||
size: Point2D::new(cw - inset * 2.0, ch - inset * 2.0),
|
||||
});
|
||||
}
|
||||
let (panel_w, panel_h) = self.ai_chat_size();
|
||||
if cw <= panel_w + AICHAT_INSET_LEFT + 16.0 || ch <= panel_h + 16.0 {
|
||||
return None;
|
||||
}
|
||||
if let Some(d) = self.chat_drag {
|
||||
return Some(Rect {
|
||||
origin: Point2D::new(d.pos_x, d.pos_y),
|
||||
size: Point2D::new(panel_w, panel_h),
|
||||
});
|
||||
}
|
||||
let (x, y) = match self.editor_state.chat.anchor {
|
||||
op_editor_core::ChatAnchor::TopLeft => {
|
||||
(cx0 + AICHAT_INSET_LEFT, cy0 + AICHAT_INSET_BOTTOM)
|
||||
}
|
||||
op_editor_core::ChatAnchor::TopRight => (
|
||||
cx0 + cw - panel_w - AICHAT_INSET_BOTTOM,
|
||||
cy0 + AICHAT_INSET_BOTTOM,
|
||||
),
|
||||
op_editor_core::ChatAnchor::BottomLeft => (
|
||||
cx0 + AICHAT_INSET_LEFT,
|
||||
cy0 + ch - panel_h - AICHAT_INSET_BOTTOM,
|
||||
),
|
||||
op_editor_core::ChatAnchor::BottomRight => (
|
||||
cx0 + cw - panel_w - AICHAT_INSET_BOTTOM,
|
||||
cy0 + ch - panel_h - AICHAT_INSET_BOTTOM,
|
||||
),
|
||||
};
|
||||
Some(Rect {
|
||||
origin: Point2D::new(x, y),
|
||||
size: Point2D::new(panel_w, panel_h),
|
||||
})
|
||||
}
|
||||
}
|
||||
27
crates/op-host-web/src/widget_host/ai_chat_geometry_tests.rs
Normal file
27
crates/op-host-web/src/widget_host/ai_chat_geometry_tests.rs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
use super::WidgetHost;
|
||||
|
||||
#[test]
|
||||
fn ai_chat_maximize_click_expands_panel_geometry_like_ts() {
|
||||
let mut host = WidgetHost::new();
|
||||
let viewport_w = 1200.0;
|
||||
let viewport_h = 800.0;
|
||||
let before = host
|
||||
.ai_chat_rect(viewport_w, viewport_h)
|
||||
.expect("chat panel visible");
|
||||
let x = before.origin.x + before.size.x - 16.0 - 50.0 + 9.0;
|
||||
let y = before.origin.y + 17.0;
|
||||
|
||||
assert!(host.apply_click(x, y, viewport_w, viewport_h));
|
||||
|
||||
let after = host
|
||||
.ai_chat_rect(viewport_w, viewport_h)
|
||||
.expect("maximized chat panel visible");
|
||||
let (cx0, cy0, cw, ch) = host.canvas_region(viewport_w, viewport_h);
|
||||
assert!(host.editor_state.chat.maximized);
|
||||
assert_eq!(after.origin.x, cx0 + 12.0);
|
||||
assert_eq!(after.origin.y, cy0 + 12.0);
|
||||
assert_eq!(after.size.x, cw - 24.0);
|
||||
assert_eq!(after.size.y, ch - 24.0);
|
||||
assert!(after.size.x > before.size.x);
|
||||
assert!(after.size.y > before.size.y);
|
||||
}
|
||||
Loading…
Reference in a new issue