# Rendering and host API

Projection layers, component mounting, languages, search and preview controls.

Signatures come from public package exports. Descriptions are checked against current JSDoc; identifiers retain their original names.

## Source reveal

`readSourcePresentation(state)` returns `mode`, `selection`, `reveal`, `composing` and `selecting`.
The presentation selection stays stable during a pointer gesture and can differ from live
`state.selection`. `reveal` is `selection` (the ordinary MSFE default) or `hidden`, which keeps
recognized syntax projected while editing. Built-in `ProjectionContext.active` already accounts
for this policy. Prefer `active` in extensions instead of revealing syntax from the live caret.
Source mode always displays the original text.

`sourceRevealSetting` is a public `PresentationSetting<SourceRevealPolicy>`. Install
`sourceRevealSetting.extension(() => 'hidden')` through `addCodeMirrorExtensions()`, then call
`sourceRevealSetting.set(editor, 'selection')`. `read(state)` returns the applied value and
`requested(state)` returns the latest request; both return `undefined` when uninstalled.
The setter returns `MsfeResult`: `not-installed` for a missing setting, `invalid-change` for an
invalid value, and rejection for destroyed or unmounted instances. Requests do not change source
or permission and defer during pointer selection and composition. Product consumers should use
[MD Editor's view](/en/guide/md-editor#four-views).

Replacement ranges participate in atomic navigation under `hidden`, preventing caret stops inside
invisible syntax. Unknown or incomplete input must remain source text; do not hide unsupported
text with CSS.

`sourceRevealResolver.of(resolveOwner)` installs an optional CodeMirror facet for the host.
`resolveOwner(state, range)` returns the canonical source range containing the original fragment,
so inline syntax can share a paragraph, list-item or cell owner. Bounds must be integer document
positions; invalid ownership throws `invalid-source-reveal-owner`. Without a resolver, each node
retains its own reveal range. Query only local syntax ancestry; do not serialize the document,
change selection or dispatch. MD Editor installs this policy for custom nodes, normalized
replacements and measured component geometry alike.

Built-in projections keep using `ProjectionContext.active`. Independent CodeMirror plugins can
call `isSourceRevealed(state, { from, to })` to apply the same mode, hidden preference and stable
pointer selection. `isSourceActive(selection, from, to)` only tests intersection. A replacement's
`revealRange` identifies its complete fragment before host ownership is resolved;
`interaction: 'atomic'` retains its explicit atomic behavior.

## Optional input proxy

`sourceInputProxy({ mediaQuery, modes?, revealMargin?, onSelectionTap? })` returns an installable CodeMirror extension, enabled
by default only in live mode when the owner-window media condition matches. MD Editor already
installs it for narrow touch devices; hosts must not install a second copy. It reuses `@quirekit/ime`
and mounts a textarea in the current Document/ShadowRoot input layer, outside rendered content.
The document, logical selection, source rules and history remain in the original CM state.
Cancellation preserves the selected source; confirmation commits once. Permission loss,
document replacement and disposal release the native host.
Optional `onSelectionTap(view, point)` returns true when the host handled a tap in an existing
selection; the proxy neither moves the caret nor focuses again. Touch end and its compatibility
click are accepted once. MD Editor uses this to toggle selection actions. Native Selection may not
own noneditable source while the proxy is active; independent embedded controls retain their ranges.
`revealMargin` reserves additional client CSS space when revealing the caret, defaulting to 8px.
MD Editor uses 32px for the painted grip and its touch target. It changes neither source position nor document layout.

`editor.focus()` selects the current input owner. Custom CM controls should use
`focusSourceView(view)`, `sourceInputHasFocus(view)` and `sourceInputElement(view)` rather than
assuming `view.hasFocus` or `contentDOM` represents mobile keyboard focus.
`sourceInputFocusChanged(update)` reports native and proxy logical focus changes.
During measurement, `sourceInputCaret(view)` returns a preedit tail rectangle in client CSS
coordinates, or null without preedit, when callers can use `coordsAtPos`. Do not focus or clear input every frame.

`sourceInputPosition(view, { x, y })` accepts a client CSS hit, skips atomic interiors and zero-height
source rows, and snaps a gap to one of its nearest visible source boundaries. It returns null when
no mounted caret can be proven; retain the previous endpoint instead of accepting a hidden `posAtCoords` hit.
`sourceCaretRect(view, pos, side?)` returns only a mounted caret with a real line box. It tries the
other association when the preferred fence/heading boundary is invisible, with one ordinary read
or at most two boundary reads, returning null when geometry cannot be proven.

During measurement, `sourceViewport(view)` intersects VisualViewport offsets, clipping ancestors and
CM scrollMargins without counting the keyboard and dock twice. `sourceScrollStep(view, delta)` measures
one vertical step in the nearest scrollable ancestor, handing off to the page at its limit;
`applySourceScrollStep(step)` performs that measured write without changing source or selection.
Consumers own a single drag frame and re-hit the latest endpoint, stopping on release, cancellation,
permission/mode changes and disposal. Accepted proxy taps, actual input and viewport resize reveal
the caret through the same bounds; manual scrolling and programmatic focus restoration retain no navigation anchor.
Mounted nearest-caret scroll delegates to the same reveal owner to avoid competing writes; visible input
performs no scroll write. Keyboard changes can await pending document-tail layout for at most eight measurements,
stopping as soon as the endpoint is visible. New user scroll intent still cancels immediately.

## Projection declarations

Use block layers for multiline previews; see [Diagrams](/en/extensions/diagrams).

### ProjectionLayer

Composable layer; block output is provided before CM computes the viewport.

```ts
export interface ProjectionLayer {
    readonly key: string;
    readonly placement: "inline" | "block";
    readonly modes?: readonly MsfeMode[];
    readonly selection?: boolean;
    readonly states?: readonly StateSlot<unknown>[];
    readonly suppressDuringComposition?: boolean;
    readonly incremental?: boolean;
    readonly references?: boolean;
    readonly build: (input: ProjectionInput) => readonly ProjectionDecoration[];
}
```

**key** — Unique capability name; collisions fail before mount.

**placement** — Inline visits only visible source; block may emit vertically structural widgets/replacements.

**modes** — Allowed presentations, default live and locked.

**selection** — Extra selection dependency, default false. Reveal behavior is handled separately by the runtime.

**states** — Typed state dependencies compared by identity before rebuilding.

**suppressDuringComposition** — Suppress only output intersecting IME's selection; default true. Hidden-syntax presentation stays projected.

**incremental** — Opt into block-local source dependencies. Builds may receive only changed complete syntax blocks.
Output and reveal ranges must stay within those blocks. Coordinate-bearing component props require componentView's mapper.
Unspecified layers keep conservative full builds.

**references** — Incremental output also depends on global Markdown reference definitions; definition changes rebuild the layer.

**build** — @param input - Canonical bounded inputs.
@returns Declarative decoration descriptions.

## Canonical inputs

Query bounded semantics; never dispatch while building.

### ProjectionInput

Bounded projection inputs, with semantic queries and typed state access.

```ts
export interface ProjectionInput extends MsfeSourceRange {
    readonly document: SourceDocument;
    readonly selection: EditorSelection;
    readonly mode: MsfeMode;
    readonly readOnly: boolean;
    query(name: string): SourceQuery;
    getState<T>(slot: StateSlot<T>): T;
}
```

**document** — Lazy canonical source.

**selection** — Directed selection; declare selection dependency when using it to build descriptions.

**mode** — Effective presentation.

**readOnly** — Host permission or locked mode.

**query** — @param name - Semantic type.
@returns Source matches within this pass, including intersecting ancestors.

**getState** — @param slot - Declared state dependency.
@returns Current value.

**from** — Inclusive source start.

**to** — Exclusive source end.

## View descriptions

Stable renderers and immutable props form retained view descriptions.

### componentView

@param renderer - Typed renderer.
@param props - Typed immutable inputs.
@param map - Pure relocation of source-coordinate props for incremental layers; no resource acquisition or source writes.
@returns Installable view description.

```ts
function componentView<Props extends object>(renderer: ComponentViewRenderer<Props>, props: Props, map?: (props: Props, offset: number) => Props): ComponentView
```

## Mount context

Scope belongs to the current view; request measurement after geometry changes.

### ComponentViewMount

Generic component inputs, independent of source-node persistence.

```ts
export interface ComponentViewMount<Props extends object> {
    readonly dom: HTMLElement;
    readonly props: Props;
    readonly host: MsfeComponentHost | undefined;
    readonly scope: TaskScope;
    readonly requestMeasure: () => void;
}
```

**dom** — Runtime-owned non-editable DOM boundary.

**props** — Feature-defined immutable props.

**host** — Existing Vue/React provider host, when installed.

**scope** — View lifetime, ended when the widget is unmounted or its document is replaced.

**requestMeasure** — Requests a coalesced CM measurement after geometry changes.

## Update and teardown

Update the retained entry and release host-owned resources during teardown.

### ComponentViewHandle

Generic retained component lifecycle.

```ts
export interface ComponentViewHandle<Props extends object> {
    ignoreEvent?(event: Event): boolean;
    update(props: Props): void;
    destroy(): void;
}
```

**ignoreEvent** — @param event - Event originating inside this component.
@returns True for component-owned controls, false to delegate native source interaction to CM. Defaults to true.

**update** — @param props - Changed inputs with the same renderer identity.

**destroy** — Removes framework entries and renderer-owned listeners.

## Code languages

See [Code blocks](/en/extensions/code-blocks) for a complete lazy registry.

### CodeLanguage

Host-owned lazy language; parsed by the canonical Markdown mixed parser.

```ts
export interface CodeLanguage {
    readonly name: string;
    readonly aliases?: readonly string[];
    readonly load: (signal: AbortSignal) => Promise<LanguageSupport>;
}
```

**name** — Exact case-insensitive fence language name.

**aliases** — Additional exact info-string names, such as ts or js.

**load** — @param signal - Editor lifetime; dynamic imports may finish after cancellation.
@returns Language parser support.

## Search panel

Components use view; replacement respects permission and composition.

### SearchPanelProps

Host renderer contract for the existing search panel lifecycle.

```ts
export interface SearchPanelProps {
    readonly query: SearchParameters;
    readonly valid: boolean;
    readonly matches: SearchMatches;
    readonly readOnly: boolean;
    readonly composing: boolean;
    readonly setQuery: (changes: Partial<SearchParameters>) => boolean;
    readonly run: (action: SearchAction) => boolean;
    readonly phrase: (key: string) => string;
}
```

**query** — Current canonical search fields.

**valid** — Whether the nonempty query can be executed.

**matches** — Match count for the current query and document.

**readOnly** — Effective editor permission.

**composing** — Whether native composition currently owns document or search-field input.

**setQuery** — @param changes - Partial query update.
@returns Whether the still-mounted panel accepted it.

**run** — @param action - Explicit search intent.
@returns Whether the action was applicable.

**phrase** — @param key - CM phrase key.
@returns Host-localized label.

## Preview configuration

Math, Mermaid and TextDiagrams expose host action controls through this option.

### PreviewOptions

Optional host UI; feature packages do not prescribe a product button skin.

```ts
export interface PreviewOptions {
    readonly controls?: ComponentViewRenderer<PreviewControlProps>;
    readonly edit?: (request: PreviewEditRequest) => boolean;
    readonly activation?: PreviewActivation;
    readonly image?: boolean;
}
```

**controls** — Existing Vue/React/native component adapter for source actions.

**edit** — Optional host editor. Capture a separate source anchor when retaining a draft beyond this callback.
@param request - Validated current source editing intent.
@returns Whether the host accepted this editing request.

**activation** — How a block preview enters editing: `click` (default) or `dblclick`, leaving single clicks to host selection. Inline formulas always use click.

**image** — Mermaid only: show the diagram as an SVG image drawn with system fonts, keeping its stylesheet out of the page.

`edit` delegates preview editing to host UI such as the shared formula panel. The request has
passed target and permission validation. For an asynchronous draft, independently call
`captureAnchor(range)`, check `anchor.target()` on confirmation and release it on close.
Return `true` after taking ownership; omitting the callback retains source reveal.
See [MD Editor](/en/guide/md-editor).

### PreviewEditRequest

An explicit editing request after the preview's mapped source has been validated.

```ts
export interface PreviewEditRequest {
    readonly editor: MsfeEditor;
    readonly range: MsfeSourceRange;
    readonly kind: PreviewProps["kind"];
}
```

**editor** — Canonical transaction owner; the host must respect read-only and composition guards.

**range** — Current, unchanged source interval including delimiters.

**kind** — Formula or fenced diagram family.

## Preview actions

Install `TextDiagrams` for PlantUML/DOT with the same `PreviewOptions`, edit callback and cancellation lifecycle. The neutral `renderTextDiagram(language, source, options)` API from `@quirekit/text-diagram/browser` returns a sanitized SVG snapshot tied to the exact language and source. `rasterizeTextDiagram(snapshot, ownerDocument, signal)` produces PNG from that snapshot. MD Editor previews, the shared source workspace and Word image export use this path for all three languages.


`renderMermaidSvg()` renders sanitized static SVG from source in a browser, sharing the preview's
serialized Mermaid queue and security configuration. Pass a cancellation signal and typography;
hosts can rasterize the result for file export. It does not read editor DOM, register SVG events,
or permit external image/style resources.

### renderMermaidSvg

@param text - Exact Mermaid source.
@param signal - Explicit lifetime.
@param typography - Effective host font role.
@returns Sanitized SVG from the shared text-diagram owner.

```ts
function renderMermaidSvg(text: string, signal: AbortSignal, typography: PreviewTypography): Promise<string>
```

### PreviewTypography

Host typography resolved only when acquiring a new preview, without a UI package dependency.

```ts
export interface PreviewTypography {
    readonly fontFamily: string;
    readonly fontSize: number;
}
```

**fontFamily** — Effective font stack used by the source document.

**fontSize** — Effective body size in CSS pixels.

Handle loading, errors, retry and source reveal.

### PreviewControlProps

Semantic preview actions passed to a host's existing component library.

```ts
export interface PreviewControlProps {
    readonly kind: PreviewProps["kind"];
    readonly readOnly: boolean;
    readonly status: "loading" | "ready" | "error";
    readonly retry: () => void;
    readonly revealSource: () => boolean;
}
```

**kind** — Renderer family, for a host-localized action name.

**readOnly** — Whether source editing is unavailable.

**status** — Current asynchronous rendering state; permission-only updates retain it.

**retry** — Retries the current source under this component's cancellable lifetime.

**revealSource** — @returns Whether the current canonical source was revealed.

## Image resources

Source destinations pass field validation before host resolution.

### ImageOptions

Resolves Markdown image destinations and stores pasted or dropped images, without storing host credentials in the document.

```ts
export interface ImageOptions {
    readonly resolve?: (source: string, signal: AbortSignal) => MsfeImageResource | Promise<MsfeImageResource>;
    readonly upload?: ImageUploader | false;
}
```

**resolve** — Receives a source URL and lifetime signal; the default uses safe browser URLs directly.

**upload** — Stores pasted and dropped images. Omitted: PNG, JPEG, GIF and WebP are inlined as `data:` URLs.
`false` leaves paste and drop to the host.

## Block controls

The core owns native hit testing, pointer lifecycle and guarded transactions; hosts own component rendering. Child flows are opt-in. Pointer updates inspect only local ancestor paths, and commands run only on release or explicit menu actions.

### SourceBlockScopeResolver

@param state - Accepted source.
@param node - Candidate container.
@returns Editable child flow, null for unhandled syntax, or false to exclude an incomplete/position-sensitive block.

```ts
export type SourceBlockScopeResolver = (state: EditorState, node: SyntaxNode) => MsfeSourceRange | null | false;
```

### sourceBlockScopes

Optional nested child-flow declarations; document-level blocks work without configuration.

```ts
sourceBlockScopes = Facet.define<SourceBlockScopeResolver>()
```

### createSourceBlockControls

@param options - Host-owned UI observers.
@returns Source-native block hit testing, drag/drop and menu intent lifecycle.

```ts
function createSourceBlockControls(options: SourceBlockControlsOptions): AnyExtension
```

### SourceBlockControlsOptions

Framework-neutral observers; UI remains in the consumer's component/provider tree.

```ts
export interface SourceBlockControlsOptions {
    readonly change: (controls: SourceBlockControls | null) => void;
    readonly openMenu: (controls: SourceBlockControls) => void;
    readonly insertionChange?: (controls: SourceInsertionControls | null) => void;
    readonly openInsertionMenu?: (controls: SourceInsertionControls) => void;
}
```

**change** — @param controls - Current snapshot, or null after exit/read-only/composition/unmount.

**openMenu** — @param controls - Exact block pinned by a click or keyboard request.

**insertionChange** — @param controls - Editable blank line snapshot, or null when no insertion entry is active.

**openInsertionMenu** — @param controls - Blank line pinned by the block-menu keyboard shortcut.

### SourceBlockControls

Small transient snapshot for a host-rendered block handle and menu.

```ts
export interface SourceBlockControls {
    readonly editor: MsfeEditor;
    readonly block: SourceBlock;
    readonly rect: SourceBlockRect;
    readonly firstLine: SourceBlockRect | null;
    readonly target: MsfeTarget;
    readonly dragging: boolean;
    readonly drop: SourceBlockDrop | null;
    readonly pointer: {
        readonly x: number;
        readonly y: number;
    } | null;
    press(event: PointerEvent): void;
    pin(open: boolean): void;
    run(command: MsfeCommand): boolean;
    focus(): void;
}
```

**editor** — Canonical editor owning source and history.

**block** — Complete hovered or keyboard-active source block.

**rect** — Current visual bounds.

**firstLine** — First visual text row for control alignment, or null for an opaque block widget.

**target** — Frozen revision and block selection for explicit commands.

**dragging** — True only after crossing the drag threshold.

**drop** — Current legal insertion line; null when no move is allowed.

**pointer** — Client position of the pointer during drag.

**press** — @param event - Real primary pointerdown on the host's drag handle.

**pin** — @param open - Host menu state, pinning the current block until closed.

**run** — @param command - Source planner for the captured block.
@returns Whether the exact still-current intent was accepted.

**focus** — Restores the native caret after menu dismissal or cancellation.

### SourceInsertionControls

Host insertion entry for empty source, sharing the block observer without drag semantics.

```ts
export interface SourceInsertionControls {
    readonly editor: MsfeEditor;
    readonly line: SourceBlankLine;
    readonly rect: SourceBlockRect;
    readonly target: MsfeTarget;
    pin(open: boolean): void;
    focus(): void;
}
```

**editor** — Canonical editor owning source and history.

**line** — Exact editable empty line and its enclosing flow.

**rect** — Native first-row geometry in viewport CSS pixels.

**target** — Frozen revision and empty-line selection for insertion.

**pin** — @param open - Pin the insertion position while its menu is open.

**focus** — Restores native editing focus after dismissal.

### SourceBlockRect

Client-space geometry read from the canonical view, never a layout authority.

```ts
export interface SourceBlockRect {
    readonly left: number;
    readonly top: number;
    readonly width: number;
    readonly height: number;
}
```

**left** — Left visual boundary in CSS pixels.

**top** — Top visual boundary in CSS pixels.

**width** — Rendered block width.

**height** — Rendered block height.

### SourceBlockDrop

Current validated insertion boundary during a pointer gesture.

```ts
export interface SourceBlockDrop {
    readonly block: SourceBlock;
    readonly side: SourceBlockSide;
    readonly rect: SourceBlockRect;
}
```

**block** — Neighbor in the same structural flow.

**side** — Before or after that neighbor.

**rect** — Passive insertion indicator in client coordinates.
