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
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
Core editor shortcuts become registry commands run by the single
useCommandShortcuts dispatcher: undo/redo, delete, duplicate (after +
select), copy/paste, Alt+arrow move, 1/2/3 device, Cmd+S/Shift+P/P site
commands and Cmd+/ cheat sheet. Tab/Esc no longer fire from buttons or
dialogs. Context menu labels come from formatShortcut.
Refs #39
Select text inside a selected Heading or Text block and a floating
toolbar offers B / I / U / Link / Unlink (execCommand; mousedown is
prevented so the selection survives; mod+b/i/u handled on the editable).
Storage: new `html` prop holding sanitised inline HTML, with `text` kept
as the plain mirror for AI summaries, search and legacy content.
Unmarked edits store html '' so legacy nodes keep their shape. The
allowlist (b strong i em u a br span; style=color only, hex/rgb/
var(--wsb-*); href via DOMPurify URI check AND safeUrl, unsafe links
unwrapped; target only _blank with forced rel=noopener) runs on commit,
on canvas render and again at export. If `text` was changed without
`html` (Sitesmith update_props), the stale html is ignored.
Each edit commits once (blur, or deselect if no blur came first) as one
setProp = one undo step; the old blur+deselect double commit is gone.
Heading colour default now tokenRef('text'). Sitesmith canvas summary
skips `html`.
Refs #46
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Frame gets data-device; RenderNode mirrors hide flags and selection onto the
dom; editor.css emulates the exported breakpoints inside the narrowed frame.
Also: data-wsb-overridden counts only set values, responsive scope ids are
zone-prefixed (h-/b-/f-) so ROOT no longer collides, and comment delimiters
are stripped from responsive declarations.
Refs #37
Restyle Craft's insertion bar via the Editor indicator prop and mirror the
live placement onto the DOM (data-drop-target/-error/-empty, is-dragging).
Refs #43
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Colour controls offer the site palette first, show the resolved colour for
var(--wsb-*) values with a token chip, and keep the stored reference.
autoTextColor resolves tokens. Site Design gains six one-click palettes.
Refs #44
Make var(--wsb-*) a first-class style value in the editor:
- utils/tokens.ts: TokenKey, TOKEN_VARS (moved from html-export.ts, same
order/content as the backend), COLOR_TOKENS palette order, tokenRef,
isTokenRef, tokenKeyOf, resolveTokenValue and buildCanvasTokenCss, which
defines the 16 vars + token base CSS scoped to .canvas-device-frame
(never :root) with every value through cssValue().
- editor/CanvasTokenStyle.tsx: <style data-wsb-canvas-tokens> driven by
useSiteDesign(), mounted in App inside SiteDesignProvider, so design
edits recolour token-backed values on the canvas instantly.
- panels/right/styles/TokenSwatchRow.tsx: palette-first swatch row that
emits token references; .token-swatch styles appended to editor.css.
- html-export.ts imports TOKEN_VARS; export output pinned byte-identical
to the pre-move baseline by hash in html-export.test.ts.
Refs #44
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add an exported block registry (keywords, search, insertBlock) consumed by
BlocksPanel, a NoticeProvider/NoticeHost toast stack, a guarded localStorage
prefs helper, a ProjectExtras store persisted as project.json 'extras', and a
CanvasOverlays mount point.
Refs #38
Refs #41
Refs #49
Refs #48
Refs #57
Refs #58
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A manual Save during Publish sent a second save with the same base_rev; the
loser 409'd against its own tab (and could abort the publish).
- useWhpApi: every save runs through one in-flight promise (SaveGuard
saveInFlight). A save requested while one is out waits, then sends only
if still dirty, with the rev the first returned; payload is read from the
editor at send time. Forced (empty/overwrite) saves always send.
- Save buttons are disabled while publishing.
- Pill: a conflict reads 'Unsaved changes' (modal open) from Save and from
Publish alike; a never-saved site (load rev null) reads 'Not saved yet'.
- Autosave interval reads runSave through a ref, so editing can't keep
restarting it and starve autosave.
- Fix stale 'Throws like exportBodyHtml' comment.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Track dirty from Craft node changes and page/design/header/footer state, cleared
only by a successful save. Persistent Unsaved/Saving/Saved/Published/Save failed
pill, beforeunload prompt while dirty or saving, one persistent error notice,
resp.ok/non-JSON handling for save and publish, and publish refuses to run
after a failed save. Draft badge no longer flips on a no-op autosave.
Refs #30
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>