fix: render streamed math incrementally - #2710
Michaelyklam wants to merge 2 commits into
Conversation
SummaryReading the diff at But I don't think this PR actually renders streamed math, because the live and settled paths emit different DOM nodes. Code referenceThe new live scan calls into function renderKatexBlocks(container){
const root=container||document;
const blocks=root.querySelectorAll('.katex-block:not([data-rendered]),.katex-inline:not([data-rendered])');
if(!blocks.length) return;
...
}That selector only matches During the live stream, however, So DiagnosisThe 150ms throttle, the To genuinely fix #2699 you need one of:
Option 2 is cleaner because it makes the live DOM match the settled DOM, avoiding a follow-up swap when Test planA test that confirms wiring (the symbols are present) is what's there today. It would catch a refactor, but it doesn't catch this class of bug. Worth adding either:
Happy to be wrong on this — if there's an existing transform from |
|
Addressed the live-DOM mismatch in follow-up commit What changed:
Verification:
GitHub Actions are queued on the new head now. |
… (no flash when delta completes a KaTeX expression) Co-authored-by: Michaelyklam <Michaelyklam@users.noreply.github.com>
…➔ 0.51.107) (#621) This PR contains the following updates: | Package | Update | Change | |---|---|---| | [ghcr.io/nesquena/hermes-webui](https://github.com/nesquena/hermes-webui) | patch | `0.51.106` → `0.51.107` | --- ### Release Notes <details> <summary>nesquena/hermes-webui (ghcr.io/nesquena/hermes-webui)</summary> ### [`v0.51.107`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v051107--2026-05-21--Release-CE-stage-400--8-PR-batch--pinned-sessions-limit-getter-rename--uploaded-file-user-turn-dedupe--active-run-repair-guard--incremental-KaTeX-streaming--profile-default-model-on-fresh-boot--French-locale-completion--update-check-error-surfacing--release-update-apply-path) [Compare Source](nesquena/hermes-webui@v0.51.106...v0.51.107) ##### Fixed - **PR [#​2718](nesquena/hermes-webui#2718 by [@​eslicarrillo](https://github.com/eslicarrillo) — Follow-up to v0.51.105's [#​2700](nesquena/hermes-webui#2700): rename `_pinnedSessionsLimit()` to `_getPinnedSessionsLimit()` so the helper matches the rest of `sessions.js`'s `_get*()` naming convention for accessors. No behavior change. - **PR [#​2723](nesquena/hermes-webui#2723 by [@​ai-ag2026](https://github.com/ai-ag2026) — Deduplicate uploaded-file user turns when the optimistic browser bubble uses plain text but the server-persisted pending turn includes the `[Attached files: ...]` suffix. Previously a turn with attachments could render twice (optimistic + persisted) in the visible transcript before reconciling. - **PR [#​2721](nesquena/hermes-webui#2721 by [@​ai-ag2026](https://github.com/ai-ag2026) — During session repair on restart, treat sessions with a live in-flight run as active. The prior code path could prune restart-stale state for sessions that were actually mid-stream when the server bounced, dropping the resumable run. Now the active-stream check gates the prune so live runs survive a restart cleanly. - **PR [#​2710](nesquena/hermes-webui#2710 by [@​Michaelyklam](https://github.com/Michaelyklam) — Render streamed math (KaTeX) incrementally during a stream so completed expressions render in place as their closing delimiter arrives, instead of all-at-once at the end of the turn. Eliminates the visual flash where prose-with-math sat as raw `\\[...\\]` markup until the final render pass. - **PR [#​2709](nesquena/hermes-webui#2709 by [@​starship-s](https://github.com/starship-s) — On a fresh boot with no persisted model in localStorage, prefer the active profile's configured default model over the static HTML option. Previously a clean install / first-load could surface the placeholder model until the user manually picked a different one, even when the profile had a default configured. Behavior note: the boot path now calls `_clearPersistedModelState()` on each load when no profile default is found, so a previously-persisted user pick is wiped on refresh — this is intentional (matches the PR's "profile default wins on fresh boot" intent and is ratified by `tests/test_model_default_boot_precedence.py`). - **PR [#​2722](nesquena/hermes-webui#2722 by [@​victorwhale](https://github.com/victorwhale) — Complete French (`fr`) locale coverage: +93 missing translation keys covering Settings, profile-ops, gateway tile, skills modal, session controls, and i18n test surfaces. Coverage 88.8% → 96.7%. - **PR [#​2717](nesquena/hermes-webui#2717 by [@​ai-ag2026](https://github.com/ai-ag2026) — Surface update-check fetch errors in the UI instead of failing silently. The background `api/updates/check` request previously swallowed network failures, so an offline / blocked-CDN scenario showed no indication that the version banner couldn't render. Now the failure is logged and exposed to the System panel's update-status card. - **PR [#​2719](nesquena/hermes-webui#2719 by [@​ai-ag2026](https://github.com/ai-ag2026) — Apply release-update target correctly when the user clicks "Check for updates" after a prior dismissal: clears the `sessionStorage` check-once stamp and forces banner re-evaluation. The prior path silently no-op'd because the once-per-tab guard fired before the explicit user click could re-trigger the fetch. </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about these updates again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMDEuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEwMS4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL3BhdGNoIl19--> Reviewed-on: https://git.erwanleboucher.dev/eleboucher/homelab/pulls/621
… (no flash when delta completes a KaTeX expression) Co-authored-by: Michaelyklam <Michaelyklam@users.noreply.github.com>
… (no flash when delta completes a KaTeX expression) Co-authored-by: Michaelyklam <Michaelyklam@users.noreply.github.com>
Thinking Path
streaming-markdownto create KaTeX placeholder nodes while tokens arrive.doneevent or on settled transcript rebuilds.What Changed
_scheduleStreamingKatex()call after each successful livesmddelta write.renderKatexBlocks(assistantBody)so repeated scans only inspect the current streamed segment.smdparser ends so the finaldonepath cannot leave a stale scheduled render.Why It Matters
Math placeholders now get a chance to render during the live SSE turn instead of waiting for the final response settlement. This keeps math-heavy answers readable while they stream without swapping in MathJax or rewriting the markdown pipeline.
Verification
/home/michael/.hermes/hermes-agent/venv/bin/python -m pytest tests/test_streaming_katex_live_render.py tests/test_streaming_markdown.py tests/test_subpath_frontend_routes.py -q→63 passednode --check static/messages.jsnode --check static/ui.jsgit diff --checkUI media: not attached. This is a live-stream timing/rendering-path change; a static before/after screenshot would be misleading because the visual difference is whether KaTeX appears during token streaming rather than after the terminal
doneevent.Risks / Follow-ups
renderKatexBlocks()still ignores already-rendered nodes.streaming-markdownever emits a closed malformed math placeholder early, the existing KaTeX fallback behavior still applies; this PR does not change the renderer's error contract.Refs #2699
Model Used
AI-assisted change with repository inspection, targeted editing, and shell-based test verification.