@flatkit/compiler 0.39.0 → 0.39.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -125,7 +125,7 @@ async function createRenderer(doc, opts = {}) {
125
125
  `);
126
126
  player.stepSim(n);
127
127
  }
128
- player.render();
128
+ if (frameOpts.script?.length) player.render();
129
129
  return el.toBuffer("png");
130
130
  },
131
131
  play,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/render.ts"],"sourcesContent":["// -----------------------------------------------------------------------------\n// render.ts -- HEADLESS PNG rendering of a Doc (skia-canvas backend), for `flatc --render`.\n//\n// Gives agents/CI an IMAGE of what they author (the \"blind positioning\" safeguard). We replay the\n// real `FlatPlayer` on a skia canvas: same paths, gradients, filters (glow/shadow via `ctx.filter`)\n// and SVG as the browser. skia-canvas is loaded through a DYNAMIC import with a NON-LITERAL specifier,\n// so it is never part of the public build graph -- install it on demand only when `--render` is used.\n// -----------------------------------------------------------------------------\nimport { FlatPlayer, type SendEvent } from '@flatkit/player'\nimport { createReplayer, type Gesture, type Handlers, type Replayer } from '@flatkit/player/debug'\nimport type { Doc, Item } from '@flatkit/types'\nimport { isGroup, isInstance } from '@flatkit/engine/layers'\nimport { mkdtempSync, writeFileSync, rmSync } from 'node:fs'\nimport { tmpdir } from 'node:os'\nimport { join } from 'node:path'\n\nexport type RenderOpts = { frame?: number; vars?: Record<string, number>; scale?: number; steps?: number; params?: Record<string, string>; script?: Gesture[]; settle?: number }\n\n/** What a replayed script reports: the `send`s it emitted, and the `expect` gestures that did not hold. */\nexport type PlayReport = { sends: SendEvent[]; expectFailures: string[]; warnings: string[] }\n\n/** Cap on `--steps` (anti-DoS: an untrusted doc must not freeze the render host). One step = 1/60 s of sim. */\nconst MAX_RENDER_STEPS = 10_000\n\n/** Minimal structural view of the `skia-canvas` surface we use. Kept local so the public build needs\n * no native binary; the real module is resolved at runtime only when `--render` runs. */\ninterface SkiaCanvas {\n Canvas: new (w: number, h: number) => HTMLCanvasElement & { toBuffer(fmt: string): Promise<Uint8Array> }\n loadImage: (src: string) => Promise<unknown>\n Path2D: typeof Path2D\n /** Browser global under Node: the mask/clip path builder does `new DOMMatrix([...])` (drawScene). */\n DOMMatrix: typeof DOMMatrix\n /** Global font registry. `use(paths)` reads each file's intrinsic name-table family; the 2-arg\n * `use(family, paths)` form FORCES that alias (fixes variable-font statics whose name table lies). */\n FontLibrary: {\n use(fontPaths: readonly string[]): Array<{ family: string }>\n use(family: string, fontPaths: readonly string[]): Array<{ family: string }>\n }\n}\n\n/** Map a font asset's MIME (or its data-URI) to a file extension skia-canvas can parse. */\nconst FONT_EXT: Record<string, string> = {\n 'font/woff2': '.woff2', 'font/woff': '.woff', 'font/ttf': '.ttf', 'font/otf': '.otf',\n 'font/collection': '.ttc', 'application/font-woff': '.woff', 'application/x-font-ttf': '.ttf',\n}\n\n/** Registers every embedded `font` asset with skia's FontLibrary so headless text uses the authored\n * faces (not host fallbacks). Fonts are baked as base64 data-URIs by flatc; FontLibrary only reads\n * FILE paths, so we materialize each into a temp dir, register it, and return that dir for cleanup. */\nfunction registerFonts(doc: Doc, FontLibrary: SkiaCanvas['FontLibrary']): { dir: string | null; families: string[] } {\n const fonts = (doc.assets ?? []).filter((a) => a.kind === 'font' && /^data:/.test(a.data ?? ''))\n if (fonts.length === 0) return { dir: null, families: [] }\n const dir = mkdtempSync(join(tmpdir(), 'flatc-fonts-'))\n const families: string[] = []\n for (const a of fonts) {\n const b64 = a.data.slice(a.data.indexOf(',') + 1)\n const ext = FONT_EXT[a.mime] ?? `.${(a.mime.split('/')[1] || 'ttf').replace(/[^\\w]/g, '')}`\n const file = join(dir, a.id.replace(/[^\\w.-]/g, '_') + ext)\n try {\n writeFileSync(file, Buffer.from(b64, 'base64'))\n // `family` alias (from `asset … font \"Name\"`) forces the registered name, so the face matches the\n // text's `font \"Name\"` even when the file's own name table is wrong (variable-font static export).\n const used = a.family ? FontLibrary.use(a.family, [file]) : FontLibrary.use([file])\n for (const f of used) if (!families.includes(f.family)) families.push(f.family)\n } catch (e) {\n process.stderr.write(`flatc: skipped font asset \"${a.id}\" (${a.mime}): ${(e as Error).message}\\n`)\n }\n }\n return { dir, families }\n}\n\n/**\n * A renderer held OPEN over a document: the expensive setup is paid once, then any number of frames.\n *\n * Every consumer that needed more than one image — a GIF, an MP4, a contact sheet, a loop check —\n * reimplemented this on top of the player, because the only thing on offer rendered a single frame and\n * paid the whole cost each time: the dynamic `skia-canvas` import, writing the embedded fonts to a temp\n * dir and registering them, decoding every image asset, building a `FlatPlayer`, installing ten globals.\n * Four such harnesses exist across two neighbouring repos, ~430 lines, each rediscovering the same\n * shims — including that `document` must exist or every `filter` and `tint` is dropped in SILENCE.\n */\nexport type Renderer = {\n /** PNG of one frame. `vars` overrides state for this frame; `script` replays gestures after the seek\n * (an `interactive` renderer only); `steps` then runs N sim steps before capture. */\n frame(frame: number, opts?: { vars?: Record<string, number>; steps?: number; script?: Gesture[] }): Promise<Uint8Array>\n /** Replays gestures on the live scene, as `flatc --play` does, and leaves it in the state they reach.\n * Successive calls continue the same session (one state, one `expect` window). Needs\n * `createRenderer(doc, { interactive: true })`. */\n play(gestures: Gesture[]): PlayReport\n /** PNG of the scene AS IT IS NOW — after a `play`, without seeking. */\n capture(): Promise<Uint8Array>\n /** The document's pixel size at the renderer's scale. */\n readonly width: number\n readonly height: number\n /** Restore the globals and drop the temp font files. Always call it — it is not optional. */\n close(): void\n}\n\n/**\n * Open a renderer over `doc`. The globals it installs (`document`, `Path2D`, `DOMMatrix`, …) stay in\n * place until `close()`, which is what makes a frame loop cheap; it also means one renderer at a time\n * per process. `params` sets a SYMBOL's exposed params (a state name or a number) before the first\n * frame — the reason a consumer had to write its own harness to preview anything with a state.\n */\nexport async function createRenderer(doc: Doc, opts: { scale?: number; params?: Record<string, string>; interactive?: boolean; settle?: number } = {}): Promise<Renderer> {\n // Non-literal specifier: tsc does not resolve it, so skia-canvas is not a build dependency.\n const skiaPkg: string = 'skia-canvas'\n let skia: SkiaCanvas\n try { skia = (await import(skiaPkg)) as unknown as SkiaCanvas }\n catch {\n throw new Error(\n 'skia-canvas is required for rendering. Install it as a dev dependency: `npm i -D skia-canvas` ' +\n '(pnpm users: also allow its build script with `pnpm approve-builds` or add \"skia-canvas\" to ' +\n 'pnpm.onlyBuiltDependencies, then reinstall). If the native binary is still missing afterwards, ' +\n 'run `node node_modules/skia-canvas/lib/prebuild.mjs download`.',\n )\n }\n const { Canvas, loadImage, Path2D, FontLibrary, DOMMatrix } = skia\n const scale = opts.scale && opts.scale > 0 ? opts.scale : 2\n const W = doc.width, H = doc.height\n\n const withParams = opts.params ? applyParams(doc, opts.params) : doc\n\n // Register embedded fonts up front so the player's `ctx.font` resolves to the authored faces.\n const { dir: fontDir, families } = registerFonts(withParams, FontLibrary)\n if (families.length) process.stderr.write(`flatc: registered ${families.length} font famil${families.length > 1 ? 'ies' : 'y'}: ${families.join(', ')}\\n`)\n\n // Pre-decode the image assets (skia reads SVG/PNG/... from the data-URIs embedded by flatc).\n const images = new Map<string, CanvasImageSource>()\n for (const a of withParams.assets ?? []) {\n if (a.kind === 'svg' || a.kind === 'image' || /^data:image\\//.test(a.data ?? '')) {\n try { images.set(a.id, (await loadImage(a.data)) as unknown as CanvasImageSource) } catch { /* unreadable asset: ignored (placeholder) */ }\n }\n }\n\n // Globals the player/rendering expects under Node (restored by close()).\n const g = globalThis as Record<string, unknown>\n const saved: Record<string, unknown> = {}\n const set = (k: string, v: unknown) => { saved[k] = g[k]; g[k] = v }\n set('Path2D', Path2D)\n set('DOMMatrix', DOMMatrix) // mask/clip layers build their clip path with `new DOMMatrix([...])` (no browser global under Node)\n set('window', { addEventListener() {}, removeEventListener() {}, devicePixelRatio: scale })\n set('addEventListener', () => {})\n set('removeEventListener', () => {})\n set('requestAnimationFrame', () => 0)\n set('cancelAnimationFrame', () => {})\n set('document', { createElement: (t: string) => (t === 'canvas' ? new Canvas(1, 1) : {}) }) // off-screen canvas (filters/tint)\n\n const pxW = Math.max(1, Math.round(W * scale))\n const pxH = Math.max(1, Math.round(H * scale))\n const handlers: Handlers = {}\n const canvas = new Canvas(pxW, pxH)\n const el = canvas as unknown as HTMLCanvasElement & { toBuffer(fmt: string): Promise<Uint8Array> }\n Object.assign(el, {\n getBoundingClientRect: () => ({ width: W, height: H, left: 0, top: 0, right: W, bottom: H }),\n // The listeners the player registers are KEPT: a replayed script fires into them (`play`).\n addEventListener: (type: string, fn: Handlers[string]) => { handlers[type] = fn },\n removeEventListener: (type: string) => { delete handlers[type] },\n setPointerCapture: () => {}, releasePointerCapture: () => {}, style: {},\n })\n\n // `interactive`: the player listens to its canvas, so gestures can be replayed on it. Off by default —\n // a renderer that only draws frames registers nothing.\n const sends: SendEvent[] = []\n let player: FlatPlayer | null = new FlatPlayer(el, withParams, { input: !!opts.interactive, audio: false, padding: 0, seed: 1, image: (id) => images.get(id) ?? null, onEvent: (e) => sends.push(e) })\n let replayer: Replayer | null = null\n const play = (gestures: Gesture[]): PlayReport => {\n if (!player) throw new Error('renderer is closed')\n if (!opts.interactive) throw new Error('this renderer cannot replay gestures: open it with createRenderer(doc, { interactive: true })')\n replayer ??= createReplayer(player, withParams, handlers, sends, { settle: opts.settle })\n for (const g of gestures) replayer.apply(g)\n return { sends: [...sends], expectFailures: [...replayer.expectFailures], warnings: [...replayer.warnings] }\n }\n\n return {\n width: pxW,\n height: pxH,\n async frame(frame, frameOpts = {}) {\n if (!player) throw new Error('renderer is closed')\n if (frameOpts.vars) for (const [k, v] of Object.entries(frameOpts.vars)) player.setVar(k, v)\n player.seek(frame) // applies frame + state\n if (frameOpts.script?.length) play(frameOpts.script)\n const steps = frameOpts.steps ?? 0\n if (steps > 0) {\n const n = Math.min(Math.floor(steps), MAX_RENDER_STEPS)\n if (steps > MAX_RENDER_STEPS) process.stderr.write(`flatc: steps clamped to ${MAX_RENDER_STEPS} (was ${steps})\\n`)\n player.stepSim(n) // run N fixed sim steps (onEnterFrame) so a stateful act unfolds before capture\n }\n player.render() // a gesture or a step paints only what it changed: draw the frame that is asked for\n return el.toBuffer('png')\n },\n play,\n async capture() {\n if (!player) throw new Error('renderer is closed')\n player.render()\n return el.toBuffer('png')\n },\n close() {\n player?.destroy()\n player = null\n for (const k in saved) { if (saved[k] === undefined) delete g[k]; else g[k] = saved[k] }\n // skia keeps the parsed faces in memory after `use()`, so the temp files are safe to drop now.\n if (fontDir) try { rmSync(fontDir, { recursive: true, force: true }) } catch { /* best-effort cleanup */ }\n },\n }\n}\n\n/**\n * A copy of `doc` with the scene's instances carrying `params` — a state NAME or a number, the same\n * spelling `--preview --set` accepts, resolved by the renderer per the symbol's ParamDef / state\n * machines. Rendering a program could only override document `var`s, so previewing a door in its `open`\n * state meant writing a harness that mutated the symbol by hand. That was one of four such harnesses.\n *\n * Set on the INSTANCE, not on the symbol's defaults: that is where a call-site value belongs, and it is\n * what `--preview` already does.\n */\nfunction applyParams(doc: Doc, params: Record<string, string>): Doc {\n const entries = Object.entries(params)\n if (!entries.length) return doc\n // Which names each symbol exposes: its typed params, plus the param each state machine drives.\n const exposed = new Map<string, Set<string>>()\n for (const sym of doc.symbols) {\n exposed.set(sym.id, new Set([...(sym.states ?? []).map((st) => st.param), ...(sym.params ?? []).map((p) => p.name)]))\n }\n const out = structuredClone(doc)\n const applied = new Set<string>()\n const visit = (items: Item[]): void => {\n for (const it of items) {\n if (isInstance(it)) {\n const known = exposed.get(it.symbolId)\n if (known) {\n for (const [k, v] of entries) {\n if (!known.has(k)) continue\n ;(it.params ??= {})[k] = v\n applied.add(k)\n }\n }\n }\n if (isGroup(it)) for (const l of it.layers) visit(l.items)\n }\n }\n for (const l of out.layers) visit(l.items)\n for (const sym of out.symbols) for (const l of sym.layers) visit(l.items)\n for (const [k] of entries) {\n if (!applied.has(k)) process.stderr.write(`flatc: no symbol exposes a param named \"${k}\"\\n`)\n }\n return out\n}\n\n/** Render `doc` to a PNG (Buffer). `frame` = target image; `vars` = state override; `scale` = x-px (default 2).\n * A one-shot convenience over `createRenderer` — reach for the renderer itself as soon as you want two. */\nexport async function renderDocToPng(doc: Doc, opts: RenderOpts = {}): Promise<Uint8Array> {\n const renderer = await createRenderer(doc, { scale: opts.scale, params: opts.params, interactive: !!opts.script?.length, settle: opts.settle })\n try {\n return await renderer.frame(opts.frame ?? 0, { vars: opts.vars, steps: opts.steps, script: opts.script })\n } finally {\n renderer.close()\n }\n}\n"],"mappings":"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","names":["g"]}
