@loom-dev/vide 0.5.3 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/reactive.ts","../src/index.ts"],"mappings":";;;;;;;;;;;;;;;;KAgBK;;UAgBY,OAAO;MACnB;GACH,OAAO,IAAI;;;iBAIG,OAAO,GAAG,SAAS,IAAI,OAAO;;iBA8B9B,OAAO;;iBAwBP,OAAO,GAAG,UAAU,UAAU;;iBAS9B,QAAQ,IAAI;;;;;;iBASZ,KAAK,GAAG,KAAK,wBAAwB,IAAI;;;cChFnD;;UAGW;YACN;WACD;WACA,OAAO,SAAS;;;;;;;iBAgBV,OACf,qBACG,QAAQ,4BAA4B;;;;;;;;;iBAoLxB,MACf,iBAAiB,UACjB,SAAS"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/reactive.ts","../src/index.ts"],"mappings":";;;;;;;;;;;;;;;;KAgBK;;UAgBY,OAAO;MACnB;GACH,OAAO,IAAI;;;iBAIG,OAAO,GAAG,SAAS,IAAI,OAAO;;iBA8B9B,OAAO;;iBAwBP,OAAO,GAAG,UAAU,UAAU;;iBAS9B,QAAQ,IAAI;;;;;;iBASZ,KAAK,GAAG,KAAK,wBAAwB,IAAI;;;cC3EnD;;UAGW;YACN;WACD;WACA,OAAO,SAAS;;;;;;;iBAgBV,OACf,qBACG,QAAQ,4BAA4B;;;;;;;;;iBA4LxB,MACf,iBAAiB,UACjB,SAAS"}
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { computeLayout, initLayout } from "@loom-dev/layout";
2
2
  import { fontShorthand, instanceFont, renderScene } from "@loom-dev/renderer";
3
3
  import { EnumItem, toPropertyValue } from "@loom-dev/runtime";
4
- import { prop } from "@loom-dev/scene";
4
+ import { fontSizeToPx, prop } from "@loom-dev/scene";
5
5
  //#region src/reactive.ts
6
6
  let currentReaction;
7
7
  /** Create a reactive source seeded with `initial`. */
