fix: centralize workspace tree toggle width#2580
Merged
1 commit merged intoMay 19, 2026
Merged
Conversation
0310fcc
LumenYoung
pushed a commit
to LumenYoung/hermes-webui
that referenced
this pull request
May 19, 2026
eleboucher
pushed a commit
to eleboucher/homelab
that referenced
this pull request
May 20, 2026
… 0.51.95) (#569) This PR contains the following updates: | Package | Update | Change | |---|---|---| | [ghcr.io/nesquena/hermes-webui](https://github.com/nesquena/hermes-webui) | patch | `0.51.92` → `0.51.95` | --- ### Release Notes <details> <summary>nesquena/hermes-webui (ghcr.io/nesquena/hermes-webui)</summary> ### [`v0.51.95`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v05195--2026-05-20--Release-BS-stage-388--5-PR-batch--live-tool-callback-event-dedup--browser-only-dashboard-links--messaging-transcript-merge-alignment--Geist-Contrast-skin--SSE-runtime-diagnostics) [Compare Source](nesquena/hermes-webui@v0.51.94...v0.51.95) ##### Fixed - **PR [#​2598](nesquena/hermes-webui#2598 by [@​AJV20](https://github.com/AJV20) — Surface live tool activity when Hermes Agent reports tools through its dedicated `tool_start_callback` / `tool_complete_callback` path, so browser chat shows the existing running tool cards instead of appearing idle until the final answer. The legacy `on_tool` callback path now early-returns for `tool.started` and `tool.completed` events when the structured callback path is already wired, preventing the same tool event from being emitted twice to the SSE stream. - **PR [#​2533](nesquena/hermes-webui#2533 by [@​AJV20](https://github.com/AJV20) — Allow Settings → System to save public browser-only Official Hermes Dashboard links (for reverse-proxy URLs) without treating them as server-side probe targets. URL sanitization runs against the configured link before save; the dashboard probe is skipped for browser-only links. - **PR [#​2607](nesquena/hermes-webui#2607 by [@​AJV20](https://github.com/AJV20) — Deduplicate messaging/CLI session transcript rows when the sidecar and state store encode the same no-id message with equivalent timestamps in different formats (e.g. `"10.0"` vs `10`), preventing repeated visible chat rows after session reconstruction. The messaging-display merge now reuses `api.models._session_message_merge_key(...)` instead of an ad-hoc dedup key, aligning with the existing append-only merge path. ##### Added - **PR [#​2521](nesquena/hermes-webui#2521 by [@​intellectronica](https://github.com/intellectronica) — Add the Geist Contrast skin to the appearance picker. New light + dark variant pair with a high-contrast yellow-on-black accent and Geist editorial typography. Default unchanged — opt-in via Settings → Appearance → Skin → Geist Contrast. Slash command `/theme geist-contrast` now resolves correctly because the lookup matches against `skin.value` rather than `skin.name`. Documented in `THEMES.md` with a forward-compatible skin count (no hard-coded value). - **PR [#​2524](nesquena/hermes-webui#2524 by [@​AJV20](https://github.com/AJV20) — Add non-sensitive SSE stream runtime diagnostics to deep health checks (`/health?deep=1`), including active stream count, subscriber totals, and offline buffered-event counts for stuck or slow WebUI chat investigations. Read-only telemetry; existing surfaces unchanged. ### [`v0.51.94`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v05194--2026-05-19--Release-BR-stage-387--10-PR-full-sweep-batch--Slice-4b-runner-adapter-facade--folder-zip-download--partial-recovery-marker-dedupe--browser-api-client-side-timeout--auto-compression-card-rotation-finish--composer-draft-rollback-fix--metadata-count-reconciliation--active-session-refresh-on-external-sidecar-updates--indexed-context-metadata--gateway-queues-approval-peek) [Compare Source](nesquena/hermes-webui@v0.51.93...v0.51.94) ##### Fixed - **PR [#​2566](nesquena/hermes-webui#2566 by [@​bjb2](https://github.com/bjb2) — Add `GET /api/folder/download?session_id=...&path=...` streaming-zip endpoint with pre-flight 413 on size/file-count cap exceeded, `os.walk(followlinks=False)` plus per-symlink workspace-root resolution check, `allowZip64=True` for large files, and a "Download Folder" item in the workspace file context menu (dir items only). Configurable caps via `HERMES_WEBUI_FOLDER_ZIP_MAX_MB` (1024 default) and `HERMES_WEBUI_FOLDER_ZIP_MAX_FILES` (50000 default). `download_folder` i18n key added across all 11 locales with `// TODO: translate` fallback markers for non-en entries. - **PR [#​2593](nesquena/hermes-webui#2593 by [@​Michaelyklam](https://github.com/Michaelyklam) (closes [#​2592](nesquena/hermes-webui#2592)) — Deduplicate cancelled/recovered partial assistant markers using the full `(content, reasoning, partial tool calls)` payload instead of only non-empty text content. Tool-only failed turns no longer append identical empty-content `_partial` messages repeatedly. Full session loads collapse adjacent duplicate partial markers from already-bloated session files while preserving a `.partial-bak-<timestamp>` backup. New helpers `_partial_message_signature()` (api/streaming.py:2593-2622) + `_partial_marker_already_present()` (api/streaming.py:2625-2641) scope the dedup search to the current user turn only. - **PR [#​2597](nesquena/hermes-webui#2597 by [@​dso2ng](https://github.com/dso2ng) (closes [#​2539](nesquena/hermes-webui#2539)) — Add a 30s default client-side timeout to the shared browser `api()` helper, with per-call `timeoutMs` overrides, `AbortController`-based cancellation, a timeout toast, and explicit 60s/120s ceilings for legitimately longer update flows. Body-read phase also raced against the timeout so a server that replies headers-OK and then stalls mid-JSON rejects cleanly. New `tests/test_api_timeout.py` covers default, override, abort, and body-read-stall paths. - **PR [#​2601](nesquena/hermes-webui#2601 by [@​starship-s](https://github.com/starship-s) — Prevent the composer-draft rollback regression introduced by [#​2581](nesquena/hermes-webui#2581 active-session external-refresh polling. Adds `opts.preserveActiveInput` to `_restoreComposerDraft` and gates the overwrite on `current && current !== text`, keeping the guard co-located with the function that owns the contract. Backend `s.save(touch_updated_at=False)` for `/api/session/draft` so draft autosaves no longer falsely advance `updated_at` and trigger the refresh poll. Supersedes parallel-discovery PR [#​2602](nesquena/hermes-webui#2602). - **PR [#​2603](nesquena/hermes-webui#2603 by [@​starship-s](https://github.com/starship-s) — Finish the running auto-compression card after the backend rotates the session id. The `compressed` SSE listener at `static/messages.js:1829-1862` used to early-return whenever `S.session.session_id !== activeSid`, but the `state` event listener at `:1656-1662` already rotates `window._compressionUi.sessionId` to the continuation id before `compressed` arrives. The strict active-session check is replaced with a cross-session safety check that still rejects mismatched events but no longer rejects the legitimate post-rotation `done` payload, so the elapsed-timer "compressing…" state no longer freezes after rotation completes. - **PR [#​2604](nesquena/hermes-webui#2604 by [@​Michaelyklam](https://github.com/Michaelyklam) (closes [#​2594](nesquena/hermes-webui#2594)) — Reconcile session metadata counts in the `/api/session?messages=0` fast path. Replaces the prior `max(sidecar_count, state_count)` heuristic with `len(merge_session_messages_append_only(sidecar_messages, state_db_messages))` so the metadata-only count matches the full-load count. Closes the followup issue filed against PR [#​2581](nesquena/hermes-webui#2581) / v0.51.93 — sidebar refresh polling no longer loops forever when `state.db` retains old rows that the append-only merge correctly filters out. - **PR [#​2605](nesquena/hermes-webui#2605 by [@​LumenYoung](https://github.com/LumenYoung) (refs [#​2581](nesquena/hermes-webui#2581)) — Make the metadata-only `/api/session?messages=0&resolve_model=0` path return the persisted sidecar `message_count` from `Session._metadata_message_count` when no session-index entry exists, so the active-session external-refresh signal still trips on legacy sessions whose sidecar contains externally-appended content. Composed cleanly with [#​2604](nesquena/hermes-webui#2604) (the legacy-fallback applies only when the reconciled merged count is zero). - **PR [#​2573](nesquena/hermes-webui#2573 by [@​espokaos-ops](https://github.com/espokaos-ops) (closes [#​2510](nesquena/hermes-webui#2510)) — Persist session-level approvals when a "Allow for this session" click lands while a stream is active and `_pending` is empty. The approval flow now peeks `_gateway_queues[sid]` to recover the queued `_ApprovalEntry`'s `pattern_keys` so `approve_session()` records the approval; the next dangerous command in the same session no longer asks again. Reduced scope to peek-only per prior review note; the `agent_session_key` round-trip plumbing was dropped (it was dead on the WebUI streaming path). ##### Added - **PR [#​2599](nesquena/hermes-webui#2599 by [@​Michaelyklam](https://github.com/Michaelyklam) (refs [#​1925](nesquena/hermes-webui#1925)) — Add the Slice 4b `RunnerRuntimeAdapter` facade — a protocol-translator client over a future runner/sidecar backend. The facade delegates `start_run`, `observe_run`, `get_run`, and control calls to an injected runner client, normalizes results into the existing `RunStartResult`/`RunEventStream`/`RunStatus`/`ControlResult` dataclasses, carries explicit `profile`/`workspace`/`model` payload fields, and returns bounded `unsupported` control results without owning `AIAgent`, stream lifecycle, cancel/approval/clarify queues, goal state, or cached-agent table. No route wiring, no default-on runner mode, no public response-shape change. - **PR [#​2600](nesquena/hermes-webui#2600 by [@​LumenYoung](https://github.com/LumenYoung) (refs [#​2266](nesquena/hermes-webui#2266)) — Slimmer WebUI follow-up from the closed LCM/context-engine PR [#​2266](nesquena/hermes-webui#2266). Adds rendering and persistence for context-engine compression-anchor metadata (when present on a session or live compression event) including an "Indexed context" detail line on auto-compression cards. No agent-layer clone orchestration; WebUI-only metadata surface. ### [`v0.51.93`](https://github.com/nesquena/hermes-webui/blob/HEAD/CHANGELOG.md#v05193--2026-05-19--Release-BQ-stage-386--10-PR-full-sweep-batch--RFC-Slice-4-runnersidecar-gate--workspace-tree-toggle-width-CSS-variable--settled-file-markdown-link-rendering--prompt-cache-coverage-percentage-fix--terminal-shell-shutdown-reap--configured-model-picker-provider-preservation--profile-aware-assistant-display-names--statedb-reconciliation-slice-1--queued-message-cross-session-drain-fix--stale-stream-writeback-supersede) [Compare Source](nesquena/hermes-webui@v0.51.92...v0.51.93) ##### Fixed - **PR [#​2580](nesquena/hermes-webui#2580 by [@​Michaelyklam](https://github.com/Michaelyklam) (refs [#​2571](nesquena/hermes-webui#2571)) — Centralize the workspace-tree toggle slot width into a `--file-tree-toggle-width` CSS variable at `:root`, referenced from both `.file-tree-toggle` and `.file-tree-toggle-placeholder` so a future width adjustment can't silently desync the two rules. Closes the followup issue filed against PR [#​2563](nesquena/hermes-webui#2563) / v0.51.92. - **PR [#​2576](nesquena/hermes-webui#2576 by [@​dobby-d-elf](https://github.com/dobby-d-elf) (closes [#​470](nesquena/hermes-webui#470)) — Preserve labeled `file://` links in settled markdown by rewriting them to `/api/media?path=...&inline=1` before the sanitizer drops them. The streamed and settled markdown paths are now symmetric on local-file anchors, while raw `file://` image sources continue to be blocked. - **PR [#​2579](nesquena/hermes-webui#2579 by [@​starship-s](https://github.com/starship-s) (refs [#​2419](nesquena/hermes-webui#2419), [#​2421](nesquena/hermes-webui#2421)) — Fix the prompt-cache hit percentage to display the fraction of the prompt served from cache (`cache_read / prompt_total`) instead of the meaningless `cache_read / (cache_read + cache_write)`. New `api/usage.py` `prompt_cache_hit_percent()` helper matches Hermes Agent's log convention; UI labels updated across all locales. - **PR [#​2582](nesquena/hermes-webui#2582 by [@​Michaelyklam](https://github.com/Michaelyklam) (refs [#​2577](nesquena/hermes-webui#2577)) — Harden embedded workspace-terminal shell cleanup so graceful WebUI shutdowns close/reap every active PTY shell and the spawned shell receives a Linux parent-death signal (`PR_SET_PDEATHSIG`) if the WebUI process dies. The terminal close path now waits again after `SIGKILL` so timed-out shells don't remain unreaped. - **PR [#​2583](nesquena/hermes-webui#2583 by [@​dobby-d-elf](https://github.com/dobby-d-elf) — Make assistant display names properly profile-aware. The saved assistant-name preference applies only to the literal `default` profile; named profiles use their own profile name. Centralizes `assistantDisplayName()` resolution across composer placeholder, `document.title` via `syncTopbar()`, message role labels via `_assistantRoleHtml()`, browser notifications, cancel-copy fallback, and empty-state on session delete. - **PR [#​2584](nesquena/hermes-webui#2584 by [@​wirtsi](https://github.com/wirtsi) (closes [#​2585](nesquena/hermes-webui#2585)) — Prevent queued follow-up messages from draining into the wrong chat when the user switches sessions during the 120ms `setBusy(false)` drain window. The drain-time guard re-queues against `sid` (not the currently-viewed session) and `_sendInProgressSid` captures the activeSid at the commit point so the re-entrant `send()` path no longer reads a stale `S.session.session_id`. - **PR [#​2587](nesquena/hermes-webui#2587 by [@​AJV20](https://github.com/AJV20) — Allow a still-running stream that was mistakenly marked interrupted by stale-pending recovery to replace its own recovery marker when it later finishes, while continuing to block stale writeback after any newer turn appends transcript content. Three new tests in `tests/test_session_sidecar_repair.py` cover the supersede-allowed and the two refuse cases. - **PR [#​2588](nesquena/hermes-webui#2588 by [@​Michaelyklam](https://github.com/Michaelyklam) (refs [#​2569](nesquena/hermes-webui#2569)) — Preserve the configured provider when choosing a configured model from the composer picker. `_getOptionProviderId()` now reads `data-provider` from temporary `<option data-custom="1">` rows (created by `selectModelFromDropdown` for configured models outside the native catalog), so the next send routes through the correct provider instead of falling back to whatever provider was already active. ##### Changed - **PR [#​2581](nesquena/hermes-webui#2581 by [@​LumenYoung](https://github.com/LumenYoung) (refs [#​2194](nesquena/hermes-webui#2194)) — First recovery slice from the closed reconciliation PR [#​2194](nesquena/hermes-webui#2194). Routes streaming session reconstruction and sidebar metadata through the reconciled state.db/session-summary path with a metadata-only fast path for sidebar polls and a single-snapshot reuse on the streaming hot path. Includes the reviewer-requested `_new_turn_context_from_messages` extraction so both legacy and streaming paths share the `_drop_checkpointed_current_user_from_context` + casual-fresh-chat suppression behavior (refs [#​1217](nesquena/hermes-webui#1217) / [#​2308](nesquena/hermes-webui#2308)). 923 LOC across `api/models.py`, `api/routes.py`, `api/streaming.py`, `static/sessions.js` + four new test files; second-pass agent diff review LGTM after the streaming-path regression was caught and fixed. ##### Documentation - **PR [#​2575](nesquena/hermes-webui#2575 by [@​Michaelyklam](https://github.com/Michaelyklam) (refs [#​1925](nesquena/hermes-webui#1925)) — Advance the runtime-adapter RFC to the Slice 4 runner/sidecar planning gate after [#​2560](nesquena/hermes-webui#2560) shipped the queue-staging clarification. The RFC now marks queue routing as staged by default, defines Slice 4a as a docs/test contract before any runner code lands, and pins default-off feature-flagging, restart/reattach success criteria, control parity, profile/workspace payload isolation, and explicit non-goals for legacy-backend removal or server-side queue scheduler work. </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/569
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Thinking Path
10pxvalues.What Changed
--file-tree-toggle-widthto the root CSS tokens..file-tree-toggleand.file-tree-toggle-placeholderto use that shared width token.Why It Matters
This prevents a future workspace-tree tweak from changing the directory toggle width without also changing the file-row placeholder width, which would reintroduce the file-indent mismatch fixed in #2563.
Verification
/home/michael/.hermes/hermes-agent/venv/bin/python -m pytest tests/test_issue2554_workspace_tree_file_indent.py -q— 2 passedgit diff --check— passedNo screenshots are attached because this is a CSS-token refactor that preserves the same computed 10px width and should not create a visual before/after delta.
Risks / Follow-ups
Closes #2571
Model Used
AI-assisted change with repository inspection, targeted editing, and shell-based test verification.