Commit Graph
16 Commits
Author SHA1 Message Date
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 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 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 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
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
shadowdao c68e459430 feat(site-builder): complete shortcut set on the command registry with a cheat sheet
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
2026-10-07 11:47:55 -07:00
shadowdaoandClaude Opus 4.8 a698f014b0 feat(site-builder): page duplicate/reorder/set-landing + fix cross-page node copy/paste
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 07:36:00 -07:00
shadowdaoandClaude Opus 4.8 f00e4db3dc test(builder): cover useVisualViewport; fix: disable Select-Parent at ROOT in desktop context menu
- useVisualViewport.test.tsx: covers the visualViewport-undefined fallback,
  the keyboardInset math on a mocked visualViewport resize, and
  listener add/remove (resize + scroll) across mount/unmount.
- ContextMenu.tsx: Select Parent was only disabled at isRoot, so selecting
  a top-level section and choosing Select Parent silently landed on the
  un-editable ROOT (no outline, no toolbar) -- a dead end. Switched the
  guard to useNodeActions' canSelectParent (false whenever the node's
  parent is ROOT or missing), matching the mobile selection toolbar's
  existing identical guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 09:19:00 -07:00
shadowdaoandClaude Opus 4.8 2c8425ffb0 feat(builder): mobile-B touch editing -- selection toolbar, tap-to-add, swipe-dismiss
Phase B makes the Craft.js editor genuinely usable by touch on top of Phase
A's responsive shell, gated entirely behind useIsMobile()/<=768px:

- Extract useNodeActions(nodeId) out of ContextMenu.tsx (move/duplicate/
  delete/select-parent), shared by the desktop right-click menu (behavior
  unchanged) and the new mobile MobileSelectionToolbar.
- MobileSelectionToolbar: bottom-fixed selection toolbar (Move Up/Down,
  Duplicate, Select Parent, Edit Styles, two-tap Delete confirm), hidden
  while a sheet is open.
- BlocksPanel: tap-to-add on mobile (insert after selection, close sheet,
  select + scroll the new node into view); desktop drag/double-click
  unchanged.
- LayersPanel rows >=44px on mobile; HeadCodeModal portaled to document.body
  (same fix TemplateModal already had); BottomSheet gets swipe-to-dismiss
  and on-screen-keyboard clearance via a new useVisualViewportInsets hook.

