From 6b47eefe9ff772d0e27bc40a40dce8e2eaae2d24 Mon Sep 17 00:00:00 2001 From: Wojtek Date: Tue, 15 Sep 2026 21:28:57 -0400 Subject: [PATCH 1/2] Scroll chat history with arrows at an empty prompt --- CHANGELOG.md | 4 ++++ README.md | 4 ++-- src/cli/chat-input.ts | 7 +++++++ src/cli/chat-view.ts | 3 ++- tests/chat-input.test.ts | 19 ++++++++++++++++++- 5 files changed, 33 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2399d9..bd89dc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ changes will be called out under **Breaking changes**. ## Unreleased +### Fixed + +- **Scroll history at an empty prompt.** Up/Down now scroll the conversation when the draft is empty, including wheel gestures that terminals translate into arrows. Ctrl+P/Ctrl+N recall submitted prompts; suggestions and multiline editing retain their controls. + ## [0.18.1] — 2026-09-15 Full notes: [`docs/releases/0.18.1.md`](docs/releases/0.18.1.md). diff --git a/README.md b/README.md index 385a62e..b2f1ff1 100644 --- a/README.md +++ b/README.md @@ -310,9 +310,9 @@ claude → you 12:05 3 members │ codex holding 12m · claude idle 3m ``` -Scroll with Page Up/Page Down or Shift+Up/Down. The input stays fixed and editable. New messages do not pull you away from older history; a count appears in the footer. Ctrl+End or `/bottom` returns to live messages. The in-memory buffer retains up to 2,000 message/notice blocks and rewraps on resize. Mouse capture is off by default: drag to select text, double-click to select a word, and copy using your terminal's usual shortcut or menu. For wheel scrolling inside the conversation, opt in with `tt chat --mouse`; this captures mouse gestures, so native selection then requires your terminal's selection modifier (often Shift). `--no-mouse` explicitly restores the default and wins if both flags are supplied. +Scroll with Page Up/Page Down or Shift+Up/Down. With an empty draft, Up/Down also scroll the conversation; this supports terminals that translate the mouse wheel into arrow keys. Use Ctrl+P/Ctrl+N to recall submitted prompts. The input stays fixed and editable. New messages do not pull you away from older history; a count appears in the footer. Ctrl+End or `/bottom` returns to live messages. The in-memory buffer retains up to 2,000 message/notice blocks and rewraps on resize. Mouse capture is off by default: drag to select text, double-click to select a word, and copy using your terminal's usual shortcut or menu. For wheel scrolling inside the conversation, opt in with `tt chat --mouse`; this captures mouse gestures, so native selection then requires your terminal's selection modifier (often Shift). `--no-mouse` explicitly restores the default and wins if both flags are supplied. -Typing `/`, `@`, or `!@` opens a suggestion list drawn over the bottom of the conversation, so nothing moves while you type. Up/Down choose, Tab or Enter accept, and Enter still sends once the word is complete (an exact `/quit` still quits). Escape closes the list first and clears the draft on a second press; Ctrl+C clears the draft. Neither quits. Alt+Enter (or Shift+Enter where the terminal supports it) adds a new line, and with the list closed Up/Down move through a multi-line draft at the same column. Pasted multiline text stays in the draft until Enter. On exit, the console restores the original terminal screen. +Typing `/`, `@`, or `!@` opens a suggestion list drawn over the bottom of the conversation, so nothing moves while you type. Up/Down choose, Tab or Enter accept, and Enter still sends once the word is complete (an exact `/quit` still quits). Escape closes the list first and clears the draft on a second press; Ctrl+C clears the draft. Neither quits. Alt+Enter (or Shift+Enter where the terminal supports it) adds a new line, and with the list closed Up/Down move through a multi-line draft at the same column. On a nonempty single-line draft, Up/Down retain prompt-history navigation; empty-draft arrows scroll the conversation. Pasted multiline text stays in the draft until Enter. On exit, the console restores the original terminal screen. History is split with Today, Yesterday, and date dividers. Earlier days are dimmed and their timestamps include the day. When someone joins after four quiet hours, everything before that is dimmed as an earlier conversation; this is a visual boundary, not a sign that a quiet agent has exited. diff --git a/src/cli/chat-input.ts b/src/cli/chat-input.ts index 743406a..0a8f1bb 100644 --- a/src/cli/chat-input.ts +++ b/src/cli/chat-input.ts @@ -176,6 +176,13 @@ export class ChatInputController { this.selectedCompletion = (this.completionIndex + direction + count) % count; return; } + // With native selection enabled, terminals may translate wheel movement + // into ordinary arrows. At an empty prompt, navigate the conversation + // instead of replacing the draft with previously submitted messages. + if (this.draft.line.length === 0) { + this.options.onScroll("lines", direction); + return; + } const moved = moveChatCursorVertical(this.draft, this.sink.columns, direction, this.verticalColumn); if (moved) { this.rl.cursor = moved.draft.cursor; diff --git a/src/cli/chat-view.ts b/src/cli/chat-view.ts index 2e8f13a..9c5d43e 100644 --- a/src/cli/chat-view.ts +++ b/src/cli/chat-view.ts @@ -62,7 +62,8 @@ export function formatChatHelp(width: number, color: boolean, keys = false): str const entries: [string, string][] = keys ? [ ["Enter", "Send; accept an incomplete suggestion first"], ["Tab", "Accept the selected suggestion"], - ["↑ / ↓", "Choose a suggestion, or move through draft lines"], + ["↑ / ↓", "Scroll when empty; choose suggestions or edit draft lines"], + ["Ctrl+P / Ctrl+N", "Recall previous / next submitted prompt"], ["Alt+Enter", "Insert a new line"], ["Esc", "Dismiss suggestions; press again to clear"], ["Ctrl+C", "Clear the draft"], diff --git a/tests/chat-input.test.ts b/tests/chat-input.test.ts index 06da0f7..a6ccebf 100644 --- a/tests/chat-input.test.ts +++ b/tests/chat-input.test.ts @@ -78,7 +78,7 @@ describe("full-screen chat input", () => { expect(submit).not.toHaveBeenCalled(); input.write("\u0001\u0005\r"); expect(submit).toHaveBeenLastCalledWith(body); - input.write("\u001b[A"); + input.write("\u0010"); expect(editor.draft.line).toBe(body); input.write("\r"); expect(submit).toHaveBeenLastCalledWith(body); @@ -197,3 +197,20 @@ describe("full-screen chat input", () => { expect(input.setRawMode).toHaveBeenLastCalledWith(true); }); }); + +test("empty-draft arrows scroll conversation without recalling prompts; Ctrl+P/N recall history", () => { + const { editor, input, scroll } = setup(true); + try { + input.write("earlier prompt\r"); + input.write("\u001b[A\u001bOA\u001b[B\u001bOB"); + expect(editor.draft).toEqual({ line: "", cursor: 0 }); + expect(scroll.mock.calls).toEqual([["lines", -1], ["lines", -1], ["lines", 1], ["lines", 1]]); + input.write("\u0010"); + expect(editor.draft.line).toBe("earlier prompt"); + input.write("\u000e"); + expect(editor.draft.line).toBe(""); + input.write("@c\u001b[B"); + expect(editor.completionIndex).toBe(1); + expect(scroll).toHaveBeenCalledTimes(4); + } finally { editor.close(); } +}); From 3f2e583bb2351fe22e452df404e435359ae10f19 Mon Sep 17 00:00:00 2001 From: Wojtek Date: Tue, 15 Sep 2026 21:29:52 -0400 Subject: [PATCH 2/2] Prepare 0.18.2 release --- CHANGELOG.md | 5 +++++ docs/releases/0.18.2.md | 18 ++++++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 docs/releases/0.18.2.md diff --git a/CHANGELOG.md b/CHANGELOG.md index bd89dc7..909bc67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ changes will be called out under **Breaking changes**. ## Unreleased +## [0.18.2] — 2026-09-16 + +Full notes: [`docs/releases/0.18.2.md`](docs/releases/0.18.2.md). + ### Fixed - **Scroll history at an empty prompt.** Up/Down now scroll the conversation when the draft is empty, including wheel gestures that terminals translate into arrows. Ctrl+P/Ctrl+N recall submitted prompts; suggestions and multiline editing retain their controls. @@ -566,6 +570,7 @@ Initial alpha. Core room protocol, SQLite-backed persistence, multi-process contention coverage, MCP smoke coverage, human guardian flow, harness installers, and the portable `talking-stick` skill. +[0.18.2]: https://github.com/mostlydev/talking-stick/releases/tag/v0.18.2 [0.18.1]: https://github.com/mostlydev/talking-stick/releases/tag/v0.18.1 [0.18.0]: https://github.com/mostlydev/talking-stick/releases/tag/v0.18.0 [0.17.0]: https://github.com/mostlydev/talking-stick/releases/tag/v0.17.0 diff --git a/docs/releases/0.18.2.md b/docs/releases/0.18.2.md new file mode 100644 index 0000000..3d408b5 --- /dev/null +++ b/docs/releases/0.18.2.md @@ -0,0 +1,18 @@ +# Talking Stick 0.18.2 + +Date: 2026-09-16 + +## Fixed + +- **Scroll history at an empty prompt.** Up/Down now scroll the conversation when the draft is empty, including wheel gestures that terminals translate into arrows. Ctrl+P/Ctrl+N recall submitted prompts; suggestions and multiline editing retain their controls. + +## Verification + +```bash +npm run typecheck +npm test +npm run build +node dist/cli.js --help +git diff --check +npm pack --dry-run +``` diff --git a/package-lock.json b/package-lock.json index 102ec98..841048a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "talking-stick", - "version": "0.18.1", + "version": "0.18.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "talking-stick", - "version": "0.18.1", + "version": "0.18.2", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 456a364..fb59865 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "talking-stick", - "version": "0.18.1", + "version": "0.18.2", "description": "CLI coordination tool for path-scoped agent handoffs.", "type": "module", "bin": {