fix(web): survive Blazor hydration swap in reconnect auto-reload #97

Merged
jasoncouture merged 1 commit from fix-reconnect-auto-reload into main 2026-05-20 00:20:38 -04:00
jasoncouture commented 2026-05-20 00:20:30 -04:00 (Migrated from github.com)

Summary

Blazor replaces the SSR-rendered ReconnectModal subtree on hydration, so the previous MutationObserver (bound to the pre-hydration node) never saw the components-reconnect-failed / components-reconnect-rejected class land on the live element. Move the observer up to document.documentElement, filter to class mutations subtree-wide, and re-resolve #components-reconnect-modal on every fire.

Test plan

  • Kill the dev server while a chat tab is open; the modal shows "Could not reconnect — reloading…" and the page reloads automatically once the class flips to components-reconnect-rejected.
## Summary Blazor replaces the SSR-rendered `ReconnectModal` subtree on hydration, so the previous `MutationObserver` (bound to the pre-hydration node) never saw the `components-reconnect-failed` / `components-reconnect-rejected` class land on the live element. Move the observer up to `document.documentElement`, filter to `class` mutations subtree-wide, and re-resolve `#components-reconnect-modal` on every fire. ## Test plan - [ ] Kill the dev server while a chat tab is open; the modal shows "Could not reconnect — reloading…" and the page reloads automatically once the class flips to `components-reconnect-rejected`.
github-actions[bot] commented 2026-05-20 00:22:12 -04:00 (Migrated from github.com)
Package Line Rate Branch Rate Complexity Health
LlamaShears.Core.Eventing 92% 88% 58
LlamaShears.Core.Abstractions 51% 33% 464
LlamaShears.Api 30% 30% 475
LlamaShears.Provider.Ollama 44% 28% 188
LlamaShears.Core.Eventing.Extensions 100% 100% 1
LlamaShears.Core 45% 42% 1540
LlamaShears.Provider.Onnx.Embeddings 37% 38% 72
LlamaShears.Provider.OpenAI 66% 65% 229
LlamaShears.Api.Web 1% 1% 482
LlamaShears.Hosting 33% 21% 27
LlamaShears.Plugins 0% 100% 1
LlamaShears.Core.Eventing 90% 78% 58
LlamaShears 52% 36% 25
LlamaShears.Plugins.Host 34% 24% 36
LlamaShears.Core.Abstractions 44% 23% 464
LlamaShears.Api 6% 2% 475
LlamaShears.Provider.Ollama 3% 1% 188
LlamaShears.IntegrationTests.Fixtures 73% 57% 64
LlamaShears.Core.Eventing.Extensions 100% 100% 1
StrangeSoft.Plugins.Host 20% 21% 87
LlamaShears.Core 43% 28% 1540
LlamaShears.Provider.Onnx.Embeddings 3% 0% 72
LlamaShears.Provider.OpenAI 2% 0% 229
LlamaShears.Api.Web 20% 9% 482
LlamaShears.Hosting 26% 8% 27
LlamaShears.Plugins 0% 100% 1
LlamaShears.Core.Eventing 87% 74% 58
LlamaShears 52% 36% 25
LlamaShears.Plugins.Host 34% 24% 36
LlamaShears.Core.Abstractions 34% 21% 464
LlamaShears.Api 7% 3% 475
LlamaShears.Provider.Ollama 3% 1% 188
LlamaShears.IntegrationTests.Fixtures 70% 52% 64
LlamaShears.Core.Eventing.Extensions 100% 100% 1
StrangeSoft.Plugins.Host 20% 21% 87
LlamaShears.Core 31% 19% 1540
LlamaShears.Provider.Onnx.Embeddings 3% 0% 72
LlamaShears.Provider.OpenAI 2% 0% 229
LlamaShears.Api.Web 17% 7% 482
LlamaShears.Hosting 26% 8% 27
LlamaShears.Analyzers.CodeFixes 85% 69% 60
LlamaShears.Analyzers 88% 76% 199
Summary 45% (11426 / 34836) 36% (2472 / 10361) 11293
Package | Line Rate | Branch Rate | Complexity | Health -------- | --------- | ----------- | ---------- | ------ LlamaShears.Core.Eventing | 92% | 88% | 58 | ✔ LlamaShears.Core.Abstractions | 51% | 33% | 464 | ➖ LlamaShears.Api | 30% | 30% | 475 | ❌ LlamaShears.Provider.Ollama | 44% | 28% | 188 | ❌ LlamaShears.Core.Eventing.Extensions | 100% | 100% | 1 | ✔ LlamaShears.Core | 45% | 42% | 1540 | ❌ LlamaShears.Provider.Onnx.Embeddings | 37% | 38% | 72 | ❌ LlamaShears.Provider.OpenAI | 66% | 65% | 229 | ➖ LlamaShears.Api.Web | 1% | 1% | 482 | ❌ LlamaShears.Hosting | 33% | 21% | 27 | ❌ LlamaShears.Plugins | 0% | 100% | 1 | ❌ LlamaShears.Core.Eventing | 90% | 78% | 58 | ✔ LlamaShears | 52% | 36% | 25 | ➖ LlamaShears.Plugins.Host | 34% | 24% | 36 | ❌ LlamaShears.Core.Abstractions | 44% | 23% | 464 | ❌ LlamaShears.Api | 6% | 2% | 475 | ❌ LlamaShears.Provider.Ollama | 3% | 1% | 188 | ❌ LlamaShears.IntegrationTests.Fixtures | 73% | 57% | 64 | ➖ LlamaShears.Core.Eventing.Extensions | 100% | 100% | 1 | ✔ StrangeSoft.Plugins.Host | 20% | 21% | 87 | ❌ LlamaShears.Core | 43% | 28% | 1540 | ❌ LlamaShears.Provider.Onnx.Embeddings | 3% | 0% | 72 | ❌ LlamaShears.Provider.OpenAI | 2% | 0% | 229 | ❌ LlamaShears.Api.Web | 20% | 9% | 482 | ❌ LlamaShears.Hosting | 26% | 8% | 27 | ❌ LlamaShears.Plugins | 0% | 100% | 1 | ❌ LlamaShears.Core.Eventing | 87% | 74% | 58 | ✔ LlamaShears | 52% | 36% | 25 | ➖ LlamaShears.Plugins.Host | 34% | 24% | 36 | ❌ LlamaShears.Core.Abstractions | 34% | 21% | 464 | ❌ LlamaShears.Api | 7% | 3% | 475 | ❌ LlamaShears.Provider.Ollama | 3% | 1% | 188 | ❌ LlamaShears.IntegrationTests.Fixtures | 70% | 52% | 64 | ➖ LlamaShears.Core.Eventing.Extensions | 100% | 100% | 1 | ✔ StrangeSoft.Plugins.Host | 20% | 21% | 87 | ❌ LlamaShears.Core | 31% | 19% | 1540 | ❌ LlamaShears.Provider.Onnx.Embeddings | 3% | 0% | 72 | ❌ LlamaShears.Provider.OpenAI | 2% | 0% | 229 | ❌ LlamaShears.Api.Web | 17% | 7% | 482 | ❌ LlamaShears.Hosting | 26% | 8% | 27 | ❌ LlamaShears.Analyzers.CodeFixes | 85% | 69% | 60 | ✔ LlamaShears.Analyzers | 88% | 76% | 199 | ✔ **Summary** | **45%** (11426 / 34836) | **36%** (2472 / 10361) | **11293** | ❌ <!-- Sticky Pull Request Commentcoverage -->
copilot-pull-request-reviewer[bot] (Migrated from github.com) reviewed 2026-05-20 00:23:28 -04:00
copilot-pull-request-reviewer[bot] (Migrated from github.com) left a comment

