,
+): FavouriteItem {
const type =
item.type === "sources" && item.primaryAction === "Run"
? "Saved search"
@@ -199,11 +209,14 @@ function toCommandItem(item: PrototypeFavouriteItem): FavouriteItem {
tabId: item.type,
set: item.set || (item.type === "services" ? "Saved services" : item.type === "forms" ? "Saved forms" : "Unsorted"),
evidence: item.sourceMeta,
- lastUsed: lastUsedByItemId[item.id] ?? "Saved",
+ lastUsed:
+ lastOpenedMap[item.id] !== undefined
+ ? formatLastOpened(lastOpenedMap[item.id])
+ : (lastUsedByItemId[item.id] ?? "Saved"),
action: item.primaryAction,
href: item.href,
icon: item.icon ?? fallbackIconByType[item.type],
- pinned: pinnedItemIds.has(item.id),
+ pinned: pinnedIds.has(item.id),
};
}
From 3a6c20239d72d9c59a871216dc165cdbccc9f277 Mon Sep 17 00:00:00 2001
From: BigSimmo <87357024+BigSimmo@users.noreply.github.com>
Date: Tue, 18 Aug 2026 06:03:55 +0800
Subject: [PATCH 3/7] fix(ui): keep document-viewer retry panel ids unique
under density tiers (PR #2074)
---
src/components/DocumentViewer.tsx | 2 +-
src/components/document-viewer/use-section-spy.ts | 4 ++++
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/components/DocumentViewer.tsx b/src/components/DocumentViewer.tsx
index 4b8d0b91e0..9a53f23c75 100644
--- a/src/components/DocumentViewer.tsx
+++ b/src/components/DocumentViewer.tsx
@@ -1394,7 +1394,7 @@ export function DocumentViewer({
past the PDF. */}
{readyDocument ? (
= {
"source-evidence": ["source-evidence-rail"],
+ // The rail's document-profile disclosure owns the canonical anchor (the spy
+ // treats it as an exclusive-accordion member), while the in-flow clinical
+ // summary card above the PDF is its phone/tablet copy.
+ "source-summary": ["source-summary-card"],
};
/**
From a98c42e2e3424b0e3f0fe56099a3533e5927102a Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 18 Aug 2026 02:17:17 +0000
Subject: [PATCH 4/7] =?UTF-8?q?fix(favourites):=20fix=20O(N=C2=B2)=20stora?=
=?UTF-8?q?ge=20callbacks=20and=20hydration=20mismatch=20in=20useSyncExter?=
=?UTF-8?q?nalStore?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: BigSimmo <87357024+BigSimmo@users.noreply.github.com>
---
.../favourites-command-library-page.tsx | 8 +++-
.../favourites/favourites-storage.ts | 37 ++++++++++---------
2 files changed, 26 insertions(+), 19 deletions(-)
diff --git a/src/components/clinical-dashboard/favourites-command-library-page.tsx b/src/components/clinical-dashboard/favourites-command-library-page.tsx
index 895a2568b3..202892925a 100644
--- a/src/components/clinical-dashboard/favourites-command-library-page.tsx
+++ b/src/components/clinical-dashboard/favourites-command-library-page.tsx
@@ -1122,9 +1122,13 @@ export function FavouritesCommandLibraryPage({ query = "", demoMode }: { query?:
const lastOpenedMap = useSyncExternalStore(
subscribeFavouritesStorage,
loadFavouriteLastOpened,
- loadFavouriteLastOpened,
+ () => ({} as Record),
+ );
+ const pinnedIds = useSyncExternalStore(
+ subscribeFavouritesStorage,
+ loadFavouritePinnedIds,
+ () => new Set(),
);
- const pinnedIds = useSyncExternalStore(subscribeFavouritesStorage, loadFavouritePinnedIds, loadFavouritePinnedIds);
const items = useMemo(
() =>
[...(demoMode ? prototypeFavouriteItems : []), ...savedRegistryFavourites].map((item) =>
diff --git a/src/components/favourites/favourites-storage.ts b/src/components/favourites/favourites-storage.ts
index 4504dde3b4..a658faba01 100644
--- a/src/components/favourites/favourites-storage.ts
+++ b/src/components/favourites/favourites-storage.ts
@@ -32,25 +32,28 @@ function notifyListeners() {
}
}
+// Single shared storage handler at module level to avoid O(N²) callbacks when
+// multiple subscribers are active (each per-subscriber handler would call
+// notifyListeners(), firing all listeners N times per storage event).
+let sharedStorageListenerAttached = false;
+function ensureSharedStorageListener() {
+ if (sharedStorageListenerAttached || typeof window === "undefined") return;
+ sharedStorageListenerAttached = true;
+ window.addEventListener("storage", (event: StorageEvent) => {
+ if (
+ event.key === DATABASE_FAVOURITES_LAST_OPENED_STORAGE_KEY ||
+ event.key === DATABASE_FAVOURITES_PINNED_STORAGE_KEY
+ ) {
+ inMemoryLastOpened = null;
+ inMemoryPinned = null;
+ notifyListeners();
+ }
+ });
+}
+
export function subscribeFavouritesStorage(listener: () => void): () => void {
+ ensureSharedStorageListener();
listeners.add(listener);
- if (typeof window !== "undefined") {
- const handleStorage = (event: StorageEvent) => {
- if (
- event.key === DATABASE_FAVOURITES_LAST_OPENED_STORAGE_KEY ||
- event.key === DATABASE_FAVOURITES_PINNED_STORAGE_KEY
- ) {
- inMemoryLastOpened = null;
- inMemoryPinned = null;
- notifyListeners();
- }
- };
- window.addEventListener("storage", handleStorage);
- return () => {
- listeners.delete(listener);
- window.removeEventListener("storage", handleStorage);
- };
- }
return () => {
listeners.delete(listener);
};
From 2b1ba0cae99cb7c737db32b6ef5dbc2574f4b145 Mon Sep 17 00:00:00 2001
From: Claude
Date: Tue, 18 Aug 2026 02:56:19 +0000
Subject: [PATCH 5/7] style: fix prettier formatting in favourites command
library page
CI's format:changed check was failing on this file's useSyncExternalStore
formatting.
---
.../favourites-command-library-page.tsx | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/src/components/clinical-dashboard/favourites-command-library-page.tsx b/src/components/clinical-dashboard/favourites-command-library-page.tsx
index 202892925a..0fa8256ba6 100644
--- a/src/components/clinical-dashboard/favourites-command-library-page.tsx
+++ b/src/components/clinical-dashboard/favourites-command-library-page.tsx
@@ -1122,13 +1122,9 @@ export function FavouritesCommandLibraryPage({ query = "", demoMode }: { query?:
const lastOpenedMap = useSyncExternalStore(
subscribeFavouritesStorage,
loadFavouriteLastOpened,
- () => ({} as Record),
- );
- const pinnedIds = useSyncExternalStore(
- subscribeFavouritesStorage,
- loadFavouritePinnedIds,
- () => new Set(),
+ () => ({}) as Record,
);
+ const pinnedIds = useSyncExternalStore(subscribeFavouritesStorage, loadFavouritePinnedIds, () => new Set());
const items = useMemo(
() =>
[...(demoMode ? prototypeFavouriteItems : []), ...savedRegistryFavourites].map((item) =>
From 09761d84ca2a882f1f7bbba0680adaf6b2373188 Mon Sep 17 00:00:00 2001
From: Claude
Date: Tue, 18 Aug 2026 03:15:43 +0000
Subject: [PATCH 6/7] fix(ui): keep document rail high-yield summary visible
when printing
`max-sm:hidden` (added to dedupe the summary panel on phone viewports)
also suppressed it during print emulation at narrow viewport widths,
since the print stylesheet only restores overflow/box-shadow and never
overrides display:none. Add `print:block`, the pattern already used
elsewhere in this codebase (verification-notice.tsx,
medication-considerations.tsx) to keep a responsively-hidden element
printable.
Fixes the "document viewer content disclosures are naturally closed
and mutually exclusive by default" Production UI failure, which set a
390px viewport and asserted the high-yield-summary content becomes
visible under print media.
---
src/components/document-viewer/document-rail-panels.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/document-viewer/document-rail-panels.tsx b/src/components/document-viewer/document-rail-panels.tsx
index d89bde41d9..58dec45dd1 100644
--- a/src/components/document-viewer/document-rail-panels.tsx
+++ b/src/components/document-viewer/document-rail-panels.tsx
@@ -138,7 +138,7 @@ export function DocumentViewerRail({
data-testid="high-yield-summary"
className={cn(
panel,
- "group min-w-0 max-sm:hidden scroll-mt-[var(--document-anchor-offset,6rem)] source-print md:col-span-2 lg:col-span-1",
+ "group min-w-0 max-sm:hidden print:block scroll-mt-[var(--document-anchor-offset,6rem)] source-print md:col-span-2 lg:col-span-1",
)}
>
Date: Tue, 18 Aug 2026 03:28:28 +0000
Subject: [PATCH 7/7] fix(test): update high-yield summary nav assertion for
mobile dedup
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The exclusive-accordion assertions after clicking the "High-yield
summary" section-nav row assumed the rail's
still opens on a 390px viewport. Since the mobile route-dedup change
hides that rail copy (max-sm:hidden) in favour of the always-visible
in-flow DocumentClinicalSummary card, jumpToDocumentSection's existing
"displayed copy" resolution (use-section-spy.ts's source-summary ->
source-summary-card alias) now scrolls to the card instead of toggling
the hidden accordion — so `summary.open` never becomes true at this
viewport. Assert the visible copy scrolls into view instead, matching
the pattern already used for the "Indexed source text" row above it.
---
tests/ui-smoke.spec.ts | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/tests/ui-smoke.spec.ts b/tests/ui-smoke.spec.ts
index 65682065d5..645269dd67 100644
--- a/tests/ui-smoke.spec.ts
+++ b/tests/ui-smoke.spec.ts
@@ -4886,7 +4886,12 @@ test.describe("Clinical KB UI smoke coverage", () => {
await expect(passages.nth(0)).toHaveJSProperty("open", false);
await clickSectionNav(/High-yield summary/);
- await expect(summary).toHaveJSProperty("open", true);
+ // At this 390px viewport the rail's high-yield-summary disclosure is
+ // hidden (superseded by the in-flow DocumentClinicalSummary card), so
+ // there is nothing for the exclusive accordion to open here —
+ // jumpToDocumentSection scrolls to the visible copy instead.
+ await expect(page.locator("#source-summary-card")).toBeInViewport();
+ await expect(summary).toHaveJSProperty("open", false);
await expect(indexedText).toHaveJSProperty("open", false);
await openImagesDisclosure();