@@ -182,7 +182,8 @@ function measureTextBounds(live) {
182
182
  const ctx = getMeasureCtx();
183
183
  if (!ctx) return void 0;
184
184
  const rawSize = live.props.get("TextSize");
185
- const size = typeof rawSize === "number" ? rawSize : 14;
185
+ const rawFontSize = live.props.get("FontSize");
186
+ const size = typeof rawSize === "number" ? rawSize : fontSizeToPx(rawFontSize instanceof EnumItem ? rawFontSize.Name : void 0) ?? 14;
186
187
  ctx.font = fontShorthand(instanceFont({
187
188
  Font: live.props.get("Font"),
188
189
  FontFace: live.props.get("FontFace")
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/reactive.ts","../src/index.ts"],"sourcesContent":["/**\n * A minimal fine-grained reactive core, mirroring vide's `source`/`derive`/\n * `effect` primitives. Its only job is to track which effects read which sources,\n * so a source write re-runs exactly the effects that depend on it. The loom vide\n * adapter wires reactive props/children onto these so a source change reschedules\n * a Scene IR rebuild (see `index.ts`).\n *\n * Unlike React (a VDOM reconciler), vide is push-based: there is no diff, signals\n * notify their subscribers directly. Both paradigms feed the *same* Scene IR.\n *\n * Reactions form an ownership tree: an effect created while another reaction runs\n * becomes that reaction's child and is disposed when the parent re-runs (or when\n * the enclosing `root` is disposed). This is what keeps dynamic effects — e.g. a\n * `derive` or a reactive child rebuilt inside an effect — from leaking.\n */\n\ntype CleanupFn = () => void;\n\ninterface Reaction {\n\trun: () => void;\n\t/** Subscriber sets this reaction belongs to, cleared before each re-run. */\n\tdeps: Set<Set<Reaction>>;\n\tcleanups: CleanupFn[];\n\t/** Effects created while this reaction was running; disposed before re-run. */\n\tchildren: Reaction[];\n}\n\n// The running reaction doubles as the current owner: dependency reads subscribe\n// it, and effects created during its run nest under it.\nlet currentReaction: Reaction | undefined;\n\n/** A vide source: call with no args to read (tracked), with one arg to write. */\nexport interface Source<T> {\n\t(): T;\n\t(value: T): T;\n}\n\n/** Create a reactive source seeded with `initial`. */\nexport function source<T>(initial: T): Source<T> {\n\tlet value = initial;\n\tconst subscribers = new Set<Reaction>();\n\tfunction accessor(...args: [T] | []): T {\n\t\tif (args.length === 0) {\n\t\t\tif (currentReaction) {\n\t\t\t\tsubscribers.add(currentReaction);\n\t\t\t\tcurrentReaction.deps.add(subscribers);\n\t\t\t}\n\t\t\treturn value;\n\t\t}\n\t\tvalue = args[0];\n\t\t// Snapshot: a reaction re-subscribes (mutating the set) as it re-runs.\n\t\tfor (const reaction of [...subscribers]) reaction.run();\n\t\treturn value;\n\t}\n\treturn accessor as Source<T>;\n}\n\nfunction disposeReaction(reaction: Reaction): void {\n\t// Dispose nested effects first, depth-first, so their cleanups run too.\n\tfor (const child of reaction.children) disposeReaction(child);\n\treaction.children = [];\n\tfor (const dep of reaction.deps) dep.delete(reaction);\n\treaction.deps.clear();\n\tfor (const fn of reaction.cleanups) fn();\n\treaction.cleanups = [];\n}\n\n/** Run `fn` now and re-run it whenever a source it read changes. */\nexport function effect(fn: () => void): void {\n\tconst reaction: Reaction = {\n\t\trun,\n\t\tdeps: new Set(),\n\t\tcleanups: [],\n\t\tchildren: [],\n\t};\n\tfunction run(): void {\n\t\t// Tear down the previous run (deps, cleanups, and any nested effects) so a\n\t\t// re-run starts clean and dynamic children don't accumulate.\n\t\tdisposeReaction(reaction);\n\t\tconst prev = currentReaction;\n\t\tcurrentReaction = reaction;\n\t\ttry {\n\t\t\tfn();\n\t\t} finally {\n\t\t\tcurrentReaction = prev;\n\t\t}\n\t}\n\tcurrentReaction?.children.push(reaction);\n\trun();\n}\n\n/** A memoized computation: a source kept in sync with `fn` via an effect. */\nexport function derive<T>(fn: () => T): () => T {\n\tconst s = source<T>(undefined as T);\n\teffect(() => {\n\t\ts(fn());\n\t});\n\treturn () => s();\n}\n\n/** Register a cleanup to run before the enclosing effect re-runs or disposes. */\nexport function cleanup(fn: CleanupFn): void {\n\tcurrentReaction?.cleanups.push(fn);\n}\n\n/**\n * Open a disposal scope. `fn` receives a `dispose` that tears down every effect\n * created within (recursively). `mount` runs the component tree inside a root so\n * unmounting disposes all its reactive bindings.\n */\nexport function root<T>(fn: (dispose: () => void) => T): T {\n\tconst owner: Reaction = {\n\t\trun: () => {},\n\t\tdeps: new Set(),\n\t\tcleanups: [],\n\t\tchildren: [],\n\t};\n\tconst prev = currentReaction;\n\tcurrentReaction = owner;\n\ttry {\n\t\treturn fn(() => disposeReaction(owner));\n\t} finally {\n\t\tcurrentReaction = prev;\n\t}\n}\n","/**\n * `@loom-dev/vide` — a vide frontend adapter for loom.\n *\n * The second proof that the Scene IR is the real contract: vide is push-based\n * fine-grained reactivity (no VDOM, no reconciler — see `./reactive`), yet it\n * feeds the *exact same* `SceneNode` tree into the *same* WASM layout engine and\n * DOM renderer that `@loom-dev/react` uses. Nothing in `@loom-dev/layout` or\n * `@loom-dev/renderer` knows which frontend produced the tree.\n *\n * Authoring mirrors vide: `create(\"Frame\")({ ...props, [1]: child })`, where\n * string keys are Roblox properties (a function value is a reactive binding) and\n * number keys are children (vide's array-part).\n */\nimport { computeLayout, initLayout } from \"@loom-dev/layout\";\nimport { fontShorthand, instanceFont, renderScene } from \"@loom-dev/renderer\";\nimport { EnumItem, toPropertyValue } from \"@loom-dev/runtime\";\nimport { type PropertyValue, prop, type SceneNode } from \"@loom-dev/scene\";\nimport { effect, root } from \"./reactive\";\n\nexport {\n\tcleanup,\n\tderive,\n\teffect,\n\troot,\n\ttype Source,\n\tsource,\n} from \"./reactive\";\n\n// --- authoring: create() -----------------------------------------------------\n\nconst VIDE_NODE = Symbol(\"loom.vide.node\");\n\n/** A vide element descriptor produced by `create`. */\nexport interface VideNode {\n\treadonly [VIDE_NODE]: true;\n\treadonly className: string;\n\treadonly props: Readonly<Record<string, unknown>>;\n}\n\nfunction isVideNode(value: unknown): value is VideNode {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\t(value as { [VIDE_NODE]?: unknown })[VIDE_NODE] === true\n\t);\n}\n\n/**\n * `create(\"Frame\")` returns a constructor taking a props table. String keys are\n * Roblox properties (a function value is bound reactively at mount); number keys\n * are children.\n */\nexport function create(\n\tclassName: string,\n): (props?: Record<string, unknown>) => VideNode {\n\treturn (props = {}) => ({ [VIDE_NODE]: true, className, props });\n}\n\n// --- live tree ---------------------------------------------------------------\n\n/**\n * A resolved, mutable mirror of a `VideNode`. Reactive props write their current\n * value into `props`; `toScene` reads it. Children are grouped into slots so a\n * reactive (function) child can swap its subtree in place without disturbing its\n * siblings; a static child is a one-shot slot.\n */\ninterface LiveNode {\n\tclassName: string;\n\tname: string;\n\tid: string;\n\tprops: Map<string, unknown>;\n\tchildren: ChildSlot[];\n}\n\n/** A positional group of children — its `nodes` are re-derived if reactive. */\ninterface ChildSlot {\n\tnodes: LiveNode[];\n}\n\nlet nextId = 0;\n\n/** Resolve a child value (a node, or nested arrays of nodes) to LiveNodes. */\nfunction resolveChildren(value: unknown, schedule: () => void): LiveNode[] {\n\tif (isVideNode(value)) return [build(value, schedule)];\n\tif (Array.isArray(value)) {\n\t\treturn value.flatMap((item) => resolveChildren(item, schedule));\n\t}\n\treturn [];\n}\n\nfunction addChildSlot(\n\tparent: LiveNode,\n\tvalue: unknown,\n\tschedule: () => void,\n): void {\n\tif (typeof value === \"function\") {\n\t\t// A reactive child (vide's control-flow pattern): rebuild this slot's\n\t\t// subtree whenever the function's sources change. The rebuilt nodes' own\n\t\t// reactive bindings nest under this effect, so they dispose on each re-run.\n\t\tconst slot: ChildSlot = { nodes: [] };\n\t\tparent.children.push(slot);\n\t\teffect(() => {\n\t\t\tslot.nodes = resolveChildren((value as () => unknown)(), schedule);\n\t\t\tschedule();\n\t\t});\n\t} else {\n\t\tparent.children.push({ nodes: resolveChildren(value, schedule) });\n\t}\n}\n\n/** Build a `LiveNode` from a `VideNode`, wiring reactive props/children. */\nfunction build(node: VideNode, schedule: () => void): LiveNode {\n\tconst live: LiveNode = {\n\t\tclassName: node.className,\n\t\tname: node.className,\n\t\tid: `v${nextId++}`,\n\t\tprops: new Map(),\n\t\tchildren: [],\n\t};\n\tfor (const [key, value] of Object.entries(node.props)) {\n\t\tif (/^\\d+$/.test(key)) {\n\t\t\taddChildSlot(live, value, schedule);\n\t\t\tcontinue;\n\t\t}\n\t\tif (key === \"Name\" && typeof value === \"string\") {\n\t\t\tlive.name = value;\n\t\t\tcontinue;\n\t\t}\n\t\tif (typeof value === \"function\") {\n\t\t\t// A reactive prop: re-resolve and repaint whenever its sources change.\n\t\t\teffect(() => {\n\t\t\t\tlive.props.set(key, (value as () => unknown)());\n\t\t\t\tschedule();\n\t\t\t});\n\t\t} else {\n\t\t\tlive.props.set(key, value);\n\t\t}\n\t}\n\treturn live;\n}\n\n// --- live tree → Scene IR ----------------------------------------------------\n\nconst TEXT_CLASSES = new Set([\"TextLabel\", \"TextButton\", \"TextBox\"]);\nlet measureCtx: CanvasRenderingContext2D | null | undefined;\nfunction getMeasureCtx(): CanvasRenderingContext2D | null {\n\tif (measureCtx === undefined) {\n\t\tmeasureCtx =\n\t\t\ttypeof document !== \"undefined\"\n\t\t\t\t? document.createElement(\"canvas\").getContext(\"2d\")\n\t\t\t\t: null;\n\t}\n\treturn measureCtx;\n}\n\n/**\n * Measure an auto-sizing text node with the font the renderer paints and emit a\n * `TextBounds` Vector2 the layout engine reads for AutomaticSize (font metrics\n * live browser-side, not in the WASM engine). Mirrors the react adapter.\n */\nfunction measureTextBounds(live: LiveNode): PropertyValue | undefined {\n\tif (!TEXT_CLASSES.has(live.className)) return undefined;\n\tconst auto = live.props.get(\"AutomaticSize\");\n\tconst autoName = auto instanceof EnumItem ? auto.Name : undefined;\n\tif (autoName !== \"X\" && autoName !== \"Y\" && autoName !== \"XY\")\n\t\treturn undefined;\n\tconst text = live.props.get(\"Text\");\n\tif (typeof text !== \"string\" || text === \"\") return undefined;\n\tconst ctx = getMeasureCtx();\n\tif (!ctx) return undefined;\n\n\tconst rawSize = live.props.get(\"TextSize\");\n\tconst size = typeof rawSize === \"number\" ? rawSize : 14;\n\tctx.font = fontShorthand(\n\t\tinstanceFont({\n\t\t\tFont: live.props.get(\"Font\"),\n\t\t\tFontFace: live.props.get(\"FontFace\"),\n\t\t}),\n\t\tsize,\n\t);\n\tconst lines = text.split(\"\\n\");\n\tlet width = 0;\n\tfor (const line of lines)\n\t\twidth = Math.max(width, ctx.measureText(line).width);\n\treturn prop.vector2({ x: Math.ceil(width), y: lines.length * size });\n}\n\n/** Snapshot the live tree as Scene IR (called fresh on every paint). */\nfunction toScene(live: LiveNode): SceneNode {\n\tconst node: SceneNode = {\n\t\tclassName: live.className,\n\t\tname: live.name,\n\t\tid: live.id,\n\t};\n\tconst properties: Record<string, PropertyValue> = {};\n\tfor (const [key, value] of live.props) {\n\t\tconst pv = toPropertyValue(value);\n\t\tif (pv !== undefined) properties[key] = pv;\n\t}\n\tconst textBounds = measureTextBounds(live);\n\tif (textBounds) properties.TextBounds = textBounds;\n\tif (Object.keys(properties).length > 0) node.properties = properties;\n\tconst children = live.children.flatMap((slot) => slot.nodes);\n\tif (children.length > 0) node.children = children.map(toScene);\n\treturn node;\n}\n\n// --- mount -------------------------------------------------------------------\n\nconst HOST_ID = \"loom-root\";\n\n/** The outer preview viewport (`#loom-root`), created if the host page lacks it. */\nfunction resolveHost(target?: HTMLElement): HTMLElement {\n\tif (target) return target;\n\tconst existing = document.getElementById(HOST_ID);\n\tif (existing) return existing;\n\tconst el = document.createElement(\"div\");\n\tel.id = HOST_ID;\n\tel.style.position = \"relative\";\n\tel.style.width = \"100vw\";\n\tel.style.height = \"100vh\";\n\tel.style.overflow = \"hidden\";\n\tdocument.body.appendChild(el);\n\treturn el;\n}\n\n/**\n * Mount a vide component into the preview DOM and return an unmount function.\n * `component` is run once to build the tree; reactive props/children drive\n * subsequent repaints. Each mount gets its own container under `#loom-root`, so\n * independent mounts don't clobber each other (the renderer replaces its own\n * container's children every commit). The tree is laid out (WASM) and rendered\n * with a ResizeObserver re-laying-out on viewport changes — same as react.\n */\nexport function mount(\n\tcomponent: () => VideNode,\n\ttarget?: HTMLElement,\n): () => void {\n\tconst host = document.createElement(\"div\");\n\thost.style.position = \"absolute\";\n\thost.style.inset = \"0\";\n\tresolveHost(target).appendChild(host);\n\n\treturn root((dispose) => {\n\t\tlet ready = false;\n\t\tlet scheduled = false;\n\t\tlet disposed = false;\n\t\tlet live: LiveNode | undefined;\n\n\t\tconst paint = (): void => {\n\t\t\tscheduled = false;\n\t\t\tif (disposed || !ready || !live) return;\n\t\t\tconst width = host.clientWidth;\n\t\t\tconst height = host.clientHeight;\n\t\t\tif (width === 0 || height === 0) return; // wait for the mount to be sized\n\t\t\ttry {\n\t\t\t\tconst scene = toScene(live);\n\t\t\t\trenderScene(scene, computeLayout(scene, { width, height }), host);\n\t\t\t} catch (err) {\n\t\t\t\t// Never let a malformed scene escape an effect or the RO callback.\n\t\t\t\tconsole.error(\"loom vide:\", err);\n\t\t\t}\n\t\t};\n\t\tconst schedule = (): void => {\n\t\t\tif (scheduled) return;\n\t\t\tscheduled = true;\n\t\t\tqueueMicrotask(paint);\n\t\t};\n\n\t\t// Build inside this root so the reactive bindings are disposable.\n\t\tlive = build(component(), schedule);\n\n\t\tconst observer = new ResizeObserver(() => paint());\n\t\tobserver.observe(host);\n\t\tvoid initLayout().then(() => {\n\t\t\tready = true;\n\t\t\tpaint();\n\t\t});\n\n\t\treturn () => {\n\t\t\tdisposed = true; // a queued microtask paint must not resurrect the DOM\n\t\t\tobserver.disconnect();\n\t\t\tdispose();\n\t\t\thost.remove();\n\t\t};\n\t});\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/reactive.ts","../src/index.ts"],"sourcesContent":["/**\n * A minimal fine-grained reactive core, mirroring vide's `source`/`derive`/\n * `effect` primitives. Its only job is to track which effects read which sources,\n * so a source write re-runs exactly the effects that depend on it. The loom vide\n * adapter wires reactive props/children onto these so a source change reschedules\n * a Scene IR rebuild (see `index.ts`).\n *\n * Unlike React (a VDOM reconciler), vide is push-based: there is no diff, signals\n * notify their subscribers directly. Both paradigms feed the *same* Scene IR.\n *\n * Reactions form an ownership tree: an effect created while another reaction runs\n * becomes that reaction's child and is disposed when the parent re-runs (or when\n * the enclosing `root` is disposed). This is what keeps dynamic effects — e.g. a\n * `derive` or a reactive child rebuilt inside an effect — from leaking.\n */\n\ntype CleanupFn = () => void;\n\ninterface Reaction {\n\trun: () => void;\n\t/** Subscriber sets this reaction belongs to, cleared before each re-run. */\n\tdeps: Set<Set<Reaction>>;\n\tcleanups: CleanupFn[];\n\t/** Effects created while this reaction was running; disposed before re-run. */\n\tchildren: Reaction[];\n}\n\n// The running reaction doubles as the current owner: dependency reads subscribe\n// it, and effects created during its run nest under it.\nlet currentReaction: Reaction | undefined;\n\n/** A vide source: call with no args to read (tracked), with one arg to write. */\nexport interface Source<T> {\n\t(): T;\n\t(value: T): T;\n}\n\n/** Create a reactive source seeded with `initial`. */\nexport function source<T>(initial: T): Source<T> {\n\tlet value = initial;\n\tconst subscribers = new Set<Reaction>();\n\tfunction accessor(...args: [T] | []): T {\n\t\tif (args.length === 0) {\n\t\t\tif (currentReaction) {\n\t\t\t\tsubscribers.add(currentReaction);\n\t\t\t\tcurrentReaction.deps.add(subscribers);\n\t\t\t}\n\t\t\treturn value;\n\t\t}\n\t\tvalue = args[0];\n\t\t// Snapshot: a reaction re-subscribes (mutating the set) as it re-runs.\n\t\tfor (const reaction of [...subscribers]) reaction.run();\n\t\treturn value;\n\t}\n\treturn accessor as Source<T>;\n}\n\nfunction disposeReaction(reaction: Reaction): void {\n\t// Dispose nested effects first, depth-first, so their cleanups run too.\n\tfor (const child of reaction.children) disposeReaction(child);\n\treaction.children = [];\n\tfor (const dep of reaction.deps) dep.delete(reaction);\n\treaction.deps.clear();\n\tfor (const fn of reaction.cleanups) fn();\n\treaction.cleanups = [];\n}\n\n/** Run `fn` now and re-run it whenever a source it read changes. */\nexport function effect(fn: () => void): void {\n\tconst reaction: Reaction = {\n\t\trun,\n\t\tdeps: new Set(),\n\t\tcleanups: [],\n\t\tchildren: [],\n\t};\n\tfunction run(): void {\n\t\t// Tear down the previous run (deps, cleanups, and any nested effects) so a\n\t\t// re-run starts clean and dynamic children don't accumulate.\n\t\tdisposeReaction(reaction);\n\t\tconst prev = currentReaction;\n\t\tcurrentReaction = reaction;\n\t\ttry {\n\t\t\tfn();\n\t\t} finally {\n\t\t\tcurrentReaction = prev;\n\t\t}\n\t}\n\tcurrentReaction?.children.push(reaction);\n\trun();\n}\n\n/** A memoized computation: a source kept in sync with `fn` via an effect. */\nexport function derive<T>(fn: () => T): () => T {\n\tconst s = source<T>(undefined as T);\n\teffect(() => {\n\t\ts(fn());\n\t});\n\treturn () => s();\n}\n\n/** Register a cleanup to run before the enclosing effect re-runs or disposes. */\nexport function cleanup(fn: CleanupFn): void {\n\tcurrentReaction?.cleanups.push(fn);\n}\n\n/**\n * Open a disposal scope. `fn` receives a `dispose` that tears down every effect\n * created within (recursively). `mount` runs the component tree inside a root so\n * unmounting disposes all its reactive bindings.\n */\nexport function root<T>(fn: (dispose: () => void) => T): T {\n\tconst owner: Reaction = {\n\t\trun: () => {},\n\t\tdeps: new Set(),\n\t\tcleanups: [],\n\t\tchildren: [],\n\t};\n\tconst prev = currentReaction;\n\tcurrentReaction = owner;\n\ttry {\n\t\treturn fn(() => disposeReaction(owner));\n\t} finally {\n\t\tcurrentReaction = prev;\n\t}\n}\n","/**\n * `@loom-dev/vide` — a vide frontend adapter for loom.\n *\n * The second proof that the Scene IR is the real contract: vide is push-based\n * fine-grained reactivity (no VDOM, no reconciler — see `./reactive`), yet it\n * feeds the *exact same* `SceneNode` tree into the *same* WASM layout engine and\n * DOM renderer that `@loom-dev/react` uses. Nothing in `@loom-dev/layout` or\n * `@loom-dev/renderer` knows which frontend produced the tree.\n *\n * Authoring mirrors vide: `create(\"Frame\")({ ...props, [1]: child })`, where\n * string keys are Roblox properties (a function value is a reactive binding) and\n * number keys are children (vide's array-part).\n */\nimport { computeLayout, initLayout } from \"@loom-dev/layout\";\nimport { fontShorthand, instanceFont, renderScene } from \"@loom-dev/renderer\";\nimport { EnumItem, toPropertyValue } from \"@loom-dev/runtime\";\nimport {\n\tfontSizeToPx,\n\ttype PropertyValue,\n\tprop,\n\ttype SceneNode,\n} from \"@loom-dev/scene\";\nimport { effect, root } from \"./reactive\";\n\nexport {\n\tcleanup,\n\tderive,\n\teffect,\n\troot,\n\ttype Source,\n\tsource,\n} from \"./reactive\";\n\n// --- authoring: create() -----------------------------------------------------\n\nconst VIDE_NODE = Symbol(\"loom.vide.node\");\n\n/** A vide element descriptor produced by `create`. */\nexport interface VideNode {\n\treadonly [VIDE_NODE]: true;\n\treadonly className: string;\n\treadonly props: Readonly<Record<string, unknown>>;\n}\n\nfunction isVideNode(value: unknown): value is VideNode {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\t(value as { [VIDE_NODE]?: unknown })[VIDE_NODE] === true\n\t);\n}\n\n/**\n * `create(\"Frame\")` returns a constructor taking a props table. String keys are\n * Roblox properties (a function value is bound reactively at mount); number keys\n * are children.\n */\nexport function create(\n\tclassName: string,\n): (props?: Record<string, unknown>) => VideNode {\n\treturn (props = {}) => ({ [VIDE_NODE]: true, className, props });\n}\n\n// --- live tree ---------------------------------------------------------------\n\n/**\n * A resolved, mutable mirror of a `VideNode`. Reactive props write their current\n * value into `props`; `toScene` reads it. Children are grouped into slots so a\n * reactive (function) child can swap its subtree in place without disturbing its\n * siblings; a static child is a one-shot slot.\n */\ninterface LiveNode {\n\tclassName: string;\n\tname: string;\n\tid: string;\n\tprops: Map<string, unknown>;\n\tchildren: ChildSlot[];\n}\n\n/** A positional group of children — its `nodes` are re-derived if reactive. */\ninterface ChildSlot {\n\tnodes: LiveNode[];\n}\n\nlet nextId = 0;\n\n/** Resolve a child value (a node, or nested arrays of nodes) to LiveNodes. */\nfunction resolveChildren(value: unknown, schedule: () => void): LiveNode[] {\n\tif (isVideNode(value)) return [build(value, schedule)];\n\tif (Array.isArray(value)) {\n\t\treturn value.flatMap((item) => resolveChildren(item, schedule));\n\t}\n\treturn [];\n}\n\nfunction addChildSlot(\n\tparent: LiveNode,\n\tvalue: unknown,\n\tschedule: () => void,\n): void {\n\tif (typeof value === \"function\") {\n\t\t// A reactive child (vide's control-flow pattern): rebuild this slot's\n\t\t// subtree whenever the function's sources change. The rebuilt nodes' own\n\t\t// reactive bindings nest under this effect, so they dispose on each re-run.\n\t\tconst slot: ChildSlot = { nodes: [] };\n\t\tparent.children.push(slot);\n\t\teffect(() => {\n\t\t\tslot.nodes = resolveChildren((value as () => unknown)(), schedule);\n\t\t\tschedule();\n\t\t});\n\t} else {\n\t\tparent.children.push({ nodes: resolveChildren(value, schedule) });\n\t}\n}\n\n/** Build a `LiveNode` from a `VideNode`, wiring reactive props/children. */\nfunction build(node: VideNode, schedule: () => void): LiveNode {\n\tconst live: LiveNode = {\n\t\tclassName: node.className,\n\t\tname: node.className,\n\t\tid: `v${nextId++}`,\n\t\tprops: new Map(),\n\t\tchildren: [],\n\t};\n\tfor (const [key, value] of Object.entries(node.props)) {\n\t\tif (/^\\d+$/.test(key)) {\n\t\t\taddChildSlot(live, value, schedule);\n\t\t\tcontinue;\n\t\t}\n\t\tif (key === \"Name\" && typeof value === \"string\") {\n\t\t\tlive.name = value;\n\t\t\tcontinue;\n\t\t}\n\t\tif (typeof value === \"function\") {\n\t\t\t// A reactive prop: re-resolve and repaint whenever its sources change.\n\t\t\teffect(() => {\n\t\t\t\tlive.props.set(key, (value as () => unknown)());\n\t\t\t\tschedule();\n\t\t\t});\n\t\t} else {\n\t\t\tlive.props.set(key, value);\n\t\t}\n\t}\n\treturn live;\n}\n\n// --- live tree → Scene IR ----------------------------------------------------\n\nconst TEXT_CLASSES = new Set([\"TextLabel\", \"TextButton\", \"TextBox\"]);\nlet measureCtx: CanvasRenderingContext2D | null | undefined;\nfunction getMeasureCtx(): CanvasRenderingContext2D | null {\n\tif (measureCtx === undefined) {\n\t\tmeasureCtx =\n\t\t\ttypeof document !== \"undefined\"\n\t\t\t\t? document.createElement(\"canvas\").getContext(\"2d\")\n\t\t\t\t: null;\n\t}\n\treturn measureCtx;\n}\n\n/**\n * Measure an auto-sizing text node with the font the renderer paints and emit a\n * `TextBounds` Vector2 the layout engine reads for AutomaticSize (font metrics\n * live browser-side, not in the WASM engine). Mirrors the react adapter.\n */\nfunction measureTextBounds(live: LiveNode): PropertyValue | undefined {\n\tif (!TEXT_CLASSES.has(live.className)) return undefined;\n\tconst auto = live.props.get(\"AutomaticSize\");\n\tconst autoName = auto instanceof EnumItem ? auto.Name : undefined;\n\tif (autoName !== \"X\" && autoName !== \"Y\" && autoName !== \"XY\")\n\t\treturn undefined;\n\tconst text = live.props.get(\"Text\");\n\tif (typeof text !== \"string\" || text === \"\") return undefined;\n\tconst ctx = getMeasureCtx();\n\tif (!ctx) return undefined;\n\n\t// `TextSize` wins, legacy `FontSize` fills in, 14 is the Roblox default —\n\t// same precedence as `@loom-dev/scene`'s `getTextSize` and the react adapter.\n\tconst rawSize = live.props.get(\"TextSize\");\n\tconst rawFontSize = live.props.get(\"FontSize\");\n\tconst size =\n\t\ttypeof rawSize === \"number\"\n\t\t\t? rawSize\n\t\t\t: (fontSizeToPx(\n\t\t\t\t\trawFontSize instanceof EnumItem ? rawFontSize.Name : undefined,\n\t\t\t\t) ?? 14);\n\tctx.font = fontShorthand(\n\t\tinstanceFont({\n\t\t\tFont: live.props.get(\"Font\"),\n\t\t\tFontFace: live.props.get(\"FontFace\"),\n\t\t}),\n\t\tsize,\n\t);\n\tconst lines = text.split(\"\\n\");\n\tlet width = 0;\n\tfor (const line of lines)\n\t\twidth = Math.max(width, ctx.measureText(line).width);\n\treturn prop.vector2({ x: Math.ceil(width), y: lines.length * size });\n}\n\n/** Snapshot the live tree as Scene IR (called fresh on every paint). */\nfunction toScene(live: LiveNode): SceneNode {\n\tconst node: SceneNode = {\n\t\tclassName: live.className,\n\t\tname: live.name,\n\t\tid: live.id,\n\t};\n\tconst properties: Record<string, PropertyValue> = {};\n\tfor (const [key, value] of live.props) {\n\t\tconst pv = toPropertyValue(value);\n\t\tif (pv !== undefined) properties[key] = pv;\n\t}\n\tconst textBounds = measureTextBounds(live);\n\tif (textBounds) properties.TextBounds = textBounds;\n\tif (Object.keys(properties).length > 0) node.properties = properties;\n\tconst children = live.children.flatMap((slot) => slot.nodes);\n\tif (children.length > 0) node.children = children.map(toScene);\n\treturn node;\n}\n\n// --- mount -------------------------------------------------------------------\n\nconst HOST_ID = \"loom-root\";\n\n/** The outer preview viewport (`#loom-root`), created if the host page lacks it. */\nfunction resolveHost(target?: HTMLElement): HTMLElement {\n\tif (target) return target;\n\tconst existing = document.getElementById(HOST_ID);\n\tif (existing) return existing;\n\tconst el = document.createElement(\"div\");\n\tel.id = HOST_ID;\n\tel.style.position = \"relative\";\n\tel.style.width = \"100vw\";\n\tel.style.height = \"100vh\";\n\tel.style.overflow = \"hidden\";\n\tdocument.body.appendChild(el);\n\treturn el;\n}\n\n/**\n * Mount a vide component into the preview DOM and return an unmount function.\n * `component` is run once to build the tree; reactive props/children drive\n * subsequent repaints. Each mount gets its own container under `#loom-root`, so\n * independent mounts don't clobber each other (the renderer replaces its own\n * container's children every commit). The tree is laid out (WASM) and rendered\n * with a ResizeObserver re-laying-out on viewport changes — same as react.\n */\nexport function mount(\n\tcomponent: () => VideNode,\n\ttarget?: HTMLElement,\n): () => void {\n\tconst host = document.createElement(\"div\");\n\thost.style.position = \"absolute\";\n\thost.style.inset = \"0\";\n\tresolveHost(target).appendChild(host);\n\n\treturn root((dispose) => {\n\t\tlet ready = false;\n\t\tlet scheduled = false;\n\t\tlet disposed = false;\n\t\tlet live: LiveNode | undefined;\n\n\t\tconst paint = (): void => {\n\t\t\tscheduled = false;\n\t\t\tif (disposed || !ready || !live) return;\n\t\t\tconst width = host.clientWidth;\n\t\t\tconst height = host.clientHeight;\n\t\t\tif (width === 0 || height === 0) return; // wait for the mount to be sized\n\t\t\ttry {\n\t\t\t\tconst scene = toScene(live);\n\t\t\t\trenderScene(scene, computeLayout(scene, { width, height }), host);\n\t\t\t} catch (err) {\n\t\t\t\t// Never let a malformed scene escape an effect or the RO callback.\n\t\t\t\tconsole.error(\"loom vide:\", err);\n\t\t\t}\n\t\t};\n\t\tconst schedule = (): void => {\n\t\t\tif (scheduled) return;\n\t\t\tscheduled = true;\n\t\t\tqueueMicrotask(paint);\n\t\t};\n\n\t\t// Build inside this root so the reactive bindings are disposable.\n\t\tlive = build(component(), schedule);\n\n\t\tconst observer = new ResizeObserver(() => paint());\n\t\tobserver.observe(host);\n\t\tvoid initLayout().then(() => {\n\t\t\tready = true;\n\t\t\tpaint();\n\t\t});\n\n\t\treturn () => {\n\t\t\tdisposed = true; // a queued microtask paint must not resurrect the DOM\n\t\t\tobserver.disconnect();\n\t\t\tdispose();\n\t\t\thost.remove();\n\t\t};\n\t});\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loom-dev/vide",
3
- "version": "0.5.3",
3
+ "version": "0.6.1",
4
4
  "description": "Vide adapter for loom — fine-grained reactivity producing the same Scene IR as the React renderer.",
5
5
  "keywords": [
6
6
  "loom",
@@ -34,10 +34,10 @@
34
34
  "access": "public"
35
35
  },
36
36
  "dependencies": {
37
- "@loom-dev/layout": "0.5.3",
38
- "@loom-dev/renderer": "0.5.3",
39
- "@loom-dev/runtime": "0.5.3",
40
- "@loom-dev/scene": "0.5.3"
37
+ "@loom-dev/layout": "0.6.1",
38
+ "@loom-dev/renderer": "0.6.1",
39
+ "@loom-dev/runtime": "0.6.1",
40
+ "@loom-dev/scene": "0.6.1"
41
41
  },
42
42
  "devDependencies": {
43
43
  "tsdown": "^0.22.12",