plans/input-history.md: - Implementation plan for shell-style up/down arrow message history in SimpleInput, deriving history from session log conversation data - Covers prop threading, history derivation, navigation state, keybinding details, modal parity, and test cases plans/model-selection.md: - Three-phase plan for model visibility and control: display current model, model picker at spawn, mid-session model switching via Zellij plans/PLAN-tool-result-display.md: - Updates to tool result display plan (pre-existing changes) plans/subagent-visibility.md: - Updates to subagent visibility plan (pre-existing changes) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
4.2 KiB
Input History (Up/Down Arrow)
Summary
Add shell-style up/down arrow navigation through past messages in SimpleInput. History is derived from the conversation data already parsed from session logs -- no new state management, no server changes.
How It Works Today
- Server parses JSONL session logs, extracts user messages with
role: "user"(conversation.py:57-66) - App.js stores parsed conversations in
conversationsstate, refreshed via SSE onconversation_mtime_nschange - SessionCard receives
conversationas a prop but does not pass it to SimpleInput - SimpleInput has no awareness of past messages
Step 1: Pipe Conversation to SimpleInput
Pass the conversation array from SessionCard into SimpleInput so it can derive history.
SessionCard.js:165-169-- addconversationprop to SimpleInput- Same for the QuestionBlock path if freeform input is used there (line 162) -- skip for now, QuestionBlock is option-based
Files: dashboard/components/SessionCard.js
Step 2: Derive User Message History
Inside SimpleInput, filter conversation to user messages only.
const userHistory = useMemo(
() => (conversation || []).filter(m => m.role === 'user').map(m => m.content),
[conversation]
);
This updates automatically whenever the session log changes (SSE triggers conversation refresh, new prop flows down).
Files: dashboard/components/SimpleInput.js
Step 3: History Navigation State
Add refs for tracking position in history and preserving the draft.
const historyIndexRef = useRef(-1); // -1 = not browsing
const draftRef = useRef(''); // saves in-progress text before browsing
Use refs (not state) because index changes don't need re-renders -- only setText triggers the visual update.
Files: dashboard/components/SimpleInput.js
Step 4: ArrowUp/ArrowDown Keybinding
In the onKeyDown handler (after the autocomplete block, before Enter-to-submit), add history navigation:
- ArrowUp: only when autocomplete is closed AND cursor is at position 0 (prevents hijacking multiline cursor movement). On first press, save current text to
draftRef. Walk backward throughuserHistory. CallsetText()with the history entry. - ArrowDown: walk forward through history. If past the newest entry, restore
draftRefand reset index to -1. - Reset on submit: set
historyIndexRef.current = -1inhandleSubmitafter successful send. - Reset on manual edit: in
onInput, resethistoryIndexRef.current = -1so typing after browsing exits history mode.
Cursor position check
const atStart = e.target.selectionStart === 0 && e.target.selectionEnd === 0;
Only intercept ArrowUp when atStart is true. This lets multiline text cursor movement work normally. ArrowDown can use similar logic (check if cursor is at end of text) or always navigate history when historyIndexRef.current !== -1 (already browsing).
Files: dashboard/components/SimpleInput.js
Step 5: Modal Parity
The Modal (Modal.js:71) also renders SimpleInput with onRespond. Verify it passes conversation through. The same SessionCard is used in enlarged mode, so this should work automatically if Step 1 is done correctly.
Files: dashboard/components/Modal.js (verify, likely no change needed)
Non-Goals
- No localStorage persistence -- history comes from session logs which survive across page reloads
- No server changes -- conversation parsing already extracts what we need
- No new API endpoints
- No changes to QuestionBlock (option-based, not free-text history)
Test Cases
| Scenario | Expected |
|---|---|
| Press up with empty input | Fills with most recent user message |
| Press up multiple times | Walks backward through user messages |
| Press down after browsing up | Walks forward; past newest restores draft |
| Press up with text in input | Saves text as draft, shows history |
| Press down past end | Restores saved draft |
| Type after browsing | Exits history mode (index resets) |
| Submit after browsing | Sends displayed text, resets index |
| Up arrow in multiline text (cursor not at pos 0) | Normal cursor movement, no history |
| New message arrives via SSE | userHistory updates, no index disruption |
| Session with no prior messages | Up arrow does nothing |