fix(web): vendor sub-tab follow-through on cross-tab source navigation#673
fix(web): vendor sub-tab follow-through on cross-tab source navigation#673
Conversation
Two cross-tab navigation entry-points to the Sources panel set one axis (namespace filter or path) but did not align the Sources panel's vendor sub-tab axis (user / claude / openai). Because the panel only renders the active vendor's content (issue #570), the chip or "active source" visual state would suggest the link worked while the visible tree showed an unrelated vendor — the user reported it as "the link doesn't actually link to anything." navigateToSourcesByNs (Settings → Namespaces → "Sources" button): - _renderMemorySourceTree's filterActive guard now reads sourcesNsFilter in addition to the path-text input, so empty memory_dir headers are hidden when only a namespace filter is active. - After computing per-vendor totals, if the current vendor has zero matches and another vendor has any, switch the active sub-tab to the vendor with the most matches. Not persisted via _writeActiveSourcesVendor — this is a navigation hint, not the user's stored preference. _navigateToSource (Home recent source list, command palette): - Resolves the target vendor from the source's memory_dir → provider before activateTab('sources'), so a Claude or OpenAI source clicked from Home doesn't land on whichever vendor sub-tab the user last viewed and silently miss the .source-item lookup that follows. Bumps app.js?v=82 → v=84 to bust browser disk cache; static asset body changes that don't bump the query string get served stale (per feedback_static_asset_cache_bust.md, see #587). Co-Authored-By: Claude <[email protected]>
Reviewer flagged a one-tick stale on the "N files · M chunks" caption
above the Sources tree: ``renderSourceTree`` was reading
``STATE.sourcesActiveVendor`` and computing the totals before
``_renderMemorySourceTree`` had a chance to fire the NS-filter
follow-through. Result was visible — clicking a Claude namespace's
Sources button while sitting on the User sub-tab showed:
active vendor tab: claude ← post-switch
tree: claude ← post-switch
chip: claude ← present
stats line: user ← stale until the next render
Until the next ``renderSourceTree`` call (filter input, sort click,
reindex), the caption read the wrong vendor's totals. Reproduced via
Playwright before fix: ``statsText: "22 files · 318 chunks"`` (user
vendor) above 284 visible Claude items.
Extracted the stats compute into ``_renderSourcesStats(vendor)`` and
moved the call inside ``_renderMemorySourceTree`` right after the
NS-filter auto-switch resolves the active vendor. ``renderSourceTree``
now just delegates to ``_renderMemorySourceTree`` — the caption can no
longer race the sub-tab. Verified all three vendor branches: stats
follow the active vendor (Claude → 357/1084, User → 22/318, OpenAI →
hidden).
Also documented the tie-break policy on the auto-switch sort: stable
sort + ``PROVIDER_ORDER = ['user','claude','openai']`` means equal
totals favour ``user``. Reviewer noted the policy wasn't named — added
the rationale ("user is the conventional primary surface") so a future
reader doesn't second-guess it.
Cold-load silent-miss in ``_navigateToSource`` (deferred per review):
acceptable as-is for now — the existing inline comment names the
trade-off. A separate follow-up can wire a ``loadSources()`` retry if
it ever surfaces.
Co-Authored-By: Claude <[email protected]>
|
Review responses pushed in Concern 1 (stats badge one-tick stale) — confirmed visible, fixed. Fix: extracted the stats compute into
Concern 2 (tie-break sort stability rationale) — documented. Concern 3 (cold-load silent miss in
|
Summary
Two cross-tab navigation entry-points into the Sources panel set one axis (namespace filter or source path) but did not align the panel's vendor sub-tab axis (user / claude / openai). Because the panel only renders the active vendor's tree (#570), the user-visible chip / active-source state suggested the link succeeded while the rendered content was an unrelated vendor — reported as "Sources 화면으로 넘어가긴 하는데 직접 링크가 안 된다."
Audit (3 parallel Explore agents) found
_navigateToSourceis the same shape of bug; both fixes share the helper and live next to each other inapp.js, so they ship together as one focused change.Changes
navigateToSourcesByNs(Settings → Namespaces → Sources button)packages/memtomem/src/memtomem/web/static/app.js—_renderMemorySourceTree:filterActivenow also returns true whenSTATE.sourcesNsFilteris set, not just when the path-text input is non-empty. Empty memory_dir headers (28 of 29 in the repro for aclaude:…namespace) are now hidden under the existingvisibleCatsRawguard._writeActiveSourcesVendor— this is a navigation hint, not the user's stored preference, so the next manual click still wins._navigateToSource(Home recent source, command palette)Same root cause, narrower entry-point. Resolves the target vendor from the source's
memory_dir → provider(looked up viaSTATE.allSources+STATE.memoryStatusByPath) beforeactivateTab('sources'), so a Claude/OpenAI source clicked from Home doesn't land on whichever sub-tab the user last viewed and silently miss the.source-item[title=path]scroll-into-view that follows. Falls through to current behaviour whenSTATE.allSourcesisn't populated yet (cold load) — no regression.Cache bust
index.htmlapp.js?v=82 → v=84so the JS body change is actually picked up by browsers (perfeedback_static_asset_cache_bust.md, unbumped query strings get served stale from disk cache).Verification
uv run ruff check packages/memtomem/src— clean.uv run pytest -m "not ollama" -k "namespace or sources or web"— 986 passed.mm web):uservendor → clickclaude:-Users-pdstudio-Work-agent-harness-memtomemnamespace's Sources button → vendor tab shifts toclaude, exactly 1 dir group with 284 file items, chip shows the namespace.defaultnamespace fromuservendor → vendor staysuser, 2 dirs + 22 files (auto-switch correctly inert when current vendor has matches)._navigateToSource(<claude path>)fromuservendor → vendor switches toclaude, target source becomes.source-item.activeand getsscrollIntoView._navigateToSource(<user path>)fromuservendor → no vendor switch, target source becomes active (regression check).Out-of-scope sibling
_searchByTag(tag)(app.js:3631-3640) populates bothsearch-inputandtag-filterwith the tag string — same axis-mismatch shape butv0.1.0-since behaviour, not a recent regression. Filed as a separate issue (#672) for an intent-vs-design-flaw decision; user-visible behaviour change so it needs its own CHANGELOG entry.Test plan
ruff checkclean_navigateToSourcecross-vendor + same-vendor regression?v=84bust takes effect🤖 Generated with Claude Code