use std::sync::LazyLock;
use crate::*;
pub(crate) fn scene_select(frame: u64, theme: &Theme) -> Element {
let items: Vec<Line<'static>> = ["Open file…", "Save", "Save As…", "Toggle theme", "Quit"]
.iter()
.map(|s| Line::from(Span::styled((*s).to_string(), theme.text_style())))
.collect();
let mut state = SelectState::new();
let target = (frame / 10) % items.len() as u64;
let down = Event::Key(Key::new(KeyCode::Down));
for _ in 0..target {
let _ = state.handle(&down, items.len());
}
view! {
col {
grow(1) { node(SelectList::new(items, &state)) }
}
}
}
static TREE_DEMO_ROWS: LazyLock<Vec<TreeRow<'static, u8>>> = LazyLock::new(|| {
vec![
TreeRow::root(1, "workspace", true),
TreeRow::new(2, Some(1), 1, "src", true),
TreeRow::new(3, Some(2), 2, "components", true),
TreeRow::new(4, Some(3), 3, "tree_list.rs", false),
TreeRow::new(5, Some(2), 2, "lib.rs", false),
TreeRow::new(6, Some(1), 1, "tests", true),
TreeRow::new(7, Some(6), 2, "integration.rs", false),
TreeRow::root(8, "target", true),
]
});
static TREE_DEMO_STATES: LazyLock<Vec<TreeState<u8>>> = LazyLock::new(|| {
let collapsed = TreeState::with_selected(1);
let mut expanded = TreeState::with_selected(2);
expanded.expand(1);
let mut deep = TreeState::with_selected(4);
deep.expand(1);
deep.expand(2);
deep.expand(3);
deep.set_offset(1);
vec![collapsed, expanded, deep]
});
pub(crate) fn scene_tree_list(frame: u64, _theme: &Theme) -> Element {
let state = &TREE_DEMO_STATES[((frame / 24) as usize) % TREE_DEMO_STATES.len()];
element(TreeList::new(&TREE_DEMO_ROWS, state).viewport(7))
}
struct KeyedDemoRow {
id: u64,
name: &'static str,
state: &'static str,
age: u16,
}
static KEYED_ROWS: [KeyedDemoRow; 6] = [
KeyedDemoRow {
id: 101,
name: "index workspace",
state: "running",
age: 4,
},
KeyedDemoRow {
id: 102,
name: "review patch",
state: "queued",
age: 12,
},
KeyedDemoRow {
id: 103,
name: "run checks",
state: "running",
age: 19,
},
KeyedDemoRow {
id: 104,
name: "refresh graph",
state: "queued",
age: 27,
},
KeyedDemoRow {
id: 105,
name: "stream logs",
state: "running",
age: 34,
},
KeyedDemoRow {
id: 106,
name: "publish report",
state: "queued",
age: 41,
},
];
static KEYED_REORDERED: [usize; 6] = [4, 0, 3, 2, 5, 1];
static KEYED_ORIGINAL: [usize; 6] = [0, 1, 2, 3, 4, 5];
static KEYED_FILTERED: [usize; 2] = [0, 4];
static KEYED_SELECTION: KeyedSelectState<u64> = KeyedSelectState::with_selected(103);
struct KeyedDemoRows {
visible: &'static [usize],
}
impl KeyedRowSource<u64> for KeyedDemoRows {
type Row = KeyedDemoRow;
fn len(&self) -> usize {
self.visible.len()
}
fn row(&self, index: usize) -> Option<&Self::Row> {
self.visible
.get(index)
.and_then(|&index| KEYED_ROWS.get(index))
}
fn key_eq(&self, _index: usize, row: &Self::Row, key: &u64) -> bool {
row.id == *key
}
}
fn keyed_demo_name(row: &KeyedDemoRow) -> Line<'_> {
Line::from(row.name)
}
fn keyed_demo_state(row: &KeyedDemoRow) -> Line<'_> {
Line::from(row.state)
}
fn keyed_demo_age(row: &KeyedDemoRow) -> Line<'_> {
Line::from(format!("{}s", row.age))
}
pub(crate) fn scene_keyed_table(frame: u64, _theme: &Theme) -> Element {
let phase = (frame / 18) % 3;
let visible: &'static [usize] = match phase {
0 => &KEYED_REORDERED,
1 => &KEYED_FILTERED,
_ => &KEYED_ORIGINAL,
};
let label = match phase {
0 => "reordered · key 103 stays selected",
1 => "filtered out · key 103 remains in host state",
_ => "stream refreshed · key 103 restored",
};
view! {
col {
fixed(1) { node(Text::raw(label)) }
grow(1) {
node(KeyedTable::source(
vec![
KeyedColumn::fixed("id", 5, |row: &KeyedDemoRow| Line::from(row.id.to_string())).right(),
KeyedColumn::flex("task", 2, keyed_demo_name),
KeyedColumn::fixed("state", 8, keyed_demo_state).hide_below(34),
KeyedColumn::fixed("age", 5, keyed_demo_age).right().optional(),
],
KeyedDemoRows { visible },
&KEYED_SELECTION,
))
}
}
}
}
pub(crate) fn scene_completion_palette(frame: u64, theme: &Theme) -> Element {
let _ = theme;
let items = vec![
CompletionItem::new("status")
.detail("Show session status")
.replacement("/status"),
CompletionItem::new("model")
.detail("Choose a model")
.replacement("/model")
.keyword("engine"),
CompletionItem::new("approvals")
.detail("Configure command approvals")
.replacement("/approvals"),
CompletionItem::new("review")
.detail("Review current changes")
.replacement("/review"),
CompletionItem::new("init")
.detail("Create project instructions")
.replacement("/init"),
];
let queries = ["", "m", "mo", "mod"];
let query = queries[((frame / 24) as usize) % queries.len()];
let mut state = CompletionState::new();
state.sync(query, &items);
element(
CompletionPalette::new(&items, &state)
.title("Commands")
.viewport(5)
.show_query(true),
)
}
pub(crate) fn scene_tabs(frame: u64, theme: &Theme) -> Element {
let labels: Vec<Line<'static>> = ["Chat", "Diff", "Logs", "Files"]
.iter()
.map(|s| Line::from(Span::styled((*s).to_string(), theme.text_style())))
.collect();
let mut state = TabsState::default();
let target = (frame / 16) % labels.len() as u64;
let right = Event::Key(Key::new(KeyCode::Right));
for _ in 0..target {
let _ = state.handle(&right, labels.len());
}
view! {
col(gap = 1) {
fixed(1) { node(Tabs::new(labels, &state)) }
fixed(1) { node(Text::new(vec![Line::from(Span::styled("←/→ or Tab to switch", theme.muted_style()))])) }
}
}
}
fn demo_keymap() -> Keymap<&'static str> {
Keymap::new()
.layer(
Layer::new("global")
.bind_labeled("?", "open help", "help")
.bind_labeled("q", "quit", "quit"),
)
.layer(
Layer::new("search")
.priority(10)
.bind_labeled("enter", "open result", "open")
.bind_labeled("ctrl+n", "next result", "next")
.bind_labeled("ctrl+p", "previous result", "previous"),
)
}
pub(crate) fn scene_key_hints(_frame: u64, theme: &Theme) -> Element {
view! {
col(gap = 1) {
node(Text::new(vec![Line::from(Span::styled("Full width", theme.accent_style()))]))
node(KeyHints::from_keymap(&demo_keymap()))
row(gap = 2) {
fixed(12) { node(Text::new(vec![Line::from(Span::styled("28 columns", theme.muted_style()))])) }
fixed(28) { node(KeyHints::from_keymap(&demo_keymap())) }
}
}
}
}
pub(crate) fn scene_keymap_help(_frame: u64, theme: &Theme) -> Element {
view! {
col(gap = 1) {
node(Text::new(vec![Line::from(Span::styled("Active bindings", theme.accent_style()))]))
node(KeymapHelp::from_keymap(&demo_keymap()))
}
}
}
pub(crate) fn scene_status_bar(frame: u64, theme: &Theme) -> Element {
let _ = frame;
let bar = StatusBar::new()
.left(vec![
Span::styled(" NORMAL ", theme.selection_style()),
Span::styled(" main.rs", theme.text_style()),
])
.right(vec![
Span::styled("utf-8 ", theme.muted_style()),
Span::styled("Ln 42, Col 7 ", theme.text_style()),
])
.background(Style::default().bg(theme.surface));
view! {
col {
fixed(1) { node(bar) }
}
}
}
pub(crate) fn scene_textinput(frame: u64, theme: &Theme) -> Element {
let full = "fix @src/parser.rs: trailing commas";
let n = ((frame / 3) as usize % (full.chars().count() + 12)).min(full.chars().count());
let typed: String = full.chars().take(n).collect();
let state = TextInputState::from_text(&typed);
let mention = Style::default()
.fg(theme.code.link)
.add_modifier(Modifier::BOLD);
let highlights: Vec<TextSpan> = state
.tokens(&[Trigger::new('@')])
.iter()
.map(|t| t.span(mention))
.collect();
view! {
col {
fixed(3) {
boxed(title = Line::from(Span::styled(" commit message ", theme.accent_style()))) {
node(
TextInput::new(&state)
.placeholder("describe the change", Style::default().fg(theme.dim))
.highlights(highlights)
)
}
}
}
}
}