use std::path::Path;
use super::super::{InspectorState, ShellState};
use super::{rail, work};
use crate::AppModel;
use crate::files_view::{FilesView, Preview};
use crate::git_tree::AgentState;
use crate::inboxview::{InboxEntry, list_inbox};
use crate::inspector::TabData;
use crate::keymap::InspectorTab;
use crate::nav::convs::{display_name_of, root_of};
use crate::steps_view::{self, StepDetail, StepsView};
use crate::transcript::{self, Transcript};
pub(super) struct Focus {
pub(super) agent_id: String,
pub(super) tip: String,
pub(super) state: AgentState,
}
pub(super) fn tab_data(
active: InspectorTab,
model: &AppModel,
state: &mut ShellState,
ws: &Path,
focus: &Focus,
) -> TabData {
let (agent_id, tip, agent_state) = (&focus.agent_id, &focus.tip, focus.state);
let in_flight = agent_state == AgentState::InFlight;
let speaker = {
let agents = model
.focused_tree()
.map(|t| t.agents.clone())
.unwrap_or_default();
let root = root_of(&agents, agent_id).unwrap_or_else(|| agent_id.clone());
display_name_of(&agents, &root)
};
let steps = state
.inspector
.steps_memo
.read(
model.derivation(),
(ws.to_path_buf(), agent_id.clone(), agent_state),
&mut || steps_view::build(ws, agent_id, agent_state),
)
.clone();
let live = build_transcript(model, &mut state.inspector, ws, agent_id, in_flight);
let history = rail::build(
model,
&mut state.inspector,
ws,
agent_id,
&speaker,
&steps,
&live,
);
let pin = rail::pinned(&history, state.inspector.eph.notch_sel);
let files = build_files(
active,
model,
&mut state.inspector,
ws,
agent_id,
pin.as_ref(),
);
let picked = state.inspector.eph.work_sel.clone();
let work = work::build(active, model, &mut state.inspector, ws);
let work_patch = work::patch(model, &mut state.inspector, ws, &work, picked.as_ref());
TabData {
transcript: rail::transcript(&live, pin.as_ref()),
speaker,
raw: state.inspector.raw,
auto: model.transcript_auto_expand(),
step_sel: state.inspector.step_sel,
step_detail: build_detail(active, ws, agent_id, &steps, state.inspector.step_sel),
step_tab: state.inspector.step_tab,
steps,
inbox: build_inbox(active, ws, agent_id),
file_preview: build_file_preview(
active,
ws,
agent_id,
&files,
state.inspector.eph.files_sel,
pin.as_ref(),
),
files,
work,
work_patch,
governing: (active == InspectorTab::Config)
.then(|| rail::governing(ws, tip, pin.as_ref()))
.flatten(),
rail: history,
pin,
}
}
fn build_files(
tab: InspectorTab,
model: &AppModel,
inspector: &mut crate::shell::InspectorState,
ws: &Path,
agent: &str,
pin: Option<&crate::rail::Pin>,
) -> FilesView {
if tab == InspectorTab::Files {
rail::files(model, inspector, ws, agent, pin)
} else {
FilesView::default()
}
}
fn build_file_preview(
tab: InspectorTab,
ws: &Path,
agent: &str,
files: &FilesView,
sel: Option<usize>,
pin: Option<&crate::rail::Pin>,
) -> Option<Preview> {
if tab != InspectorTab::Files {
return None;
}
let FilesView::Present { entries, .. } = files else {
return None;
};
let entry = entries.get(sel?)?;
if entry.is_dir {
return None;
}
Some(rail::preview(ws, agent, &entry.rel_path, pin))
}
pub(in crate::shell) fn build_transcript(
model: &AppModel,
inspector: &mut InspectorState,
ws: &Path,
agent: &str,
in_flight: bool,
) -> std::sync::Arc<Transcript> {
let committed = std::sync::Arc::clone(inspector.tx_memo.read(
model.derivation(),
(ws.to_path_buf(), agent.to_string()),
&mut || std::sync::Arc::new(transcript::build(ws, agent)),
));
let stream = in_flight
.then(|| {
let tree = model.tree(ws)?;
tree.agents.iter().find(|a| a.agent_id == agent)
})
.flatten()
.map(|a| a.stream.clone());
match stream {
Some(stream) => std::sync::Arc::new(committed.with_live(&stream)),
None => committed,
}
}
fn build_detail(
tab: InspectorTab,
ws: &Path,
agent: &str,
steps: &StepsView,
sel: Option<usize>,
) -> Option<StepDetail> {
if tab != InspectorTab::Steps {
return None;
}
let seq = &steps.steps.get(sel?)?.seq;
Some(steps_view::detail(ws, agent, seq))
}
fn build_inbox(tab: InspectorTab, ws: &Path, agent: &str) -> Vec<InboxEntry> {
if tab == InspectorTab::Inbox {
list_inbox(ws, agent)
} else {
Vec::new()
}
}