use super::super::*;
use super::{MissionControlApp, completion_worker::CompletionWorker};
use crate::config::{HeaderPanel, PanelLayoutSettings, set_panel_layout};
use crate::tui::state::ModalKind;
pub(super) fn panel_layout(ui: &state::MissionControlState) -> PanelLayoutSettings {
PanelLayoutSettings {
open: ui
.modals
.header_modals
.iter()
.filter_map(|modal| match modal {
ModalKind::Summary => Some(HeaderPanel::Summary),
ModalKind::ActivityTree => Some(HeaderPanel::ActivityTree),
ModalKind::SessionFiles => Some(HeaderPanel::SessionFiles),
_ => None,
})
.collect(),
pinned: ui.modals.header_modals_pinned,
opened_by_pin: ui.modals.header_modals_opened_by_pin,
..Default::default()
}
}
pub(super) fn restore_panel_layout(
ui: &mut state::MissionControlState,
layout: &PanelLayoutSettings,
) {
if ui.transcript_only_layout {
return;
}
ui.modals.header_modals.clear();
for panel in &layout.open {
let modal = match panel {
HeaderPanel::Summary => ModalKind::Summary,
HeaderPanel::ActivityTree => ModalKind::ActivityTree,
HeaderPanel::SessionFiles => ModalKind::SessionFiles,
};
if !ui.modals.header_modals.contains(&modal) {
ui.modals.header_modals.push(modal);
}
}
ui.modals.header_modals_pinned = layout.pinned && !ui.modals.header_modals.is_empty();
ui.modals.header_modals_opened_by_pin = layout.opened_by_pin && ui.modals.header_modals_pinned;
ui.focus_prompt();
}
#[derive(Default)]
pub(super) struct PanelLayoutPersistenceRuntime {
worker: Option<CompletionWorker<()>>,
queued: Option<PanelLayoutSettings>,
retained_failure: Option<String>,
}
impl PanelLayoutPersistenceRuntime {
pub(super) fn is_pending(&self) -> bool {
self.worker.is_some() || self.queued.is_some()
}
fn record_result(&mut self, result: Result<(), String>) {
self.retained_failure = result
.err()
.map(|error| format!("failed to save panel layout: {error}"));
}
pub(super) fn cleanup(&mut self, paths: &crate::config::McPaths) -> Option<String> {
if let Some(worker) = self.worker.take() {
self.record_result(worker.join().map(|_| ()));
}
if let Some(layout) = self.queued.take() {
self.record_result(set_panel_layout(paths, &layout).map_err(|error| error.to_string()));
}
self.retained_failure.take()
}
}
impl MissionControlApp {
pub(super) fn persist_panel_layout_if_changed(
&mut self,
before: &PanelLayoutSettings,
ui: &mut state::MissionControlState,
) {
if ui.transcript_only_layout {
return;
}
let layout = panel_layout(ui);
if &layout == before {
return;
}
self.settings.tui.panel_layout = layout.clone();
self.panel_layout_persistence.queued = Some(layout);
self.reap_panel_layout_persistence(ui);
}
pub(super) fn reap_panel_layout_persistence(
&mut self,
ui: &mut state::MissionControlState,
) -> bool {
let runtime = &mut self.panel_layout_persistence;
if !runtime.is_pending() {
return false;
}
if let Some(worker) = &runtime.worker {
if !worker.is_finished() {
return false;
}
let worker = runtime.worker.take().expect("finished panel layout worker");
runtime.record_result(worker.join().map(|_| ()));
}
if let Some(layout) = runtime.queued.take() {
let paths = self.config.paths.clone();
match CompletionWorker::spawn("panel-layout-save", self.events.clone(), move || {
set_panel_layout(&paths, &layout).map_err(|error| error.to_string())
}) {
Ok(worker) => runtime.worker = Some(worker),
Err(error) => runtime.record_result(Err(error.to_string())),
}
}
if let Some(error) = &runtime.retained_failure
&& ui.status != *error
{
ui.status = error.clone();
return true;
}
false
}
}