Skip to content

Recall previously-sent prompts in the composer with the Up / Down arrows #52

Description

@DragonnZhang

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:

  1. The composer renders and starts empty.
  2. Seed localStorage['craft-prompt-history'] = ['alpha one','beta two','gamma three'].
  3. ArrowUp recalls the newest (gamma three); further Ups walk older
    (beta two, alpha one) and clamp at the oldest.
  4. 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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions