Skip to content

fix(web): persist pending plan input drafts and stabilize preset answers#1137

Open
jamesx0416 wants to merge 8 commits into
pingdotgg:mainfrom
jamesx0416:t3code/fix-plan-input-cursor-draft
Open

fix(web): persist pending plan input drafts and stabilize preset answers#1137
jamesx0416 wants to merge 8 commits into
pingdotgg:mainfrom
jamesx0416:t3code/fix-plan-input-cursor-draft

Conversation

@jamesx0416

@jamesx0416 jamesx0416 commented Mar 16, 2026

Copy link
Copy Markdown
Contributor

Closes #375
Closes #918
Closes #528
Supersedes #921

What Changed

  • persist pending plan-mode user-input drafts in a dedicated thread/request-scoped store so they survive thread switches
  • make pending answers explicitly track whether the active source is a preset option or custom text
  • make preset selection authoritative so stale custom editor state cannot override it during submit
  • remove the timeout-based preset auto-advance/submit race and cover the pending-input flows with browser and unit tests

Why

Plan-mode pending user input had three user-facing correctness bugs in the web client:

  • while answering a plan-mode question, moving the caret away from the end of the custom input would snap it back to the end, which made mid-text edits effectively impossible
  • if you switched to another thread and then came back, the in-progress pending answer could disappear because that draft state was local to ChatView
  • if you typed custom text and then clicked a preset option, the flow could race and submit the stale custom text instead of the clicked preset

This keeps the fix scoped to the pending-input flow and makes answer transitions deterministic.

UI Changes

Interaction fix in the pending-input composer/preset flow.

  • pending custom drafts now survive thread navigation
  • caret edits in the custom answer field no longer snap to the end
  • clicking a preset after typing custom text now submits the preset instead of the stale custom answer

Videos

Before:
https://drive.google.com/file/d/14BZBw1eOdVpHUkeXesZhcPjferbygFmV/view?usp=sharing

After:
https://drive.google.com/file/d/12kl7Yd2Y5cy4UMer7jSU74Uc-CjpLcYS/view?usp=sharing

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Verification

  • bun fmt
  • bun lint
  • bun typecheck
  • bun run --cwd apps/web test -- src/pendingUserInput.test.ts
  • bun run --cwd apps/web test:browser -- src/components/ChatView.browser.tsx

Note

Persist pending plan input drafts per thread and stabilize preset answer selection

  • Adds a Zustand store (pendingUserInputDraftStore.ts) that persists pending user input answers and question indices to localStorage per thread/request, restoring them across page reloads and pruning drafts for inactive requests.
  • Introduces an explicit answerSource discriminator ('option' | 'custom') in pendingUserInput.ts so answer resolution is deterministic when both a custom answer and selected options exist.
  • Single-select option clicks in ComposerPendingUserInputPanel.tsx now immediately advance to the next question or submit on the last question, replacing a 200ms auto-advance timer.
  • Stale custom text is suppressed when a preset option is selected, and the composer stays in sync with the active draft question with cursor position preserved.
  • Risk: answer resolution behavior changes when both a custom answer and selected options are stored — answerSource now takes precedence over the previous heuristic that always preferred non-empty custom text.

Macroscope summarized 34f182c.

Summary by CodeRabbit

  • New Features
    • Pending user-input answers now save as per-conversation drafts that persist across sessions and thread navigation.
    • Preset (option) vs custom answers are tracked separately so selecting presets won’t be lost to stale custom text.
    • Option selection can auto-advance or auto-submit in multi-question prompts.
  • Bug Fixes
    • Preserves in-progress edits and caret position when navigating between threads.

Note

Medium Risk
Touches chat submit paths and persisted client state for orchestration user-input; logic is localized but incorrect answer resolution could still mis-submit plan prompts.

Overview
Fixes plan-mode pending user-input bugs by persisting drafts in a new thread/request-scoped Zustand store (localStorage) instead of ephemeral ChatView state, so in-progress answers survive thread switches and reloads.

Draft answers now carry an answerSource (option | custom) so resolution and UI treat preset vs custom explicitly—stale composer text cannot win over a clicked preset at submit. Single-select presets advance or submit immediately via onSelectOption options; the 200ms timer auto-advance in ComposerPendingUserInputPanel is removed. ChatView adds composer↔draft sync (with a short post-preset suppression window), duplicate-submit guarding on user-input responses, and cleanup of drafts for inactive requests.

