Sitesmith: AI site builder addon (frontend) #1

Merged
jknapp merged 10 commits from sitesmith-ai-builder into main 2026-05-24 17:11:03 +00:00
Owner

Summary

Frontend half of Sitesmith, the new AI site builder addon. Adds a chat modal to the Craft.js editor that takes a natural-language prompt and applies the AI's response (a replace, patch, or ask) to the editor state. Backed by the WHP-side PR: cloud-hosting-platform/whp#18

Spec / plan are in the WHP repo:

  • docs/superpowers/specs/2026-05-23-sitesmith-ai-builder-design.md
  • docs/superpowers/plans/2026-05-23-sitesmith-ai-builder.md

What's in this PR

  • HtmlBlock hardened with DOMPurify (closes pre-existing XSS hole; the previous component rendered unsanitized markup directly into the DOM). Iframes restricted to YouTube/Vimeo/Maps; <script>, <form>, <style>, on* event handlers all stripped.
  • Vitest added as the unit-test framework (Playwright continues for E2E)
  • canvas-summary.ts — compresses current canvas state to a ~1.5K-token textual summary the AI reads each turn (excludes style props, asset URLs, opaque HtmlBlock contents)
  • TypeScript types (src/types/sitesmith.ts): SitesmithStatus, SitesmithSummary, SitesmithMessage, SitesmithResponse (discriminated union over replace/patch/ask), SitesmithPatchOp (5-variant union), SerializedTreeNode
  • useSitesmith hook — entitlement, history, send (adapts to the existing useEditorConfig().whpConfig shape)
  • apply-ai-response.ts — applies all 5 patch ops (update_props / replace_node / insert_after / insert_before / delete_node) plus full replace flows for scope=site, page, section. Includes serializeTreeForCraft to convert AI's nested tree into the Craft.js flat-nodes shape, and findNodeIdByAiNodeId for stable AI-handle → Craft-id resolution.
  • PageContext extended with 4 new methods: replaceAllPages, replaceCurrentPage, setHeader, setFooter (mirrors existing setHeaderCraftState / setFooterCraftState / setPagesCraftState pattern)
  • Sitesmith UI panel (src/panels/sitesmith/):
    • SitesmithButton.tsx — topbar button with locked/capped visual states
    • SitesmithModal.tsx — full-screen modal orchestrating chat + apply
    • MessageList.tsx, ChatInput.tsx, UpgradeBanner.tsx, ScopeConfirmDialog.tsx
  • TopBar mounts the button + modal
  • LayersPanel prefers props.aiName when present (falls back to existing displayName for non-AI nodes)
  • Playwright E2E suite (tests/sitesmith.spec.ts): 4 scenarios — locked, capped, bonus-credit override, full build+patch (preserves manual edit across turns)

Backward compatibility

  • The HtmlBlock DOMPurify change is the one behavior shift: existing customer content that included <script> / event handlers in HtmlBlock will be stripped on render. Discussed with operator — only one test site uses HtmlBlock at the moment, no impact.
  • All other changes are pure additions to the editor — new panels, new buttons, new utilities, new tests.
  • Bundle grew from ~460KB to ~786KB JS (DOMPurify + Sitesmith UI). Within reason for a feature this size.

Test plan

  • npm run test:unit — 17 unit tests pass:
    • 5 DOMPurify sanitizer tests (script tags, event handlers, javascript: URLs, YouTube allow, form/input strip)
    • 3 canvas-summary tests (line-per-node, excludes style, truncation)
    • 9 apply-ai-response tests (tree serialize, ID generation, node finder)
  • npx tsc --noEmit — clean
  • npm run build — bundle succeeds (786KB JS / 20KB CSS)

Follow-up (Task 23 — needs staging deploy + test user creation):

  • Build dist via npm run build and copy to /docker/whp/web/site-builder/ on staging
  • Create 4 staging test users via WHP admin: sitesmith_disabled, sitesmith_enabled, sitesmith_capped, sitesmith_bonus — each owning one site
  • Set entitlements per the SQL in tests/sitesmith.spec.ts header comment
  • PLAYWRIGHT_BASE_URL=https://192.168.1.105:8080 SITESMITH_TEST_PASSWORD=... npm run test:e2e:sitesmith
  • Manual E2E checklist (in WHP-PR plan Task 23 Step 4)

Co-Authored-By

🤖 Generated with Claude Code

