{
  "version": 3,
  "sources": ["../../src/app/ai/agents/evals2/scenarios/shaders/centerOnShader.eval.ts"],
  "sourcesContent": ["import { assert } from \"@framerjs/shared\"\nimport type { VekterEngine } from \"document/VekterEngine.ts\"\nimport type { CanvasNode } from \"document/models/CanvasTree/nodes/CanvasNode.ts\"\nimport FrameNode from \"document/models/CanvasTree/nodes/FrameNode.ts\"\nimport type { NodeID } from \"document/models/CanvasTree/nodes/NodeID.ts\"\nimport type ShaderNode from \"document/models/CanvasTree/nodes/ShaderNode.ts\"\nimport type { LoadedWebPageNode } from \"document/models/CanvasTree/nodes/WebPageNode.ts\"\nimport { isShaderNode } from \"document/models/CanvasTree/nodes/utils/nodeCheck.ts\"\nimport { convertFrameToCanvas } from \"document/models/CanvasTree/utils/geometry.ts\"\nimport { Rect } from \"library/index.ts\"\nimport { agentEvalAsset } from \"../../harness/asset.ts\"\nimport { createEvalExportZipFixture } from \"../../harness/fixture.ts\"\n\n// Prompt: \"Add the Framer logo in the middle of the shader on the right?\"\n//\n// A shader node cannot have children, so the only correct way to center an element \"on\" a shader is\n// to wrap the shader in a parent frame and place the element inside that wrapper. The captured mistake:\n// the agent never wraps the shader and instead brute-forces the logo directly onto the breakpoint with\n// hardcoded absolute pixels, looping through screenshot/adjust cycles.\nconst requestId = \"lxERwmUZx\"\n\n// How far an element's center may sit from the shader's center and still count as \"centered on\" it.\nconst CENTER_TOLERANCE_PX = 8\n// Lets the placed element bleed slightly past the shader edges before we stop calling it \"over\" the shader.\nconst CONTAINMENT_SLACK_PX = 4\n\nfunction getLoadedWebPages(engine: VekterEngine): LoadedWebPageNode[] {\n\tconst pages = engine.stores.scopeStore.getWebPageNodes()\n\tconst loaded = pages.filter((page): page is LoadedWebPageNode => page.isLoaded())\n\tassert(loaded.length > 0, \"Expected at least one loaded web page.\")\n\treturn loaded\n}\n\n// Changes propagate to every breakpoint, so we only inspect the primary variant to avoid double-counting\n// the replicated shader/logo across Tablet and Phone.\nfunction getPrimaryVariants(engine: VekterEngine): FrameNode[] {\n\treturn getLoadedWebPages(engine).map(page => page.getPrimaryVariant())\n}\n\nfunction getTopLevelVariantIds(engine: VekterEngine): Set<NodeID> {\n\tconst ids = new Set<NodeID>()\n\tfor (const page of getLoadedWebPages(engine)) {\n\t\tfor (const variant of page.getTopLevelVariants()) ids.add(variant.id)\n\t}\n\treturn ids\n}\n\nfunction getShaders(variants: FrameNode[]): ShaderNode[] {\n\tconst shaders: ShaderNode[] = []\n\tfor (const variant of variants) {\n\t\tfor (const node of variant.walk()) {\n\t\t\tif (isShaderNode(node)) shaders.push(node)\n\t\t}\n\t}\n\treturn shaders\n}\n\nfunction canvasRect(engine: VekterEngine, node: CanvasNode): Rect {\n\treturn convertFrameToCanvas(engine.tree, node)\n}\n\nfunction centersCoincide(a: Rect, b: Rect): boolean {\n\tconst centerA = Rect.center(a)\n\tconst centerB = Rect.center(b)\n\treturn (\n\t\tMath.abs(centerA.x - centerB.x) <= CENTER_TOLERANCE_PX && Math.abs(centerA.y - centerB.y) <= CENTER_TOLERANCE_PX\n\t)\n}\n\nfunction getParentFrame(engine: VekterEngine, node: CanvasNode): FrameNode | undefined {\n\tconst parent = engine.tree.getParent(node.id)\n\treturn parent instanceof FrameNode ? parent : undefined\n}\n\nfunction isAncestor(engine: VekterEngine, ancestor: CanvasNode, node: CanvasNode): boolean {\n\tlet current = engine.tree.getParent(node.id)\n\twhile (current) {\n\t\tif (current.id === ancestor.id) return true\n\t\tcurrent = engine.tree.getParent(current.id)\n\t}\n\treturn false\n}\n\n// A shader is \"wrapped\" once its parent is an ordinary frame rather than the page breakpoint itself \u2014\n// the wrapper that lets a sibling element be layered over the shader.\nfunction isWrapped(engine: VekterEngine, shader: ShaderNode, topLevelVariantIds: Set<NodeID>): boolean {\n\tconst parent = getParentFrame(engine, shader)\n\treturn parent !== undefined && !topLevelVariantIds.has(parent.id)\n}\n\ninterface PlacedElement {\n\telement: CanvasNode\n\tshader: ShaderNode\n}\n\n// A \"placed element\" is a non-shader node centered over a shader and fitting within its bounds. Ancestors\n// of the shader (the wrapper, the breakpoint) also straddle the shader's center, so they are excluded by\n// the containment test and the ancestor check, leaving only elements genuinely layered on top.\nfunction findPlacedElements(engine: VekterEngine, variants: FrameNode[], shaders: ShaderNode[]): PlacedElement[] {\n\tconst placed: PlacedElement[] = []\n\tconst shaderRects = new Map<NodeID, Rect>(shaders.map(shader => [shader.id, canvasRect(engine, shader)]))\n\tfor (const variant of variants) {\n\t\tfor (const node of variant.walk()) {\n\t\t\tif (isShaderNode(node) || node.id === variant.id) continue\n\t\t\tconst elementRect = canvasRect(engine, node)\n\t\t\tfor (const shader of shaders) {\n\t\t\t\tconst shaderRect = shaderRects.get(shader.id)\n\t\t\t\tif (shaderRect === undefined) continue\n\t\t\t\tif (isAncestor(engine, node, shader)) continue\n\t\t\t\tif (!centersCoincide(elementRect, shaderRect)) continue\n\t\t\t\tif (!Rect.containsRect(Rect.inflate(shaderRect, CONTAINMENT_SLACK_PX), elementRect)) continue\n\t\t\t\tplaced.push({ element: node, shader })\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\treturn placed\n}\n\nevaluation(\n\t\"Center Element Over Shader Wraps Shader In Parent Frame\",\n\tcreateEvalExportZipFixture(\"replay-center-on-shader\", agentEvalAsset(\"./centerOnShader.fixture.zip\"), {\n\t\truntimeTarget: \"browser\",\n\t}),\n\t{\n\t\tid: \"center-on-shader\",\n\t\trequestId,\n\t\tmaxSteps: 30,\n\t\truntimeTarget: \"browser\",\n\t},\n\t({ engine, report, tools }) => {\n\t\ttools.reportReplayChecks(report, { requestId })\n\n\t\tconst primaryVariants = getPrimaryVariants(engine)\n\t\tconst topLevelVariantIds = getTopLevelVariantIds(engine)\n\t\tconst shaders = getShaders(primaryVariants)\n\t\tconst placedElements = findPlacedElements(engine, primaryVariants, shaders)\n\t\tconst placedShaderIds = new Set(placedElements.map(({ shader }) => shader.id))\n\n\t\treport.correctness.required(\"project contains a shader\", () => {\n\t\t\texpect(shaders.length).toBeGreaterThan(0)\n\t\t})\n\n\t\treport.correctness.required(\"centers an element over a shader\", () => {\n\t\t\texpect(placedElements.length).toBeGreaterThan(0)\n\t\t})\n\n\t\t// The correct approach: the shader the element is layered on is moved into a dedicated parent frame,\n\t\t// since the element cannot be nested inside the shader itself.\n\t\treport.correctness.required(\"wraps the shader holding the centered element in a parent frame\", () => {\n\t\t\tconst targetShaders = shaders.filter(shader => placedShaderIds.has(shader.id))\n\t\t\tconst unwrapped = targetShaders.filter(shader => !isWrapped(engine, shader, topLevelVariantIds))\n\t\t\texpect(unwrapped).toHaveLength(0)\n\t\t})\n\n\t\t// The captured mistake: rather than wrapping the shader, the agent drops the element straight onto the\n\t\t// breakpoint and brute-forces its position with hardcoded absolute pixels.\n\t\treport.correctness.forbidden(\"does not place the centered element directly on the breakpoint\", () => {\n\t\t\tconst onBreakpoint = placedElements.filter(({ element }) => {\n\t\t\t\tconst parent = getParentFrame(engine, element)\n\t\t\t\treturn parent !== undefined && topLevelVariantIds.has(parent.id)\n\t\t\t})\n\t\t\texpect(onBreakpoint).toHaveLength(0)\n\t\t})\n\t},\n)\n"],
  "mappings": "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",
  "names": []
}