onAdvanceActivePendingUserInput on the last question with incomplete answers now jumps to the first unanswered question. Tests in pendingUserInput.test.ts cover the new resolution and progress behavior.

Reviewed by Cursor Bugbot for commit 34f182c. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actions github-actions Bot added size:XXL 1,000+ changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Mar 16, 2026
@coderabbitai

coderabbitai Bot commented Mar 16, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Per-thread persistent drafts for pending user input were added via a new Zustand store; components migrated from local state to this draft store, pending-answer source tracking (option vs custom) was introduced, Composer panel selection API changed to include advance/submit flags, and tests were extended for multi-question and navigation flows.

Changes

Cohort / File(s) Summary
Draft Store Infrastructure
apps/web/src/pendingUserInputDraftStore.ts
New persistent, debounced Zustand store for per-thread pending-user-input drafts, with APIs to set question index, set answers, clear inactive requests, and a thread-specific selector.
Pending Input Domain Logic
apps/web/src/pendingUserInput.ts, apps/web/src/pendingUserInput.test.ts
Added PendingUserInputAnswerSource and answerSource on draft answers; implemented setPendingUserInputSelectedOption, updated custom-answer handling and progress derivation to respect explicit answer source; tests added/updated for option vs custom semantics.
ChatView Migration
apps/web/src/components/ChatView.tsx, apps/web/src/components/ChatView.browser.tsx
Replaced component-local pending-input state with draft-store hooks (usePendingUserInputDraftStore, usePendingUserInputThreadDraft), added lifecycle cleanup (clearInactiveRequests), refs to coordinate write sources, and updated handlers to persist drafts. Browser tests adjusted/expanded to exercise draft persistence, caret/selection behavior, and multi-question flows.
Composer Panel API Changes
apps/web/src/components/chat/ComposerPendingUserInputPanel.tsx
Reworked onSelectOption signature to accept options?: { advanceToNextQuestion?: boolean; submitIfComplete?: boolean }, removed onAdvance prop and internal auto-advance timer; selection now signals auto-advance/submit through options flags.
Test Utilities & Fixtures
apps/web/src/components/ChatView.browser.tsx (test helpers), apps/web/src/pendingUserInput.test.ts
Added test fixtures and utilities (composer interaction helpers, snapshot creators) and multiple tests validating preservation of pending drafts, preset vs custom handling, cursor/edit behavior, and multi-question flows.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Composer
    participant ChatView
    participant DraftStore
    participant Storage

    User->>Composer: type or select answer
    Composer->>ChatView: onChange/onSelect(questionId, label, {advance/submit?})
    ChatView->>DraftStore: setAnswer(threadId, requestId, questionId, draft)
    DraftStore->>Storage: persist (debounced)
    Storage-->>DraftStore: persisted

    Note over User,ChatView: navigate away and return

    User->>ChatView: switch to thread
    ChatView->>DraftStore: usePendingUserInputThreadDraft(threadId)
    DraftStore-->>ChatView: restored answers & questionIndex
    ChatView->>Composer: supply restored draft
    Composer-->>User: render preserved input
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐰 I nibbled drafts into cozy store,

No lost replies when hopping door to door.
Options and text kept tidy and clear,
Cursor freed to leap back, hop and steer.
Cheers — your answers wait, safe and near.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed All code changes directly address the three linked issues: #375 (cursor movement fix via draft store preventing snap-back), #918 (draft persistence via Zustand store surviving thread switches), and #528 (editable cursor behavior enabled by proper state management).
Out of Scope Changes check ✅ Passed All changes are scoped to pending user input flows: draft store, pending answer source tracking, auto-advance logic refactoring, and related test additions. No unrelated modifications detected outside the stated objectives.
Title check ✅ Passed The title clearly summarizes the main change: persisting pending plan input drafts and stabilizing preset answers.
Description check ✅ Passed The description matches the template well with What Changed, Why, UI Changes, Checklist, Videos, and Verification.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@jamesx0416

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Mar 16, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

1 similar comment
@coderabbitai

coderabbitai Bot commented Mar 16, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: 3

Caution

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

⚠️ Outside diff range comments (1)
apps/web/src/components/ChatView.tsx (1)

3485-3491: ⚠️ Potential issue | 🟠 Major

Pass the user-input responding state into the pending-input panel.

