chore(plans): add input-history and model-selection plans
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>
This commit is contained in:
96
plans/input-history.md
Normal file
96
plans/input-history.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# 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
|
||||
|
||||
1. Server parses JSONL session logs, extracts user messages with `role: "user"` (`conversation.py:57-66`)
|
||||
2. App.js stores parsed conversations in `conversations` state, refreshed via SSE on `conversation_mtime_ns` change
|
||||
3. SessionCard receives `conversation` as a prop but does **not** pass it to SimpleInput
|
||||
4. 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` -- add `conversation` prop 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.
|
||||
|
||||
```js
|
||||
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.
|
||||
|
||||
```js
|
||||
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 through `userHistory`. Call `setText()` with the history entry.
|
||||
- **ArrowDown**: walk forward through history. If past the newest entry, restore `draftRef` and reset index to -1.
|
||||
- **Reset on submit**: set `historyIndexRef.current = -1` in `handleSubmit` after successful send.
|
||||
- **Reset on manual edit**: in `onInput`, reset `historyIndexRef.current = -1` so typing after browsing exits history mode.
|
||||
|
||||
### Cursor position check
|
||||
|
||||
```js
|
||||
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 |
|
||||
Reference in New Issue
Block a user