Skip to content

fix(frontend): preserve composer drafts across remounts - #3035

Open
Alphaxiaoteng wants to merge 2 commits into
Chainlit:mainfrom
Alphaxiaoteng:fix-3022-preserve-composer-draft
Open

fix(frontend): preserve composer drafts across remounts#3035
Alphaxiaoteng wants to merge 2 commits into
Chainlit:mainfrom
Alphaxiaoteng:fix-3022-preserve-composer-draft

Conversation

@Alphaxiaoteng

@Alphaxiaoteng Alphaxiaoteng commented Aug 31, 2026

Copy link
Copy Markdown

Summary

Regression evidence

With the new owner-swap test kept in place and the composer temporarily restored to local state, the test failed as expected:

Expected the element to have value: draft message
Received:

The URL-prompt boundary also failed before its guard was added:

Expected: edited draft
Received: initial prompt

The first bounded-state revision also proved that merely hiding a stale session record was insufficient: switching a → b → a restored draft message. The final implementation clears that single record when the session changes.

Verification

  • pnpm --filter @chainlit/app test -- tests/MessageComposer.spec.tsx — 4 passed
  • pnpm --filter @chainlit/app test — 6 files, 36 tests passed
  • pnpm type-check — frontend and react-client passed; copilot reports its repository-configured skip
  • pnpm lint and pnpm lint:fix — passed
  • pnpm format-check and pnpm format — passed
  • pnpm build — react-client, frontend, and copilot production builds passed
  • git diff --check — passed

Prior work and AI assistance

PR #3028 previously explored the same session-scoped state direction and was closed by its author before maintainer review. Thank you to @Iams4kura for that public investigation. This PR independently re-ran the baseline, adds explicit owner-level, session-lifecycle, and URL-prompt regressions, and follows this repository's required AI attribution. OpenAI Codex assisted with implementation and review; both commits include the required Co-Authored-By trailer.

Co-Authored-By: OpenAI Codex <noreply@openai.com>

@cubic-dev-ai cubic-dev-ai 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.

1 issue found across 4 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="frontend/tests/MessageComposer.spec.tsx">

<violation number="1" location="frontend/tests/MessageComposer.spec.tsx:143">
P3: The session-isolation test only checks that a new session shows an empty draft; it never switches back to session 'a' to confirm the original draft is still retained. Since the PR's stated goal is to "isolate drafts between sessions" (each session keeps its own draft), a regression that clears the draft globally on session change would pass this test. Add a follow-up assertion that rerendering with sessionId 'a' restores 'draft message'.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread frontend/src/state/chat.ts Outdated
Comment thread frontend/src/components/chat/MessageComposer/index.tsx Outdated
});
view.rerender(<ComposerOwners sessionId="b" />);

await waitFor(() => {

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.

P3: The session-isolation test only checks that a new session shows an empty draft; it never switches back to session 'a' to confirm the original draft is still retained. Since the PR's stated goal is to "isolate drafts between sessions" (each session keeps its own draft), a regression that clears the draft globally on session change would pass this test. Add a follow-up assertion that rerendering with sessionId 'a' restores 'draft message'.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At frontend/tests/MessageComposer.spec.tsx, line 143:

<comment>The session-isolation test only checks that a new session shows an empty draft; it never switches back to session 'a' to confirm the original draft is still retained. Since the PR's stated goal is to "isolate drafts between sessions" (each session keeps its own draft), a regression that clears the draft globally on session change would pass this test. Add a follow-up assertion that rerendering with sessionId 'a' restores 'draft message'.</comment>

<file context>
@@ -0,0 +1,172 @@
+    });
+    view.rerender(<ComposerOwners sessionId="b" />);
+
+    await waitFor(() => {
+      expect(screen.getByRole('textbox')).toHaveValue('');
+    });
</file context>

Co-Authored-By: OpenAI Codex <noreply@openai.com>
@Alphaxiaoteng

Copy link
Copy Markdown
Author

Addressed the review in 89b1968:

  • Replaced the atomFamily with one bounded active-session draft record, and clear that record when sessionId changes, so Recoil cannot accumulate one atom per conversation.
  • Moved URL-prompt initialization state into the same record. A user-edited or explicitly deleted prompt now stays edited/deleted across the WelcomeScreen → Footer remount.
  • Clarified the session-boundary contract: drafts are intentionally discarded when the session changes rather than retained for later restoration. The regression now exercises a → b → a and expects the old draft to remain discarded.

Reverified on the new head: focused composer tests 4/4, full frontend suite 36/36, type-check, lint, Prettier, complete recursive production build, and git diff --check all passed.

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.

Composer remounts whenever the message list transitions empty↔non-empty, dropping in-progress input

1 participant