1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
//! Startup workspace-picker state.
//!
//! Drawn by `src/ui/startup_picker.rs`; routed by `src/tui.rs` via
//! `App::startup_picker`. See the UI module for the user-facing
//! description.
use crate::app::App;
/// Action the picker fires when the user commits a selection. The
/// caller (`tui.rs` keymap) translates this into the appropriate
/// `App` method calls.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StartupPickerAction {
/// Dismiss the picker; user continues in the workspace mnml was
/// already launched at.
NewFile,
/// Dismiss + fire `view.discovery` (the fuzzy file picker).
OpenFile,
/// Dismiss + fire `view.add_workspace` (the path prompt that
/// canonicalizes a path + adds it as an extra workspace).
OpenFolder,
/// Switch the file-tree focus to a configured `[[workspaces]]`
/// row. Index matches `App::switch_workspace` (0 = primary,
/// 1+ = extras).
SwitchWorkspace(usize),
/// Open a subdirectory of `[ui] projects_dir` as the workspace.
OpenProject(std::path::PathBuf),
}
/// Mutable state for the startup picker — currently just which row
/// is highlighted. Lives on `App.startup_picker: Option<...>`; the
/// picker is shown iff that's `Some`.
#[derive(Debug, Default, Clone, Copy)]
pub struct StartupPickerState {
pub selected: usize,
}
impl App {
/// Should the picker be shown on startup? Triggered by
/// `--startup-picker` CLI flag or `MNML_STARTUP_PICKER=1` env
/// (the env var is how the mnml.app launcher plumbs the flag
/// through, since LaunchServices doesn't forward arbitrary
/// CLI args).
pub fn want_startup_picker(cli_flag: bool) -> bool {
if cli_flag {
return true;
}
matches!(std::env::var("MNML_STARTUP_PICKER").as_deref(), Ok("1"))
}
pub fn dismiss_startup_picker(&mut self) {
self.startup_picker = None;
}
pub fn startup_picker_move(&mut self, delta: isize) {
let n = crate::ui::startup_picker::row_count(self);
if n == 0 {
return;
}
if let Some(p) = self.startup_picker.as_mut() {
let cur = p.selected as isize;
let new = (cur + delta).rem_euclid(n as isize) as usize;
p.selected = new;
}
}
/// Apply the user's selection. Returns `Some(action)` so the
/// caller can fire downstream commands (`view.discovery` etc.)
/// after `self.dismiss_startup_picker()` runs.
pub fn startup_picker_commit(&mut self) -> Option<StartupPickerAction> {
let p = self.startup_picker.as_ref()?;
let action = crate::ui::startup_picker::action_for(self, p.selected);
self.dismiss_startup_picker();
action
}
/// Direct-jump variant — handles a `'1'..='9'` keystroke.
pub fn startup_picker_press_digit(&mut self, ch: char) -> Option<StartupPickerAction> {
let idx = crate::ui::startup_picker::row_for_key(self, ch)?;
if let Some(p) = self.startup_picker.as_mut() {
p.selected = idx;
}
self.startup_picker_commit()
}
}