- ColumnLayout: exposes style.alignItems on its flex ROW (aligns uneven
columns) -- render/toHtml already spread `style` onto the row div, so this
is a craft.props default + panel control addition, no structural change.
- Container/Section: root element is now unconditionally display:flex;
flex-direction:column (both editor render and toHtml), so the new
Vertical Alignment control maps to style.justifyContent, paired with a
Min Height (NumericUnitInput) control on style.minHeight. Default
justify-content/align-items reproduce ordinary block-flow stacking, so
this is a visual no-op for existing published content. Works in both
normal and "boxed" (contentWidth) modes -- the boxed inner wrapper's own
margin:0-auto horizontal centering is preserved via flex auto-margin
override semantics.
- ContainerStylePanel (serves Container/Section/Columns) distinguishes the
Columns case from Container/Section via nodeProps.columns/split presence
(no typeName plumbing needed) to pick align-items vs justify-content for
the shared Vertical Alignment control.
- All 3 owned components: added margin/padding (per-side)/border/box-shadow/
opacity style defaults + AnimationControl/VisibilityControl-backed
animation/animationDelay/hideOnDesktop/hideOnTablet/hideOnMobile props.
New containerBoxModel.tsx (package-local, not shared.tsx) DRYs the
box-model + border/effects + animation/visibility panel sections across
the single shared ContainerStylePanel, mirroring the sibling media
package's mediaBoxModel.tsx.
- Tests: extended all 3 *.toHtml.test.ts files (align-items/justify-content/
min-height emission, box-model style emission, craft.props presence).
673 tests green, tsc + vite build clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adversarial re-review found the C1 fix incomplete plus an adjacent
same-class XSS, both reachable via the AI update_props path and
deserialized saved state:
- cssPropsToString only ran sanitizeCssValue on typeof-string values, so a
non-string style value (array/object) with a valid key skipped
sanitization entirely and was template-coerced raw into style="...",
e.g. { color: ['red', '"><img src=x onerror=alert(1)>'] }. Now every
non-number value is coerced with String() and sanitized; numbers stay
raw. sanitizeBreakoutChars also now escapes < and > (previously only ;
and ") as defense-in-depth, since values can reach it from non-string
sources.
- props.tag (Container) and props.level (Heading) were interpolated raw
into the tag position of exported HTML (`<${tag}`, `<${level}`) with no
runtime validation, letting a malicious value break out of the tag
entirely. Both are now allowlisted/clamped against their known-safe sets
(div/section/article/header/footer/main; h1-h6), falling back to
div/h2. Applied in Container's live render + toHtml, Heading's live
render + toHtml, and the typeName==='div' fallback branch in
html-export.ts's renderNode (hit for unresolved/legacy node types).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Container.craft.props collected cssId/cssClass since before Phase E1 but
nothing rendered or exported them. Add live "CSS ID" / "CSS Class" text
inputs to ContainerStylePanel (guarded on nodeProps.cssId/cssClass
!== undefined) and emit id=/class= in both the editor render and toHtml.
cssId takes precedence over the existing anchorId prop when both are set
(only one id attribute can be emitted); anchorId is used as a fallback.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Each component defined a .craft.related.settings panel that was never
rendered -- the right panel renders only GuidedStyles (per-type
*StylePanel components), never .related.settings. Removed all dead
settings components across every component, their settings-only helpers
(including the dead uploadToWhp/showBrowser/handleBrowse asset-browse
blocks in Logo/Navbar/VideoBlock/FeaturesGrid, and CtasEditor in
_cta-helpers), and dropped the now-empty related keys. Render output,
.craft props/rules, and toHtml statics are unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace divergent, buggy local esc/escapeHtml helpers across 26 files with
imports from src/utils/escape (escapeHtml/escapeAttr). Attribute call sites use
escapeAttr, text-content sites use escapeHtml. Several toHtml outputs now
correctly escape & where old local escapers omitted it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
textAlign only affects inline content, so block children like ImageBlock
(display:block, width:100%) ignored it. Switch to flex-column with
align-items mapped from textAlign whenever alignment is set; layout is
unchanged when alignment is unset.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>