Commit Graph
8 Commits
Author SHA1 Message Date
shadowdaoandClaude Opus 5.5 5f99df23a7 feat(site-builder): per-breakpoint style model, device context and responsive export
- utils/breakpoints.ts: single source for 767/768/991/992; head
  VISIBILITY/RESPONSIVE CSS built from it (flex-stacking rule moved from
  768 to the 767 mobile boundary).
- utils/responsive-style.ts: styleTablet/styleMobile partial overrides,
  cascade resolution, pure setters, scoped @media CSS per node.
- state/DeviceContext.tsx: real DeviceProvider (owns the one-time mobile
  forcing from EditorShell); TopBar, overflow menu, ReportIssueModal and
  Canvas read useDevice() instead of props.
- editor/RenderNode.tsx: resolves the effective style for the current
  device in one place and marks overridden nodes with data-wsb-overridden.
  Craft passes an opaque <DefaultRender/> to onRender, so the component
  element is rebuilt with the resolved style (cloneElement cannot work).
- utils/html-export.ts: hidden nodes skipped; overrides emitted as one
  body-level <style data-wsb-responsive> block + data-wsb-r scope attr;
  injectAttrs skips leading <style> elements.
- test-utils/editorHarness.tsx: optional onRender/wrap options.

Refs #45
Refs #37

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 11:28:44 -07:00
shadowdaoandClaude Opus 5.5 f8ac1fbe42 feat(site-builder): token module, canvas token variables, palette swatch row
Make var(--wsb-*) a first-class style value in the editor:

- utils/tokens.ts: TokenKey, TOKEN_VARS (moved from html-export.ts, same
  order/content as the backend), COLOR_TOKENS palette order, tokenRef,
  isTokenRef, tokenKeyOf, resolveTokenValue and buildCanvasTokenCss, which
  defines the 16 vars + token base CSS scoped to .canvas-device-frame
  (never :root) with every value through cssValue().
- editor/CanvasTokenStyle.tsx: <style data-wsb-canvas-tokens> driven by
  useSiteDesign(), mounted in App inside SiteDesignProvider, so design
  edits recolour token-backed values on the canvas instantly.
- panels/right/styles/TokenSwatchRow.tsx: palette-first swatch row that
  emits token references; .token-swatch styles appended to editor.css.
- html-export.ts imports TOKEN_VARS; export output pinned byte-identical
  to the pre-move baseline by hash in html-export.test.ts.

Refs #44

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 11:16:29 -07:00
shadowdaoandClaude Opus 4.8 5c44dd545c fix(site-builder): bounce stays visible + springier; image/video crop fills (cover) + resize shrinks footprint
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 16:04:50 -07:00
shadowdaoandClaude Opus 4.8 25dfcbb725 fix(site-builder): coerce bare-number animation delay to a valid CSS time (2 -> 2s)
data-animation-delay is stored as a plain seconds string (e.g. '2'); the reveal
script assigned it raw to el.style.animationDelay, which is invalid CSS and no-ops.
Suffix 's' onto bare numbers (leaving '2s'/'200ms' alone) so entrance-animation
delays actually apply. Backend generateCompiledHTML gets the byte-identical change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 12:34:51 -07:00
shadowdaoandClaude Opus 4.8 2dcc2b4d21 fix(site-builder): entrance-animation reveal script survives Preview + well-formed void-tag attrs + no-JS fallback
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 12:12:01 -07:00
shadowdaoandClaude Opus 4.8 0291ddce9a feat(site-builder): per-page SEO meta + favicon + design-token CSS-var wiring + published-output a11y/perf
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 07:14:56 -07:00
shadowdaoandClaude Opus 4.8 7ba91d9829 fix(builder): sanitize non-string style values + allowlist element tags (XSS)
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>
2026-07-12 17:44:59 -07:00
shadowdaoandClaude Opus 4.8 e5f30a4a56 Fix C1/C2: XSS via unsanitized style keys and animation attrs
C1: cssPropsToString emitted the camelToKebab'd style-object KEY
unsanitized while only sanitizing the VALUE. A malicious style key
containing a quote (reachable via AI update_props or deserialized
saved state) could close the style="..." attribute and inject a live
element. Now validates each key against a CSS property/custom-prop
allowlist and drops anything that doesn't match.

C2: buildDataAttrs (html-export.ts) interpolated props.animation and
props.animationDelay directly into data-animation="..."/
data-animation-delay="..." with no escaping, for every exported node.
Now routes both through escapeAttr.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 17:27:22 -07:00