1use std::sync::Arc;
6
7use rpi_tui::Container;
8
9use crate::config;
10
11pub fn setup_done_path() -> Option<std::path::PathBuf> {
14 config::agent_dir().ok().map(|d| d.join(".setup_done"))
15}
16
17pub fn setup_done() -> bool {
19 setup_done_path().map(|p| p.exists()).unwrap_or(false)
20}
21
22pub fn mark_setup_done() -> std::io::Result<()> {
24 let path = setup_done_path().ok_or_else(|| {
25 std::io::Error::new(std::io::ErrorKind::NotFound, "no home dir for .rpi/agent")
26 })?;
27 if let Some(parent) = path.parent() {
28 std::fs::create_dir_all(parent)?;
29 }
30 std::fs::write(&path, b"1")
31}
32
33pub fn maybe_first_time_setup(chat: &Arc<Container>) -> bool {
36 use rpi_tui::Component;
37 use rpi_tui::{DynamicBorder, Spacer, Text};
38 if setup_done() {
39 return false;
40 }
41 let accent = rpi_tui::theme().colors.accent;
42 let muted = rpi_tui::theme().colors.muted;
43 let border = DynamicBorder::with_color(accent);
44 let mut lines: Vec<String> = Vec::new();
45 lines.extend(border.render(80));
46 lines.push(format!(
47 " {} Welcome to rpi!",
48 accent.fg(&bold("Welcome to rpi!"))
49 ));
50 lines.push(format!(
51 " {} Pick a theme with /theme (dark/light/monochrome).",
52 muted.fg("Pick a theme with /theme (dark/light/monochrome).")
53 ));
54 lines.push(format!(
55 " {} Type /help for commands.",
56 muted.fg("Type /help for commands.")
57 ));
58 lines.extend(border.render(80));
59 for line in lines {
60 chat.add_child(Arc::new(Text::new(line, 1, 0)));
61 }
62 chat.add_child(Arc::new(Spacer::new(1)));
63 let _ = mark_setup_done();
64 true
65}
66
67fn bold(s: &str) -> String {
68 format!("\x1b[1m{s}\x1b[22m")
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74
75 #[test]
76 fn test_sentinel_paths_under_agent_dir() {
77 let _guard = crate::config::test_support::env_lock().lock().unwrap();
80 let prev = std::env::var_os(crate::config::CONFIG_DIR_ENV);
81 let tmp = tempfile::TempDir::new().unwrap();
82 std::env::set_var(crate::config::CONFIG_DIR_ENV, tmp.path());
83 let agent = crate::config::agent_dir().unwrap();
84 assert_eq!(agent.as_path(), tmp.path());
85 if let Some(p) = setup_done_path() {
86 assert!(p.ends_with(".setup_done"));
87 assert_eq!(p.parent().unwrap(), agent);
88 }
89 match prev {
90 Some(v) => std::env::set_var(crate::config::CONFIG_DIR_ENV, v),
91 None => std::env::remove_var(crate::config::CONFIG_DIR_ENV),
92 }
93 }
94}