feat: Design tab, live style editing, route-aware artboards, canvas onboarding

Design tab (4-tab inspector: Design / Props / Diff / Graph)
- Click any component in a live artboard to inspect computed CSS
- Typography, Layout, Visual sections auto-populated from getComputedStyle
- Every property is inline-editable; changes apply live via PATCH_ELEMENT_STYLE
- Zero source-code modifications — purely in-browser style overrides

Protocol additions (packages/renderer)
- REQUEST_ELEMENT_STYLES / ELEMENT_STYLES round-trip for CSS inspection
- PATCH_ELEMENT_STYLE for live style patching

Live SDK (packages/live-sdk)
- respondWithStyles(): reads 30 curated computed CSS properties
- patchElementStyle(): applies inline style override to fiber's DOM element

Canvas store
- selectedComponentStyles: current element's CSS map
- styleEditEvent mailbox: Design tab → LiveArtboard style dispatch (Zustand v5 compatible)

Route-aware artboards
- Each artboard now has an optional route (e.g. /dashboard)
- buildSrc(base, route) helper in Artboard.tsx
- Route field editable in Inspector Props tab, persisted to metadata_jsonb

On-canvas onboarding
- Empty canvas now shows 3-step guide: press A → CLI command → paste URL
- CLI command shown inline in a highlighted code block

Changelog and Handoff updated

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
SinachPat
2026-04-30 22:04:37 +01:00
co-authored by Claude Sonnet 4.6
parent 8514ffdc1a
commit edada3091e
10 changed files with 669 additions and 35 deletions
+3
View File
@@ -9,6 +9,8 @@ export interface CanvasArtboard {
width: number;
height: number;
renderUrl?: string;
/** Route path appended to renderUrl when rendering a specific screen, e.g. "/dashboard". */
route?: string;
}
@@ -21,6 +23,7 @@ function toCanvasArtboard(ab: Artboard): CanvasArtboard | null {
if (x === null || y === null || width === null || height === null) return null;
const base: CanvasArtboard = { id: ab.id, label: ab.name, x, y, width, height };
if (typeof meta['renderUrl'] === 'string') base.renderUrl = meta['renderUrl'];
if (typeof meta['route'] === 'string' && meta['route']) base.route = meta['route'];
return base;
}