Startup picker
When you launch mnml from a terminal you already know which workspace you want — you typed it on the command line. Some launch paths don’t have that context — a wrapper script, a shell alias, or a launcher that hands mnml $HOME and walks away. Rather than dropping you into whatever cwd happened to leak in, mnml can pop a chooser first.
The startup picker is the overlay that fills that gap. It’s a small chooser that comes up on launch and lets you pick where to go before you see the editor. It’s opt-in — pass --startup-picker or set MNML_STARTUP_PICKER=1 in a wrapper.
┌─ Open mnml — Esc to skip ─────────────────────────┐│ ││ Pick a workspace or action: ││ ▸ [1] New file (in current workspace) ││ [2] Open file… ││ [3] Open folder… ││ [4] Open: work ││ [5] Open: mnml-family ││ [6] Open: notes ││ ││ ↑↓ move · Enter select · Esc skip ││ │└───────────────────────────────────────────────────┘When the picker appears
Section titled “When the picker appears”mnml shows the picker when either of these is true on launch:
- The
--startup-pickerCLI flag was passed. - The
MNML_STARTUP_PICKERenvironment variable is set to1.
In every other case mnml goes straight to the editor with no overlay. The picker is opt-in — running mnml from a shell never shows it unless you ask.
Env-var alias
Section titled “Env-var alias”For shells that don’t easily pass --startup-picker, MNML_STARTUP_PICKER=1 mnml produces the same effect. Handy in a wrapper script, a shell alias, or a launcher that spawns mnml on a fresh session where you’d otherwise land in $HOME.
Picker rows
Section titled “Picker rows”The picker assembles its rows from three fixed actions plus your configured workspaces:
| Row | Source | What it does |
|---|---|---|
[1] New file (in current workspace) |
Always present | Dismisses the picker — you continue in whatever workspace mnml was launched at (usually $HOME from Finder). |
[2] Open file… |
Always present | Dismisses the picker and immediately fires view.discovery (the fuzzy file picker) so you can search any file in the current workspace. |
[3] Open folder… |
Always present | Dismisses the picker and fires view.add_workspace — the path prompt that canonicalizes a typed path (supports ~/) and adds it as an extra workspace section in the file rail. |
[4]…[9] |
First 6 entries from your [[workspaces]] config |
Switches the file-tree focus to that configured workspace. |
The picker shows at most 9 rows (the keys 1-9 are the only direct-jump hotkeys), so only the first 6 entries from [[workspaces]] make it in (rows 1-3 are reserved for the fixed actions). The rest are still reachable later via view.switch_workspace and the rail headers — they just don’t get a startup-picker row.
Configuring the workspace rows
Section titled “Configuring the workspace rows”The picker reads [[workspaces]] from your normal mnml config (~/.config/mnml/config.toml or a per-workspace overlay):
[[workspaces]]name = "work"path = "~/Projects/work-stuff"
[[workspaces]]name = "family"path = "~/Projects/mnml-family"
[[workspaces]]path = "~/Projects/notes" # name defaults to "notes" (basename)name is what shows in the picker row (Open: work). When omitted, mnml uses the path’s basename. See Workspaces & the file rail for the full schema and how these entries integrate with the file-rail’s sibling-workspace pattern.
| Key | Action |
|---|---|
↑ / ↓ / j / k |
Move the highlight (wraps top↔bottom) |
Enter |
Commit the focused row |
1-9 |
Direct-jump — selects and commits in one keystroke |
Esc / q |
Skip the picker and continue at the launch workspace |
“Skip” and “[1] New file” land at the same place — they both leave you in whatever workspace mnml was launched at (the same place you’d be without the picker). The picker isn’t a workspace-switcher gate; it’s a chooser that defaults to “don’t pick anything.”
What “skip” means in practice
Section titled “What “skip” means in practice”The workspace mnml was started at is whatever the launcher’s cwd was — for a plain terminal launch, whatever you cd’d into first; for a wrapper script, whatever the script sets. Skipping the picker (Esc / q / Row 1) means you start in that directory: the file rail roots at the launch cwd, the IPC mailbox goes to <cwd>/.mnml/ipc/, etc.
If you want to switch to a different workspace from there, the file-rail’s workspace headers and view.switch_workspace work the same as in any other mnml session. The picker is just a faster path to that switch on first launch.
Empty workspace state
Section titled “Empty workspace state”The picker is one half of the no-real-workspace experience. The other half is the file rail’s empty state, which kicks in whenever mnml’s workspace path (canonicalized) equals your $HOME — the exact situation you land in when a launcher spawns mnml with no folder argument and you happen to have $HOME as cwd.
Instead of trying to enumerate your entire home directory as if it were a project (which would dump Documents/, Downloads/, Library/, every dotfile, …), the workspace tree section paints a vscode-style panel:
> ~
No workspace open
▸ Open file… ▸ Open folder…
Press Esc here to skip.The two ▸ rows are clickable — they’re wired to the same commands as their picker counterparts:
| Row | Command | Effect |
|---|---|---|
▸ Open file… |
view.discovery |
Opens the fuzzy file picker. |
▸ Open folder… |
view.add_workspace |
Prompts for a folder path (supports ~/) and adds it as an extra workspace section. |
Predicate — when does it trigger?
Section titled “Predicate — when does it trigger?”The empty-state check is exact: mnml canonicalizes both its workspace path and $HOME, and compares them. If they match, you get the empty panel; if they differ by even one directory level, you get a normal file tree.
In practice:
cd ~ && mnml .→ empty state (workspace ==$HOME).cd ~/Projects/mnml && mnml .→ normal tree (workspace ==~/Projects/mnml, not$HOME).mnml ~/Projects/mnmlfrom anywhere → normal tree.
If you ever see the empty state and didn’t expect it, the fix is to cd into a real project root before launching mnml — or click ▸ Open folder… to point mnml at one without restarting.
The rest of the rail (GIT section, INTEGRATIONS section, statusline) behaves normally in empty-workspace mode; only the workspace-tree slot swaps its contents.
Source
Section titled “Source”The picker’s state and key handling live in src/app/startup_picker.rs; the overlay drawing lives in src/ui/startup_picker.rs. The empty-workspace state predicate (is_empty_workspace) and panel painter (draw_empty_workspace_state) live in src/ui/tree_view.rs. All four modules are small and independent — adding more action rows is a matter of extending the StartupPickerAction enum or appending to the empty-state row list.
- Workspaces & the file rail —
[[workspaces]]schema in depth, sibling workspaces, the marker pattern - Install — the install paths whose wrapper scripts / aliases can set
MNML_STARTUP_PICKER=1