Release v0.51.252 — Release HT (stage-q24) (#3530)
Some checks failed
Release & Docker / release (push) Has been cancelled
Some checks failed
Release & Docker / release (push) Has been cancelled
## Release v0.51.252 — Release HT (stage-q24) Two trivially-safe @rodboev changes (independent). ### Fixed | Issue | Author | Fix | |-------|--------|-----| | #2481 | @rodboev | The floating "selected-text reply" button now has `user-select:none`, so its own label can't get caught in a text selection (no bleed-through). CSS one-liner. | ### Docs - README **Compatibility** section: upgrade WebUI + hermes-agent together until the stable agent API (#2491) lands. (@rodboev) ### Dropped from this batch - **#2977 `/use` skill command** was staged here but **dropped** — the Codex regression gate found an async stale-directive race (`cmdUse()` awaits `/api/skills` but `send()` doesn't await the handler → a fast next send can miss it, or a stale directive leaks to a later message) plus an over-eager `finally` clear that silently discards the directive on a local slash-command early-return. Held with `changes-requested` + a detailed rework note (tracked pending promise + clear-on-consume). Concept approved; needs lifecycle hardening. ### Gate - Full pytest suite: **7570 passed, 0 failed** - ESLint: CLEAN · ruff: CLEAN · browser-smoke: CLEAN - Codex (regression): **SAFE TO SHIP** — `user-select:none` scoped to the button only, README docs-only, no `/use` code remains Co-authored-by: rodboev <rodboev@users.noreply.github.com>
This commit is contained in:
@@ -3,6 +3,14 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v0.51.252] — 2026-06-03 — Release HT (stage-q24 — selection-bleed fix + compatibility docs)
|
||||
|
||||
### Fixed
|
||||
- The floating "selected-text reply" button no longer lets its own label get caught in a text selection (`user-select:none`), so dragging a selection near the button doesn't bleed into it. (#2481, @rodboev)
|
||||
|
||||
### Docs
|
||||
- README now has a **Compatibility** section documenting that the WebUI is tested against the matching hermes-agent release and that both should be upgraded together (until the stable agent API #2491 lands). (@rodboev)
|
||||
|
||||
## [v0.51.251] — 2026-06-03 — Release HS (stage-q23 — composer ~/ path autocomplete)
|
||||
|
||||
### Fixed
|
||||
|
||||
10
README.md
10
README.md
@@ -535,6 +535,16 @@ Full design notes and the endpoint catalog are in [`ARCHITECTURE.md`](ARCHITECTU
|
||||
|
||||
---
|
||||
|
||||
## Compatibility
|
||||
|
||||
The WebUI is tested against the current hermes-agent release at the time of each WebUI release. Until [#2491](https://github.com/nesquena/hermes-webui/issues/2491) lands a stable agent API, the WebUI imports agent Python modules directly (`api/config.py`, `api/providers.py`, `api/streaming.py`), so version skew can cause silent import errors or incorrect behaviour.
|
||||
|
||||
**Upgrade both together.** When you update WebUI, update hermes-agent to the same release date. Running a pinned older agent against a newer WebUI (or vice versa) is untested and unsupported until #2491.
|
||||
|
||||
**Docker users: pin both image tags** rather than using `latest` on one and a fixed tag on the other. When upgrading the multi-container setup, follow the agent-image upgrade procedure in [`docs/docker.md`](docs/docker.md) (which requires dropping the `hermes-agent-src` volume before recreating). The current agent/WebUI coupling is tracked in [`docs/rfcs/agent-source-boundary.md`](docs/rfcs/agent-source-boundary.md).
|
||||
|
||||
---
|
||||
|
||||
## Docs
|
||||
|
||||
**Start here**
|
||||
|
||||
@@ -2569,7 +2569,7 @@
|
||||
.msg-row:hover .msg-actions{opacity:1;}
|
||||
.msg-action-btn{background:none;border:none;color:var(--muted);cursor:pointer;font-size:13px;padding:2px 5px;border-radius:5px;transition:color .12s,background .12s;line-height:1;}
|
||||
.msg-action-btn:hover{color:var(--accent-text);background:var(--accent-bg);}
|
||||
.selected-text-reply-btn{position:fixed;z-index:1200;display:inline-flex;align-items:center;gap:6px;padding:7px 11px;border:2px solid var(--accent);border-radius:999px;background:var(--bg);color:var(--text);box-shadow:0 8px 24px rgba(0,0,0,.26),0 0 0 1px var(--surface);font-size:12px;font-weight:700;line-height:1;cursor:pointer;opacity:0;pointer-events:none;transform:translateY(4px);transition:opacity .12s ease,transform .12s ease,background .12s ease,border-color .12s ease;}
|
||||
.selected-text-reply-btn{position:fixed;z-index:1200;display:inline-flex;align-items:center;gap:6px;padding:7px 11px;border:2px solid var(--accent);border-radius:999px;background:var(--bg);color:var(--text);box-shadow:0 8px 24px rgba(0,0,0,.26),0 0 0 1px var(--surface);font-size:12px;font-weight:700;line-height:1;cursor:pointer;opacity:0;pointer-events:none;transform:translateY(4px);transition:opacity .12s ease,transform .12s ease,background .12s ease,border-color .12s ease;user-select:none;}
|
||||
.selected-text-reply-btn.visible{opacity:1;pointer-events:auto;transform:translateY(0);}
|
||||
.selected-text-reply-btn:hover{background:var(--accent-bg);border-color:var(--accent-hover);}
|
||||
.selected-text-reply-btn:focus-visible{background:var(--accent-bg);border-color:var(--accent-hover);outline:2px solid var(--focus-ring);outline-offset:2px;}
|
||||
|
||||
@@ -90,3 +90,15 @@ def test_selected_text_reply_styles_and_i18n_exist_for_all_locales():
|
||||
keys = set(key_pattern.findall(block))
|
||||
missing = sorted(required - keys)
|
||||
assert not missing, f"{locale} missing selected-text reply keys: {missing}"
|
||||
|
||||
|
||||
def test_selected_text_reply_button_has_user_select_none():
|
||||
css = read("static/style.css")
|
||||
# The base rule must carry user-select:none so browser selection never
|
||||
# renders on or through the button, regardless of hover background opacity.
|
||||
assert "user-select:none" in css
|
||||
# Confirm it lives inside the .selected-text-reply-btn rule, not elsewhere.
|
||||
rule_match = re.search(
|
||||
r'\.selected-text-reply-btn\{[^}]*user-select:none[^}]*\}', css
|
||||
)
|
||||
assert rule_match, ".selected-text-reply-btn base rule must include user-select:none"
|
||||
|
||||
34
tests/test_readme_compat_section.py
Normal file
34
tests/test_readme_compat_section.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def test_readme_has_compatibility_section():
|
||||
repo_root = Path(__file__).resolve().parents[1]
|
||||
readme = (repo_root / "README.md").read_text(encoding="utf-8")
|
||||
|
||||
assert "## Compatibility" in readme, (
|
||||
"README.md must contain a ## Compatibility section documenting the "
|
||||
"tested hermes-agent compatibility policy"
|
||||
)
|
||||
|
||||
assert "Upgrade both together" in readme, (
|
||||
"README.md Compatibility section must include upgrade-together guidance "
|
||||
'("Upgrade both together")'
|
||||
)
|
||||
|
||||
assert "pin both image tags" in readme, (
|
||||
"README.md Compatibility section must include Docker pin guidance "
|
||||
'("pin both image tags")'
|
||||
)
|
||||
|
||||
assert "docs/docker.md" in readme, (
|
||||
"README.md Compatibility section must cross-link to docs/docker.md"
|
||||
)
|
||||
|
||||
assert "docs/rfcs/agent-source-boundary.md" in readme, (
|
||||
"README.md Compatibility section must cross-link to "
|
||||
"docs/rfcs/agent-source-boundary.md"
|
||||
)
|
||||
|
||||
assert "#2491" in readme, (
|
||||
"README.md Compatibility section must reference issue #2491"
|
||||
)
|
||||
Reference in New Issue
Block a user