Skip to content

feat(terminal): add terminal composer#916

Open
PeppaPigw wants to merge 4 commits into
crynta:mainfrom
PeppaPigw:feat/terminal-composer
Open

feat(terminal): add terminal composer#916
PeppaPigw wants to merge 4 commits into
crynta:mainfrom
PeppaPigw:feat/terminal-composer

Conversation

@PeppaPigw

@PeppaPigw PeppaPigw commented Jul 1, 2026

Copy link
Copy Markdown

What

Adds a terminal composer for drafting terminal input before sending it to the active terminal session.

The composer can be toggled from the terminal, resized, scrolled locally, syntax-highlighted, and used to either send immediately or enqueue prompts for later submission.

Why

Longer prompts for AI CLIs are awkward to write directly in the terminal input line. A dedicated composer gives users a normal editor-like drafting area with shortcuts, syntax highlighting, and a per-terminal waitlist while keeping the terminal-first workflow intact.

How

  • Adds a terminal-scoped composer panel in the existing workspace input bar surface.
  • Adds composer shortcuts for toggle, send, queue, and send-next-queued actions using the platform primary modifier (Command on macOS, Ctrl on Windows/Linux).
  • Adds a per-leaf prompt queue and clickable queued prompt row.
  • Adds configurable composer syntax modes and default syntax rules in Settings > General > Terminal.
  • Defaults regular terminals to Bash syntax and active AI CLI sessions (claude, codex, gemini) to Markdown syntax.
  • Uses CodeMirror language extensions for highlighting and bracket behavior; this does not mutate user input.

This PR is intentionally scoped to terminal composer behavior. It does not change Markdown preview/rendering behavior.

Testing

  • pnpm exec tsc --noEmit clean
  • pnpm test clean: 39 files / 270 tests
  • pnpm lint completed with exit 0; existing repository warnings remain
  • Manual smoke-test affected feature
  • UI tested in a packaged local Tauri app build on macOS
  • Platforms tested: macOS
  • Shells tested: zsh, plus active claude/codex-style AI CLI detection paths

Not applicable:

  • No src-tauri/ changes in this PR.
  • No #[tauri::command] signature changes.

Screenshots / GIFs

Composer preview:

Composer preview

Queued prompt waitlist:

Queued prompt waitlist

Syntax selection:

Syntax selection

Notes reviewer

This is a feature-sized terminal UX change, but the implementation is kept within terminal composer UI/state, shortcut registration, settings persistence, and terminal agent activity metadata needed for AI CLI syntax defaults. Happy to split or adjust scope if preferred.

Summary by CodeRabbit

  • New Features
    • Added a Terminal Composer for the active terminal with toggle, send, queue, and close actions.
    • Added a queued prompt panel with one-click sending and per-item sending.
    • Added Terminal Composer commands and new keyboard shortcuts, shown via the command palette.
    • Added Terminal “Composer syntax mode” and regex-based syntax rules in settings, including shell-style autocomplete and resizable composer height.
  • Bug Fixes
    • Improved global shortcut handling so disabled bindings don’t prevent other matches.
    • Updated queued/composer sending to dequeue only after successful submission.

@PeppaPigw PeppaPigw requested a review from crynta as a code owner July 1, 2026 13:28
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds a terminal composer subsystem with draft and queue storage, a CodeMirror-based editor, prompt queue rendering, terminal activity plumbing, shortcut and command-palette actions, and persisted composer syntax settings.

Changes

Terminal Composer Feature

