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>
The popover is measured once open and clamped to the viewport with an
8px margin; near the bottom edge it flips above the '+'.
Refs #41
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A cache-hit search aborts any older in-flight stock_search and clears
loading; StockPhotoTab only applies the newest request's result
- A bare 404 means 'stock unavailable' only for the stock_search probe;
on stock_import it is an ordinary error (an explicit invalid/unknown
action error still disables the tab)
Refs #55
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Templates reference the site tokens, so with the checkbox off a template
follows the site's own palette. Label and a state-dependent help line now
say so.
Refs #44
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Switches NavStylePanel from nodeProps.style to useResolvedStyle(nodeProps)
and adds OverrideDots on Margin and Padding, the conversion #45 applied
to the other panels. NavStylePanel has no style-based text-alignment
control (Menu alignment is a top-level prop), so no dot there.
Refs #45
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- GuidedStyles classifies Opening Hours / Call Bar / Reviews / Booking to
BusinessStylePanel (#60) and shows a Regenerate button in the type
header, hidden when edit.regenerate-section's when() is false (#58)
- LayersPanel TYPE_ICONS for the four business blocks (FA 4.7) (#60)
- Context menu entries 'Save as section…' and 'Regenerate this section'
invoke site.save-as-section / edit.regenerate-section (#52, #58)
- commands: useCommandState(id) hook (registered/enabled/run)
- Layers: keep dropEffect 'move' on refused targets so drop fires and the
refusal notice shows; renaming to the default name stores no
custom.label (#47)
Refs #60
Refs #58
Refs #52
Refs #47
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Icon button with tooltip 'Search (Ctrl/Cmd+K)' calls openCommandPalette()
- Preview passes fontScanHtml: composedBody and trims metaTitle so the
Preview head matches the published head
Refs #49
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Frontend half of the backend-followups head parity work:
- CSS reset gains html,body{overflow-x:clip} (no phantom h-scroll, sticky
navbar still works)
- buildFontsLink adds per-element preset fonts found in the page HTML
(fontScanHtml option, defaults to the body); own-key preset lookup so
names like 'constructor' behave like the backend's isset
- blank/whitespace design tokens fall back to the defaults
- description/ogTitle/twitterCard are trimmed
- history snapshot preview scans header+body+footer and trims metaTitle
Re-baselines the two TOKEN_VARS-move document hashes (the clip rule is
proven to be the only change) and pins the four complete heads from WHP
scripts/test-site-builder-head-parity.php.
Refs #54
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A page-scope Sitesmith replace with a master zone open loaded the AI page
onto the zone canvas. On Done, its first section became the new master and
the sync rewrote every instance on every page with it, orphaning all
overrides. replaceCurrentPage now returns false inside a master zone and
the apply path reports an error asking the user to click Done first.
Refs #57
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Make a top-level section global (context menu), place instances from the
Section library's new Global tab, edit the master once in its own zone
("Editing global section" banner, Done - update N instances) and every
instance on every page/header/footer is rebuilt from it. Instances are real
inlined subtrees (custom.global on the root, custom.gkey on every node), so
export and published output are unchanged static HTML.
- utils/global-sections.ts: pure model (createMaster, instantiate,
syncInstances, collectOverridesFromInstance, applyOverrides, detach,
findInstanceRoot, master zone round trip). Unchanged input returns the
identical string; sync keeps the instance root id and the instance's own
inner ids per gkey.
- PageContext: __global__:<id> zone (editGlobal / finishGlobal); leaving it
commits the master (rev + 1 only when it changed), syncs every stored
slot and applies the sync to the returned-to canvas as ONE undo step.
- GlobalSectionsContext: makeGlobal / insertInstance / detach / setOverride
/ resetOverride / remove (refused while used), one undo step each.
- Inline text edits inside an instance record an override (text + html)
via useGlobalOverrideCommit; GuidedStyles shows a Global section panel
(Edit master, Detach, content override fields) instead of style panels.
- RenderNode: Global badge, dashed outline, lock icon on inner nodes.
- buildSavePayload: a master zone on the canvas is never saved into a page.
Refs #57
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Registers site.download-zip (WHP mode only; shows in the ⋯ menu and the
palette) via ExportCommand, mounted in CanvasOverlays. useSiteExport POSTs
?action=export_zip with the CSRF header: a zip answer is saved through a
Blob + anchor click, anything else (JSON error, older backend, network
failure, a stream cut short) becomes an error notice.
Refs #42
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Token-based sections (21 total across four families) with tests covering
insert, export (no stray hex, alt on images, relative links) and tree shape.
Refs #52
Adds 17 token-based sections across contact, hours, gallery and local strips,
with a real-editor test covering insert, hex-free export, alt text and links.
Refs #52
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
URL/anchor/tel/mailto edits from LinkPicker now go through
continuousSetProp (key link:<i>:href); a page pick stays a discrete
step via discreteSetProp. source/pageId stripping and the followPages
mergeLinks re-merge remain inside the updater.
Refs #53
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Publish now runs client-side checks (image alt, meta description, title
length, H1, empty links, contrast, noindex landing, empty page) over the
current state and shows a grouped checklist with Fix links and
"Publish anyway". Adds ExportOptions.canonicalUrl -> <link rel="canonical">
after the favicon line in both Preview and export.
Refs #54
Adds a site.history command (WHP mode, only when history_list answers) opening
a modal of saves/publishes by day. Preview opens the snapshot re-exported with
its own design; Restore site / Restore one page load the snapshot as unsaved
state (history cleared, dirty marked) so the user still clicks Save.
Refs #51
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- utils/nav-links.ts: shared NavLink type (source/pageId/children),
pageHref, buildPageLinks, mergeLinks, adoptPageLinks, rewriteHeaderLinks
- PageData.nav {show, parentId, label} + updatePageNav; a nav-sync effect in
PageProvider materialises page-following links into the stored header
(or the live header nodes while it is being edited); string/identity
guards keep it a no-op when nothing changed
- Navbar/Menu: followPages prop (default false; the default header seed is
true), one-level dropdowns (hover/focus-within + inline click toggle,
indented in the hamburger layout), hover/CTA colours default to the
primary token
- NavStylePanel: Follow pages toggle (adopts matching manual links, keeps
custom text as the page's menu label), read-only page rows with Hide from
menu, sub-link editor for manual links, LinkPicker records pageId
- Page Settings: Show in menu / Menu label / Nest under; Pages panel badge
- useWhpApi: nav passes through pages_craft_state on save and load
Legacy navs (no followPages) are never rewritten.
Refs #53
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Edge handles on a selected Section/Container/Columns write padding via the
per-device setter as one undo burst; Alt = opposite side, Shift = all four,
Ctrl/Cmd snaps to 8px, Escape cancels.
Refs #56
SectionType, Social, Pricing, Form and the generic props editor already
wrote tablet/mobile overrides through the device-aware useNodeProp setters
but still read nodeProps.style, so a mobile padding/margin/alignment edit
looked like a no-op and had no reset dot. They now read useResolvedStyle()
and show OverrideDots on Padding, Margin and Text Alignment.
NavStylePanel has the same gap but is outside this task's files.
Refs #45
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5-step profile wizard (name/type, services, area, tone, palette preview) saved to
extras.sitesmithProfile and sent with every Sitesmith request. Regenerate this
section rewrites the selected block for the business as a single undo step.
Refs #58
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Search + type chips in the Assets panel and picker library; Stock photos
entry in the picker (feature-detected, hidden when stock_disabled/absent);
bgPosition focal point for Hero and Background Section.
Refs #55
Catalog skeleton with per-family files (one seed each for hero, services,
contact), live token-styled thumbnails, insert-after-selection, and
save-as-section into project extras.
Refs #52
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
On tablet/mobile the useNodeProp style setters now route responsive keys
(spacing, font size, line height, letter spacing, alignment, size, flex
layout) into styleTablet/styleMobile; colours and other keys still write
style. An empty value removes the override. Continuous-burst keys include
the device so a desktop drag and a mobile drag never merge.
- shared.tsx: device-aware setters, new setResponsiveStyleProp /
clearResponsiveStyleProp exports, setPropStylesContinuous
- responsive-panel.tsx: useResolvedStyle, useOverrideState, DeviceBanner
- OverrideDot: filled dot resets the override (one undo step), hollow dot
marks an inherited value; wired into Text/Button/Container/Image/Media/
BackgroundSection panels and both box-model sections
- Columns: "Stack on this device" toggle (flexDirection override)
- GuidedStyles: device banner under the type header
- RenderNode: every component node renders through one stable
ResolvedRender so gaining/losing an override or switching device no
longer remounts the node (the remount dropped inline-edit focus/caret
and blanked a selected Heading)
Refs #45