# 渲染与宿主 API

投影层、组件挂载、语言、搜索以及图表操作协议。

本页签名从公共包导出生成，中文说明与当前 JSDoc 同步校验。代码标识保持原名。

## 源码显露

`readSourcePresentation(state)` 返回 `mode`、`selection`、`reveal`、`composing` 与 `selecting`。
`selection` 是呈现使用的稳定选区，拖选期间可能与实时 `state.selection` 不同。
`reveal` 为 `selection`（普通 MSFE 默认）或 `hidden`；后者让已识别的语法在编辑中保持投影。
内置投影与组件的 `ProjectionContext.active` 已结合此策略，扩展优先使用 `active` 判断，
不要自行读取实时光标后强制显露。纯源码模式始终显示原文。

`sourceRevealSetting` 是公开 `PresentationSetting<SourceRevealPolicy>`：通过
`addCodeMirrorExtensions()` 安装 `sourceRevealSetting.extension(() => 'hidden')`，再用
`sourceRevealSetting.set(editor, 'selection')` 提交呈现请求。`read(state)` 返回已应用值，
`requested(state)` 返回最近请求，未安装时都返回 `undefined`。setter 返回 `MsfeResult`，
未安装时为 `not-installed`，无效值为 `invalid-change`；销毁或未挂载时也会拒绝。
请求不改变权限或源码，拖选与组合输入期间延迟生效。产品用户优先使用
[MD Editor 的 view](/guide/md-editor#四种视图)。

直接创建的替换范围在 `hidden` 下参与原子导航，避免光标进入不可见语法。
未知/不完整内容应继续显示源码；请勿用 CSS 让无投影合同的文本消失。

`sourceRevealResolver.of(resolveOwner)` 是宿主可安装的 CodeMirror facet。
`resolveOwner(state, range)` 返回包含原片段的规范源码范围，用来将行内标记归入段落、列表项
或单元格；范围必须是文档内的整数坐标，否则抛出 `invalid-source-reveal-owner`。
不安装时保留节点自身的显露范围。解析函数应只查询局部语法祖先，不读取全文、修改选区或提交事务。
MD Editor 已安装此策略，自定义节点、规范化替换和组件高度共用它。

内置投影继续读取 `ProjectionContext.active`。独立 CodeMirror 插件使用
`isSourceRevealed(state, { from, to })` 查询同一策略；它同时检查模式、隐藏偏好和拖选稳定选区。
`isSourceActive(selection, from, to)` 只做范围相交判断，不应用宿主范围或模式。
规范化替换的 `revealRange` 先表示完整片段，再由 resolver 归入宿主语义范围；
`interaction: 'atomic'` 的替换仍保留显式原子交互。

## 可选输入代理

`sourceInputProxy({ mediaQuery, modes?, revealMargin?, onSelectionTap? })` 返回可安装的 CodeMirror extension，默认仅在
`live` 模式匹配宿主窗口媒体条件时启用。MD Editor 已为窄屏触摸设备安装，不需宿主重复安装。
它复用 `@quirekit/ime`，把可编辑 textarea 放到当前 Document/ShadowRoot 的独立输入层；
正文只负责展示，文档、选区、源码规则和撤销仍使用原 CM state。取消 preedit 不删除选区，
确认只写一笔事务；权限收回、文档替换和卸载释放 native host。
可选 `onSelectionTap(view, point)` 返回 true 时，宿主已处理已有选区的短按，代理不移动
caret、不重新 focus；触摸结束与兼容 click 只接受一次。MD Editor 用它切换选区操作菜单。
代理有效期间原生 Selection 不得持有非 editable 正文范围，独立嵌入控件仍保留自己的选区。
`revealMargin` 指定 caret 揭示时额外保留的 client CSS 空间，默认 8px；MD Editor 使用
32px，为圆点及触摸热区留出空间。它不改变源位置或文档布局。

`editor.focus()` 会选择当前输入 owner。使用 CM escape hatch 的自定义控件应使用
`focusSourceView(view)`、`sourceInputHasFocus(view)` 和 `sourceInputElement(view)`，不能假定
`view.hasFocus` 或 `contentDOM` 代表移动键盘焦点。`sourceInputFocusChanged(update)` 同时报告
原生与代理的逻辑焦点变化。几何阶段可用 `sourceInputCaret(view)` 读取当前 preedit 尾端的
client CSS 矩形；没有 preedit 时返回 null，继续使用 `coordsAtPos`。禁止每帧 focus 或清除输入。

`sourceInputPosition(view, { x, y })` 接受 client CSS 命中，跳过原子替换内部与零高度源行，
将间隙吸附到最近两侧的可见边界；无法证明挂载可见时返回 null，应保留上一个端点。
不要再直接把 `posAtCoords` 的隐藏位置提交为移动端选区。
`sourceCaretRect(view, pos, side?)` 只返回具有真实行框的 mounted caret；首选关联侧
不可见时尝试另一侧，避免围栏/标题边界丢失光标。正常只读一侧，边界最多两侧；无几何时返回 null。

`sourceViewport(view)` 在几何阶段读取 VisualViewport、裁剪祖先和 CM scrollMargins 的
可用区域，含 offsetTop；键盘与 dock 不重复计数。`sourceScrollStep(view, delta)` 只测量
最近可滚动祖先的一次垂直推进，耗尽后交给外层页面；`applySourceScrollStep(step)` 在写阶段
执行该测量，不改变源或选区。连续拖选由消费方保留一个活动帧并重算最新端点，必须在松手、
取消、权限/模式变化和销毁时停止。代理短按、实际输入与视口 resize 会按同一边界揭示 caret；
普通手动滚动和仅恢复程序焦点不会建立持续导航锚点。已挂载的 nearest caret-scroll
委托同一揭示 owner，避免竞争滚动；可见输入零滚动写入。键盘变化时若末尾空间尚未更新，
最多八次测量等待可用布局，端点可见即结束，新的用户滚动仍立即取消。

## 投影声明

跨行预览使用 block；可操作示例见 [图表](/extensions/diagrams)。

### ProjectionLayer

可组合投影层；块级输出在 CodeMirror 计算视口前提供。

```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** — 唯一能力名称；冲突会在挂载前报错。

**placement** — inline 仅访问可见源码；block 可产生影响纵向布局的组件和替换。

**modes** — 允许出现的模式，默认 live 和 locked。

**selection** — 额外的选区依赖，默认 false；源码显露由运行时单独处理。

**states** — 状态依赖列表，重建前按值的身份进行比较。

**suppressDuringComposition** — 仅抑制与组合输入选区相交的投影，默认 true；隐藏标记的文档编辑模式仍保持投影。

**incremental** — 启用块级源码依赖。构建时可能仅收到变更后的完整语法块，输出和显露范围须位于这些块内；组件中带源码坐标的属性须由 componentView 的映射函数处理。未配置时保持保守的全量构建。

**references** — 增量输出同时依赖全局 Markdown 引用定义，定义变化时重新构建该投影层。

**build** — 参数 input：有界的规范输入。返回声明式装饰描述。

## 规范输入

查询有界语义，不在构建期间提交事务。

### ProjectionInput

投影层的有界输入，提供语义查询与类型化状态访问。

```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** — 惰性读取的规范源码。

**selection** — 有方向的选区；构建时读取它需声明 selection 依赖。

**mode** — 当前实际呈现模式。

**readOnly** — 综合宿主权限和 locked 模式得到的只读状态。

**query** — 参数 name：语义名称。返回本轮范围中的匹配，包含相交祖先。

**getState** — 参数 slot：已声明的状态依赖。返回当前值。

**from** — 包含在范围内的起始源码位置。

**to** — 不包含在范围内的结束源码位置。

## 视图描述

稳定 renderer 与不可变 props 形成可保留的组件描述。

### componentView

参数 renderer：类型化渲染器；props：类型化不可变输入；map：增量投影层中源码坐标属性的纯映射，不获取资源或写入源码。返回可安装的视图描述。

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

## 挂载上下文

scope 是当前组件生命周期；几何变化后请求测量。

### ComponentViewMount

通用组件挂载输入，与源码节点的持久化协议独立。

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

**dom** — 运行时拥有的不可编辑 DOM 边界。

**props** — 功能定义的不可变属性。

**host** — 已安装的 Vue 或 React Provider 宿主。

**scope** — 组件卸载或文档替换时结束的视图生命周期。

**requestMeasure** — 几何尺寸变化后，请求合并执行的 CodeMirror 测量。

## 保留与清理

更新复用原入口，销毁释放宿主创建的所有资源。

### ComponentViewHandle

保留组件的更新与清理协议。

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

**ignoreEvent** — 参数 event：组件内事件。返回 true 表示由组件控件处理；false 交回 CodeMirror。默认 true。

**update** — 参数 props：渲染器身份不变时更新的属性。

**destroy** — 移除框架入口及渲染器自己注册的监听器。

## 代码语言

完整惰性加载目录见 [代码块](/extensions/code-blocks)。

### CodeLanguage

宿主声明的惰性语言目录，由同一 Markdown 混合解析器使用。

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

**name** — 大小写不敏感、精确匹配的围栏语言名。

**aliases** — 额外精确别名，例如 ts 或 js。

**load** — 参数 signal：编辑器生命周期；动态导入可能在取消后完成。返回语言解析支持。

## 搜索面板

组件使用 view 属性，替换动作必须考虑权限和组合输入。

### SearchPanelProps

替换现有搜索面板的宿主渲染器协议。

```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** — 当前规范搜索字段。

**valid** — 非空查询是否有效且可执行。

**matches** — 当前查询在正文中的命中计数。

**readOnly** — 编辑器的实际只读权限。

**composing** — 正文或搜索框是否处于原生组合输入。

**setQuery** — 参数 changes：查询字段的局部更新。返回面板仍挂载时是否接受。

**run** — 参数 action：搜索意图。返回动作是否适用。

**phrase** — 参数 key：CodeMirror 文案键。返回宿主本地化标签。

## 预览配置

Math、Mermaid 与 TextDiagrams 的公开配置用于宿主操作组件。

### PreviewOptions

配置宿主预览控件，功能包不规定按钮的产品外观。

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

**controls** — 用于源码操作的 Vue、React 或原生组件适配器。

**edit** — 可选的宿主编辑回调。需要在回调结束后保留草稿时，请独立捕获源码锚点。
@param request - 已校验的当前源码编辑意图。
@returns 宿主是否接受本次编辑请求。

**activation** — 块级预览进入编辑的方式：`click`（默认）或 `dblclick`，后者把单击留给宿主的选中。行内公式始终单击进入。

**image** — 仅用于 Mermaid：以使用系统字体的 SVG 图片显示图形，避免将图形样式表注入页面。

`edit` 可将预览的编辑操作交给共享公式面板等宿主 UI。请求已经过目标和权限校验；
若异步保留草稿，须另行 `captureAnchor(range)`，确认时检查 `anchor.target()`，关闭时
释放锚点。返回 `true` 表示接管成功；不配置时沿用源码显露。见 [MD Editor](/guide/md-editor)。

### PreviewEditRequest

预览映射的源码通过校验后发出的明确编辑请求。

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

**editor** — 权威事务所属实例；宿主必须遵循只读与组合输入限制。

**range** — 包含分隔符的当前未被修改的源码范围。

**kind** — 公式或围栏图表的类型。

## 预览操作

PlantUML/DOT 可由 `TextDiagrams` 安装并复用同一 `PreviewOptions`、编辑回调与取消生命周期。公共中立接口 `renderTextDiagram(language, source, options)` 位于 `@quirekit/text-diagram/browser`，返回绑定精确语言与源码的净化 SVG 快照；`rasterizeTextDiagram(snapshot, ownerDocument, signal)` 生成同一快照的 PNG。MD Editor 三语言预览、共享源码面板和 Word 图片导出使用这条路径。


`renderMermaidSvg()` 在浏览器中从源码生成清理后的静态 SVG，与编辑器预览共用 Mermaid
串行队列和安全配置。宿主传入取消信号与字体，可继续转成 PNG 用于文件导出；该函数不读取
编辑器 DOM，不注册 SVG 事件，也不允许外部图片或样式资源。

### renderMermaidSvg

参数 text：完整 Mermaid 源码；signal：本次渲染的生命周期；typography：宿主字体配置。返回共享文本绘图模块净化后的 SVG，与其他 Mermaid 请求串行渲染。

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

### PreviewTypography

获取新预览时解析的宿主字体配置，不依赖 UI 包。

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

**fontFamily** — 源文档使用的有效字体栈。

**fontSize** — 正文的有效字号，单位为 CSS 像素。

处理加载、错误、重试和源码显露。

### PreviewControlProps

传给宿主组件库的预览状态与动作。

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

**kind** — 渲染器类型，用于本地化操作名称。

**readOnly** — 是否禁止编辑源码。

**status** — 异步渲染状态；仅权限变化时保留原渲染状态。

**retry** — 在当前组件可取消的生命周期中重试。

**revealSource** — 返回是否成功显露当前规范源码。

## 图片资源

源码地址先经过字段校验，再交给宿主 resolver。

### ImageOptions

解析 Markdown 图片目标并保存粘贴、拖入的图片，宿主凭证不写入文档。

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

**resolve** — 接收源码地址和生命周期信号；默认直接使用安全的浏览器地址。

**upload** — 保存粘贴与拖入的图片。省略时 PNG、JPEG、GIF、WebP 内联为 `data:` 地址；`false` 表示粘贴与拖入交给宿主处理。

## 块控制协议

核心管理原生命中、指针生命周期和版本化事务，宿主管理组件渲染。嵌套子流须显式声明。指针更新只查询局部祖先路径，松手或执行菜单动作时才运行命令。

### SourceBlockScopeResolver

参数 state：已接受的源码。参数 node：候选容器。返回可编辑的子块范围；未处理语法返回 null，不完整或具有位置约束的块返回 false。

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

### sourceBlockScopes

扩展可声明嵌套子块范围；文档根级块不需要额外配置。

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

### createSourceBlockControls

参数 options：宿主 UI 观察回调。返回管理原生块命中、拖放和菜单意图生命周期的扩展。

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

### SourceBlockControlsOptions

不依赖框架的观察回调；UI 保留在使用方的组件和 Provider 树中。

```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** — 参数 controls：当前快照；退出、只读、组合输入或卸载后为 null。

**openMenu** — 参数 controls：点击或键盘请求固定的精确块。

**insertionChange** — 参数 controls：可编辑空行快照；没有活动插入入口时为 null。

**openInsertionMenu** — 参数 controls：块菜单快捷键固定的空行。

### SourceBlockControls

供宿主渲染块手柄与菜单的小型临时快照。

```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** — 持有源码与历史的编辑器。

**block** — 完整源码块；插入目标属于同一个结构范围。

**rect** — 客户端坐标系中的块边界或被动插入线。

**firstLine** — 用于控件对齐的首个视觉文本行；无法获取内部行的不透明块组件返回 null。

**target** — 用于明确命令操作的冻结版本与块选区。

**dragging** — 超过拖动阈值后才为 true。

**drop** — 当前合法插入线；不能移动时为 null。

**pointer** — 拖动期间的客户端指针位置。

**press** — 参数 event：宿主拖柄上真实的主指针 pointerdown 事件。

**pin** — 参数 open：宿主菜单状态；打开期间固定当前块。

**run** — 参数 command：捕获块的源码规划器。返回该精确目标仍有效且操作被接受时的成功状态。

**focus** — 菜单关闭或取消后恢复原生编辑器焦点。

### SourceInsertionControls

供宿主展示空行插入入口；复用块观察器，不提供空行拖动语义。

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

**editor** — 持有源码与历史的编辑器。

**line** — 准确的可编辑空行及其所属内容流。

**rect** — 原生首行在视口中的矩形，单位为 CSS 像素。

**target** — 供插入使用的冻结版本与空行选区。

**pin** — 参数 open：打开菜单期间固定插入位置。

**focus** — 关闭后恢复原生编辑焦点。

### SourceBlockRect

从当前视图读取的客户端坐标几何，不作为排版依据。

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

**left** — 左侧边界，单位为 CSS 像素。

**top** — 视觉上边界，单位为 CSS 像素。

**width** — 渲染块宽度。

**height** — 渲染块高度。

### SourceBlockDrop

指针手势当前校验通过的插入位置。

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

**block** — 完整源码块；插入目标属于同一个结构范围。

**side** — 位于目标块之前或之后。

**rect** — 客户端坐标系中的块边界或被动插入线。
