feat(text-editing): E6 — lifecycle + placeholder + auto-scroll + TextInput + closure (campaign complete)#67
Merged
Merged
Conversation
…Input + closure
Final phase of the text-editing campaign (E1–E6). Completes the F-tier editor
surface: focus lifecycle, placeholder rendering, auto-scroll, the host-facing
`EditSubmitted` Message, the `buiy_widgets::TextInput` widget bundle — and the
campaign closure (errata fold + spec status flip). Headless + one additive GPU
golden.
What lands:
- **Focus lifecycle** (`text/edit/lifecycle.rs`): a `Local`-based transition
detector. On blur, `focus_lifecycle` seals the open undo group and removes
the active preedit (the E5-deferred focus-loss removal, with the
`invalidate_intrinsics` + Taffy dirty-mark so the orphaned preedit glyphs
don't persist a frame), while RETAINING the selection (web parity). Caret
visibility is NOT written here — `write_caret_blink` is reworked into the
single focus-aware owner: an editor caret that is not the `FocusedEntity` is
forced steady-hidden (this also fixes the latent pre-E6 bug that a blurred
caret kept blinking); the bare-`CaretVisual`/no-focus-resource paths keep the
global phase, so the E3/E5 goldens are unaffected.
- **Placeholder** (`text/edit/placeholder.rs`): a separate display-only
`PlaceholderBuffer`, shaped under the `SharedFontSystem` lock by
`sync_placeholder` (it has no downstream shaper, unlike the editor buffer),
shown when `value().is_empty() && !has_preedit()` in the
`color.text.placeholder` token. It paints as a SEPARATE additive branch in
the glyph producer — the §3.2 extract tripwire stays on the editor buffer
only (the placeholder is not part of the editor's `ComputedTextLayout`).
- **Auto-scroll** (`text/edit/scroll.rs`): a pure `clamp_into_view` + a system
that pans the layout `ScrollOffset` (x single-line / y multi-line) to keep
the caret in the clip viewport after each move/edit; clamps against
`ResolvedLayout.size`.
- **`EditSubmitted`** Message (single-line Enter) — finalizing the § 11
taxonomy; an audit test pins all nine Messages registered.
- **`buiy_widgets::TextInput::{single_line,multi_line}`** — an `impl Bundle`
(the `Button::new` precedent) composing the core `TextEditState` (built via
the new `TextEditState::for_font_size`, which keeps `cosmic_text::Metrics`
out of `buiy_widgets`) + markers + focus; `focus_on_click` is widget-side
policy (core never auto-focuses).
Two majors caught in review + fixed before coding (the caret-visibility
ownership collision; the placeholder buffer being shaped by nobody / the §3.2
tripwire) and two more in-driver (the placeholder damage-gate; the GPU golden's
ink assertion).
Campaign closure (docs): the editing spec is flipped from design-only to
**implemented (editing, E1–E6)**; six "As landed (E_n)" errata folded
(E1 intrinsics-cache relocation, E4 empty-`Change` drop, E5 `ime_position`
is `Vec2`, E6 `Edges` are `Length` / `A11yRole::Text` / lock-free `set_text`);
the spec README, `docs/README.md`, and `follow-ups.md` (the named deferrals)
updated.
Gate: headless 1010 / 0 / 57; GPU lane (RX 6700 XT) 8 / 0 incl. the new
`placeholder_paints_when_empty_and_ink_when_typed` golden.
**This completes the text-editing campaign — the F-tier editor surface of
foundation text.md § 3.5 is delivered (E1–E6).**
Plan: docs/plans/2026-06-13-buiy-text-editing-e6-lifecycle-widget-closure.md
Co-Authored-By: Claude Fable 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.
Final phase of the text-editing campaign (E1–E6). Completes the F-tier editor surface and closes the campaign. Headless + one additive GPU golden.
What lands
text/edit/lifecycle.rs): aLocaltransition detector. On blur: seals the undo group + removes the active preedit (the E5-deferred removal, with the dirty-mark) + retains selection (web parity). Caret visibility is reworked —write_caret_blinkbecomes the single focus-aware owner (a non-FocusedEntityeditor caret is steady-hidden; also fixes a latent blurred-caret-blink bug); bare-CaretVisualpaths keep the global phase so the E3/E5 goldens are unaffected.text/edit/placeholder.rs): a separate display-onlyPlaceholderBuffer, shaped under the lock bysync_placeholder, shown whenvalue().is_empty() && !has_preedit(). Paints as a SEPARATE additive branch — the §3.2 extract tripwire stays on the editor buffer only.text/edit/scroll.rs): pansScrollOffsetto keep the caret in the clip viewport (x single-line / y multi-line), clamping againstResolvedLayout.size.EditSubmitted(single-line Enter) — finalizes the § 11 taxonomy; an audit test pins all nine Messages.buiy_widgets::TextInput::{single_line,multi_line}— animpl Bundle(theButton::newprecedent) overTextEditState(via the newcosmic-freefor_font_size) + markers + focus;focus_on_clickis widget-side.Two majors caught in review + fixed before coding (caret-visibility ownership; the placeholder being shaped by nobody / §3.2 tripwire), two more in-driver.
Campaign closure (docs)
The editing spec is flipped from design-only to implemented (editing, E1–E6); six "As landed (E_n)" errata folded (E1 intrinsics-cache, E4 empty-
Change, E5ime_positionVec2, E6EdgesLength /A11yRole::Text/ lock-freeset_text); spec README,docs/README.md, andfollow-ups.mdupdated.Gate
Headless 1010 / 0 / 57; GPU lane (RX 6700 XT) 8 / 0 incl. the new
placeholder_paints_when_empty_and_ink_when_typedgolden. fmt/clippy/doc clean.This PR completes the text-editing campaign — the F-tier editor surface of foundation text.md § 3.5 is delivered (E1–E6).
Plan:
docs/plans/2026-06-13-buiy-text-editing-e6-lifecycle-widget-closure.md· Campaign:docs/plans/2026-06-13-buiy-text-editing-campaign.md🤖 Generated with Claude Code