mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-08-11 03:09:57 +00:00
fix(v3): reject accidental text-selection of UI chrome (user-select policy) (#637)
* fix(v3): reject accidental text-selection of UI chrome (user-select policy)
Dragging/double-clicking across the v3 UI marquee-highlighted buttons, labels,
the sidebar, transport, and the note-highway HUD — looks broken (reported Mac +
Windows). Default the v3 shell to user-select:none on html, then opt CONTENT
back in. Decided by a 4-lens panel (UX / a11y / dev-ops / plugin-ecosystem);
their guardrails are baked in:
- Form fields ALWAYS re-enabled (input/textarea/select/[contenteditable]) so the
caret + IME composition never break. No `* { user-select:none }` (WebKit input
bug 82692).
- Plugin screens (.screen[id^="plugin-"]) stay selectable BY INHERITANCE (no `*`,
so a plugin's own non-select chrome still wins) — a plugin's copyable text
(lyrics, chords, results), including community/out-of-tree plugins that never
adopt the class, isn't silently locked.
- Core read-only content opts back in by CONTAINER via a hand-authored
`.fb-selectable` (not a Tailwind utility — so runtime-installed plugins get it
too): the whole Settings panel (paths, device names, version, diagnostics,
About) and the now-playing song metadata. Answers the open "keep settings
copyable?" question: yes, at the container.
Cosmetic only — never used to lock copy-worthy text (errors/IDs/paths/versions/
metadata stay selectable; WCAG 2.2 allows copy-paste as a mechanism). v3-only
(v2 unchanged; v3.css loads only on /v3); plain CSS, no Tailwind rebuild; no
desktop/Electron changes (standard OS-framed window). `.fb-selectable` is
documented in CLAUDE.md for plugin authors.
Tests: tests/js/v3_user_select_policy.test.js (html default, form-field
re-enable, plugin-screen carve without `*`, .fb-selectable, container opt-ins,
and the no-`*`-rule guardrail).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QbexxfTt8q2tAn436MqGWF
* fix(v3): address review of the user-select policy (#637)
Review (manual + Codex) of the v3 text-selection policy:
- P1 (real bug): the now-playing HUD metadata opted into `.fb-selectable` but
its `#player-hud` parent is `pointer-events: none`, so the mouse could never
reach the text to select it — the opt-in was inert. Add `pointer-events-auto`
to the metadata block (verified in-browser: user-select:text + pointer-
events:auto, while the HUD parent stays pointer-events:none).
- Coverage: the PR's a11y guardrail promised copyable text stays selectable
"incl. in modals/toasts", but only Settings + the HUD were opted in. Blanket-
opt the focused copyable surfaces back in by selector — `.feedBack-modal`,
`[role="dialog"]`, `#fb-notify-stack`, `#v3-fb-toast`, `#scan-banner` — so
errors / IDs / paths / file names in dialogs, toasts, and the scan banner stay
copyable. These are focused panels, not dense card lists, so re-enabling
selection there can't recreate the across-cards marquee mess.
(Deliberately NOT opting in the library grid / dashboard / profile card lists:
making dense card text selectable would reintroduce exactly that marquee mess
on a drag — copy song metadata from the now-playing HUD / Settings instead.)
- Test (P3): assert the selectable rule's selectors order-independently, cover
the new modal/toast/banner surfaces, and check the HUD block carries BOTH
fb-selectable and pointer-events-auto (class-order independent).
Verified in a real browser (chromium): html=none, sidebar chrome=none, input=
text, Settings=text, HUD meta=text+pointer-events:auto, dialog/modal=text.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: byrongamatos <xasiklas@gmail.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
byrongamatos
parent
a791a0d8fe
commit
8fbbc761fc
@@ -4,6 +4,62 @@
|
||||
* `fb` palette in tailwind.config.js.
|
||||
*/
|
||||
|
||||
/* ── Text-selection policy (v3) ──────────────────────────────────────────────
|
||||
Accidental drag/double-click selection of app chrome (sidebar, transport, the
|
||||
note highway/HUD, buttons, labels) makes the UI look broken and is never
|
||||
useful — so default the interface to non-selectable, then opt *content* back
|
||||
in. v3-only: this sheet loads only on /v3 (v2 is unchanged). The panel's
|
||||
guardrails are baked in:
|
||||
- NEVER a `* { user-select:none }` rule — it breaks input carets / IME
|
||||
composition on WebKit (bug 82692); we scope to `html` and re-enable below.
|
||||
- This is cosmetic only; it protects nothing (DevTools defeats it) and must
|
||||
never be used to "lock" copy-worthy text away (a11y: keep errors, IDs,
|
||||
paths, versions, metadata, lyrics selectable — incl. in modals/toasts). */
|
||||
html { -webkit-user-select: none; user-select: none; }
|
||||
|
||||
/* Form fields are ALWAYS selectable/editable — protects the caret + IME
|
||||
(including CJK / dead-key composition). The default must never swallow typing.
|
||||
`.fb-selectable *` forces descendants so a child element's own non-select
|
||||
can't strand copy-worthy text inside a content island. */
|
||||
input, textarea, select,
|
||||
[contenteditable]:not([contenteditable="false"]),
|
||||
[contenteditable]:not([contenteditable="false"]) * {
|
||||
-webkit-user-select: text; user-select: text;
|
||||
}
|
||||
|
||||
/* Plugin screens are content surfaces (editor, tabview, lyrics, theory, chord
|
||||
text, …). Re-enable their mounted subtree by INHERITANCE (no `*`) so the host
|
||||
policy can't silently make a plugin's copyable text un-selectable — including
|
||||
community / out-of-tree plugins that never adopt `.fb-selectable`. A plugin
|
||||
that wants its own chrome non-selectable still wins via its own element rule
|
||||
(which this inherited value doesn't override). */
|
||||
.screen[id^="plugin-"] { -webkit-user-select: text; user-select: text; }
|
||||
|
||||
/* Core read-only content opts back in by CONTAINER (lower-drift than tagging
|
||||
each value — a new setting added later inherits "selectable" for free):
|
||||
the Settings panel (values, paths, device names, version, diagnostics,
|
||||
About) and the now-playing song metadata (both tagged `.fb-selectable`).
|
||||
Plugins re-enable their own copyable regions with this same class
|
||||
(documented in CLAUDE.md).
|
||||
|
||||
The focused, transient surfaces below ALWAYS carry copy-worthy text (errors,
|
||||
IDs, file paths, device/version strings) per the a11y guardrail, so they're
|
||||
blanket-opted-in by selector rather than hand-tagged — they're single focused
|
||||
panels, not dense card lists, so re-enabling selection there can't recreate
|
||||
the across-cards marquee mess the policy prevents:
|
||||
- modals / dialogs: `.feedBack-modal`, `[role="dialog"]` (confirm, edit-meta,
|
||||
retune result/error, calibration, filter drawer);
|
||||
- toasts: `#fb-notify-stack`, `#v3-fb-toast`;
|
||||
- the library scan banner (`#scan-banner` — shows the current file path).
|
||||
(Dense card lists — the library grid, dashboard, profile — are intentionally
|
||||
left non-selectable; copy their text from the now-playing HUD / Settings.) */
|
||||
.fb-selectable, .fb-selectable *,
|
||||
.feedBack-modal, .feedBack-modal *,
|
||||
[role="dialog"], [role="dialog"] *,
|
||||
#fb-notify-stack, #fb-notify-stack *,
|
||||
#v3-fb-toast, #v3-fb-toast *,
|
||||
#scan-banner, #scan-banner * { -webkit-user-select: text; user-select: text; }
|
||||
|
||||
/* The v3 tuner card replaces the tuner plugin's floating launcher — hide it. */
|
||||
#tuner-toggle-btn { display: none !important; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user