This still passes respondingRequestIds from approval responses. While thread.user-input.respond is in flight, the preset buttons and digit shortcuts remain enabled, so the new auto-submit path can dispatch duplicate user-input responses.

💡 Suggested fix
                     <ComposerPendingUserInputPanel
                       pendingUserInputs={pendingUserInputs}
-                      respondingRequestIds={respondingRequestIds}
+                      respondingRequestIds={respondingUserInputRequestIds}
                       answers={activePendingDraftAnswers}
                       questionIndex={activePendingQuestionIndex}
                       onSelectOption={onSelectActivePendingUserInputOption}
                     />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/ChatView.tsx` around lines 3485 - 3491, Replace the
generic approval-response `respondingRequestIds` prop with the user-input
responding state so the pending-input panel disables presets while a
`thread.user-input.respond` is in flight: locate the
ComposerPendingUserInputPanel usage and either pass an existing variable that
tracks user-input responses (e.g., userInputRespondingRequestIds or
threadUserInputRespondingIds) or add a selector/prop that returns the request
IDs for `thread.user-input.respond`, and pass that prop (name it clearly like
userInputRespondingRequestIds) into ComposerPendingUserInputPanel instead of the
approval `respondingRequestIds` so the panel can correctly disable
buttons/shortcuts during user-input respond.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/components/ChatView.browser.tsx`:
- Around line 406-419: The secondary thread fixture copies
baseSnapshot.threads[0]!.session so its session.threadId still equals THREAD_ID;
update the copied thread to have a session whose threadId is SECOND_THREAD_ID
(and if the session object contains an id that encodes the thread, update that
too) so the thread’s session uniquely references SECOND_THREAD_ID instead of
THREAD_ID (locate the object with SECOND_THREAD_ID in the diff and
replace/override its session.threadId accordingly).

In `@apps/web/src/components/ChatView.tsx`:
- Around line 666-668: The effect currently calls
clearInactivePendingUserInputDraftRequests(threadId,
activePendingUserInputRequestIds) immediately, which can delete restored drafts
before pending requests hydrate; change the effect to guard so it only runs
after pendingUserInputs have been hydrated (e.g., add pendingUserInputs to the
dependency array and check that pendingUserInputs is not undefined/null and/or
that pendingUserInputs.length > 0 OR activePendingUserInputRequestIds.length >
0) before invoking clearInactivePendingUserInputDraftRequests; this ensures you
only prune drafts once the thread's pending inputs have loaded.

In `@apps/web/src/pendingUserInput.ts`:
- Around line 56-68: Normalize the stored customAnswer to collapse
whitespace-only drafts: keep calling normalizeDraftAnswer(customAnswer) into
normalizedCustomAnswer, then compute a storedCustomAnswer variable that is
customAnswer when normalizedCustomAnswer is truthy, but if
normalizedCustomAnswer is falsy and customAnswer is only whitespace
(customAnswer?.trim() === ""), set storedCustomAnswer to "" (otherwise keep the
original customAnswer); return storedCustomAnswer instead of the raw
customAnswer and leave the existing logic around selectedOptionLabel and
answerSource (references: normalizeDraftAnswer, customAnswer,
selectedOptionLabel, normalizedCustomAnswer).

---

Outside diff comments:
In `@apps/web/src/components/ChatView.tsx`:
- Around line 3485-3491: Replace the generic approval-response
`respondingRequestIds` prop with the user-input responding state so the
pending-input panel disables presets while a `thread.user-input.respond` is in
flight: locate the ComposerPendingUserInputPanel usage and either pass an
existing variable that tracks user-input responses (e.g.,
userInputRespondingRequestIds or threadUserInputRespondingIds) or add a
selector/prop that returns the request IDs for `thread.user-input.respond`, and
pass that prop (name it clearly like userInputRespondingRequestIds) into
ComposerPendingUserInputPanel instead of the approval `respondingRequestIds` so
the panel can correctly disable buttons/shortcuts during user-input respond.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 1358f907-5b9b-4fab-b8ee-b7a93d0a5f6b

📥 Commits

Reviewing files that changed from the base of the PR and between e6d9a27 and 7bf2a1d.

📒 Files selected for processing (6)
  • apps/web/src/components/ChatView.browser.tsx
  • apps/web/src/components/ChatView.tsx
  • apps/web/src/components/chat/ComposerPendingUserInputPanel.tsx
  • apps/web/src/pendingUserInput.test.ts
  • apps/web/src/pendingUserInput.ts
  • apps/web/src/pendingUserInputDraftStore.ts

