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>
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import React, { useEffect, useCallback, useRef } from 'react';
|
||||
import { useEditor } from '@craftjs/core';
|
||||
import { findDeletableTarget } from '../../utils/craft-helpers';
|
||||
import { useSitesmithModal } from '../../state/SitesmithContext';
|
||||
import { buildSitesmithTarget } from '../../utils/sitesmith-target';
|
||||
|
||||
interface ContextMenuProps {
|
||||
visible: boolean;
|
||||
@@ -27,6 +29,7 @@ export const ContextMenu: React.FC<ContextMenuProps> = ({
|
||||
onClose,
|
||||
}) => {
|
||||
const { actions, query } = useEditor();
|
||||
const { open: openSitesmith } = useSitesmithModal();
|
||||
const menuRef = useRef<HTMLDivElement>(null);
|
||||
const clipboardRef = useRef<string | null>(null);
|
||||
|
||||
@@ -143,6 +146,17 @@ export const ContextMenu: React.FC<ContextMenuProps> = ({
|
||||
onClose();
|
||||
}, [nodeId, actions, getParentId, onClose]);
|
||||
|
||||
const askSitesmith = useCallback(() => {
|
||||
if (!nodeId || nodeId === 'ROOT') return;
|
||||
try {
|
||||
const target = buildSitesmithTarget(query, nodeId);
|
||||
if (target) openSitesmith(target);
|
||||
} catch (e) {
|
||||
console.error('Ask Sitesmith failed:', e);
|
||||
}
|
||||
onClose();
|
||||
}, [nodeId, query, openSitesmith, onClose]);
|
||||
|
||||
const deleteNode = useCallback(() => {
|
||||
const target = findDeletableTarget(query, nodeId);
|
||||
if (!target) {
|
||||
@@ -162,6 +176,12 @@ export const ContextMenu: React.FC<ContextMenuProps> = ({
|
||||
const isRoot = nodeId === 'ROOT' || !nodeId;
|
||||
|
||||
const items: MenuItem[] = [
|
||||
{
|
||||
label: '✨ Ask Sitesmith',
|
||||
action: askSitesmith,
|
||||
disabled: isRoot,
|
||||
dividerAfter: true,
|
||||
},
|
||||
{
|
||||
label: 'Duplicate',
|
||||
shortcut: 'Ctrl+D',
|
||||
|
||||
Reference in New Issue
Block a user