Sync specs & docs: AI chat, Code panel, JSX export

- Merge from master: 19 commits (AI chat + Code tab)
- Update specs: editor-ui (Properties panel → Design|Code|AI tabs,
  AI chat panel, model selector, 10 AI tools, DirectChatTransport,
  Code panel with JSX export), scene-graph (sceneNodeToJsx),
  testing (AI chat Playwright, 14 JSX export tests)
- Update docs: features (AI Chat + Code Panel sections, Properties
  Panel tabs), figma-comparison (AI tools 🟡, Dev Mode 🟡,
  Code snippets 🟡, 83/150), roadmap (Phase 5 AI + Code),
  keyboard-shortcuts (⌘J , ⌘B/I/U )
- Archive sync-ai-chat-code-tab change
This commit is contained in:
Anton A S 2026-03-01 12:33:57 +03:00
parent fbb397ad45
commit 15abce330f
14 changed files with 283 additions and 12 deletions

View file

@ -107,6 +107,9 @@ Core extraction, CLI, MCP server, design guidelines, screenshot verification loo
- .fig roundtrip tests with LFS fixtures (material3.fig 87K nodes, nuxtui.fig 314K nodes)
- .fig import O(n²) → O(n) fix (37s → 535ms on 87K nodes), ByteBuffer optimization
- test:coverage script
- AI chat: OpenRouter direct (no backend), Stronghold key storage, 10 tools (create_shape, set_fill, etc.), model selector, ⌘J toggle, streaming markdown, Playwright tests with mock transport
- Code panel: sceneNodeToJsx() export, Prism.js highlighting, line numbers, copy button, 14 tests
- Properties panel restructured: Design | Code | AI tabs
- npm publishing preparation for core and cli packages
**Planned:**

View file

