Skip to content

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

Open
DragonnZhang wants to merge 1 commit into
mainfrom
loop/composer-prompt-history
Open

Recall previously-sent prompts in the composer with the Up / Down arrows#53
DragonnZhang wants to merge 1 commit into
mainfrom
loop/composer-prompt-history

Conversation

@DragonnZhang

Copy link
Copy Markdown
Collaborator

Closes #52

What & why

Terminal shells, the Claude Code CLI, and the Codex desktop composer all
let you recall a prompt you already sent by pressing Up in the composer.
OpenWork's composer (FreeFormInput) had no prompt history: once a message
was sent it was gone from the input, so re-running or tweaking a previous prompt
meant retyping it or copying it back out of the transcript. Outside the inline
mention / slash / label menus, ArrowUp / ArrowDown in the composer only moved
the caret.

This adds shell-style prompt history recall:

  • Each sent prompt is appended to a global history list (newest last),
    persisted in localStorage (craft-prompt-history), trimmed, with immediate
    duplicate collapse, capped at 100 entries.
  • Up in an empty composer (or while already recalling) walks backwards
    (newest → oldest), clamping at the oldest.
  • Down while recalling walks forwards; paging past the newest entry
    restores the draft you started from and exits recall.
  • Any real edit, submitting, or switching sessions exits recall.
  • With a non-empty draft and not recalling, Up/Down keep normal
    caret-movement — recall never hijacks editing.

Frontend-only. No backend / qwen-code change. Recall drives the existing
controlled value prop (the same path @mention selection already uses), and
persistence reuses the existing lib/local-storage.ts helper. No new i18n keys.

Changes

  • components/app-shell/input/prompt-history.ts (new) — pure, DOM-free history
    model: pushPromptHistory (trim / immediate-repeat collapse / cap),
    prevPromptIndex / nextPromptIndex / promptAt (index-based Up/Down
    navigation), and getPromptHistory / recordPrompt (localStorage round-trip).
  • components/app-shell/input/FreeFormInput.tsx — history refs, an
    applyRecalledPrompt helper (drives setInput + caret), the ArrowUp/ArrowDown
    branch in handleKeyDown, recall capture in submitMessage, reset on real edit
    (handleInputChange) and on session switch, and data-testid="composer-input"
    on the composer for e2e.
  • lib/local-storage.ts — new promptHistory key (craft-prompt-history).
  • components/app-shell/input/__tests__/prompt-history.test.ts (new) — 20 unit
    tests for the module.
  • e2e/assertions/prompt-history.assert.ts (new) — CDP assertion (below).

Verification (DoD)

  • bun run typecheck:allzero new errors. packages/{core,shared,server-core,server,session-tools-core}
    pass clean; packages/ui passes clean (checked independently); the only errors
    are the 11 pre-existing ones in apps/electron (auto-update.ts owner/repo,
    a settings-default-thinking test tuple, two test files importing vitest) —
    none in the files this PR touches.
  • bun test — the set of failing tests is byte-for-byte identical to main,
    verified by diffing the sorted, timing-stripped failure lists from a clean-main
    run and this branch in the same environment: 56 pre-existing failures on
    both
    (BrowserCDP, RPC handler profiles, RoutedClient, startWebuiHttpServer,
    the i18n sorted checks, resource-bundle, …). This change adds zero new
    failures, and adds 20 new passing unit tests (prompt-history.test.ts). (The
    raw N fail total is order-dependent when the whole suite runs unisolated; the
    deterministic signal is the failing-test set, which is unchanged.)
  • Renderer build (bun run build:renderer, where this change lives) — ✅ builds
    cleanly.
  • CDP assertion transpiles (bun build e2e/assertions/prompt-history.assert.ts) — ✅.

CDP e2e assertion (prompt-history.assert.ts)

Drives the real built app over CDP entirely in the draft (no-session) state — no
seeded conversation and 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 recall 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.

⚠️ Local CDP run was blocked by this sandbox's egress policy, not by the
feature. The Electron binary download (GitHub release host) returns 403 from
the egress proxy, so the app can't be built/launched here (xvfb is present;
only the binary fetch fails) — the same limitation noted on the other open
loop-bot PRs. The assertion transpiles and is included so CI / a reviewer can
run bun run e2e in an environment with normal network access. Everything that
doesn't require launching Electron (typecheck, unit tests, renderer build) passes
with zero delta vs main.

Part of the autonomous desktop-feature loop (loop-bot).


Generated by Claude Code

Recall previously-sent prompts in the chat composer, shell-style:
- Up in an empty (or already-recalling) composer walks history newest->oldest
- Down walks newer again and restores the original draft past the newest entry
- edit / submit / session-switch exit recall mode; drafts keep normal caret nav

Sent prompts are persisted globally in localStorage (craft-prompt-history),
trimmed, immediate-duplicate-collapsed, capped at 100. The navigation model is a
pure, unit-tested module (prompt-history.ts). No new i18n keys.

Adds a data-testid on the composer input and a CDP assertion driving Up/Down in
the draft state.

Closes #52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

2 participants