Commit Graph
317 Commits
Author SHA1 Message Date
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
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