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:
@@ -0,0 +1,55 @@
|
||||
import React, { createContext, useCallback, useContext, useMemo, useState } from 'react';
|
||||
|
||||
/**
|
||||
* Optional target for a Sitesmith chat session. When set, the modal renders a
|
||||
* "Editing X" banner and the chat input is biased toward modifying just that
|
||||
* subtree — the user's prompt is augmented server-side with the node's
|
||||
* serialized tree, and the AI is instructed to return a `patch` op (typically
|
||||
* `replace_node`) rather than a full-site replace.
|
||||
*/
|
||||
export interface SitesmithTarget {
|
||||
/** Craft.js node id, used to find the node when applying the patch. */
|
||||
nodeId: string;
|
||||
/** Human-readable component name, shown in the modal header. */
|
||||
displayName: string;
|
||||
/** The component's serialized subtree (used to build a usable AI prompt). */
|
||||
treeJson: string;
|
||||
}
|
||||
|
||||
interface SitesmithContextValue {
|
||||
isOpen: boolean;
|
||||
target: SitesmithTarget | null;
|
||||
open: (target?: SitesmithTarget) => void;
|
||||
close: () => void;
|
||||
}
|
||||
|
||||
const SitesmithCtx = createContext<SitesmithContextValue>({
|
||||
isOpen: false,
|
||||
target: null,
|
||||
open: () => {},
|
||||
close: () => {},
|
||||
});
|
||||
|
||||
export const useSitesmithModal = () => useContext(SitesmithCtx);
|
||||
|
||||
export const SitesmithProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [target, setTarget] = useState<SitesmithTarget | null>(null);
|
||||
|
||||
const open = useCallback((t?: SitesmithTarget) => {
|
||||
setTarget(t ?? null);
|
||||
setIsOpen(true);
|
||||
}, []);
|
||||
|
||||
const close = useCallback(() => {
|
||||
setIsOpen(false);
|
||||
setTarget(null);
|
||||
}, []);
|
||||
|
||||
const value = useMemo<SitesmithContextValue>(
|
||||
() => ({ isOpen, target, open, close }),
|
||||
[isOpen, target, open, close],
|
||||
);
|
||||
|
||||
return <SitesmithCtx.Provider value={value}>{children}</SitesmithCtx.Provider>;
|
||||
};
|
||||
Reference in New Issue
Block a user