Commit Graph
304 Commits
Author SHA1 Message Date
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
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
shadowdaoandClaude Opus 5.5 aae94b1da2 fix(canvas): keep the empty-container block popover inside the viewport
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>
2026-10-07 12:48:49 -07:00
shadowdaoandClaude Opus 5.5 822d43cf61 fix(stock): stale searches can't overwrite newer results; scope 404
- 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>
2026-10-07 12:48:18 -07:00
shadowdaoandClaude Opus 5.5 4bae531339 fix(templates): explain what the apply-design checkbox now means
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>
2026-10-07 12:47:04 -07:00
shadowdaoandClaude Opus 5.5 d5bfb02911 fix(styles): NavStylePanel reads the per-device resolved style
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>
2026-10-07 12:46:16 -07:00
shadowdaoandClaude Opus 5.5 9697dc95a3 feat(editor): wire business panel, section commands and Layers follow-ups
- 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>
2026-10-07 12:45:16 -07:00
shadowdaoandClaude Sonnet 5.5 2877bc06fa feat(site-builder): tokenise colours hard-coded inside components
Component render/toHtml now use var(--wsb-*) instead of hex so Site Design
palette changes reach FeaturesGrid, Testimonials, Accordion, PricingTable,
CTA/CallToAction, forms, Gallery, Tabs and others. Exemption lists removed.

Refs #44

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-07 12:44:39 -07:00
shadowdaoandClaude Opus 5.5 ebf75ebfe0 feat(topbar): search button opens the command palette; Preview head parity
- 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>
2026-10-07 12:41:36 -07:00
shadowdaoandClaude Opus 5.5 db54341006 fix(export): Preview head parity with the published head
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>
2026-10-07 12:41:36 -07:00
shadowdao 8227d768e6 Merge issue-57 (Refs #57) 2026-10-07 12:37:19 -07:00
shadowdao 07eba1ba69 Merge issue-52-content-C (Refs #52) 2026-10-07 12:37:19 -07:00
shadowdao 7fb5215571 Merge issue-52-content-B (Refs #52) 2026-10-07 12:37:19 -07:00
shadowdao cb887bc02f Merge issue-52-content-A (Refs #52) 2026-10-07 12:37:19 -07:00
shadowdaoandClaude Opus 5.5 dcc7e27be4 fix(site-builder): refuse Sitesmith page replace while editing a global master
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>
2026-10-07 12:35:56 -07:00
shadowdaoandClaude Opus 5.5 dd8b99be06 feat(site-builder): reusable global sections with per-page overrides
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>
2026-10-07 12:28:53 -07:00
shadowdao e0c48fc9b6 Merge whp-zip-export frontend (Refs #42)
# Conflicts:
#	craft/src/editor/CanvasOverlays.tsx
2026-10-07 12:24:38 -07:00
shadowdaoandClaude Opus 5.5 97469dbabf feat(site-builder): "Download site (.zip)" command
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>
2026-10-07 12:15:44 -07:00
shadowdao 74dcbb003b feat(site-builder): add hero, services, about and cta sections to the library
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
2026-10-07 12:15:12 -07:00
shadowdao 761d2255f8 feat(site-builder): add social-proof, FAQ, pricing and team sections
Refs #52
2026-10-07 12:14:30 -07:00
shadowdaoandClaude Sonnet 5.5 a16a724048 feat(site-builder): contact, hours, gallery and local-business sections
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>
2026-10-07 12:13:05 -07:00
shadowdao 48061d37ae Merge issue-60 (Refs #60) 2026-10-07 12:09:09 -07:00
shadowdao edae0d6107 Merge issue-58-fe (Refs #58)
# Conflicts:
#	craft/src/editor/CanvasOverlays.tsx
#	craft/src/styles/editor.css
2026-10-07 12:09:09 -07:00
shadowdao 0ac420a23c Merge issue-55-fe (Refs #55)
# Conflicts:
#	craft/src/styles/editor.css
2026-10-07 12:09:09 -07:00
shadowdao 05c39961ce Merge issue-52-fe (Refs #52)
# Conflicts:
#	craft/src/editor/CanvasOverlays.tsx
#	craft/src/styles/editor.css
2026-10-07 12:09:09 -07:00
shadowdao 1d34d1b3b1 Merge issue-53 (Refs #53)
# Conflicts:
#	craft/src/styles/editor.css
2026-10-07 12:09:09 -07:00
shadowdaoandClaude Opus 5.5 4379ffac64 fix(site-builder): nav link href typing is one undo burst again
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>
2026-10-07 12:07:58 -07:00
shadowdao d39e1ecc31 Merge issue-56 (Refs #56)
# Conflicts:
#	craft/src/editor/CanvasOverlays.tsx
#	craft/src/styles/editor.css
2026-10-07 12:06:59 -07:00
shadowdao 0a59294fee Merge issue-54-fe (Refs #54)
# Conflicts:
#	craft/src/styles/editor.css
2026-10-07 12:06:59 -07:00
shadowdao 4a81d0b89f Merge issue-51-fe (Refs #51)
# Conflicts:
#	craft/src/editor/CanvasOverlays.tsx
#	craft/src/styles/editor.css
2026-10-07 12:06:59 -07:00
shadowdao 1eaf8e39fb Merge issue-49 (Refs #49)
# Conflicts:
#	craft/src/styles/editor.css
2026-10-07 12:06:59 -07:00
shadowdao eb87a39619 Merge issue-48 (Refs #48)
# Conflicts:
#	craft/src/styles/editor.css
2026-10-07 12:06:59 -07:00
shadowdao c0fd183c11 feat(site-builder): pre-publish checklist and canonical link
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
2026-10-07 12:05:28 -07:00
shadowdaoandClaude Sonnet 5.5 a9c8f4d65a feat(site-builder): version history modal with preview and restore
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>
2026-10-07 12:04:17 -07:00
shadowdaoandClaude Sonnet 5.5 0bd6b74ba5 feat(site-builder): copy/paste style and saved style presets
Refs #48

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-07 12:03:27 -07:00
shadowdaoandClaude Sonnet 5.5 a80667dfe6 feat(site-builder): multi-select with bulk styling and mixed-type actions
Shift/cmd/ctrl-click multi-select via DefaultEventHandlers, same-type bulk
styling as one undo step, mixed-type Delete/Duplicate/Move block, grouped
undo for array-aware edit.* commands.

Refs #50

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-07 12:03:26 -07:00
shadowdaoandClaude Opus 5.5 52477bff45 feat(site-builder): menus that follow pages, with dropdowns
- 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>
2026-10-07 12:03:16 -07:00
shadowdaoandClaude Sonnet 5.5 1512cb7645 feat(site-builder): Cmd+K command palette
Refs #49

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-07 12:03:07 -07:00
shadowdao 092c8f6747 feat(site-builder): drag padding handles on the canvas
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
2026-10-07 12:02:32 -07:00
shadowdao 5430f13877 Merge issue-45 (Refs #45)
# Conflicts:
#	craft/src/panels/right/styles/ButtonStylePanel.tsx
#	craft/src/panels/right/styles/shared.tsx
#	craft/src/styles/editor.css
2026-10-07 11:58:40 -07:00