* feat(code): isolate Design JSX execution - Transform JSX with the existing Sucrase dependency and execute it in a disposable opaque-origin iframe worker - Block ambient network capabilities and enforce source, timeout, output, depth, and element limits - Validate that only bounded plain structured data returns to the application * feat(code): add editable Design JSX - Add a lazy CodeMirror editor with JSX syntax support, completion, diagnostics, and bounded scrolling - Convert validated sandbox output into trusted Design JSX helpers before rendering - Apply or insert JSX as one undoable graph transaction while preserving dirty drafts * feat(code): localize JSX editor actions - Add translated-message fallbacks for editing, applying, inserting, and draft state - Document the editable JSX workflow in the unreleased changelog * fix(code): satisfy typed sandbox validation - Keep the sandbox document terminator literal and preserve optional selection handling under type-aware lint * test(code): resolve sandbox probes through app aliases * fix(code): bound sandbox results before cloning - Enforce string, array, object, depth, element, and byte limits inside the disposable worker - Retain host-side validation as a second structured-data boundary * fix(code): keep JSX and HTML editing modes separate - Switch generated Tailwind output back to OpenPencil JSX before editing - Close the JSX editor when opening HTML/CSS import and avoid stacking both editors * feat(code): support authored Design JSX programs - Allow local constants, function components, arrays, conditionals, fragments, and multiple roots - Preserve replacement positions for multiple selected roots and reject mixed-parent or locked selections - Cover multi-root undo, redo, and all-or-nothing rollback * feat(code): add explicit JSX view mode - Let authors leave CodeMirror without applying a draft - Keep the editable surface and HTML/CSS importer mutually exclusive * feat(code): diagnose unknown JSX vocabulary - Warn on OpenPencil elements and properties that are absent from the canonical schema - Surface diagnostics inline through CodeMirror lint markers * test(code): accept WebKit isolation diagnostics - Cover the sandbox architecture against Playwright WebKit - Accept engine-specific wording while preserving the same unavailable-window assertion * refactor(code): consolidate Design JSX vocabulary - Drive renderer warnings, completion, and diagnostics from one supported-property schema - Recognize locally declared components and add focused schema and transform tests - Replace CodeMirror basicSetup with an explicit feature set and remove the umbrella package * fix(code): support Design JSX variable helpers * refactor(code): unify JSX sandbox validation * fix(code): account for complete sandbox output * fix(code): preserve locked JSX descendants * fix(code): preserve JSX sibling order * fix(code): recompute JSX parent layouts * feat(code): add live code previews * test(code): centralize graph assertions * fix(code): harden live preview sessions * docs: describe live code editing * fix(code): update editor accessibility labels * fix(code): use theme-aware error colors * fix(dev): stop Vite disconnect error loops * fix(code): clarify live preview status * fix(ui): avoid tooltip attribute warnings * test(code): assert semantic preview status * refactor(code): remove obsolete editor messages * fix(code): harden preview concurrency and isolation * fix(code): cancel stale reset previews
74 lines
2.4 KiB
TypeScript
74 lines
2.4 KiB
TypeScript
import type { Page } from '@playwright/test'
|
|
|
|
export type CodeNodeSummary = {
|
|
type: string
|
|
name: string
|
|
text: string
|
|
}
|
|
|
|
export function getFirstSelectedNodeId(page: Page): Promise<string | null> {
|
|
return page.evaluate(() => {
|
|
const store = window.openPencil?.getStore?.()
|
|
if (!store) throw new Error('OpenPencil store not initialized')
|
|
return [...store.state.selectedIds][0] ?? null
|
|
})
|
|
}
|
|
|
|
export function hasNode(page: Page, id: string | null): Promise<boolean> {
|
|
return page.evaluate((nodeId) => {
|
|
const store = window.openPencil?.getStore?.()
|
|
if (!store) throw new Error('OpenPencil store not initialized')
|
|
return nodeId !== null && store.graph.getNode(nodeId) !== undefined
|
|
}, id)
|
|
}
|
|
|
|
export function hasNodeNamed(page: Page, name: string): Promise<boolean> {
|
|
return page.evaluate((nodeName) => {
|
|
const store = window.openPencil?.getStore?.()
|
|
if (!store) throw new Error('OpenPencil store not initialized')
|
|
return [...store.graph.getAllNodes()].some((node) => node.name === nodeName)
|
|
}, name)
|
|
}
|
|
|
|
export async function waitForNode(page: Page, id: string | null): Promise<void> {
|
|
await page.waitForFunction((nodeId) => {
|
|
const store = window.openPencil?.getStore?.()
|
|
return nodeId !== null && store?.graph.getNode(nodeId) !== undefined
|
|
}, id)
|
|
}
|
|
|
|
export async function waitForNodeNamed(page: Page, name: string): Promise<void> {
|
|
await page.waitForFunction((nodeName) => {
|
|
const store = window.openPencil?.getStore?.()
|
|
return [...(store?.graph.getAllNodes() ?? [])].some((node) => node.name === nodeName)
|
|
}, name)
|
|
}
|
|
|
|
export function getCodeNodeSummaries(page: Page): Promise<CodeNodeSummary[]> {
|
|
return page.evaluate(() => {
|
|
const store = window.openPencil?.getStore?.()
|
|
if (!store) throw new Error('OpenPencil store not initialized')
|
|
return [...store.graph.getAllNodes()].map((node) => ({
|
|
type: node.type,
|
|
name: node.name,
|
|
text: node.text
|
|
}))
|
|
})
|
|
}
|
|
|
|
export function getUndoLabel(page: Page): Promise<string | null> {
|
|
return page.evaluate(() => {
|
|
const store = window.openPencil?.getStore?.()
|
|
if (!store) throw new Error('OpenPencil store not initialized')
|
|
return store.undo.undoLabel
|
|
})
|
|
}
|
|
|
|
export function getMobileDrawerSnap(page: Page): Promise<string> {
|
|
return page.evaluate(() => {
|
|
const store = window.openPencil?.getStore?.()
|
|
if (!store) throw new Error('OpenPencil store not initialized')
|
|
return store.state.mobileDrawerSnap
|
|
})
|
|
}
|