fix(ui): restore Firefox text selection for room public key and contract ID - #536
Conversation
The Room Public Key, Contract ID and Secret Version fields in the room-details panel carried Tailwind's `select-all` (`user-select: all`). Firefox parses that rule — the computed value really is `all` — but selecting inside an <input> under it yields a ZERO-length selection, so click-drag and double-click both did nothing and Ctrl+C copied nothing. Chromium and WebKit select the whole value under the same rule, which is why the bug presented as Firefox-specific. Declare `user-select: text` explicitly instead of relying on the `auto` default, and add a copy button beside the two long values. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QaMA3cqXTBiTNmPYcMeZsi
`ui/assets/styles.css` is gitignored (it is a build product), but only
`build-ui` and `dev-example` depended on `build-tailwind`. The other four
dx-driven tasks — `build-ui-example`, `build-ui-no-sync`,
`build-ui-example-no-sync` and `dev` — did not, so on a clean checkout they
fail with:
error: Asset at /assets/styles.css doesn't exist
`build-ui-example-no-sync` is the task AGENTS.md tells you to run before the
Playwright suite, so this bites anyone following the documented workflow. CI
is unaffected either way because build.yml runs `npm run build:css` as its
own explicit step, which is why this went unnoticed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QaMA3cqXTBiTNmPYcMeZsi
The manifest declared `dioxus = "0.7.3"` while Cargo.lock has resolved
0.7.9 for some time. Two concrete consequences:
- AGENTS.md documents ui/Cargo.toml as a debugging trap: someone checking a
stale `dx` CLI against the declared version would wrongly conclude a 0.7.3
CLI was correct.
- build.yml keys its `dx` cache on `hashFiles('ui/Cargo.toml')`, with a
comment saying a dioxus bump should invalidate the cached CLI. That only
works if the declared version tracks the real one.
0.7.9 is the latest stable dioxus (0.8.0-alpha.0 is a pre-release and is
deliberately not adopted here). Cargo.lock is byte-identical before and
after, so resolution and every WASM artifact are unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QaMA3cqXTBiTNmPYcMeZsi
… 320px Review pass on the new spec found three gaps: - The copy-button tests only asserted the label flips to "Copied!", which is a side effect that is identical whether or not the two buttons are wired to the right values. Capture what the app actually writes to the clipboard (by patching document.execCommand, since copy_to_clipboard deliberately uses execCommand so it works in the sandboxed iframe) and assert each button copies ITS field. Mutation-tested: swapping the two values in the component makes exactly these two tests fail. - No coverage that the "Copied!" feedback resets when the panel is closed and reopened, even though copy-clipboard-feedback.spec.ts holds the sibling Export Identity button to that same contract. - The overflow check pinned the viewport to 1280px, so the narrow-width risk that adding a button beside each value actually introduces was untested. Add a 320px check (the smallest width responsive-layout.spec.ts covers) asserting both buttons stay visible and neither the panel nor the document overflows. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QaMA3cqXTBiTNmPYcMeZsi
Review (Full tier)Tier: Full, because the diff touches Lenses run sequentially in-session (no Findings, all fixed in f58f0f91. Copy-button tests were non-discriminating (testing lens). They asserted 2. No reset-on-reopen coverage (big-picture lens). 3. Narrow-viewport overflow untested (skeptical lens). The overflow check Considered and deliberately not changed
Verification notesEvery browser claim in this PR is measured, not inferred. The Firefox gate was [AI-assisted - Claude] |
The reset test flaked once on mobile-safari in a full-suite run (it passed on retry). Investigated rather than retried: - Standalone it passes 10/10 on mobile-safari. - An instrumented 25-cycle open/copy/close/reopen loop showed a stale label 0 times in BOTH WebKit and Firefox, so the reset behaviour itself is sound. The sensitivity is actionability, not correctness: the panel is a `fixed inset-0` overlay, so the room-header (i) button only becomes clickable again once it has fully unmounted, and under the full suite's parallel load (five projects each booting a WASM app) that settle can outrun a 5s budget. Wait for the reopen affordance explicitly and give the reopen assertions the same 15s budget other app-level waits in this suite use. Still non-vacuous: making the copy state persist across remount (the exact regression this guards) fails it with `Received string: "Copied!"`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QaMA3cqXTBiTNmPYcMeZsi
Verification updateA flake in my own new test, found and fixed. The reset-on-reopen test
The sensitivity is actionability, not correctness: the panel is a Local full suite, final run: 682 passed, 23 skipped, 0 flaky. Filed while working on this
[AI-assisted - Claude] |
Problem
In the room-details panel (the (i) icon beside the room title), the Room
Public Key and Contract ID could not be selected or copied in Firefox.
Double-click selected nothing, click-drag selected nothing. The same fields
worked in Chrome, Brave and Safari.
Root cause: all three read-only fields in that panel carried Tailwind's
select-all, which compiles to-webkit-user-select: all; user-select: all.Firefox parses the rule — the computed value really is
all— but selectinginside an
<input>under it yields a zero-length selection, so there isnothing for
Ctrl+Cto copy. Chromium and WebKit select the whole value underthe same rule, which is why the bug presented as Firefox-specific.
Measured with a standalone repro (no River code), counting characters selected
by a click-drag across the field and then by a double-click:
user-select: alluser-select: textApproach
user-select: textexplicitly (select-text) on the threeread-only fields — Room Public Key, Contract ID, and Secret Version — rather
than relying on the
autodefault. The Secret Version field had theidentical defect and is fixed in the same pass.
existing
crate::util::copy_to_clipboardhelper (which usesexecCommand('copy')so it works in the gateway's sandboxed iframe).Manual text selection works independently of the button.
flexrow;flex-1 min-w-0keeps a long base58value from widening the modal.
One deliberate behaviour change in Chromium/WebKit
Worth calling out against the issue's "Chrome/Brave/Safari behavior remains
unchanged" criterion. Under
user-select: all, a single click in thesefields selected the entire value in Chromium/WebKit. Under
user-select: texta single click places a caret instead; drag, double-click, Ctrl+A and Ctrl+C
all behave normally. That is inherent to the fix the issue asks for in
requirement 4, and the new copy button more than restores the one-action copy —
this time in Firefox too, where it never worked.
There is no way to keep click-selects-all and fix Firefox: re-selecting the
whole value from a click/focus handler would clobber a drag selection, which is
the exact behaviour being restored.
Why not just drop the class and let
autoapply? The issue asks for the valueto be explicitly selectable, and an explicit declaration is what stops the
next person reintroducing
select-all. A rustdoc block onCopyButtonrecordsthe measurement table and says not to restore it.
Testing
New
ui/tests/room-info-key-selection.spec.ts, which runs on all fivePlaywright projects (CI installs chromium + firefox + webkit).
The Firefox gate is verified, not assumed. With the fix reverted and the
UI rebuilt, the 4 mouse-selection tests fail on Firefox; with the fix they
pass. The loaded build was confirmed by reading the input's computed
user-selectout of the live DOM before each run, per the AGENTS.md warningabout stale builds.
Coverage per field: click-drag selects, double-click selects,
Ctrl+Ccopiesa mouse selection,
Ctrl+A+Ctrl+Ccopies the whole value, selectingdoes not close the modal, the copy button reports "Copied!", and the panel
does not overflow horizontally.
The
Ctrl+Cassertions are deliberately mouse-driven. An earlierCtrl+Aversion passed even on the broken build (keyboard select-all is not blocked by
user-select: all), i.e. it was non-discriminating.The two keyboard-copy tests are skipped on WebKit. Isolated to a bare page
with two inputs and no River code, Playwright's WebKit will not deliver a
keyboard copy to a readonly input at all:
These fields were already
readonlybefore this PR, so that is a harnessproperty, not a regression. Mouse selection — the behaviour this PR is about —
is still asserted on webkit and mobile-safari.
Results: new spec 57 passed / 8 skipped across all projects. Full existing
suite 661 passed / 23 skipped / 1 flaky, plus
cargo test -p river-ui --bins780 passed and
cargo fmt --checkclean. The one flaky test(
invitation-sandbox.spec.tson mobile-safari) is unrelated to this change andpasses 6/6 standalone; filed as #538.
Also in this PR
Two small fixes found while getting a clean checkout to build. Both are
independent of the bug fix and easy to drop if you'd rather they went
separately.
Makefile.toml—ui/assets/styles.cssis gitignored (a buildproduct), but only
build-uianddev-exampledepended onbuild-tailwind.build-ui-example,build-ui-no-sync,build-ui-example-no-syncanddevdid not, so on a clean checkout theyfail with
Asset at /assets/styles.css doesn't exist.build-ui-example-no-syncis the task AGENTS.md tells you to run before thePlaywright suite. CI is unaffected either way because build.yml runs
npm run build:cssas its own explicit step, which is why this wentunnoticed. Verified by deleting
styles.cssand rebuilding.ui/Cargo.toml— declareddioxus = "0.7.3"while Cargo.lock hasresolved 0.7.9 for some time. AGENTS.md documents that stale declaration as a
debugging trap, and build.yml keys its
dxcache onhashFiles('ui/Cargo.toml')expecting a dioxus bump to invalidate the cachedCLI — which only works if the declared version tracks the real one. 0.7.9 is
the latest stable (0.8.0-alpha.0 is a pre-release and is deliberately not
adopted). Cargo.lock is byte-identical before and after, so resolution and
every WASM artifact are unchanged.
No delegate/contract WASM was rebuilt or committed.
Closes #537
[AI-assisted - Claude]