{"slug": "my-ai-page-builder-couldn-t-see-its-own-pages-here-s-how-i-gave-it-eyes-and-free", "title": "My AI page builder couldn't see its own pages. Here's how I gave it eyes (and free GPT-6.1 Sol)", "summary": "A developer behind OpenConstructor, a free open-source AI page builder, added a post-render layout check that walks the rendered canvas, collects visible text, form controls and charts, and flags non-nested element pairs whose bounding boxes overlap by more than 25% of the smaller element's area, feeding the model one concrete fix request per round. The update also adds pre-generation web search for time-sensitive requests, a script-free CSS-only tab/accordion pattern built on :has() and <details>, and marker-based partial edits (<!-- part 3 -->, <!-- replace 5 -->) so large pages no longer exceed the 8,000-token answer limit.", "body_md": "[openconstructor](https://openconstructor.pw) is a free, open-source page builder. You describe a page, AI agents build it on a canvas, and you export plain HTML. This update came from watching it fail. A friend asked for \"a site about the current strongest LLMs with charts\", and the AI:\n\nEvery one of those has the same root cause: **the model writes blind**. It never sees the page, it never sees today's world, and it gets cut off when a page is long. Here's what I changed, with the code that does it.\n\n**TL;DR**\n\nThe model can't see that its chart sits on top of the table. The browser can. After each answer is drawn on the canvas, the studio walks the rendered page and collects everything you'd actually see: text, form controls, and charts (an `<svg>` with at least three marks). Then it compares every pair that isn't nested.\n\n``` js\nconst w = Math.min(a.box.right, b.box.right) - Math.max(a.box.left, b.box.left);\nconst h = Math.min(a.box.bottom, b.box.bottom) - Math.max(a.box.top, b.box.top);\nif (w > 2 && h > 2 && w * h >= Math.min(a.area, b.area) * 0.25) found.push(`${describe(a.el)} overlaps ${describe(b.el)}`);\n```\n\nIf anything is found, the model gets one concrete fix request. That means \"`<label.chk> “Google”` overlaps `<select.dev> “All models”`\", not \"please check the layout\". It gets one round only, so it can't loop.\n\nThe tricky part was false alarms. A giant headline with `line-height: .8` \"overlapped\" the nav under it, though the letters were nowhere near it. A text node's client rect is the font's whole content area, accent room and descender room included. Trimming each line box to roughly the letters fixed it:\n\n``` js\nconst top = q.top + fontSize * 0.16, bottom = q.bottom - fontSize * 0.2;\n```\n\nOverlaps that are part of the design pass the check: text on a photo, a collage of prints over a big word, the invisible radios behind custom tabs.\n\nModels confidently present what they remember as current. Mine *could* ask for a web search, but it almost never did. So now the studio searches **before** the model writes, whenever a request mentions news, prices, \"latest\", rankings, a year, or AI models:\n\nThat last rule matters more than it sounds. Before it, the model filled the \"score\" column with plausible numbers it made up. Now it leaves a dash.\n\nThe pages are HTML + CSS only. Scripts are off on the canvas and in exports, on purpose. So every \"All · Code · Math\" filter the AI drew was a picture of a button. Instead of letting it write JS, I gave it one markup pattern and add the CSS myself after the answer:\n\n```\n<div class=\"oc-tabs\">\n  <input type=\"radio\" name=\"lb\" id=\"lb-1\" checked><label for=\"lb-1\">All</label>\n  <input type=\"radio\" name=\"lb\" id=\"lb-2\"><label for=\"lb-2\">Code</label>\n  <div class=\"oc-panels\"><div>…overall…</div><div>…coding…</div></div>\n</div>\n.oc-tabs > .oc-panels > * { display: none }\n.oc-tabs:has(> input:nth-of-type(2):checked) > .oc-panels > :nth-child(2) { display: block }\n```\n\n`:has()` does the switching: one rule per tab, up to eight. Accordions are `<details>` and toggles are checkboxes. It works on the canvas, in exports and on published pages, and there's still no script.\n\nAsking for one more section on a 7-section page used to make the model return the *entire* page. That's the CSS of the design system plus every section, and it often ran past the 8,000-token answer limit and got cut off. When it did fit, it took over three minutes.\n\nNow a big page goes to the model with markers, `<!-- part 3 -->`, and the model answers with only the changes:\n\n``` php\n<!-- after 3 -->\n<section class=\"charts\">…</section>\n<!-- replace 5 -->\n<footer>…</footer>\n```\n\nA small parser applies them. If the answer gets cut off mid-block, the unfinished last block is dropped and the rest still applies. Edits are faster, and the parts nobody asked to change stay the same.\n\nTo push the look up, I studied a dozen recent Awwwards Sites of the Day. I didn't copy their code (most of it is WebGL anyway). I wrote down the *moves* they share:\n\nThese became eight art directions, each with rules and an example hero I wrote from scratch. The planner picks one for the business, and every section agent follows it.\n\nopenconstructor sends your key straight from your browser to the AI service. It never goes through my server. That makes CORS the entry test: if a service's API doesn't answer browser requests, it can't be added. I went through the free-LLM lists on GitHub and probed 26 APIs. Some highlights:\n\n| Works from the browser | Doesn't (no CORS) | \n|---|---|\n| OVHcloud (no key at all), LLM7 (no key), Pollinations, Vercel AI Gateway, Scaleway, Alibaba, DeepInfra, Fireworks, Nebius, Novita, Chutes | NVIDIA NIM, Kilo Code, Cloudflare, Ollama Cloud, SambaNova, GitHub Models | \n\nThe other half of the work was **honest \"$0\" labels**. Several services list free and paid models side by side in different formats: LLM7 has \"turbo\" vs \"pro\" tiers, Pollinations has a `paid_only` flag, and some report prices per token while others report them per million. If you get that wrong, someone picks a paid model marked free and hits \"insufficient balance\". Now only models that actually cost nothing get the $0 badge.\n\nThe nicest find: **[Pollinations](https://pollinations.ai)** gives a free key with a daily allowance, and a lot of strong models fall under it. GPT-6.1 Sol, GPT-6 Luna, Kimi K3.1 Pro and DeepSeek V4.1 Flash are all marked as not paid-only. Get a key at enter.pollinations.ai, paste it under ⚙ AI keys, and pick `openai/gpt-6.1-sol`.\n\nTwo caveats so nobody's surprised:\n\nFor free page-building it's a great option all the same.\n\nIf you want zero setup, OVHcloud and LLM7 work with no key or sign-up at all. They're rate-limited, but that's enough to try things.\n\n`AbortController` covers every request of the turn, including the parallel agents.`<img src=\"icon:trophy\">` or `<img src=\"logo:anthropic\">`, and the studio inlines the real SVG: about 1,500 Lucide icons, plus company logos from Simple Icons. No more empty squares where a logo should be.\nopenconstructor is free and open source, and it works without an account. Try it at **[openconstructor.pw](https://openconstructor.pw)** and grab the code on **[GitHub](https://github.com/Kuloka/openconstructor)**.\n\nIf you've solved \"the model can't see what it built\" differently, I'd love to hear how. The overlap check is crude, and I'm sure there are smarter ways.", "url": "https://wpnews.pro/news/my-ai-page-builder-couldn-t-see-its-own-pages-here-s-how-i-gave-it-eyes-and-free", "canonical_source": "https://dev.to/timofeus_248d45d3902a91a3/my-ai-page-builder-couldnt-see-its-own-pages-heres-how-i-gave-it-eyes-and-free-gpt-61-sol-16e7", "published_at": "2026-11-15 21:10:48+00:00", "updated_at": "2026-11-15 21:21:23.701134+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "generative-ai", "developer-tools"], "entities": ["OpenConstructor", "Awwwards"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/my-ai-page-builder-couldn-t-see-its-own-pages-here-s-how-i-gave-it-eyes-and-free", "markdown": "https://wpnews.pro/news/my-ai-page-builder-couldn-t-see-its-own-pages-here-s-how-i-gave-it-eyes-and-free.md", "text": "https://wpnews.pro/news/my-ai-page-builder-couldn-t-see-its-own-pages-here-s-how-i-gave-it-eyes-and-free.txt", "jsonld": "https://wpnews.pro/news/my-ai-page-builder-couldn-t-see-its-own-pages-here-s-how-i-gave-it-eyes-and-free.jsonld"}}