diff options
Diffstat (limited to 'src/app/api/layouts.rs')
| -rw-r--r-- | src/app/api/layouts.rs | 77 |
1 files changed, 76 insertions, 1 deletions
diff --git a/src/app/api/layouts.rs b/src/app/api/layouts.rs index 2b83d027..709d9f39 100644 --- a/src/app/api/layouts.rs +++ b/src/app/api/layouts.rs @@ -4,7 +4,7 @@ use ratatui::layout::Direction; use crate::api::schema::{ EventData, EventEnvelope, EventKind, LayoutApplyParams, LayoutDescription, LayoutExportParams, - LayoutNode, LayoutPane, ResponseResult, SplitDirection, + LayoutNode, LayoutPane, LayoutSetSplitRatioParams, ResponseResult, SplitDirection, }; use crate::app::{App, Mode}; use crate::layout::{Node, PaneId}; @@ -214,6 +214,38 @@ impl App { encode_success(id, ResponseResult::LayoutApply { layout }) } + pub(super) fn handle_layout_set_split_ratio( + &mut self, + id: String, + params: LayoutSetSplitRatioParams, + ) -> String { + if !params.ratio.is_finite() { + return encode_error(id, "invalid_ratio", "ratio must be finite"); + } + let Some((ws_idx, tab_idx)) = self.resolve_layout_export_target(&LayoutExportParams { + tab_id: params.tab_id, + pane_id: params.pane_id, + }) else { + return encode_error(id, "layout_not_found", "layout target not found"); + }; + + let changed = self + .state + .workspaces + .get_mut(ws_idx) + .and_then(|ws| ws.tabs.get_mut(tab_idx)) + .is_some_and(|tab| tab.layout.set_ratio_at(¶ms.path, params.ratio)); + if !changed { + return encode_error(id, "split_not_found", "split path not found"); + } + + self.schedule_session_save(); + let Some(layout) = self.layout_description(ws_idx, tab_idx) else { + return encode_error(id, "layout_not_found", "layout unavailable"); + }; + encode_success(id, ResponseResult::LayoutApply { layout }) + } + fn resolve_layout_export_target(&self, params: &LayoutExportParams) -> Option<(usize, usize)> { match (params.tab_id.as_deref(), params.pane_id.as_deref()) { (Some(_), Some(_)) => None, @@ -636,6 +668,49 @@ mod tests { assert_eq!(pane.pane_id, Some(app.public_pane_id(0, right).unwrap())); } + #[test] + fn layout_set_split_ratio_updates_existing_split() { + let mut app = app_with_workspace(); + app.state.workspaces[0].test_split(Direction::Horizontal); + + let response = app.handle_layout_set_split_ratio( + "req".into(), + LayoutSetSplitRatioParams { + tab_id: None, + pane_id: None, + path: vec![], + ratio: 0.72, + }, + ); + + let success: SuccessResponse = serde_json::from_str(&response).unwrap(); + let ResponseResult::LayoutApply { layout } = success.result else { + panic!("expected layout apply response"); + }; + let LayoutNode::Split { ratio, .. } = layout.root else { + panic!("expected split layout root"); + }; + assert!((ratio - 0.72).abs() < f32::EPSILON); + } + + #[test] + fn layout_set_split_ratio_rejects_missing_split() { + let mut app = app_with_workspace(); + + let response = app.handle_layout_set_split_ratio( + "req".into(), + LayoutSetSplitRatioParams { + tab_id: None, + pane_id: None, + path: vec![], + ratio: 0.72, + }, + ); + + let error: ErrorResponse = serde_json::from_str(&response).unwrap(); + assert_eq!(error.error.code, "split_not_found"); + } + #[tokio::test] async fn layout_apply_replaces_tab_with_requested_tree() { let mut app = app_with_workspace(); |