1
+ {"version":3,"sources":["../../src/cli/render.ts"],"sourcesContent":["// -----------------------------------------------------------------------------\n// render.ts -- HEADLESS PNG rendering of a Doc (skia-canvas backend), for `flatc --render`.\n//\n// Gives agents/CI an IMAGE of what they author (the \"blind positioning\" safeguard). We replay the\n// real `FlatPlayer` on a skia canvas: same paths, gradients, filters (glow/shadow via `ctx.filter`)\n// and SVG as the browser. skia-canvas is loaded through a DYNAMIC import with a NON-LITERAL specifier,\n// so it is never part of the public build graph -- install it on demand only when `--render` is used.\n// -----------------------------------------------------------------------------\nimport { FlatPlayer, type SendEvent } from '@flatkit/player'\nimport { createReplayer, type Gesture, type Handlers, type Replayer } from '@flatkit/player/debug'\nimport type { Doc, Item } from '@flatkit/types'\nimport { isGroup, isInstance } from '@flatkit/engine/layers'\nimport { mkdtempSync, writeFileSync, rmSync } from 'node:fs'\nimport { tmpdir } from 'node:os'\nimport { join } from 'node:path'\n\nexport type RenderOpts = { frame?: number; vars?: Record<string, number>; scale?: number; steps?: number; params?: Record<string, string>; script?: Gesture[]; settle?: number }\n\n/** What a replayed script reports: the `send`s it emitted, and the `expect` gestures that did not hold. */\nexport type PlayReport = { sends: SendEvent[]; expectFailures: string[]; warnings: string[] }\n\n/** Cap on `--steps` (anti-DoS: an untrusted doc must not freeze the render host). One step = 1/60 s of sim. */\nconst MAX_RENDER_STEPS = 10_000\n\n/** Minimal structural view of the `skia-canvas` surface we use. Kept local so the public build needs\n * no native binary; the real module is resolved at runtime only when `--render` runs. */\ninterface SkiaCanvas {\n Canvas: new (w: number, h: number) => HTMLCanvasElement & { toBuffer(fmt: string): Promise<Uint8Array> }\n loadImage: (src: string) => Promise<unknown>\n Path2D: typeof Path2D\n /** Browser global under Node: the mask/clip path builder does `new DOMMatrix([...])` (drawScene). */\n DOMMatrix: typeof DOMMatrix\n /** Global font registry. `use(paths)` reads each file's intrinsic name-table family; the 2-arg\n * `use(family, paths)` form FORCES that alias (fixes variable-font statics whose name table lies). */\n FontLibrary: {\n use(fontPaths: readonly string[]): Array<{ family: string }>\n use(family: string, fontPaths: readonly string[]): Array<{ family: string }>\n }\n}\n\n/** Map a font asset's MIME (or its data-URI) to a file extension skia-canvas can parse. */\nconst FONT_EXT: Record<string, string> = {\n 'font/woff2': '.woff2', 'font/woff': '.woff', 'font/ttf': '.ttf', 'font/otf': '.otf',\n 'font/collection': '.ttc', 'application/font-woff': '.woff', 'application/x-font-ttf': '.ttf',\n}\n\n/** Registers every embedded `font` asset with skia's FontLibrary so headless text uses the authored\n * faces (not host fallbacks). Fonts are baked as base64 data-URIs by flatc; FontLibrary only reads\n * FILE paths, so we materialize each into a temp dir, register it, and return that dir for cleanup. */\nfunction registerFonts(doc: Doc, FontLibrary: SkiaCanvas['FontLibrary']): { dir: string | null; families: string[] } {\n const fonts = (doc.assets ?? []).filter((a) => a.kind === 'font' && /^data:/.test(a.data ?? ''))\n if (fonts.length === 0) return { dir: null, families: [] }\n const dir = mkdtempSync(join(tmpdir(), 'flatc-fonts-'))\n const families: string[] = []\n for (const a of fonts) {\n const b64 = a.data.slice(a.data.indexOf(',') + 1)\n const ext = FONT_EXT[a.mime] ?? `.${(a.mime.split('/')[1] || 'ttf').replace(/[^\\w]/g, '')}`\n const file = join(dir, a.id.replace(/[^\\w.-]/g, '_') + ext)\n try {\n writeFileSync(file, Buffer.from(b64, 'base64'))\n // `family` alias (from `asset … font \"Name\"`) forces the registered name, so the face matches the\n // text's `font \"Name\"` even when the file's own name table is wrong (variable-font static export).\n const used = a.family ? FontLibrary.use(a.family, [file]) : FontLibrary.use([file])\n for (const f of used) if (!families.includes(f.family)) families.push(f.family)\n } catch (e) {\n process.stderr.write(`flatc: skipped font asset \"${a.id}\" (${a.mime}): ${(e as Error).message}\\n`)\n }\n }\n return { dir, families }\n}\n\n/**\n * A renderer held OPEN over a document: the expensive setup is paid once, then any number of frames.\n *\n * Every consumer that needed more than one image — a GIF, an MP4, a contact sheet, a loop check —\n * reimplemented this on top of the player, because the only thing on offer rendered a single frame and\n * paid the whole cost each time: the dynamic `skia-canvas` import, writing the embedded fonts to a temp\n * dir and registering them, decoding every image asset, building a `FlatPlayer`, installing ten globals.\n * Four such harnesses exist across two neighbouring repos, ~430 lines, each rediscovering the same\n * shims — including that `document` must exist or every `filter` and `tint` is dropped in SILENCE.\n */\nexport type Renderer = {\n /** PNG of one frame. `vars` overrides state for this frame; `script` replays gestures after the seek\n * (an `interactive` renderer only); `steps` then runs N sim steps before capture. */\n frame(frame: number, opts?: { vars?: Record<string, number>; steps?: number; script?: Gesture[] }): Promise<Uint8Array>\n /** Replays gestures on the live scene, as `flatc --play` does, and leaves it in the state they reach.\n * Successive calls continue the same session (one state, one `expect` window). Needs\n * `createRenderer(doc, { interactive: true })`. */\n play(gestures: Gesture[]): PlayReport\n /** PNG of the scene AS IT IS NOW — after a `play`, without seeking. */\n capture(): Promise<Uint8Array>\n /** The document's pixel size at the renderer's scale. */\n readonly width: number\n readonly height: number\n /** Restore the globals and drop the temp font files. Always call it — it is not optional. */\n close(): void\n}\n\n/**\n * Open a renderer over `doc`. The globals it installs (`document`, `Path2D`, `DOMMatrix`, …) stay in\n * place until `close()`, which is what makes a frame loop cheap; it also means one renderer at a time\n * per process. `params` sets a SYMBOL's exposed params (a state name or a number) before the first\n * frame — the reason a consumer had to write its own harness to preview anything with a state.\n */\nexport async function createRenderer(doc: Doc, opts: { scale?: number; params?: Record<string, string>; interactive?: boolean; settle?: number } = {}): Promise<Renderer> {\n // Non-literal specifier: tsc does not resolve it, so skia-canvas is not a build dependency.\n const skiaPkg: string = 'skia-canvas'\n let skia: SkiaCanvas\n try { skia = (await import(skiaPkg)) as unknown as SkiaCanvas }\n catch {\n throw new Error(\n 'skia-canvas is required for rendering. Install it as a dev dependency: `npm i -D skia-canvas` ' +\n '(pnpm users: also allow its build script with `pnpm approve-builds` or add \"skia-canvas\" to ' +\n 'pnpm.onlyBuiltDependencies, then reinstall). If the native binary is still missing afterwards, ' +\n 'run `node node_modules/skia-canvas/lib/prebuild.mjs download`.',\n )\n }\n const { Canvas, loadImage, Path2D, FontLibrary, DOMMatrix } = skia\n const scale = opts.scale && opts.scale > 0 ? opts.scale : 2\n const W = doc.width, H = doc.height\n\n const withParams = opts.params ? applyParams(doc, opts.params) : doc\n\n // Register embedded fonts up front so the player's `ctx.font` resolves to the authored faces.\n const { dir: fontDir, families } = registerFonts(withParams, FontLibrary)\n if (families.length) process.stderr.write(`flatc: registered ${families.length} font famil${families.length > 1 ? 'ies' : 'y'}: ${families.join(', ')}\\n`)\n\n // Pre-decode the image assets (skia reads SVG/PNG/... from the data-URIs embedded by flatc).\n const images = new Map<string, CanvasImageSource>()\n for (const a of withParams.assets ?? []) {\n if (a.kind === 'svg' || a.kind === 'image' || /^data:image\\//.test(a.data ?? '')) {\n try { images.set(a.id, (await loadImage(a.data)) as unknown as CanvasImageSource) } catch { /* unreadable asset: ignored (placeholder) */ }\n }\n }\n\n // Globals the player/rendering expects under Node (restored by close()).\n const g = globalThis as Record<string, unknown>\n const saved: Record<string, unknown> = {}\n const set = (k: string, v: unknown) => { saved[k] = g[k]; g[k] = v }\n set('Path2D', Path2D)\n set('DOMMatrix', DOMMatrix) // mask/clip layers build their clip path with `new DOMMatrix([...])` (no browser global under Node)\n set('window', { addEventListener() {}, removeEventListener() {}, devicePixelRatio: scale })\n set('addEventListener', () => {})\n set('removeEventListener', () => {})\n set('requestAnimationFrame', () => 0)\n set('cancelAnimationFrame', () => {})\n set('document', { createElement: (t: string) => (t === 'canvas' ? new Canvas(1, 1) : {}) }) // off-screen canvas (filters/tint)\n\n const pxW = Math.max(1, Math.round(W * scale))\n const pxH = Math.max(1, Math.round(H * scale))\n const handlers: Handlers = {}\n const canvas = new Canvas(pxW, pxH)\n const el = canvas as unknown as HTMLCanvasElement & { toBuffer(fmt: string): Promise<Uint8Array> }\n Object.assign(el, {\n getBoundingClientRect: () => ({ width: W, height: H, left: 0, top: 0, right: W, bottom: H }),\n // The listeners the player registers are KEPT: a replayed script fires into them (`play`).\n addEventListener: (type: string, fn: Handlers[string]) => { handlers[type] = fn },\n removeEventListener: (type: string) => { delete handlers[type] },\n setPointerCapture: () => {}, releasePointerCapture: () => {}, style: {},\n })\n\n // `interactive`: the player listens to its canvas, so gestures can be replayed on it. Off by default —\n // a renderer that only draws frames registers nothing.\n const sends: SendEvent[] = []\n let player: FlatPlayer | null = new FlatPlayer(el, withParams, { input: !!opts.interactive, audio: false, padding: 0, seed: 1, image: (id) => images.get(id) ?? null, onEvent: (e) => sends.push(e) })\n let replayer: Replayer | null = null\n const play = (gestures: Gesture[]): PlayReport => {\n if (!player) throw new Error('renderer is closed')\n if (!opts.interactive) throw new Error('this renderer cannot replay gestures: open it with createRenderer(doc, { interactive: true })')\n replayer ??= createReplayer(player, withParams, handlers, sends, { settle: opts.settle })\n for (const g of gestures) replayer.apply(g)\n return { sends: [...sends], expectFailures: [...replayer.expectFailures], warnings: [...replayer.warnings] }\n }\n\n return {\n width: pxW,\n height: pxH,\n async frame(frame, frameOpts = {}) {\n if (!player) throw new Error('renderer is closed')\n if (frameOpts.vars) for (const [k, v] of Object.entries(frameOpts.vars)) player.setVar(k, v)\n player.seek(frame) // applies frame + state\n if (frameOpts.script?.length) play(frameOpts.script)\n const steps = frameOpts.steps ?? 0\n if (steps > 0) {\n const n = Math.min(Math.floor(steps), MAX_RENDER_STEPS)\n if (steps > MAX_RENDER_STEPS) process.stderr.write(`flatc: steps clamped to ${MAX_RENDER_STEPS} (was ${steps})\\n`)\n player.stepSim(n) // run N fixed sim steps (onEnterFrame) so a stateful act unfolds before capture\n }\n // A replayed gesture paints only what it changed: draw again after a script. NOT otherwise — the seek\n // (and `stepSim`) have just drawn this frame, and a second draw goes through the filter cache, which\n // is not the same pixels to the bit as the first (one to three levels in 255 under a blur).\n if (frameOpts.script?.length) player.render()\n return el.toBuffer('png')\n },\n play,\n async capture() {\n if (!player) throw new Error('renderer is closed')\n player.render()\n return el.toBuffer('png')\n },\n close() {\n player?.destroy()\n player = null\n for (const k in saved) { if (saved[k] === undefined) delete g[k]; else g[k] = saved[k] }\n // skia keeps the parsed faces in memory after `use()`, so the temp files are safe to drop now.\n if (fontDir) try { rmSync(fontDir, { recursive: true, force: true }) } catch { /* best-effort cleanup */ }\n },\n }\n}\n\n/**\n * A copy of `doc` with the scene's instances carrying `params` — a state NAME or a number, the same\n * spelling `--preview --set` accepts, resolved by the renderer per the symbol's ParamDef / state\n * machines. Rendering a program could only override document `var`s, so previewing a door in its `open`\n * state meant writing a harness that mutated the symbol by hand. That was one of four such harnesses.\n *\n * Set on the INSTANCE, not on the symbol's defaults: that is where a call-site value belongs, and it is\n * what `--preview` already does.\n */\nfunction applyParams(doc: Doc, params: Record<string, string>): Doc {\n const entries = Object.entries(params)\n if (!entries.length) return doc\n // Which names each symbol exposes: its typed params, plus the param each state machine drives.\n const exposed = new Map<string, Set<string>>()\n for (const sym of doc.symbols) {\n exposed.set(sym.id, new Set([...(sym.states ?? []).map((st) => st.param), ...(sym.params ?? []).map((p) => p.name)]))\n }\n const out = structuredClone(doc)\n const applied = new Set<string>()\n const visit = (items: Item[]): void => {\n for (const it of items) {\n if (isInstance(it)) {\n const known = exposed.get(it.symbolId)\n if (known) {\n for (const [k, v] of entries) {\n if (!known.has(k)) continue\n ;(it.params ??= {})[k] = v\n applied.add(k)\n }\n }\n }\n if (isGroup(it)) for (const l of it.layers) visit(l.items)\n }\n }\n for (const l of out.layers) visit(l.items)\n for (const sym of out.symbols) for (const l of sym.layers) visit(l.items)\n for (const [k] of entries) {\n if (!applied.has(k)) process.stderr.write(`flatc: no symbol exposes a param named \"${k}\"\\n`)\n }\n return out\n}\n\n/** Render `doc` to a PNG (Buffer). `frame` = target image; `vars` = state override; `scale` = x-px (default 2).\n * A one-shot convenience over `createRenderer` — reach for the renderer itself as soon as you want two. */\nexport async function renderDocToPng(doc: Doc, opts: RenderOpts = {}): Promise<Uint8Array> {\n const renderer = await createRenderer(doc, { scale: opts.scale, params: opts.params, interactive: !!opts.script?.length, settle: opts.settle })\n try {\n return await renderer.frame(opts.frame ?? 0, { vars: opts.vars, steps: opts.steps, script: opts.script })\n } finally {\n renderer.close()\n }\n}\n"],"mappings":"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","names":["g"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flatkit/compiler",
3
- "version": "0.39.0",
3
+ "version": "0.39.1",
4
4
  "description": "The FlatInk language (parser + AST) and compiler (.flatink → .flatpack). Ships the flatc CLI.",
5
5
  "license": "MIT",
6
6
  "author": "Zwyk Studio",
@@ -57,9 +57,9 @@
57
57
  "docs"
58
58
  ],
59
59
  "dependencies": {
60
- "@flatkit/player": "0.39.0",
61
- "@flatkit/engine": "0.39.0",
62
- "@flatkit/types": "0.39.0"
60
+ "@flatkit/engine": "0.39.1",
61
+ "@flatkit/player": "0.39.1",
62
+ "@flatkit/types": "0.39.1"
63
63
  },
64
64
  "peerDependencies": {
65
65
  "skia-canvas": "^3.0.8 || ^4.0.0-rc7"