192 lines
12 KiB
Markdown
192 lines
12 KiB
Markdown
# 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 exposes `max_width`, documented
|
||
as line wrapping; its default is infinity. `draw_with` constructs 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
|
||
to `self.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:141` provides `with_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 at
|
||
`behavior.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:
|
||
|
||
1. Rename only OA root receiver Lifeline
|
||
`5ceab400-0000-4000-8000-00000000044e` to 18 `W` characters
|
||
(`WWWWWWWWWWWWWWWWWW`).
|
||
2. Rename only SA root receiver Lifeline
|
||
`5ceab400-0000-4000-8000-000000000836` to 18 `界` characters.
|
||
3. 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:
|
||
|
||
```rust
|
||
// 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.
|