Skip to content

Add stashed prompts - #260

Merged
sak0a merged 2 commits into
mainfrom
feat/add-stashed-prompts
Jul 31, 2026
Merged

sak0a merged 2 commits into
mainfrom
feat/add-stashed-prompts

Conversation

@sak0a

@sak0a sak0a commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

What Changed

  • Add a global, provider-agnostic prompt stash for composer text and image attachments, capped at 20 LIFO entries.
  • Add the rebindable composer.stash command (default mod+s with !terminalFocus), including browser Save suppression only when the command resolves.
  • Add desktop stash badge/picker UI with keyboard restore/delete behavior and accessible labels.
  • Persist immediately to guarded localStorage in direct mode, fall back to page-session memory, and keep Hosted Hub strictly memory-only.
  • Add defensive hydration, image budgets, async attachment finalization, atomic restore, merge/dedupe/cap handling, and context-preserving draft clearing.
  • Add focused unit and browser coverage for storage failures, hosted isolation, keybinding behavior, restore semantics, image races, and preserved composer state.

Why

This lets users temporarily put aside a prompt and recover it after switching threads, providers, or models without carrying provider/model/mode/context state into the destination composer. The implementation follows the final global-queue design from pingdotgg/t3code#4787 and deliberately excludes image compression.

UI Changes

A compact bookmark badge appears beside the desktop composer when stashes exist. It opens a keyboard-accessible picker with prompt previews, relative times, image/pending states, and visible delete controls. The frozen web phone tier is unchanged, and no flying-prompt animation was added.

Screenshots/video are not included in this draft.

Validation

Passed:

  • bun install --frozen-lockfile
  • bun fmt
  • targeted bunx oxfmt --check for all 19 changed files
  • bun lint (existing unrelated warnings only)
  • bun typecheck
  • bun run typecheck:effect
  • bun run test
  • bun run build
  • bun run build --filter=@ryco/web
  • bun run --cwd apps/web test:browser (70 files, 580 passed, 1 expected failure)

Known repository baseline: bun run fmt:check still reports four pre-existing generated declaration files (scripts/lib/brand-assets.d.ts, scripts/lib/build-target-arch.d.ts, scripts/lib/resolve-catalog.d.ts, and scripts/lib/update-manifest.d.ts). They were left unchanged because they are unrelated to this feature.

Checklist

  • This PR is focused on the web/Electron composer stash feature
  • I explained what changed and why
  • I included before/after screenshots for UI changes
  • No animation was added, so an interaction video is not applicable

Summary by CodeRabbit

  • New Features

    • Added prompt stashing for text and images, including restore, delete, preview, and stash browsing.
    • Added a default mod+s shortcut and configurable keybinding support.
    • Added stash count indicators, keyboard navigation, attachment limits, duplicate filtering, and image recovery handling.
    • Preserved contexts and model settings when clearing or restoring prompts.
  • Bug Fixes

    • Added resilient storage fallback and cleanup for invalid or unavailable stash data.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@sak0a, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 32 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Free

Run ID: 0007046b-b108-4012-943e-289a12ed9e04

📥 Commits

Reviewing files that changed from the base of the PR and between 8ed0b40 and ecff7b3.

📒 Files selected for processing (3)
  • apps/web/src/components/ChatView.browser.tsx
  • apps/web/src/components/chat/ChatComposer.tsx
  • apps/web/src/components/chat/ComposerStashPicker.tsx
📝 Walkthrough

Walkthrough

Adds prompt stashing for composer text and images, including persistence, restoration, deletion, attachment limits, keyboard shortcuts, picker UI, draft-state preservation, and browser coverage.

Changes

Prompt stash

Layer / File(s) Summary
Contracts and draft clearing
packages/contracts/src/keybindings.ts, packages/shared/src/keybindings.ts, packages/client-runtime/src/state/composer/*, apps/web/src/lib/keybindingCategories.*, KEYBINDINGS.md
Adds the composer.stash command and mod+s binding, exposes draft clearing while preserving contexts, and documents the command.
Stash schema and storage
packages/client-runtime/src/state/composer/promptStash.*, apps/web/src/promptStashStore.*, apps/web/src/composerDraftPersistence.ts, apps/web/src/components/PromptStashStore.browser.tsx
Adds validated persistence, hydration repair, attachment budgets, eviction, memory fallback, image finalization, and storage tests.
Composer stash flow and picker
apps/web/src/components/chat/ChatComposer.tsx, apps/web/src/components/chat/ComposerStashBadge.tsx, apps/web/src/components/chat/ComposerStashPicker.tsx
Adds stash creation, restoration, deletion, shortcut handling, image processing, toasts, badge display, and picker interactions.
End-to-end stash validation
apps/web/src/components/ChatView.browser.tsx
Covers keybindings, persistence, context retention, picker behavior, deletion, delayed image finalization, deduplication, and attachment limits.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant ChatComposer
  participant PromptStashStore
  participant ComposerDraftStore
  participant ComposerStashPicker
  User->>ChatComposer: Press mod+s
  ChatComposer->>PromptStashStore: Stash or retrieve prompt entry
  PromptStashStore->>ComposerDraftStore: Persist or clear composer content
  ChatComposer->>ComposerStashPicker: Open stash picker
  ComposerStashPicker->>ChatComposer: Restore or delete selected entry
Loading

Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login.

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XXL labels Jul 31, 2026
@sak0a
sak0a marked this pull request as ready for review July 31, 2026 01:48

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8ed0b40a23

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

function mergeStashedPrompt(currentPrompt: string, stashedPrompt: string): string {
if (stashedPrompt.trim().length === 0) return currentPrompt;
if (stripInlineTerminalContextPlaceholders(currentPrompt).trim().length === 0) {
return `${currentPrompt}${stashedPrompt}`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve a separator after terminal-context placeholders

When a draft retains terminal contexts, clearPromptAndImages leaves only their inline placeholders, so this branch concatenates the restored text directly onto the last placeholder. At send time that placeholder materializes as a label such as @terminal-1:3-4, producing @terminal-1:3-4Stashed text rather than a separated prompt. Insert whitespace between the preserved placeholders and restored text.

Useful? React with 👍 / 👎.

Comment on lines +1907 to +1908
const handler = (event: globalThis.KeyboardEvent) => {
const command = resolveShortcutCommand(event, keybindings, {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Ignore stash shortcuts while another dialog is active

When Settings > Keybindings is recording a shortcut, this long-lived capture listener runs before the settings listener registered later, resolves the default mod+s, stops propagation, and stashes the hidden composer instead of recording the key. The same unexpected mutation can occur from other dialog inputs. Apply the existing event.defaultPrevented and shouldIgnoreGlobalNavigationShortcut guards before resolving this global command.

Useful? React with 👍 / 👎.

return;
}

stashSnapshotsRef.current.set(entryId, { key: snapshotKey, images });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Release completed in-memory image snapshots

Every image stash retains the original File objects and blob preview URLs in this map, but successful image finalization never removes them; they remain until restore, delete, eviction, or component unmount. Since a stash may contain eight 10 MiB images and the queue holds 20 entries, routine repeated stashing can retain roughly 1.6 GiB despite the persisted-image budget and can exhaust the browser process. Keep the snapshot only while encoding or while Undo is actionable, then release it and revoke unreferenced blob URLs.

Useful? React with 👍 / 👎.

Comment on lines +79 to +81
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
if (entries.length === 0) return;
event.preventDefault();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stop intercepting arrows after focus leaves the stash picker

If the picker is open and the user clicks back into the composer, the picker remains mounted while this window-level handler still prevents every ArrowUp/ArrowDown event. Cursor navigation in the editor is therefore replaced by changing the hidden picker selection until the picker is explicitly closed. Restrict these keys to focus within the picker, or close the picker when focus moves outside it.

Useful? React with 👍 / 👎.

@sak0a
sak0a merged commit 8b78f25 into main Jul 31, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant