Skip to content

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 │
│ │
└───────────────────────────────────────────────────┘

mnml shows the picker when either of these is true on launch:

  • The --startup-picker CLI flag was passed.
  • The MNML_STARTUP_PICKER environment variable is set to 1.

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.

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.

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.

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.”

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.

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.

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/mnml from 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.

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