Also fixes two pre-existing bugs surfaced only by driving a real Craft.js
document with Playwright touch input (masked by tests that mock
@craftjs/core): regenerateTreeIds structuredClone'd a live node's whole
data object, including the component function reference in data.type,
throwing DataCloneError and silently breaking Duplicate/Paste for every
node type; and an earlier useNodeActions draft cached canMoveUp/canMoveDown
inside a useEditor collector closed over nodeId, which goes stale for one
render whenever the selection changes without an unrelated store event.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 08:07:51 -07:00
shadowdaoandClaude Opus 4.8 473fe8d421 feat(builder): item 16 — context menu FA icon per action
Every menu item now has an FA icon (previously only "Ask Sitesmith" did,
leaving the rest visually misaligned): Duplicate fa-clone, Copy
fa-files-o, Paste fa-clipboard, Move Up/Down fa-arrow-up/down, Select
Parent fa-level-up, Delete fa-trash (kept its danger-red color). The
clipboard-group / structure-group separator already existed via
dividerAfter — no structural change needed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 20:56:21 -07:00
shadowdaoandClaude Opus 4.8 eeb0660d83 Replace emoji-as-icons in editor chrome with Font Awesome
Unicode emoji/glyphs (Sitesmith's sparkle, lock, close X) render as
tofu on systems without an emoji font. Swap for the FA4 glyphs the
rest of the chrome already uses:
- SitesmithButton/SitesmithModal: sparkle -> fa-magic, lock -> fa-lock
- ContextMenu "Ask Sitesmith" entry: sparkle -> fa-magic (via new
  optional MenuItem.icon field)
- TemplateModal/SitesmithModal close buttons, PagesPanel delete,
  AssetsPanel delete/cancel: &#10005; -> fa-times / fa-trash

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 20:14:29 -07:00
shadowdaoandClaude Opus 4.8 6421306849 feat(builder): implement Ctrl+C/V copy-paste shortcuts
The context menu advertised Ctrl+C/Ctrl+V hints but useKeyboardShortcuts
only handled undo/redo/delete/duplicate/escape -- pressing them did
nothing. Add a tiny shared module-level clipboard (src/hooks/clipboard.ts)
used by both the keyboard hook and the context menu so copying via one
entry point and pasting via the other stay consistent.

Ctrl/Cmd+C stores the selected node id (skipping ROOT). Ctrl/Cmd+V
inserts a copy as a sibling of the current selection via
regenerateTreeIds, mirroring the existing context-menu paste behavior.
Both respect the existing "disabled while typing" guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 15:01:30 -07:00
shadowdaoandClaude Opus 4.8 97123c4c58 fix(builder): regenerate node ids on duplicate/paste to prevent state corruption
Craft.js duplicate (ContextMenu + keyboard shortcut) and paste were reusing
the original node's toNodeTree() output verbatim, so addNodeTree() inserted
duplicate node ids into the editor tree. Added regenerateTreeIds() which
deep-clones a NodeTree and remaps rootNodeId, node map keys, node.id,
internal node.data.parent, node.data.nodes, and node.data.linkedNodes via
Craft.js's own getRandomId(). Also fixed pasteNode to insert as a sibling
of the right-clicked node (using its parent) instead of using a leaf node
as the new parent, which previously threw.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 12:18:44 -07:00
shadowdaoandClaude Opus 4.7 d0925d9e2d site-builder: dynamic CTAs, section anchors, edit-with-Sitesmith
Three related features:

1. Dynamic CTA buttons on HeroSimple, CTASection, CallToAction.
   New shared ctas[] array (text + href + variant + target) replaces the
   primary/secondary pair. Settings panel gets add/remove/reorder controls.
   Legacy fields stay readable for backwards compat — first user edit
   migrates the section onto the new array.

2. Anchor IDs on all layout/section components (Container, Section,
   BackgroundSection, ColumnLayout, plus 6 section blocks done by parallel
   subagent, plus Hero/CTA/CallToAction). Anchor input lives in the
   settings panel with an "auto from heading" button that walks the
   subtree for the first Heading.text. Renders as id="..." on the
   outermost element so #anchor URLs resolve.

3. Edit-with-Sitesmith targeted invocation. Right-click → "Ask Sitesmith"
   and a button at the top of the right-side settings panel both open the
   modal pre-targeted at the selected node. The node's serialized subtree
   is sent to the server; system prompt is augmented to require a patch
   with replace_node. Editor lifts modal state into a new SitesmithContext.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 12:43:28 -07:00
shadowdaoandClaude Opus 4.7 1558626b84 fix(delete): redirect Delete to owning component when target is empty linked node
Linked Craft.js nodes (column children of ColumnLayout, section-inner of
Section, etc.) are structurally non-deletable — actions.delete throws and
the error was silently swallowed. Empty layouts ended up undeletable from
the canvas because clicks always landed on the linked children that fill
the layout's visible area.

Adds findDeletableTarget(): when target is a linked node and ALL its
linked siblings are also empty (i.e., the layout itself is empty),
redirect deletion to the owning parent. Refuses to redirect when any
sibling has content, to protect against nuking a 3-col layout that has
content in other cols.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 20:42:17 -07:00
shadowdaoandClaude Opus 4.6 91a6b6f34b Add Craft.js site builder (v2) - complete rebuild from GrapesJS
Rebuilt the visual site builder from scratch using Craft.js, React 18,
and TypeScript. The new editor renders directly in the DOM (no iframe),
supports 40+ components, multi-page with shared header/footer, 16
templates, full-spectrum color/gradient controls, custom head code
injection, save/publish workflow, and auto-save.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:31:16 -07:00