Comment thread apps/web/src/components/ChatView.browser.tsx Outdated
Comment thread apps/web/src/components/ChatView.tsx Outdated
Comment thread apps/web/src/pendingUserInput.ts
@jamesx0416

jamesx0416 commented Mar 18, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Mar 18, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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 (2)
apps/web/src/components/ChatView.tsx (1)

2648-2648: Extract the suppression TTL into a named constant.

Using a named constant improves readability and makes timing adjustments safer.

💡 Suggested refactor
+const PENDING_CUSTOM_RESTORE_SUPPRESSION_MS = 500;
-        expiresAtMs: performance.now() + 500,
+        expiresAtMs: performance.now() + PENDING_CUSTOM_RESTORE_SUPPRESSION_MS,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/ChatView.tsx` at line 2648, Replace the magic number
500 used for suppression TTL in the expiresAtMs assignment with a named constant
(e.g., SUPPRESSION_TTL_MS) declared near the top of the ChatView component or
module scope; update the expiresAtMs: performance.now() + 500 expression in the
code that sets suppression (the occurrence around expiresAtMs in ChatView.tsx)
to use SUPPRESSION_TTL_MS, and choose a clear name and location so future
changes to the suppression duration are centralized and readable.
apps/web/src/components/ChatView.browser.tsx (1)

686-700: Use the latest matching thread.user-input.respond request in the helper.

This helper currently picks the first matching request, which can become stale if a test emits multiple responses.

💡 Suggested change
-      const request = wsRequests.find((wsRequest) => {
+      const request = [...wsRequests].reverse().find((wsRequest) => {
         if (wsRequest._tag !== ORCHESTRATION_WS_METHODS.dispatchCommand) {
           return false;
         }
         const command =
           "command" in wsRequest && wsRequest.command && typeof wsRequest.command === "object"
             ? (wsRequest.command as Record<string, unknown>)
             : null;
         return command?.type === "thread.user-input.respond";
       });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/ChatView.browser.tsx` around lines 686 - 700, The