Pull request overview

This PR updates the Blazor reconnect auto-reload script to keep working after hydration, where Blazor can replace the SSR-rendered reconnect modal subtree (breaking observers bound to the original element).

Changes:

  • Move reconnect-modal resolution into a check() function so the live #components-reconnect-modal element is re-found each time.
  • Re-scope the MutationObserver to document.documentElement with subtree-wide class attribute observation to catch class flips on the hydrated element.
  • Add inline documentation describing the hydration swap behavior and the rationale for the broader observation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

## Pull request overview This PR updates the Blazor reconnect auto-reload script to keep working after hydration, where Blazor can replace the SSR-rendered reconnect modal subtree (breaking observers bound to the original element). **Changes:** - Move reconnect-modal resolution into a `check()` function so the live `#components-reconnect-modal` element is re-found each time. - Re-scope the `MutationObserver` to `document.documentElement` with subtree-wide `class` attribute observation to catch class flips on the hydrated element. - Add inline documentation describing the hydration swap behavior and the rationale for the broader observation. --- 💡 <a href="/jasoncouture/llama-shears/new/main?filename=.github/instructions/*.instructions.md" class="Link--inTextBlock" target="_blank" rel="noopener noreferrer">Add Copilot custom instructions</a> for smarter, more guided reviews. <a href="https://docs.github.com/en/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot" class="Link--inTextBlock" target="_blank" rel="noopener noreferrer">Learn how to get started</a>.
@ -19,1 +26,4 @@
attributeFilter: ['class'],
});
check();
})();
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-05-20 00:23:27 -04:00

The observer only listens for attributes mutations. If Blazor swaps in a new #components-reconnect-modal element that already has the terminal class set at insertion time (no subsequent class attribute change), check() will never run again and the auto-reload won’t trigger. Consider also observing childList (or otherwise triggering check() on subtree replacements) so a replaced modal gets evaluated even when its class doesn’t mutate after insertion.

The observer only listens for `attributes` mutations. If Blazor swaps in a new `#components-reconnect-modal` element that already has the terminal class set at insertion time (no subsequent `class` attribute change), `check()` will never run again and the auto-reload won’t trigger. Consider also observing `childList` (or otherwise triggering `check()` on subtree replacements) so a replaced modal gets evaluated even when its class doesn’t mutate after insertion.
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-05-20 00:23:27 -04:00

Watching document.documentElement for class attribute changes subtree-wide will call check() on every class update anywhere in the app, which can be very frequent in Blazor-rendered UIs. To reduce overhead, consider narrowing the observation (e.g., keep a lightweight childList observer to detect when the reconnect modal is swapped, and attach an attributeFilter:['class'] observer only to the current modal element; or filter the mutation list to cases that involve #components-reconnect-modal).

Watching `document.documentElement` for `class` attribute changes subtree-wide will call `check()` on every class update anywhere in the app, which can be very frequent in Blazor-rendered UIs. To reduce overhead, consider narrowing the observation (e.g., keep a lightweight `childList` observer to detect when the reconnect modal is swapped, and attach an `attributeFilter:['class']` observer only to the current modal element; or filter the mutation list to cases that involve `#components-reconnect-modal`).
Sign in to join this conversation.
No description provided.