## Summary Frontend half of **Sitesmith**, the new AI site builder addon. Adds a chat modal to the Craft.js editor that takes a natural-language prompt and applies the AI's response (a `replace`, `patch`, or `ask`) to the editor state. Backed by the WHP-side PR: https://repo.anhonesthost.net/cloud-hosting-platform/whp/pulls/18 **Spec / plan are in the WHP repo:** - `docs/superpowers/specs/2026-05-23-sitesmith-ai-builder-design.md` - `docs/superpowers/plans/2026-05-23-sitesmith-ai-builder.md` ## What's in this PR - **HtmlBlock hardened with DOMPurify** (closes pre-existing XSS hole; the previous component rendered unsanitized markup directly into the DOM). Iframes restricted to YouTube/Vimeo/Maps; `<script>`, `<form>`, `<style>`, `on*` event handlers all stripped. - **Vitest added** as the unit-test framework (Playwright continues for E2E) - **`canvas-summary.ts`** — compresses current canvas state to a ~1.5K-token textual summary the AI reads each turn (excludes style props, asset URLs, opaque HtmlBlock contents) - **TypeScript types** (`src/types/sitesmith.ts`): `SitesmithStatus`, `SitesmithSummary`, `SitesmithMessage`, `SitesmithResponse` (discriminated union over replace/patch/ask), `SitesmithPatchOp` (5-variant union), `SerializedTreeNode` - **`useSitesmith` hook** — entitlement, history, send (adapts to the existing `useEditorConfig().whpConfig` shape) - **`apply-ai-response.ts`** — applies all 5 patch ops (update_props / replace_node / insert_after / insert_before / delete_node) plus full `replace` flows for scope=site, page, section. Includes `serializeTreeForCraft` to convert AI's nested tree into the Craft.js flat-nodes shape, and `findNodeIdByAiNodeId` for stable AI-handle → Craft-id resolution. - **PageContext extended** with 4 new methods: `replaceAllPages`, `replaceCurrentPage`, `setHeader`, `setFooter` (mirrors existing `setHeaderCraftState` / `setFooterCraftState` / `setPagesCraftState` pattern) - **Sitesmith UI panel** (`src/panels/sitesmith/`): - `SitesmithButton.tsx` — topbar button with locked/capped visual states - `SitesmithModal.tsx` — full-screen modal orchestrating chat + apply - `MessageList.tsx`, `ChatInput.tsx`, `UpgradeBanner.tsx`, `ScopeConfirmDialog.tsx` - **TopBar mounts the button + modal** - **LayersPanel prefers `props.aiName`** when present (falls back to existing `displayName` for non-AI nodes) - **Playwright E2E suite** (`tests/sitesmith.spec.ts`): 4 scenarios — locked, capped, bonus-credit override, full build+patch (preserves manual edit across turns) ## Backward compatibility - The HtmlBlock DOMPurify change is the one behavior shift: existing customer content that included `<script>` / event handlers in HtmlBlock will be stripped on render. Discussed with operator — only one test site uses HtmlBlock at the moment, no impact. - All other changes are pure additions to the editor — new panels, new buttons, new utilities, new tests. - Bundle grew from ~460KB to ~786KB JS (DOMPurify + Sitesmith UI). Within reason for a feature this size. ## Test plan - [x] `npm run test:unit` — 17 unit tests pass: - 5 DOMPurify sanitizer tests (script tags, event handlers, javascript: URLs, YouTube allow, form/input strip) - 3 canvas-summary tests (line-per-node, excludes style, truncation) - 9 apply-ai-response tests (tree serialize, ID generation, node finder) - [x] `npx tsc --noEmit` — clean - [x] `npm run build` — bundle succeeds (786KB JS / 20KB CSS) Follow-up (Task 23 — needs staging deploy + test user creation): - [ ] Build dist via `npm run build` and copy to `/docker/whp/web/site-builder/` on staging - [ ] Create 4 staging test users via WHP admin: `sitesmith_disabled`, `sitesmith_enabled`, `sitesmith_capped`, `sitesmith_bonus` — each owning one site - [ ] Set entitlements per the SQL in `tests/sitesmith.spec.ts` header comment - [ ] `PLAYWRIGHT_BASE_URL=https://192.168.1.105:8080 SITESMITH_TEST_PASSWORD=... npm run test:e2e:sitesmith` - [ ] Manual E2E checklist (in WHP-PR plan Task 23 Step 4) ## Co-Authored-By 🤖 Generated with [Claude Code](https://claude.com/claude-code)
jknapp added 10 commits 2026-05-24 16:58:37 +00:00
Closes XSS hole in HtmlBlock by sanitizing user/AI-supplied markup
through DOMPurify before passing to dangerouslySetInnerHTML. Adds
Vitest + jsdom for unit testing with 5 passing tests covering script
stripping, on-event handler removal, javascript: URL blocking, iframe
allowlist, and form/input stripping.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add apply-ai-response.ts with serializeTreeForCraft, buildNodeTree, findNodeIdByAiNodeId,
and useApplyAiResponse hook covering replace (site/page/section), patch (5 ops), and ask.
Extend PageContext with replaceAllPages, replaceCurrentPage, setHeader, setFooter helpers
that mirror the existing actions.deserialize/loadState pattern.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
jknapp merged commit 5c5066c20b into main 2026-05-24 17:11:03 +00:00
jknapp deleted branch sitesmith-ai-builder 2026-05-24 17:11:03 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: cloud-hosting-platform/site-builder#1