Skip to content

feat: chat persistence fixes, mobile UX, analytics scrubbing, and toolbar polish#44

Merged
batmn-dev merged 7 commits intomainfrom
observe-these-fists-of-vengeance
Feb 26, 2026
Merged

feat: chat persistence fixes, mobile UX, analytics scrubbing, and toolbar polish#44
batmn-dev merged 7 commits intomainfrom
observe-these-fists-of-vengeance

Conversation

@batmn-dev
Copy link
Owner

@batmn-dev batmn-dev commented Feb 24, 2026

Summary

  • Fix stale closure data loss in edit persistencecacheAndAddMessage now reads the current IndexedDB cache directly instead of relying on stale closure-captured React state, preventing sequential calls from silently dropping messages. Edited user messages are deferred to onFinish to avoid mutating provider state mid-batch.
  • Scrub PII from analytics and improve observability — Adds a PII scrubbing layer for PostHog events with tests, ensuring no user-identifiable data leaks into analytics.
  • Simplify user message rendering and persist model per chat — Streamlines the user message component and stores the selected model at the chat level.
  • Improve mobile sidebar UX and add share action — Enhances mobile sidebar behavior, adds a share/publish drawer, and refines header layout.
  • Auto-play toolbar reveal on stream completion — Fresh assistant responses animate the action toolbar in via a mask-reveal keyframe; historical messages keep the hover-triggered reveal.
  • Include Convex auth loading in ChatsProvider — Prevents flash of empty state while auth is still resolving.
  • New agent skills — Documents the dual-message-state architecture and stale-closure-persistence pattern for future reference.

Test plan

  • Send a message and verify the assistant toolbar animates in after streaming completes
  • Reload the page and verify historical messages show toolbar on hover (no auto-animation)
  • Edit a user message mid-conversation and verify both the edited user message and new assistant response persist after refresh
  • Rapidly edit and re-send to confirm no messages are silently dropped
  • Verify mobile sidebar opens/closes correctly and share action works
  • Confirm chat list does not flash empty on initial auth load

Made with Cursor


Summary by cubic

Strengthens chat edit persistence (even on aborted/error streams) and stabilizes mobile share publishing. Also improves mobile UX, scrubs PII from analytics, polishes the assistant toolbar, persists the selected model per chat, and simplifies user message rendering.

  • New Features

    • Mobile share drawer and sidebar polish: close on nav, hamburger icon, smoother sheet.
    • Persist model per chat from the header; keep last-used for new chats.
    • Assistant toolbar auto-reveals after a fresh stream; history stays hover-only.
  • Bug Fixes

    • Edit persistence: read from IndexedDB in cacheAndAddMessage, defer writes to onFinish, set createdAt; persists on abort/error and prevents drops/dupes.
    • Analytics: scrub PII for PostHog (inputs/outputs) with tests; replace swallowed tool-call-log errors with structured console.warn.
    • Share publishing: use stable chat IDs and URL-encoded text to avoid broken X links.
    • Prevent chat list flicker during Convex auth; deprecate /api/payclaw/status via headers + log.
    • Assistant actions: remove render-time ref access; drive reveal from props to satisfy lint.

Written for commit 32dbb2b. Summary will update on new commits.


Note

