diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cf74261..bea127de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/README.md b/README.md index a036ecf3..5c18922f 100644 --- a/README.md +++ b/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** diff --git a/static/style.css b/static/style.css index 0dae0554..91cbecd6 100644 --- a/static/style.css +++ b/static/style.css @@ -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;} diff --git a/tests/test_issue2481_selected_text_reply.py b/tests/test_issue2481_selected_text_reply.py index ee7b273b..45133fc2 100644 --- a/tests/test_issue2481_selected_text_reply.py +++ b/tests/test_issue2481_selected_text_reply.py @@ -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" diff --git a/tests/test_readme_compat_section.py b/tests/test_readme_compat_section.py new file mode 100644 index 00000000..379d7699 --- /dev/null +++ b/tests/test_readme_compat_section.py @@ -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" + )