Fix Dashboard showing empty when plugin registry API times out
Details
The Dashboard RefreshAsync method called AllPlugins.Clear() before
fetching the remote plugin registry, so when the API call timed out
the entire catch block skipped populating locally installed plugins,
system metrics, and workspace activation state — leaving the UI stuck
showing "0 installed · 0 available" with empty metric cards.
Restructured RefreshAsync to:
- Fetch local installed versions first (never fails)
- Wrap the remote registry fetch in its own try/catch so timeouts
are non-fatal — locally installed plugins still appear
- Always load system metrics regardless of registry availability
Also increased PrivStackApiClient timeout from 15s to 30s since the
plugin registry endpoint can be slow.
Remove N+1 individual entity reads from BacklinkService index build
Details
Phase 2 of BuildIndexAsync already calls WikiLinkParser.ExtractContentFromEntity
on each entity from ReadList. The "load individually" block re-fetched entities
without content via individual SdkAction.Read calls, but the individual Read
returns the same JSON shape — ExtractContentFromEntity produces the same empty
result. This caused 21+ unnecessary contact.Read SDK calls at startup.
Removing the block eliminates the N+1 query pattern with no behavioral change,
since entities that genuinely lack parseable text fields won't gain content from
a re-fetch.
Fix RSS feed refresh not detecting new articles
Details
Root cause: deduplication used URL-only comparison, but feeds where
all entries share the same link (e.g., changelog feeds pointing to
/changelog) would reject every article after the first as a "duplicate".
Fix: add EntryId field to RssArticle populated from the Atom <id> or
RSS <guid> element. Dedup now checks entry ID first (globally unique
per spec), falling back to URL comparison for feeds without entry IDs.
Pre-build backlink index at startup
Details
Adds BacklinkService.PreBuildIndexAsync() and calls it during the
deferred background services block in App.axaml.cs. This eliminates
the ~700ms cold-start delay when the user first clicks an item with
the Info Panel open, since the cross-plugin backlink index is already
warm by the time they interact.
The existing SemaphoreSlim guard in EnsureIndexBuiltAsync ensures
concurrent calls (from a user click racing the startup pre-build)
are safe. If the pre-build fails, the index falls back to lazy
initialization on first query (existing behavior).
Fix feed selector dropdown showing empty
Details
MenuFlyout.Opening event doesn't fire when wired via XAML attribute
because MenuFlyout is not a Control in the visual tree — it's an
AvaloniaObject attached via Button.Flyout. Wire the event in the
constructor instead, where we can directly access the flyout instance.
Get notified about new releases