Skip to content

fix(extension): repair Included Memories popup data handling and lifecycle#1261

Open
abhay-codes07 wants to merge 1 commit into
supermemoryai:mainfrom
abhay-codes07:fix/extension-included-memories-popup
Open

fix(extension): repair Included Memories popup data handling and lifecycle#1261
abhay-codes07 wants to merge 1 commit into
supermemoryai:mainfrom
abhay-codes07:fix/extension-included-memories-popup

Conversation

@abhay-codes07

Copy link
Copy Markdown
Contributor

Fixes #1257

What

The "Included Memories" popup pipeline on ChatGPT/Claude/T3 had four related defects, all rooted in the same ~180-line block duplicated across the three content scripts:

  1. Comma fragmentation. The background script returns memories as a string[]; assigning that to dataset.memoriesData coerces it into one comma-joined string, and the popup re-split it with .split(/[,\n]/). Any memory containing a comma showed up as several fake rows, and the index-based remove buttons spliced the wrong entries while rewriting a desynced prompt injection.
  2. Last-memory wipe. The removal cleanup used length <= 1, so removing one of two memories also silently discarded the remaining one.
  3. Listener/DOM leak. Every successful fetch appended a fresh popup to <body> and registered a new document-level click listener that was never removed. With auto-search firing per debounced input change, a few minutes of prompt editing accumulates dozens of orphaned global click handlers and stacked popups.
  4. Degraded prompt text. Removals re-joined the memories with " ," and the numbering baked in by the background handler went stale, leaving the injected block as 1. foo , 3. bar.

Fix

Since the block was byte-identical in chatgpt.ts, claude.ts, and t3.ts (apart from the prompt-element lookup), the popup now lives in a shared utils/included-memories.ts:

  • memories are stored as JSON on the dataset, so content survives the round-trip regardless of what characters it contains
  • the injected dataset.supermemories block is always rebuilt from that single source of truth, with numbering applied at build time so it stays correct after removals
  • rows re-render from the array instead of juggling data-memory-index attributes
  • removing entries keeps the last one until none remain (=== 0), and only then clears the injection and restores the icon
  • each popup disposes its predecessor (node, document listener, auto-dismiss timer) before mounting, so repeated auto-searches no longer leak

The background handler now returns raw memory strings and skips empty results; each content script passes its own site-specific prompt-element lookup. Net −206 lines.

Testing

  • bun run compile (tsc --noEmit) — clean
  • bun run build (wxt, chrome-mv3) — clean
  • biome check on all touched files — clean

The extension package has no test harness, so the helper was written as pure functions (serializeMemories/parseMemories/buildPromptInjection) plus a self-contained popup factory to keep the behaviour easy to reason about and to unit-test later if a harness lands. Happy to add screenshots/screen recording of the popup flow if that helps review.

cc @MaheshtheDev

…lifecycle

The related-memories flow on ChatGPT/Claude/T3 round-tripped the
background's string[] response through a dataset attribute, which
coerces the array into one comma-joined string. The popup then re-split
that string on /[,\n]/, so any memory whose content contains a comma
was fragmented into multiple fake rows, and the index-based remove
buttons spliced the wrong entries and rewrote a desynced prompt
injection. Three more defects lived in the same duplicated block:

- every fetch appended a fresh popup to <body> and registered a new
  document-level click listener that was never removed, so auto-search
  leaked one popup + one global listener per keystroke burst
- the removal cleanup used length <= 1, so removing one of two
  memories silently threw away the last remaining memory too
- removal re-joined memories with " ," and left the numbering baked
  into the strings, corrupting the injected prompt block

The block was byte-identical across chatgpt.ts, claude.ts, and t3.ts,
so the popup now lives in a shared utils/included-memories.ts helper:
memories are stored as JSON on the dataset (commas survive), the prompt
block is always rebuilt from that single source of truth, rows re-render
from the array instead of juggling indices, removing entries keeps the
last one until none remain (=== 0), and each popup disposes its
predecessor (listener, node, auto-dismiss timer) before mounting.

The background handler now returns raw memory strings and skips empty
ones; numbering is applied at injection time so it stays correct after
removals.

Verified with tsc --noEmit and a full wxt build.
Copilot AI review requested due to automatic review settings July 12, 2026 18:44

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@graphite-app graphite-app Bot added the extension related to Supermemory Chrome Extension label Jul 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

extension related to Supermemory Chrome Extension

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Extension: Included Memories popup fragments comma-containing memories, leaks global click listeners, and drops the last memory on removal

2 participants