@godot-scene-web/layout 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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/anchor-grammar.ts","../src/anchors.ts","../src/flow-wrap.ts","../src/container-types.ts","../src/style-box.ts","../src/theme.ts","../src/minimum-size.ts","../src/rects.ts","../src/layout-node.ts","../src/container-layout.ts","../src/membership.ts","../src/index.ts"],"sourcesContent":["import type { GodotRect } from \"./types\";\nexport interface GodotNodeAnchor {\n anchorTo: string;\n from: string;\n to: string;\n offset?: { x?: number; y?: number };\n}\nexport type GodotAnchorMap = Record<string, GodotNodeAnchor>;\nexport type GodotAnchorVerticalEdge =\n | \"top\"\n | \"bottom\"\n | \"vcenter\"\n | \"contentTop\"\n | \"contentBottom\";\nexport type GodotAnchorHorizontalEdge =\n | \"left\"\n | \"right\"\n | \"hcenter\"\n | \"contentLeft\"\n | \"contentRight\";\nexport interface GodotAnchorEdge {\n vertical: GodotAnchorVerticalEdge;\n horizontal: GodotAnchorHorizontalEdge;\n}\nconst verticals: Array<[string, GodotAnchorVerticalEdge]> = [\n [\"contentbottom\", \"contentBottom\"],\n [\"contenttop\", \"contentTop\"],\n [\"vcenter\", \"vcenter\"],\n [\"bottom\", \"bottom\"],\n [\"top\", \"top\"],\n];\nconst horizontals: Record<string, GodotAnchorHorizontalEdge> = {\n contentright: \"contentRight\",\n contentleft: \"contentLeft\",\n hcenter: \"hcenter\",\n right: \"right\",\n left: \"left\",\n};\nexport function parseAnchorEdge(token: string): GodotAnchorEdge | undefined {\n const normalized = token.toLowerCase();\n if (normalized === \"center\")\n return { vertical: \"vcenter\", horizontal: \"hcenter\" };\n for (const [key, vertical] of verticals) {\n if (!normalized.startsWith(key)) continue;\n const horizontal = horizontals[normalized.slice(key.length)];\n if (horizontal) return { vertical, horizontal };\n }\n return undefined;\n}\nexport function anchorEdgePoint(\n token: string,\n own: GodotRect,\n content: GodotRect,\n): { x: number; y: number } | undefined {\n const edge = parseAnchorEdge(token);\n if (!edge) return undefined;\n const x =\n edge.horizontal === \"contentRight\"\n ? content.x + content.width\n : edge.horizontal === \"contentLeft\"\n ? content.x\n : edge.horizontal === \"hcenter\"\n ? own.x + own.width / 2\n : edge.horizontal === \"right\"\n ? own.x + own.width\n : own.x;\n const y =\n edge.vertical === \"contentBottom\"\n ? content.y + content.height\n : edge.vertical === \"contentTop\"\n ? content.y\n : edge.vertical === \"vcenter\"\n ? own.y + own.height / 2\n : edge.vertical === \"bottom\"\n ? own.y + own.height\n : own.y;\n return { x, y };\n}\n","import { anchorEdgePoint, type GodotAnchorMap } from \"./anchor-grammar\";\nimport type {\n GodotLayoutDiagnostic,\n GodotLayoutNode,\n GodotRect,\n} from \"./types\";\n\n// The anchor declaration types and the edge grammar live in core (shared with\n// the browser-native CSS anchor-positioning emitter in `html`); re-export them\n// so layout's public API is unchanged.\nexport type { GodotAnchorMap, GodotNodeAnchor } from \"./anchor-grammar\";\n\nconst edgePoint = anchorEdgePoint;\n\n/** Union of a node's visible direct children's rendered rects (its content box). */\nfunction contentExtent(\n node: GodotLayoutNode,\n layoutByPath: Map<string, GodotLayoutNode>,\n): GodotRect {\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n let found = false;\n for (const childPath of node.children) {\n const child = layoutByPath.get(childPath);\n if (!child || child.visible === false) {\n continue;\n }\n const rect = child.renderedRect;\n minX = Math.min(minX, rect.x);\n minY = Math.min(minY, rect.y);\n maxX = Math.max(maxX, rect.x + rect.width);\n maxY = Math.max(maxY, rect.y + rect.height);\n found = true;\n }\n if (!found) {\n // No content: collapse to a zero-size box at the node's own origin so a\n // `contentBottom`/`contentRight` edge equals the node's top-left (matching a\n // flow container with nothing in it).\n const { x, y } = node.renderedRect;\n return { x, y, width: 0, height: 0 };\n }\n return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };\n}\n\n/** Translate a node and its whole subtree by `(dx, dy)` in global space. */\nfunction translateSubtree(\n root: GodotLayoutNode,\n dx: number,\n dy: number,\n layoutByPath: Map<string, GodotLayoutNode>,\n): void {\n const stack = [root.path];\n while (stack.length > 0) {\n const node = layoutByPath.get(stack.pop() as string);\n if (!node) {\n continue;\n }\n // `rect` drives the renderer's relative CSS `left/top`; `renderedRect` is the\n // global rect the layout-diff oracle compares. Both shift by the same pure\n // translation. Only the anchored root's parent stays put, so the root's offset\n // relative to its parent changes while descendants move rigidly with it.\n node.rect = {\n x: node.rect.x + dx,\n y: node.rect.y + dy,\n width: node.rect.width,\n height: node.rect.height,\n };\n node.renderedRect = {\n x: node.renderedRect.x + dx,\n y: node.renderedRect.y + dy,\n width: node.renderedRect.width,\n height: node.renderedRect.height,\n };\n if (node.cumulativeTransform) {\n node.cumulativeTransform = {\n ...node.cumulativeTransform,\n tx: node.cumulativeTransform.tx + dx,\n ty: node.cumulativeTransform.ty + dy,\n };\n }\n for (const childPath of node.children) {\n stack.push(childPath);\n }\n }\n}\n\n/**\n * Resolve every declared anchor, translating each anchored node (and its subtree)\n * so its `to` edge lands on its target's `from` edge. Targets that are themselves\n * anchored are resolved first; cycles are broken with a diagnostic.\n */\nexport function resolveAnchors(\n layoutByPath: Map<string, GodotLayoutNode>,\n anchors: GodotAnchorMap | undefined,\n diagnostics: GodotLayoutDiagnostic[],\n): void {\n if (!anchors) {\n return;\n }\n const resolved = new Set<string>();\n const inProgress = new Set<string>();\n\n const resolveOne = (path: string): void => {\n if (resolved.has(path)) {\n return;\n }\n const anchor = anchors[path];\n const self = layoutByPath.get(path);\n if (!anchor || !self) {\n resolved.add(path);\n return;\n }\n if (inProgress.has(path)) {\n diagnostics.push({\n severity: \"warning\",\n code: \"anchor-cycle\",\n message: `Anchor cycle detected resolving ${path}; leaving it unmoved.`,\n nodePath: path,\n });\n return;\n }\n inProgress.add(path);\n // Resolve the target first when it is itself anchored, so its `from` edge is\n // already in its final position.\n if (anchors[anchor.anchorTo]) {\n resolveOne(anchor.anchorTo);\n }\n const target = layoutByPath.get(anchor.anchorTo);\n if (!target) {\n diagnostics.push({\n severity: \"warning\",\n code: \"anchor-target-missing\",\n message: `Anchor target '${anchor.anchorTo}' not found for ${path}.`,\n nodePath: path,\n });\n } else {\n const targetPoint = edgePoint(\n anchor.from,\n target.renderedRect,\n contentExtent(target, layoutByPath),\n );\n const selfPoint = edgePoint(\n anchor.to,\n self.renderedRect,\n contentExtent(self, layoutByPath),\n );\n if (!targetPoint || !selfPoint) {\n diagnostics.push({\n severity: \"warning\",\n code: \"anchor-edge-unparsed\",\n message: `Unrecognized anchor edge (from='${anchor.from}', to='${anchor.to}') for ${path}.`,\n nodePath: path,\n });\n } else {\n const dx = targetPoint.x - selfPoint.x + (anchor.offset?.x ?? 0);\n const dy = targetPoint.y - selfPoint.y + (anchor.offset?.y ?? 0);\n if (dx !== 0 || dy !== 0) {\n translateSubtree(self, dx, dy, layoutByPath);\n }\n }\n }\n inProgress.delete(path);\n resolved.add(path);\n };\n\n for (const path of Object.keys(anchors)) {\n resolveOne(path);\n }\n}\n","import type { GodotRect } from \"./types\";\n\nexport interface FlowEntry<T> {\n item: T;\n size: { width: number; height: number };\n}\n\nexport interface PlacedFlowEntry<T> extends FlowEntry<T> {\n x: number;\n y: number;\n}\n\n/**\n * Wraps flow-container entries into lines, mirroring Godot's\n * `FlowContainer::_resort()` wrapping. The cross axis advances by the line's\n * largest cross extent plus the relevant separation.\n */\nexport function flowWrapLines<T>(\n rect: GodotRect,\n entries: FlowEntry<T>[],\n vertical: boolean,\n hSeparation: number,\n vSeparation: number,\n): PlacedFlowEntry<T>[][] {\n const lines: PlacedFlowEntry<T>[][] = [];\n let currentLine: PlacedFlowEntry<T>[] = [];\n let cursorX = rect.x;\n let cursorY = rect.y;\n let lineCrossSize = 0;\n for (const entry of entries) {\n const size = entry.size;\n if (vertical) {\n if (cursorY > rect.y && cursorY + size.height > rect.y + rect.height) {\n lines.push(currentLine);\n currentLine = [];\n cursorX += lineCrossSize + hSeparation;\n cursorY = rect.y;\n lineCrossSize = 0;\n }\n currentLine.push({ ...entry, x: cursorX, y: cursorY });\n cursorY += size.height + vSeparation;\n lineCrossSize = Math.max(lineCrossSize, size.width);\n } else {\n if (cursorX > rect.x && cursorX + size.width > rect.x + rect.width) {\n lines.push(currentLine);\n currentLine = [];\n cursorX = rect.x;\n cursorY += lineCrossSize + vSeparation;\n lineCrossSize = 0;\n }\n currentLine.push({ ...entry, x: cursorX, y: cursorY });\n cursorX += size.width + hSeparation;\n lineCrossSize = Math.max(lineCrossSize, size.height);\n }\n }\n if (currentLine.length > 0) {\n lines.push(currentLine);\n }\n return lines;\n}\n\n/** Total cross-axis extent of wrapped lines: Σ line cross size + separations. */\nexport function flowCrossExtent<T>(\n lines: PlacedFlowEntry<T>[][],\n vertical: boolean,\n hSeparation: number,\n vSeparation: number,\n): number {\n const crossSeparation = vertical ? hSeparation : vSeparation;\n const lineCrossSizes = lines.map((line) =>\n line.reduce(\n (max, entry) =>\n Math.max(max, vertical ? entry.size.width : entry.size.height),\n 0,\n ),\n );\n return (\n lineCrossSizes.reduce((sum, size) => sum + size, 0) +\n Math.max(0, lines.length - 1) * crossSeparation\n );\n}\n","import { asBoolean } from \"@godot-scene-web/core\";\nimport type { IndexedNode } from \"./types\";\n\nexport function isBoxContainerType(type: string): boolean {\n return (\n type === \"BoxContainer\" ||\n type === \"HBoxContainer\" ||\n type === \"VBoxContainer\"\n );\n}\n\nexport function boxContainerHorizontal(indexed: IndexedNode): boolean {\n if (indexed.node.type === \"HBoxContainer\") {\n return true;\n }\n if (indexed.node.type === \"VBoxContainer\") {\n return false;\n }\n return !(asBoolean(indexed.props.vertical) ?? false);\n}\n\nexport function isFlowContainerType(type: string): boolean {\n return (\n type === \"FlowContainer\" ||\n type === \"HFlowContainer\" ||\n type === \"VFlowContainer\"\n );\n}\n\nexport function flowContainerVertical(indexed: IndexedNode): boolean {\n if (indexed.node.type === \"VFlowContainer\") {\n return true;\n }\n if (indexed.node.type === \"HFlowContainer\") {\n return false;\n }\n return asBoolean(indexed.props.vertical) ?? false;\n}\n","import {\n asResourceRef,\n asString,\n type GodotResource,\n type GodotVariant,\n} from \"@godot-scene-web/core\";\nimport { normalizeRect, numeric } from \"./rects\";\nimport type { GodotLayoutOptions, GodotRect, IndexedNode } from \"./types\";\n\nexport interface StyleBoxMetrics {\n left: number;\n top: number;\n right: number;\n bottom: number;\n}\n\nexport function contentRectForStyleBox(\n rect: GodotRect,\n metrics: StyleBoxMetrics,\n): GodotRect {\n return normalizeRect({\n x: rect.x + metrics.left,\n y: rect.y + metrics.top,\n width: rect.width - metrics.left - metrics.right,\n height: rect.height - metrics.top - metrics.bottom,\n });\n}\n\nexport function panelStyleBoxMetrics(\n indexed: IndexedNode,\n options: GodotLayoutOptions,\n): StyleBoxMetrics {\n const stylebox =\n resolveStyleBox(\n indexed.props[\"theme_override_styles/panel\"],\n indexed,\n options,\n ) ??\n resolveStyleBox(\n options.resolveTheme?.(indexed.node, \"panel\"),\n indexed,\n options,\n );\n return styleBoxMetrics(stylebox);\n}\n\nexport function resolveStyleBox(\n value: GodotVariant | undefined,\n indexed: IndexedNode,\n options: GodotLayoutOptions,\n): unknown {\n const ref = asResourceRef(value);\n return ref ? options.resolveResource?.(ref, indexed.node) : value;\n}\n\nfunction styleBoxMetrics(resource: unknown): StyleBoxMetrics {\n const { type, properties } = resourceDocument(resource);\n if (!type || type === \"StyleBoxEmpty\") {\n return { left: 0, top: 0, right: 0, bottom: 0 };\n }\n const props = properties ?? {};\n if (type === \"StyleBoxFlat\") {\n const borders = {\n left:\n numeric(props, \"border_width_left\") ??\n numeric(props, \"border_width_all\") ??\n 0,\n top:\n numeric(props, \"border_width_top\") ??\n numeric(props, \"border_width_all\") ??\n 0,\n right:\n numeric(props, \"border_width_right\") ??\n numeric(props, \"border_width_all\") ??\n 0,\n bottom:\n numeric(props, \"border_width_bottom\") ??\n numeric(props, \"border_width_all\") ??\n 0,\n };\n return {\n left: styleBoxMargin(props, \"left\", borders.left),\n top: styleBoxMargin(props, \"top\", borders.top),\n right: styleBoxMargin(props, \"right\", borders.right),\n bottom: styleBoxMargin(props, \"bottom\", borders.bottom),\n };\n }\n return {\n left: Math.max(\n 0,\n numeric(props, \"content_margin_left\") ??\n numeric(props, \"content_margin_all\") ??\n 0,\n ),\n top: Math.max(\n 0,\n numeric(props, \"content_margin_top\") ??\n numeric(props, \"content_margin_all\") ??\n 0,\n ),\n right: Math.max(\n 0,\n numeric(props, \"content_margin_right\") ??\n numeric(props, \"content_margin_all\") ??\n 0,\n ),\n bottom: Math.max(\n 0,\n numeric(props, \"content_margin_bottom\") ??\n numeric(props, \"content_margin_all\") ??\n 0,\n ),\n };\n}\n\nfunction styleBoxMargin(\n props: Record<string, GodotVariant>,\n side: \"left\" | \"top\" | \"right\" | \"bottom\",\n fallback: number,\n): number {\n const value =\n numeric(props, `content_margin_${side}`) ??\n numeric(props, \"content_margin_all\");\n return value !== undefined && value >= 0 ? value : fallback;\n}\n\nfunction resourceDocument(resource: unknown): {\n type?: string;\n properties?: Record<string, GodotVariant>;\n} {\n if (!resource || typeof resource !== \"object\") {\n return {};\n }\n const record = resource as Record<string, unknown>;\n const directDocument = record.document;\n const document =\n directDocument &&\n typeof directDocument === \"object\" &&\n !Array.isArray(directDocument)\n ? (directDocument as GodotResource)\n : undefined;\n const properties =\n document?.properties ??\n (record.properties &&\n typeof record.properties === \"object\" &&\n !Array.isArray(record.properties)\n ? (record.properties as Record<string, GodotVariant>)\n : undefined);\n return {\n type:\n asString(document?.header?.attributes.type) ??\n (typeof record.type === \"string\" ? record.type : undefined),\n properties,\n };\n}\n","import { asNumber } from \"@godot-scene-web/core\";\nimport { numeric } from \"./rects\";\nimport type { GodotLayoutOptions, IndexedNode } from \"./types\";\n\n/**\n * Godot's built-in default theme constants for container nodes, mirroring\n * `scene/theme/default_theme.cpp` (4.5.1, `set_constant(..., Math::round(4 *\n * scale))` at scale 1). A node that does not carry an explicit\n * `theme_override_constants/*` (and whose project theme provides nothing) still\n * inherits these from the default theme, so the layout engine must fall back to\n * them rather than to 0 — e.g. an HBoxContainer with no separation override lays\n * its children out 4px apart, not flush. MarginContainer margins default to 0 in\n * the default theme, so they intentionally have no entry here.\n */\nconst DEFAULT_THEME_CONSTANTS: Record<string, Record<string, number>> = {\n BoxContainer: { separation: 4 },\n HBoxContainer: { separation: 4 },\n VBoxContainer: { separation: 4 },\n GridContainer: { h_separation: 4, v_separation: 4 },\n FlowContainer: { h_separation: 4, v_separation: 4 },\n HFlowContainer: { h_separation: 4, v_separation: 4 },\n VFlowContainer: { h_separation: 4, v_separation: 4 },\n};\n\nfunction defaultThemeConstant(\n indexed: IndexedNode,\n name: string,\n): number | undefined {\n const type = indexed.node.type;\n if (!type) {\n return undefined;\n }\n return DEFAULT_THEME_CONSTANTS[type]?.[name];\n}\n\nexport function themeNumber(\n indexed: IndexedNode,\n name: string,\n options: GodotLayoutOptions,\n): number | undefined {\n return (\n numeric(indexed.props, `theme_override_constants/${name}`) ??\n numeric(indexed.props, `theme_constant_${name}`) ??\n asNumber(options.resolveTheme?.(indexed.node, name)) ??\n defaultThemeConstant(indexed, name)\n );\n}\n","import { asBoolean, asResourceRef, asVector2 } from \"@godot-scene-web/core\";\nimport {\n boxContainerHorizontal,\n flowContainerVertical,\n} from \"./container-types\";\nimport { flowCrossExtent, flowWrapLines } from \"./flow-wrap\";\nimport { numeric } from \"./rects\";\nimport { panelStyleBoxMetrics } from \"./style-box\";\nimport { themeNumber } from \"./theme\";\nimport type { GodotLayoutOptions, GodotRect, IndexedNode } from \"./types\";\n\ntype Size = { width: number; height: number };\n\nexport function preferredSize(\n indexed: IndexedNode,\n byPath: Map<string, IndexedNode>,\n options: GodotLayoutOptions,\n): Size {\n return (\n combinedMinimum(indexed, byPath, options) ??\n explicitSizeFromProps(indexed) ??\n sizeFromOffsets(indexed) ?? { width: 0, height: 0 }\n );\n}\n\nexport function explicitSizeFromProps(indexed: IndexedNode): Size | undefined {\n const size = asVector2(indexed.props.size);\n if (size) {\n return { width: size.x, height: size.y };\n }\n const width = numeric(indexed.props, \"size_width\");\n const height = numeric(indexed.props, \"size_height\");\n return width !== undefined && height !== undefined\n ? { width, height }\n : undefined;\n}\n\nexport function sizeFromOffsets(indexed: IndexedNode): Size | undefined {\n const left = numeric(indexed.props, \"offset_left\") ?? 0;\n const top = numeric(indexed.props, \"offset_top\") ?? 0;\n const right = numeric(indexed.props, \"offset_right\");\n const bottom = numeric(indexed.props, \"offset_bottom\");\n if (right === undefined || bottom === undefined) {\n return undefined;\n }\n return {\n width: Math.max(0, right - left),\n height: Math.max(0, bottom - top),\n };\n}\n\nexport function customMinimum(indexed: IndexedNode): Size | undefined {\n const vector = asVector2(indexed.props.custom_minimum_size);\n if (vector) {\n return { width: vector.x, height: vector.y };\n }\n const width =\n numeric(indexed.props, \"custom_minimum_width\") ??\n numeric(indexed.props, \"minimum_width\");\n const height =\n numeric(indexed.props, \"custom_minimum_height\") ??\n numeric(indexed.props, \"minimum_height\");\n return width !== undefined && height !== undefined\n ? { width, height }\n : undefined;\n}\n\nfunction declaredCombinedMinimum(indexed: IndexedNode): Size | undefined {\n const vector = asVector2(indexed.props.combined_minimum_size);\n if (vector) {\n return { width: vector.x, height: vector.y };\n }\n const width = numeric(indexed.props, \"combined_minimum_width\");\n const height = numeric(indexed.props, \"combined_minimum_height\");\n return width !== undefined && height !== undefined\n ? { width, height }\n : undefined;\n}\n\nexport function combinedMinimum(\n indexed: IndexedNode,\n byPath: Map<string, IndexedNode>,\n options: GodotLayoutOptions,\n currentRect?: GodotRect,\n): Size | undefined {\n const declared = declaredCombinedMinimum(indexed);\n const custom = customMinimum(indexed);\n const internal = internalMinimum(indexed, options, currentRect);\n const container = containerMinimumSize(indexed, byPath, options);\n return maxSize(declared, maxSize(custom, maxSize(internal, container)));\n}\n\nfunction maxSize(a: Size | undefined, b: Size | undefined): Size | undefined {\n if (!a) {\n return b;\n }\n if (!b) {\n return a;\n }\n return {\n width: Math.max(a.width, b.width),\n height: Math.max(a.height, b.height),\n };\n}\n\n/**\n * Container analogue of Godot `Container::get_minimum_size()`, memoized on the\n * IndexedNode. Non-container controls return `undefined` (a plain Control/Node\n * does not size to its children). Recurses through `byPath` children, which\n * already include flattened instanced PackedScene content.\n */\nexport function containerMinimumSize(\n indexed: IndexedNode,\n byPath: Map<string, IndexedNode>,\n options: GodotLayoutOptions,\n): Size | undefined {\n if (indexed.minimumSize !== undefined) {\n return indexed.minimumSize ?? undefined;\n }\n const size = computeContainerMinimum(indexed, byPath, options);\n indexed.minimumSize = size ?? null;\n return size;\n}\n\nfunction computeContainerMinimum(\n indexed: IndexedNode,\n byPath: Map<string, IndexedNode>,\n options: GodotLayoutOptions,\n): Size | undefined {\n const type = indexed.node.type ?? \"Node\";\n switch (type) {\n case \"BoxContainer\":\n case \"HBoxContainer\":\n case \"VBoxContainer\":\n return boxMinimum(indexed, byPath, options);\n case \"GridContainer\":\n return gridMinimum(indexed, byPath, options);\n case \"FlowContainer\":\n case \"HFlowContainer\":\n case \"VFlowContainer\":\n return flowMinimum(indexed, byPath, options);\n case \"MarginContainer\":\n return marginMinimum(indexed, byPath, options);\n case \"CenterContainer\":\n return maxChildMinimum(indexed, byPath, options);\n case \"AspectRatioContainer\":\n return maxChildMinimum(indexed, byPath, options);\n case \"PanelContainer\":\n return panelMinimum(indexed, byPath, options);\n case \"ScrollContainer\":\n return scrollMinimum(indexed, byPath, options);\n default:\n return undefined;\n }\n}\n\nexport function visibleChildren(\n indexed: IndexedNode,\n byPath: Map<string, IndexedNode>,\n): IndexedNode[] {\n return indexed.children\n .map((path) => byPath.get(path))\n .filter(\n (child): child is IndexedNode =>\n Boolean(child) && (asBoolean(child!.props.visible) ?? true),\n );\n}\n\nfunction childSizes(\n indexed: IndexedNode,\n byPath: Map<string, IndexedNode>,\n options: GodotLayoutOptions,\n): Size[] {\n return visibleChildren(indexed, byPath).map(\n (child) =>\n combinedMinimum(child, byPath, options) ?? { width: 0, height: 0 },\n );\n}\n\nfunction boxMinimum(\n indexed: IndexedNode,\n byPath: Map<string, IndexedNode>,\n options: GodotLayoutOptions,\n): Size {\n const horizontal = boxContainerHorizontal(indexed);\n const separation = themeNumber(indexed, \"separation\", options) ?? 0;\n const sizes = childSizes(indexed, byPath, options);\n let main = 0;\n let cross = 0;\n sizes.forEach((size, index) => {\n const childMain = horizontal ? size.width : size.height;\n const childCross = horizontal ? size.height : size.width;\n main += childMain + (index === 0 ? 0 : separation);\n cross = Math.max(cross, childCross);\n });\n return horizontal\n ? { width: main, height: cross }\n : { width: cross, height: main };\n}\n\nfunction gridMinimum(\n indexed: IndexedNode,\n byPath: Map<string, IndexedNode>,\n options: GodotLayoutOptions,\n): Size {\n const columns = Math.max(\n 1,\n Math.floor(numeric(indexed.props, \"columns\") ?? 1),\n );\n const hSeparation =\n themeNumber(indexed, \"h_separation\", options) ??\n themeNumber(indexed, \"separation\", options) ??\n 0;\n const vSeparation =\n themeNumber(indexed, \"v_separation\", options) ??\n themeNumber(indexed, \"separation\", options) ??\n 0;\n const sizes = childSizes(indexed, byPath, options);\n const columnWidths = new Map<number, number>();\n const rowHeights = new Map<number, number>();\n let maxColumn = 0;\n let maxRow = 0;\n sizes.forEach((size, index) => {\n const column = index % columns;\n const row = Math.floor(index / columns);\n columnWidths.set(\n column,\n Math.max(columnWidths.get(column) ?? 0, size.width),\n );\n rowHeights.set(row, Math.max(rowHeights.get(row) ?? 0, size.height));\n maxColumn = Math.max(maxColumn, column);\n maxRow = Math.max(maxRow, row);\n });\n const width =\n [...columnWidths.values()].reduce((sum, value) => sum + value, 0) +\n hSeparation * maxColumn;\n const height =\n [...rowHeights.values()].reduce((sum, value) => sum + value, 0) +\n vSeparation * maxRow;\n return { width, height };\n}\n\nfunction flowMinimum(\n indexed: IndexedNode,\n byPath: Map<string, IndexedNode>,\n options: GodotLayoutOptions,\n): Size {\n const vertical = flowContainerVertical(indexed);\n const hSeparation =\n themeNumber(indexed, \"h_separation\", options) ??\n themeNumber(indexed, \"separation\", options) ??\n 0;\n const vSeparation =\n themeNumber(indexed, \"v_separation\", options) ??\n themeNumber(indexed, \"separation\", options) ??\n 0;\n const sizes = childSizes(indexed, byPath, options);\n const maxWidth = sizes.reduce((max, size) => Math.max(max, size.width), 0);\n const maxHeight = sizes.reduce((max, size) => Math.max(max, size.height), 0);\n\n // Godot's flow minimum cross extent comes from the previous resort at the\n // container's actual size. The iterative fixpoint in `resolveGodotSceneTree`\n // feeds the resolved main-axis extent back via `flowMainExtent`; before that\n // is known we fall back to the authored offset/explicit size, then to the\n // single-line floor (the browser's `flex-wrap` stays authoritative on-screen).\n const offsetSize = explicitSizeFromProps(indexed) ?? sizeFromOffsets(indexed);\n const offsetMain = vertical ? offsetSize?.height : offsetSize?.width;\n const mainExtent =\n indexed.flowMainExtent ??\n (offsetMain !== undefined && offsetMain > 0 ? offsetMain : undefined);\n if (mainExtent !== undefined && mainExtent > 0) {\n const rect: GodotRect = vertical\n ? { x: 0, y: 0, width: maxWidth, height: mainExtent }\n : { x: 0, y: 0, width: mainExtent, height: maxHeight };\n const lines = flowWrapLines(\n rect,\n sizes.map((size) => ({ item: size, size })),\n vertical,\n hSeparation,\n vSeparation,\n );\n const crossExtent = flowCrossExtent(\n lines,\n vertical,\n hSeparation,\n vSeparation,\n );\n return vertical\n ? { width: crossExtent, height: maxHeight }\n : { width: maxWidth, height: crossExtent };\n }\n return { width: maxWidth, height: maxHeight };\n}\n\nfunction marginMinimum(\n indexed: IndexedNode,\n byPath: Map<string, IndexedNode>,\n options: GodotLayoutOptions,\n): Size {\n const left = themeNumber(indexed, \"margin_left\", options) ?? 0;\n const top = themeNumber(indexed, \"margin_top\", options) ?? 0;\n const right = themeNumber(indexed, \"margin_right\", options) ?? 0;\n const bottom = themeNumber(indexed, \"margin_bottom\", options) ?? 0;\n const max = maxChildMinimum(indexed, byPath, options);\n return { width: max.width + left + right, height: max.height + top + bottom };\n}\n\nfunction panelMinimum(\n indexed: IndexedNode,\n byPath: Map<string, IndexedNode>,\n options: GodotLayoutOptions,\n): Size {\n const metrics = panelStyleBoxMetrics(indexed, options);\n const max = maxChildMinimum(indexed, byPath, options);\n return {\n width: max.width + metrics.left + metrics.right,\n height: max.height + metrics.top + metrics.bottom,\n };\n}\n\nfunction scrollMinimum(\n indexed: IndexedNode,\n byPath: Map<string, IndexedNode>,\n options: GodotLayoutOptions,\n): Size {\n const metrics = panelStyleBoxMetrics(indexed, options);\n const largest = maxChildMinimum(indexed, byPath, options);\n const horizontalDisabled =\n (numeric(indexed.props, \"horizontal_scroll_mode\") ?? 1) === 0;\n const verticalDisabled =\n (numeric(indexed.props, \"vertical_scroll_mode\") ?? 1) === 0;\n return {\n width:\n metrics.left + metrics.right + (horizontalDisabled ? largest.width : 0),\n height:\n metrics.top + metrics.bottom + (verticalDisabled ? largest.height : 0),\n };\n}\n\nfunction maxChildMinimum(\n indexed: IndexedNode,\n byPath: Map<string, IndexedNode>,\n options: GodotLayoutOptions,\n): Size {\n return childSizes(indexed, byPath, options).reduce(\n (max, size) => ({\n width: Math.max(max.width, size.width),\n height: Math.max(max.height, size.height),\n }),\n { width: 0, height: 0 },\n );\n}\n\nexport const TEXT_CONTENT_TYPES = new Set([\"Label\", \"RichTextLabel\"]);\n\nfunction internalMinimum(\n indexed: IndexedNode,\n options: GodotLayoutOptions,\n currentRect?: GodotRect,\n): Size | undefined {\n // Text nodes derive a content-driven minimum from the host's measurement of\n // their text (the analogue of a `TextureRect`'s intrinsic texture size below).\n // `combinedMinimum` already takes the max against `custom_minimum_size`, so this\n // matches Godot's `Label::get_minimum_size()` = max(custom, paragraph box).\n if (TEXT_CONTENT_TYPES.has(indexed.node.type ?? \"\")) {\n const size = options.resolveTextContentSize?.(\n indexed.node,\n indexed.path,\n indexed.props,\n // During layout the node already has a column width (`currentRect.width`);\n // hand it to the host so a reflowing label wraps to it. In the bottom-up\n // minimum pass there is no rect yet, so this is `undefined`.\n currentRect?.width,\n );\n if (!size || size.width < 0 || size.height < 0) {\n return undefined;\n }\n return size;\n }\n if (indexed.node.type !== \"TextureRect\") {\n return undefined;\n }\n const textureRef = asResourceRef(indexed.props.texture);\n if (!textureRef) {\n return undefined;\n }\n const textureSize = resourceSize(\n options.resolveResource?.(textureRef, indexed.node),\n );\n if (!textureSize || textureSize.width <= 0 || textureSize.height <= 0) {\n return undefined;\n }\n const expandMode = numeric(indexed.props, \"expand_mode\") ?? 0;\n if (expandMode === 0) {\n return textureSize;\n }\n if (expandMode === 2) {\n return { width: currentRect?.height ?? 0, height: 0 };\n }\n if (expandMode === 3) {\n return {\n width:\n ((currentRect?.height ?? 0) * textureSize.width) / textureSize.height,\n height: 0,\n };\n }\n if (expandMode === 4) {\n return { width: 0, height: currentRect?.width ?? 0 };\n }\n if (expandMode === 5) {\n return {\n width: 0,\n height:\n ((currentRect?.width ?? 0) * textureSize.height) / textureSize.width,\n };\n }\n return undefined;\n}\n\nfunction resourceSize(resource: unknown): Size | undefined {\n if (!resource || typeof resource !== \"object\") {\n return undefined;\n }\n const record = resource as Record<string, unknown>;\n const size = record.size;\n if (size && typeof size === \"object\" && !Array.isArray(size)) {\n const sizeRecord = size as Record<string, unknown>;\n const width =\n typeof sizeRecord.width === \"number\" ? sizeRecord.width : undefined;\n const height =\n typeof sizeRecord.height === \"number\" ? sizeRecord.height : undefined;\n return width !== undefined && height !== undefined\n ? { width, height }\n : undefined;\n }\n const width = typeof record.width === \"number\" ? record.width : undefined;\n const height = typeof record.height === \"number\" ? record.height : undefined;\n return width !== undefined && height !== undefined\n ? { width, height }\n : undefined;\n}\n\nexport function hasExpandFlag(\n indexed: IndexedNode,\n horizontal: boolean,\n): boolean {\n const flags =\n numeric(\n indexed.props,\n horizontal ? \"size_flags_horizontal\" : \"size_flags_vertical\",\n ) ?? 0;\n return (flags & 2) === 2;\n}\n\nexport function hasFillFlag(\n indexed: IndexedNode,\n horizontal: boolean,\n): boolean {\n const flags =\n numeric(\n indexed.props,\n horizontal ? \"size_flags_horizontal\" : \"size_flags_vertical\",\n ) ?? 0;\n return (flags & 1) === 1;\n}\n\nexport function alignmentOffset(\n indexed: IndexedNode,\n available: number,\n content: number,\n): number {\n const alignment = numeric(indexed.props, \"alignment\") ?? 0;\n if (alignment === 1) {\n return Math.max(0, (available - content) / 2);\n }\n if (alignment === 2) {\n return Math.max(0, available - content);\n }\n return 0;\n}\n","import { asNumber, asVector2, type GodotVariant } from \"@godot-scene-web/core\";\nimport {\n combinedMinimum,\n explicitSizeFromProps,\n sizeFromOffsets,\n} from \"./minimum-size\";\nimport type { GodotLayoutOptions, GodotRect, IndexedNode } from \"./types\";\n\nexport function rootRect(\n indexed: IndexedNode,\n viewport: GodotRect,\n byPath: Map<string, IndexedNode>,\n options: GodotLayoutOptions,\n): GodotRect {\n const minimum = combinedMinimum(indexed, byPath, options);\n const width =\n numeric(indexed.props, \"size_width\") ??\n sizeFromOffsets(indexed)?.width ??\n minimum?.width ??\n viewport.width;\n const height =\n numeric(indexed.props, \"size_height\") ??\n sizeFromOffsets(indexed)?.height ??\n minimum?.height ??\n viewport.height;\n return { x: viewport.x, y: viewport.y, width, height };\n}\n\nexport function controlRect(\n indexed: IndexedNode,\n parent: GodotRect,\n byPath: Map<string, IndexedNode>,\n options: GodotLayoutOptions,\n): GodotRect {\n const leftAnchor = numeric(indexed.props, \"anchor_left\") ?? 0;\n const topAnchor = numeric(indexed.props, \"anchor_top\") ?? 0;\n const rightAnchor = numeric(indexed.props, \"anchor_right\") ?? leftAnchor;\n const bottomAnchor = numeric(indexed.props, \"anchor_bottom\") ?? topAnchor;\n const position = asVector2(indexed.props.position);\n const leftOffset =\n numeric(indexed.props, \"offset_left\") ??\n numeric(indexed.props, \"position_x\") ??\n position?.x ??\n 0;\n const topOffset =\n numeric(indexed.props, \"offset_top\") ??\n numeric(indexed.props, \"position_y\") ??\n position?.y ??\n 0;\n const explicitSize = explicitSizeFromProps(indexed);\n // A missing right/bottom offset is Godot's default of 0 (the edge sits on its\n // anchor), not a copy of the start offset. Defaulting to the start offset would\n // give a spanning anchored node (anchor_right>anchor_left) zero size instead of\n // reaching the parent edge. An explicit `size` still derives the far offset.\n const rightOffset =\n numeric(indexed.props, \"offset_right\") ??\n (explicitSize ? leftOffset + explicitSize.width : 0);\n const bottomOffset =\n numeric(indexed.props, \"offset_bottom\") ??\n (explicitSize ? topOffset + explicitSize.height : 0);\n const left = parent.x + parent.width * leftAnchor + leftOffset;\n const top = parent.y + parent.height * topAnchor + topOffset;\n const right = parent.x + parent.width * rightAnchor + rightOffset;\n const bottom = parent.y + parent.height * bottomAnchor + bottomOffset;\n const rect = normalizeRect({\n x: left,\n y: top,\n width: right - left,\n height: bottom - top,\n });\n return growToMinimum(rect, indexed, byPath, options);\n}\n\n/**\n * Expands and repositions a rect when its combined minimum size exceeds the\n * offset-derived size, mirroring `Control::_size_changed`. The grow direction\n * (BEGIN shifts the start edge, BOTH centers, END only expands) applies\n * unconditionally, independent of anchors.\n */\nexport function growToMinimum(\n rect: GodotRect,\n indexed: IndexedNode,\n byPath: Map<string, IndexedNode>,\n options: GodotLayoutOptions,\n): GodotRect {\n const minimum = combinedMinimum(indexed, byPath, options, rect);\n if (!minimum) {\n return rect;\n }\n let { x, y, width, height } = rect;\n if (minimum.width > width) {\n const delta = minimum.width - width;\n const grow = numeric(indexed.props, \"grow_horizontal\") ?? 1;\n if (grow === 0) {\n x -= delta;\n } else if (grow === 2) {\n x -= delta / 2;\n }\n width = minimum.width;\n }\n if (minimum.height > height) {\n const delta = minimum.height - height;\n const grow = numeric(indexed.props, \"grow_vertical\") ?? 1;\n if (grow === 0) {\n y -= delta;\n } else if (grow === 2) {\n y -= delta / 2;\n }\n height = minimum.height;\n }\n return { x, y, width, height };\n}\n\nexport function numeric(\n props: Record<string, GodotVariant>,\n name: string,\n): number | undefined {\n return asNumber(props[name]);\n}\n\nexport function normalizeRect(rect: GodotRect): GodotRect {\n return {\n x: rect.x,\n y: rect.y,\n width: Math.max(0, rect.width),\n height: Math.max(0, rect.height),\n };\n}\n\n/**\n * Apply a node's own `scale` around `pivotOffset` to its layout rect, producing\n * the on-screen global rect. Mirrors the renderer's `transform: scale(...)` with\n * `transform-origin: <pivotOffset>` (see node-style.ts): the pivot is a fixed\n * point, so the top-left corner maps to `rect.pos + pivot·(1 − scale)` and the\n * size scales. Godot's `Control.get_global_rect()` includes scale the same way,\n * which is what the live-game layout-diff compares against. Identity scale\n * returns the rect unchanged.\n */\nexport function scaledRect(\n rect: GodotRect,\n scale: { x: number; y: number },\n pivotOffset: { x: number; y: number },\n): GodotRect {\n if (scale.x === 1 && scale.y === 1) {\n return rect;\n }\n return {\n x: rect.x + pivotOffset.x * (1 - scale.x),\n y: rect.y + pivotOffset.y * (1 - scale.y),\n width: rect.width * scale.x,\n height: rect.height * scale.y,\n };\n}\n\n/**\n * A 2×3 affine `(x,y) -> (a·x + c·y + tx, b·x + d·y + ty)` — the linear 2×2 part\n * `[[a,c],[b,d]]` plus a translation. Columns are the transformed basis axes:\n * column 0 `(a,b)` is the x-axis, column 1 `(c,d)` the y-axis. This represents a\n * node's full ancestor transform (scale AND rotation about a pivot) so a node's\n * `renderedRect` matches the live game's `get_global_rect()`, which includes every\n * ancestor's transform. A pure scale+translate is `b=c=0` (`a=sx`, `d=sy`).\n */\nexport interface RectAffine {\n a: number;\n b: number;\n c: number;\n d: number;\n tx: number;\n ty: number;\n}\n\nexport const IDENTITY_AFFINE: RectAffine = {\n a: 1,\n b: 0,\n c: 0,\n d: 1,\n tx: 0,\n ty: 0,\n};\n\n/** Compose two affines (2×3 matrix multiply) so the result maps `x -> outer(inner(x))`. */\nexport function composeAffine(\n outer: RectAffine,\n inner: RectAffine,\n): RectAffine {\n return {\n a: outer.a * inner.a + outer.c * inner.b,\n b: outer.b * inner.a + outer.d * inner.b,\n c: outer.a * inner.c + outer.c * inner.d,\n d: outer.b * inner.c + outer.d * inner.d,\n tx: outer.a * inner.tx + outer.c * inner.ty + outer.tx,\n ty: outer.b * inner.tx + outer.d * inner.ty + outer.ty,\n };\n}\n\n/**\n * Apply an affine to a rect, producing an axis-aligned `GodotRect` matching Godot\n * `get_global_rect()`: the top-left ORIGIN is transformed through the full\n * (rotation-bearing) matrix, while width/height take the *unrotated* extent — each\n * axis scaled by its column magnitude (`hypot`), NOT inflated to a rotated bounding\n * box. For a pure scale+translate (`b=c=0`) this reduces bit-for-bit to the old\n * `sx·x+tx` behavior.\n */\nexport function applyAffine(transform: RectAffine, rect: GodotRect): GodotRect {\n const { a, b, c, d, tx, ty } = transform;\n if (a === 1 && b === 0 && c === 0 && d === 1 && tx === 0 && ty === 0) {\n return rect;\n }\n return {\n x: a * rect.x + c * rect.y + tx,\n y: b * rect.x + d * rect.y + ty,\n width: rect.width * Math.hypot(a, b),\n height: rect.height * Math.hypot(c, d),\n };\n}\n\n/**\n * The scale+rotation transform a node applies to its own content and all its\n * descendants, in the global (unscaled-layout) coordinate space, pivoting about the\n * node's global pivot `rect.pos + pivotOffset`. The linear part is Godot's\n * `Transform2D(rotation, scale)` basis — x-axis `(cos·sx, sin·sx)`, y-axis\n * `(-sin·sy, cos·sy)` — i.e. rotate∘scale, which is what the live game composes.\n *\n * NOTE: `rotation` is in radians. Correct when the rotation pivot is the rect origin\n * (`pivotOffset` 0, the only case in the captured data and the case the CSS renderer\n * pivots at the origin for); a non-zero pivot combined with self-rotation would also\n * shift the node's own top-left, which `renderedRect` does not model.\n */\nexport function ownTransformAffine(\n rect: GodotRect,\n scale: { x: number; y: number },\n rotation: number,\n pivotOffset: { x: number; y: number },\n): RectAffine {\n const gx = rect.x + pivotOffset.x;\n const gy = rect.y + pivotOffset.y;\n const cos = Math.cos(rotation);\n const sin = Math.sin(rotation);\n const a = cos * scale.x;\n const b = sin * scale.x;\n const c = -sin * scale.y;\n const d = cos * scale.y;\n // Translate so the global pivot `g` stays fixed: t = g − M·g.\n return {\n a,\n b,\n c,\n d,\n tx: gx - (a * gx + c * gy),\n ty: gy - (b * gx + d * gy),\n };\n}\n\n/** Pure scale-about-pivot (no rotation) — `ownTransformAffine` with `rotation = 0`. */\nexport function ownScaleAffine(\n rect: GodotRect,\n scale: { x: number; y: number },\n pivotOffset: { x: number; y: number },\n): RectAffine {\n return ownTransformAffine(rect, scale, 0, pivotOffset);\n}\n","import { asNumber } from \"@godot-scene-web/core\";\nimport { deriveNodeVisuals } from \"@godot-scene-web/scene-graph\";\nimport {\n applyAffine,\n composeAffine,\n IDENTITY_AFFINE,\n ownTransformAffine,\n scaledRect,\n} from \"./rects\";\nimport type { GodotLayoutNode, GodotRect, IndexedNode } from \"./types\";\n\nexport function makeLayoutNode(\n indexed: IndexedNode,\n rect: GodotRect,\n parent?: GodotLayoutNode,\n): GodotLayoutNode {\n // The rect-free render fields (scale, pivot, z-index, alignment, visibility,\n // resource refs, …) are derived by the shared `deriveNodeVisuals` — the SAME\n // function the browser-native producer (`deriveSceneGraph`) uses, so computed\n // and browser modes can never drift on these. This node only adds the\n // rect-domain geometry on top.\n const base = deriveNodeVisuals(indexed, parent?.zIndex);\n // `rotation` (radians) ?? `rotation_degrees` (degrees) ?? 0 — matching the CSS\n // renderer's precedence (node-style.ts). Rotation propagates to DESCENDANTS via\n // `cumulativeTransform` but is deliberately left out of this node's OWN\n // `renderedRect` (see below).\n const rotationDegrees = asNumber(indexed.props.rotation_degrees);\n const rotation =\n asNumber(indexed.props.rotation) ??\n (rotationDegrees === undefined ? 0 : (rotationDegrees * Math.PI) / 180);\n // `renderedRect` must match the live game's `get_global_rect()`, which includes\n // every ancestor's transform. The layout positions nodes in unscaled space and\n // applies each node's transform as a CSS `transform` that cascades to its\n // descendants, so bake the parent's cumulative transform onto this node's own\n // scaled rect, and pass our own composed transform down to our children. The\n // node's OWN rotation is intentionally NOT applied to its own `renderedRect`\n // (rotation stays visual-only for the node itself — `get_global_rect()` reports\n // the unrotated origin/size when pivoting at the origin); it IS folded into\n // `cumulativeTransform` so descendant origins follow the rotated frame.\n const parentTransform = parent?.cumulativeTransform ?? IDENTITY_AFFINE;\n const renderedRect = applyAffine(\n parentTransform,\n scaledRect(rect, base.scale, base.pivotOffset),\n );\n const cumulativeTransform = composeAffine(\n parentTransform,\n ownTransformAffine(rect, base.scale, rotation, base.pivotOffset),\n );\n return { ...base, rect, renderedRect, cumulativeTransform };\n}\n","import { asNumber, type GodotVariant } from \"@godot-scene-web/core\";\nimport { flowWrapLines } from \"./flow-wrap\";\nimport { makeLayoutNode } from \"./layout-node\";\nimport {\n alignmentOffset,\n combinedMinimum,\n hasExpandFlag,\n hasFillFlag,\n preferredSize,\n TEXT_CONTENT_TYPES,\n visibleChildren,\n} from \"./minimum-size\";\nimport { normalizeRect, numeric } from \"./rects\";\nimport { contentRectForStyleBox, panelStyleBoxMetrics } from \"./style-box\";\nimport { themeNumber } from \"./theme\";\nimport type {\n GodotLayoutDiagnostic,\n GodotLayoutNode,\n GodotLayoutOptions,\n GodotRect,\n IndexedNode,\n} from \"./types\";\n\nexport {\n boxContainerHorizontal,\n flowContainerVertical,\n isBoxContainerType,\n isFlowContainerType,\n} from \"./container-types\";\n\nexport type LayoutChildDispatcher = (\n indexed: IndexedNode,\n rect: GodotRect,\n byPath: Map<string, IndexedNode>,\n layoutByPath: Map<string, GodotLayoutNode>,\n diagnostics: GodotLayoutDiagnostic[],\n options: GodotLayoutOptions,\n) => void;\n\nfunction aspectAlignmentFactor(value: GodotVariant | undefined): number {\n const alignment = asNumber(value) ?? 1;\n if (alignment === 0) {\n return 0;\n }\n if (alignment === 2) {\n return 1;\n }\n return 0.5;\n}\n\nexport function layoutBoxContainerChildren(\n parent: IndexedNode,\n rect: GodotRect,\n byPath: Map<string, IndexedNode>,\n layoutByPath: Map<string, GodotLayoutNode>,\n diagnostics: GodotLayoutDiagnostic[],\n options: GodotLayoutOptions,\n horizontal: boolean,\n layoutChildren: LayoutChildDispatcher,\n): void {\n const separation = themeNumber(parent, \"separation\", options) ?? 0;\n // Skip invisible children: a hidden control takes no space in a Godot container\n // (matching `visibleChildren` in minimum-size). Without this, an invisible\n // sibling would still consume main-axis extent and shift later children.\n const children = visibleChildren(parent, byPath);\n const sizes = children.map((child) => preferredSize(child, byPath, options));\n // Reflow text children to their laid-out column width. A wrapping `RichTextLabel`'s\n // height depends on its width; in the bottom-up minimum pass it reports width 0 (so\n // it never widens its container) and a single-line height. For a vertical box the\n // cross axis is the child's width, which the container fixes here — re-measure each\n // text child at that width so its height matches the wrapped paragraph (matching\n // Godot's `fit_content` reflow). Non-text children and horizontal boxes are untouched.\n if (!horizontal) {\n children.forEach((child, index) => {\n if (!TEXT_CONTENT_TYPES.has(child.node.type ?? \"\")) return;\n const cross = axisPlacement(\n rect.x,\n rect.width,\n sizes[index].width,\n child,\n true,\n );\n const reflowed = combinedMinimum(child, byPath, options, {\n x: 0,\n y: 0,\n width: cross.size,\n height: 0,\n });\n if (reflowed)\n sizes[index] = { width: sizes[index].width, height: reflowed.height };\n });\n }\n const totalMinimum =\n sizes.reduce(\n (sum, size) => sum + (horizontal ? size.width : size.height),\n 0,\n ) +\n Math.max(0, children.length - 1) * separation;\n const available = horizontal ? rect.width : rect.height;\n const remaining = Math.max(0, available - totalMinimum);\n const expandCount = children.filter((child) =>\n hasExpandFlag(child, horizontal),\n ).length;\n let cursor =\n (horizontal ? rect.x : rect.y) +\n (expandCount > 0 ? 0 : alignmentOffset(parent, available, totalMinimum));\n\n children.forEach((child, index) => {\n const minimum = sizes[index] ?? { width: 0, height: 0 };\n const extra =\n expandCount > 0 && hasExpandFlag(child, horizontal)\n ? remaining / expandCount\n : 0;\n const fillExtra = hasFillFlag(child, horizontal) ? extra : 0;\n const crossAxis = horizontal\n ? axisPlacement(rect.y, rect.height, minimum.height, child, false)\n : axisPlacement(rect.x, rect.width, minimum.width, child, true);\n const childRect = horizontal\n ? {\n y: crossAxis.position,\n x: cursor,\n width: minimum.width + fillExtra,\n height: crossAxis.size,\n }\n : {\n x: crossAxis.position,\n y: cursor,\n width: crossAxis.size,\n height: minimum.height + fillExtra,\n };\n cursor +=\n (horizontal ? minimum.width + extra : minimum.height + extra) +\n separation;\n const layout = makeLayoutNode(\n child,\n childRect,\n layoutByPath.get(parent.path),\n );\n layoutByPath.set(child.path, layout);\n layoutChildren(\n child,\n childRect,\n byPath,\n layoutByPath,\n diagnostics,\n options,\n );\n });\n}\n\nexport function layoutGridContainerChildren(\n parent: IndexedNode,\n rect: GodotRect,\n byPath: Map<string, IndexedNode>,\n layoutByPath: Map<string, GodotLayoutNode>,\n diagnostics: GodotLayoutDiagnostic[],\n options: GodotLayoutOptions,\n layoutChildren: LayoutChildDispatcher,\n): void {\n const columns = Math.max(\n 1,\n Math.floor(numeric(parent.props, \"columns\") ?? 1),\n );\n const hSeparation =\n themeNumber(parent, \"h_separation\", options) ??\n themeNumber(parent, \"separation\", options) ??\n 0;\n const vSeparation =\n themeNumber(parent, \"v_separation\", options) ??\n themeNumber(parent, \"separation\", options) ??\n 0;\n const children = visibleChildren(parent, byPath);\n const sizes = children.map((child) => preferredSize(child, byPath, options));\n const columnWidths = Array.from({ length: columns }, (_, column) =>\n Math.max(\n 0,\n ...sizes\n .filter((_, index) => index % columns === column)\n .map((size) => size.width),\n ),\n );\n const rowCount = Math.ceil(children.length / columns);\n const rowHeights = Array.from({ length: rowCount }, (_, row) =>\n Math.max(\n 0,\n ...sizes\n .slice(row * columns, row * columns + columns)\n .map((size) => size.height),\n ),\n );\n const parentLayout = layoutByPath.get(parent.path);\n\n children.forEach((child, index) => {\n const column = index % columns;\n const row = Math.floor(index / columns);\n const x =\n rect.x +\n columnWidths.slice(0, column).reduce((sum, width) => sum + width, 0) +\n column * hSeparation;\n const y =\n rect.y +\n rowHeights.slice(0, row).reduce((sum, height) => sum + height, 0) +\n row * vSeparation;\n const cell = {\n x,\n y,\n width: columnWidths[column] ?? 0,\n height: rowHeights[row] ?? 0,\n };\n const horizontalPlacement = axisPlacement(\n cell.x,\n cell.width,\n sizes[index]?.width ?? 0,\n child,\n true,\n );\n const verticalPlacement = axisPlacement(\n cell.y,\n cell.height,\n sizes[index]?.height ?? 0,\n child,\n false,\n );\n const childRect = {\n x: horizontalPlacement.position,\n y: verticalPlacement.position,\n width: horizontalPlacement.size,\n height: verticalPlacement.size,\n };\n layoutByPath.set(\n child.path,\n makeLayoutNode(child, childRect, parentLayout),\n );\n layoutChildren(\n child,\n childRect,\n byPath,\n layoutByPath,\n diagnostics,\n options,\n );\n });\n}\n\nexport function layoutFlowContainerChildren(\n parent: IndexedNode,\n rect: GodotRect,\n byPath: Map<string, IndexedNode>,\n layoutByPath: Map<string, GodotLayoutNode>,\n diagnostics: GodotLayoutDiagnostic[],\n options: GodotLayoutOptions,\n vertical: boolean,\n layoutChildren: LayoutChildDispatcher,\n): void {\n const hSeparation =\n themeNumber(parent, \"h_separation\", options) ??\n themeNumber(parent, \"separation\", options) ??\n 0;\n const vSeparation =\n themeNumber(parent, \"v_separation\", options) ??\n themeNumber(parent, \"separation\", options) ??\n 0;\n const entries = visibleChildren(parent, byPath).map((child) => ({\n item: child,\n size: preferredSize(child, byPath, options),\n }));\n const lines = flowWrapLines(\n rect,\n entries,\n vertical,\n hSeparation,\n vSeparation,\n );\n for (const line of lines) {\n const crossSize = line.reduce(\n (max, entry) =>\n Math.max(max, vertical ? entry.size.width : entry.size.height),\n 0,\n );\n for (const entry of line) {\n const childRect = vertical\n ? {\n x: entry.x,\n y: entry.y,\n width: crossSize,\n height: entry.size.height,\n }\n : {\n x: entry.x,\n y: entry.y,\n width: entry.size.width,\n height: crossSize,\n };\n layoutByPath.set(\n entry.item.path,\n makeLayoutNode(entry.item, childRect, layoutByPath.get(parent.path)),\n );\n layoutChildren(\n entry.item,\n childRect,\n byPath,\n layoutByPath,\n diagnostics,\n options,\n );\n }\n }\n}\n\nexport function layoutAspectRatioContainerChildren(\n parent: IndexedNode,\n rect: GodotRect,\n byPath: Map<string, IndexedNode>,\n layoutByPath: Map<string, GodotLayoutNode>,\n diagnostics: GodotLayoutDiagnostic[],\n options: GodotLayoutOptions,\n layoutChildren: LayoutChildDispatcher,\n): void {\n const ratio = numeric(parent.props, \"ratio\") ?? 1;\n const safeRatio = ratio === 0 ? 1 : ratio;\n const stretchMode = numeric(parent.props, \"stretch_mode\") ?? 2;\n const alignX = aspectAlignmentFactor(parent.props.alignment_horizontal);\n const alignY = aspectAlignmentFactor(parent.props.alignment_vertical);\n const parentLayout = layoutByPath.get(parent.path);\n\n for (const childPath of parent.children) {\n const child = byPath.get(childPath);\n if (!child) {\n continue;\n }\n const minimum = combinedMinimum(child, byPath, options) ?? {\n width: 0,\n height: 0,\n };\n const base = { width: safeRatio, height: 1 };\n let scaleFactor: number;\n if (stretchMode === 0) {\n scaleFactor = rect.width / base.width;\n } else if (stretchMode === 1) {\n scaleFactor = rect.height / base.height;\n } else if (stretchMode === 3) {\n scaleFactor = Math.max(\n rect.width / base.width,\n rect.height / base.height,\n );\n } else {\n scaleFactor = Math.min(\n rect.width / base.width,\n rect.height / base.height,\n );\n }\n const width = Math.max(minimum.width, base.width * scaleFactor);\n const height = Math.max(minimum.height, base.height * scaleFactor);\n const childRect = {\n x: rect.x + (rect.width - width) * alignX,\n y: rect.y + (rect.height - height) * alignY,\n width,\n height,\n };\n layoutByPath.set(\n child.path,\n makeLayoutNode(child, childRect, parentLayout),\n );\n layoutChildren(\n child,\n childRect,\n byPath,\n layoutByPath,\n diagnostics,\n options,\n );\n }\n}\n\nexport function layoutPanelContainerChildren(\n parent: IndexedNode,\n rect: GodotRect,\n byPath: Map<string, IndexedNode>,\n layoutByPath: Map<string, GodotLayoutNode>,\n diagnostics: GodotLayoutDiagnostic[],\n options: GodotLayoutOptions,\n layoutChildren: LayoutChildDispatcher,\n): void {\n const content = contentRectForStyleBox(\n rect,\n panelStyleBoxMetrics(parent, options),\n );\n layoutFitChildren(\n parent,\n content,\n byPath,\n layoutByPath,\n diagnostics,\n options,\n layoutChildren,\n );\n}\n\nexport function layoutScrollContainerChildren(\n parent: IndexedNode,\n rect: GodotRect,\n byPath: Map<string, IndexedNode>,\n layoutByPath: Map<string, GodotLayoutNode>,\n diagnostics: GodotLayoutDiagnostic[],\n options: GodotLayoutOptions,\n layoutChildren: LayoutChildDispatcher,\n): void {\n const content = contentRectForStyleBox(\n rect,\n panelStyleBoxMetrics(parent, options),\n );\n const scrollX = numeric(parent.props, \"scroll_horizontal\") ?? 0;\n const scrollY = numeric(parent.props, \"scroll_vertical\") ?? 0;\n const parentLayout = layoutByPath.get(parent.path);\n for (const childPath of parent.children) {\n const child = byPath.get(childPath);\n if (!child) {\n continue;\n }\n const size = preferredSize(child, byPath, options);\n const childRect = {\n x: content.x - scrollX,\n y: content.y - scrollY,\n width: hasExpandFlag(child, true)\n ? Math.max(content.width, size.width)\n : size.width,\n height: hasExpandFlag(child, false)\n ? Math.max(content.height, size.height)\n : size.height,\n };\n layoutByPath.set(\n child.path,\n makeLayoutNode(child, childRect, parentLayout),\n );\n layoutChildren(\n child,\n childRect,\n byPath,\n layoutByPath,\n diagnostics,\n options,\n );\n }\n}\n\nexport function layoutMarginChildren(\n parent: IndexedNode,\n rect: GodotRect,\n byPath: Map<string, IndexedNode>,\n layoutByPath: Map<string, GodotLayoutNode>,\n diagnostics: GodotLayoutDiagnostic[],\n options: GodotLayoutOptions,\n layoutChildren: LayoutChildDispatcher,\n): void {\n const left = themeNumber(parent, \"margin_left\", options) ?? 0;\n const top = themeNumber(parent, \"margin_top\", options) ?? 0;\n const right = themeNumber(parent, \"margin_right\", options) ?? 0;\n const bottom = themeNumber(parent, \"margin_bottom\", options) ?? 0;\n const content = normalizeRect({\n x: rect.x + left,\n y: rect.y + top,\n width: rect.width - left - right,\n height: rect.height - top - bottom,\n });\n // Godot's MarginContainer calls fit_child_in_rect on the content rect: managed\n // children fill it per-axis honoring size flags (FILL fills, SHRINK_* uses the\n // minimum at begin/center/end), rather than keeping their authored anchor/offset\n // size. layoutFitChildren is that exact logic (also used by PanelContainer).\n layoutFitChildren(\n parent,\n content,\n byPath,\n layoutByPath,\n diagnostics,\n options,\n layoutChildren,\n );\n}\n\nfunction layoutFitChildren(\n parent: IndexedNode,\n content: GodotRect,\n byPath: Map<string, IndexedNode>,\n layoutByPath: Map<string, GodotLayoutNode>,\n diagnostics: GodotLayoutDiagnostic[],\n options: GodotLayoutOptions,\n layoutChildren: LayoutChildDispatcher,\n): void {\n const parentLayout = layoutByPath.get(parent.path);\n for (const childPath of parent.children) {\n const child = byPath.get(childPath);\n if (!child) {\n continue;\n }\n const minimum = preferredSize(child, byPath, options);\n const horizontal = axisPlacement(\n content.x,\n content.width,\n minimum.width,\n child,\n true,\n );\n const vertical = axisPlacement(\n content.y,\n content.height,\n minimum.height,\n child,\n false,\n );\n const childRect = {\n x: horizontal.position,\n y: vertical.position,\n width: horizontal.size,\n height: vertical.size,\n };\n layoutByPath.set(\n child.path,\n makeLayoutNode(child, childRect, parentLayout),\n );\n layoutChildren(\n child,\n childRect,\n byPath,\n layoutByPath,\n diagnostics,\n options,\n );\n }\n}\n\nexport function layoutCenterChildren(\n parent: IndexedNode,\n rect: GodotRect,\n byPath: Map<string, IndexedNode>,\n layoutByPath: Map<string, GodotLayoutNode>,\n diagnostics: GodotLayoutDiagnostic[],\n options: GodotLayoutOptions,\n layoutChildren: LayoutChildDispatcher,\n): void {\n for (const childPath of parent.children) {\n const child = byPath.get(childPath);\n if (!child) {\n continue;\n }\n const size = preferredSize(child, byPath, options);\n const childRect = {\n x: rect.x + (rect.width - size.width) / 2,\n y: rect.y + (rect.height - size.height) / 2,\n width: size.width,\n height: size.height,\n };\n layoutByPath.set(\n childPath,\n makeLayoutNode(child, childRect, layoutByPath.get(parent.path)),\n );\n layoutChildren(\n child,\n childRect,\n byPath,\n layoutByPath,\n diagnostics,\n options,\n );\n }\n}\n\nfunction axisPlacement(\n start: number,\n available: number,\n minimum: number,\n indexed: IndexedNode,\n horizontalAxis: boolean,\n): { position: number; size: number } {\n const flags =\n numeric(\n indexed.props,\n horizontalAxis ? \"size_flags_horizontal\" : \"size_flags_vertical\",\n ) ?? 0;\n const shrinkCenter = (flags & 4) === 4;\n const shrinkEnd = (flags & 8) === 8;\n const size =\n shrinkCenter || shrinkEnd ? minimum : Math.max(available, minimum);\n const position = shrinkEnd\n ? start + Math.max(0, available - size)\n : shrinkCenter\n ? start + Math.max(0, (available - size) / 2)\n : start;\n return { position, size };\n}\n","import type { SceneGraph, SceneGraphNode } from \"@godot-scene-web/scene-graph\";\nimport { isBoxContainerType, isFlowContainerType } from \"./container-types\";\n\n/**\n * The nodes the rect cascade ({@link resolveGodotSceneTree}) would lay out, without\n * running it. The cascade drops exactly one class of node: an invisible child of a\n * Box/Grid/Flow container — and its whole subtree — because a hidden control takes\n * no space in those containers (`visibleChildren` in minimum-size.ts; every other\n * container type and plain parents lay out all children). Consumers that only need\n * per-node structural fields (`drawOrder`, `type`, `source`, `properties`) can use\n * this instead of the cascade and skip all rect math; `membership.test.ts` pins the\n * equivalence against the cascade.\n */\nexport function flattenSceneGraphNodes(graph: SceneGraph): SceneGraphNode[] {\n const byPath = new Map(graph.nodes.map((node) => [node.path, node]));\n const out: SceneGraphNode[] = [];\n const visit = (node: SceneGraphNode): void => {\n out.push(node);\n const skipsInvisibleChildren =\n isBoxContainerType(node.type) ||\n isFlowContainerType(node.type) ||\n node.type === \"GridContainer\";\n for (const childPath of node.children) {\n const child = byPath.get(childPath);\n if (!child) {\n continue;\n }\n if (skipsInvisibleChildren && !child.visible) {\n continue;\n }\n visit(child);\n }\n };\n for (const node of graph.nodes) {\n if (node.parentPath === null) {\n visit(node);\n }\n }\n return out;\n}\n","import type { SceneGraph, SceneGraphNode } from \"@godot-scene-web/scene-graph\";\nimport { resolveAnchors } from \"./anchors\";\nimport {\n boxContainerHorizontal,\n flowContainerVertical,\n isBoxContainerType,\n isFlowContainerType,\n layoutAspectRatioContainerChildren,\n layoutBoxContainerChildren,\n layoutCenterChildren,\n layoutFlowContainerChildren,\n layoutGridContainerChildren,\n layoutMarginChildren,\n layoutPanelContainerChildren,\n layoutScrollContainerChildren,\n} from \"./container-layout\";\nimport { makeLayoutNode } from \"./layout-node\";\nimport { controlRect, rootRect } from \"./rects\";\nimport type { GodotSceneTree } from \"./types\";\n\nexport type {\n GodotAnchorEdge,\n GodotAnchorHorizontalEdge,\n GodotAnchorVerticalEdge,\n} from \"./anchor-grammar\";\nexport { anchorEdgePoint, parseAnchorEdge } from \"./anchor-grammar\";\nexport { flattenSceneGraphNodes } from \"./membership\";\nexport type {\n GodotAnchorMap,\n GodotLayoutDiagnostic,\n GodotLayoutModel,\n GodotLayoutNode,\n GodotLayoutOptions,\n GodotNodeAnchor,\n GodotRect,\n GodotSceneTree,\n GodotSceneTreeDiagnostic,\n GodotSceneTreeNode,\n GodotTextRunMetric,\n} from \"./types\";\nexport function isGodotSceneTree(\n value: unknown,\n): value is import(\"./types\").GodotSceneTree {\n if (typeof value !== \"object\" || value === null) return false;\n const tree = value as {\n viewport?: unknown;\n nodes?: unknown;\n diagnostics?: unknown;\n resourceStatuses?: unknown;\n };\n return (\n isRectLike(tree.viewport) &&\n Array.isArray(tree.nodes) &&\n tree.nodes.every(isTreeNode) &&\n (tree.diagnostics === undefined || Array.isArray(tree.diagnostics)) &&\n (tree.resourceStatuses === undefined ||\n Array.isArray(tree.resourceStatuses))\n );\n}\nfunction isRectLike(value: unknown): value is import(\"./types\").GodotRect {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { x?: unknown }).x === \"number\" &&\n typeof (value as { y?: unknown }).y === \"number\" &&\n typeof (value as { width?: unknown }).width === \"number\" &&\n typeof (value as { height?: unknown }).height === \"number\"\n );\n}\nfunction isTreeNode(\n value: unknown,\n): value is import(\"./types\").GodotSceneTreeNode {\n if (typeof value !== \"object\" || value === null) return false;\n const node = value as Record<string, unknown>;\n return (\n typeof node.path === \"string\" &&\n typeof node.name === \"string\" &&\n typeof node.type === \"string\" &&\n (typeof node.parentPath === \"string\" || node.parentPath === null) &&\n Array.isArray(node.children) &&\n isRectLike(node.rect) &&\n typeof node.visible === \"boolean\" &&\n typeof node.zIndex === \"number\" &&\n typeof node.drawOrder === \"number\" &&\n typeof node.zAsRelative === \"boolean\" &&\n typeof node.showBehindParent === \"boolean\" &&\n typeof node.clipContents === \"boolean\" &&\n typeof node.properties === \"object\" &&\n node.properties !== null &&\n Array.isArray(node.resourceRefs)\n );\n}\n\nimport type {\n GodotLayoutDiagnostic,\n GodotLayoutNode,\n GodotLayoutOptions,\n GodotRect,\n IndexedNode,\n} from \"./types\";\n\nconst DEFAULT_VIEWPORT: GodotRect = { x: 0, y: 0, width: 1280, height: 720 };\n\n// Flow containers report their wrapped cross extent based on the width a parent\n// gives them, so a content-sizing ancestor can only converge by re-laying out\n// with the resolved width fed back (mirroring Godot's cross-frame `cached_size`).\n// Scenes without flow containers resolve in a single pass.\nconst MAX_LAYOUT_PASSES = 8;\n\n// Rebuild the rect engine's mutable working node from a shared `SceneGraphNode`.\n// The graph (from `deriveSceneGraph`) is the immutable structural + visual\n// derivation shared with the browser emitter; the cascade needs a per-call node\n// it can memoize minimum sizes / flow extents on, so it works on these\n// reconstructed copies. Field values are unchanged — only the carrier differs,\n// so the rect cascade and its goldens are untouched.\nfunction toIndexedNode(node: SceneGraphNode): IndexedNode {\n return {\n node: node.source ?? {\n name: node.name,\n type: node.type,\n attributes: {},\n properties: node.properties,\n },\n path: node.path,\n parentPath: node.parentPath,\n children: [...node.children],\n props: node.properties,\n order: node.drawOrder,\n };\n}\n\nexport function resolveGodotSceneTree(\n graph: SceneGraph,\n options: GodotLayoutOptions = {},\n): GodotSceneTree {\n const viewport = { ...DEFAULT_VIEWPORT, ...options.viewport };\n const indexed = graph.nodes.map(toIndexedNode);\n const byPath = new Map(indexed.map((node) => [node.path, node]));\n const roots = indexed.filter((node) => node.parentPath === null);\n const flows = indexed.filter((node) =>\n isFlowContainerType(node.node.type ?? \"Node\"),\n );\n\n let layoutByPath = new Map<string, GodotLayoutNode>();\n let diagnostics: GodotLayoutDiagnostic[] = [];\n for (let pass = 0; pass < MAX_LAYOUT_PASSES; pass++) {\n layoutByPath = new Map<string, GodotLayoutNode>();\n diagnostics = [];\n for (const node of indexed) {\n node.minimumSize = undefined;\n }\n for (const root of roots) {\n layoutNode(\n root.path,\n viewport,\n byPath,\n layoutByPath,\n diagnostics,\n options,\n );\n }\n if (flows.length === 0 || !updateFlowExtents(flows, layoutByPath)) {\n break;\n }\n if (pass === MAX_LAYOUT_PASSES - 1) {\n diagnostics.push({\n severity: \"warning\",\n code: \"flow-layout-unconverged\",\n message:\n \"Flow container layout did not converge within the pass limit; rects may be unsettled.\",\n });\n }\n }\n\n // Apply declarative anchors once layout (incl. flow wrapping) has settled, so a\n // node can be positioned against another node's final rendered/content edge.\n resolveAnchors(layoutByPath, options.anchorsByPath, diagnostics);\n\n return {\n viewport,\n nodes: [...layoutByPath.values()].sort(\n (left, right) =>\n left.zIndex - right.zIndex || left.drawOrder - right.drawOrder,\n ),\n diagnostics,\n resourceStatuses: graph.resourceStatuses,\n };\n}\n\n/**\n * Records each flow container's resolved main-axis extent for the next pass.\n * Returns true if any extent changed (i.e. another pass is warranted).\n */\nfunction updateFlowExtents(\n flows: IndexedNode[],\n layoutByPath: Map<string, GodotLayoutNode>,\n): boolean {\n let changed = false;\n for (const flow of flows) {\n const node = layoutByPath.get(flow.path);\n if (!node) {\n continue;\n }\n const extent = flowContainerVertical(flow)\n ? node.rect.height\n : node.rect.width;\n if (\n flow.flowMainExtent === undefined ||\n Math.abs(flow.flowMainExtent - extent) > 1e-6\n ) {\n flow.flowMainExtent = extent;\n changed = true;\n }\n }\n return changed;\n}\n\nfunction layoutNode(\n path: string,\n parentRect: GodotRect,\n byPath: Map<string, IndexedNode>,\n layoutByPath: Map<string, GodotLayoutNode>,\n diagnostics: GodotLayoutDiagnostic[],\n options: GodotLayoutOptions,\n): GodotLayoutNode | undefined {\n const indexed = byPath.get(path);\n if (!indexed) {\n return undefined;\n }\n if (layoutByPath.has(path)) {\n return layoutByPath.get(path);\n }\n const rect =\n indexed.parentPath === null\n ? rootRect(indexed, parentRect, byPath, options)\n : controlRect(indexed, parentRect, byPath, options);\n const parentLayout = indexed.parentPath\n ? layoutByPath.get(indexed.parentPath)\n : undefined;\n const computedNode = makeLayoutNode(indexed, rect, parentLayout);\n layoutByPath.set(path, computedNode);\n layoutNodeChildren(indexed, rect, byPath, layoutByPath, diagnostics, options);\n\n return computedNode;\n}\n\nfunction layoutNodeChildren(\n indexed: IndexedNode,\n rect: GodotRect,\n byPath: Map<string, IndexedNode>,\n layoutByPath: Map<string, GodotLayoutNode>,\n diagnostics: GodotLayoutDiagnostic[],\n options: GodotLayoutOptions,\n): void {\n const type = indexed.node.type ?? \"Node\";\n if (isBoxContainerType(type)) {\n layoutBoxContainerChildren(\n indexed,\n rect,\n byPath,\n layoutByPath,\n diagnostics,\n options,\n boxContainerHorizontal(indexed),\n layoutNodeChildren,\n );\n } else if (type === \"AspectRatioContainer\") {\n layoutAspectRatioContainerChildren(\n indexed,\n rect,\n byPath,\n layoutByPath,\n diagnostics,\n options,\n layoutNodeChildren,\n );\n } else if (type === \"GridContainer\") {\n layoutGridContainerChildren(\n indexed,\n rect,\n byPath,\n layoutByPath,\n diagnostics,\n options,\n layoutNodeChildren,\n );\n } else if (isFlowContainerType(type)) {\n layoutFlowContainerChildren(\n indexed,\n rect,\n byPath,\n layoutByPath,\n diagnostics,\n options,\n flowContainerVertical(indexed),\n layoutNodeChildren,\n );\n } else if (type === \"PanelContainer\") {\n layoutPanelContainerChildren(\n indexed,\n rect,\n byPath,\n layoutByPath,\n diagnostics,\n options,\n layoutNodeChildren,\n );\n } else if (type === \"ScrollContainer\") {\n layoutScrollContainerChildren(\n indexed,\n rect,\n byPath,\n layoutByPath,\n diagnostics,\n options,\n layoutNodeChildren,\n );\n } else if (type === \"MarginContainer\") {\n layoutMarginChildren(\n indexed,\n rect,\n byPath,\n layoutByPath,\n diagnostics,\n options,\n layoutNodeChildren,\n );\n } else if (type === \"CenterContainer\") {\n layoutCenterChildren(\n indexed,\n rect,\n byPath,\n layoutByPath,\n diagnostics,\n options,\n layoutNodeChildren,\n );\n } else {\n for (const childPath of indexed.children) {\n layoutNode(childPath, rect, byPath, layoutByPath, diagnostics, options);\n }\n }\n}\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@godot-scene-web/layout",
3
+ "version": "0.1.0",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "description": "Godot Control layout interpreter for web rendering.",
7
+ "publishConfig": {
8
+ "access": "public",
9
+ "registry": "https://registry.npmjs.org/"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/tfoxy/godot-scene-web.git"
14
+ },
15
+ "homepage": "https://github.com/tfoxy/godot-scene-web#readme",
16
+ "bugs": {
17
+ "url": "https://github.com/tfoxy/godot-scene-web/issues"
18
+ },
19
+ "sideEffects": false,
20
+ "exports": {
21
+ ".": {
22
+ "development": "./src/index.ts",
23
+ "types": "./dist/index.d.ts",
24
+ "import": "./dist/index.js"
25
+ },
26
+ "./anchors": {
27
+ "development": "./src/anchor-grammar.ts",
28
+ "types": "./dist/anchor-grammar.d.ts",
29
+ "import": "./dist/anchor-grammar.js"
30
+ }
31
+ },
32
+ "main": "./dist/index.js",
33
+ "types": "./dist/index.d.ts",
34
+ "files": [
35
+ "dist",
36
+ "LICENSE"
37
+ ],
38
+ "dependencies": {
39
+ "@godot-scene-web/core": "0.1.0",
40
+ "@godot-scene-web/scene-graph": "0.1.0"
41
+ },
42
+ "scripts": {
43
+ "build": "tsdown",
44
+ "typecheck": "tsc -p tsconfig.json --noEmit",
45
+ "test": "vitest run --root ../.. --config ../../vitest.config.ts packages/layout/test"
46
+ }
47
+ }