helper waitForPendingUserInputResponse currently finds the first matching
wsRequest and can return stale data; instead locate the latest matching
"thread.user-input.respond" dispatchCommand by scanning wsRequests for all
entries where wsRequest._tag === ORCHESTRATION_WS_METHODS.dispatchCommand and
the command object has type === "thread.user-input.respond" and pick the last
one (e.g., findLast or reverse iterate) before asserting and extracting answers;
update the request lookup in waitForPendingUserInputResponse to use that latest
match so tests that emit multiple responses use the most recent message.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/web/src/components/ChatView.browser.tsx`:
- Around line 686-700: The helper waitForPendingUserInputResponse currently
finds the first matching wsRequest and can return stale data; instead locate the
latest matching "thread.user-input.respond" dispatchCommand by scanning
wsRequests for all entries where wsRequest._tag ===
ORCHESTRATION_WS_METHODS.dispatchCommand and the command object has type ===
"thread.user-input.respond" and pick the last one (e.g., findLast or reverse
iterate) before asserting and extracting answers; update the request lookup in
waitForPendingUserInputResponse to use that latest match so tests that emit
multiple responses use the most recent message.

In `@apps/web/src/components/ChatView.tsx`:
- Line 2648: Replace the magic number 500 used for suppression TTL in the
expiresAtMs assignment with a named constant (e.g., SUPPRESSION_TTL_MS) declared
near the top of the ChatView component or module scope; update the expiresAtMs:
performance.now() + 500 expression in the code that sets suppression (the
occurrence around expiresAtMs in ChatView.tsx) to use SUPPRESSION_TTL_MS, and
choose a clear name and location so future changes to the suppression duration
are centralized and readable.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a70128a3-606a-47a9-98aa-e05c679d260b

📥 Commits

Reviewing files that changed from the base of the PR and between 7bf2a1d and f70ba78.

📒 Files selected for processing (2)
  • apps/web/src/components/ChatView.browser.tsx
  • apps/web/src/components/ChatView.tsx

- Move pending user-input draft state into a per-thread draft store
- Track explicit answer source (`option` vs `custom`) to avoid stale custom text overriding preset picks
- Preserve pending input edits/cursor behavior across caret moves and thread navigation
- Update pending-input panel flow to auto-advance/submit on option select and add coverage
- Keep secondary snapshot thread sessions aligned with their thread IDs
- Only clear inactive pending-input drafts when an active thread exists
- Pass user-input responding request IDs to the composer pending-input panel
- Pass explicit isResponding (from activePendingIsResponding) to the pending-
  user-input panel so presets disable during thread.user-input.respond, instead
  of the generic approval respondingRequestIds that left them clickable.
- Guard clearInactivePendingUserInputDraftRequests to skip pruning while thread
  activities have not hydrated, preventing restored drafts from being deleted
  during a brief empty-activities window on thread switch.
- Collapse whitespace-only custom drafts to empty string in
  setPendingUserInputCustomAnswer so pure-whitespace text is never persisted.
- Update ChatComposer to the panel's new onSelectOption prop surface.
@jamesx0416 jamesx0416 force-pushed the t3code/fix-plan-input-cursor-draft branch from f70ba78 to 74ee23c Compare June 24, 2026 11:01
@github-actions github-actions Bot added size:XL 500-999 changed lines (additions + deletions). and removed size:XXL 1,000+ changed lines (additions + deletions). labels Jun 24, 2026
Comment thread apps/web/src/components/ChatView.tsx
Comment thread apps/web/src/components/chat/ComposerPendingUserInputPanel.tsx
Comment thread apps/web/src/pendingUserInput.ts
@macroscopeapp

macroscopeapp Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

1 blocking correctness issue found. This PR introduces a new persistence store and complex timing-based state synchronization for draft handling, going beyond a simple bug fix. Additionally, an open review comment identifies a potential issue where user input could be dropped in multi-question flows, warranting human verification.

You can customize Macroscope's approvability policy. Learn more.

jamesx0416 and others added 2 commits June 24, 2026 22:51
Co-authored-by: macroscopeapp[bot] <170038800+macroscopeapp[bot]@users.noreply.github.com>
@jamesx0416 jamesx0416 force-pushed the t3code/fix-plan-input-cursor-draft branch from 95ef6c2 to e64daf7 Compare July 5, 2026 09:13
Comment thread apps/web/src/components/ChatView.tsx
Comment thread apps/web/src/components/ChatView.tsx Outdated
Comment thread apps/web/src/components/ChatView.tsx
@jamesx0416 jamesx0416 force-pushed the t3code/fix-plan-input-cursor-draft branch from 05c65cb to 0a4e02c Compare July 5, 2026 10:40
Comment thread apps/web/src/pendingUserInputDraftStore.ts
Comment thread apps/web/src/pendingUserInputDraftStore.ts Outdated
Comment thread apps/web/src/components/ChatView.tsx
@jamesx0416 jamesx0416 force-pushed the t3code/fix-plan-input-cursor-draft branch from 0a4e02c to 67380ce Compare July 5, 2026 10:46
Comment thread apps/web/src/pendingUserInput.ts
Comment thread apps/web/src/components/ChatView.tsx
@jamesx0416 jamesx0416 force-pushed the t3code/fix-plan-input-cursor-draft branch 2 times, most recently from 38bd4a9 to 06014b1 Compare July 5, 2026 10:58
Comment thread apps/web/src/components/ChatView.tsx
@jamesx0416 jamesx0416 force-pushed the t3code/fix-plan-input-cursor-draft branch from 06014b1 to 36d7fb7 Compare July 5, 2026 11:07
Comment thread apps/web/src/pendingUserInputDraftStore.ts Outdated
Comment thread apps/web/src/components/ChatView.tsx Outdated
Comment thread apps/web/src/components/ChatView.tsx Outdated
@jamesx0416 jamesx0416 force-pushed the t3code/fix-plan-input-cursor-draft branch from 36d7fb7 to b01557a Compare July 5, 2026 11:14
Comment thread apps/web/src/components/ChatView.tsx
Comment thread apps/web/src/components/ChatView.tsx
@jamesx0416 jamesx0416 force-pushed the t3code/fix-plan-input-cursor-draft branch from b01557a to 1978f3f Compare July 5, 2026 11:21

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 1978f3f. Configure here.

Comment thread apps/web/src/pendingUserInputDraftStore.ts
Comment thread apps/web/src/components/chat/ChatComposer.tsx
@jamesx0416 jamesx0416 force-pushed the t3code/fix-plan-input-cursor-draft branch from 1978f3f to 34f182c Compare July 6, 2026 04:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL 500-999 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

1 participant