Low Risk
Deletes only .agents/archive/* markdown reference documents; no runtime code or configuration changes, so risk is limited to losing historical internal documentation.

Overview
Removes several large, outdated .agents/archive/* markdown documents (AI SDK upgrade research, self-hosting descope plan, icon migration plans/mappings, installation plan, and model registry update notes) to reduce repository noise and prevent stale internal guidance from being referenced.

Written by Cursor Bugbot for commit 32dbb2b. This will update automatically on new commits. Configure here.

batmn-dev and others added 4 commits February 23, 2026 15:31
- Add scrubForAnalytics utility to redact sensitive fields/patterns
  before PostHog capture (emails, phones, keys, addresses).
- Replace silently-swallowed tool-call-log write errors with structured
  console.warn JSON for debuggability.
- Mark /api/payclaw/status as deprecated with HTTP headers and log.

Co-authored-by: Cursor <cursoragent@cursor.com>
- Replace markdown rendering in user messages with plain-text
  whitespace-pre-wrap, removing custom component overrides in both
  message-user and chat-preview-panel.
- Enhance ModelSelectorHeader to read the current chat's persisted
  model and write model changes back to the chat record.
- Add Payclaw architecture hardening plan (#47).
- Add ChatGPT nav list widget analysis research.

Co-authored-by: Cursor <cursoragent@cursor.com>
Restructure sidebar header for consistent home link placement, close
sidebar on chat navigation, swap to hamburger menu icon, add share
option to chat actions menu on mobile, and smooth sheet animations.

Co-authored-by: Cursor <cursoragent@cursor.com>
…toolbar reveal

- Read IndexedDB cache directly in cacheAndAddMessage instead of stale closure
  state, preventing the second sequential call from dropping the first's write
- Defer edited user message persistence to onFinish to avoid provider state
  mutations during the sendMessage/setMessages React batch
- Ensure createdAt on cached messages for correct sort order
- Auto-play mask-reveal animation on fresh stream completion; keep hover-trigger
  for historical messages
- Include Convex auth loading in ChatsProvider isLoading
- Add dual-message-state and stale-closure-persistence agent skills

Co-authored-by: Cursor <cursoragent@cursor.com>
@vercel
Copy link

vercel bot commented Feb 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
not-a-wrapper Ready Ready Preview, Comment Feb 26, 2026 8:47pm

@greptile-apps
Copy link

greptile-apps bot commented Feb 24, 2026

Greptile Summary

This PR fixes a critical stale closure data loss bug in edit persistence, adds PII scrubbing to analytics, improves mobile UX, and polishes the assistant toolbar animation.

Key changes:

  • Fixed stale closure bug where sequential cacheAndAddMessage calls would overwrite each other by reading current IndexedDB cache instead of relying on closure-captured React state
  • Deferred edited user message persistence to onFinish via pendingEditUserMsgRef to avoid mutating provider state mid-batch
  • Added scrubForAnalytics layer with comprehensive tests to redact PII (emails, phones, payment info) from PostHog events
  • Auto-play toolbar reveal animation on fresh assistant responses using didStreamRef and CSS mask-reveal keyframe
  • Fixed flash of empty chat list by including isConvexAuthLoading in loading condition
  • Simplified user message rendering by removing unnecessary markdown processing
  • Enhanced mobile sidebar with improved close button placement and navigation behavior
  • Added share/publish drawer component for public conversation sharing

Confidence Score: 5/5

  • Safe to merge with high confidence
  • The core persistence fix is well-architected with defensive programming (reading from IndexedDB directly), PII scrubbing has comprehensive test coverage, UI changes are polish improvements, and the deferred persistence pattern cleanly separates concerns
  • No files require special attention

Important Files Changed

Filename Overview
lib/chat-store/messages/provider.tsx Fixed stale closure bug by reading current IndexedDB cache instead of using closure-captured React state in cacheAndAddMessage
app/components/chat/use-chat-core.ts Added deferred edit persistence via pendingEditUserMsgRef to persist edited user messages in onFinish after stream completes
app/components/chat/use-chat-edit.ts Integrated deferred edit persistence by calling setPendingEditUserMessage to stage edited messages for onFinish
lib/posthog/scrub.ts Implemented PII scrubbing layer that redacts sensitive keys and patterns (email, phone) before sending analytics events
lib/posthog/tests/scrub.test.ts Comprehensive test coverage for PII scrubbing with nested objects, arrays, patterns, and edge cases
app/api/chat/route.ts Applied scrubForAnalytics to message input/output and improved error logging for tool call failures
app/components/chat/message-assistant.tsx Added auto-play toolbar reveal animation for fresh streams using didStreamRef and mask-reveal keyframe
lib/chat-store/chats/provider.tsx Fixed flash of empty state by including isConvexAuthLoading in loading condition
app/components/layout/share-publish-drawer.tsx New component for sharing/publishing conversations with copy link and social sharing actions

Sequence Diagram

sequenceDiagram
    participant User
    participant EditUI as Edit UI
    participant ChatCore as use-chat-core
    participant Provider as MessagesProvider
    participant IDB as IndexedDB
    participant Convex

    User->>EditUI: Edit message
    EditUI->>ChatCore: setPendingEditUserMessage(editedMsg)
    Note over ChatCore: Store in pendingEditUserMsgRef
    EditUI->>ChatCore: sendMessage()
    ChatCore->>Provider: Stream starts
    Provider-->>User: Display streaming response
    
    Note over ChatCore: onFinish triggered
    ChatCore->>Provider: cacheAndAddMessage(pendingEdit)
    Provider->>IDB: Read current cache
    IDB-->>Provider: Return existing messages
    Provider->>IDB: Write [existing + editedUser]
    Provider->>Convex: Persist editedUser
    
    ChatCore->>Provider: cacheAndAddMessage(assistantMsg)
    Provider->>IDB: Read current cache (includes editedUser)
    IDB-->>Provider: Return messages with editedUser
    Provider->>IDB: Write [existing + assistant]
    Provider->>Convex: Persist assistant
Loading

Last reviewed commit: 6b6aa99

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

3 issues found across 26 files

Prompt for AI agents (all 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="app/components/layout/share-publish-drawer.tsx">

<violation number="1" location="app/components/layout/share-publish-drawer.tsx:40">
P2: URL-encode the tweet text before interpolating it into the intent URL; unencoded spaces/punctuation can produce an invalid query string.</violation>
</file>

<file name="app/components/chat/use-chat-core.ts">

<violation number="1" location="app/components/chat/use-chat-core.ts:186">
P2: Pending edited user messages are only persisted on successful stream completion. If the stream aborts/errors, the early return skips this block, so edited user messages can be dropped from persistence. Consider persisting (or at least clearing) pending edits before the early return on error/abort.</violation>
</file>

<file name="app/components/layout/chat-actions-menu.tsx">

<violation number="1" location="app/components/layout/chat-actions-menu.tsx:68">
P1: Share action targets the active session chatId instead of the menu’s chat prop, so sharing from a chat list will publish the wrong chat. Use the `chat.id` prop for mutations and drawer rendering.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Remove outdated archive, plans, and research files from .agents/.
Simplify and consolidate .cursor/rules; drop 070-documentation and 090-icon-sizing.

Co-authored-by: Cursor <cursoragent@cursor.com>
Use a derived flag from current message props to drive the post-stream action reveal,
avoiding ref reads/writes during render and satisfying react-hooks/refs lint rules.

Made-with: Cursor
Persist pending edited user messages even when streams abort or error, and tighten share behavior by using stable chat IDs and URL-encoding X share text to avoid malformed links.

Made-with: Cursor
@batmn-dev batmn-dev merged commit e102710 into main Feb 26, 2026
7 checks passed
Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 3 files (changes from recent commits).

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="app/components/layout/share-publish-drawer.tsx">

<violation number="1">
P2: Opening an external URL with `_blank` should include `noopener,noreferrer` to prevent reverse-tabnabbing via `window.opener`.</violation>
</file>

<file name="app/components/chat/use-chat-core.ts">

<violation number="1">
P2: The new early return on abort/error skips message ID reconciliation after persisting a pending edited message, which can leave duplicate local/server copies of that message.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@@ -0,0 +1,98 @@
"use client"
Copy link

@cubic-dev-ai cubic-dev-ai bot Feb 26, 2026

Choose a reason for hiding this comment

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

P2: Opening an external URL with _blank should include noopener,noreferrer to prevent reverse-tabnabbing via window.opener.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/components/layout/share-publish-drawer.tsx, line 41:

<comment>Opening an external URL with `_blank` should include `noopener,noreferrer` to prevent reverse-tabnabbing via `window.opener`.</comment>

<file context>
@@ -37,7 +37,8 @@ export function SharePublishDrawer({
     const text = `Check out this conversation I shared with Not A Wrapper! ${publicLink}`
-    window.open(`https://x.com/intent/tweet?text=${text}`, "_blank")
+    const encodedText = encodeURIComponent(text)
+    window.open(`https://x.com/intent/tweet?text=${encodedText}`, "_blank")
   }
 
</file context>
Suggested change
"use client"
window.open(`https://x.com/intent/tweet?text=${encodedText}`, "_blank", "noopener,noreferrer")
Fix with Cubic

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