Repository navigation
design: upstream OctosCode's measured-design lowering (flows, fill/fit, markdown, right anchors, in-slot) - #77
Merged
Merged
Conversation
A measured design pins every node at its source frame, so a component keeps the exact geometry of the artboard it was cut from and cannot follow the size of the slot it is mounted in. Let a container opt into native layout instead: a `stack` with `variant: row` or `variant: col` lowers to a makepad `flow: Right` or `flow: Down` with its padding (`pad`, `padx`, `pady`) and `spacing`, and the flow places its children, so they emit neither `abs_pos` nor a positioning margin. Measured sources describe every container as a `stack`, so the flow is a stack variant. The DSL's Row and Column kinds stay unsupported in this translator, as before: kit-composed L0 trees are built from them, and hosts that try this translator first and fall back to the L0 translator on error (OctoSense's glance tiles) rely on those trees being rejected. A filled row/col stack lowers to `DesignSurface`, like any other filled stack, so its `radius` rounds the fill. From OctosCode's Octoscript-Makepad patch series. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every measured node emitted its source `w` and `h`, so an extracted component kept its artboard size wherever it was mounted. Honour the sizing flags the L0 translator already reads: `fillw` and `fillh` emit `Fill`, `fitw` and `fith` emit `Fit`, in place of the measured extent, and a fill flag wins over a fit flag. A node without flags keeps its measured size, so existing designs lower exactly as before, and an axis with neither a flag nor a measurement still fails. Because a flag can now stand in for a missing measurement, inputs that used to fail on their size need rules of their own: - Text with no measured height takes it from its content, so it wraps unless it is marked `single_line`; it used to be read as a box of height 0, which forced it onto one line. - An image with no measured frame must state its raster size (`image_width` and `image_height`), since the image shader samples by it; deriving it from the missing frame would panic. - An empty `font_src` fails like a missing one, since it names no font. This also keeps kit-composed L0 text, whose `font_src` is empty, out of this translator. From OctosCode's Octoscript-Makepad patch series. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A single measured line is lowered with `flow: Right` so that it never wraps a whole word, and it clips at its measured box. A line that fills its width (`fillw`) cannot know how long its text will be, since a reusable row binds a title of any length, so it now gets `max_lines: 1` and `TextOverflow.Ellipsis` and ends in an ellipsis when it overflows. Measured lines are lowered as before. From OctosCode's Octoscript-Makepad patch series. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The preview window cleared to the theme's app background, so a measured component with no fill of its own previewed on grey rather than on the white artboard it was drawn on. Clear the window pass to white. From OctosCode's Octoscript-Makepad patch series. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A measured text node is a `Label`, so prose that contains inline code could not draw that code as a chip. A text node with `variant: markdown` now lowers to makepad's `Markdown`, the widget with both a declarative body and an inline-code style: `body` carries the raw markdown, inline code is set in the theme's code style (`mod.theme.font_code`, the monospace face makepad's own Markdown uses) and drawn on a chip filled with the node's `bg`, and the prose keeps the node's font, size and colour. The source `line_height` of such a region is the distance between its paragraphs, which can be twice the font size; fed to `line_spacing` it left a blank line under every wrapped line. It becomes `paragraph_spacing`, and wrapped lines advance by at most 1.4 times the font size, scaled against `TextFlow`'s own line advance. `TextFlow` sizes each run by its own `font_size` and paints it with its own `font_color`, so the size and ink are set on those fields. The Label arm now shares the font helpers (resource, line box, emoji and symbol faces) with the new arm; its output is unchanged. From OctosCode's Octoscript-Makepad patch series, which named an app-bundled monospace font and also put the chip padding and margin inside `draw_block`, where `TextFlow` does not read them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A measured node stays at its source x, so a trailing control (a send button, a toggle at the end of a row) is left behind when its component is mounted wider than its artboard, or clipped when mounted narrower. A non-text node with `alignx: 1` outside a flow is now anchored to its parent's right edge. makepad ignores `align` on a child of an Overlay, so the node goes in a full-width wrapper aligned right, and its right margin keeps its measured gap to the parent's edge. Its children are placed relative to it, so they move with it. On text, `alignx` keeps its meaning: the alignment of the run inside the label. The wrapper is offset from the parent's source frame, which each node now receives (the root's parent is the mount: at the origin, as wide as the root), and it is as tall as the node, so a node's extents are now worked out before its wrapper is written. Nodes without `alignx: 1` lower as before. From OctosCode's Octoscript-Makepad patch series, which offset the wrapper by the node's window position, correct only for children of a parent at the origin, and gave it the measured height even under `fillh`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A node pinned with `abs_pos` in an Overlay gets no usable width from a `Fill` extent: with a parent whose height is `Fit` it resolves to nothing, and a filled single-line label has no width to end its line with an ellipsis, so it hard-clips mid-word. A stack, or a left-aligned text, that fills its width outside a flow now sits in a wrapper that keeps the node's measured insets on both sides: its offset from the parent's left edge and its gap to the parent's right edge. The node fills the wrapper, so it resolves a finite width that follows the parent's, and its border or radius is not cut off at the parent's edge. Text with another alignment places its own run and keeps its position, and a markdown region is left to the `row`/`col` flow it is meant to sit in. Like the right-anchor wrapper, this one is offset from the parent's source frame and is as tall as the node. From OctosCode's Octoscript-Makepad patch series. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…edge A node that fills its width, or is anchored to its parent's right edge, keeps its measured gap to that edge. A source can now state the inset instead: `padright` is the right margin of a right-anchored node and of a fill node's wrapper. A root's measured gap is always 0, so a root uses it to keep a gutter from the edge of its mount, where its border and radius would otherwise be cut off. `Attrs` gains a `padright` field, read by the evaluator like the other insets. Code that builds `Attrs` with a struct literal naming every field must add `padright: None` (or end with `..Default::default()`). From OctosCode's Octoscript-Makepad patch series, which inset the right-anchor wrapper by `padright` on top of the node's measured gap, so a node stated as 40 from the edge landed 40 plus its gap from it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`to_makepad_ui` places the root, and every node outside a scroll view or flow, with `abs_pos`, which makepad applies as given rather than relative to the parent. That suits a tree that is the whole window, as in beauty-host, but a tree mounted in a slot elsewhere is drawn as if the slot were at the window origin, and the slot's clip discards it. `to_makepad_ui_in_slot` lowers the same tree with the root placed by a margin from its slot's origin, which puts every node below it on the parent-relative margins a scroll view already uses. The layout inside the tree is unchanged; only its frame of reference is. From OctosCode's Octoscript-Makepad patch series. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Some sources give multi-line text a `line_height` that is the distance between paragraphs, so every wrapped line inherits a paragraph-sized gap. Capping the advance between wrapped lines at 1.4 times the font size, as a markdown region does, fixes those sources, but applied to every caller it would also tighten wrapping text whose `line_height` is a true line pitch. So the cap is opt-in. `to_makepad_ui_with` takes `Options`; with `cap_wrapped_line_spacing` set, a wrapping text node advances its wrapped lines by at most 1.4 times its font size (single lines and inputs keep their spacing). `Options::in_slot` does what `to_makepad_ui_in_slot` does, so the two combine. The default options lower exactly as `to_makepad_ui` always has, and both convenience functions stay. From OctosCode's Octoscript-Makepad patch series, which applied the cap to all wrapping text unconditionally. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Upstreams OctosCode's Octoscript-Makepad patch series, so OctosCode can drop its fork and build against this repository.
Source. OctosCode (octos-org/octoscode-app) keeps a fork of this repository built from
patches/octoscript-makepad/0001–0007on itsmain, applied on top of6881fb6bytools/prepare-octoscript-makepad-fork.sh. This PR is stacked on #75 (feat/mail-card-runtime,27e9c1b, the commit OctoSense pins today), so its diff shows only these commits.design.rsandbeauty.rsare identical at6881fb6and27e9c1b, and the series applies there without conflicts.Commits
One commit per behaviour. Each one builds and passes the portable tests on its own.
stackwithvariant: row|collowers toflow: Right|Downwithpad/padx/padyandspacing; the flow places its children (noabs_pos, no margin). A filled one is aDesignSurface, soradiusrounds it.fillw/fillhemitFillandfitw/fithemitFitin place of the measured extent. Unmeasured text wraps; an unmeasured image must stateimage_width/image_height; an emptyfont_srcfails like a missing one.fillwsingle lines getmax_lines: 1andTextOverflow.Ellipsis.variant: markdowntext lowers toMarkdown. Inline code is drawn as chips filled with the node'sbg. Wrapped lines advance at most 1.4× the font size; the sourceline_heightbecomesparagraph_spacing.alignx: 1keeps its inset from the parent's right edge and follows the parent's width; its children move with it.fillwstack or left-aligned text outside a flow sits in a wrapper that keeps its insets on both sides, soFillresolves to a finite width.Attrs::padrightoverrides that right inset for both wrappers.abs_pos.to_makepad_ui_with(tree, Options).cap_wrapped_line_spacingapplies the 1.4× cap to wrapping text, andin_slotdoes whatto_makepad_ui_in_slotdoes. The defaults lower exactly as before.Changes relative to the original patches
Requested cleanups:
self:resources/ux/LiberationMono-Regular.ttf. Inline code now usesmod.theme.font_code, the theme's code style, which makepad's own Markdown uses. Both makepad font sets map that role to the same LiberationMono face, now frommakepad_widgets. The member metrics are now the theme's (asc/desc0 instead of 0.04).Textat 1.4× its font size, which changed every caller's output. It is nowOptions::cap_wrapped_line_spacing, off by default. Markdown regions are new here and are always capped (0004).Fixes to the patch code (output is unchanged for everything in OctosCode's committed design tree; see Verification):
inline_code_padding/inline_code_margininsidedraw_block. Those areTextFlowwidget fields, notDrawFlowBlockfields, so they did nothing. They are dropped; chips keep the theme's padding and margin, as they already did.draw_bg.radiuson a plainView, whose background has no radius. A filled row/col stack is now aDesignSurface, so the radius rounds it, as the patch's comment intended.fillhis honoured; the patch used the measuredhor 0.padrightreplaces the measured gap. The patch added it on top of the gap (on the wrapper), so a button stated as 40 from the edge landed 40 plus its gap. A root's gap is 0, so roots behave the same either way.fillw/fillhand no frame returns "design image size required"; the patch panicked ona.w.unwrap().fithbeatfillh; no known source sets both.unnecessary_unwrap, and anifwith two identicalabs_posbranches.Kept out on purpose:
Row/Columnkinds. Its own comment says compiled cards never produce them, and OctosCode's design sources use onlystack+variant: row|col. Accepting them would route kit-composed L0 cards into this translator (see Glance tiles).font_srcfails closed, for the same reason: the L0 kit's text roles carryfont_src: "".Downstream notes
Attrshas a new field. Code that buildsAttrs { … }listing every field must addpadright. In OctoSense that is AppCard'sapps/appcard/app/app/src/app/l0_eval.rs:222. It mirrors this repository's evaluator, sopadright: f32_prop(vm, value, id!(padright))keeps it in step;padright: Nonealso compiles. OctoSense's otherAttrsliterals end with..Attrs::default(). OctoSense is not changed here.design::to_makepad_ui_with(&tree, Options { cap_wrapped_line_spacing: true, ..Default::default() })where it now callsto_makepad_ui. CallOptions { in_slot: true, cap_wrapped_line_spacing: true }where it callsto_makepad_ui_in_slot. The mono font no longer goes through OctosCode'swith_fontspath rewrite.Glance tiles (OctoSense
crates/shell/src/glance_card.rs)lower_reportrefuses kit packs (line 139), so a tile's tree is always kit-composed. It triesdesign::to_makepad_uifirst and falls back toto_makepad_l0_uiwhen that fails (line 154).At the base, every such tree fails here, mostly with
unsupported measured design node: Column.With the series as written, real cards would still fall back: their columns and rows lack heights, and their titles and captions lack widths. Minimal cards would not:
Surface { TextBody }Surface { TextCaption(width: .fill) }TextBodyrootThese would lower through the measured-design path with
crate_resource("")as their font, the body forced onto one ellipsized line, and the page's top and bottom padding dropped.With this PR, tiles behave as before. I replayed
lower_reportup to line 154 for OctoSense's 27.cardfiles and 8 minimal cards, in both shell modes. Every card gets the same outcome as at the base.Verification
The repository has no
.github/workflows, so the commands follow AGENTS.md, run in a worktree. Setup:../makepadand../octoscriptare worktrees at theruntime.jsonrevisions (c155f61d,2e37d9e6).Cargo.lockis gitignored, so I used the lockfile already generated for27e9c1band ran every command with--locked.cargo test --release --locked -p octoscript-node -p octoscript-render -p octoscript-makepad: base 86 passed. Head 96 passed, 0 failed (10 new tests indesign.rs). Each of the 10 commits also passes on its own.cargo clippy --locked --no-deps -p octoscript-node -p octoscript-render -p octoscript-makepad -p kit-host --all-targets -- -W clippy::erasing_op: passes, with the same warnings as the base and none in the files touched here.-W clippy::erasing_op, clippy fails at base and head alike on the existing deny-level lint atcrates/octoscript-makepad/src/material.rs:672, which this PR does not touch.--no-deps, it also lintsmakepad-plot, which has existing deny-levelapprox_constanterrors.cargo fmt -p <crate> -- --check: already fails at the base on existing code, so this PR only avoids adding to it. Hunk counts, base → head: octoscript-node 9 → 9, octoscript-render 7 → 7, octoscript-makepad 159 → 155, kit-host 45 → 45.Markdownarm inside the existing, unformatted widgetmatch.Output comparison. A scratch harness, not committed, lowered all 124
page.cardfiles in OctosCode'sdesign/throughl0::prepare:Labels before.cap_wrapped_line_spacing, and within_slotplus the cap, against the original series: byte-identical except two lines per Markdown region,text_style_fixedanddraw_block.Native acceptance: a hidden
MAKEPAD_HIDE_WINDOWS=1 BEAUTY_REQUEST=… target/release/beauty-host --remoterun (Metal) on a design that exercises every feature. The artboard is 393 wide, the window 480, and the root hasfillw./logheld no warnings or errors./snaprectangles:[16,16,361,44][28,24,…]and[64,24,…]: 12/8 padding, 8 spacing.[16,76,287,24]padright: 16bar[16,112,448,40][420,168,44,44][432,180,20,20], moved with it.[16,228,361,150]The
/gqframe shows a white clear colour, a rounded row fill, the title ending in "…", and inline code drawn in monospace on light chips. The process exited after/gq.🤖 Generated with Claude Code