Commit Graph
378 Commits
Author SHA1 Message Date
shadowdaoandClaude Sonnet 5.5 8a481a87d8 Sitesmith modal: flow tests (phases, cancel, resume, errors, vision off, sites/assets payload, self-review round trip)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-01 10:43:23 -08:00
shadowdaoandClaude Sonnet 5.5 784d85346e Sitesmith: tests for payload, design, attachments, panels, self-review runner
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-01 10:40:55 -08:00
shadowdaoandClaude Sonnet 5.5 79159dcb28 Sitesmith modal: live job status, cancel, resume, attachments, Sites I like, asset awareness, self-review toggle, design in the request
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-01 10:39:47 -08:00
shadowdaoandClaude Sonnet 5.5 f0b3dd1dba Sitesmith capture: CSS judged via CSSOM (no regex), single URL gate (no % / dot segments / backslash), all resources inlined through a bounded no-credentials fetch, CSP img-src data: only; bypass-class proof tests (unit + Firefox/Chromium)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-01 10:37:56 -08:00
shadowdaoandClaude Sonnet 5.5 c4f45541a3 Sitesmith: contrast repair wired into apply-ai-response; scoped guard exception for the script-less capture frame
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-01 10:32:50 -08:00
shadowdaoandClaude Sonnet 5.5 197b9c46ab Sitesmith self-review capture: sanitise page HTML before it reaches the frame (inert DOMParser allow-list, fixed-point guard), CSP + script-less sandbox, real-browser hostile-page proof; UI building blocks (attachments, sites I like, asset awareness, design payload)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-01 10:31:29 -08:00
shadowdaoandClaude Sonnet 5.5 0fde4dc22f Sitesmith: contrast auto-repair for model responses; capture module (html-to-image) for self-review
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-01 10:23:26 -08:00
shadowdaoandClaude Sonnet 5.5 2f5c311118 Sitesmith hook: start+poll transport, failure-safe results, refs/assets wrappers
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-01 09:54:55 -08:00
shadowdao ab7a10073b Merge fix/editor-parity into release/editor-parity (on top of captcha + Font Awesome) 2026-11-01 09:26:21 -08:00
shadowdaoandClaude Sonnet 5.5 ae02eb7d0d test(site-builder): parity harness neutralises documented editor-only differences (empty drop zone, empty-image placeholder)
Container marks the applied drop zone with data-wsb-dropzone.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-01 09:18:29 -08:00
shadowdaoandClaude Sonnet 5.5 d75f9d90ca fix(site-builder): published Text/Heading keep plain-text newlines as <br> like the canvas
Canvas renders plain text via textToInlineHtml (newline -> <br>); toHtml used escapeHtml,
so line breaks typed in the editor vanished on publish (TTM about page: ~100px shorter).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-01 09:16:26 -08:00
shadowdaoandClaude Sonnet 5.5 7d77201e5a fix(site-builder): Container 40px drop-zone min-height only when empty
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-01 09:14:28 -08:00
shadowdaoandClaude Sonnet 5.5 01d5ba5496 fix(site-builder): HeroSimple canvas render uses the exporter's declaration order (style wins over minHeight prop)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-01 09:13:54 -08:00
shadowdaoandClaude Sonnet 5.5 7a1ab36e63 fix(site-builder): editor canvas drops blank style values like the exporter
React assigns '' for a blank style value and a blank longhand after a shorthand
({padding:'20px', paddingTop:''}) wipes the shorthand in the canvas; the exporter
drops blanks. ResolvedRender is the one place node style reaches React, so
normalise there.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-01 09:12:35 -08:00
shadowdaoandClaude Sonnet 5.5 64fc21866c test(site-builder): canvas parity harness loads stored state verbatim (no blank-style stripping)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-01 09:11:52 -08:00
shadowdaoandClaude Sonnet 5.5 d6bfc51ae3 Merge fix/canvas-reset into fix/editor-parity
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-01 09:10:23 -08:00
shadowdaoandClaude Sonnet 5.5 dc415db2d3 fix(site-builder): editor canvas renders content with the published base reset
Published pages/Preview ship a head reset (*{margin:0;padding:0}, img block,
a inherit, body line-height 1.6 / #1f2937 / 16px); the editor canvas had none,
so h1-h6/p margins, line-height and inherited size/colour differed.

- editor.css: one commented block scoping the reset to .canvas-device-frame,
  every selector zero-specificity (:where) so inline styles / class rules win
  exactly as when published; editor chrome untouched (snapshot identical).
- tokens.ts: canvas heading/link token rules made zero-specificity too.
- utils/css-reset.ts: single source for the reset declarations (html-export
  imports it; test asserts byte-identity with the backend head and that the
  editor.css block carries the same declarations).
- scripts/e2e-canvas-parity.mjs (npm run test:browser:canvas): real editor vs
  published HTML in Firefox + Chromium, boxes within 1px + computed styles.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-01 08:23:19 -08:00
shadowdaoandClaude Sonnet 5.5 dbf3dda3e4 fix(site-builder): hero gradient blank on editor canvas - shorthand + undefined longhands
HeroSimple emitted 'background' together with backgroundImage/Size/Position
that were undefined. React assigns '' for those on first render and Chromium
and Firefox then wipe the shorthand's gradient (background-image: none), so
the canvas hero rendered white. Build the style with only the relevant keys
(shared by render and toHtml); exported HTML is byte-identical.

Adds a real-browser (Chromium+Firefox) regression test: npm run test:browser:bg,
and toHtml byte-identity assertions.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-01 07:52:34 -08:00
shadowdaoandClaude Sonnet 5.5 7b3baa815c feat(site-builder): Font Awesome 7.3.1 icons (per-network brands) in editor and exports
Exporters emit fa-solid/fa-brands/fa-regular classes through utils/fa-icon.ts;
SocialLinks gets X/TikTok/Bluesky/Threads/Discord/Mastodon/Reddit/Telegram;
the editor loads all.min.css + v4-shims (SRI) so editor chrome FA4 names work.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0185ukXgKV8v9BUrQWXSUW4P
2026-10-31 11:59:46 -07:00
shadowdaoandClaude Sonnet 5.5 bb97e5b396 feat(forms): captcha is mandatory on every relay form; remove the toggle
relayFormWiring always emits captcha="1" for relay-wired forms (legacy captcha prop ignored,
old projects with captcha:false load fine). CaptchaMock always shown for relay-wired forms;
FormStylePanel toggle replaced by a read-only note.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0185ukXgKV8v9BUrQWXSUW4P
2026-10-31 11:29:07 -07:00
shadowdaoandClaude Sonnet 5.5 92da689e62 feat(forms): captcha prop + panel toggle + marker attribute for relay forms
captcha=true emits captcha="1", explicit false emits captcha="0", undefined emits
nothing (legacy designs byte-identical). New forms default true. Editor shows an inert mock.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0185ukXgKV8v9BUrQWXSUW4P
2026-10-31 08:25:12 -07:00
shadowdaoandClaude Opus 5.5 76f0ba9ca1 fix(site-builder): navbar logo image never shown — no logoType control
The Navbar renders logoImage only when logoType === 'image', but the
Logo/Text toggle lived in the settings UI removed in 65a10a1 (2026-07-12);
NavStylePanel kept the image picker and never gained the toggle. A picked
logo was stored and silently ignored (editor and published site showed the
text fallback "MySite"; seen on olsdigital.com).

- Logo Type Text/Image toggle in the Navbar Logo section
- picking an image sets logoImage + logoType:'image' in one undo step

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 05:50:33 -07:00
shadowdao bdb4789903 Merge fe-after-backend: client_id, warnings, limits, head parity 2026-10-07 13:52:24 -07:00
shadowdaoandClaude Sonnet 5.5 9d425afa29 feat(editor): adopt backend final-fix contract (client_id, warnings, limits, head parity)
client_id per editor load (memory only), already_saved, save warnings via notices,
upload_max_bytes enforced client-side with the server error text on 413,
project_max_bytes warning, headCode in the Preview font scan, pinned backend heads.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-07 13:49:53 -07:00
shadowdaoandClaude Opus 5.5 eb401cd979 fix(sections): inserting from the section library closes the mobile Blocks sheet
The section library now calls closeSheet() after inserting a section (or a
global section). It mounts in CanvasOverlays, beside EditorShell, so it could
not reach the MobileChromeProvider inside the shell; the provider moves up to
App around the shell and the overlays so both share one sheet state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:41:22 -07:00
shadowdaoandClaude Opus 5.5 472e5e4d81 fix(selection): Esc clears a multi-selection; context menu acts on all selected
Esc was only claimed by select.parent for a single node, so a multi-selection
could not be dropped from the keyboard. Add select.clear (Esc, >1 selected)
through the existing dispatcher, which already skips text fields and open
dialogs. The context menu's Duplicate/Delete now run the multi-selection core
commands (edit.duplicate / edit.delete) when the clicked node is part of a
multi-selection, instead of acting on the first node only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:41:22 -07:00
shadowdaoandClaude Opus 5.5 84bc626ed0 fix(sitesmith): first-run wizard only for users who can use Sitesmith
The business-profile wizard was forced on an empty canvas even when
Sitesmith was locked (paid addon), capped or switched off. The decision now
waits for the entitlement and skips the wizard unless the user can chat
(OK_MONTHLY / OK_BONUS); a failed entitlement falls back to the modal.
useSitesmith reports an ok:false entitlement as an error so it cannot hang.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:41:22 -07:00
shadowdaoandClaude Opus 5.5 92c88496a7 fix(publish-checklist): Fix on SEO checks opens the page's SEO settings
Title-length, missing meta description and noindex-landing checks carry
fix: 'page-settings', but Fix only closed the list. It now shows that page's
PageSettingsModal in place of the checklist; closing it finishes the Fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:41:22 -07:00
shadowdaoandClaude Opus 5.5 49ed687c3b fix(topbar): mobile overflow menu reaches Version history and the command palette
On mobile the collapsed topbar had no route to Version history, Export ZIP,
Business profile or the command palette. Add a 'Version history' item (only
when the site.history command is registered, run through the registry) and a
'More commands…' item that opens the command palette.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:41:22 -07:00
shadowdaoandClaude Opus 5.5 c62a333257 fix(editor): header/footer zone previews show the active device's overrides
The zone previews render the exported HTML, whose <style data-wsb-responsive>
@media rules evaluate against the browser viewport, so tablet/mobile modes
showed desktop styling for header/footer. ZonePreview now applies the active
device's override rules unconditionally (applyResponsiveCssForDevice) and
drops the rest.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:40:54 -07:00
shadowdaoandClaude Opus 5.5 e20c604413 fix(editor): mobile column stacking follows the desktop style, as published
Published mobile stacking is the global RESPONSIVE_CSS rule matching the
DESKTOP inline style, so un-stacking Columns on mobile under a stacked tablet
(styleMobile.flexDirection 'row') publishes un-stacked. The editor's
device-emulation copy matched the resolved inline style and showed it stacked.
RenderNode now marks nodes with a flexDirection/display override with
data-wsb-base-stack (baseStacksOnMobile) and editor.css keys off it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:40:54 -07:00
shadowdaoandClaude Opus 5.5 579ea5370c fix(tokens): canvas token CSS falls back to defaults for blank tokens
Export/published :root token CSS replaced a blank/whitespace token with its
default; the editor canvas emitted an empty custom property, invalidating
every var() that read it. Share one buildTokenVarDecls between the canvas
and the export so both apply the same fallback.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:40:54 -07:00
shadowdaoandClaude Opus 5.5 faf129c509 fix(preview): gate head animation CSS/noscript on composed header+body+footer
Preview (TopBar handlePreview + history snapshot preview) gated the
animation CSS and no-JS fallback on the page body only, while the published
head (generateCompiledHTML) gates on the composed header+body+footer. Add
exportComposedPreviewHtml (head scanned over composed content for fonts and
animation; body replaced with composed HTML + one reveal script) and use it
from both Preview paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:40:54 -07:00
shadowdaoandClaude Opus 5.5 a2d18b7f4b fix(save): a save during a page/zone switch persists the target page, not the old canvas
PageContext.loadState deserializes on setTimeout(0); a save landing in that
window serialized the previous page's still-mounted canvas into the new active
page's slot. loadState now records the pending state and sendSave uses it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:33:58 -07:00
shadowdaoandClaude Opus 5.5 6d22aabdc0 fix(global-sections): block structural edits inside a global-section instance
syncInstances rebuilds an instance's subtree from the master, so a node
dropped, pasted, duplicated or moved into an instance vanished from every page
on the next master edit. Chosen approach: BLOCK (not preserve-on-sync):
- every resolver component's Craft canMoveIn/canMoveOut refuses a canvas inside
  an instance (canvas DnD, Layers DnD, actions.move); synthetic shell-inner
  nodes now carry the component's rules instead of hardcoded allow-all
- insertBlock (BlocksPanel / EmptyContainerAdd / palette), paste (menu +
  shortcut), duplicate (menu + shortcut) and Sitesmith patch/section replace
  redirect to after the instance or refuse; no empty-container '+' inside one
The instance root still moves/duplicates as a whole; content overrides are
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:32:34 -07:00
shadowdaoandClaude Opus 5.5 ca500d36a9 fix(global-sections): Templates, Sitesmith site, Reset site and history Restore refused while a master is open
Each replaced site content under an open master zone: Templates cleared the zone
and committed the template's first section as the new master (rewriting every
instance); replaceAllPages left the zone without committing; history Restore
loaded page content around it. Templates button/command/overflow item and the
history Restore buttons are disabled with a 'Click Done first' notice, and the
handlers (TemplateModal.handleLoad, replaceAllPages -> false, Sitesmith site,
Reset site, restoreSite/restorePage) refuse as a hard guard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:26:37 -07:00
shadowdaoandClaude Opus 5.5 996c9e92ec fix(global-sections): save/autosave/publish inside a master zone persist the live master
The zone's live master was neither in extras nor in the stored pages until
Done, so a save from inside the zone succeeded without it and cleared dirty;
closing the tab lost the edit (publish shipped stale instances). The save
payload now carries the master committed into the library and every stored
page/header/footer synced, computed exactly as Done would without touching the
editor (PageContext.globalZoneSaveView).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:23:46 -07:00
shadowdaoandClaude Opus 5.5 37ed85bf42 fix(navbar): logo on a stored light bar follows the link text in the editor
Legacy Navbar nodes store backgroundColor '#ffffff' + textColor '#3f3f46'; the
editor took the logo colour from the Site Design text (light on a dark design)
while the export already uses logoColor || textCol. Same dark-palette class as
finding 8.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:22:22 -07:00
shadowdaoandClaude Opus 5.5 4b18d68389 fix(history): restore each page's menu settings (nav)
snapshotToPages mapped only id/name/slug/craftState/seo and
planPageRestore built/replaced pages without nav, so 'Restore site' and
'Restore one page' dropped every page's nav (show/parentId/label). The
NavSync effect then rewrote the follow-pages header: hidden pages
reappeared, dropdown nesting flattened, custom labels reverted.