Layer / File(s) Summary
Composer state, language, and editor
src/modules/terminal/composer/terminalComposerStore.ts, src/modules/terminal/composer/composerLanguage.ts, src/modules/terminal/composer/composerLayout.ts, src/modules/terminal/composer/composerShellCompletion.ts, src/modules/terminal/composer/composerEditor.ts, src/modules/terminal/composer/*.test.ts
Adds per-leaf draft/queue state, syntax mode resolution and extension loading, height clamping, shell completion, and the CodeMirror editor with tests.
Terminal activity and composer UI
src/modules/terminal/lib/agentActivity.ts, src/modules/terminal/lib/useTerminalSession.ts, src/modules/terminal/index.ts, src/modules/terminal/composer/TerminalComposer.tsx, src/modules/terminal/composer/TerminalPromptQueue.tsx
Tracks active agent identity per PTY, exposes submit/activity helpers, and adds the composer and queued-prompt UI.
App wiring, shortcuts, and palette
src/app/App.tsx, src/app/components/WorkspaceInputBar.tsx, src/modules/shortcuts/shortcuts.ts, src/modules/shortcuts/shortcuts.test.ts, src/modules/shortcuts/lib/useGlobalShortcuts.ts, src/modules/command-palette/commands.ts, src/modules/command-palette/commands.test.ts
Wires composer state into the app, adds terminal/composer shortcuts and palette actions, and updates disabled-shortcut handling.
Composer syntax settings
src/modules/settings/store.ts, src/settings/sections/GeneralSection.tsx
Persists composer syntax mode/rules and adds settings UI to edit them.

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

Possibly related PRs

  • crynta/terax-ai#597: Both PRs touch src/modules/terminal/lib/useTerminalSession.ts and the submitToLeaf behavior used by the composer send flow.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title follows Conventional Commits and accurately summarizes the main terminal composer feature.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai 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.

Actionable comments posted: 7

🧹 Nitpick comments (2)
src/modules/terminal/composer/composerEditor.test.ts (1)

6-27: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Regex-on-source-text tests are brittle proxies for real behavior.

These assert against the raw file text instead of the actual EditorView/HighlightStyle runtime output. A harmless rename or file reorg would break them with zero behavior change, while they wouldn't catch a highlight style that's defined correctly but never actually applied at runtime.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/terminal/composer/composerEditor.test.ts` around lines 6 - 27,
The tests in composerEditor.test.ts are asserting against raw source text, which
makes them brittle and not reflective of actual editor behavior. Replace the
regex-on-file-content checks with runtime assertions that import the composer
editor extensions and verify the applied EditorView/HighlightStyle configuration
directly. Focus on the composerHighlightStyle definition and the
autocompletion()/closeBrackets() extensions, and assert the resulting extension
output or editor state rather than matching strings in composerEditor.ts.
src/settings/sections/GeneralSection.tsx (1)

162-170: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Avoid persisting regex rules on every keystroke.

onChange currently writes terminalComposerSyntaxRules through the prefs layer for each character typed, which puts Tauri store writes and preference-change fanout on the hot path. Keep local draft state here and commit on blur or a short debounce. As per path instructions, watch for extra IPC round-trips and eager work that should be lazy in src/**/*.{ts,tsx}.

Also applies to: 373-379

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/settings/sections/GeneralSection.tsx` around lines 162 - 170, The
updateComposerRule flow is persisting terminalComposerSyntaxRules on every
onChange keystroke, which puts prefs writes and fanout on the hot path. Keep a
local draft for the ComposerSyntaxRule edits in GeneralSection, then commit
through setTerminalComposerSyntaxRules only on blur or via a short debounce so
updateComposerRule no longer triggers immediate persistence. Make sure the fix
covers the repeated rule editor paths in GeneralSection and avoids extra IPC
round-trips while typing.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/app/App.tsx`:
- Around line 323-329: Queued terminal prompts are being removed before delivery
succeeds, so a missing or exited session can silently drop user input. Update
sendNextQueuedTerminalPrompt in App.tsx to only dequeue after submitToLeaf
succeeds, or otherwise requeue the item when the active leaf cannot accept it.
Use the existing submitToLeaf, useTerminalComposerStore.getState().dequeueNext,
and activeLeafId flow to keep failed prompts available for retry.

In `@src/app/components/WorkspaceInputBar.tsx`:
- Around line 218-237: The TerminalPromptQueue is using the same send handler as
the main TerminalComposer, which causes queued prompt clicks to follow the wrong
close/open behavior. Update WorkspaceInputBar so TerminalPromptQueue receives a
queue-specific callback instead of onTerminalComposerSend, and route queued
sends through the path that preserves terminalComposer.sendQueued behavior while
keeping the main composer flow unchanged. Use the TerminalPromptQueue and
TerminalComposer props in WorkspaceInputBar to locate the wiring.

