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.
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.
@quirekit/msfe-coreComposable layer; block output is provided before CM computes the viewport.
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[];
}Canonical inputs
Query bounded semantics; never dispatch while building.
@quirekit/msfe-coreBounded projection inputs, with semantic queries and typed state access.
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;
}View descriptions
Stable renderers and immutable props form retained view descriptions.
@quirekit/msfe-core@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.
function componentView<Props extends object>(renderer: ComponentViewRenderer<Props>, props: Props, map?: (props: Props, offset: number) => Props): ComponentViewMount context
Scope belongs to the current view; request measurement after geometry changes.
@quirekit/msfe-coreGeneric component inputs, independent of source-node persistence.
export interface ComponentViewMount<Props extends object> {
readonly dom: HTMLElement;
readonly props: Props;
readonly host: MsfeComponentHost | undefined;
readonly scope: TaskScope;
readonly requestMeasure: () => void;
}Update and teardown
Update the retained entry and release host-owned resources during teardown.
@quirekit/msfe-coreGeneric retained component lifecycle.
export interface ComponentViewHandle<Props extends object> {
ignoreEvent?(event: Event): boolean;
update(props: Props): void;
destroy(): void;
}Code languages
See Code blocks for a complete lazy registry.
@quirekit/msfe-coreHost-owned lazy language; parsed by the canonical Markdown mixed parser.
export interface CodeLanguage {
readonly name: string;
readonly aliases?: readonly string[];
readonly load: (signal: AbortSignal) => Promise<LanguageSupport>;
}Search panel
Components use view; replacement respects permission and composition.
@quirekit/msfe-coreHost renderer contract for the existing search panel lifecycle.
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;
}Preview configuration
Math, Mermaid and TextDiagrams expose host action controls through this option.
@quirekit/msfe-markdown-extrasOptional host UI; feature packages do not prescribe a product button skin.
export interface PreviewOptions {
readonly controls?: ComponentViewRenderer<PreviewControlProps>;
readonly edit?: (request: PreviewEditRequest) => boolean;
readonly activation?: PreviewActivation;
readonly image?: boolean;
}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.
@quirekit/msfe-markdown-extrasAn explicit editing request after the preview's mapped source has been validated.
export interface PreviewEditRequest {
readonly editor: MsfeEditor;
readonly range: MsfeSourceRange;
readonly kind: PreviewProps["kind"];
}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.
@quirekit/msfe-markdown-extras@param text - Exact Mermaid source. @param signal - Explicit lifetime. @param typography - Effective host font role. @returns Sanitized SVG from the shared text-diagram owner.
function renderMermaidSvg(text: string, signal: AbortSignal, typography: PreviewTypography): Promise<string>@quirekit/msfe-markdown-extrasHost typography resolved only when acquiring a new preview, without a UI package dependency.
export interface PreviewTypography {
readonly fontFamily: string;
readonly fontSize: number;
}Handle loading, errors, retry and source reveal.
@quirekit/msfe-markdown-extrasSemantic preview actions passed to a host's existing component library.
export interface PreviewControlProps {
readonly kind: PreviewProps["kind"];
readonly readOnly: boolean;
readonly status: "loading" | "ready" | "error";
readonly retry: () => void;
readonly revealSource: () => boolean;
}Image resources
Source destinations pass field validation before host resolution.
@quirekit/msfe-imageResolves Markdown image destinations and stores pasted or dropped images, without storing host credentials in the document.
export interface ImageOptions {
readonly resolve?: (source: string, signal: AbortSignal) => MsfeImageResource | Promise<MsfeImageResource>;
readonly upload?: ImageUploader | false;
}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.
@quirekit/msfe-core@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.
export type SourceBlockScopeResolver = (state: EditorState, node: SyntaxNode) => MsfeSourceRange | null | false;@quirekit/msfe-coreOptional nested child-flow declarations; document-level blocks work without configuration.
sourceBlockScopes = Facet.define<SourceBlockScopeResolver>()@quirekit/msfe-core@param options - Host-owned UI observers. @returns Source-native block hit testing, drag/drop and menu intent lifecycle.
function createSourceBlockControls(options: SourceBlockControlsOptions): AnyExtension@quirekit/msfe-coreFramework-neutral observers; UI remains in the consumer's component/provider tree.
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;
}@quirekit/msfe-coreSmall transient snapshot for a host-rendered block handle and menu.
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;
}@quirekit/msfe-coreHost insertion entry for empty source, sharing the block observer without drag semantics.
export interface SourceInsertionControls {
readonly editor: MsfeEditor;
readonly line: SourceBlankLine;
readonly rect: SourceBlockRect;
readonly target: MsfeTarget;
pin(open: boolean): void;
focus(): void;
}@quirekit/msfe-coreClient-space geometry read from the canonical view, never a layout authority.
export interface SourceBlockRect {
readonly left: number;
readonly top: number;
readonly width: number;
readonly height: number;
}@quirekit/msfe-coreCurrent validated insertion boundary during a pointer gesture.
export interface SourceBlockDrop {
readonly block: SourceBlock;
readonly side: SourceBlockSide;
readonly rect: SourceBlockRect;
}