MoUI keeps text measurement in the platform-neutral runtime while letting
native and Web hosts install the renderer-backed text system that matches their
platform. The public boundary is @core.TextSystem; measurement-only legacy
shapes are no longer part of the framework contract.
Native Skia is the recommended native renderer/text route, Web uses browser
WebGPU plus browser text integration, and the native WGPU provider stack below
remains an explicit diagnostic route for comparing provider behavior.
The Sun CPU raster backend now exposes a renderer-backed TextSystem over
moui_sun/text, including font registration, fallback-face resolution,
single-line measurement adapted to core grapheme-stable caret arrays, and
paragraph geometry driven by the same Sun fallback line plan while preserving
the shared core paragraph result contract.
Sun resolves requested font families first, then appends the remaining
registered faces as a fallback chain, so mixed text such as Latin plus emoji can
measure and draw without missing-glyph diagnostics when registered font coverage
exists.
SunRasterRenderer::text_maturity_diagnostic() exposes a structured diagnostic
summary for the renderer-owned font state: registered font count, paragraph
line metrics, selection rectangles, hit testing, lightweight bidi visual-order
metadata, and explicit pending flags for native paragraph and color emoji
readiness.
SunRasterRenderer::text_selection_geometry_diagnostic() mirrors the same
paragraph contract for grapheme-normalized selection-rectangle and hit-test
audits and exposes visual-order observation fields from the Sun paragraph
metadata.
SunRasterRenderer::text_visual_order_diagnostic() mirrors Skia's dedicated
visual-order diagnostic shape for Sun by reporting logical/visual text,
cluster counts, segment count, glyph count, width, and bidi readiness from the
Sun paragraph metadata.
SunRasterRenderer::emoji_font_fallback_diagnostic() audits the registered-font
fallback chain for emoji-hint text: it reports requested family, registered font
and resolved-face counts, fallback spans, covered/missing scalar counts,
missing-glyph recovery readiness, grapheme cluster count, representative
fallback character, source/shaper labels, measured glyph extents, resolved
missing-glyph count, fallback language/script tags for emoji and non-ASCII
representative text, fallback-request readiness, resolved fallback face index
plus stable chain label, a stable coverage-mask glyph key, and
glyph_format=coverage-mask, while
keeping deterministic color emoji explicitly pending.
When no registered font face covers a draw, Sun still falls back to
renderer-local placeholder glyphs. Bidi, advanced shaping, and color emoji
claims are covered by the shared conformance and real Skia proof matrix rather
than a separate text-system blocker status.
core/text_layout.mbtdefinesTextSystem,TextSystem::fallback(),TextSystem::layout_paragraph(), and font data registration. The deterministic fallback keeps per-character caret arrays for stable geometry while a UAX #29-styleTextGraphemeBoundariesscanner folds cluster interiors back to the cluster start. It covers CRLF/control breaks, Hangul L/V/T sequences, Extend/ZWJ/SpacingMark, Prepend, regional-indicator pairs, emoji ZWJ sequences, emoji tags, ZWNJ as an Extend boundary, and Indic virama/linker conjuncts without swallowing virama followed by Latin text or whitespace. The paragraph contract returns line metrics, caret rectangles, selection rectangles, hit-test results, and visual-order metadata with explicit readiness flags, so simplified fallback or diagnostic renderer layout can expose geometry without claiming native SkParagraph or bidi parity. Core text editing uses the same cluster boundaries for basic left/right caret movement and shift-selection.AppRuntimeexposestext_system()andset_text_system()so hosts can install a platform text system before layout, painting, hit testing, selection, and IME anchor geometry are produced. The underlyingRuntimeStatestores the active system as an engine detail.FontSpeccarries a structured family stack. The default stack isSystemUi; concrete font names are resolved by the active native or Web provider unless app code requests a named family.core/owns only the neutral contract and deterministic fallback. It does not importMilky2018/moon_cosmic,Milky2018/moon_swash, CoreText, DirectWrite, fontconfig, HarfBuzz, FreeType, or browser APIs.
The live text path is:
View[Msg] -> DrawCommand::DrawText(TextRun) -> active TextSystem measurement -> renderer glyph path
Caret positions, selection geometry, wrapping, clipping, and IME request
coordinates should all use the same active TextSystem that layout uses.
Rich text runs may contain explicit newline characters; core splits those runs
into line fragments for drawing, hit testing, caret geometry, and document
height so renderer-backed text systems never need to interpret embedded
newlines inside a single draw command.
Focused text controls are queried by native and Web hosts outside ordinary text editing, including focus probing, IME candidate-anchor sync, surrounding-text updates, context menu state, and redraw-driven composition geometry. These queries must stay cheap for large documents.
For Markdown editing, moui_richtext keeps the canonical source inside
MarkdownDocumentSession and exposes a windowed editor path rather than a
formatter callback. controlled_markdown_session_editor returns the full
source to the host, but caret and selection geometry come from
MarkdownDocumentSession::rich_text_window for the current ScrollState,
viewport height, and overscan. Ordinary focused_text_input() calls should
use cached source length plus the session selection and should not construct
TextGraphemeBoundaries for the whole document.
Use the full grapheme-boundary scanner when an operation actually changes or
normalizes text: keyboard movement, selection normalization, delete-range
calculation, paste/input transforms, composition cursor offsets, or raw/UTF-8
offset conversion for IME handoff. Do not put whole-document grapheme scans,
full Markdown parsing, full RichTextDocument construction, or document-height
walks in paint, focus probing, scroll handling, or ordinary host IME polling.
The expected Markdown Editor scroll/input pipeline is:
ScrollState -> MarkdownDocumentSession height index -> visible block window
-> active TextSystem caret/hit-test geometry -> TextInputState / DrawCommand
Package tests can prove the structural contract, such as "scrolling does not reparse" and "scrolling marks paint dirty, not layout dirty". Claims about native IME anchoring, candidate-window placement, or visible scrolling smoothness still require the matching-host smoke evidence described below.
render/wgpu owns the native provider protocol, provider response validation,
fallback composition, glyph atlas upload, and renderer-side cache keys. It does
not depend on the standalone Cosmic provider package.
Provider packages are intentionally separate:
render/wgpu/cosmic_text/: Moon Cosmic provider used directly by examples that selectMoonCosmic, and as the fallback provider for platform defaults.render/wgpu/coretext/: macOS CoreText/CoreGraphics provider. macOS defaults to this provider composed with Cosmic fallback.render/wgpu/directwrite/: Windows DirectWrite scaffold. Windows defaults to this scaffold composed with Cosmic fallback until the real DirectWrite engine returns platform layout and raster data.render/wgpu/fontconfig/: Linux fontconfig/FreeType provider boundary. Linux defaults to this provider composed with Cosmic fallback. The provider can return native color-emoji glyphs from Noto Color Emoji for explicit emoji family runs when FreeType and the font are available; broader shaping, measurement, and non-emoji raster data still fall back to Cosmic until the full fontconfig/HarfBuzz path is implemented.render/wgpu/text_protocol/: shared native-stub payload protocol for UTF-32 input, versionedFontSpecencoding, measure/run/raster envelopes, and embedded-font registration payloads.
Native WGPU text-engine selection belongs to the WGPU provider packages, not to
the platform host cores. Use
MacosWgpuAppOptions::new(text_engine=...),
WindowsWgpuAppOptions::new(text_engine=...), or
LinuxWgpuAppOptions::new(text_engine=...) through
backend/<platform>/wgpu.run_app_with_options. PlatformDefault composes the
platform provider with Cosmic fallback; MoonCosmic selects the Cosmic provider
directly. Showcase also has explicit macos_wgpu_cosmic, windows_wgpu_cosmic, and
linux_wgpu_cosmic entrypoints for comparing those paths. The separate Showcase and
Markdown Editor *_skia entrypoints select Skia provider packages, not WGPU
text-provider variants. By default, Skia basic text
measurement and drawing
resolve the MoUI FontSpec family stack, weight, and style through moui_skia
FontMgr and Font. The system FontMgr path now builds a FontFallbackRequest
with a representative coverage character, preferring emoji hints, then
non-ASCII code points, then the first code point before falling back to regular
family matching. It also attaches inferred BCP47 script language tags for
emoji, CJK, kana, Hangul, Hebrew, Arabic, Devanagari, Thai, Lao, Sinhala,
Khmer, Myanmar, and Latin/default text so native FontMgr
match_fallback_request can use language-aware fallback metadata before
family matching. Its TextSystem returns Skia font-metric baseline/height
plus caret positions for basic input geometry. When
moui_skia/native is linked with SkShaper support, the Skia text system maps
shaped-run source clusters back to MoUI's per-character caret array; otherwise
it falls back to Skia-measured prefix carets. Both caret paths apply
representative combining-mark, Indic matra/virama, Arabic mark, Thai mark,
Lao mark, Sinhala mark, Khmer vowel/coeng, Myanmar mark, Hangul Jamo L/V/T
clusters, keycap,
emoji-modifier, variation-selector,
regional-indicator pairs, emoji tag-sequence flags, Unicode prepend marks, and
ZWJ cluster interior stabilization. For the system FontMgr route, single-line
TextRun rendering now consumes the same shaped glyph payload that measurement,
layout width, and caret mapping use. Native drawing first replays that payload
through the checked moui_skia/native glyph-run draw contract; if SkShaper or
the glyph payload is unavailable, it falls back to draw_text_utf8 so text
remains visible. The mixed-run fallback segmenter follows the same
Indic virama/linker boundary, including virama plus Extend marks before an
Indic consonant, so per-run font fallback does not split that edit cluster.
Skia segmentation and fallback caret stabilization now route through core
TextGraphemeBoundaries directly, with Skia white-box tests comparing cluster
slices and IME-facing nearest_boundary_utf8_offset conversion against the
core scanner so renderer text smoke, editor movement, selection geometry, hit
testing, and host IME requests share one boundary source.
Linux Skia resolves its default FontMgr through fontconfig/FreeType
when those Skia port headers are available, with a directory font manager
fallback over common system font directories. If the selected Skia font produces
a blank or incomplete glyph run, the renderer retries measurement and drawing;
the system FontMgr path first tries platform emoji font candidates for
emoji-hint text and then falls back to Skia's default font. Complete fallback
runs are accepted immediately, while partial default-font fallback is accepted
only when it reduces missing glyph ids or recovers visible text from a blank
primary run, so startup Showcase text remains visible and text-field caret
positions stay aligned with the drawn glyphs without silently treating an
equally incomplete fallback run as recovered. Skia drawing aligns the selected
glyph run inside TextRun.frame and
clips the canvas to that same frame before rasterization, so bounded text
controls use the same platform-neutral frame for measurement, caret geometry,
and native raster output. skia_text_system() also inherits the core paragraph
layout contract. In fallback-safe builds it can still produce wrapped line
metrics, paragraph caret rectangles, selection rectangles, and hit-test results
from the existing Skia measurement path while keeping native_paragraph_ready
and bidi_visual_order_ready false. When skia_paragraph_available() is true, the
Skia text system routes paragraph layout through the native Paragraph
wrapper, consumes SkParagraph line metrics, selection boxes, and hit-test
offsets through the binding, and sets readiness metadata only when line
metrics, caret geometry, selection rectangles, hit testing, and
mixed-direction visual-order metadata are all valid. The default
selection-box policy is the SkParagraph equivalent of
RectHeightStyle::kMax plus RectWidthStyle::kTight. Invalid or unavailable
SkParagraph geometry falls back to the existing core paragraph result without
promoting those readiness flags. The native binding also exposes optional
SkParagraph paint for paragraph-route smoke coverage; MoUI's single-line
TextRun main path still uses the shaped glyph/TextBlob-equivalent glyph-run
route rather than SkParagraph paint. The macOS, Windows, and Linux Skia providers default
to the system FontMgr path, so normal native Skia entrypoints exercise
platform font lookup, emoji retry, and optional SkShaper when linked. The
renderer package also exposes skia_text_system() directly so the Skia
measurement path can be included in native diagnostic text conformance without
requiring a platform window or treating provider checks as runtime observation.
Those diagnostics assert monotonic caret coverage and max-width clamping for
mixed CJK, emoji, ZWJ emoji, Indic mark, Arabic mark, Thai mark, Lao mark,
Sinhala mark, Khmer vowel/coeng, Myanmar mark, Hangul Jamo, and bidi samples,
and they now
inject skia_text_system() into a public AppRuntime text field to prove the
Skia measurement path drives focused text-input composition caret geometry and
selection highlight drawing. Fallback-safe diagnostics do not claim
native-platform IME runtime behavior or native SkParagraph runtime parity; the
text maturity preflight now marks bidi reordering and paragraph line breaking
ready, while only matching-host real Skia SkParagraph smoke logs may claim
native paragraph runtime observation. macOS tester-owned first-frame smoke entrypoints still explicitly
select SkiaFontResolution::EmptyTypeface; that keeps CLI smoke runs on the
safer default-font retry path without changing the normal app default. The fallback-safe Skia
renderer tests also consume the
new moui_skia FontFallbackRequest, TextMeasurementDescriptor,
TextShapingDescriptor, ShapedTextRunDescriptor, and
ShapedGlyphRunDescriptor resource plans through an internal descriptor
preflight so font fallback and shaped-run cache keys stay auditable without
requiring real Skia linkage. backend_info() also reports a fallback-safe
text maturity audit ready or text maturity audit pending summary: it counts the audited descriptor,
fallback-request, representative shaped/fallback caret, emoji-hint, and
empty-typeface retry boundaries, missing-glyph recovery rule, and the Skia
mixed-run fallback segment path together with Unicode 17 grapheme, bidi,
paragraph, and deterministic color emoji readiness. The paragraph API is now present for line metrics
and geometry, and the optional SkParagraph route is wired through the native
binding. The text maturity preflight now marks bidi reordering and paragraph
line breaking ready; matching-host macOS, Windows, and Linux Skia mainline smoke
logs provide the real SkParagraph observation. Deterministic color emoji is
reported through runtime glyph format metadata, while future Unicode grapheme
data refreshes and broader typography benchmarks are conformance maintenance.
Renderer text/emoji smoke now has a stronger audit boundary:
colorEmojiPixels must carry high-saturation glyph/raster observation plus
font-metadata and glyph-metadata tokens. The native Skia
text/emoji smoke records the requested emoji family, Skia text-system id,
shaper path, RGBA glyph format, cluster count, pixel counts, stable glyph key,
measured glyph size, inferred fallback script/language-tag counts, fallback
request character, resolved missing-glyph count, and
emoji-hint/fallback-request/missing-glyph-recovery status through
skia_emoji_font_fallback_diagnostic(); it also records paragraph
selection rectangles, line-range geometry, hit-test diagnostics, and visual
bidi-order diagnostics through skia_text_selection_geometry_diagnostic() /
skia_text_visual_order_diagnostic(). Passing native smoke now requires the
SkParagraph markers engine=skparagraph native_paragraph_ready=true line-metrics later-line-pixels for paragraphWrapping,
engine=skparagraph bidi_visual_order_ready=true visual-order for
bidiLayout, and engine=skparagraph selection-rects line-range rect-geometry hit-test for
selectionRects; fallback geometry, caret-only diagnostics, and heuristic
visual-order logs are not enough for the smoke marker.
For native Skia, colorEmojiPixels also requires fallback-request,
emoji-hint, and stable-glyph-key smoke tokens so high-saturation pixels are
tied to the FontMgr fallback request and glyph metadata path rather than a
generic raster-only observation. The Skia emoji/font fallback diagnostic payload
also records the inferred fallback language tags, primary script tag, request
language count, fallback request character, and missing-glyph recovery audit
fields used by that request.
WebGPU wasm records the browser canvas font stack plus glyph atlas key and size
metadata. Renderer smoke also reserves separate
marker keys for selectionRects, graphemeEditing, imeCandidateAnchor,
and imeCompositionVisual; those keys must be backed by selection rectangle,
line-range, grapheme-boundary, edit-action, candidate-anchor, surrounding-text,
composition-range, and preedit-pixel observation before text or IME readiness is
mentioned in release notes. Native Skia imeCandidateAnchor also requires utf8-offsets
observation for grapheme-normalized cursor and anchor offsets, and native Skia
imeCompositionVisual also requires composition-cursor observation.
The native Skia text/emoji smoke records graphemeEditing,
imeCandidateAnchor, and imeCompositionVisual from the shared
TextGraphemeBoundaries, host IME diagnostics including UTF-8 cursor and anchor
offsets plus composition cursor geometry, Skia text-system geometry, and
captured text-field composition pixels.
These are renderer/text-system smoke markers, not matching-host native IME
runtime observation.
Platform runtime observation splits native IME readiness further: native
status=passed entries must also record imeSurroundingText,
imeCommitDelete, imeCursorUpdate, imeScrollAnchor,
imeScaleDprAnchor, and imeResizeAnchor observations from matching-host
Showcase artifacts. Those logs must carry
matching-host runtime, native-app, renderer=skia, the matching app marker,
platform-protocol, candidate-window, surrounding-text, composition-visual,
commit/delete, cursor-update, scroll, scale/DPR, and resize markers, so
package-only composition tests and coarse textInput
observations cannot promote native IME readiness by themselves.
These fields make CI artifacts easier to audit, but they are not yet a
guarantee of exact cross-platform typeface identity, glyph-id determinism,
native IME behavior, or full Unicode bidi layout parity.
Native provider responses must report valid metrics, monotonic caret positions covering the input text, and raster glyph payloads whose cache keys include all raster-affecting inputs such as glyph identity, font size, style, weight, and scale. Invalid layout or raster data is rejected at the renderer boundary and falls back when a fallback engine was supplied. Run-layout responses may omit carets, but any non-empty caret array must be monotonic and cover the input text before glyphs are accepted for atlas upload.
The Cosmic provider loads platform emoji fallback font candidates when they are available. For provider-fragile emoji diagnostics, it safe-maps representative single-codepoint, variation-selector, and ZWJ samples to equal-length layout text before shaping, so measurement and native run-layout responses keep monotonic caret coverage without claiming full emoji shaping parity.
The Web path stays on wasm-gc + window/web + browser WebGPU host imports.
backend/web installs a browser Canvas-backed TextSystem and the WebGPU
runtime draws text through a DPR-aware canvas-rasterized glyph atlas. Measurement
and drawing use the same CSS system-ui stack generated from FontSpec.
The Web host also routes browser IME composition events and accepts
TextInputSession IME requests for focused text controls, including cursor
geometry and surrounding-text updates. This covers browser input-method
plumbing; broader shaping, bidi, and deterministic browser glyph rasterization
remain tracked conformance topics rather than Web-host capability claims.
The browser runtime assets live under backend/web/*.js; Web example packages
should only supply app-specific entrypoints, wasm URLs, canvas hosts, and UI
callbacks.
Apps can register embedded font bytes through AppRuntime::register_font_data.
Native WGPU forwards those bytes to the active provider and, when present, the
composed fallback provider. CoreText v1 attempts process-local registration
under the requested family alias. Future DirectWrite and
fontconfig/HarfBuzz/FreeType implementations should use the same hook for
private font collections.
Remote font loading is intentionally outside the current backend contract.
Proof Status
Text shaping and emoji text are declared supported in renderer-capability-report.md.
Their proof boundary is:
- L1 proof:
pr-profilejob covers grapheme break, caret stabilization, UAX#29 fixtures, emoji cluster detection, deterministic measurement, and runtime glyph format checks. - L2 proof:
macos-real-skia/linux-real-skia/windows-real-skiarun--run-text-emoji-smokeon every PR, covering SkShaper/SkParagraph smoke markers, bidi Arabic and mixed-direction visual-order markers, keycap/ regional-indicator/skin-tone-modifier fallback diagnostics, and deterministic color glyph format metadata. - Maintenance: future Unicode grapheme data refreshes require regenerating property predicates and fixtures.
Native SkParagraph / Bidi Readiness
Native Skia paragraph layout and bidi visual-order promotion use the SkParagraph implementation path when MOUI_SKIA_ENABLE_SKPARAGRAPH=1 and skia_paragraph_available() are true. The release claim is backed by macOS, Windows, and Linux real Skia smoke logs with SkParagraph line metrics, later-line pixels, selection rectangles, line ranges, hit tests, and mixed-direction visual-order observation.
The text maturity preflight marks bidi reordering and paragraph line breaking ready for the core grapheme boundary contract; matching-host smoke logs provide the required SkParagraph markers.
Core Grapheme Boundary Contract
Core now exposes TextGraphemeBoundaries as the single UAX-style cluster-boundary contract used by fallback caret stabilization, left/right caret movement, selection/range normalization, surrounding delete ranges, composition cursor offsets, rich text hit testing, raw UTF-8 offset conversion, and nearest_boundary_utf8_offset conversion for later IME handoff. This keeps deterministic text-field, selection, and IME-anchor geometry on one path.
The repo now has an offline GraphemeBreakTest.txt-style fixture and generator guard (scripts/generate-grapheme-break-fixtures.mjs --check) for curated samples plus a vendored Unicode 17.0 default grapheme break fixture generated from moui/core/testdata/GraphemeBreakTest-17.0.0.txt. moon test moui/core --target native runs the curated fixture, full boundary fixture, and a full editing fixture that checks is_boundary, floor/ceil/nearest boundary snapping, collapsed and expanded range normalization, surrounding delete ranges, raw boundary-to-UTF-8 offset conversion, and every-index nearest_boundary_utf8_offset snapping across every Unicode 17 sample. A separate full layout fixture checks fallback paragraph caret rectangles, collapsed selection rectangles, and hit-test offsets snap to the same Unicode 17 boundaries. moui/render/skia also generates a Skia white-box fixture from the same file so moon test moui/render/skia --target native verifies skia_grapheme_cluster_texts against the Unicode 17 default break samples and checks every-index nearest_boundary_utf8_offset snapping against the Skia-produced cluster boundaries.
The full Unicode fixtures use distinct generated helper/test names and are checked with the commands documented in the original fixture generation section (see the file history for the exact generator invocations).
The scanner now drives core grapheme break classes from generated Unicode 17.0 property predicates for CR/LF/control, Prepend, Extend, SpacingMark, Regional_Indicator, ZWJ, Extended_Pictographic, and Indic_Conjunct_Break Linker/Consonant/Extend. Regenerate those predicates with node scripts/generate-grapheme-property-data.mjs --grapheme-property <Unicode-17.0.0-GraphemeBreakProperty.txt> --emoji-data <Unicode-17.0.0-emoji-data.txt> --derived-core-properties <Unicode-17.0.0-DerivedCoreProperties.txt> --check after downloading the pinned Unicode files from the official Unicode 17.0.0 UCD URLs. It treats ZWNJ as an Extend code point for GB9, applies the Unicode 17 Indic_Conjunct_Break linker rule instead of the earlier hand-written virama shortcut, and keeps deletion and IME offsets aligned with the full default grapheme break fixture.
Native IME Runtime Readiness
Native IME runtime readiness is platform-scoped: macOS has recorded matching-host Markdown Editor/AppKit artifacts for candidate anchors, surrounding text, composition visuals, commit/delete behavior, cursor updates, scroll anchors, scale/DPR anchors, resize anchors, and Markdown Editor IME dogfood, so the macOS platform observation entry can set those observations to yes. Linux IME protocol functionality was verified on 2026-07-11 via WSL2 (WSLg Wayland): check_moui_linux_smoke.sh IME probe passed all 8 fields (enabled/hint/surrounding/cursor/updated/updated_hint/updated_cursor/disabled all true). Full interactive input evidence (pointer/keyboard and destroy sequence) still requires matching Wayland desktop host (Ubuntu 24.04+) Showcase or Markdown Editor runtime logs. Windows still requires equivalent matching MSVC host runtime logs before its native IME runtime path can be called ready.
Native WGPU Provider Status
Native WGPU can preserve RGBA color glyph payloads through the provider protocol and glyph atlas path, with Cosmic platform emoji fallback candidate loading, Cosmic color swash preservation, provider-safe emoji layout mapping, and a CoreText AppleColorEmoji RGBA path covered by focused tests. Stable and diagnostic tests assert caret counts, monotonicity, clamping, editor selection behavior, IME anchor geometry, core fallback cluster stabilization and movement, CRLF/control segmentation, emoji ZWJ restrictions, regional-indicator pairing, Indic conjuncts, and provider fallback safety across mixed bidi, CJK, single-codepoint emoji, variation-selector emoji, and ZWJ emoji samples; Cosmic run-layout tests additionally assert glyph output plus caret coverage through the safe-mapped layout path. Skia renderer tests cover the same representative emoji caret coverage, shaped-run and fallback caret stabilization for combining-mark, Indic matra/virama, Arabic mark, Thai mark, Lao mark, Sinhala mark, Khmer vowel/coeng, Myanmar mark, Hangul Jamo L/V/T clusters, emoji-modifier, variation-selector, regional-indicator pairs, emoji tag-sequence flags, Unicode prepend marks, and ZWJ cluster interiors, the system-FontMgr-only emoji font retry boundary, and fallback-safe descriptor resource plans for text measurement and shaping. Native diagnostic conformance also injects the Skia text system into a text field runtime to validate composition caret geometry and selection highlight drawing.
Platform Text Provider Status
Linux native Skia is the Preview Ready text/render route and continues to use the Skia text system described above. The Linux native WGPU fontconfig/FreeType provider remains diagnostic, with explicit FreeType color-emoji handling and composed Cosmic fallback for general text. Windows DirectWrite remains a diagnostic provider route with Cosmic fallback.
Web can surface browser emoji and font fallback behavior, while stable Web adapter tests keep the host-backed TextSystem contract deterministic.
Documentation Sync Requirement
Text changes that affect renderer feature status must update render/capabilities.mbt, render/capabilities_test.mbt, and docs/renderer-capability-report.md.
- Focused text inputs expose MoUI's default copy, cut, paste, undo, redo, and select-all commands through host context menus, so keyboard shortcuts and native menu selections share the same selection, clipboard, and Unicode paste dispatch path.
- Linux native Skia is the Preview Ready text/render route and continues to use the Skia text system described above. The Linux native WGPU fontconfig/FreeType provider remains diagnostic, with explicit FreeType color-emoji handling and composed Cosmic fallback for general text. Windows DirectWrite remains a diagnostic provider route with Cosmic fallback.
- Web can surface browser emoji and font fallback behavior, while stable Web
adapter tests keep the host-backed
TextSystemcontract deterministic. - Text changes that affect renderer feature status must update
render/capabilities.mbt,render/capabilities_test.mbt, anddocs/renderer-capability-report.md.
Text conformance is split into two layers:
- Stable tests run inside normal package checks and cover
core,render/wgpu,render/wgpu/cosmic_text,render/webgpu_adapter, andbackend/web. - Diagnostic matrix tests live under
moui/tests/text_conformance/and are opt-in. They compare core fallback, Cosmic, platform-default composed fallback, malformed-provider fallback, and Web text systems where the current host can actually exercise them. Strict failures stay limited to contract invariants; width/baseline differences across engines are diagnostic unless the contract says otherwise.
Focused checks for text-system work:
sh scripts/check.sh --profile full
moon test moui/tests/text_conformance/native --target native
moon test moui/core --target native
moon test moui/render/wgpu --target native
moon test moui/render/wgpu/cosmic_text --target native
moon test moui/render/skia --target native
moon test moui/render/webgpu_adapter --target wasm-gc
moon test moui/backend/web --target wasm-gcPlatform text provider changes should also run the affected WGPU provider
package tests. Public API changes require moon info and review of generated
pkg.generated.mbti diffs.