@ -69,18 +69,22 @@ Both the layers panel and properties panel are resizable — drag the edge betwe
## Properties Panel
Context-sensitive panel with sections:
Tabbed interface with **Design** | **Code** | **AI** tabs (reka-ui Tabs).
The **Design** tab is context-sensitive with sections:
- **Appearance** — opacity, corner radius (uniform or per-corner with independent toggle), visibility
- **Fill** — solid/gradient/image type picker, gradient stop editor, hex input, opacity
- **Stroke** — color, weight, opacity, cap, join, dash pattern
- **Effects** — add/remove effects, type picker (drop shadow, inner shadow, layer blur, background blur, foreground blur), inline expanded controls (offset, blur, spread, color for shadows; blur radius for blurs), per-effect visibility toggle
- **Typography** — font family (FontPicker with virtual scroll and search), weight, size, alignment
- **Typography** — font family (FontPicker with virtual scroll and search), weight, size, alignment, B/I/U/S buttons
- **Layout** — auto-layout controls when enabled
- **Position** — alignment buttons, rotation, flip
- **Export** — scale, format (PNG/JPG/WEBP), live preview, multi-export
- **Page** — canvas background color (shown when no nodes selected)
The **Code** tab shows JSX export of the selection (see [Code Panel](#code-panel)). The **AI** tab provides an AI chat interface (see [AI Chat](#ai-chat)).
## Group/Ungroup
⌘G groups selected nodes. ⇧⌘G ungroups. Nodes are sorted by visual position when grouping to preserve reading order.
@ -189,7 +193,23 @@ Two rendering paths:
- `renderTreeNode()` — tree → scene graph (any runtime, no external deps)
- `renderJsx()` — JSX string → esbuild → tree → scene graph (CLI/headless)
Covered by 27 tests for all node types, layout props, effects, and nesting.
## AI Chat
Built-in AI assistant accessible via the AI tab in the properties panel or <kbd></kbd><kbd>J</kbd>. Communicates directly with OpenRouter from the browser — no backend server required. API key stored securely in Tauri Stronghold (localStorage fallback in browser).
**Model selector** with curated models: Claude, Gemini, GPT, DeepSeek, Qwen, Kimi, Llama — stored in `@open-pencil/core` constants with benchmark-ranked tags. Responses stream as markdown (vue-stream-markdown).
**10 AI tools** wired to the editor store with valibot schemas: `create_shape`, `set_fill`, `set_stroke`, `update_node`, `set_layout`, `delete_node`, `select_nodes`, `get_page_tree`, `get_selection`, `rename_node`. The ToolLoopAgent executes tools automatically in a loop. Tool calls display as collapsible timeline entries in the chat (reka-ui Collapsible).
Tested with Playwright using mock transport for CI.
## Code Panel
The Code tab in the properties panel shows the JSX representation of the current selection. Uses `sceneNodeToJsx()` from `@open-pencil/core` to convert the SceneNode subtree into JSX with Tailwind-like shorthand props. Prism.js syntax highlighting with line numbers and a copy-to-clipboard button. Multi-selection shows each node's JSX. The exported JSX is compatible with `renderJsx()` for round-trip creation.
## Code Quality

View file

@ -6,7 +6,7 @@ Feature-by-feature comparison of Figma Design capabilities with Open Pencil's cu
✅ Supported — feature works end-to-end · 🟡 Partial — core behavior exists, some sub-features missing · 🔲 Not yet implemented
:::
**Coverage:** 80 of 150 Figma feature items addressed — 64 ✅ fully supported, 16 🟡 partial, 70 🔲 not yet. Last updated: 2026-03-01.
**Coverage:** 83 of 150 Figma feature items addressed — 64 ✅ fully supported, 19 🟡 partial, 67 🔲 not yet. Last updated: 2026-03-01.
## Interface & Navigation
@ -27,7 +27,7 @@ Feature-by-feature comparison of Figma Design capabilities with Open Pencil's cu
| Layer outlines view | 🔲 | Wireframe view of all layers |
| Custom file thumbnails | 🔲 | Thumbnail generated on export, but no custom thumbnail picker |
| Nudge value settings | 🔲 | Default 1px/10px; Figma allows custom small/big nudge values |
| AI tools | 🔲 | Figma AI features (rename, search, generate); Open Pencil plans MCP-based AI in Phase 5 |
| AI tools | 🟡 | 10 AI tools via OpenRouter (create/modify/delete shapes, fills/strokes, layout); no AI-generated images or AI-powered search yet |
## Layers & Shapes
@ -204,9 +204,9 @@ Feature-by-feature comparison of Figma Design capabilities with Open Pencil's cu
| Real-time multiplayer | 🔲 | Planned (Yjs-based) |
| Cursor chat | 🔲 | Inline chat bubbles at cursor |
| Branching & merging | 🔲 | Version branches for design files |
| Dev Mode (inspect) | 🔲 | CSS/code generation from designs |
| Dev Mode (inspect) | 🟡 | Code tab shows JSX representation of selection; no CSS properties or handoff specs |
| Code Connect | 🔲 | Link design components to code |
| Code snippets | 🔲 | Auto-generated code from layers |
| Code snippets | 🟡 | JSX export with syntax highlighting and copy; no CSS/Swift/Kotlin snippets |
| Figma for VS Code | 🔲 | Editor plugin integration |
| MCP server | 🔲 | Planned for Phase 5; 117-tool MCP server from figma-use |
| CLI tools | 🟡 | Headless CLI: info, tree, find, export, analyze (colors/typography/spacing/clusters), node, pages, variables; MCP server not yet |

View file

@ -68,6 +68,7 @@ Full Figma-compatible shortcut map. ✅ = implemented.
| <kbd></kbd><kbd>0</kbd> | Zoom to 100% | ✅ |
| <kbd></kbd><kbd>1</kbd> | Zoom to Fit | 🔲 |
| <kbd></kbd><kbd>2</kbd> | Zoom to Selection | 🔲 |
| <kbd></kbd><kbd>J</kbd> | Toggle AI Chat | ✅ |
## Object
@ -96,9 +97,9 @@ Full Figma-compatible shortcut map. ✅ = implemented.
| Shortcut | Action | Status |
|----------|--------|--------|
| <kbd></kbd><kbd>B</kbd> | Bold | 🔲 |
| <kbd></kbd><kbd>I</kbd> | Italic | 🔲 |
| <kbd></kbd><kbd>U</kbd> | Underline | 🔲 |
| <kbd></kbd><kbd>B</kbd> | Bold | |
| <kbd></kbd><kbd>I</kbd> | Italic | |
| <kbd></kbd><kbd>U</kbd> | Underline | |
| <kbd></kbd><kbd></kbd><kbd>X</kbd> | Strikethrough | 🔲 |
## Canvas Interaction

View file

@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-03-01

View file

@ -0,0 +1,19 @@
# Design: Sync AI chat, Code tab
## Approach
Documentation-only. Update/create specs for AI chat and Code panel, propagate to VitePress docs and comparison matrix.
## Affected spec domains
1. **editor-ui** — Properties panel restructured to Design | Code | AI tabs, DesignPanel extraction, ChatPanel + sub-components, CodePanel, ⌘J shortcut
2. **scene-graph** (new requirement) — sceneNodeToJsx() export function
3. **testing** — Playwright chat tests with mock transport, 14 JSX export tests
## New spec domain consideration
AI chat could warrant its own spec domain, but it's tightly integrated into editor-ui (tab in properties panel, tools call editor store). Keep in editor-ui for now.
## VitePress docs updates
- features.md: add "AI Chat" section, add "Code Panel" section, update Properties Panel description
- figma-comparison.md: AI tools 🔲→🟡, Code snippets 🔲→🟡, Dev Mode 🔲→🟡
- roadmap.md: Phase 5 delivered items (AI chat, Code tab)
- keyboard-shortcuts.md: ⌘J (toggle AI chat)

View file

@ -0,0 +1,25 @@
# Proposal: Sync AI chat, Code tab
## Why
19 commits from master (6268d8b..c59d5fc) introduce AI chat integration and a Code panel for JSX export.
## What changed
### AI chat (5d23c3e → bf8636c, 15 commits)
- ChatPanel with message list, tool timeline, typing indicator, API key setup via Tauri Stronghold
- DirectChatTransport: no backend server, calls OpenRouter directly from browser
- ToolLoopAgent with 10 AI tools (valibot schemas): create_shape, set_fill, set_stroke, update_node, set_layout, delete_node, select_nodes, get_page_tree, get_selection, rename_node
- Model selector with curated list: Claude 4.6, Gemini 3.1, GPT-5.3, DeepSeek V3.2, Qwen 3.5, Kimi K2.5, Llama
- ⌘J keyboard shortcut to toggle chat
- Properties panel restructured: Design | Code | AI tabs (reka-ui Tabs)
- DesignPanel extracted from PropertiesPanel
- ChatMessage, ChatInput, APIKeySetup sub-components
- reka-ui ScrollArea/Collapsible/Tooltip, tw-animate-css, vue-stream-markdown for streaming
- Playwright tests with mock transport, tool call e2e test
- X-OpenRouter-Title header for app identification
### Code tab / JSX export (c59d5fc)
- sceneNodeToJsx() in @open-pencil/core: converts SceneNode subtree to JSX using builder syntax
- CodePanel.vue with Prism.js syntax highlighting, line numbers, copy button
- Third tab in properties panel
- 14 new tests covering shapes, text, layout, effects, multi-selection

View file

@ -0,0 +1,63 @@
# editor-ui Specification (delta)
## Modified Requirements
### Requirement: Properties panel with sections (MODIFIED)
The properties panel SHALL be organized as a tabbed interface with Design | Code | AI tabs (reka-ui Tabs). The Design tab contains the existing property sections (DesignPanel). The Code tab shows JSX export. The AI tab shows the chat interface.
#### Scenario: Switch to Code tab
- **WHEN** user clicks the Code tab in the properties panel
- **THEN** the JSX code representation of the selection is displayed
#### Scenario: Switch to AI tab
- **WHEN** user clicks the AI tab
- **THEN** the AI chat interface is displayed
## New Requirements
### Requirement: AI chat panel
The editor SHALL include an AI chat panel accessible via the AI tab in properties panel or ⌘J shortcut. The panel provides: message list with streaming markdown (vue-stream-markdown), tool call timeline with collapsible details (reka-ui Collapsible), typing indicator, API key setup for OpenRouter (stored in Tauri Stronghold). Messages scroll automatically (reka-ui ScrollArea).
#### Scenario: Send message
- **WHEN** user types a message and presses Enter in the chat input
- **THEN** the message is sent to OpenRouter and a streaming response appears
#### Scenario: Toggle chat
- **WHEN** user presses ⌘J
- **THEN** the properties panel switches to the AI tab (or back to Design)
### Requirement: AI model selector
The chat input SHALL include a model selector with curated models: Claude, Gemini, GPT, DeepSeek, Qwen, Kimi, Llama. Models are stored in @open-pencil/core constants with benchmark-ranked tags.
#### Scenario: Change model
- **WHEN** user selects a different model from the selector
- **THEN** subsequent messages use the selected model
### Requirement: AI tools
The chat SHALL support 10 design tools with valibot schemas: create_shape, set_fill, set_stroke, update_node, set_layout, delete_node, select_nodes, get_page_tree, get_selection, rename_node. Tools execute against the editor store. The ToolLoopAgent calls tools automatically in a loop.
#### Scenario: AI creates a shape
- **WHEN** user asks "Create a blue rectangle 200×100"
- **THEN** the AI calls create_shape with type RECTANGLE, width 200, height 100, and set_fill with blue color
#### Scenario: Tool call visible in chat
- **WHEN** the AI executes a tool
- **THEN** a collapsible tool call section shows the tool name, input, and output
### Requirement: DirectChatTransport
The chat SHALL communicate directly with OpenRouter from the browser — no backend server required. The API key is stored securely in Tauri Stronghold (or localStorage fallback in browser). X-OpenRouter-Title header identifies the app.
#### Scenario: No backend required
- **WHEN** user configures an OpenRouter API key and sends a message
- **THEN** the request goes directly to OpenRouter without a proxy server
### Requirement: Code panel with JSX export
The properties panel Code tab SHALL display the selected node(s) as JSX code using sceneNodeToJsx(). The panel provides Prism.js syntax highlighting, line numbers, and a copy-to-clipboard button. Multi-selection shows each node's JSX.
#### Scenario: View JSX for selection
- **WHEN** user selects a frame and clicks the Code tab
- **THEN** the JSX representation of the frame and its children is displayed with syntax highlighting
#### Scenario: Copy JSX
- **WHEN** user clicks the copy button in the Code panel
- **THEN** the JSX code is copied to the clipboard

View file

@ -0,0 +1,14 @@
# scene-graph Specification (delta)
## New Requirements
### Requirement: Scene node to JSX export
@open-pencil/core SHALL export a sceneNodeToJsx() function that converts a SceneNode subtree into JSX string using builder function syntax (Frame, Text, Rectangle, etc.) with Tailwind-like shorthand props. The output is valid JSX that can be consumed by renderJsx().
#### Scenario: Export frame with children
- **WHEN** sceneNodeToJsx is called on a frame with a rectangle and text child
- **THEN** a JSX string with Frame wrapping Rectangle and Text is returned
#### Scenario: Export with effects
- **WHEN** sceneNodeToJsx is called on a node with drop shadow
- **THEN** the JSX includes shadow props in the output

View file

@ -0,0 +1,17 @@
# testing Specification (delta)
## New Requirements
### Requirement: AI chat Playwright tests
The test suite SHALL include Playwright e2e tests for the AI chat panel with mock transport (no real LLM calls). Tests cover: sending a message, receiving a response, tool call display. Real LLM tests available via TEST_REAL_LLM=1 flag.
#### Scenario: Chat with mock transport
- **WHEN** a Playwright test sends a message via the chat input
- **THEN** the mock transport returns a response and the message appears in the chat
### Requirement: JSX export tests
The test suite SHALL include tests for sceneNodeToJsx() covering shapes, text, layout props, effects, and multi-selection. Currently 14 tests.
#### Scenario: Export rectangle to JSX
- **WHEN** sceneNodeToJsx is called on a rectangle with blue fill
- **THEN** the output includes Rectangle component with bg prop

View file

@ -0,0 +1,35 @@
# Tasks: Sync AI chat, Code tab
Reference: 19 commits from master (6268d8b..c59d5fc).
Delta-specs in `openspec/changes/sync-ai-chat-code-tab/specs/` (editor-ui, scene-graph, testing).
Proposal: `proposal.md`, Design: `design.md` — validated and aligned with delta-specs.
Section 1 (specs) MUST complete before Section 2 (docs) — docs reference merged spec content.
## 1. Merge Delta-Specs into Main Specs
Each task merges ADDED and MODIFIED requirements from the delta-spec into the baseline at `openspec/specs/<domain>/spec.md`. Untouched baseline requirements are preserved. No conflicts between domains.
- [x] 1.1 Update `openspec/specs/editor-ui/spec.md` — MODIFIED: "Properties panel" → Design | Code | AI tabs. ADDED: "AI chat panel" (message list, streaming markdown, tool timeline, API key, ⌘J), "AI model selector" (curated models in core constants), "AI tools" (10 tools with valibot, ToolLoopAgent), "DirectChatTransport" (no backend, OpenRouter, Stronghold), "Code panel with JSX export" (Prism.js, line numbers, copy)
- [x] 1.2 Update `openspec/specs/scene-graph/spec.md` — ADDED: "Scene node to JSX export" (sceneNodeToJsx function)
- [x] 1.3 Update `openspec/specs/testing/spec.md` — ADDED: "AI chat Playwright tests" (mock transport, tool call e2e), "JSX export tests" (14 tests)
## 2. Update VitePress Docs
All target files already exist — no sidebar/nav changes needed.
- [x] 2.1 Update `docs/guide/features.md`:
- **Modify "Properties Panel" bullet list**: add Code and AI tabs to description
- **Add "AI Chat" section** (after "Code Quality"): ⌘J toggle, OpenRouter direct (no backend), Stronghold key storage, model selector (Claude/Gemini/GPT/DeepSeek/Qwen/Kimi/Llama), 10 AI tools (create_shape, set_fill, etc.), streaming markdown, tool call timeline, Playwright tests with mock transport
- **Add "Code Panel" section** (after "AI Chat"): sceneNodeToJsx() in core, Prism.js highlighting, line numbers, copy button, Design | Code | AI tabs
- [x] 2.2 Update `docs/guide/figma-comparison.md` — specific row changes:
- "AI tools" 🔲→🟡: "10 AI tools via OpenRouter (create/modify/delete shapes, set fills/strokes, layout); no AI-generated content or AI search yet"
- "Code snippets" 🔲→🟡: "JSX export from selection with syntax highlighting and copy; no CSS/Swift/Kotlin snippets"
- "Dev Mode (inspect)" 🔲→🟡: "Code tab shows JSX representation; no CSS properties or handoff specs"
- No other rows change status. After: 64✅, 19🟡, 67🔲 = 150 total. Update coverage line
- [x] 2.3 Update `docs/development/roadmap.md` — Phase 5 Delivered: add AI chat (OpenRouter, 10 tools, model selector, Stronghold, ⌘J, Playwright tests), Code panel (sceneNodeToJsx, Prism.js, 14 tests)
- [x] 2.4 Update `docs/reference/keyboard-shortcuts.md` — add ⌘J (Toggle AI chat) with ✅ status
## 3. Verify
- [x] 3.1 Run `bun run docs:build` — verify build passes. Confirm: coverage count matches actual ✅/🟡/🔲 tallies via grep -c, no broken anchor warnings

View file

@ -48,8 +48,8 @@ The layers panel SHALL display a tree view of the document hierarchy using Reka
- **WHEN** user clicks the visibility icon next to a layer
- **THEN** the node is hidden/shown on canvas
### Requirement: Properties panel with sections
The properties panel SHALL be split into sections: Appearance (opacity, corner radius with independent mode, visibility toggle), Fill, Stroke, Effects, Typography, Layout, Position.
### Requirement: Properties panel with tabs
The properties panel SHALL be organized as a tabbed interface with Design | Code | AI tabs (reka-ui Tabs). The Design tab (DesignPanel) contains sections: Appearance (opacity, corner radius with independent mode, visibility toggle), Fill, Stroke, Effects, Typography, Layout, Position. The Code tab shows JSX export. The AI tab shows the chat interface.
#### Scenario: Properties panel shows fill section
- **WHEN** user selects a rectangle with a blue fill
@ -292,3 +292,50 @@ The TypographySection SHALL include B/I/U/S toggle buttons for Bold, Italic, Und
#### Scenario: Underline button with no selection
- **WHEN** user clicks the Underline button during editing with no selection
- **THEN** the entire text node's underline style toggles
### Requirement: AI chat panel
The editor SHALL include an AI chat panel accessible via the AI tab in properties panel or ⌘J shortcut. The panel provides: message list with streaming markdown (vue-stream-markdown), tool call timeline with collapsible details (reka-ui Collapsible), typing indicator, API key setup for OpenRouter (stored in Tauri Stronghold). Messages scroll automatically (reka-ui ScrollArea).
#### Scenario: Send message
- **WHEN** user types a message and presses Enter in the chat input
- **THEN** the message is sent to OpenRouter and a streaming response appears
#### Scenario: Toggle chat
- **WHEN** user presses ⌘J
- **THEN** the properties panel switches to the AI tab (or back to Design)
### Requirement: AI model selector
The chat input SHALL include a model selector with curated models: Claude, Gemini, GPT, DeepSeek, Qwen, Kimi, Llama. Models are stored in @open-pencil/core constants with benchmark-ranked tags.
#### Scenario: Change model
- **WHEN** user selects a different model from the selector
- **THEN** subsequent messages use the selected model
### Requirement: AI tools
The chat SHALL support 10 design tools with valibot schemas: create_shape, set_fill, set_stroke, update_node, set_layout, delete_node, select_nodes, get_page_tree, get_selection, rename_node. Tools execute against the editor store. The ToolLoopAgent calls tools automatically in a loop.
#### Scenario: AI creates a shape
- **WHEN** user asks "Create a blue rectangle 200×100"
- **THEN** the AI calls create_shape with type RECTANGLE, width 200, height 100, and set_fill with blue color
#### Scenario: Tool call visible in chat
- **WHEN** the AI executes a tool
- **THEN** a collapsible tool call section shows the tool name, input, and output
### Requirement: DirectChatTransport
The chat SHALL communicate directly with OpenRouter from the browser — no backend server required. The API key is stored securely in Tauri Stronghold (or localStorage fallback in browser). X-OpenRouter-Title header identifies the app.
#### Scenario: No backend required
- **WHEN** user configures an OpenRouter API key and sends a message
- **THEN** the request goes directly to OpenRouter without a proxy server
### Requirement: Code panel with JSX export
The properties panel Code tab SHALL display the selected node(s) as JSX code using sceneNodeToJsx(). The panel provides Prism.js syntax highlighting, line numbers, and a copy-to-clipboard button. Multi-selection shows each node's JSX.
#### Scenario: View JSX for selection
- **WHEN** user selects a frame and clicks the Code tab
- **THEN** the JSX representation of the frame and its children is displayed with syntax highlighting
#### Scenario: Copy JSX
- **WHEN** user clicks the copy button in the Code panel
- **THEN** the JSX code is copied to the clipboard

View file

@ -263,3 +263,14 @@ SceneNode SHALL support a `styleRuns` array of `{start, length, style}` where st
#### Scenario: Render JSX string
- **WHEN** renderJsx('<Frame w={100}><Text>Hi</Text></Frame>') is called
- **THEN** a scene graph with a FRAME parent and TEXT child is produced
### Requirement: Scene node to JSX export
@open-pencil/core SHALL export a sceneNodeToJsx() function that converts a SceneNode subtree into JSX string using builder function syntax (Frame, Text, Rectangle, etc.) with Tailwind-like shorthand props. The output is valid JSX consumable by renderJsx().
#### Scenario: Export frame with children
- **WHEN** sceneNodeToJsx is called on a frame with a rectangle and text child
- **THEN** a JSX string with Frame wrapping Rectangle and Text is returned
#### Scenario: Export with effects
- **WHEN** sceneNodeToJsx is called on a node with drop shadow
- **THEN** the JSX includes shadow props in the output

View file

@ -121,3 +121,17 @@ The test suite SHALL include tests for the JSX renderer covering all node types
#### Scenario: Render Frame with children
- **WHEN** a JSX tree with Frame containing Rectangle and Text is rendered
- **THEN** the scene graph contains correct parent-child relationships and properties
### Requirement: AI chat Playwright tests
The test suite SHALL include Playwright e2e tests for the AI chat panel with mock transport (no real LLM calls). Tests cover: sending a message, receiving a response, tool call display. Real LLM tests available via TEST_REAL_LLM=1 flag.
#### Scenario: Chat with mock transport
- **WHEN** a Playwright test sends a message via the chat input
- **THEN** the mock transport returns a response and the message appears in the chat
### Requirement: JSX export tests
The test suite SHALL include tests for sceneNodeToJsx() covering shapes, text, layout props, effects, and multi-selection. Currently 14 tests.
#### Scenario: Export rectangle to JSX
- **WHEN** sceneNodeToJsx is called on a rectangle with blue fill
- **THEN** the output includes Rectangle component with bg prop