diff --git a/crates/maestro-tui/src/app.rs b/crates/maestro-tui/src/app.rs index 379ac41..cd84f48 100644 --- a/crates/maestro-tui/src/app.rs +++ b/crates/maestro-tui/src/app.rs @@ -797,10 +797,14 @@ impl App { /// Open a stream for `sid` if one isn't already running. fn attach(&mut self, sid: Uuid) { + self.attach_with_view(sid, self.session_view(sid)); + } + + fn attach_with_view(&mut self, sid: Uuid, view: SessionView) { if self.is_attached(sid) { return; } - match self.session_view(sid) { + match view { SessionView::Terminal => match self.daemon.attach_url(sid) { Ok(ws_url) => { let handle = SessionTask::spawn(ws_url, 24, 80); @@ -1493,7 +1497,7 @@ impl App { if session.view == SessionView::Structured { self.state.mode = Mode::Normal; } - self.attach(sid); + self.attach_with_view(sid, session.view); } Err(e) => { // A failed POST belongs in the form it came from (#64), not a @@ -3021,6 +3025,53 @@ mod tests { ); } + #[tokio::test] + async fn create_response_selects_transport_before_catalog_event() { + use axum::routing::post; + use axum::Json; + + async fn spawn_session( + Json(req): Json, + ) -> Json { + let id = match req.view { + SessionView::Terminal => Uuid::from_u128(187), + SessionView::Structured => Uuid::from_u128(188), + }; + let mut session = test_session(id, req.cwd.to_str().unwrap()); + session.view = req.view; + session.command = req.command; + Json(session) + } + + let router = + axum::Router::new().route(maestro_protocol::routes::SESSIONS, post(spawn_session)); + let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); + let base = format!("http://{}", listener.local_addr().unwrap()); + tokio::spawn(async move { axum::serve(listener, router).await.unwrap() }); + + let mut app = make_app_at(&base); + let request = |view| CreateSessionRequest { + view, + cwd: std::path::PathBuf::from("/srv/work"), + command: "claude".into(), + env: Default::default(), + metadata: Default::default(), + worktree: None, + }; + + let structured_id = Uuid::from_u128(188); + app.submit_spawn(request(SessionView::Structured)).await; + assert!(app.catalog.snapshot().sessions.is_empty()); + assert!(app.structured.contains_key(&structured_id)); + assert!(app.emulators.get(structured_id).is_none()); + + let terminal_id = Uuid::from_u128(187); + app.submit_spawn(request(SessionView::Terminal)).await; + assert!(app.catalog.snapshot().sessions.is_empty()); + assert!(app.emulators.get(terminal_id).is_some()); + assert!(!app.structured.contains_key(&terminal_id)); + } + /// The full data load against a live daemon stub: presets replace the /// built-ins, projects land sorted, and the first generated name seeds /// the field.