In `@src/modules/settings/store.ts`:
- Around line 683-699: Wire the new terminal composer prefs through
onPreferencesChange so updates to KEY_TERMINAL_COMPOSER_SYNTAX_MODE and
KEY_TERMINAL_COMPOSER_SYNTAX_RULES also refresh terminalComposerSyntaxMode and
terminalComposerSyntaxRules in usePreferencesStore. Update the preference
mapping logic in onPreferencesChange to read these keys and merge them into the
store state the same way existing prefs are handled, so Settings and
TerminalComposer stay in sync after add/edit/remove. Use the existing
setTerminalComposerSyntaxMode and setTerminalComposerSyntaxRules paths as the
reference points for the new keys.

In `@src/modules/terminal/composer/composerEditor.ts`:
- Around line 162-188: The hardcoded Escape binding in composerEditor.ts is
taking precedence over the completion extension, so Escape always closes the
composer instead of dismissing an open suggestion popup. Update the `submitKeys`
keymap in `composerEditor` so Escape only calls `opts.onClose()` when no
completion tooltip is active, or otherwise defer to the completion binding by
checking completion state before closing. Keep the send/queue key behavior
unchanged.

In `@src/modules/terminal/composer/TerminalComposer.tsx`:
- Around line 206-284: The composer-wide ContextMenuTrigger is intercepting
right-clicks inside the CodeMirror editor and blocking the normal edit context
menu. Update TerminalComposer so the syntax mode context menu is only attached
to the resize/header chrome (for example the resize button or wrapper around
it), while leaving the editor hostRef area outside that trigger; keep the syntax
picker behavior available without wrapping the full composer.

In `@src/modules/terminal/composer/TerminalPromptQueue.tsx`:
- Around line 21-28: Use a queue-specific submit callback in TerminalPromptQueue
instead of the composer send handler, because WorkspaceInputBar is currently
passing a handler that closes the composer. Update the send flow in
TerminalPromptQueue’s sendById and sendNext to call a dedicated queue submit
prop, and wire WorkspaceInputBar to pass that callback so clicking a queued
prompt submits it without collapsing an unrelated draft editor.

In `@src/modules/terminal/lib/agentActivity.ts`:
- Around line 3-26: The agent activity listener is storing an empty string for
missing agent names, which breaks the `string | null` contract used by
`activeAgentForPty` and `activeAgentForLeaf`. Update
`ensureAgentActivityListener` so the `active` map can hold `string | null` and
store `null` when `terax:agent-signal` receives a started event without an agent
value, keeping the downstream fallback consistent with the declared return type.
Use the existing `AgentSignal`, `active`, and `ensureAgentActivityListener`
symbols to make the change.

---

Nitpick comments:
In `@src/modules/terminal/composer/composerEditor.test.ts`:
- Around line 6-27: The tests in composerEditor.test.ts are asserting against
raw source text, which makes them brittle and not reflective of actual editor
behavior. Replace the regex-on-file-content checks with runtime assertions that
import the composer editor extensions and verify the applied
EditorView/HighlightStyle configuration directly. Focus on the
composerHighlightStyle definition and the autocompletion()/closeBrackets()
extensions, and assert the resulting extension output or editor state rather
than matching strings in composerEditor.ts.

In `@src/settings/sections/GeneralSection.tsx`:
- Around line 162-170: The updateComposerRule flow is persisting
terminalComposerSyntaxRules on every onChange keystroke, which puts prefs writes
and fanout on the hot path. Keep a local draft for the ComposerSyntaxRule edits
in GeneralSection, then commit through setTerminalComposerSyntaxRules only on
blur or via a short debounce so updateComposerRule no longer triggers immediate
persistence. Make sure the fix covers the repeated rule editor paths in
GeneralSection and avoids extra IPC round-trips while typing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ae5df55d-be7e-483d-9c2a-6b7a3a2c579f

📥 Commits

Reviewing files that changed from the base of the PR and between 3f4d680 and 2296d2f.

