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
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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
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>
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>