Skip to content

Agents cannot measure text: layout containers, auto-height, and window.bento.validate() #194

Description

@nyblnet

Split out of #160 (finding 7), reported by @thinkbig1979. The other eight
findings there were documentation gaps or silent bugs and are fixed in #193.
This one is a feature, and it is the most substantial thing in that report.

The problem, stated narrowly

It is not that Bento uses absolute pixel positioning. Absolute pixels are what
let the morph engine, the editor's drag handles and the renderer all work from
one representation, and that is a good trade — a layout engine in the middle
would mean the model no longer says where anything is, which is the property
morph depends on.

The problem is narrower: an agent cannot measure text. The rendered height
of a string, for a given font, size, weight and lineHeight, is unknowable
without a render. So every card, caption and two-line heading is a guess, and
overflow and collisions are downstream of that one gap.

In the authoring run behind #160, laying out a three-photo row meant computing
3*288 + 2*112 = 1088 by hand, then repeating equivalent arithmetic for a 2×2
card grid and a three-stat row. That was the largest single consumer of
authoring effort after sourcing images.

This matters more over time, not less. The document is the interchange unit for
AI authoring, and agents.md is a supported entry point — so the ergonomics of
generating a deck blind are a first-class concern, not a nicety.

Prevention

a. Layout containers resolved at author time. An element carries something
like layout: {type: "row"|"column"|"grid", gap, padding, cols} and its
children supply content plus an optional weight. The document resolves it to
concrete geometry when it loads and writes the pixels back.

The stored file therefore stays absolute-pixel: morph, the editor and the
renderer are untouched, and the declarative form is a pure authoring
convenience that disappears on first save. That is what makes this fit the
codebase rather than fight it. Every grid in the deck from #160 would collapse
to one declaration.

Open questions: whether the resolved geometry is written back into the model
(simple, but the layout key then describes something already true) or kept as
a live container the editor re-resolves on edit (more useful, much larger
change, and it puts a layout engine back in the middle). Format additivity
means old shells must ignore layout and still render the deck correctly —
which the write-back approach gets for free and the live-container approach
does not.

b. Auto-height text. h: "auto" on a text element, resolved to a concrete
number at render and stored back. This removes the guess for the element type
that causes nearly all of it, and is much smaller than (a).

c. A positioning table in the docs. Done in #193agents.md now carries
column arithmetic for 2, 3 and 4 columns and a 60/40 split on the 1280×720
canvas, with the gutters worked out.

Feedback: window.bento.validate()

window.bento already exposes doc, serialize(), loadDoc(), comments(),
updates and i18n. A validate() returning structured warnings would let any
agent close the loop on its own output:

  • unknown keys, with element id and path
  • text elements whose rendered content overflows their box
  • elements outside the canvas, or past the 96px margin
  • fx.enter on a morph-arrival slide (it will never run)
  • dash-march on a shape with no dashed stroke
  • chart option keys the renderer dropped

Note the fourth item is narrower than in the original report: fx.countUp on a
morph arrival now works for elements with no morph partner (#193), so only
fx.enter is still a dead end.

This is the cheapest item here and probably the highest leverage. Findings 1, 3
and 4 of #160 would each have taken seconds to spot instead of a runtime
teardown — and unlike (a) and (b) it changes no rendering behaviour and cannot
break a shipped file, because it only reads.

Suggested order

validate() first, then h: "auto", then layout containers if they still look
worth it. The first two are additive and independently useful; the third is a
design decision that deserves its own discussion before anyone writes code.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions