feat(chat): persist more composer input history with global or session scope; recallable with up/down arrow keys (#3035)

* feat(chat): persist input history

* feat(settings): configure input history scope

* fix(web): keep input history validation packaged

* fix(chat): preserve input history across tabs

* fix(settings): restore prompt history limit

* fix(settings): keep history deletion warning visible

* fix(i18n): restore Turkish Git empty state translations

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
This commit is contained in:
Matt Visnovsky
2026-09-05 19:19:03 +03:00
committed by GitHub
co-authored by Bohdan Triapitsyn
parent 29480383cc
commit 1d6b15bc04
49 changed files with 3353 additions and 458 deletions
@@ -23,7 +23,7 @@ existing mobile fixed-position rules unchanged.
|---|---|
| `language/` | What the text *means*: `@` references, `/` and `#` tokens, markdown, and which picker a caret asks for |
| `editor/` | The CodeMirror view that renders the language and owns the caret |
| `state/` | Composer state with a lifecycle: drafts, mobile shell, history, popup placement, draft targeting |
| `state/` | Composer-local lifecycle state: ArrowUp/ArrowDown browsing, draft stash/restore, mobile shell, popup placement, draft targeting |
| `submit/` | Turning what the user has into what gets sent |
| `attachments/` | Files: paths, drop payloads |
| `ui/` | Presentation |
@@ -192,6 +192,25 @@ and the send path reading the same grammar.
state and registers its application shortcuts locally. The selectors only
consume their shared prefix while the draft target UI is mounted.
## Input recall ownership
Prompt recall has two owners on purpose.
- `packages/ui/src/stores/useInputHistoryStore.ts` owns the persisted source of
truth. It keeps the runtime-scoped global bucket and the runtime + directory
+ session bucket, each capped by the configurable input-history limit. That
setting defaults to 40 entries.
- `state/useMessageHistory.ts` owns only keyboard traversal through whichever
bucket the composer was given. It stashes the current draft on entry and
restores it on the way back out.
- `ChatInput.tsx` owns the recalled-entry presentation. If the user edits a
recalled prompt, the UI may show an overlay state for "this came from
history", but that edit does not rewrite stored history.
Transcript visibility is not part of this contract anymore. Revert markers may
hide older user messages from the chat timeline, but they do not decide what
ArrowUp and ArrowDown can recall.
## Mobile
`state/useMobileComposerShell.ts` and `state/useMobileViewportPin.ts` are
@@ -209,12 +228,14 @@ hardware.
The package has no DOM test environment, so coverage stops at the state and
logic layers: the language, the submit assembly, path and drop handling, text
splicing, large-paste detection, paste-offer invalidation, message history, and
the CodeMirror language extension at the `EditorState` level.
splicing, large-paste detection, paste-offer invalidation, input-history
traversal, and the CodeMirror language extension at the `EditorState` level.
Rendering, focus, keyboard behavior, IME and WKWebView are **not covered by
tests** and are verified by hand. Do not report a change to them as validated
on the strength of type-check and unit tests.
tests** and are verified by hand. That includes ArrowUp and ArrowDown recall,
caret placement after recall, restored drafts, and any edited-entry overlay.
Do not report a change to them as validated on the strength of type-check and
unit tests.
Run tests per file (`bun test <path>`): `mock.module` is process-global, so
suites that install module mocks are order-dependent.