OS/2.sTypoAscender from the font binary, read as the face loads. Every path that
builds a font face records it, so a font arrives measured the same way whether it came with a
document or from the font library.fontBoundingBoxAscent is not merely the wrong one of the two — the browser also rounds it to a
whole pixel, so a single font reports a different ratio at different point sizes. Nothing that
needs to agree with InDesign should be derived from it.set_available_modes() decides
whether the mode control is drawn at all — its default, ['editing'], draws nothing — and
set_comment_source() decides whether any comment interface exists. An embedder that calls
neither gets the previous bundle's behaviour exactly.set_available_modes() takes { render: false } for a host that draws the mode control itself,
and set_comments_open() / get_comments_open() / toggle_comments() drive the rail from
outside. typograph_comments_toggled reports the rail's state and its thread count so a host
button can follow it. An embedder that passes neither keeps the widget-drawn control.'viewing' is a third mode, for a host whose user may only read. It mutes the canvas and hides
the authoring surface without offering any comment interface. The parameter bar and the add-page
tile are hidden by container rather than by key, because both rebuild themselves on selection and
would otherwise reappear. An empty mode list still means "editing", so nothing changes for an
embedder that passes one.typograph-comments class on each of its three roots, and every
rule in the stylesheet is written through it. A host that resets colour across the editor — the
Typograph portal does, with main.main > .content * { color: inherit }, to keep its own theme
out — outranks a single-class rule, and the panel rendered with black buttons and black pin
initials until the widget's own rules were given the higher specificity they need.select_comment(id) no longer needs the threads to have arrived. An id the widget does not know
yet is remembered and opened when the first list() resolves, which is what a deep link from a
notification needs — a host timeout raced the network.canvas_view on an animation frame, because canvas fires no event when
the view moves. Zoom, scroll, pan and a window resize all move the pins and none of them announce
themselves. The loop compares the view against the last one it drew and writes nothing when they
match, so an idle editor does no layout work.docs/API-EditorUI.md for the full surface: set_available_modes, set_mode, get_mode,
set_comment_source, set_current_user, refresh_comments, select_comment, and the
typograph_mode_changed / typograph_comment_* events.