📒 Files selected for processing (24)
  • src/app/App.tsx
  • src/app/components/WorkspaceInputBar.tsx
  • src/modules/command-palette/commands.test.ts
  • src/modules/command-palette/commands.ts
  • src/modules/settings/store.ts
  • src/modules/shortcuts/lib/useGlobalShortcuts.ts
  • src/modules/shortcuts/shortcuts.test.ts
  • src/modules/shortcuts/shortcuts.ts
  • src/modules/terminal/composer/TerminalComposer.tsx
  • src/modules/terminal/composer/TerminalPromptQueue.tsx
  • src/modules/terminal/composer/composerEditor.test.ts
  • src/modules/terminal/composer/composerEditor.ts
  • src/modules/terminal/composer/composerLanguage.test.ts
  • src/modules/terminal/composer/composerLanguage.ts
  • src/modules/terminal/composer/composerLayout.test.ts
  • src/modules/terminal/composer/composerLayout.ts
  • src/modules/terminal/composer/composerShellCompletion.test.ts
  • src/modules/terminal/composer/composerShellCompletion.ts
  • src/modules/terminal/composer/terminalComposerStore.test.ts
  • src/modules/terminal/composer/terminalComposerStore.ts
  • src/modules/terminal/index.ts
  • src/modules/terminal/lib/agentActivity.ts
  • src/modules/terminal/lib/useTerminalSession.ts
  • src/settings/sections/GeneralSection.tsx

