Commit graph hyperhive/frontend/packages/shared/src/dom.js
Author SHA1 Message Date
iris
e5b307df1b frontend: hive-btn — drop the is= customized-built-in, use an autonomous element instead
Per review: don't use is=, it reads as a hack (and it is one — is=-upgraded
built-ins can never host a shadow root, which is what caused the crash this PR
fixes in the first place). <hive-btn> is now a normal autonomous custom element
wrapping a real <button> inside its own shadow root, so it gets its shadow
encapsulation back (matching hive-dialog/hive-toast) instead of the document-
level stylesheet workaround from the previous commit.

delegatesFocus: true on the shadow root means .focus() on the host (what
modal.js calls for autofocus) reaches the inner button directly. The inner
button's native click is a composed event, so host-level click listeners
(what modal.js/themedPrompt already use) keep working unchanged.

modal.js: el('button', { is: 'hive-btn', ... }) -> el('hive-btn', { ... }) at
the one call site. dom.js: removed the is= special case from el() entirely —
it existed only to support this one now-gone usage. Build clean.
2026-07-29 23:17:19 +02:00
iris
a588ed42ce frontend: add <hive-btn> component, use it for dialog buttons
mara: 'wait the common styles is literally just the button stuff? pls
make a button component now as part of this pr and replace the usage
in the modal. replacing all usages and finally removing the btn styles
from common css is a follow up then.'

<hive-btn> (hive-btn.js) is a customized built-in <button is="hive-btn">
with its own shadow root -- extending HTMLButtonElement keeps every
native button behaviour (click/keyboard activation, :disabled, form
participation) instead of re-implementing it on a generic wrapper.
Shadow root holds only an adopted stylesheet + a <slot>, so the
button's light-DOM content (label, or asyncBtn's swapped-in spinner
span) renders through unchanged -- slotted content stays styled by the
light-DOM cascade, so the global .spinner class still applies.
Variants (cancel/confirm/danger) are a 'variant' attribute, not a CSS
class, since they're a semantic prop of the component.

Customized built-ins aren't supported in Safari/WebKit -- fine here,
the project targets recent Firefox only (same reasoning as the
original custom-elements pilot).

Wired into modal.js: HiveDialog's buttons now render as
<button is="hive-btn" variant="...">, replacing the old
component-common.css .btn copy -- deleted that file + component-
styles.js entirely (their sole purpose was giving dialog buttons a
.btn look, which hive-btn now owns properly). dom.js's el() gained
 support so it can create customized built-ins the same way it
creates everything else. hive-dialog.css dropped the now-dead
.cancel/.confirm/.confirm.danger rules.

Per mara's scoping: NOT touching the other .btn consumers across the
app (dashboard/agent submit buttons, form() helper, etc.) or removing
.btn from dashboard/common.css / agent/agent.css in this PR -- that
migration + cleanup is an explicit follow-up.

Verified with a full frontend build (grepped bundled JS for hive-btn/
variant to confirm it inlines); nix fmt clean.
2026-07-29 12:55:54 +02:00
iris
7110a25cf6 frontend: extract themed dialogs + async-form handler to shared, wire agent UI
The dashboard has a themed modal/dialog system (modal.js: themedToast/
themedConfirm/themedPrompt) and a data-async form submit interceptor
(bindAsyncForms) that every dashboard action routes through. The
per-agent UI never adopted either — it had its own more primitive
data-async handler using native window.confirm()/alert() (8 call
sites) and a duplicated el() DOM helper.

- Moved el() out of dashboard/common.js into shared/src/dom.js.
- Moved modal.js + modal.css from dashboard/src/ to shared/src/,
  updating its internal el import.
- Moved bindAsyncForms from dashboard/common.js into shared/forms.js,
  alongside the asyncBtn primitive it's built on.
- Updated every dashboard file's imports to the new shared locations
  (no re-export shims).
- agent.css now @imports shared/modal.css so the dialogs render
  themed there too.
- agent/app.js: dropped its local el()/data-async duplicate, wired
  bindAsyncForms(), and replaced all 8 window.confirm() sites with
  themedConfirm (async, wrapped in a fire-and-forget IIFE where the
  call site needs a synchronous boolean return, e.g. the slash-command
  dispatcher).

Closes hyperhive#2791. Verified with a full frontend build
(npm run build) — both dashboard and agent bundles compile clean and
agent.css picks up the .tc-* dialog styles it previously lacked.
2026-07-27 18:55:17 +02:00