Files
Ely-GPUI-Components/examples/gallery/pages/terminal/panels.rs
T

164 lines
5.8 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use std::rc::Rc;
use ely_gpui_component::{
forms::{Input, TextInput},
terminal::{LogLevel, LogLine, LogViewer, Process, ProcessList},
theme::{ActiveTheme, Radius},
typography::Caption,
};
use gpui::{App, IntoElement, ParentElement, SharedString, Styled, Window, div, px};
use crate::ui::{keep, section, set};
/// Two hundred lines of a service's day, the same each time.
fn log() -> Rc<Vec<LogLine>> {
let targets = [
"ely::shell",
"ely::pty",
"gpui::window",
"ely::store",
"net::http",
];
let messages = [
(LogLevel::Debug, "frame drawn in 4.1 ms"),
(LogLevel::Info, "window opened at 1280×820"),
(LogLevel::Trace, "key down: cmd-k"),
(LogLevel::Info, "\x1b[1mzsh\x1b[0m started, pid 4821"),
(LogLevel::Warn, "slow frame: 38 ms, over budget"),
(LogLevel::Debug, "layout cached: 212 nodes"),
(LogLevel::Info, "GET /api/quotes \x1b[32m200\x1b[0m 18 ms"),
(
LogLevel::Error,
"GET /api/orders \x1b[31m503\x1b[0m retry in 2 s",
),
(LogLevel::Info, "saved workspace, 3 panes"),
];
Rc::new(
(0..200)
.map(|ix: usize| {
let (level, message) = messages[(ix * 7 + ix / 3) % messages.len()];
LogLine {
at: format!(
"09:{:02}:{:02}.{:03}",
ix / 60 % 60,
ix % 60,
ix * 37 % 1000
)
.into(),
level,
target: targets[ix * 3 % targets.len()].into(),
message: message.into(),
}
})
.collect(),
)
}
fn field(
key: &'static str,
placeholder: &'static str,
window: &mut Window,
cx: &mut App,
) -> gpui::Entity<TextInput> {
window.use_keyed_state(key, cx, move |window, cx| {
TextInput::new(window, cx).placeholder(placeholder)
})
}
pub fn logs(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
let lines = keep("terminal-log", log, window, cx);
let least = keep("terminal-least", || LogLevel::Debug, window, cx);
let follow = keep("terminal-follow", || true, window, cx);
let query = field("terminal-log-query", "Filter lines", window, cx);
let (now_lines, now_least, now_follow) =
(lines.read(cx).clone(), *least.read(cx), *follow.read(cx));
let text = query.read(cx).text().to_string();
let (pick, keep_up) = (least.clone(), follow.clone());
let theme = cx.theme();
section(
"LogViewer",
"A log from a chosen level up, filtered as you type, following new lines until a scroll up lets go. Long logs draw only the lines in view.",
cx,
)
.child(div().w(px(320.)).child(Input::new(&query)))
.child(
div()
.w(px(840.))
.h(px(280.))
.p_2()
.rounded(theme.radius(Radius::Md))
.border_1()
.border_color(theme.colors.border)
.child(
LogViewer::new("terminal-log-viewer", now_lines)
.least(now_least)
.query(text)
.follow(now_follow)
.on_least(move |level, _, cx| set(&pick, level, cx))
.on_follow(move |on, _, cx| set(&keep_up, on, cx)),
),
)
}
fn processes() -> Vec<Process> {
let process = |pid, name: &str, user: &str, cpu, memory_mb: u64| Process {
pid,
name: name.to_string().into(),
user: user.to_string().into(),
cpu,
memory: memory_mb * 1024 * 1024,
};
vec![
process(4821, "zsh", "ely", 0.2, 6),
process(512, "WindowServer", "_windowserver", 14.6, 412),
process(9001, "gallery", "ely", 23.4, 188),
process(733, "rust-analyzer", "ely", 41.8, 1_240),
process(88, "launchd", "root", 0.1, 12),
process(2210, "cargo", "ely", 6.3, 96),
process(3302, "Safari", "ely", 8.9, 734),
process(3303, "Safari Networking", "ely", 1.4, 88),
process(401, "mds_stores", "root", 3.2, 140),
process(6120, "codex", "ely", 2.7, 310),
]
}
pub fn process_list(window: &mut Window, cx: &mut App) -> impl IntoElement + use<> {
let running = keep("terminal-processes", processes, window, cx);
let chosen = keep("terminal-chosen", Vec::<u32>::new, window, cx);
let said = keep("terminal-ended", || None::<SharedString>, window, cx);
let query = field("terminal-process-query", "Find a process", window, cx);
let (now, picked, told) = (
running.read(cx).clone(),
chosen.read(cx).clone(),
said.read(cx).clone(),
);
let text = query.read(cx).text().to_string();
let (select, end, clear, tell) = (
chosen.clone(),
running.clone(),
chosen.clone(),
said.clone(),
);
section(
"ProcessList",
"Running processes, busiest first: found by a query, sorted by any column, and the selected ones ended.",
cx,
)
.child(div().w(px(320.)).child(Input::new(&query)))
.child(
div().w(px(840.)).child(
ProcessList::new("terminal-process-list", now)
.query(text)
.selected(picked)
.on_select(move |pids, _, cx| set(&select, pids, cx))
.on_end(move |pids, _, cx| {
let left: Vec<Process> = end.read(cx).iter().filter(|process| !pids.contains(&process.pid)).cloned().collect();
set(&end, left, cx);
set(&clear, Vec::new(), cx);
set(&tell, Some(format!("Ended {} process{}.", pids.len(), if pids.len() == 1 { "" } else { "es" }).into()), cx);
}),
),
)
.children(told.map(Caption::new))
}