12 KiB
B4 Create capsule typography — read-only review, 2026-09-11
Codex-ytr83r / server_audit. Owned output: this report only. No production, compiler, native, Git or frozen-fixture changes. Gitea ownership #255; issue #2 and comments through #251 read. The user's #250 clarification permits sequence constraints; Codex-956atf retains the coherent canvas interaction design. This review concerns only B4 label containment and its existing hit shape.
Finding: fixed capsule does not bound its label
P2 presentation risk, confirmed missing containment; exact native overflow
extent unmeasured. behavior/scenario_lifecycle/render.rs:96 fixes the birth
capsule to 140×28 world units. At line 305 its label is drawn 8 units from the left
and 6 from the top, at 12 px, using short(&marker.name, 18) and otherwise default
canvas Text. The name is the local Lifeline name (line 100), not the Create
occurrence's message name.
views.rs:25 collapses whitespace and limits Unicode scalar count: 18 or fewer
scalars are returned intact; longer text gets 17 scalars plus an ellipsis. It
measures neither glyph advances nor grapheme clusters. Thus there is 124 units of
symmetrically padded width, or 132 to the outer right edge, but no corresponding
text bound. An 18-scalar label whose shaped advance exceeds 132 units can extend
beyond the capsule; wide Latin and CJK names are concrete candidate inputs. At
12 px, even an average advance over 7.33 units crosses that outer-edge threshold.
This is a threshold calculation, not a measured font result. Combining marks,
ligatures, fallback fonts and glyph overhang also make scalar count unsuitable
as an ink-bound guarantee. Splitting a grapheme is a secondary consequence of
the same truncation strategy, not a separately verified native defect.
Core name validation (oa-core/src/validation.rs:54, :65, element name check
at :275) permits these ordinary Unicode characters. There is no model rule
making a name fit a graphical capsule. Changing model names or source text to
solve display width would therefore be inappropriate.
Exact selection uses only marker.capsule.contains(point) at render.rs:183;
the nearby-marker distance also uses the same rectangle at :329.
behavior.rs:1735/:1792 combines these shapes with bounded nearest-stroke
tolerance. Text does not expand the marker hit shape. If visible text extends
more than the screen tolerance outside the capsule, clicking it can miss the
Create or select another nearby item. The capsule geometry and arrow endpoint
remain mutually aligned; the potential mismatch is overflowing label ink.
Installed Iced behavior, not an assumed browser text model
Desktop enables Iced 0.14 WGPU/canvas (crates/oa-desktop/Cargo.toml:14). Read the
actual registry source under
.local/toolchain/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/:
iced_graphics-0.14.0/src/geometry/text.rs: Text exposesmax_width, documented as line wrapping; its default is infinity.draw_withconstructs a paragraph with that width and infinite height. Default line height is 1.2×size.iced_wgpu-0.14.0/src/geometry.rs:306: ordinary positive uniform scale follows the cached-text path. It passes the text's width and infinite height, and clips toself.clip_bounds(line 361), not the previously filled rounded rectangle. Other transforms use glyph paths, also without an implicit capsule clip.iced_core-0.14.0/src/text.rs:134: shaping can use font fallback; default Auto selects based on ASCII/non-ASCII unless shaping features override it. Font defaults to SansSerif (font.rs:20). No capsule-specific font is supplied.iced_graphics-0.14.0/src/geometry/frame.rs:141provideswith_clip, backed by a fresh child frame (iced_wgpu ... geometry.rs:412). The child's transform must be handled deliberately: Scene already scales the outer frame atbehavior.rs:2057.
Therefore setting max_width = 124 alone is not a one-line ellipsis fix. It
can wrap into the fixed 28-unit capsule or below it; long unbroken words also
depend on wrapping behavior. Default text has no capsule-height bound.
Another scalar-count estimate, including the state-canvas approximation
(state_canvas.rs:298), cannot guarantee Unicode containment either.
Two bounded corrections
| Approach | Benefit | Cost / evidence needed |
|---|---|---|
| A — clip the existing one-line display to the capsule interior | Smallest robust containment change; keeps capsule size, arrow endpoints, original hit IDs and all lifetime/layout semantics. Keep the current shortened display and unbounded single-line shaping inside a finite clip. | May cut a glyph or hide an existing ellipsis. Full authored name remains a separate lifeline property. Clip must use the actual screen transform; a naive world-space with_clip inside the scaled parent can regress zoom. |
| B — fit a single-line, measured prefix plus ellipsis, with the same interior clip as a final bound | Gives a deliberate truncation signal and uses available width well for both narrow and wide names. Grapheme-safe prefix selection preserves user-visible characters. | Requires the same font/size/shaping measurement as drawing and bounded/cached work; a different platform text API is not authoritative. More implementation than A; wrapping or an average-character-width estimate is not an equivalent replacement. |
Recommend A as the smallest immediate B4 correction, with B only if root chooses readable ellipsis as part of this bounded polish and can use Iced's actual text measurement. Neither requires enlarging capsules or changing lane spacing. Automatic width expansion would affect endpoint/hit/layout contracts and overlap the separately owned constrained-canvas design.
Retain model/source names exactly. Verify screen-space clip placement at 0.5×, 1× and 2×, plus minimum Fit for hit identity. The WGPU child frame does not inherit the parent's current transform; pass/restore the explicit scale and clip bounds through the narrow drawing bridge or use an equivalent renderer-supported bounded primitive. This report does not supply an uncompiled implementation.
Separate pre-native variation and falsifier
Do not edit the six frozen source files or their baseline/OA-rename/SA-rename expectations. In a separate fresh typography checkpoint derived from baseline, prepare an exact expected Project/SourceBundle before native opening:
- Rename only OA root receiver Lifeline
5ceab400-0000-4000-8000-00000000044eto 18Wcharacters (WWWWWWWWWWWWWWWWWW). - Rename only SA root receiver Lifeline
5ceab400-0000-4000-8000-000000000836to 18界characters. - Preserve both Create occurrence IDs (
...0456,...083e), lane identities, all rows/scopes, the full remainder of the model and unchanged CRLF rationale bytes. Prepare a separate later variation with a combining/emoji cluster across the 17-scalar cutoff if truncation quality is being changed.
Ordinary core/source admission of those exact variations is a pre-native gate; none was compiled or generated by this reviewer. A unit-renderer geometry test can establish unchanged capsule/endpoint/hit IDs, but it cannot establish native glyph width or clipping. Use actual Iced text measurement or captured WGPU output for that claim.
At normal readable zoom, inspect each Create capsule with the actual runtime fonts and record screenshot/scale/DPI. Current-code falsifier: label ink crosses the right capsule border, and a point on that ink more than 8 screen px outside the capsule fails to select its Create (assuming no nearer overlapping item). A particular font keeping these names within bounds disproves that specific visual reproduction, not the missing containment in source; record the measured result rather than claiming overflow from character count alone.
Correction acceptance: all label ink stays inside the padded capsule, no wrapped second line appears, exact capsule and near-stroke selection still returns the original Create at all tested scales, and passive open/zoom/select/ Save leaves the whole prepared Project/SourceBundle unchanged. Native appearance, fallback coverage, antialiasing at minimum Fit and exact screenshot pixel extents remain pending. No additional model error, lifecycle semantic defect or general canvas redesign is asserted by this typography review.
Frozen implementation sketch and name access
This is an uncompiled recommendation, not a production change. For the current
Scene transform (positive uniform scale only, no camera translation), pass
self.scale from behavior.rs:2167 into draw_markers. Keep capsule fill/stroke
and existing hit geometry unchanged. Replace only its label draw with:
// scale is the same finite positive value used by Scene's outer Frame.
if scale.is_finite() && scale > 0.0 {
let inner = Rectangle::new(
marker.capsule.position() + iced::Vector::new(8.0, 4.0),
Size::new(124.0, 20.0),
);
let screen = Rectangle::new(
Point::new(inner.x * scale, inner.y * scale),
Size::new(inner.width * scale, inner.height * scale),
);
if let Some(clip) = screen.intersection(&Rectangle::with_size(frame.size())) {
frame.with_clip(clip, |child| {
// Child starts with identity transform. Coordinates stay absolute
// within this canvas; do not subtract the clip origin.
child.scale(scale);
child.fill_text(Text {
content: short(&marker.name, 18),
position: marker.capsule.position() + iced::Vector::new(8.0, 6.0),
size: 12.0.into(),
color,
..Default::default()
});
});
}
}
Rectangle::intersection(&Rectangle) exists in iced_core 0.14.0
rectangle.rs:199; Frame::size, with_clip and scale are existing APIs.
Intersecting the screen clip with the outer canvas size preserves the parent's
viewport bound. The current uniform scale takes WGPU's cached-text path; no glyph
mesh/background reordering is introduced by this sketch. Any future camera
translation needs the same translation in both screen clip and child transform,
under the separately owned canvas work. Do not widen the hit rectangle to hide
an overflow defect. Measured ellipsis remains optional and separate from this
minimum containment change.
The capsule intentionally selects the original Create occurrence, not the
Lifeline whose name it displays. Keep that identity unchanged. To inspect the
full Lifeline name, select its neutral lane header/guide, then Selection →
Properties. scenario/workspace.rs:256 displays p.named(app.selected) without
short, and :270 sends that exact ID to EditProperties; forms.rs:172 loads
the selected project record. Native acceptance should confirm the full name in
the Inspector and unchanged raw value after cancel. It must not expect clicking
the birth capsule to rename the Lifeline.
Add a separate combining/emoji display variation before native input, without
modifying frozen fixtures: a name beginning with 16 ASCII characters followed by
e plus U+0301, and another with a joined emoji sequence near the cutoff. Keep
their complete source strings as the oracle. Missing installed glyphs are a
recorded font-coverage limitation, not grounds to require another font; bounding
and identity checks still apply to the actual fallback/tofu output. Long CJK,
combining and emoji cases are proposed inputs, not tested screenshots.
Frozen immediately for the user's reboot. No remaining owned compiler/native helper exists; read-only file-inspection commands completed. Root owns evidence commit, later correction ratification and execution.