61. Idle Tip Line Below the TUI Input¶
- Status: accepted
- Date: 2026-10-17
Context¶
milk's feature surface is large (38 slash commands, F1-F4 panels, ctrl+r
search, @-path completion, ! direct bash, ctrl+enter background jobs)
and mostly invisible until a user already knows it. /help exists but is a
wall of text nobody reads unprompted. The welcome screen lists a handful of
hints — and only while the transcript is empty. Once a session is underway,
nothing surfaces the rest.
Reference harnesses solve this the same way. Both surveyed codebases (MiMo-Code, an opencode fork, and opencode itself) show an idle-screen hint surface on their home screen:
- a rotating
● Tip <text>line directly below the input box (feature-plugins/home/tips.tsx→home_bottomslot), drawn from a static built-in pool of ~80-95 one-liners, mostly keyboard shortcuts and feature upsells; - rotating example placeholders inside the empty input
(
"Fix a TODO in the codebase"…); - static footer shortcut reminders (
@ attach file,/ commands).
Freshness in those harnesses is rotation only: MiMo-Code re-picks a
weighted-random tip every 10s (no seen-tracking, repeats possible);
opencode picks one random tip per home-screen mount. Both gate visibility
with a persisted tips_hidden flag and a tips_toggle keybind. Content is
mostly static, with a few state-conditioned variants (no provider connected
→ "run /connect"; platform branch; keybind-derived shortcut text so the
copy cannot drift from the real bindings).
milk has no "home screen" — its TUI is one persistent transcript view with
the input at the bottom — but it already has the right slot: the hint
region under the main area (tabHints, the completion-hints area), whose
line count viewportHeight already accounts for.
Decision¶
milk shows a one-line idle tip below the input area, in the existing
hint region, whenever it is not occupied by completion hints
((*model).hintRegion, cmd/milk/layout.go). One row is reserved while
tips are enabled, so the layout never jumps when a turn starts or ends; the
line yields the slot to completion hints (the user's active task) and is
absent under the PTY pane, whose layout branch renders no hint rows.
Rendering (renderTip, cmd/milk/tips.go): a gold tip: label, dim
prose, yellow {span} highlights, truncated to the available width with an
ellipsis — one row is a hard layout invariant. The line ends with the
browse-shortcut reference (tipNavRef), which yields before the tip text
does on narrow terminals.
Contents: three sources, precedence order¶
- Contextual hints (
contextualTip) — derived from live state at render time, so they are fresh by construction: no primary agent →/config init; no agents reachable →/agent; update pending →/update installwith the actual release tag. Deliberately tiny: anything the status bar already announces permanently (jobs running, workflow in progress) does not belong here. - Curated tips (
curatedTips) — hand-written one-liners for the features and key bindings that exist nowhere else (@-paths,!bash, ctrl+r, F1-F4, ctrl+enter, …), in priority order. The order is the priority (the MiMo-Code weighted-pool idea, expressed by position instead of random weights): the rotation starts at the top, so a fresh session sees the most valuable tips first. - Registry-derived tips (
registryTips) — generated from the slash-command registry (slashcmd.go, issue #226): every non-hidden TUI-hosted command no curated tip names gets{/name <hint>} — descstraight from the registry's owndesc/hint. A command gets a tip the day it lands in the registry; a renamed or re-described command cannot leave a stale tip behind. The same single-source invariant the registry already grants for help, completion andavailable_commands_update.
Freshness¶
- Rotation (
tipsTick): every 20s the tip advances one position throughbuildTipList()(curated first, then the derived tail) — a deterministic cycle, no repeats until the pool is exhausted. The tick freezes while a turn runs or a prompt is pending (the row stays, the content stops changing — the user's attention is elsewhere) and resumes when they are idle again. No seen-tip persistence across sessions; a fresh session restarts at the top of the priority order. - Contextual tips are recomputed every render, so they can never go stale.
- Derived tips are rebuilt from the registry, so they track the command set by construction.
Control¶
/tips [on|off] (registry entry, TUI-hosted) toggles the line and
persists the default to config show_tips (unset = on) via the same
saveLocalOrGlobal path /colorize uses; the bare query reports the
state and where the shown tip sits in the rotation. The toggle reflows
the layout by exactly one row (syncLayout).
Extended 2026-10-19 (tip browsing). The same registry entry grew
/tips next|prev|pick [<n>|<text>]|search <query> plus the global
Alt+Up/Alt+Down shortcuts (same any-mode availability as F1–F4):
step through the pool, jump to a numbered or text-matched tip (echoed to
the transcript at full width), search it. Every manual move restarts the
cycling timer — tipsTickMsg.gen vs tipsGen, the toastTickMsg generation
check — so the 20s countdown starts fresh and the tick in flight is
dropped instead of cutting the browse short. The rendered line carries the
shortcut reference itself (tipNavRef, · prev/next: Alt+Up/Down) —
dropped on narrow terminals before the tip text is cut, so the
visualization teaches its own navigation. A manual browse pins the
rotation's entry over contextual hints (tipPinned) until the next
automatic advance, and a browse with the line hidden echoes the tip to
the transcript — stepping is never a silent no-op. Registry-derived tips
render the registry's full argument grammar ({/name <hint>}), and the
hints behind them were completed and corrected so that grammar is worth
rendering (the /server gap).
Consequences¶
- Users idle at the prompt discover features they would never have found in
/help— the exact gap both reference harnesses target. - The command surface is now self-documenting twice over:
/helpfor when someone asks, the tip line for when nobody does — and the derived half cannot drift from the registry. - One reserved row is the cost on every screen while tips are on;
/tips offor"show_tips": falsegives it back. Completion hints still expand the region to as many rows as they need, as before. - Curated tips are priority-ordered copy; editing the order changes what
fresh sessions see first. Adding a curated tip for a command retires its
derived duplicate automatically (the
{/cmd …}coverage scan), so the pool never double-tips a command. - Known gaps, deliberate: no input-box example placeholders (the harnesses' other hint surface) — milk's input shows the route/agent prompt label instead, and example prompts there would fight it; no localization (milk has no i18n layer at all); no per-tip suppression or seen-tip history.