@flatkit/player 0.1.0
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.
- package/LICENSE +21 -0
- package/dist/chunk-BXKJAHHO.js +1681 -0
- package/dist/chunk-BXKJAHHO.js.map +1 -0
- package/dist/debug.d.ts +28 -0
- package/dist/debug.js +193 -0
- package/dist/debug.js.map +1 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/player-Bxx8wsSa.d.ts +252 -0
- package/package.json +51 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/drawScene.ts","../src/player.ts","../src/hit.ts"],"sourcesContent":["// -----------------------------------------------------------------------------\n// runtime/drawScene.ts -- PURE DRAWING CORE, shared by editor + player.\n//\n// Draws a FlatInk document (layers -> items -> matter) at a given frame.\n// CEL MODEL: each layer resolves its content via `resolveLayerAt` (held matter\n// + present containers with their tweened pose) -- no more scope-wide override map nor\n// content map. Recursive (nested groups + symbols, local frame per instance),\n// with an anti-cycle guard. NO mutation of the document.\n//\n// GOLDEN RULE: this module depends ONLY on pure modules (paint/smooth/layers/bbox/\n// cel/transform). It NEVER imports `polygon-clipping`, React, or the store\n// -> embeddable as-is in a lightweight standalone player.\n// -----------------------------------------------------------------------------\nimport type { Doc, Group, Instance, Item, Layer, Region, Text } from '@flatkit/types'\nimport { regionBBox, type BBox } from '@flatkit/engine/bbox'\nimport { regionPaint, type Paint, type Tint } from '@flatkit/engine/paint'\nimport { cssFilterString, type Filter } from '@flatkit/engine/filters'\nimport { containerLayers, getSymbol, hiddenLayerIds, isContainer, isGroup, isInstance, isText, isImage, isRegion, maskMap, guideMap } from '@flatkit/engine/layers'\nimport { pathToBezier, transformPath, type Path } from '@flatkit/engine/path'\nimport { resolveInstanceFrame, type BaseOf } from '@flatkit/engine/timeline'\nimport { resolveLayerAt } from '@flatkit/engine/cel'\nimport type { ExprContext } from '@flatkit/engine/expr'\nimport { apply, compose, IDENTITY, type Transform } from '@flatkit/engine/transform'\n\n/**\n * Render context for a scope: fps (for `time` expressions) + expression context.\n * `freezeNested` (EDITOR): we only animate the current scope; nested symbols are frozen\n * at their frame 0 (their position follows the scope's tween, but their internal timeline does not play).\n * The player leaves `freezeNested` absent -> full, composed animation.\n */\nexport type RenderCtx = {\n fps: number\n expr?: ExprContext\n freezeNested?: boolean\n image?: (assetId: string) => CanvasImageSource | null\n // PLAYER: per-object interaction state for `self.hovered`/`self.grabbed`/`self.pressed` in channel exprs.\n itemState?: (id: string) => { hovered: number; grabbed: number; pressed: number } | undefined\n // EDITOR: preview of a transform/move applied IN PLACE (z-index preserved) to the items\n // whose id is in `ids` -- `m` (translation or affine) wraps their drawing.\n preview?: { ids: Set<string>; m: Transform }\n // PLAYER (perf): cache of the FILTERED composites of \"render-static\" subtrees (still scenery).\n // Absent (editor/preview) = no cache. `imageEpoch` moves on every decoded image -> invalidates the cache.\n filterCache?: Map<string, FilterCacheEntry>\n imageEpoch?: number\n}\n\n/** Entry of the filtered composite cache: FINAL bitmap (content+tint+filter) in screen px at (ox,oy).\n * `canvas` absent = we have only RECORDED the signature (1 observation frame before baking) -- used\n * to NOT bake an object that moves every frame (unstable signature => we would always stay in miss). */\nexport type FilterCacheEntry = { canvas?: HTMLCanvasElement; sig: string; ox: number; oy: number; ow: number; oh: number }\ntype CacheSlot = { map: Map<string, FilterCacheEntry>; id: string; sig: string }\n\nconst clamp01 = (v: number) => Math.max(0, Math.min(1, v))\n\n/** Current scale of the context (doc px -> screen px) -- to scale filters with the zoom. */\nconst scaleOf = (ctx: CanvasRenderingContext2D): number => { const m = ctx.getTransform(); return Math.hypot(m.a, m.b) || 1 }\n\nexport function applyTransform(ctx: CanvasRenderingContext2D, t: Transform) {\n ctx.transform(t.a, t.b, t.c, t.d, t.e, t.f)\n}\n\n/**\n * BASE pose resolver for expressions (and the editor display): finds an\n * item (recursive into groups, not instances = distinct scope) -> transform/opacity.\n * Kept for the editor (overlay/InfoPanel) as long as the old model coexists.\n */\nexport function baseResolver(layers: Layer[]): BaseOf {\n return (id) => {\n let found: { transform?: Transform; opacity?: number } | undefined\n const walk = (items: Item[]) => {\n for (const it of items) {\n if (found) return\n if (it.id === id) {\n found = { transform: isContainer(it) ? it.transform : undefined, opacity: it.opacity }\n return\n }\n if (isGroup(it)) for (const l of it.layers) walk(l.items)\n }\n }\n for (const l of layers) walk(l.items)\n return found\n }\n}\n\n// -- Off-screen canvas pool (tint/filters) -----------------------------------\n// Isolating a tint or filters requires an off-screen canvas. We size it to the object's BOX\n// (+ filter margin), NOT to the full screen: a 200x200 object should not blur a\n// 2400x1600 canvas (cost ~100x; causes framerate drops on scenes with glows).\n// The pool REUSES canvases (stacked by recursion depth); sizes are\n// rounded up to a multiple (BUCKET) to stabilize reuse and avoid re-allocations.\n// `acquire`/`release` stay balanced (try/finally) so that `scratchTop` returns to 0.\nconst scratchPool: HTMLCanvasElement[] = []\nlet scratchTop = 0\nconst SCRATCH_BUCKET = 256\ntype Scratch = { canvas: HTMLCanvasElement; ctx: CanvasRenderingContext2D }\nfunction acquireScratch(w: number, h: number, maxW: number, maxH: number): Scratch | null {\n if (typeof document === 'undefined') return null\n const bw = Math.min(maxW, Math.max(SCRATCH_BUCKET, Math.ceil(w / SCRATCH_BUCKET) * SCRATCH_BUCKET))\n const bh = Math.min(maxH, Math.max(SCRATCH_BUCKET, Math.ceil(h / SCRATCH_BUCKET) * SCRATCH_BUCKET))\n let canvas = scratchPool[scratchTop]\n if (!canvas) { canvas = document.createElement('canvas'); scratchPool[scratchTop] = canvas }\n const ctx = canvas.getContext('2d')\n if (!ctx) return null\n if (canvas.width !== bw || canvas.height !== bh) {\n canvas.width = bw // resizing resets all the context state + clears\n canvas.height = bh\n } else {\n // Reused as-is -> we reset the state (comp/alpha/filter from a previous use) + clear.\n ctx.setTransform(1, 0, 0, 1, 0, 0)\n ctx.globalCompositeOperation = 'source-over'\n ctx.globalAlpha = 1\n ctx.filter = 'none'\n ctx.clearRect(0, 0, bw, bh)\n }\n scratchTop++\n return { canvas, ctx }\n}\nfunction releaseScratch() { if (scratchTop > 0) scratchTop-- }\n\n// Margin (SCREEN px) by which the filters spill outside the object's box -> we enlarge the\n// off-screen area by that much so we do not truncate blur/shadow/glow. `scale` = doc px -> screen px.\nfunction filterSpreadPx(filters: Filter[] | undefined, scale: number): number {\n if (!filters || filters.length === 0) return 0\n let m = 0\n for (const f of filters) {\n if (f.type === 'blur') m += Math.max(0, f.radius) * 3\n else if (f.type === 'glow') m += Math.max(0, f.blur) * 2\n else if (f.type === 'shadow') m += Math.abs(f.dx) + Math.abs(f.dy) + Math.max(0, f.blur) * 2\n }\n return m * scale\n}\n\n// Expands `acc` (screen box) by the 4 corners of a LOCAL rectangle transformed by `m`.\nfunction expandRect(acc: BBox, m: Transform, x0: number, y0: number, x1: number, y1: number): void {\n const pts = [apply(m, { x: x0, y: y0 }), apply(m, { x: x1, y: y0 }), apply(m, { x: x1, y: y1 }), apply(m, { x: x0, y: y1 })]\n for (const p of pts) {\n if (p.x < acc.minX) acc.minX = p.x\n if (p.y < acc.minY) acc.minY = p.y\n if (p.x > acc.maxX) acc.maxX = p.x\n if (p.y > acc.maxY) acc.maxY = p.y\n }\n}\n\n// SCREEN bounding box of the rendered content of `items` under the screen matrix `matrix` (recursive:\n// containers/instances/symbols, like collectShape but numeric). Used to size the off-screen\n// area of a filtered/tinted object.\nfunction accumDevBBox(doc: Doc, items: Item[], frame: number, matrix: Transform, seen: Set<string>, acc: BBox, rctx: RenderCtx): void {\n for (const it of items) {\n if (it.hidden) continue\n if (isContainer(it)) {\n if (isInstance(it) && seen.has(it.symbolId)) continue\n const t = compose(matrix, it.transform)\n if (isInstance(it)) {\n const sym = getSymbol(doc, it.symbolId)\n const local = sym?.timeline ? resolveInstanceFrame(it.playback, frame, sym.timeline.durationFrames) : frame\n const sub: RenderCtx = { fps: subFps(sym?.timeline?.fps, rctx), expr: rctx.expr }\n const next = new Set([...seen, it.symbolId])\n for (const l of containerLayers(doc, it)) if (l.visible) accumDevBBox(doc, resolveLayerAt(l, local, { fps: sub.fps, ctx: sub.expr, parent: t }), local, t, next, acc, sub)\n } else if (isGroup(it) && it.timeline) {\n const sub: RenderCtx = { fps: subFps(it.timeline.fps, rctx), expr: rctx.expr }\n for (const l of it.layers) if (l.visible) accumDevBBox(doc, resolveLayerAt(l, frame, { fps: sub.fps, ctx: sub.expr, parent: t }), frame, t, seen, acc, sub)\n } else {\n for (const l of containerLayers(doc, it)) if (l.visible) accumDevBBox(doc, resolveLayerAt(l, frame, { fps: rctx.fps, ctx: rctx.expr, parent: t }), frame, t, seen, acc, rctx)\n }\n } else if (isText(it)) {\n expandRect(acc, compose(matrix, it.transform), 0, 0, it.box.w, it.box.h)\n } else if (isImage(it)) {\n expandRect(acc, compose(matrix, it.transform), 0, 0, it.w, it.h)\n } else {\n const b = regionBBox(it as Region)\n if (b) expandRect(acc, matrix, b.minX, b.minY, b.maxX, b.maxY)\n }\n }\n}\n\nconst matOf = (m: DOMMatrix): Transform => ({ a: m.a, b: m.b, c: m.c, d: m.d, e: m.e, f: m.f })\n\n// -- Render-staticness (for the filtered composite cache) --------------------\n// A subtree is \"render-static\" if its APPEARANCE depends neither on the frame nor on expressions:\n// no channel expression, no text `bind`, no animated timeline/cel. -> its filtered\n// composition changes only with the screen transform (zoom/pan) or with an asset load, so it is cacheable.\nconst staticMemo = new WeakMap<object, boolean>()\nconst hasExpr = (it: Item): boolean => 'expressions' in it && !!it.expressions && Object.keys(it.expressions).length > 0\nfunction layersStatic(doc: Doc, layers: Layer[], seen: Set<string>): boolean {\n for (const l of layers) {\n if (l.cels && l.cels.length) return false // animated layer (keyframes)\n for (const it of l.items) if (!isRenderStatic(doc, it, seen)) return false\n }\n return true\n}\nexport function isRenderStatic(doc: Doc, it: Item, seen: Set<string> = new Set()): boolean {\n const memo = staticMemo.get(it)\n if (memo !== undefined) return memo // structural result (stable as long as the doc does not change)\n if ((isText(it) && it.bind) || hasExpr(it)) { staticMemo.set(it, false); return false }\n let result = true\n if (isInstance(it)) {\n if (seen.has(it.symbolId)) return true // cycle: DO NOT memoize (depends on `seen`)\n result = getSymbol(doc, it.symbolId)?.timeline ? false : layersStatic(doc, containerLayers(doc, it), new Set([...seen, it.symbolId]))\n } else if (isGroup(it)) {\n result = it.timeline ? false : layersStatic(doc, it.layers, seen)\n }\n // leaf regions/images without an expression -> static (result stays true)\n staticMemo.set(it, result)\n return result\n}\n\n/** Cache slot for a filtered container, iff the player provides `filterCache` AND the subtree is\n * static. Signature = screen transform + tint + filter + image epoch (busted on asset load). */\nfunction filterCacheSlot(rctx: RenderCtx, doc: Doc, it: Item, ctx: CanvasRenderingContext2D, tint: Tint | null, filterStr: string): CacheSlot | undefined {\n if (!rctx.filterCache || typeof document === 'undefined' || !isRenderStatic(doc, it)) return undefined\n const m = ctx.getTransform()\n const r = (n: number) => Math.round(n * 100) / 100\n const sig = `${r(m.a)},${r(m.b)},${r(m.c)},${r(m.d)},${r(m.e)},${r(m.f)}|${tint ? `${tint.color}:${tint.amount}` : ''}|${filterStr}|${rctx.imageEpoch ?? 0}`\n return { map: rctx.filterCache, id: it.id, sig }\n}\n\n/**\n * Composes isolated content (tint + filters) while limiting the off-screen area to the object's BOX\n * (+ filter margin) instead of the full screen. `devBBox` = screen box of the object; `null` or too\n * large -> full-screen fallback. `draw(octx)` paints the content (the brush sets its own transform).\n */\nexport function compositeFiltered(\n ctx: CanvasRenderingContext2D,\n opacity: number,\n tint: Tint | null,\n filters: Filter[] | undefined,\n scale: number,\n devBBox: BBox | null,\n draw: (octx: CanvasRenderingContext2D) => void,\n cache?: CacheSlot,\n): void {\n // Cache HIT: the subtree is static and its transform/tint/filter have not changed -> we\n // reblit the final bitmap (no redraw, no refiltering). This is THE \"paper theatre\" win.\n const prev = cache ? cache.map.get(cache.id) : undefined\n if (prev && prev.canvas && prev.sig === cache!.sig) {\n // HIT: baked bitmap and unchanged signature -> reblit (no redraw/refiltering). THE \"paper theatre\" win.\n ctx.save()\n ctx.globalAlpha *= opacity\n ctx.setTransform(1, 0, 0, 1, 0, 0)\n ctx.drawImage(prev.canvas, 0, 0, prev.ow, prev.oh, prev.ox, prev.oy, prev.ow, prev.oh)\n ctx.restore()\n return\n }\n // We only BAKE if the signature was ALREADY the one from the previous frame (stable object) -- otherwise an\n // object moved every frame would pay a useless bake on every miss.\n const stable = !!(prev && prev.sig === cache!.sig)\n const filterStr = cssFilterString(filters, scale)\n const cw = ctx.canvas.width\n const ch = ctx.canvas.height\n let ox = 0, oy = 0, ow = cw, oh = ch\n if (devBBox && devBBox.minX <= devBBox.maxX) {\n const m = filterSpreadPx(filters, scale)\n ox = Math.max(0, Math.floor(devBBox.minX - m))\n oy = Math.max(0, Math.floor(devBBox.minY - m))\n ow = Math.max(1, Math.min(cw, Math.ceil(devBBox.maxX + m)) - ox)\n oh = Math.max(1, Math.min(ch, Math.ceil(devBBox.maxY + m)) - oy)\n if (ow >= cw && oh >= ch) { ox = 0; oy = 0; ow = cw; oh = ch } // full-screen object -> no gain\n }\n const scratch = acquireScratch(ow, oh, cw, ch)\n if (!scratch) { // no DOM (tests) -> direct fallback without isolation\n ctx.save(); ctx.globalAlpha *= opacity; draw(ctx); ctx.restore(); return\n }\n const octx = scratch.ctx\n try {\n const dev = ctx.getTransform()\n octx.setTransform(dev.a, dev.b, dev.c, dev.d, dev.e - ox, dev.f - oy) // off-screen origin = (ox,oy) screen\n draw(octx)\n if (tint) {\n octx.setTransform(1, 0, 0, 1, 0, 0)\n octx.globalCompositeOperation = 'source-atop'\n octx.globalAlpha = clamp01(tint.amount)\n octx.fillStyle = tint.color\n octx.fillRect(0, 0, ow, oh)\n }\n // MISS. STABLE object (same signature as the previous frame) -> we bake the filtered result into\n // a persistent canvas and blit it flat; the following frames will HIT.\n const store = cache && stable && typeof document !== 'undefined' ? ensureCacheCanvas(cache, ox, oy, ow, oh) : null\n const cctx = store ? store.canvas!.getContext('2d') : null\n if (store && cctx) {\n cctx.setTransform(1, 0, 0, 1, 0, 0)\n cctx.globalAlpha = 1; cctx.globalCompositeOperation = 'source-over'\n cctx.filter = filterStr || 'none'\n cctx.clearRect(0, 0, store.canvas!.width, store.canvas!.height)\n cctx.drawImage(scratch.canvas, 0, 0, ow, oh, 0, 0, ow, oh)\n store.sig = cache!.sig; store.ox = ox; store.oy = oy; store.ow = ow; store.oh = oh\n ctx.save()\n ctx.globalAlpha *= opacity\n ctx.setTransform(1, 0, 0, 1, 0, 0)\n ctx.drawImage(store.canvas!, 0, 0, ow, oh, ox, oy, ow, oh)\n ctx.restore()\n } else {\n // 1st observation OR volatile object: we RECORD the signature (without baking) and blit filtered directly.\n if (cache) cache.map.set(cache.id, { sig: cache.sig, ox, oy, ow, oh })\n ctx.save()\n ctx.globalAlpha *= opacity\n ctx.setTransform(1, 0, 0, 1, 0, 0)\n if (filterStr) ctx.filter = filterStr\n ctx.drawImage(scratch.canvas, 0, 0, ow, oh, ox, oy, ow, oh)\n ctx.restore()\n }\n } finally {\n releaseScratch()\n }\n}\n\n/** Entry WITH a persistent canvas (>= ow x oh) to store a filtered composite; (re)allocates if needed. */\nfunction ensureCacheCanvas(cache: CacheSlot, ox: number, oy: number, ow: number, oh: number): FilterCacheEntry & { canvas: HTMLCanvasElement } {\n const e = cache.map.get(cache.id)\n if (e?.canvas && e.canvas.width >= ow && e.canvas.height >= oh) return e as FilterCacheEntry & { canvas: HTMLCanvasElement }\n const canvas = document.createElement('canvas')\n canvas.width = Math.max(1, ow); canvas.height = Math.max(1, oh)\n const fresh = { canvas, sig: '', ox, oy, ow, oh }\n cache.map.set(cache.id, fresh)\n return fresh\n}\n\n/**\n * Clipping by ALPHA (and not by vector path): for masks whose matter contains\n * TEXT or IMAGES (`ctx.clip` cannot clip them). We paint the content off-screen, then we\n * apply the matter in `destination-in` (= we keep the content ONLY where the matter is opaque),\n * and recompose. The area is bounded to the SCREEN box of the matter (the rest is masked anyway).\n * `draw*` set their own transform; no DOM (tests) -> fallback without mask.\n */\nfunction compositeMasked(\n ctx: CanvasRenderingContext2D,\n opacity: number,\n devBBox: BBox | null,\n blit: GlobalCompositeOperation,\n drawContent: (octx: CanvasRenderingContext2D) => void,\n drawMatter: (octx: CanvasRenderingContext2D) => void,\n): void {\n const cw = ctx.canvas.width\n const ch = ctx.canvas.height\n let ox = 0, oy = 0, ow = cw, oh = ch\n if (devBBox && devBBox.minX <= devBBox.maxX) {\n ox = Math.max(0, Math.floor(devBBox.minX))\n oy = Math.max(0, Math.floor(devBBox.minY))\n ow = Math.max(1, Math.min(cw, Math.ceil(devBBox.maxX)) - ox)\n oh = Math.max(1, Math.min(ch, Math.ceil(devBBox.maxY)) - oy)\n if (ow >= cw && oh >= ch) { ox = 0; oy = 0; ow = cw; oh = ch }\n }\n const scratch = acquireScratch(ow, oh, cw, ch)\n if (!scratch) { ctx.save(); ctx.globalAlpha *= opacity; drawContent(ctx); ctx.restore(); return }\n const octx = scratch.ctx\n try {\n const dev = ctx.getTransform()\n octx.setTransform(dev.a, dev.b, dev.c, dev.d, dev.e - ox, dev.f - oy) // off-screen origin = (ox,oy) screen\n drawContent(octx)\n octx.setTransform(dev.a, dev.b, dev.c, dev.d, dev.e - ox, dev.f - oy)\n octx.globalCompositeOperation = 'destination-in' // keep the content only under the matter's alpha\n drawMatter(octx)\n octx.globalCompositeOperation = 'source-over'\n ctx.save()\n ctx.globalAlpha *= opacity\n ctx.globalCompositeOperation = blit // additive content (glow) -> we ADD the clipped result\n ctx.setTransform(1, 0, 0, 1, 0, 0)\n ctx.drawImage(scratch.canvas, 0, 0, ow, oh, ox, oy, ow, oh)\n ctx.restore()\n } finally {\n releaseScratch()\n }\n}\n\n/** Does a mask's matter contain text/an image? (-> alpha clipping rather than vector). */\nexport function itemsHaveGlyph(doc: Doc, items: Item[], seen: Set<string> = new Set()): boolean {\n for (const it of items) {\n if (it.hidden) continue\n if (isText(it) || isImage(it)) return true\n if (isContainer(it)) {\n if (isInstance(it) && seen.has(it.symbolId)) continue\n const next = isInstance(it) ? new Set([...seen, it.symbolId]) : seen\n for (const l of containerLayers(doc, it)) if (l.visible && itemsHaveGlyph(doc, resolveLayerAt(l, 0, {}), next)) return true\n }\n }\n return false\n}\n\n/** fps of a container's sub-scope (its own timeline, or inherited). */\nconst subFps = (tlFps: number | undefined, parent: RenderCtx): number => tlFps ?? parent.fps\n\n// Stack-overflow guard: an untrusted .flatpack may nest groups infinitely (instances\n// are already protected by `seen`, groups are not). Well beyond any real rig.\nconst MAX_NEST = 256\n\n/**\n * Draws the content of a container (already posed by the parent layer):\n * - library instance = new scope (local frame via resolveInstanceFrame);\n * - local symbol (group WITH a timeline) = new scope synced on the parent frame;\n * - group without a timeline (legacy) = same scope as the parent.\n */\nfunction renderContainerChildren(\n ctx: CanvasRenderingContext2D,\n doc: Doc,\n it: Group | Instance,\n frame: number,\n hidden: Set<string> | null,\n seen: Set<string>,\n rctx: RenderCtx,\n parent: Transform = IDENTITY, // = WORLD space of this container (the children live inside it)\n depth = 0,\n) {\n if (isInstance(it)) {\n const sym = getSymbol(doc, it.symbolId)\n // EDITOR (freezeNested): sub-scope frozen at 0; otherwise local frame (full animation, player).\n const local = rctx.freezeNested ? 0 : sym?.timeline ? resolveInstanceFrame(it.playback, frame, sym.timeline.durationFrames) : frame\n renderLayers(ctx, doc, containerLayers(doc, it), local, hidden, seen, { fps: subFps(sym?.timeline?.fps, rctx), expr: rctx.expr, freezeNested: rctx.freezeNested, image: rctx.image, filterCache: rctx.filterCache, imageEpoch: rctx.imageEpoch, itemState: rctx.itemState }, parent, depth + 1)\n } else if (isGroup(it) && it.timeline) {\n renderLayers(ctx, doc, it.layers, rctx.freezeNested ? 0 : frame, hidden, seen, { fps: subFps(it.timeline.fps, rctx), expr: rctx.expr, freezeNested: rctx.freezeNested, image: rctx.image, filterCache: rctx.filterCache, imageEpoch: rctx.imageEpoch, itemState: rctx.itemState }, parent, depth + 1)\n } else {\n // Group without a timeline = same scope as the parent (not a sub-scope) -> follows the scope's frame.\n renderLayers(ctx, doc, containerLayers(doc, it), frame, hidden, seen, rctx, parent, depth + 1)\n }\n}\n\n/**\n * Bezier path of a region (outline + holes), shiftable. Hybrid model: the\n * subpaths without handles are smoothed (Catmull-Rom, matter look), those with handles\n * render their literal cubic (see engine/path.ts).\n */\nexport function regionPath(region: Region, dx = 0, dy = 0): Path2D {\n const path = new Path2D()\n for (const sub of region.path.subpaths) {\n const bz = pathToBezier(sub)\n if (!bz) continue\n path.moveTo(bz.start.x + dx, bz.start.y + dy)\n for (const s of bz.segs) path.bezierCurveTo(s.c1.x + dx, s.c1.y + dy, s.c2.x + dx, s.c2.y + dy, s.p.x + dx, s.p.y + dy)\n if (sub.closed) path.closePath()\n }\n return path\n}\n\n/** Recursive collection of the shapes (regions) of ALREADY RESOLVED items into `path`, with transforms. */\nfunction collectShape(\n doc: Doc,\n items: Item[],\n frame: number,\n matrix: Transform,\n seen: Set<string>,\n path: Path2D,\n rctx: RenderCtx,\n) {\n for (const it of items) {\n if (it.hidden) continue\n if (isContainer(it)) {\n if (isInstance(it) && seen.has(it.symbolId)) continue\n const t = compose(matrix, it.transform)\n if (isInstance(it)) {\n const sym = getSymbol(doc, it.symbolId)\n const local = sym?.timeline ? resolveInstanceFrame(it.playback, frame, sym.timeline.durationFrames) : frame\n const sub: RenderCtx = { fps: subFps(sym?.timeline?.fps, rctx), expr: rctx.expr }\n const next = new Set([...seen, it.symbolId])\n for (const l of containerLayers(doc, it)) if (l.visible) collectShape(doc, resolveLayerAt(l, local, { fps: sub.fps, ctx: sub.expr, parent: t }), local, t, next, path, sub)\n } else if (isGroup(it) && it.timeline) {\n const sub: RenderCtx = { fps: subFps(it.timeline.fps, rctx), expr: rctx.expr }\n for (const l of it.layers) if (l.visible) collectShape(doc, resolveLayerAt(l, frame, { fps: sub.fps, ctx: sub.expr, parent: t }), frame, t, seen, path, sub)\n } else {\n for (const l of containerLayers(doc, it)) if (l.visible) collectShape(doc, resolveLayerAt(l, frame, { fps: rctx.fps, ctx: rctx.expr, parent: t }), frame, t, seen, path, rctx)\n }\n } else {\n path.addPath(regionPath(it as Region), new DOMMatrix([matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f]))\n }\n }\n}\n\n/** Path of a guide layer = subpaths of its resolved matter (baked xform), or null if empty. */\nexport function guidePathOf(guide: Layer, frame: number, rctx: RenderCtx): Path | null {\n const items = resolveLayerAt(guide, frame, { fps: rctx.fps, ctx: rctx.expr })\n const subpaths = []\n for (const it of items) {\n if (!isRegion(it)) continue\n const path = it.xform ? transformPath(it.path, it.xform) : it.path\n subpaths.push(...path.subpaths)\n }\n return subpaths.length ? { subpaths } : null\n}\n\n/** Clip path of a mask layer = union of its resolved matter (regions, containers). */\nexport function maskClipPath(doc: Doc, mask: Layer, frame: number, rctx: RenderCtx): Path2D {\n const path = new Path2D()\n collectShape(doc, resolveLayerAt(mask, frame, { fps: rctx.fps, ctx: rctx.expr }), frame, IDENTITY, new Set(), path, rctx)\n return path\n}\n\n/** Canvas style of a paint (solid or gradient), anchored to `bbox`. Reused for fill AND stroke. */\nfunction paintStyle(ctx: CanvasRenderingContext2D, paint: Paint, bbox: ReturnType<typeof regionBBox>, fallback: string): string | CanvasGradient {\n if (paint.type === 'solid') return paint.color\n const b = paint.box ?? bbox\n if (!b) return fallback\n const w = b.maxX - b.minX\n const h = b.maxY - b.minY\n let g: CanvasGradient\n if (paint.type === 'linear') {\n const cx = (b.minX + b.maxX) / 2\n const cy = (b.minY + b.maxY) / 2\n const a = (paint.angle * Math.PI) / 180\n const ux = Math.cos(a)\n const uy = Math.sin(a)\n const half = (Math.abs(w * ux) + Math.abs(h * uy)) / 2\n g = ctx.createLinearGradient(cx - ux * half, cy - uy * half, cx + ux * half, cy + uy * half)\n } else {\n const cx = b.minX + paint.cx * w\n const cy = b.minY + paint.cy * h\n g = ctx.createRadialGradient(cx, cy, 0, cx, cy, Math.max(0.0001, paint.r * Math.max(w, h)))\n }\n for (const s of paint.stops) g.addColorStop(clamp01(s.offset), s.color)\n return g\n}\n\nfunction fillStyleFor(ctx: CanvasRenderingContext2D, region: Region): string | CanvasGradient {\n return paintStyle(ctx, regionPaint(region), regionBBox(region), region.color)\n}\n\n/** Draws a list of ALREADY RESOLVED items (poses applied) at a frame. */\nexport function renderItems(\n ctx: CanvasRenderingContext2D,\n doc: Doc,\n items: Item[],\n frame: number,\n hidden: Set<string> | null,\n seen: Set<string>,\n rctx: RenderCtx,\n parent: Transform = IDENTITY,\n depth = 0,\n) {\n for (const it of items) {\n if (hidden?.has(it.id)) continue\n if (it.hidden) continue // hidden in the outliner\n const opacity = it.opacity ?? 1\n if (opacity <= 0) continue\n // Blend mode (add/screen = additive light, multiply = shadow). Saved/restored around the item.\n const blend = 'blend' in it ? it.blend : undefined\n const op = blend === 'add' ? 'lighter' : blend === 'screen' ? 'screen' : blend === 'multiply' ? 'multiply' : null\n if (op) { ctx.save(); ctx.globalCompositeOperation = op }\n // Edit preview (drag): we wrap the item's drawing WITHIN the layer order -> z-index preserved.\n const pm = rctx.preview?.ids.has(it.id) ? rctx.preview.m : null\n if (pm) ctx.save()\n if (pm) applyTransform(ctx, pm)\n renderOneItem(ctx, doc, it, frame, hidden, seen, rctx, opacity, parent, depth)\n if (pm) ctx.restore()\n if (op) ctx.restore()\n }\n}\n\n/** Draws ONE resolved item (without the masking/preview logic, handled by the caller). */\nfunction renderOneItem(\n ctx: CanvasRenderingContext2D,\n doc: Doc,\n it: Item,\n frame: number,\n hidden: Set<string> | null,\n seen: Set<string>,\n rctx: RenderCtx,\n opacity: number,\n parent: Transform = IDENTITY,\n depth = 0,\n) {\n {\n if (isContainer(it)) {\n if (isInstance(it) && seen.has(it.symbolId)) return // anti-cycle guard\n const next = isInstance(it) ? new Set([...seen, it.symbolId]) : seen\n const ctm = it.transform\n const childParent = compose(parent, ctm) // WORLD space of the container's children\n const tint = it.tint && it.tint.amount > 0.001 ? it.tint : null\n const scale = scaleOf(ctx)\n const filterStr = cssFilterString(it.filters, scale)\n // Tint AND/OR filters -> isolate the content off-screen (area = object's box) then recompose.\n if (tint || filterStr) {\n const acc: BBox = { minX: Infinity, minY: Infinity, maxX: -Infinity, maxY: -Infinity }\n accumDevBBox(doc, [it], frame, matOf(ctx.getTransform()), seen, acc, rctx)\n const devBBox = acc.minX <= acc.maxX ? acc : null\n // Cache (player): static subtree -> we record the filtered composite and reblit it.\n const cache = filterCacheSlot(rctx, doc, it, ctx, tint, filterStr)\n compositeFiltered(ctx, opacity, tint, it.filters, scale, devBBox, (octx) => {\n applyTransform(octx, ctm)\n renderContainerChildren(octx, doc, it, frame, hidden, next, rctx, childParent, depth)\n }, cache)\n } else {\n ctx.save()\n ctx.globalAlpha *= opacity\n applyTransform(ctx, ctm)\n renderContainerChildren(ctx, doc, it, frame, hidden, next, rctx, childParent, depth)\n ctx.restore()\n }\n } else if (isText(it)) {\n const acc: BBox = { minX: Infinity, minY: Infinity, maxX: -Infinity, maxY: -Infinity }\n expandRect(acc, compose(matOf(ctx.getTransform()), it.transform), 0, 0, it.box.w, it.box.h)\n paintLeaf(ctx, it.tint, it.filters, opacity, acc, scaleOf(ctx), (c) => paintText(c, it))\n } else if (isImage(it)) {\n const src = rctx.image?.(it.assetId) ?? null\n const acc: BBox = { minX: Infinity, minY: Infinity, maxX: -Infinity, maxY: -Infinity }\n expandRect(acc, compose(matOf(ctx.getTransform()), it.transform), 0, 0, it.w, it.h)\n paintLeaf(ctx, it.tint, it.filters, opacity, acc, scaleOf(ctx), (c) => paintImage(c, it, src))\n } else {\n // Region: fill (unless noFill) + optional outline (stroke), same Bezier path.\n const reg = it as Region\n if (reg.filters?.length) {\n // RARE case: filters on a path -> we isolate off-screen then recompose (closure tolerated here).\n const lb = regionBBox(reg)\n const acc: BBox = { minX: Infinity, minY: Infinity, maxX: -Infinity, maxY: -Infinity }\n if (lb) expandRect(acc, matOf(ctx.getTransform()), lb.minX, lb.minY, lb.maxX, lb.maxY)\n paintLeaf(ctx, undefined, reg.filters, opacity, acc, scaleOf(ctx), (c) => paintRegion(c, reg))\n } else {\n // HOT path (the majority of regions): no allocation (no closure).\n ctx.save()\n if (opacity < 1) ctx.globalAlpha *= opacity\n paintRegion(ctx, reg)\n ctx.restore()\n }\n }\n }\n}\n\n/** Paints a region (fill + outline) into `c`. Module function (zero allocation per call). */\nfunction paintRegion(c: CanvasRenderingContext2D, reg: Region) {\n const path = regionPath(reg)\n if (!reg.noFill) {\n c.fillStyle = fillStyleFor(c, reg)\n c.fill(path, 'evenodd')\n }\n if (reg.stroke) {\n const s = reg.stroke\n c.lineWidth = s.width\n c.lineCap = s.cap ?? 'round'\n c.lineJoin = s.join ?? 'round'\n if (s.miterLimit != null) c.miterLimit = s.miterLimit\n c.setLineDash(s.dash ?? [])\n c.strokeStyle = paintStyle(c, s.paint, regionBBox(reg), reg.color)\n c.stroke(path)\n }\n}\n\n/** CSS font of a text (reused by the editor to measure). */\nexport const textFont = (t: Text): string => `${t.italic ? 'italic ' : ''}${t.weight ?? 400} ${t.size}px ${t.font}`\n\n/** Paints a \"live\" text at full opacity (local origin = top-left corner, lines aligned within `box`). */\nfunction paintText(ctx: CanvasRenderingContext2D, t: Text) {\n ctx.save()\n applyTransform(ctx, t.transform)\n ctx.fillStyle = t.color\n ctx.textBaseline = 'top'\n ctx.font = textFont(t)\n ctx.textAlign = t.align\n const x = t.align === 'left' ? 0 : t.align === 'right' ? t.box.w : t.box.w / 2\n const lh = t.size * t.lineHeight\n const lines = t.wrap && t.box.w > 0 ? wrapLines(ctx, t.content, t.box.w) : t.content.split('\\n')\n for (let i = 0; i < lines.length; i++) ctx.fillText(lines[i], x, i * lh)\n ctx.restore()\n}\n\n/** Greedy word-wrap within `maxW` (local px). Respects explicit `\\n`; breaks at spaces. */\nexport function wrapLines(ctx: Pick<CanvasRenderingContext2D, 'measureText'>, content: string, maxW: number): string[] {\n const out: string[] = []\n for (const para of content.split('\\n')) {\n const words = para.split(/\\s+/).filter(Boolean)\n if (words.length === 0) { out.push(''); continue }\n let line = words[0]\n for (let i = 1; i < words.length; i++) {\n const test = line + ' ' + words[i]\n if (ctx.measureText(test).width <= maxW) line = test\n else { out.push(line); line = words[i] }\n }\n out.push(line)\n }\n return out\n}\n\n/** Paints a bitmap image (gray placeholder while not yet decoded). */\nfunction paintImage(ctx: CanvasRenderingContext2D, im: import('@flatkit/types').Image, src: CanvasImageSource | null) {\n ctx.save()\n applyTransform(ctx, im.transform)\n if (src) ctx.drawImage(src, 0, 0, im.w, im.h)\n else { ctx.fillStyle = 'rgba(127,131,140,0.18)'; ctx.fillRect(0, 0, im.w, im.h) }\n ctx.restore()\n}\n\n/** Paints a leaf (text/image) with opacity + tint (Flash) + filters (P4.2), off-screen if needed.\n * `devBBox` = screen box of the leaf (to size the off-screen area to the object, not the full screen). */\nfunction paintLeaf(ctx: CanvasRenderingContext2D, tint: Tint | undefined, filters: Filter[] | undefined, opacity: number, devBBox: BBox, scale: number, draw: (c: CanvasRenderingContext2D) => void) {\n const t = tint && tint.amount > 0.001 ? tint : null\n const filterStr = cssFilterString(filters, scale)\n if (!t && !filterStr) {\n ctx.save()\n if (opacity < 1) ctx.globalAlpha *= opacity\n draw(ctx)\n ctx.restore()\n return\n }\n compositeFiltered(ctx, opacity, t, filters, scale, devBBox.minX <= devBBox.maxX ? devBBox : null, draw)\n}\n\n/** Draws a stack of layers at a frame (each layer resolves its content via the cels). */\nexport function renderLayers(\n ctx: CanvasRenderingContext2D,\n doc: Doc,\n layers: Layer[],\n frame: number,\n hidden: Set<string> | null,\n seen: Set<string>,\n rctx: RenderCtx,\n parent: Transform = IDENTITY, // WORLD transform of the scope (for toLocal/toGlobal conversions in binding)\n depth = 0,\n) {\n if (depth > MAX_NEST) return // untrusted doc: pathological nesting -> we stop\n const hid = hiddenLayerIds(layers) // layer hidden, or under a hidden folder/mask\n const masks = maskMap(layers) // child layer of a mask -> its mask\n const guides = guideMap(layers) // child layer of a guide layer -> its guide\n const clipCache = new Map<string, Path2D>() // one mask clips several children -> reuse\n const maskCache = new Map<string, { items: Item[]; glyph: boolean }>() // resolved matter + clip type\n const guideCache = new Map<string, Path | null>() // one guide drives several children -> reuse\n for (const layer of layers) {\n if (hid.has(layer.id)) continue\n if (layer.isMask) continue // the mask's shape is not painted (it only serves to clip)\n if (layer.isGuide) continue // the guide path is not painted (the editor shows it as an overlay)\n const a = ctx.globalAlpha\n ctx.globalAlpha = a * layer.opacity\n const guideLayer = guides.get(layer.id)\n let guidePath: Path | null | undefined\n if (guideLayer) {\n guidePath = guideCache.get(guideLayer.id)\n if (guidePath === undefined) { guidePath = guidePathOf(guideLayer, frame, rctx); guideCache.set(guideLayer.id, guidePath) }\n }\n const items = resolveLayerAt(layer, frame, { fps: rctx.fps, ctx: rctx.expr, guide: guidePath ?? undefined, orient: layer.orientToGuide, parent, itemState: rctx.itemState })\n const mask = masks.get(layer.id)\n if (mask) {\n let mi = maskCache.get(mask.id)\n if (!mi) {\n const mItems = resolveLayerAt(mask, frame, { fps: rctx.fps, ctx: rctx.expr })\n mi = { items: mItems, glyph: itemsHaveGlyph(doc, mItems, new Set()) }\n maskCache.set(mask.id, mi)\n }\n if (mi.glyph) {\n // TEXT/IMAGE matter -> alpha clipping (the content only appears within the silhouette).\n const acc: BBox = { minX: Infinity, minY: Infinity, maxX: -Infinity, maxY: -Infinity }\n accumDevBBox(doc, mi.items, frame, matOf(ctx.getTransform()), seen, acc, rctx)\n const matter = mi.items\n // Additive content (blend add) -> we add the clipped result (a glow that passes through the silhouette).\n const blit: GlobalCompositeOperation = items.some((it) => 'blend' in it && it.blend === 'add') ? 'lighter' : 'source-over'\n compositeMasked(ctx, 1, acc.minX <= acc.maxX ? acc : null, blit,\n (octx) => renderItems(octx, doc, items, frame, hidden, seen, rctx, parent, depth),\n (octx) => renderItems(octx, doc, matter, frame, hidden, seen, rctx, parent, depth),\n )\n } else {\n // Vector matter (common case) -> fast clip, unchanged.\n let clip = clipCache.get(mask.id)\n if (!clip) { const p = new Path2D(); collectShape(doc, mi.items, frame, IDENTITY, new Set(), p, rctx); clip = p; clipCache.set(mask.id, clip) }\n ctx.save()\n ctx.clip(clip, 'evenodd')\n renderItems(ctx, doc, items, frame, hidden, seen, rctx, parent, depth)\n ctx.restore()\n }\n } else {\n renderItems(ctx, doc, items, frame, hidden, seen, rctx, parent, depth)\n }\n ctx.globalAlpha = a\n }\n}\n","// -----------------------------------------------------------------------------\n// player.ts -- the FlatInk animation player, standalone and lightweight.\n//\n// Loads a FlatInk document (the `.flatink` format = this JSON `Doc`) and plays it\n// in a <canvas>. No editor dependency: no React, no store, no material engine\n// (`polygon-clipping`) -- the material is already baked into the document, the\n// player only evaluates the timeline and draws.\n// -----------------------------------------------------------------------------\nimport type { Asset, Doc, Layer, Point, Text } from '@flatkit/types'\nimport { resolveInstanceFrame, scheduleSounds, type Timeline } from '@flatkit/engine/timeline'\nimport { compileExpr, evalExpr, exprScope, type ExprContext, type Compiled } from '@flatkit/engine/expr'\nimport { runActions, MAX_SEND_TEXT, SEND_EVENT_NAME, type Action, type ActionHost, type ItemEvent } from '@flatkit/engine/actions'\nimport { containerLayers, getSymbol, isGroup, isInstance, isText } from '@flatkit/engine/layers'\nimport { renderLayers, type FilterCacheEntry } from './drawScene'\nimport { withCels } from '@flatkit/engine/migrateCel'\nimport { sanitizeDoc } from '@flatkit/engine/validateDoc'\nimport { applyInstanceBinds } from '@flatkit/engine/instanceBind'\nimport { importedFunctions } from '@flatkit/engine/stdlib'\nimport { namedChannels, objectChannelsById, objectParentTransform, type NamedChannels, type ObjectChannels } from '@flatkit/engine/sceneRefs'\nimport { itemBoundsByName, itemBoundsById, dropZoneBounds, tracePathByName, groupTargets } from '@flatkit/engine/groups'\nimport { projectToPath, samplePathAt, type Path } from '@flatkit/engine/path'\nimport { apply, invert, spaceConversions, IDENTITY, type Transform } from '@flatkit/engine/transform'\nimport type { Interactor } from '@flatkit/types'\nimport { hitChains } from './hit'\n\n/** A replayable gesture (`--play` / `--record` format). Coords in SCENE units. `id` = pointerId (default 1).\n * Prefer SEMANTIC gestures (`drag`/`tap` by object NAME): robust, readable, and it is the engine that\n * resolves the coords (cf. [[flatink-semantic-gestures]]). The low-level gestures remain for special cases. */\nexport type Gesture =\n // Semantic (by NAME) -- the engine resolves them into down/move/up.\n | { type: 'drag'; source: string; target: string; id?: number } // drags the `source` object onto the `target` zone\n | { type: 'tap'; target: string; id?: number } // clicks at the center of the `target` object\n | { type: 'scratch'; target: string; id?: number } // sweeps a `reveal` target's bbox (covers it -> fraction ~1)\n | { type: 'connect'; source: string; target: string; id?: number } // pulls a `link` wire from `source` to `target` (resolves the target index)\n // Low-level (scene coords).\n | { type: 'down' | 'move' | 'up' | 'cancel'; x: number; y: number; id?: number }\n | { type: 'set'; name: string; value: number } // drives a variable from the \"host\"\n | { type: 'wait'; frames: number } // lets the simulation run N fixed steps (60 Hz): `every frame` + playhead advance\n // Assertion (CI self-check): compares the `send`s emitted SINCE the last `expect` (sequence of names)\n // and the current state of the variables; any mismatch is reported in PlayResult.expectFailures (-> exit != 0 in CLI).\n | { type: 'expect'; sends?: string[]; vars?: Record<string, number | number[]> }\n\nexport type PlayerOptions = {\n autoplay?: boolean\n loop?: boolean\n padding?: number // margin around the page (CSS px)\n audio?: boolean // play the audio tracks (default: true)\n input?: boolean // attach the mouse/keyboard listeners (default: true); false = non-interactive preview (gallery): plays the anim without reacting to clicks/keys\n render?: boolean // paint the canvas (default: true); false = headless (logic/sends only, no Canvas API required)\n image?: (assetId: string) => CanvasImageSource | null // injected image provider (headless skia PNG rendering); absent = browser decoding via <img>\n // Maps an asset to a TRUSTED url/source to load (image src, audio fetch). Default: embedded `data:` URIs\n // only — never a remote URL. To serve EXTERNAL (local/same-origin) assets, pass `sameOriginAssetResolver(baseUrl)`:\n // the HOST picks the origin, the (untrusted) document only supplies a relative key, so the security contract holds.\n resolveAsset?: (asset: Asset) => string | null\n onEvent?: (event: { name: string; value?: number | string }) => void // DSL `send` channel -> host (Moiki)\n}\n\ntype View = { tx: number; ty: number; scale: number }\n// `reveal` interactor: a grid of cells over the object's world bbox; ticked cells accumulate the coverage.\ntype RevealGrid = { minX: number; minY: number; cell: number; cols: number; rows: number }\ntype RevealState = { revealCells: Set<number>; revealGrid: RevealGrid }\n\n/** \"Contain\" transform: the page fits entirely inside the canvas, centered. */\nfunction fit(cssW: number, cssH: number, docW: number, docH: number, pad: number): View {\n const scale = Math.max(0.0001, Math.min((cssW - 2 * pad) / docW, (cssH - 2 * pad) / docH))\n return { tx: (cssW - docW * scale) / 2, ty: (cssH - docH * scale) / 2, scale }\n}\n\nconst playerImgCache = new Map<string, HTMLImageElement>()\n\n/**\n * An embedded asset MUST be a self-contained `data:` URI (cf. Asset.data). A `.flatpack` is untrusted\n * content (it may be embedded in a third-party page): we never let it drive the player to a remote URL,\n * which would otherwise turn an asset reference into a tracking beacon / SSRF / CSRF request to an\n * arbitrary origin. `blob:`/`http(s):`/`file:` references are rejected.\n */\nconst isEmbeddedData = (data: string | undefined): data is string => !!data && data.startsWith('data:')\n\n/**\n * Resolver for EXTERNAL (non-embedded) assets. Pass it as `PlayerOptions.resolveAsset` to serve media from\n * a host-controlled folder instead of embedding everything as `data:`. It treats `asset.data` as a RELATIVE\n * key, resolves it against the host-trusted `baseUrl`, and returns the URL only if it stays SAME-ORIGIN as\n * baseUrl. An embedded `data:` URI is passed through; any absolute URL / scheme / protocol-relative `//host`\n * in `asset.data` (a document trying to pick its own origin) yields null. The HOST owns the origin; the\n * untrusted document only supplies a key — so the no-arbitrary-fetch contract still holds.\n */\nexport function sameOriginAssetResolver(baseUrl: string): (asset: Asset) => string | null {\n let base: URL\n try { base = new URL(baseUrl) } catch { return () => null }\n return (asset) => {\n const data = asset.data\n if (typeof data !== 'string' || !data) return null\n if (data.startsWith('data:')) return data // embedded asset → always fine\n if (data.startsWith('//') || /^[a-z][a-z0-9+.-]*:/i.test(data)) return null // carries its own scheme/authority → reject\n let url: URL\n try { url = new URL(data, base) } catch { return null }\n return url.origin === base.origin ? url.href : null\n }\n}\n\n// FIXED-STEP simulation for `onEnterFrame` (game logic / physics): we run it at a\n// CONSTANT rate (independent of rAF/screen), not \"once per rendered frame\". Otherwise a\n// loop doing \"x += speed\" every frame would run 2x too fast at 120 Hz and in slow motion below\n// 60 Hz. 60 Hz = the reference cadence (the one the demos were tuned to). The PLAYHEAD\n// (playback position, tweens) stays based on real time -> smooth rendering at any refresh rate.\n/** Variables -> the player's working Map, CLONING the ARRAYS: `set arr[i] = ...` mutates in place,\n * so without cloning the player would modify the editor doc's array (broken bricks that \"stay broken\"). */\nexport function cloneVars(vars: Record<string, number | number[]> | undefined): Map<string, number | number[]> {\n return new Map(Object.entries(vars ?? {}).map(([k, v]) => [k, Array.isArray(v) ? [...v] : v]))\n}\n/** Deep copy of a variables Map (clones the arrays) -- for the interpolation snapshot. */\nexport function cloneVarMap(m: Map<string, number | number[]>): Map<string, number | number[]> {\n const out = new Map<string, number | number[]>()\n for (const [k, v] of m) out.set(k, Array.isArray(v) ? [...v] : v)\n return out\n}\n/** Interpolated vars `lerp(prev, cur, alpha)` -- numbers and arrays; incompatible types -> `cur`. */\nexport function lerpVars(prev: Map<string, number | number[]>, cur: Map<string, number | number[]>, alpha: number): Map<string, number | number[]> {\n const out = new Map<string, number | number[]>()\n for (const [k, c] of cur) {\n const p = prev.get(k)\n if (typeof c === 'number' && typeof p === 'number') out.set(k, p + (c - p) * alpha)\n else if (Array.isArray(c) && Array.isArray(p) && p.length === c.length) out.set(k, c.map((ci, i) => (p[i] as number) + (ci - (p[i] as number)) * alpha))\n else out.set(k, c)\n }\n return out\n}\n\nconst SIM_HZ = 60\nconst SIM_STEP = 1 / SIM_HZ // seconds per simulation step\nconst SIM_MAX_STEPS = 30 // \"spiral of death\" safeguard after a long pause (backgrounded tab)\n\nconst CLICK_EVENTS: readonly ItemEvent[] = ['click']\nconst HOVER_EVENTS: readonly ItemEvent[] = ['enter', 'leave']\n// Grabbing: an item carrying one of these handlers becomes \"grabbed\" on pointerdown. While it is,\n// pointermove sends it `drag` (even if the pointer leaves the item), and pointerup -> `release`.\nconst GRAB_EVENTS: readonly ItemEvent[] = ['press', 'release', 'drag', 'longpress']\nconst LONGPRESS_MS = 500 // hold without moving -> `held`\nconst LONGPRESS_TOL = 6 // movement tolerance (world px) before canceling the hold\n\n/**\n * Fixed-step accumulator: how many simulation steps to run this tick, and the remainder to carry over.\n * Pure (testable without rAF/DOM). `dt` must already be clamped by the caller. The number of steps is\n * bounded by `max` (anti-runaway); the surplus time is then dropped (not hoarded).\n */\nexport function simSteps(acc: number, dt: number, step: number, max: number): { steps: number; acc: number } {\n let a = acc + dt\n let n = 0\n while (a >= step && n < max) { a -= step; n++ }\n if (n >= max) a = 0 // hit the ceiling -> drop the accumulated backlog\n return { steps: n, acc: a }\n}\n\n// -- Audio (WebAudio): context + decoded buffers, shared across players. --\nlet playerAudioCtx: AudioContext | null = null\nconst playerAudioBuffers = new Map<string, AudioBuffer | 'loading'>()\nfunction getAudioCtx(): AudioContext {\n if (!playerAudioCtx) playerAudioCtx = new (window.AudioContext ?? (window as unknown as { webkitAudioContext: typeof AudioContext }).webkitAudioContext)()\n return playerAudioCtx\n}\n\nexport class FlatPlayer {\n private readonly ctx: CanvasRenderingContext2D\n private doc: Doc\n private readonly loop: boolean\n private readonly pad: number\n private dpr = 1\n private cssW = 0\n private cssH = 0\n private view: View = { tx: 0, ty: 0, scale: 1 }\n private frame = 0\n private playing = false\n private raf = 0\n private last = 0\n private simAcc = 0 // time accumulator for the fixed-step simulation (onEnterFrame)\n // Render interpolation (anti-judder): we draw the motion driven by `onEnterFrame` at the INTERPOLATED\n // position between the two last sim steps, by `simAlpha = simAcc/SIM_STEP`. Otherwise a 60 Hz sim\n // on a 120 Hz screen (ProMotion) stutters. Cf. \"Fix Your Timestep\" (Gaffer).\n private prevSimVars: Map<string, number | number[]> | null = null\n private simAlpha = 1\n private simActive = false\n private audioOn: boolean\n private readonly onEvent?: (event: { name: string; value?: number | string }) => void\n private renderOn = true\n private readonly imageProvider?: (assetId: string) => CanvasImageSource | null\n private readonly resolveAsset: (asset: Asset) => string | null\n // Gesture recording (`--record`): we play by hand, capture down/up/cancel + the `move`s\n // DURING a drag (reduced volume), with `wait`s (elapsed frames) between them -> replayable script.\n private recording: Gesture[] | null = null\n private recordFrame = 0\n // Perf: cache of static filtered composites (set dressing). `imageEpoch` bumps on each decoded image\n // -> invalidates the entries depending on an asset that just loaded. Cleared when the doc changes.\n private readonly filterCache = new Map<string, FilterCacheEntry>()\n private imageEpoch = 0\n private activeSources: AudioBufferSourceNode[] = []\n // -- Interaction (Layer B) --\n private vars = new Map<string, number | number[]>()\n private procs = new Map<string, { params: string[]; body: Action[] }>() // fn name(p) { ... }\n private valueFuncs: { name: string; params: string[]; comp: Compiled }[] = [] // fn name(p) = expr (compiled)\n private funcDepth = 0 // anti-recursion guard (procedures + value functions)\n private readonly mouse = { x: 0, y: 0, dx: 0, dy: 0 } // dx/dy = movement SINCE the last tick (reset to 0 after onEnterFrame) -> \"is the mouse moving this frame?\"\n private readonly heldKeys = new Set<string>()\n private readonly keyProxy = new Proxy(\n {},\n { get: (_t, k) => (typeof k === 'string' && this.heldKeys.has(k) ? 1 : 0) },\n ) as Record<string, number>\n private hovered: string | null = null\n private hoverIds = new Set<string>() // ALL ids in the topmost hit chain under the pointer (for self.hovered feedback, handler-independent)\n private selfChannels: ObjectChannels | null = null // `self` in a handler = the targeted object's channels (set for the duration of runActions)\n private selfParent: Transform | null = null // world transform of the targeted object's parent -> toLocal/toGlobal conversions\n // namedChannels resolves the WHOLE scene (costly); memoized per frame -- otherwise recomputed on every\n // evalNumber (hundreds/frame in a game) -> stutter. Invalidated by inputs (cf. bustNamed).\n private namedCache: NamedChannels | null = null\n private namedFrame = Number.NaN\n // -- Grabbing (drag / press / long-press) --\n private grabbed: string | null = null // grabbed item (between pointerdown and pointerup)\n private grabStart: Point = { x: 0, y: 0 } // world point of the grab (long-press tolerance)\n private dragActive: { it: Interactor; offX: number; offY: number; parentInv: Transform; tracePath?: Path | null; traceMaxT?: number; revealCells?: Set<number>; revealGrid?: RevealGrid } | null = null // \"drag\" interactor in progress (parentInv cached at grab time)\n // `reveal` coverage PERSISTED per target across grabs → true monotonicity (a child scratching with\n // several short strokes keeps accumulating instead of resetting to the current stroke each grab).\n private readonly revealStates = new Map<string, RevealState>()\n private longPressTimer: ReturnType<typeof setTimeout> | null = null\n private lastFrameInt = -1\n private readonly onResize = () => {\n this.measure()\n this.render()\n }\n /** Invalidates the named-objects cache (input changed outside of a frame advance). */\n private bustNamed(): void {\n this.namedFrame = Number.NaN\n }\n private readonly onKeyDown = (e: KeyboardEvent) => {\n this.heldKeys.add(e.key)\n if (e.key === ' ') this.heldKeys.add('Space')\n this.bustNamed()\n }\n private readonly onKeyUp = (e: KeyboardEvent) => {\n this.heldKeys.delete(e.key)\n if (e.key === ' ') this.heldKeys.delete('Space')\n this.bustNamed()\n }\n private readonly onPointerLeave = () => {\n // Safety net: if pointer capture is not supported, a pointer that leaves releases the grab.\n if (this.grabbed) {\n const id = this.grabbed\n this.clearGrab()\n this.fireEvent(id, 'release')\n }\n if (this.hovered) {\n this.fireEvent(this.hovered, 'leave')\n this.hovered = null\n }\n this.hoverIds.clear() // pointer left the canvas → nothing hovered\n this.render()\n }\n private worldPoint(e: { clientX: number; clientY: number }): Point {\n const r = this.canvas.getBoundingClientRect()\n return { x: (e.clientX - r.left - this.view.tx) / this.view.scale, y: (e.clientY - r.top - this.view.ty) / this.view.scale }\n }\n /**\n * Target of an event at a point: we walk ALL the hit chains (top to bottom) and, within each,\n * from deepest to root. The first item carrying a handler for `event` wins. This way a click\n * \"falls through\" a non-interactive item placed on top down to the clickable one below, instead\n * of being swallowed by it.\n */\n private pickTarget(chains: string[][], events: readonly ItemEvent[]): string | null {\n const inter = this.doc.interactions\n if (!inter) return null\n for (const chain of chains) {\n for (let i = chain.length - 1; i >= 0; i--) {\n if (inter.some((x) => x.targetId === chain[i] && events.includes(x.event))) return chain[i]\n }\n }\n return null\n }\n private interactorFor(id: string): Interactor | undefined {\n return this.doc.interactors?.find((x) => x.targetId === id)\n }\n /** Is the drag active? `enabled` absent = always; otherwise true iff the expression is != 0. */\n private interactorEnabled(it: Interactor): boolean {\n return !it.enabled || this.evalNumber(it.enabled) !== 0\n }\n /** Topmost grabbable item carrying an ACTIVE (drag) interactor, at a point. */\n private pickInteractor(chains: string[][]): string | null {\n const ins = this.doc.interactors\n if (!ins?.length) return null\n for (const chain of chains) for (let i = chain.length - 1; i >= 0; i--) if (ins.some((x) => x.targetId === chain[i] && this.interactorEnabled(x))) return chain[i]\n return null\n }\n /** Applies the current drag: position = pointer + grab offset, then snap, then confine; writes varX/varY.\n * Snap/confine are in WORLD space (where the visual grid and the zone bbox live); the result is brought back into\n * the object's PARENT space (`parentInv` cached at grab time -- no scene walk per movement). */\n /** Writes a gesture output: simple variable `name`, or array element `name[idx]` (idx is EVALUATED).\n * The indexed form is the natural output under `each` (e.g. `drag hx[i], hy[i]` -> `hx[0]`... after unfolding). */\n private writeOut(target: string, value: number): void {\n const lb = target.indexOf('[')\n if (lb < 0) { this.vars.set(target, value); return }\n const a = this.vars.get(target.slice(0, lb))\n if (!Array.isArray(a)) return\n const i = Math.round(this.evalNumber(target.slice(lb + 1, target.lastIndexOf(']'))))\n if (i >= 0 && i < a.length) a[i] = value\n }\n private applyDrag(p: Point): void {\n const d = this.dragActive\n if (!d) return\n if (d.it.axis === 'turn') { // rotation: the object points toward the cursor (pivot->pointer angle, in degrees)\n const piv = d.it.pivot ?? { x: 0, y: 0 }\n let a = (Math.atan2(p.y - piv.y, p.x - piv.x) * 180) / Math.PI\n if (d.it.grid && d.it.grid > 0) a = Math.round(a / d.it.grid) * d.it.grid\n if (d.it.varX) this.writeOut(d.it.varX, a)\n this.bustNamed()\n return\n }\n if (d.it.axis === 'trace') { // follow a path: progress 0..1 (monotone) as long as we stay within tolerance\n const path = d.tracePath\n if (path && path.subpaths.length) {\n const t = projectToPath(path, p)\n const near = samplePathAt(path, t).point\n const tol = d.it.grid && d.it.grid > 0 ? d.it.grid : 24 // tolerance (px); default 24\n if (Math.hypot(p.x - near.x, p.y - near.y) <= tol) d.traceMaxT = Math.max(d.traceMaxT ?? 0, t)\n if (d.it.varX) this.writeOut(d.it.varX, d.traceMaxT ?? 0)\n this.bustNamed()\n }\n return\n }\n if (d.it.axis === 'reveal') { // scratch/wipe: ticks the cells of a grid covered by the brush -> fraction 0..1 (monotone)\n const g = d.revealGrid\n const cells = d.revealCells\n if (g && cells) {\n const brush = d.it.grid && d.it.grid > 0 ? d.it.grid : 24 // brush radius (px); default 24\n const c0 = Math.max(0, Math.floor((p.x - brush - g.minX) / g.cell)) // bounds of the nearby cells (avoids sweeping the whole grid)\n const c1 = Math.min(g.cols - 1, Math.floor((p.x + brush - g.minX) / g.cell))\n const r0 = Math.max(0, Math.floor((p.y - brush - g.minY) / g.cell))\n const r1 = Math.min(g.rows - 1, Math.floor((p.y + brush - g.minY) / g.cell))\n for (let r = r0; r <= r1; r++) for (let c = c0; c <= c1; c++) {\n const cx = g.minX + (c + 0.5) * g.cell\n const cy = g.minY + (r + 0.5) * g.cell\n if (Math.hypot(p.x - cx, p.y - cy) <= brush) cells.add(r * g.cols + c) // cell center within the brush -> ticked\n }\n if (d.it.varX) this.writeOut(d.it.varX, cells.size / (g.cols * g.rows))\n this.bustNamed()\n }\n return\n }\n if (d.it.axis === 'link') { // pull a wire: the free end follows the pointer (the author DRAWS the wire via endX/endY); target resolved on release\n if (d.it.varX) this.writeOut(d.it.varX, p.x)\n if (d.it.varY) this.writeOut(d.it.varY, p.y)\n this.bustNamed()\n return\n }\n let x = p.x + d.offX\n let y = p.y + d.offY\n if (d.it.grid && d.it.grid > 0) { x = Math.round(x / d.it.grid) * d.it.grid; y = Math.round(y / d.it.grid) * d.it.grid }\n if (d.it.confine) {\n const b = itemBoundsByName(this.doc, d.it.confine)\n if (b) { x = Math.max(b.minX, Math.min(b.maxX, x)); y = Math.max(b.minY, Math.min(b.maxY, y)) }\n }\n const local = apply(d.parentInv, { x, y }) // world -> parent-local (identity at the root)\n if (d.it.axis !== 'y' && d.it.varX) this.writeOut(d.it.varX, local.x)\n if (d.it.axis !== 'x' && d.it.varY) this.writeOut(d.it.varY, local.y)\n this.bustNamed()\n }\n /** Reveal state for a target: the grid of cells (side = brush) over its WORLD bbox, with the ticked cells\n * PERSISTED across grabs (`revealStates`) so coverage accumulates monotonically over several strokes. */\n private revealGridFor(id: string, it: Interactor): RevealState | Record<string, never> {\n const cached = this.revealStates.get(id)\n if (cached) return cached // keep accumulating from a previous grab\n const b = itemBoundsById(this.doc, id)\n if (!b) return {}\n const cell = it.grid && it.grid > 0 ? it.grid : 24\n const cols = Math.max(1, Math.ceil((b.maxX - b.minX) / cell))\n const rows = Math.max(1, Math.ceil((b.maxY - b.minY) / cell))\n const state: RevealState = { revealCells: new Set<number>(), revealGrid: { minX: b.minX, minY: b.minY, cell, cols, rows } }\n this.revealStates.set(id, state)\n return state\n }\n /** On release of a `link`: 1st target (named child of the group) containing the pointer -> index 1..n (0 = none).\n * If linked, the wire end (endX/endY) sticks to the target center; otherwise it stays at the pointer (the author handles\n * the \"return\" via target == 0). Several links coexist: one `link` interactor per source object. */\n private resolveLink(it: Interactor, pointer: Point): void {\n const targets = it.confine ? groupTargets(this.doc, it.confine) : []\n let hit = 0\n for (let i = 0; i < targets.length; i++) {\n const b = targets[i].bbox\n if (pointer.x >= b.minX && pointer.x <= b.maxX && pointer.y >= b.minY && pointer.y <= b.maxY) {\n hit = i + 1\n if (it.varX) this.writeOut(it.varX, (b.minX + b.maxX) / 2)\n if (it.varY) this.writeOut(it.varY, (b.minY + b.maxY) / 2)\n break\n }\n }\n if (it.varT) this.writeOut(it.varT, hit)\n this.bustNamed()\n }\n /** On release: `when dropped on Zone` whose tested point falls within the zone bbox.\n * Tested point = the object's CENTER by default, or the POINTER if `at pointer`. Zone = the group's\n * explicit `hitbox` if present, otherwise the (static) bbox of its content. */\n private fireDrops(id: string, pointer: Point): void {\n const drops = this.doc.interactions?.filter((x) => x.targetId === id && x.event === 'drop')\n if (!drops?.length) return\n const pos = objectChannelsById(this.doc, id, this.frame, this.exprCtx(), this.fps)\n const center: Point = { x: pos?.x ?? pointer.x, y: pos?.y ?? pointer.y }\n for (const d of drops) {\n if (!d.over) continue\n const t = d.atPointer ? pointer : center\n const b = dropZoneBounds(this.doc, d.over)\n if (b && t.x >= b.minX && t.x <= b.maxX && t.y >= b.minY && t.y <= b.maxY) runActions(d.actions, this.host)\n }\n }\n private fireEvent(id: string, event: ItemEvent): void {\n const matched = this.doc.interactions?.filter((x) => x.targetId === id && x.event === event)\n if (!matched?.length) return // no handler -> we skip the self/conversion setup (2 scene walks)\n // `self` + conversions in the handler: resolved BEFORE (ctx without self to avoid recursion),\n // set for the duration of the actions, then restored.\n const prevSelf = this.selfChannels\n const prevParent = this.selfParent\n this.selfChannels = null\n this.selfParent = null\n const ctx = this.exprCtx()\n this.selfChannels = objectChannelsById(this.doc, id, this.frame, ctx, this.fps) ?? null\n this.selfParent = objectParentTransform(this.doc, id, this.frame, ctx, this.fps) ?? IDENTITY\n for (const x of matched) runActions(x.actions, this.host)\n this.selfChannels = prevSelf\n this.selfParent = prevParent\n }\n private readonly onPointerMove = (e: PointerEvent) => {\n const p = this.worldPoint(e)\n this.mouse.dx += p.x - this.mouse.x // accumulate the movement until the next tick\n this.mouse.dy += p.y - this.mouse.y\n this.mouse.x = p.x\n this.mouse.y = p.y\n this.bustNamed() // the mouse moved -> objects bound to mouse.* must be refreshed\n // Grab in progress: the grabbed item receives `drag` (even if the pointer leaves it).\n if (this.grabbed) {\n this.record('move', p, e.pointerId) // record moves ONLY during a drag (reduced volume)\n if (Math.hypot(p.x - this.grabStart.x, p.y - this.grabStart.y) > LONGPRESS_TOL) this.cancelLongPress()\n this.canvas.style.cursor = 'grabbing'\n this.applyDrag(p) // \"drag\" interactor: writes varX/varY (no-op if no active drag)\n this.fireEvent(this.grabbed, 'drag')\n this.render()\n return\n }\n if (this.doc.interactions?.length || this.doc.interactors?.length) {\n const chains = hitChains(this.doc, this.frame, this.exprCtx(), p)\n this.hoverIds = new Set(chains[0] ?? []) // topmost stack under the pointer → drives self.hovered feedback\n this.canvas.style.cursor = (this.pickTarget(chains, GRAB_EVENTS) ?? this.pickInteractor(chains)) ? 'grab' : this.pickTarget(chains, CLICK_EVENTS) ? 'pointer' : 'default'\n const hov = this.pickTarget(chains, HOVER_EVENTS)\n if (hov !== this.hovered) {\n if (this.hovered) this.fireEvent(this.hovered, 'leave')\n if (hov) this.fireEvent(hov, 'enter')\n this.hovered = hov\n }\n }\n this.render() // follows the mouse (and reflects enter/leave)\n }\n private readonly onPointerDown = (e: PointerEvent) => {\n if (!this.doc.interactions?.length && !this.doc.interactors?.length) return\n const p = this.worldPoint(e)\n this.record('down', p, e.pointerId)\n const chains = hitChains(this.doc, this.frame, this.exprCtx(), p)\n const clickId = this.pickTarget(chains, CLICK_EVENTS)\n const grabId = this.pickTarget(chains, GRAB_EVENTS) ?? this.pickInteractor(chains) // grabbable = handler OR interactor\n if (clickId) this.fireEvent(clickId, 'click')\n if (grabId) {\n this.grabbed = grabId\n this.grabStart = p\n const inter = this.interactorFor(grabId)\n if (inter && this.interactorEnabled(inter)) { // capture the grab offset (the clicked point stays under the cursor) + the parent transform\n const ctx = this.exprCtx()\n const pos = objectChannelsById(this.doc, grabId, this.frame, ctx, this.fps)\n const parent = objectParentTransform(this.doc, grabId, this.frame, ctx, this.fps) ?? IDENTITY\n this.dragActive = { it: inter, offX: (pos?.x ?? p.x) - p.x, offY: (pos?.y ?? p.y) - p.y, parentInv: invert(parent), ...(inter.axis === 'trace' ? { tracePath: inter.confine ? tracePathByName(this.doc, inter.confine) : null, traceMaxT: 0 } : {}), ...(inter.axis === 'reveal' ? this.revealGridFor(grabId, inter) : {}) }\n }\n this.canvas.setPointerCapture?.(e.pointerId) // keep the drag even if the pointer leaves the canvas\n this.fireEvent(grabId, 'press')\n this.cancelLongPress()\n this.longPressTimer = setTimeout(() => {\n this.longPressTimer = null\n if (this.grabbed === grabId) { this.fireEvent(grabId, 'longpress'); this.render() }\n }, LONGPRESS_MS)\n }\n if (clickId || grabId) this.render() // reflects the changes (variables/frame)\n }\n private readonly onPointerUp = (e: PointerEvent) => {\n if (!this.grabbed) return\n const id = this.grabbed\n const p = this.worldPoint(e)\n this.record('up', p, e.pointerId)\n this.canvas.releasePointerCapture?.(e.pointerId)\n // Write the gesture outputs BEFORE emitting `release`, so a `when released` handler can read them\n // (link target index / end position) — consistent with `drag`, which writes its vars before `dragged`.\n if (this.dragActive?.it.axis === 'link') this.resolveLink(this.dragActive.it, p) // tests the reached target -> writes the index (0 = none)\n this.fireEvent(id, 'release')\n if (this.dragActive) this.fireDrops(id, p) // `when dropped on Zone`\n this.clearGrab()\n this.render()\n }\n // Interrupted gesture (canceled touch, OS gesture): we release WITHOUT a drop (the pointer did not \"let go\" on a target).\n private readonly onPointerCancel = (e: PointerEvent) => {\n if (!this.grabbed) return\n const id = this.grabbed\n this.record('cancel', this.worldPoint(e), e.pointerId)\n this.canvas.releasePointerCapture?.(e.pointerId)\n this.fireEvent(id, 'release')\n this.clearGrab()\n this.render()\n }\n private cancelLongPress(): void {\n if (this.longPressTimer !== null) { clearTimeout(this.longPressTimer); this.longPressTimer = null }\n }\n private clearGrab(): void {\n this.grabbed = null\n this.dragActive = null\n this.cancelLongPress()\n }\n /** Surface exposed to the action interpreter (frame-actions, future onClick). */\n private readonly host: ActionHost = {\n play: () => this.play(),\n pause: () => this.pause(),\n seek: (f) => this.seek(f),\n labelFrame: (name) => this.doc.timeline?.labels?.find((l) => l.name === name)?.frame,\n setVar: (name, v) => { this.vars.set(name, v) },\n setIndex: (name, i, v) => { const a = this.vars.get(name); if (Array.isArray(a) && i >= 0 && i < a.length) a[i] = v },\n callProc: (name, args) => this.callProc(name, args),\n evalNumber: (src) => this.evalNumber(src),\n emit: (name, value) => this.emit(name, value),\n textContent: (itemId) => this.textContent(itemId),\n playSound: (assetId) => this.playSound(assetId),\n }\n\n constructor(\n private readonly canvas: HTMLCanvasElement,\n doc: Doc,\n opts: PlayerOptions = {},\n ) {\n const ctx = canvas.getContext('2d')\n if (!ctx) throw new Error('FlatPlayer: 2D context unavailable')\n this.ctx = ctx\n this.doc = applyInstanceBinds(withCels(sanitizeDoc(doc)))\n this.loop = opts.loop ?? true\n this.pad = opts.padding ?? 0\n this.audioOn = opts.audio ?? true\n this.onEvent = opts.onEvent\n this.renderOn = opts.render ?? true\n this.imageProvider = opts.image\n // Default resolver: embedded `data:` URIs only (no remote fetch) — the secure default.\n this.resolveAsset = opts.resolveAsset ?? ((a) => (isEmbeddedData(a.data) ? a.data : null))\n this.vars = cloneVars(doc.variables)\n this.buildFunctions()\n this.measure()\n this.render()\n this.fireLoad()\n window.addEventListener('resize', this.onResize)\n if (opts.input ?? true) { // false (gallery preview): plays the anim but does not attach the inputs\n globalThis.addEventListener('keydown', this.onKeyDown)\n globalThis.addEventListener('keyup', this.onKeyUp)\n this.canvas.addEventListener('pointermove', this.onPointerMove)\n this.canvas.addEventListener('pointerdown', this.onPointerDown)\n this.canvas.addEventListener('pointerup', this.onPointerUp)\n this.canvas.addEventListener('pointercancel', this.onPointerCancel)\n this.canvas.addEventListener('pointerleave', this.onPointerLeave)\n }\n if (opts.autoplay) this.play()\n }\n\n /**\n * Timelines of \"active\" symbols (referenced by >=1 instance), deduplicated,\n * with a representative local frame. NB (v1): single playhead -> the symbol\n * actions share the global state; gotoFrame/play acts on the root.\n */\n private activeSymbolTimelines(rootFrame: number): { tl: Timeline; frame: number }[] {\n const out: { tl: Timeline; frame: number }[] = []\n const seenSym = new Set<string>()\n const walk = (layers: Layer[], frame: number, seen: Set<string>) => {\n for (const layer of layers) {\n for (const it of layer.items) {\n if (isInstance(it)) {\n if (seen.has(it.symbolId)) continue\n const sym = getSymbol(this.doc, it.symbolId)\n const local = sym?.timeline ? resolveInstanceFrame(it.playback, frame, sym.timeline.durationFrames) : frame\n if (sym?.timeline && !seenSym.has(it.symbolId)) {\n seenSym.add(it.symbolId)\n out.push({ tl: sym.timeline, frame: local })\n }\n walk(containerLayers(this.doc, it), local, new Set([...seen, it.symbolId]))\n } else if (isGroup(it)) {\n walk(it.layers, frame, seen)\n }\n }\n }\n }\n walk(this.doc.layers, rootFrame, new Set())\n return out\n }\n\n /** Actions on load (onLoad): root + active symbols. */\n private fireLoad(): void {\n let changed = false\n if (this.doc.timeline?.onLoad?.length) {\n runActions(this.doc.timeline.onLoad, this.host)\n changed = true\n }\n for (const s of this.activeSymbolTimelines(0)) {\n if (s.tl.onLoad?.length) {\n runActions(s.tl.onLoad, this.host)\n changed = true\n }\n }\n if (changed) this.render()\n }\n\n /** (Re)compiles the available functions: imported packages (`use ...`) + doc functions (`fn ...`,\n * which TAKE PRECEDENCE on a name clash). -> procedures + value functions. */\n private buildFunctions(): void {\n this.procs.clear()\n this.valueFuncs = []\n for (const f of [...importedFunctions(this.doc.imports), ...(this.doc.functions ?? [])]) {\n if (f.kind === 'proc') this.procs.set(f.name, { params: f.params, body: f.body })\n else this.valueFuncs.push({ name: f.name, params: f.params, comp: compileExpr(f.expr) })\n }\n }\n\n /** Calls a procedure `fn name(p) { ... }`: binds the params (save/restore), bounds the recursion. */\n private callProc(name: string, args: number[]): void {\n const f = this.procs.get(name)\n if (!f || this.funcDepth > 64) return\n const saved = f.params.map((p) => [p, this.vars.get(p)] as const)\n f.params.forEach((p, i) => this.vars.set(p, args[i] ?? 0))\n this.funcDepth++\n runActions(f.body, this.host)\n this.funcDepth--\n for (const [p, v] of saved) { if (v === undefined) this.vars.delete(p); else this.vars.set(p, v) }\n }\n\n /** Runtime context for expressions: variables (flattened), mouse, keys, random, value functions,\n * + scene objects by name (`Hero.x`, cf. sceneRefs). */\n private exprCtx(vars: Map<string, number | number[]> = this.vars): ExprContext {\n const interp = vars !== this.vars // interpolated render context -> we do not touch the memo (frame unchanged)\n const ctx: ExprContext = { mouse: this.mouse, keys: this.keyProxy, random: () => Math.random() }\n for (const [k, v] of vars) ctx[k] = v\n for (const vf of this.valueFuncs) { // fn name(p) = expr -> closure (the body sees globals + math + time + params)\n ctx[vf.name] = (...args: number[]) => {\n if (this.funcDepth > 64 || !vf.comp.ok) return Number.NaN\n const local = exprScope(ctx, this.frame / this.fps, this.frame)\n vf.params.forEach((p, i) => { local[p] = args[i] ?? 0 })\n this.funcDepth++\n const r = evalExpr(vf.comp.node, local, Number.NaN)\n this.funcDepth--\n return r\n }\n }\n // Named scene objects (Hero.x, Enemy.y...): resolved with the BASE ctx (above) to\n // avoid any recursive cross-reference; never overwrites a variable/function of the same name.\n // Memoized for the current frame: reused by all the evalNumber of a single tick (the variable\n // mutations within a frame do not bust it -> a named object's position = start-of-frame snapshot,\n // consistent with the \"one level\" resolution; bustNamed() refreshes it on mouse/keyboard/seek/load).\n if (interp) {\n // Interpolated render: we recompute the named channels from the interpolated vars (no memo).\n const named = namedChannels(this.doc, this.frame, ctx, this.fps)\n for (const name in named) if (!(name in ctx)) ctx[name] = named[name]\n } else {\n if (!this.namedCache || this.namedFrame !== this.frame) {\n this.namedCache = namedChannels(this.doc, this.frame, ctx, this.fps)\n this.namedFrame = this.frame\n }\n for (const name in this.namedCache) if (!(name in ctx)) ctx[name] = this.namedCache[name]\n }\n // `self` set during a handler's execution (cf. fireEvent); in a channel binding, cel/timeline\n // re-inject it with the binding's object (priority). Absent (null) outside a handler -> no `self`.\n if (this.selfChannels) ctx.self = this.selfChannels\n // World<->local conversions relative to the handler's object (cf. RFC coordinate-spaces): a WORLD point\n // (mouse.x, Hero.x) -> the object's PARENT space (where its x/y live), and inverse.\n if (this.selfParent) Object.assign(ctx, spaceConversions(this.selfParent))\n return ctx\n }\n private evalNumber(src: string): number {\n const c = compileExpr(src)\n if (!c.ok) return 0\n return evalExpr(c.node, exprScope(this.exprCtx(), this.frame / this.fps, this.frame), 0)\n }\n\n /**\n * Emits a `send` event toward the host. Silent no-op if no `onEvent` is provided\n * (e.g. editor preview). Defense-in-depth validation (the parser already guarantees the name):\n * conforming name, finite number (NaN -> 0, DSL convention), text <= MAX_SEND_TEXT (truncated).\n * If the host callback throws, we catch and log -- the player does not break.\n */\n private emit(name: string, value?: number | string): void {\n if (!this.onEvent || !SEND_EVENT_NAME.test(name)) return\n let v = value\n if (typeof v === 'number') { if (!Number.isFinite(v)) v = 0 }\n else if (typeof v === 'string' && v.length > MAX_SEND_TEXT) v = v.slice(0, MAX_SEND_TEXT)\n try {\n this.onEvent(v === undefined ? { name } : { name, value: v })\n } catch (e) {\n console.error('FlatPlayer: the onEvent callback threw an exception', e)\n }\n }\n\n /** Live content of a Text item resolved by id OR name (for `text(\"...\")`). `''` + warning if absent. */\n private textContent(key: string): string {\n const t = this.findText(key)\n if (!t) {\n console.warn(`FlatPlayer: text(\"${key}\") -- no Text item \"${key}\" (id or name) in the document`)\n return ''\n }\n return t.content.length > MAX_SEND_TEXT ? t.content.slice(0, MAX_SEND_TEXT) : t.content\n }\n\n /**\n * Looks up a Text item in the scene (layers + groups) and the library symbols.\n * Resolves by `id` first (stable id set via `text \"...\" as \"<id>\"`), then by `name` as a fallback --\n * like the rest of the text format references by name (`object \"x\"`, `instance \"Sym\" as \"y\"`).\n */\n private findText(key: string): Text | undefined {\n const scan = (layers: Layer[], match: (t: Text) => boolean): Text | undefined => {\n for (const layer of layers) {\n for (const it of layer.items) {\n if (isText(it)) { if (match(it)) return it }\n else if (isGroup(it)) { const f = scan(it.layers, match); if (f) return f }\n }\n }\n return undefined\n }\n const find = (match: (t: Text) => boolean): Text | undefined => {\n const inScene = scan(this.doc.layers, match)\n if (inScene) return inScene\n for (const s of this.doc.symbols) { const f = scan(s.layers, match); if (f) return f }\n return undefined\n }\n return find((t) => t.id === key) ?? find((t) => t.name === key)\n }\n\n /** Plays an audio clip (asset) as a one-shot (`sound \"id\"` DSL). No-op if audio is off / asset absent. */\n private playSound(assetId: string): void {\n if (!this.audioOn) return\n const c = getAudioCtx()\n if (c.state === 'suspended') void c.resume()\n const buf = playerAudioBuffers.get(assetId)\n if (!buf || buf === 'loading') { this.decodeAudio(assetId); return } // decoded in the background -> audible on the next trigger\n const src = c.createBufferSource()\n src.buffer = buf\n src.connect(c.destination)\n src.onended = () => { this.activeSources = this.activeSources.filter((s) => s !== src) } // frees the finished one-shot (anti-leak if `sound` is spammed)\n src.start()\n this.activeSources.push(src)\n }\n\n get fps(): number {\n return Math.max(1, this.doc.timeline?.fps ?? 24) // safeguard: fps <= 0 (dubious timeline directive) -> no division by 0\n }\n get duration(): number {\n return Math.max(1, this.doc.timeline?.durationFrames ?? 1)\n }\n get currentFrame(): number {\n return this.frame\n }\n get isPlaying(): boolean {\n return this.playing\n }\n\n /**\n * Reads a state variable (Layer B) from the host: score, difficulty... `undefined` if absent.\n * Returns a COPY of the arrays (the host cannot mutate the internal state by reference).\n */\n getVar(name: string): number | number[] | undefined {\n const v = this.vars.get(name)\n return Array.isArray(v) ? [...v] : v\n }\n\n /** Snapshot (copied) of all the state variables -- for debugging / the headless harness. */\n allVars(): Record<string, number | number[]> {\n const out: Record<string, number | number[]> = {}\n for (const [k, v] of this.vars) out[k] = Array.isArray(v) ? [...v] : v\n return out\n }\n\n // -- Gesture recording (`--record`) --\n /** Starts recording (clears the previous one). Play the activity by hand, then `stopRecording()`. */\n startRecording(): void { this.recording = []; this.recordFrame = this.frame }\n /** Stops recording and returns the gesture script (replayable by `--play` / `playHeadless`). */\n stopRecording(): Gesture[] { const r = this.recording ?? []; this.recording = null; return r }\n get isRecording(): boolean { return this.recording != null }\n\n /** Center (RESOLVED origin, expressions included) of a named object, in world coords -- for the\n * semantic gestures `drag`/`tap` by name. `null` if the object does not exist. */\n objectCenter(name: string): Point | null {\n this.exprCtx() // (re)computes the named-objects cache for the current frame\n const ch = this.namedCache?.[name]\n return ch ? { x: ch.x, y: ch.y } : null\n }\n /** Captures a gesture (no-op outside recording). Inserts a `wait` = frames elapsed since the last gesture. */\n private record(type: 'down' | 'move' | 'up' | 'cancel', p: Point, id: number): void {\n if (!this.recording) return\n const dframes = Math.round(this.frame - this.recordFrame)\n if (dframes > 0) { this.recording.push({ type: 'wait', frames: dframes }); this.recordFrame = this.frame }\n const r = (n: number) => Math.round(n * 100) / 100\n this.recording.push({ type, x: r(p.x), y: r(p.y), ...(id !== 1 ? { id } : {}) })\n }\n\n /**\n * Writes a state variable (Layer B) from the host, then redraws -> bidirectional\n * driving channel (the host sets the difficulty, injects a value, etc.). Clones the arrays.\n */\n setVar(name: string, value: number | number[]): void {\n this.vars.set(name, Array.isArray(value) ? [...value] : value)\n this.bustNamed() // host-driven change -> named objects bound to this variable must be refreshed\n this.render()\n }\n\n /** Replaces the played document (resets the framing + the variables, keeps the frame). */\n load(doc: Doc): void {\n this.doc = applyInstanceBinds(withCels(sanitizeDoc(doc)))\n this.vars = cloneVars(doc.variables)\n this.namedCache = null // new document -> named-objects cache stale\n this.filterCache.clear() // new document -> filter bitmaps stale\n this.revealStates.clear() // new document -> reveal coverage resets\n this.bustNamed()\n this.buildFunctions()\n this.measure()\n this.render()\n this.fireLoad()\n }\n\n private measure(): void {\n const r = this.canvas.getBoundingClientRect()\n this.dpr = window.devicePixelRatio || 1\n this.cssW = r.width\n this.cssH = r.height\n this.canvas.width = Math.max(1, Math.round(r.width * this.dpr))\n this.canvas.height = Math.max(1, Math.round(r.height * this.dpr))\n this.view = fit(r.width, r.height, this.doc.width, this.doc.height, this.pad)\n }\n\n /** Draws the current frame (pure, without advancing time). */\n render(): void {\n if (!this.renderOn) return // headless: no painting (no Canvas API required)\n const { ctx, doc, view, dpr } = this\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0)\n ctx.clearRect(0, 0, this.cssW, this.cssH)\n ctx.save()\n ctx.translate(view.tx, view.ty)\n ctx.scale(view.scale, view.scale)\n if (doc.background) {\n ctx.fillStyle = doc.background\n ctx.fillRect(0, 0, doc.width, doc.height)\n }\n ctx.beginPath()\n ctx.rect(0, 0, doc.width, doc.height)\n ctx.clip()\n // Anti-judder: during a game's playback (sim active), we draw the motion at the INTERPOLATED\n // position between the two last sim steps (the mouse/playhead themselves stay at the current instant).\n const expr = this.playing && this.simActive && this.prevSimVars && this.simAlpha < 1\n ? this.exprCtx(lerpVars(this.prevSimVars, this.vars, this.simAlpha))\n : this.exprCtx()\n renderLayers(ctx, doc, doc.layers, this.frame, null, new Set(), { fps: this.fps, expr, image: (id) => this.imageFor(id), filterCache: this.filterCache, imageEpoch: this.imageEpoch, itemState: (id) => this.itemStateFor(id) })\n ctx.restore()\n }\n\n /** Interaction state of an item for `self.hovered`/`self.grabbed`/`self.pressed` in its channel exprs.\n * Returns undefined when the item is neither hovered nor grabbed (the cheap, common path → flags 0). */\n private itemStateFor(id: string): { hovered: number; grabbed: number; pressed: number } | undefined {\n const hovered = this.hoverIds.has(id) ? 1 : 0\n const grabbed = this.grabbed === id ? 1 : 0\n return hovered || grabbed ? { hovered, grabbed, pressed: grabbed } : undefined\n }\n\n // Decoded image of an asset (module cache). `null` while not loaded -> re-render on decode.\n private imageFor(assetId: string): CanvasImageSource | null {\n if (this.imageProvider) return this.imageProvider(assetId) // headless backend (skia): pre-decoded images\n const a = this.doc.assets?.find((x) => x.id === assetId)\n if (!a) return null\n const url = this.resolveAsset(a) // host-trusted url (default: data: URIs only, no remote fetch)\n if (url == null) return null\n let img = playerImgCache.get(a.id)\n if (!img) {\n img = new Image()\n img.onload = () => { this.imageEpoch++; this.render() } // asset loaded -> invalidate the filter cache\n img.src = url\n playerImgCache.set(a.id, img)\n }\n return img.complete && img.naturalWidth > 0 ? img : null\n }\n\n seek(frame: number): void {\n this.frame = Math.max(0, Math.min(this.duration, frame))\n this.lastFrameInt = Math.floor(this.frame) // a seek does not trigger the frame-actions (anti-loop)\n if (this.playing) this.startAudio(this.frame) // resyncs the audio\n this.render()\n }\n\n /**\n * Advances the simulation by `steps` FIXED steps (60 Hz) without RAF: runs `onEnterFrame`\n * (root + active symbols) and advances the playhead as if real time elapsed.\n * Used by the headless mode (`--play`, `wait` gesture) to let a physics simulation\n * unfold between two gestures -- in Node, requestAnimationFrame does not exist.\n */\n stepSim(steps: number): void {\n const rootSim = this.doc.timeline?.onEnterFrame\n for (let i = 0; i < Math.max(0, Math.floor(steps)); i++) {\n let f = this.frame + SIM_STEP * this.fps\n if (f >= this.duration) f = this.loop ? f % this.duration : this.duration\n this.frame = f\n const symSims = this.activeSymbolTimelines(f).filter((s) => s.tl.onEnterFrame?.length)\n if (rootSim?.length) runActions(rootSim, this.host)\n for (const s of symSims) runActions(s.tl.onEnterFrame!, this.host)\n this.mouse.dx = 0 // movement consumed by this step (same contract as the real tick)\n this.mouse.dy = 0\n this.fireFrameActions()\n }\n this.render()\n }\n\n // -- Audio --\n get audioEnabled(): boolean {\n return this.audioOn\n }\n /** Enables/disables audio (cuts immediately if off; (re)starts if on and playing). */\n setAudio(on: boolean): void {\n if (on === this.audioOn) return\n this.audioOn = on\n if (!on) this.stopAudio()\n else if (this.playing) this.startAudio(this.frame)\n }\n private stopAudio(): void {\n for (const s of this.activeSources) { try { s.stop() } catch { /* already stopped */ } }\n this.activeSources = []\n }\n private decodeAudio(assetId: string): void {\n if (playerAudioBuffers.has(assetId)) return\n const a = this.doc.assets?.find((x) => x.id === assetId)\n if (!a) return\n const url = this.resolveAsset(a) // host-trusted url (default: data: URIs only, no remote fetch)\n if (url == null) return\n playerAudioBuffers.set(assetId, 'loading')\n fetch(url).then((r) => r.arrayBuffer()).then((b) => getAudioCtx().decodeAudioData(b)).then((buf) => playerAudioBuffers.set(assetId, buf)).catch(() => playerAudioBuffers.delete(assetId))\n }\n /** (Re)schedules the audio clips for a playback starting from `fromFrame`. */\n private startAudio(fromFrame: number): void {\n this.stopAudio()\n const sounds = this.doc.timeline?.sounds\n if (!this.audioOn || !sounds?.length) return\n const c = getAudioCtx()\n if (c.state === 'suspended') void c.resume()\n const now = c.currentTime + 0.03\n for (const sch of scheduleSounds(sounds, this.fps, fromFrame, now)) {\n const buf = playerAudioBuffers.get(sch.clip.assetId)\n if (!buf || buf === 'loading') { this.decodeAudio(sch.clip.assetId); continue } // heard on the next start\n if (!sch.clip.loop && sch.offset >= buf.duration) continue\n const src = c.createBufferSource()\n src.buffer = buf\n src.loop = !!sch.clip.loop\n const g = c.createGain()\n g.gain.value = sch.clip.gain ?? 1\n src.connect(g).connect(c.destination)\n src.start(sch.when, Math.max(0, sch.offset))\n this.activeSources.push(src)\n }\n }\n\n /** Triggers the frame-actions when the playhead enters a new whole frame. */\n private fireFrameActions(): void {\n const fi = Math.floor(this.frame)\n if (fi === this.lastFrameInt) return\n this.lastFrameInt = fi\n const fa = this.doc.timeline?.frameActions\n if (fa) for (const e of fa) if (e.frame === fi) runActions(e.actions, this.host)\n }\n\n play(): void {\n if (this.playing) return\n this.playing = true\n this.last = performance.now()\n this.simAcc = 0\n this.prevSimVars = null; this.simAlpha = 1; this.simActive = false // restart from a clean interpolation state\n this.startAudio(this.frame)\n const tick = (now: number) => {\n if (!this.playing) return\n const dt = Math.min((now - this.last) / 1000, 0.25) // clamp the big gaps (backgrounded tab) -> no explosive catch-up\n this.last = now\n\n // 1) PLAYHEAD: based on real time (smooth, independent of refresh rate) + looping.\n let f = this.frame + dt * this.fps\n if (f >= this.duration) {\n if (this.loop) { f %= this.duration; this.startAudio(f) } // restarts the audio on loop\n else {\n f = this.duration\n this.playing = false\n this.stopAudio()\n }\n }\n this.frame = f\n\n // 2) onEnterFrame at a FIXED step (60 Hz) -> framerate-independent physics. The set of active\n // symbols is frozen for this tick (the frame does not move between steps, except a gotoFrame from an action).\n const symTLs = this.activeSymbolTimelines(f)\n const rootSim = this.doc.timeline?.onEnterFrame\n const symSims = symTLs.filter((s) => s.tl.onEnterFrame?.length)\n if (rootSim?.length || symSims.length) {\n this.simActive = true\n const { steps, acc } = simSteps(this.simAcc, dt, SIM_STEP, SIM_MAX_STEPS)\n this.simAcc = acc\n for (let i = 0; i < steps && this.playing; i++) { // an action can pause -> we stop\n this.prevSimVars = cloneVarMap(this.vars) // state BEFORE the step -> interpolation target\n if (rootSim?.length) runActions(rootSim, this.host) // root\n for (const s of symSims) runActions(s.tl.onEnterFrame!, this.host) // active symbols\n }\n // Remaining step fraction -> we draw between `prevSimVars` and the current state (0..1).\n this.simAlpha = Math.min(1, this.simAcc / SIM_STEP)\n } else {\n this.simAcc = 0 // \"pure tween\" demo: no simulation, we do not hoard backlog\n this.simActive = false\n this.prevSimVars = null\n }\n this.mouse.dx = 0 // movement consumed by this frame (onEnterFrame) -> the \"mouse at rest\" hands control back to the keyboard\n this.mouse.dy = 0\n\n // 3) frame-actions (on the current frame) + single render.\n this.fireFrameActions() // can change frame/playing (gotoFrame, pause...)\n this.render()\n if (this.playing) this.raf = requestAnimationFrame(tick)\n }\n this.raf = requestAnimationFrame(tick)\n }\n\n pause(): void {\n this.playing = false\n this.simActive = false // no more playback -> the render goes back to the real values (not interpolated)\n cancelAnimationFrame(this.raf)\n this.stopAudio()\n }\n toggle(): void {\n if (this.playing) this.pause()\n else this.play()\n }\n stop(): void {\n this.pause()\n this.seek(0)\n }\n\n /** Releases the listeners. To be called when the player is no longer used. */\n destroy(): void {\n this.pause()\n window.removeEventListener('resize', this.onResize)\n globalThis.removeEventListener('keydown', this.onKeyDown)\n globalThis.removeEventListener('keyup', this.onKeyUp)\n this.canvas.removeEventListener('pointermove', this.onPointerMove)\n this.canvas.removeEventListener('pointerdown', this.onPointerDown)\n this.canvas.removeEventListener('pointerup', this.onPointerUp)\n this.canvas.removeEventListener('pointercancel', this.onPointerCancel)\n this.canvas.removeEventListener('pointerleave', this.onPointerLeave)\n this.cancelLongPress()\n }\n}\n","// -----------------------------------------------------------------------------\n// runtime/hit.ts -- click/hover test, PURE and LIGHTWEIGHT (zero `polygon-clipping`).\n//\n// Returns the CHAIN of items under a point (from the root container down to the\n// deepest item), accounting for ANIMATED poses at EVERY level (nested groups AND\n// symbols, with a per-instance local frame). Home-grown point-in-polygon (even-odd\n// ray-casting -> handles holes). Knows nothing about the fill engine -> lightweight\n// and embeddable.\n// -----------------------------------------------------------------------------\nimport type { Doc, Group, Instance, Item, Layer, Path, Point, Region, SymbolDef } from '@flatkit/types'\nimport { containerLayers, getSymbol, hiddenLayerIds, isContainer, isGroup, isInstance, isText, isImage, maskMap, guideMap } from '@flatkit/engine/layers'\nimport { apply, invert, compose, IDENTITY, type Transform } from '@flatkit/engine/transform'\nimport { resolveInstanceFrame, type Timeline } from '@flatkit/engine/timeline'\nimport { resolveLayerAt } from '@flatkit/engine/cel'\nimport { pathToPolygons } from '@flatkit/engine/path'\nimport { guidePathOf } from './drawScene'\nimport type { ExprContext } from '@flatkit/engine/expr'\n\n/** Does the point fall inside the shape of a mask layer? (resolved fill; containers = non-blocking) */\nfunction pointInMask(mask: Layer, frame: number, fps: number, ctx: ExprContext | undefined, pt: Point): boolean {\n let hasContainer = false\n for (const it of resolveLayerAt(mask, frame, { fps, ctx })) {\n if (it.hidden) continue\n if (isContainer(it)) { hasContainer = true; continue }\n if (pointInPolygons(pathToPolygons((it as Region).path), pt)) return true // fill = concrete polygons (no transform)\n }\n return hasContainer // complex mask shape (symbol/group) -> we do not block the selection\n}\n\n/** Does the point (parent space) fall inside a local box [0,0]-[w,h] of a transformed item? */\nfunction pointInBox(transform: import('@flatkit/engine/transform').Transform, w: number, h: number, pt: Point): boolean {\n const p = apply(invert(transform), pt)\n return p.x >= 0 && p.x <= w && p.y >= 0 && p.y <= h\n}\n\n/** Point inside a set of rings (outline + holes), even-odd rule. */\nexport function pointInPolygons(rings: Point[][], pt: Point): boolean {\n let inside = false\n for (const ring of rings) {\n for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {\n const a = ring[i]\n const b = ring[j]\n if (a.y > pt.y !== b.y > pt.y) {\n const x = a.x + ((pt.y - a.y) / (b.y - a.y)) * (b.x - a.x)\n if (pt.x < x) inside = !inside\n }\n }\n }\n return inside\n}\n\n/** Distance from a point to the segment [a,b]. */\nfunction distToSeg(p: Point, a: Point, b: Point): number {\n const dx = b.x - a.x\n const dy = b.y - a.y\n const l2 = dx * dx + dy * dy\n if (l2 === 0) return Math.hypot(p.x - a.x, p.y - a.y)\n const t = Math.max(0, Math.min(1, ((p.x - a.x) * dx + (p.y - a.y) * dy) / l2))\n return Math.hypot(p.x - (a.x + t * dx), p.y - (a.y + t * dy))\n}\n\n/** Is the point within `dist` of the PATH (outline entities: only the stroke is clickable, not the interior)? */\nexport function pointNearPath(path: Path, pt: Point, dist: number): boolean {\n for (const sp of path.subpaths) {\n const ring = pathToPolygons({ subpaths: [sp] })[0]\n if (!ring || ring.length < 2) continue\n const segCount = sp.closed ? ring.length : ring.length - 1\n for (let i = 0; i < segCount; i++) if (distToSeg(pt, ring[i], ring[(i + 1) % ring.length]) <= dist) return true\n }\n return false\n}\n\n/**\n * Does the `inner` path run along the EDGE of the `outer` polygon? (>= `frac` of its points within `dist`)\n * Used for \"double-click = fill + its outlines\": keeps only the outlines that actually follow the\n * boundary of the fill (not those that merely overlap it).\n */\nexport function pathFollowsPolygons(inner: Path, outer: Point[][], dist: number, frac = 0.7): boolean {\n const pts = pathToPolygons(inner).flat()\n if (pts.length === 0) return false\n let near = 0\n for (const p of pts) {\n for (const ring of outer) {\n let onRing = false\n for (let i = 0; i < ring.length && !onRing; i++) if (distToSeg(p, ring[i], ring[(i + 1) % ring.length]) <= dist) onRing = true\n if (onRing) { near++; break }\n }\n }\n return near / pts.length >= frac\n}\n\n/**\n * Does an item take part in the runtime (player) HIT? No if hidden, near-invisible (opacity ~0 ->\n * lets the click through), or explicitly `noHit` (non-interactive but ALWAYS drawn). For a\n * container, `noHit` short-circuits its whole subtree (we do not descend).\n */\nconst hittable = (it: Item): boolean => !it.hidden && (it.opacity ?? 1) > 0.01 && !it.noHit\n\n/** Is a region an OUTLINE ENTITY (noFill + stroke)? Hit by proximity to the stroke. */\nfunction hitRegion(r: Region, pt: Point): boolean {\n if (r.noFill && r.stroke) return pointNearPath(r.path, pt, r.stroke.width / 2 + 4)\n return pointInPolygons(pathToPolygons(r.path), pt)\n}\n\n/**\n * Sub-scope frame of a container, consistent with rendering:\n * - `freeze` (EDITOR): sub-scopes (instances / local symbols) FROZEN at 0 (like freezeNested);\n * - otherwise (PLAYER): animated local frame. Groups without a timeline follow the parent frame.\n */\nfunction subScopeFrame(it: Group | Instance, sym: SymbolDef | undefined, frame: number, freeze: boolean): number {\n const subScope = isInstance(it) || (isGroup(it) && !!it.timeline)\n if (!subScope) return frame\n if (freeze) return 0\n return isInstance(it) && sym?.timeline ? resolveInstanceFrame(it.playback, frame, sym.timeline.durationFrames) : frame\n}\n\n// Same cap as the renderer (drawScene.ts): an untrusted doc with pathological container nesting must not\n// blow the stack on a click/hover. `seen` only breaks instance-symbol cycles; plain groups need a depth cap.\nconst MAX_NEST = 256\n\n/** Chain of items hit within a scope (container first, deeper next), or null. */\nfunction hitInScope(\n doc: Doc,\n layers: Layer[],\n timeline: Timeline | undefined,\n frame: number,\n ctx: ExprContext,\n pt: Point,\n seen: Set<string>,\n freeze: boolean,\n parent: Transform = IDENTITY,\n depth = 0,\n): string[] | null {\n if (depth > MAX_NEST) return null // pathological nesting -> stop\n const fps = timeline?.fps ?? 24\n const hid = hiddenLayerIds(layers)\n const masks = maskMap(layers)\n const guides = guideMap(layers)\n for (let li = layers.length - 1; li >= 0; li--) {\n const layer = layers[li]\n if (hid.has(layer.id) || layer.isMask) continue\n const mask = masks.get(layer.id)\n if (mask && !pointInMask(mask, frame, fps, ctx, pt)) continue // outside the mask\n const gl = guides.get(layer.id)\n const guide = gl ? guidePathOf(gl, frame, { fps, expr: ctx }) ?? undefined : undefined\n const items = resolveLayerAt(layer, frame, { fps, ctx, guide, orient: layer.orientToGuide, parent }) // resolved content (guided poses included)\n for (let i = items.length - 1; i >= 0; i--) {\n const it = items[i]\n if (!hittable(it)) continue // hidden / near-invisible / non-interactive -> lets the click through\n if (isContainer(it)) {\n if (isInstance(it) && seen.has(it.symbolId)) continue\n const local = apply(invert(it.transform), pt) // transform = resolved pose\n const inst = isInstance(it)\n const sym = inst ? getSymbol(doc, it.symbolId) : undefined\n const subTl = inst ? sym?.timeline : isGroup(it) && it.timeline ? it.timeline : timeline // local symbol = its timeline; legacy group = parent scope\n const subFrame = subScopeFrame(it, sym, frame, freeze)\n const next = inst ? new Set([...seen, it.symbolId]) : seen\n const deeper = hitInScope(doc, containerLayers(doc, it), subTl, subFrame, ctx, local, next, freeze, compose(parent, it.transform), depth + 1)\n if (deeper) return [it.id, ...deeper]\n } else if (isText(it)) {\n if (pointInBox(it.transform, it.box.w, it.box.h, pt)) return [it.id]\n } else if (isImage(it)) {\n if (pointInBox(it.transform, it.w, it.h, pt)) return [it.id]\n } else {\n if (hitRegion(it as Region, pt)) return [it.id] // fill = interior; outline entity = proximity to the stroke\n }\n }\n }\n return null\n}\n\n/** Chain of items under `worldPt` at the given frame (animated poses at all levels, PLAYER: composed). */\nexport function hitChain(doc: Doc, frame: number, ctx: ExprContext, worldPt: Point): string[] {\n return hitInScope(doc, doc.layers, doc.timeline, frame, ctx, worldPt, new Set(), false) ?? []\n}\n\n/**\n * ALL item chains under `worldPt`, in Z order (from TOPMOST to bottom). Where `hitChain` returns\n * only the topmost chain, this one returns them all -- which lets the player make a click/hover\n * \"fall\" through a NON-interactive item placed on top, down to the clickable one beneath (otherwise\n * a plain decorative text would intercept the click without passing it along).\n */\nfunction collectInScope(\n doc: Doc,\n layers: Layer[],\n timeline: Timeline | undefined,\n frame: number,\n ctx: ExprContext,\n pt: Point,\n seen: Set<string>,\n freeze: boolean,\n out: string[][],\n parent: Transform = IDENTITY,\n depth = 0,\n): void {\n if (depth > MAX_NEST) return // pathological nesting -> stop\n const fps = timeline?.fps ?? 24\n const hid = hiddenLayerIds(layers)\n const masks = maskMap(layers)\n const guides = guideMap(layers)\n for (let li = layers.length - 1; li >= 0; li--) {\n const layer = layers[li]\n if (hid.has(layer.id) || layer.isMask) continue\n const mask = masks.get(layer.id)\n if (mask && !pointInMask(mask, frame, fps, ctx, pt)) continue\n const gl = guides.get(layer.id)\n const guide = gl ? guidePathOf(gl, frame, { fps, expr: ctx }) ?? undefined : undefined\n const items = resolveLayerAt(layer, frame, { fps, ctx, guide, orient: layer.orientToGuide, parent })\n for (let i = items.length - 1; i >= 0; i--) {\n const it = items[i]\n if (!hittable(it)) continue // hidden / near-invisible / non-interactive -> lets the click through\n if (isContainer(it)) {\n if (isInstance(it) && seen.has(it.symbolId)) continue\n const local = apply(invert(it.transform), pt)\n const inst = isInstance(it)\n const sym = inst ? getSymbol(doc, it.symbolId) : undefined\n const subTl = inst ? sym?.timeline : isGroup(it) && it.timeline ? it.timeline : timeline\n const subFrame = subScopeFrame(it, sym, frame, freeze)\n const next = inst ? new Set([...seen, it.symbolId]) : seen\n const deeper: string[][] = []\n collectInScope(doc, containerLayers(doc, it), subTl, subFrame, ctx, local, next, freeze, deeper, compose(parent, it.transform), depth + 1)\n for (const d of deeper) out.push([it.id, ...d]) // container hit only via a descendant\n } else if (isText(it)) {\n if (pointInBox(it.transform, it.box.w, it.box.h, pt)) out.push([it.id])\n } else if (isImage(it)) {\n if (pointInBox(it.transform, it.w, it.h, pt)) out.push([it.id])\n } else {\n if (hitRegion(it as Region, pt)) out.push([it.id])\n }\n }\n }\n}\n\n/** All hit chains under `worldPt`, from topmost to bottom (see `collectInScope`). */\nexport function hitChains(doc: Doc, frame: number, ctx: ExprContext, worldPt: Point): string[][] {\n const out: string[][] = []\n collectInScope(doc, doc.layers, doc.timeline, frame, ctx, worldPt, new Set(), false, out)\n return out\n}\n\n/**\n * TOP-LEVEL item under the point in a given scope, at the current frame (animated poses taken into\n * account) -- the \"time-aware\" equivalent of `hitContext` for selection in the editor. Ignores\n * locked layers.\n */\nexport function hitContextAt(\n doc: Doc,\n layers: Layer[],\n timeline: Timeline | undefined,\n frame: number,\n worldPt: Point,\n ctx?: ExprContext,\n freeze = true, // EDITOR: sub-scopes frozen (consistent with freezeNested rendering)\n): { item: Item; layerId: string } | null {\n const fps = timeline?.fps ?? 24\n const hid = hiddenLayerIds(layers)\n const masks = maskMap(layers)\n const guides = guideMap(layers)\n for (let li = layers.length - 1; li >= 0; li--) {\n const layer = layers[li]\n if (hid.has(layer.id) || layer.locked || layer.isMask) continue\n const mask = masks.get(layer.id)\n if (mask && !pointInMask(mask, frame, fps, ctx, worldPt)) continue // outside the mask\n const gl = guides.get(layer.id)\n const guide = gl ? guidePathOf(gl, frame, { fps, expr: ctx }) ?? undefined : undefined\n const items = resolveLayerAt(layer, frame, { fps, ctx, guide, orient: layer.orientToGuide })\n for (let i = items.length - 1; i >= 0; i--) {\n const it = items[i]\n if (it.hidden) continue\n if ((it.opacity ?? 1) <= 0.01) continue // invisible (opacity ~0) -> lets the click through\n if (isContainer(it)) {\n const local = apply(invert(it.transform), worldPt) // transform = resolved pose\n const inst = isInstance(it)\n const sym = inst ? getSymbol(doc, it.symbolId) : undefined\n const subTl = inst ? sym?.timeline : isGroup(it) && it.timeline ? it.timeline : timeline // local symbol = its timeline; legacy group = parent scope\n const subFrame = subScopeFrame(it, sym, frame, freeze)\n const seen = inst ? new Set([it.symbolId]) : new Set<string>()\n if (hitInScope(doc, containerLayers(doc, it), subTl, subFrame, ctx ?? {}, local, seen, freeze, it.transform)) return { item: it, layerId: layer.id }\n } else if (isText(it)) {\n if (pointInBox(it.transform, it.box.w, it.box.h, worldPt)) return { item: it, layerId: layer.id }\n } else if (isImage(it)) {\n if (pointInBox(it.transform, it.w, it.h, worldPt)) return { item: it, layerId: layer.id }\n } else {\n if (hitRegion(it as Region, worldPt)) return { item: it, layerId: layer.id }\n }\n }\n }\n return null\n}\n"],"mappings":"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","names":["resolveInstanceFrame","containerLayers","getSymbol","isGroup","isInstance","isText","apply","invert","IDENTITY","containerLayers","getSymbol","hiddenLayerIds","isContainer","isGroup","isInstance","isText","isImage","maskMap","guideMap","apply","compose","IDENTITY","resolveInstanceFrame","resolveLayerAt","resolveLayerAt","isContainer","apply","isInstance","isGroup","resolveInstanceFrame","MAX_NEST","IDENTITY","MAX_NEST","hiddenLayerIds","maskMap","guideMap","resolveLayerAt","isContainer","isInstance","apply","getSymbol","isGroup","containerLayers","compose","isText","isImage","apply","IDENTITY","invert","isInstance","getSymbol","resolveInstanceFrame","containerLayers","isGroup","isText"]}
|
package/dist/debug.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Doc } from '@flatkit/types';
|
|
2
|
+
import { G as Gesture } from './player-Bxx8wsSa.js';
|
|
3
|
+
|
|
4
|
+
type PlayResult = {
|
|
5
|
+
sends: {
|
|
6
|
+
name: string;
|
|
7
|
+
value?: number | string;
|
|
8
|
+
}[];
|
|
9
|
+
vars: Record<string, number | number[]>;
|
|
10
|
+
steps?: TraceStep[];
|
|
11
|
+
expectFailures?: string[];
|
|
12
|
+
};
|
|
13
|
+
/** Trace of ONE gesture: its description, the `send`s emitted during it, and the variable diff. */
|
|
14
|
+
type TraceStep = {
|
|
15
|
+
gesture: string;
|
|
16
|
+
sends: {
|
|
17
|
+
name: string;
|
|
18
|
+
value?: number | string;
|
|
19
|
+
}[];
|
|
20
|
+
changed: Record<string, [number | number[] | undefined, number | number[]]>;
|
|
21
|
+
};
|
|
22
|
+
/** Plays `doc`, replays `gestures`, returns the collected `send`s plus the final state of the variables.
|
|
23
|
+
* `trace`: adds `steps` (sends + variable diff PER gesture) -- for inspection / the debug-player. */
|
|
24
|
+
declare function playHeadless(doc: Doc, gestures: Gesture[], opts?: {
|
|
25
|
+
trace?: boolean;
|
|
26
|
+
}): PlayResult;
|
|
27
|
+
|
|
28
|
+
export { Gesture, type PlayResult, type TraceStep, playHeadless };
|
package/dist/debug.js
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FlatPlayer
|
|
3
|
+
} from "./chunk-BXKJAHHO.js";
|
|
4
|
+
|
|
5
|
+
// src/headless.ts
|
|
6
|
+
import { itemBoundsByName, dropZoneBounds } from "@flatkit/engine/groups";
|
|
7
|
+
import { isGroup } from "@flatkit/engine/layers";
|
|
8
|
+
var MAX_SWEEP = 2e3;
|
|
9
|
+
function findItemByName(items, name) {
|
|
10
|
+
for (const it of items) {
|
|
11
|
+
if ("name" in it && it.name === name) return it;
|
|
12
|
+
if (isGroup(it)) for (const l of it.layers) {
|
|
13
|
+
const r = findItemByName(l.items, name);
|
|
14
|
+
if (r) return r;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
function revealBrushFor(doc, name) {
|
|
20
|
+
let id = null;
|
|
21
|
+
for (const l of doc.layers) {
|
|
22
|
+
const it = findItemByName(l.items, name);
|
|
23
|
+
if (it) {
|
|
24
|
+
id = it.id;
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const inter = id ? doc.interactors?.find((x) => x.targetId === id) : void 0;
|
|
29
|
+
return inter?.grid && inter.grid > 0 ? inter.grid : 24;
|
|
30
|
+
}
|
|
31
|
+
function sweepPoints(b, brush) {
|
|
32
|
+
const w = b.maxX - b.minX, h = b.maxY - b.minY;
|
|
33
|
+
let step = Math.max(1, brush);
|
|
34
|
+
let cols = Math.max(1, Math.ceil(w / step)), rows = Math.max(1, Math.ceil(h / step));
|
|
35
|
+
if (cols * rows > MAX_SWEEP) {
|
|
36
|
+
const f = Math.sqrt(cols * rows / MAX_SWEEP);
|
|
37
|
+
step *= f;
|
|
38
|
+
cols = Math.max(1, Math.ceil(w / step));
|
|
39
|
+
rows = Math.max(1, Math.ceil(h / step));
|
|
40
|
+
}
|
|
41
|
+
const pts = [];
|
|
42
|
+
for (let r = 0; r < rows; r++) {
|
|
43
|
+
const y = Math.min(b.maxY, b.minY + (r + 0.5) * step);
|
|
44
|
+
const cs = Array.from({ length: cols }, (_v, c) => c);
|
|
45
|
+
if (r % 2) cs.reverse();
|
|
46
|
+
for (const c of cs) pts.push({ x: Math.min(b.maxX, b.minX + (c + 0.5) * step), y });
|
|
47
|
+
}
|
|
48
|
+
return pts;
|
|
49
|
+
}
|
|
50
|
+
var fakeCtx = () => new Proxy({}, {
|
|
51
|
+
get: (_t, p) => p === "measureText" ? () => ({ width: 0 }) : p === "getTransform" ? () => ({ a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }) : () => {
|
|
52
|
+
},
|
|
53
|
+
set: () => true
|
|
54
|
+
});
|
|
55
|
+
var fakeCanvas = (handlers, w, h) => ({
|
|
56
|
+
getContext: () => fakeCtx(),
|
|
57
|
+
getBoundingClientRect: () => ({ width: w, height: h, left: 0, top: 0, right: w, bottom: h }),
|
|
58
|
+
addEventListener: (type, fn) => {
|
|
59
|
+
handlers[type] = fn;
|
|
60
|
+
},
|
|
61
|
+
removeEventListener: (type) => {
|
|
62
|
+
delete handlers[type];
|
|
63
|
+
},
|
|
64
|
+
setPointerCapture: () => {
|
|
65
|
+
},
|
|
66
|
+
releasePointerCapture: () => {
|
|
67
|
+
},
|
|
68
|
+
style: {}
|
|
69
|
+
});
|
|
70
|
+
function ensureDomGlobals() {
|
|
71
|
+
const g = globalThis;
|
|
72
|
+
const undo = [];
|
|
73
|
+
const set = (k, v) => {
|
|
74
|
+
if (g[k] === void 0) {
|
|
75
|
+
g[k] = v;
|
|
76
|
+
undo.push(() => {
|
|
77
|
+
delete g[k];
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
set("window", { addEventListener: () => {
|
|
82
|
+
}, removeEventListener: () => {
|
|
83
|
+
}, devicePixelRatio: 1 });
|
|
84
|
+
set("requestAnimationFrame", () => 0);
|
|
85
|
+
set("cancelAnimationFrame", () => {
|
|
86
|
+
});
|
|
87
|
+
set("addEventListener", () => {
|
|
88
|
+
});
|
|
89
|
+
set("removeEventListener", () => {
|
|
90
|
+
});
|
|
91
|
+
return () => undo.forEach((f) => f());
|
|
92
|
+
}
|
|
93
|
+
var describeGesture = (g) => g.type === "drag" ? `drag ${g.source}->${g.target}` : g.type === "tap" ? `tap ${g.target}` : g.type === "connect" ? `connect ${g.source}->${g.target}` : g.type === "scratch" ? `scratch ${g.target}` : g.type === "set" ? `set ${g.name}=${g.value}` : g.type === "wait" ? `wait ${g.frames}` : g.type === "expect" ? "expect" : `${g.type} (${g.x},${g.y})`;
|
|
94
|
+
function playHeadless(doc, gestures, opts = {}) {
|
|
95
|
+
const restore = ensureDomGlobals();
|
|
96
|
+
const handlers = {};
|
|
97
|
+
const sends = [];
|
|
98
|
+
const pl = new FlatPlayer(fakeCanvas(handlers, doc.width, doc.height), doc, { input: true, padding: 0, render: false, onEvent: (e) => sends.push(e) });
|
|
99
|
+
const ev = (x, y, id = 1) => ({ clientX: x, clientY: y, pointerId: id });
|
|
100
|
+
const fire = (type, p, id = 1) => {
|
|
101
|
+
const h = handlers[`pointer${type}`];
|
|
102
|
+
if (h) h(ev(p.x, p.y, id));
|
|
103
|
+
};
|
|
104
|
+
const grabPoint = (name) => {
|
|
105
|
+
const c = pl.objectCenter(name);
|
|
106
|
+
if (c) return c;
|
|
107
|
+
const b = itemBoundsByName(doc, name);
|
|
108
|
+
if (!b) throw new Error(`gesture: object "${name}" not found in the scene`);
|
|
109
|
+
return { x: (b.minX + b.maxX) / 2, y: (b.minY + b.maxY) / 2 };
|
|
110
|
+
};
|
|
111
|
+
const dropPoint = (name) => {
|
|
112
|
+
const b = dropZoneBounds(doc, name);
|
|
113
|
+
if (!b) throw new Error(`gesture: zone "${name}" not found in the scene`);
|
|
114
|
+
return { x: (b.minX + b.maxX) / 2, y: (b.minY + b.maxY) / 2 };
|
|
115
|
+
};
|
|
116
|
+
let sendCursor = 0;
|
|
117
|
+
const expectFailures = [];
|
|
118
|
+
const applyGesture = (g) => {
|
|
119
|
+
if (g.type === "set") {
|
|
120
|
+
pl.setVar(g.name, g.value);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (g.type === "wait") {
|
|
124
|
+
pl.stepSim(g.frames);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (g.type === "drag") {
|
|
128
|
+
const id = g.id ?? 1, t = dropPoint(g.target);
|
|
129
|
+
fire("down", grabPoint(g.source), id);
|
|
130
|
+
fire("move", t, id);
|
|
131
|
+
fire("up", t, id);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (g.type === "tap") {
|
|
135
|
+
const id = g.id ?? 1, c = grabPoint(g.target);
|
|
136
|
+
fire("down", c, id);
|
|
137
|
+
fire("up", c, id);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (g.type === "connect") {
|
|
141
|
+
const id = g.id ?? 1, t = grabPoint(g.target);
|
|
142
|
+
fire("down", grabPoint(g.source), id);
|
|
143
|
+
fire("move", t, id);
|
|
144
|
+
fire("up", t, id);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (g.type === "scratch") {
|
|
148
|
+
const id = g.id ?? 1;
|
|
149
|
+
const b = itemBoundsByName(doc, g.target);
|
|
150
|
+
if (!b) throw new Error(`gesture: object "${g.target}" not found in the scene`);
|
|
151
|
+
const pts = sweepPoints(b, revealBrushFor(doc, g.target));
|
|
152
|
+
fire("down", pts[0], id);
|
|
153
|
+
for (let i = 1; i < pts.length; i++) fire("move", pts[i], id);
|
|
154
|
+
fire("up", pts[pts.length - 1], id);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (g.type === "expect") {
|
|
158
|
+
const got = sends.slice(sendCursor).map((s) => s.name);
|
|
159
|
+
sendCursor = sends.length;
|
|
160
|
+
if (g.sends && JSON.stringify(got) !== JSON.stringify(g.sends)) expectFailures.push(`expect: expected sends [${g.sends.join(", ")}], received [${got.join(", ")}]`);
|
|
161
|
+
if (g.vars) for (const [k, v] of Object.entries(g.vars)) {
|
|
162
|
+
const cur = pl.getVar(k);
|
|
163
|
+
if (JSON.stringify(cur) !== JSON.stringify(v)) expectFailures.push(`expect: ${k} expected ${JSON.stringify(v)}, got ${JSON.stringify(cur)}`);
|
|
164
|
+
}
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
fire(g.type, { x: g.x, y: g.y }, g.id);
|
|
168
|
+
};
|
|
169
|
+
try {
|
|
170
|
+
const steps = [];
|
|
171
|
+
for (const g of gestures) {
|
|
172
|
+
if (!opts.trace) {
|
|
173
|
+
applyGesture(g);
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
const before = pl.allVars(), sIdx = sends.length;
|
|
177
|
+
applyGesture(g);
|
|
178
|
+
const after = pl.allVars();
|
|
179
|
+
const changed = {};
|
|
180
|
+
for (const k of /* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)]))
|
|
181
|
+
if (JSON.stringify(before[k]) !== JSON.stringify(after[k])) changed[k] = [before[k], after[k]];
|
|
182
|
+
steps.push({ gesture: describeGesture(g), sends: sends.slice(sIdx), changed });
|
|
183
|
+
}
|
|
184
|
+
return { sends, vars: pl.allVars(), ...opts.trace ? { steps } : {}, ...expectFailures.length ? { expectFailures } : {} };
|
|
185
|
+
} finally {
|
|
186
|
+
pl.destroy();
|
|
187
|
+
restore();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
export {
|
|
191
|
+
playHeadless
|
|
192
|
+
};
|
|
193
|
+
//# sourceMappingURL=debug.js.map
|