use crate::AppModel;
use crate::cli_outbound::Cli;
use crate::nav::menu::Seat;
use crate::nav::tabs::{Kind, Tab};
use crate::theme;
use super::ShellState;
use super::menus::Target;
const WORKSPACES_HINT: &str = "A workspace walls off one sphere of work — personal, an employer, a client. \
Its conversations, its config, and the balls it claims live inside that wall \
and never touch another workspace's.";
const TAB_HINT: &str = "Switch to this workspace: the conversation list, the composer and the balls \
section all re-aim at it. Right-click for its delete / unpin entries. The ↑ / ↓ \
roster walk crosses workspaces too, landing here on its own.";
const OVERFLOW_HINT: &str = "Workspaces that are real but not regimes — checkouts yog did not raise, and \
read-only replays. Open the menu to focus one, or ★ to pin it as a tab. No key of \
its own: Tab reaches it, Space opens it.";
pub fn render(ui: &mut egui::Ui, model: &mut AppModel, state: &mut ShellState, lernie: &Cli) {
let rows = super::chrome::ws_rows(model);
ui.horizontal(|ui| {
let total = crate::nav::tabs::strip_total(&rows);
if total > 0 {
ui.colored_label(theme::BRAZEN, format!("⚑ {total} need attention"));
} else {
ui.weak("⚑ nothing stirs");
}
if ui
.add_enabled(total > 0, egui::Button::new("⏭ next"))
.on_hover_text(
"jump to the next conversation needing attention. No key of its own: \
Tab reaches it, Space presses it — the ↑ / ↓ walk gets there too.",
)
.on_disabled_hover_text("nothing needs attention — nothing to jump to")
.clicked()
{
model.jump_next_attention();
super::focus::request(state);
}
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
let bar = crate::nav::tabs::build(&rows, model.focused_ws_name().as_deref());
overflow_menu(ui, model, state, &bar);
if ui
.button("new")
.on_hover_text("new workspace — name a fresh sphere wall (w)")
.clicked()
{
super::new_ws::open(state);
}
for tab in bar.tabs.iter().rev() {
workspace_tab(ui, model, state, lernie, tab);
}
ui.label("Workspaces: ").on_hover_text(WORKSPACES_HINT);
});
});
}
fn workspace_tab(
ui: &mut egui::Ui,
model: &mut AppModel,
state: &mut ShellState,
lernie: &Cli,
tab: &Tab,
) {
let badge = if tab.attention > 0 {
format!("⚑{} ", tab.attention)
} else {
String::new()
};
let label = ui
.selectable_label(
tab.selected,
format!("{badge}{}{}", tab.name, tab.kind_suffix()),
)
.on_hover_text(TAB_HINT);
if label.clicked() {
super::focus::workspace(model, state, &tab.name);
}
super::menus::attach(
&label,
Seat::WorkspaceTab {
named: tab.kind == Kind::Named,
pinned: tab.pinned,
},
&Target::Tab(tab.clone()),
model,
state,
lernie,
);
}
fn overflow_menu(
ui: &mut egui::Ui,
model: &mut AppModel,
state: &mut ShellState,
bar: &crate::nav::tabs::TabBar,
) {
if bar.overflow.is_empty() {
return;
}
let attention = bar.overflow_attention();
let title = if attention > 0 {
format!("⋯ ⚑{attention}")
} else {
"⋯".to_owned()
};
ui.menu_button(title, |ui| {
for entry in &bar.overflow {
ui.horizontal(|ui| {
let badge = if entry.attention > 0 {
format!(" ⚑{}", entry.attention)
} else {
String::new()
};
let title = format!("{}{}{badge}", entry.name, entry.kind_suffix());
if ui
.selectable_label(entry.selected, title)
.on_hover_text(TAB_HINT)
.clicked()
{
super::focus::workspace(model, state, &entry.name);
ui.close_menu();
}
let hint = if entry.pinned {
"pinned as a tab — click to unpin. No key of its own: Tab reaches \
it, Space presses it."
} else {
"pin as a tab. No key of its own: Tab reaches it, Space presses it."
};
if ui
.selectable_label(entry.pinned, "★")
.on_hover_text(hint)
.clicked()
{
model.toggle_pin(&entry.name);
}
});
}
})
.response
.on_hover_text(OVERFLOW_HINT);
}