SnapshotPage now carries `nav?: PageNav`, accepted only when it is a
plain non-null, non-array object (shallow-copied), else omitted.
planPageRestore restores it in id/slug replace mode (like seo) and on
append. HistoryModal already passes these rows straight to
setPagesCraftState, which keeps nav, so no wiring change was needed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:20:24 -07:00
shadowdaoandClaude Opus 5.5 c00985a3a1 fix(responsive): derive Container/Section flex layout from the resolved per-device style in export
Tablet/mobile Alignment and Vertical-alignment overrides published only the
raw text-align/justify-content declarations, while the editor (which renders
from the device-resolved style) also applied the derived flex layout
(display:flex;flex-direction:column;align-items). Desktop-centred containers
stayed centred on mobile, mobile-only centring/vertical-align did nothing, and
in boxed mode the flex sits on the inner div which had no scope at all.

- Container/Section: single containerLayout()/sectionLayout() helper used by
  render, toHtml and a new static responsiveLayout hook.
- responsiveCssForNode takes an optional DerivedLayoutFn: per device it diffs
  the layout derived from the resolved style against what already applies
  (desktop for tablet, tablet for mobile), emits changes (and resets for
  dropped keys), and scopes boxed-mode inner rules as [data-wsb-r=x]>div.
- html-export passes the component's responsiveLayout hook.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:20:24 -07:00
shadowdaoandClaude Opus 5.5 6afb1c9b22 fix(site-builder): keep legacy light-surface text readable on dark palettes
Nodes saved before the component-tokens work (9ab0102) store light surface
hexes -- ContactForm inputBg '#ffffff', SubscribeForm style.backgroundColor
'#f8fafc', Testimonials cardBg '#f8fafc' -- while the text on those surfaces
became var(--wsb-text)/var(--wsb-muted). On a dark Site Design palette (light
text token, e.g. Agency #fafafa) that rendered white-on-white.

Text on a STORED light surface (hex #rgb/#rrggbb or rgb()) now keeps the
pre-token dark hex (input #1f2937, subscribe heading #1f2937, testimonial
quote #374151 / name #18181b / title #64748b); absent, token or dark surfaces
keep the token text. Stored props are never rewritten. Applied in both the
editor render and toHtml export.

Generalises Navbar's isLightNavBg into utils/color.ts (isLightColor /
textOnSurface, plus parseColor moved out of publish-checks and re-exported);
Navbar and autoTextColor now share it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:20:24 -07:00
shadowdaoandClaude Opus 5.5 7fe47160e9 fix(export): keep injected attrs out of the WHP-FORM marker comment
injectAttrs() skipped leading <style> blocks and then inserted the
responsive/visibility/animation attrs before the first '>'. Relay forms
(ContactForm, SubscribeForm, FormContainer with a recipient) start their
toHtml with `<!--WHP-FORM ...-->`, so the attrs landed inside the marker
(`thankyou=""-- data-wsb-r="...">`): the comment was left unterminated,
Preview swallowed the form, and the publish step refused the whole site.

injectAttrs now skips any leading run of whitespace, <style> blocks and
<!--...--> comments (any order, repeated) and targets the first real
element; a string that is only such a run is returned unchanged. The
marker wire format is untouched.

Tests port the backend's strict marker regex from FormRelayRewrite.php
and export each relay form with a recipient + styleMobile/styleTablet +
hideOnMobile + animation through exportBodyHtml.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:20:24 -07:00
shadowdaoandClaude Opus 5.5 ff826a03c4 fix(api): 180s publish timeout, "Still publishing..." after 30s
Save/load keep 30s; publish of a large site legitimately runs longer and was
reported as failed at 30s while the server kept going.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:19:11 -07:00
shadowdaoandClaude Opus 5.5 6d6994f009 fix(preview): render customer head code only in a sandboxed srcdoc iframe
Preview and history Preview opened the composed page (raw site head code
included) as a panel-origin blob: tab, so a customer's <script> ran with the
viewer's panel session and CSRF token (root included). Both now open an empty
shell tab inside the click and render the page in an <iframe srcdoc> with
sandbox="allow-scripts allow-popups allow-forms" (no allow-same-origin):
an opaque origin, no opener. Tests assert the sandbox attribute and scan the
source for any remaining text/html blob / document.write / blob tab path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:18:11 -07:00
shadowdao d4400e49ad Merge component-tokens 2026-10-07 13:01:14 -07:00
shadowdao c35a73bb12 Merge integration-followups 2026-10-07 13:01:14 -07:00
shadowdaoandClaude Opus 5.5 bbae73c132 fix(styles): box-model and shadow presets are discrete undo steps
Preset buttons in BoxModelSection / BorderEffectsSection now write through
the discrete setters (setPropStyles / setPropStyle), so a keyboard-activated
preset (no pointerup or focusout to close the burst) never merges into the
previous edit of the same key. SpacingControl gains an optional onPreset
for this; margin/padding presets are also one undo step for all four sides
in the media panels.

Refs #7

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 12:54:13 -07:00
shadowdaoandClaude Opus 5.5 7d25bf181c fix(api): time out hung save/publish/load; show PHP error text
- save, publish and load run under a 30s AbortController timeout (raced,
  so a stalled body read can't hang either); save/publish resolve to
  { success:false, error:'timeout', message } and load rejects with a
  readable Error, so a hung request no longer blocks the save queue
- readApiResult falls back to obj.error when obj.message is absent, so
  PHP texts like 'Another save of this site is still in progress...' are
  shown instead of 'server returned HTTP 503'

Refs #4
Refs #47

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 12:51:48 -07:00
shadowdaoandClaude Sonnet 5.5 e347a4334d feat(site-builder): tokenise remaining component design colours (Navbar, Menu, Footer, SocialLinks, StickyCallBar, BackgroundSection, ContentSlider, Countdown, HeroSimple)
Refs #44

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-07 12:50:47 -07:00
shadowdaoandClaude Opus 5.5 25de75c34f fix(shortcuts): bare-key shortcuts never fire while a dialog is open
Single-key shortcuts (1/2/3 device switch) are skipped while any editor
dialog or modal is open (role=dialog/alertdialog, aria-modal, dialog[open],
.modal-backdrop) or focus is inside one. Dialog-like page content inside
the canvas frame does not count.

Refs #39

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 12:49:46 -07:00