From 7bc6fda4f87fe57a582756aa08004e9113d6ce2d Mon Sep 17 00:00:00 2001 From: Youzini-afk <13153778771cx@gmail.com> Date: Sat, 4 Apr 2026 01:28:26 +0800 Subject: [PATCH] Fix delayed recall card mount on new user messages --- index.js | 2 ++ tests/p0-regressions.mjs | 60 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 6f18ffc..0a4d5f7 100644 --- a/index.js +++ b/index.js @@ -2527,6 +2527,8 @@ function armPersistedRecallMessageUiObserver(sessionId, runAttempt) { persistedRecallUiRefreshObserver.observe(chatRoot, { childList: true, subtree: true, + attributes: true, + attributeFilter: ["mesid", "data-mesid", "data-message-id"], }); return true; } diff --git a/tests/p0-regressions.mjs b/tests/p0-regressions.mjs index e3c01ed..97ed73e 100644 --- a/tests/p0-regressions.mjs +++ b/tests/p0-regressions.mjs @@ -1008,6 +1008,11 @@ class FakeElement { .replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()); this.dataset[datasetKey] = normalized; } + this.ownerDocument?._notifyMutation({ + type: "attributes", + target: this, + attributeName: key, + }); } getAttribute(name) { @@ -1201,10 +1206,12 @@ class FakeMutationObserver { this.callback = callback; this.documentRef = documentRef; this.active = false; + this.options = {}; } - observe() { + observe(_target = null, options = {}) { this.active = true; + this.options = { ...options }; this.documentRef._registerObserver(this); } @@ -1215,6 +1222,16 @@ class FakeMutationObserver { _notify(record) { if (!this.active) return; + if (record?.type === "attributes" && !this.options?.attributes) return; + if (record?.type === "childList" && !this.options?.childList) return; + if ( + record?.type === "attributes" && + Array.isArray(this.options?.attributeFilter) && + this.options.attributeFilter.length > 0 && + !this.options.attributeFilter.includes(String(record.attributeName || "")) + ) { + return; + } queueMicrotask(() => { if (this.active) this.callback([record]); }); @@ -1471,6 +1488,46 @@ async function testRecallCardDelayedDomInsertionEventuallyRenders() { } } +async function testRecallCardDelayedStableMessageIndexEventuallyRenders() { + const chat = [ + { + is_user: true, + mes: "user-0", + extra: { + bme_recall: buildPersistedRecallRecord({ + injectionText: "recall-0", + selectedNodeIds: ["n1"], + nowIso: "2026-01-01T00:00:00.000Z", + }), + }, + }, + ]; + const harness = await createRecallUiHarness({ chat }); + const messageElement = createMessageElement(harness.document, 0, { + stableId: false, + withMesBlock: true, + isUser: true, + }); + harness.chatRoot.appendChild(messageElement); + + try { + harness.api.schedulePersistedRecallMessageUiRefresh(); + await waitForTick(); + messageElement.setAttribute("mesid", "0"); + await waitForTick(); + await waitForTick(); + await new Promise((resolve) => setTimeout(resolve, 35)); + await waitForTick(); + + assert.equal( + harness.chatRoot.querySelectorAll(".bme-recall-card").length, + 1, + ); + } finally { + harness.restoreGlobals(); + } +} + async function testRecallCardDoesNotMountOnNonUserFloor() { const chat = [ { @@ -4520,6 +4577,7 @@ await testGenerationRecallImmediateAfterCommandsBackfillsPersistedRecord(); await testRecallCardMountsOnStandardUserMessageDom(); await testRecallCardSkipsMountWithoutStableMessageIndex(); await testRecallCardDelayedDomInsertionEventuallyRenders(); +await testRecallCardDelayedStableMessageIndexEventuallyRenders(); await testRecallCardDoesNotMountOnNonUserFloor(); await testRecallCardRefreshCleansLegacyBadgeAndAvoidsDuplicates(); await testRecallCardExpandedContentRerendersAfterRecordUpdate();