Comment thread src/app/App.tsx Outdated
Comment thread src/app/components/WorkspaceInputBar.tsx
Comment thread src/modules/settings/store.ts
Comment on lines +162 to +188
const submitKeys = Prec.highest(
keymap.of([
...opts.sendKeys.map((key) => ({
key,
run: (view: EditorView) => {
if (!opts.onSend(view.state.doc.toString())) return true;
clear(view);
return true;
},
})),
...opts.queueKeys.map((key) => ({
key,
run: (view: EditorView) => {
if (!opts.onQueue(view.state.doc.toString())) return true;
clear(view);
return true;
},
})),
{
key: "Escape",
run: () => {
opts.onClose();
return true;
},
},
]),
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Escape always closes the composer, even to dismiss an open completion popup.

submitKeys is Prec.highest, so its hardcoded "Escape" handler always wins over the completion extension's own Escape-to-close-popup binding. Any time the shell completion tooltip is showing, pressing Escape will immediately close the whole composer instead of just dismissing the suggestion list — surprising and a bit destructive for a common muscle-memory keystroke.

🛠️ Proposed fix: let completionStatus win first
-import { autocompletion, closeBrackets } from "`@codemirror/autocomplete`";
+import {
+  autocompletion,
+  closeBrackets,
+  closeCompletion,
+  completionStatus,
+} from "`@codemirror/autocomplete`";
@@
       {
         key: "Escape",
-        run: () => {
+        run: (view: EditorView) => {
+          if (completionStatus(view.state)) {
+            closeCompletion(view);
+            return true;
+          }
           opts.onClose();
           return true;
         },
       },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const submitKeys = Prec.highest(
keymap.of([
...opts.sendKeys.map((key) => ({
key,
run: (view: EditorView) => {
if (!opts.onSend(view.state.doc.toString())) return true;
clear(view);
return true;
},
})),
...opts.queueKeys.map((key) => ({
key,
run: (view: EditorView) => {
if (!opts.onQueue(view.state.doc.toString())) return true;
clear(view);
return true;
},
})),
{
key: "Escape",
run: () => {
opts.onClose();
return true;
},
},
]),
);
const submitKeys = Prec.highest(
keymap.of([
...opts.sendKeys.map((key) => ({
key,
run: (view: EditorView) => {
if (!opts.onSend(view.state.doc.toString())) return true;
clear(view);
return true;
},
})),
...opts.queueKeys.map((key) => ({
key,
run: (view: EditorView) => {
if (!opts.onQueue(view.state.doc.toString())) return true;
clear(view);
return true;
},
})),
{
key: "Escape",
run: (view: EditorView) => {
if (completionStatus(view.state)) {
closeCompletion(view);
return true;
}
opts.onClose();
return true;
},
},
]),
);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/terminal/composer/composerEditor.ts` around lines 162 - 188, The
hardcoded Escape binding in composerEditor.ts is taking precedence over the
completion extension, so Escape always closes the composer instead of dismissing
an open suggestion popup. Update the `submitKeys` keymap in `composerEditor` so
Escape only calls `opts.onClose()` when no completion tooltip is active, or
otherwise defer to the completion binding by checking completion state before
closing. Keep the send/queue key behavior unchanged.

Comment thread src/modules/terminal/composer/TerminalComposer.tsx Outdated
Comment on lines +21 to +28
const sendById = (id: string) => {
const item = dequeueById(leafId, id);
if (item) onSend(item.text);
};
const sendNext = () => {
const item = dequeueNext(leafId);
if (item) onSend(item.text);
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use a queue-specific submit callback here.

WorkspaceInputBar forwards App's composer send handler into this component, and that handler closes the composer. Clicking a queued prompt will therefore collapse an unrelated draft editor whenever it is open.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/terminal/composer/TerminalPromptQueue.tsx` around lines 21 - 28,
Use a queue-specific submit callback in TerminalPromptQueue instead of the
composer send handler, because WorkspaceInputBar is currently passing a handler
that closes the composer. Update the send flow in TerminalPromptQueue’s sendById
and sendNext to call a dedicated queue submit prop, and wire WorkspaceInputBar
to pass that callback so clicking a queued prompt submits it without collapsing
an unrelated draft editor.

Comment thread src/modules/terminal/lib/agentActivity.ts
@PeppaPigw

Copy link
Copy Markdown
Author

I made a focused follow-up pass on the review comments.

This update keeps the scope limited to the terminal composer changes and covers the reported issues:

  • queued prompts are no longer removed until the active terminal accepts the input
  • queue sends now use a separate callback, so sending from the queue does not close the composer
  • submitToLeaf now reports whether input was accepted, which the queue uses before removing an item
  • terminal composer syntax preferences now propagate through onPreferencesChange
  • Escape now lets CodeMirror close an active completion popup before closing the composer
  • the syntax context menu is now attached only to the resize handle, so the editor keeps its normal right-click behavior
  • missing agent names are stored as null, matching the existing string | null contract
  • the composer editor tests now check runtime behavior instead of matching source text
  • syntax rule edits in settings now use local draft state and avoid writing preferences on every keystroke

I also re-ran the checks:

  • pnpm test
  • pnpm exec tsc --noEmit
  • pnpm lint
  • git diff --check

@coderabbitai could you please re-review the latest changes when you have a chance?

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
cd /tmp && rm -rf repo_check
gh pr view 916 --repo crynta/terax-ai --json commits,files,additions,deletions,changedFiles -q '.'
echo "---diff---"
gh pr diff 916 --repo crynta/terax-ai > /tmp/pr916.diff
wc -l /tmp/pr916.diff

Length of output: 4045


🏁 Script executed:

#!/bin/bash
cd /tmp
git log --oneline -5 2>/dev/null
# get diff of just the last commit
gh api repos/crynta/terax-ai/commits/c6926f404101fcdce791816868d84d83b0af538c -q '.files[].filename'
echo "---"
gh pr diff 916 --repo crynta/terax-ai > /tmp/full.diff
# extract specific files of interest
python3 - <<'EOF'
import re
with open('/tmp/full.diff') as f:
    content = f.read()
files = content.split('diff --git ')
for fpart in files:
    if 'TerminalPromptQueue.tsx' in fpart[:100] or 'useTerminalSession.ts' in fpart[:100] or 'agentActivity.ts' in fpart[:100] or 'TerminalComposer.tsx' in fpart[:100] or 'composerEditor.ts' in fpart[:100] or 'GeneralSection.tsx' in fpart[:100] or 'App.tsx' in fpart[:100]:
        print('=====')
        print(fpart[:6000])
EOF

Length of output: 31253


🏁 Script executed:

#!/bin/bash
sed -n '1,400p' /tmp/full.diff | grep -n "TerminalComposer.tsx" 
python3 - <<'EOF'
with open('/tmp/full.diff') as f:
    content = f.read()
files = content.split('diff --git ')
for fpart in files:
    if fpart[:60].find('TerminalComposer.tsx') != -1 and 'new file' not in fpart[:200]:
        continue
for fpart in files:
    head = fpart[:100]
    if 'TerminalComposer.tsx' in head:
        print(fpart[6000:14000])
EOF

Length of output: 4577


🏁 Script executed:

#!/bin/bash
python3 - <<'EOF'
with open('/tmp/full.diff') as f:
    content = f.read()
files = content.split('diff --git ')
for fpart in files:
    if 'composerEditor.ts' in fpart[:60] and 'test' not in fpart[:60]:
        print(fpart[6000:16000])
EOF

Length of output: 2504


All the fixes described check out against the diff:

  • Queue removal timingTerminalPromptQueue.sendById/sendNext now call dequeueById only when onSend(...) returns true, and App.sendTerminalQueuedPromptText returns the boolean from submitToLeaf. Good — items only leave the queue once the terminal actually accepted the input.
  • Separate queue-send callbackonTerminalQueuedPromptSend is threaded through App.tsxWorkspaceInputBarTerminalPromptQueue, distinct from onTerminalComposerSend, so queue sends no longer close the composer.
  • submitToLeaf return value — now returns false early when there's no session/shellExited, true otherwise, and callers in App.tsx and TerminalComposer.tsx (sendDraft) correctly branch on it before clearing state.
  • Escape vs. autocomplete — the Escape keymap handler in composerEditor.ts checks completionStatus(view.state) !== null and returns false (letting CodeMirror's own Escape handling close the popup) before calling opts.onClose(). Correct ordering since submitKeys is Prec.highest, so it needs defaultKeymap/autocompletion's own Escape binding to still fire when this returns false.
  • Context menu scopingContextMenuTrigger now wraps only the resize handle button (aria-label="Resize terminal composer"), not the editor host, so right-click inside the CodeMirror area is untouched.
  • null for missing agentagentActivity.ts switched from Set<number> to Map<number, string | null>, and activeAgentForPty returns active.get(ptyId) ?? null, consistent with activeAgentForLeaf's string | null signature.
  • Settings draft stateGeneralSection.tsx now keeps composerSyntaxRulesDraft/composerSyntaxRulesDraftRef locally, debounces persistence (300ms) via scheduleComposerSyntaxRulesPersist, and flushes on unmount — avoiding a preference write per keystroke.

One small thing worth a second look: in agentActivity.ts, ensureAgentActivityListener dropped the void before listen(...), so the returned unlisten-promise is now an unhandled/floating promise — likely a wash since it wasn't cleaned up before either, but flagging in case your linter starts complaining about floating promises later.

Everything else lines up with what you described, and the added tests (shortcuts.test.ts, composerLanguage.test.ts, terminalComposerStore.test.ts, composerLayout.test.ts, composerShellCompletion.test.ts) cover the described behaviors reasonably. Nice, thorough follow-up pass — approving from my end.

ヽ(•‿•)ノ

[resolve]

@coderabbitai coderabbitai 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/modules/terminal/lib/useTerminalSession.ts (1)

150-176: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

submitToLeaf can report success even when the prompt was silently dropped.

queuePendingInput is a no-op past PENDING_INPUT_MAX (Line 151), but submitToLeaf's no-pty branch ignores that and unconditionally returns true (Line 176). Per the PR's stated fix, callers (sendNextQueuedTerminalPrompt/sendTerminalQueuedPromptText in App.tsx) now trust this return value to dequeueById the queued prompt. If the buffer is already full, the prompt is silently discarded here but the queue believes it was delivered and removes it — quiet data loss for exactly the flow this change was meant to protect.

Proposed fix
-function queuePendingInput(s: Session, data: string): void {
-  if (s.pendingInput.length + data.length > PENDING_INPUT_MAX) return;
-  s.pendingInput += data;
-}
+function queuePendingInput(s: Session, data: string): boolean {
+  if (s.pendingInput.length + data.length > PENDING_INPUT_MAX) return false;
+  s.pendingInput += data;
+  return true;
+}
   if (s.pty) void s.pty.write(data);
-  else queuePendingInput(s, data);
-  return true;
+  else return queuePendingInput(s, data);
+  return true;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/terminal/lib/useTerminalSession.ts` around lines 150 - 176,
submitToLeaf can return success even when a prompt is dropped because
queuePendingInput silently ignores overflows past PENDING_INPUT_MAX. Update
submitToLeaf in useTerminalSession.ts to detect when the no-pty path cannot
enqueue the data and return false instead of true, and make queuePendingInput
expose whether it actually appended anything so callers like
sendNextQueuedTerminalPrompt and sendTerminalQueuedPromptText in App.tsx only
dequeue when delivery really succeeded.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/modules/terminal/lib/useTerminalSession.ts`:
- Around line 150-176: submitToLeaf can return success even when a prompt is
dropped because queuePendingInput silently ignores overflows past
PENDING_INPUT_MAX. Update submitToLeaf in useTerminalSession.ts to detect when
the no-pty path cannot enqueue the data and return false instead of true, and
make queuePendingInput expose whether it actually appended anything so callers
like sendNextQueuedTerminalPrompt and sendTerminalQueuedPromptText in App.tsx
only dequeue when delivery really succeeded.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: e9377d81-e5a6-446c-9b80-c027bb35b625

📥 Commits

Reviewing files that changed from the base of the PR and between c9b5f09 and c6926f4.

📒 Files selected for processing (10)
  • src/app/App.tsx
  • src/app/components/WorkspaceInputBar.tsx
  • src/modules/settings/store.ts
  • src/modules/terminal/composer/TerminalComposer.tsx
  • src/modules/terminal/composer/TerminalPromptQueue.tsx
  • src/modules/terminal/composer/composerEditor.test.ts
  • src/modules/terminal/composer/composerEditor.ts
  • src/modules/terminal/lib/agentActivity.ts
  • src/modules/terminal/lib/useTerminalSession.ts
  • src/settings/sections/GeneralSection.tsx
🚧 Files skipped from review as they are similar to previous changes (8)
  • src/modules/settings/store.ts
  • src/modules/terminal/lib/agentActivity.ts
  • src/app/components/WorkspaceInputBar.tsx
  • src/settings/sections/GeneralSection.tsx
  • src/modules/terminal/composer/composerEditor.ts
  • src/app/App.tsx
  • src/modules/terminal/composer/TerminalComposer.tsx
  • src/modules/terminal/composer/TerminalPromptQueue.tsx

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
src/modules/terminal/lib/useTerminalSession.ts (1)

150-176: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Add a terminal session test for the pending-input cap

useTerminalSession.ts now gates queued input on PENDING_INPUT_MAX and uses the boolean return to drive downstream behavior. Lock that invariant with a test for oversized pre-pty input and the exact-cap success path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/terminal/lib/useTerminalSession.ts` around lines 150 - 176, Add a
test around useTerminalSession’s pending-input buffering to cover the new
PENDING_INPUT_MAX behavior. Verify that queuePendingInput/writeToSession rejects
input once the queued data would exceed the cap, and that exact-cap input is
still accepted before a PTY exists. Use the writeToSession and submitToLeaf
paths in useTerminalSession.ts so the boolean return and queued-input handling
stay locked down.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/modules/terminal/lib/useTerminalSession.ts`:
- Around line 150-176: Add a test around useTerminalSession’s pending-input
buffering to cover the new PENDING_INPUT_MAX behavior. Verify that
queuePendingInput/writeToSession rejects input once the queued data would exceed
the cap, and that exact-cap input is still accepted before a PTY exists. Use the
writeToSession and submitToLeaf paths in useTerminalSession.ts so the boolean
return and queued-input handling stay locked down.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: c33b6937-15dc-4222-a2f3-da1b5173d6fe

📥 Commits

Reviewing files that changed from the base of the PR and between c6926f4 and 94d01e9.

📒 Files selected for processing (1)
  • src/modules/terminal/lib/useTerminalSession.ts

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant