Problem or Motivation
Terminal shells, the Claude Code CLI, and the Codex desktop composer all
let you recall a prompt you already sent by pressing the Up arrow in the
composer — Codex documents it explicitly ("cancel the current run and recover
your previous prompt by pressing the up arrow key in the composer"), and it's the
same muscle-memory as a shell's history (↑/↓).
OpenWork's chat composer (FreeFormInput) has no prompt history. Once you
send a message it's gone from the input; to re-run or tweak a previous prompt you
have to retype it or scroll up and copy it out of the transcript. ArrowUp /
ArrowDown in the composer today only drive the inline mention / slash / label
menus — outside those menus they just move the caret.
Proposed Solution
Add shell-style prompt history recall to the composer:
- Each sent prompt is appended to a history list (newest last), persisted in
the renderer's localStorage (global, like a shell history file), trimmed,
with immediate-duplicate collapse, capped at 100 entries.
- Pressing Up in an empty composer (or while already recalling) walks
backwards through history (newest → oldest), clamping at the oldest.
- Pressing Down while recalling walks forwards (older → newer); paging
past the newest entry restores the draft you started from and exits recall.
- Any real edit, submitting, or switching sessions exits recall mode.
- When the composer already has a draft and you aren't recalling,
Up/Down
keep their normal caret-movement behaviour — so recall never hijacks editing.
Feasibility
Classification: pure frontend — candidate. No qwen-code backend involvement.
- The composer's imperative handle (
RichTextInputHandle) already exposes
value / setValue / setSelectionRange, and recall drives the existing
controlled value prop (via setInput) — the same mechanism @mention
selection already uses. No new IPC, no main-process change.
- Persistence reuses the existing
lib/local-storage.ts KEYS/get/set
helper (one new key, craft-prompt-history), exactly as the recent
reduce-motion work did.
- The navigation model (push / prev / next / entry-at) is a small pure module,
so it is fully unit-testable without a DOM.
- No new i18n keys — the feature has no visible labels.
Alternatives Considered
- Per-session history instead of global: closer to some apps, but a global
history matches shell / Codex behaviour and is simpler; can be scoped later.
- A visible history dropdown: heavier and less discoverable than the
universally-expected ↑ affordance; could be layered on later.
Additional Context
Acceptance criteria (CDP e2e assertion)
A new e2e/assertions/prompt-history.assert.ts drives the real built app over CDP
entirely in the draft (no-session) state — no seeded conversation, no backend. The
composer reads its history fresh from localStorage, so the assertion seeds three
prior prompts there and walks them:
- The composer renders and starts empty.
- Seed
localStorage['craft-prompt-history'] = ['alpha one','beta two','gamma three'].
ArrowUp recalls the newest (gamma three); further Ups walk older
(beta two, alpha one) and clamp at the oldest.
ArrowDown walks newer again (beta two, gamma three) and, once past the
newest, restores the original empty draft.
Reading the composer's actual text after each keypress proves the arrows really
change the composer contents, not merely toggle state. In addition, the pure
history module ships with unit tests (prompt-history.test.ts) covering push
semantics (trim, immediate-repeat collapse, cap) and the Up/Down navigation
round-trip.
Part of the autonomous desktop-feature loop (loop-bot).
Problem or Motivation
Terminal shells, the Claude Code CLI, and the Codex desktop composer all
let you recall a prompt you already sent by pressing the Up arrow in the
composer — Codex documents it explicitly ("cancel the current run and recover
your previous prompt by pressing the up arrow key in the composer"), and it's the
same muscle-memory as a shell's history (
↑/↓).OpenWork's chat composer (
FreeFormInput) has no prompt history. Once yousend a message it's gone from the input; to re-run or tweak a previous prompt you
have to retype it or scroll up and copy it out of the transcript.
ArrowUp/ArrowDownin the composer today only drive the inline mention / slash / labelmenus — outside those menus they just move the caret.
Proposed Solution
Add shell-style prompt history recall to the composer:
the renderer's
localStorage(global, like a shell history file), trimmed,with immediate-duplicate collapse, capped at 100 entries.
backwards through history (newest → oldest), clamping at the oldest.
past the newest entry restores the draft you started from and exits recall.
Up/Downkeep their normal caret-movement behaviour — so recall never hijacks editing.
Feasibility
Classification: pure frontend — candidate. No qwen-code backend involvement.
RichTextInputHandle) already exposesvalue/setValue/setSelectionRange, and recall drives the existingcontrolled
valueprop (viasetInput) — the same mechanism@mentionselection already uses. No new IPC, no main-process change.
lib/local-storage.tsKEYS/get/sethelper (one new key,
craft-prompt-history), exactly as the recentreduce-motion work did.
so it is fully unit-testable without a DOM.
Alternatives Considered
history matches shell / Codex behaviour and is simpler; can be scoped later.
universally-expected
↑affordance; could be layered on later.Additional Context
Acceptance criteria (CDP e2e assertion)
A new
e2e/assertions/prompt-history.assert.tsdrives the real built app over CDPentirely in the draft (no-session) state — no seeded conversation, no backend. The
composer reads its history fresh from
localStorage, so the assertion seeds threeprior prompts there and walks them:
localStorage['craft-prompt-history'] = ['alpha one','beta two','gamma three'].ArrowUprecalls the newest (gamma three); further Ups walk older(
beta two,alpha one) and clamp at the oldest.ArrowDownwalks newer again (beta two,gamma three) and, once past thenewest, restores the original empty draft.
Reading the composer's actual text after each keypress proves the arrows really
change the composer contents, not merely toggle state. In addition, the pure
history module ships with unit tests (
prompt-history.test.ts) covering pushsemantics (trim, immediate-repeat collapse, cap) and the Up/Down navigation
round-trip.
Part of the autonomous desktop-feature loop (
loop-bot).