# @quillmark/svelte 中文文档

规范地址：https://npmcn.com/package/%40quillmark/svelte/

版本：0\.30\.0
npm 元数据更新时间：2026-11-02T04:14:39.338Z
中文译文更新时间：2026-11-02T06:46:49.207Z
软件包许可：Apache-2\.0

本文由上游 README 机器翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40quillmark/svelte
原文对照：https://npmcn.com/package/%40quillmark/svelte/?lang=original
项目主页：https://github.com/borb-sh/quillmark-js/tree/main/packages/svelte#readme
源码仓库：https://github.com/borb-sh/quillmark-js

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：1.2 MB，210 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=22
- 运行依赖：13 个
- peer 依赖：svelte ^5\.33\.0，@quillmark/wasm \>=0\.134\.1-0 \<0\.135\.0-0
- 首次发布：2026-08-07T01:29:14.228Z
- 最新版本发布：2026-11-02T04:14:39.033Z
- 已发布版本数：48（近 12 个月 48 个）

## 中文简介

编辑器，实时预览和源表面在Quillmark WASM会话。

## 安装

```sh
npm install @quillmark/svelte
```

也可以使用：`pnpm add @quillmark/svelte`、`yarn add @quillmark/svelte`、`bun add @quillmark/svelte`

## README

# @quillmark /苗条的

编辑器+实时预览组件 [Quillmark](https://github.com/borb-sh/quillmark) WASM消费者。一个所见即所得 **VisualEditor**，还有一块帆布。 **预览** 这将绘制编译后的文档，并将单击往返到编辑器 `@quillmark/wasm` 会话。香草- ts芯与薄苗条5包装。

## 安装

```sh
npm install @quillmark/svelte
```

`svelte@^5.33` 和 `@quillmark/wasm` 是对等依赖关系：会话的句柄跨越包边界，因此使用者提供双方生成它们的一个副本。

## 子路径

每个子路径都是它自己的模块根；捆绑器只提取您导入的条目所到达的内容。

| 进口 | 表面 |
| --- | --- |
| `@quillmark/svelte/core` | 表面的共同点是： `DocPath`/`Place` 地址词汇， `EditorError` 频道, `init`． 没有UI框架。 |
| `@quillmark/svelte/preview` | 现场预览： `createPreview` + `<Preview>`． 编辑器端没有达到任何值。 |
| `@quillmark/svelte/visual` | 联邦所见即所得： `<VisualEditor>`，编解码器 `createField` 散文叶。 |
| `@quillmark/svelte` | 再出口 `/core`. |
| `@quillmark/svelte/preset` | 表面周围页面的可选样式表([THEMING.md](https://npmcn.com/package/%40quillmark/svelte/THEMING.md)）。 |

## 打开会话

这个 **消费者** 拥有会话和句柄，并驱动每个编辑；表面是它的视图。手柄来自 `@quillmark/wasm` Peer，从来没有在这里再出口过。

```ts
import { Engine } from '@quillmark/wasm';
import { init } from '@quillmark/svelte/core';

// Quill and Document are exported only through init().
const { Quill, Document } = await init();

const quill = Quill.fromTree(tree); // tree: Map<string, Uint8Array> of the quill dir

// A NEW document: seeded from the quill's schema.
const doc = quill.seedDocument();

// An EXISTING document: parsed back from what you stored.
const doc = Document.fromMarkdown(markdown); // canonical Quillmark markdown
const doc = Document.fromStored(json); // the versioned storage DTO (`doc.toStored()`)

const session = await new Engine().open(quill, doc);
// free() on teardown the handles you MINTED: this quill, this doc, this session.
```

`<VisualEditor>` 不需要会议，和第一 `open` 加载渲染构建，因此安装编辑器的主机 `doc` 和 `quill` 首先显示它，而不等待负载。会议在它后面开始： `await engine.load(quill)`,然后 `engine.open(quill, doc)`，因此它的第一次编译读取与此同时输入的内容([HOSTING.md](https://npmcn.com/package/%40quillmark/svelte/prose/canon/HOSTING.md)）。

## 羽毛笔从文档中解析

存储的文档不携带其笔的字节，只携带一个引用： `doc.quillRef` 是 `name@version`，坚持降价本身。分辨率是 **主机代码**：读取ref，将其映射到a `Quill`、开放。没有表面分辨率，因此每个文档按结构保留一个分辨率。

```ts
const doc = Document.fromMarkdown(stored); // Document, from the gate above
const quill = await registry.getQuill(doc.quillRef); // your ref → Quill mapping
const session = await new Engine().open(quill, doc);
```

`@quillmark/quiver` 有这样的登记处吗？它交还的羽毛笔是这样的吗 **借来的**：它被缓存到每个引用并与每个调用者共享，因此释放它会影响下一个调用者。

打开命名为 **不同的** Quill是相同的序列，顺序是：解析新的ref，交换编辑器的道具，释放被替换的句柄，然后在编辑器后面打开下一个会话。 `<VisualEditor>` 在新的键盘上重新设置键 `doc` (见下图); `<Preview>` 按金额交换(`{#key session}`）。

## 多么值得信任的文件啊

**一、 `Document` 也许是读者没有写的** —一个导入、一个共享文件、来自多租户存储的一行—以及两个属性，无论如何都要占用一个属性。Markdown变成了类型化节点，然后是DOM，而不是标记字符串：这个包没有 `{@html}`,没有 `innerHTML` 也没有 `eval`，因此文档的文本不能变成标记。并且链接仅为 `http`， `https`， `mailto`， `tel`， `ftp`，或者一个根本不命名方案的值；每一个其他方案绘制作为一个惰性跨度，在可编辑的表面和提示卡，油漆 `$ext.editor.tips` 以外的任何 `contenteditable`． 这个门是渲染的，而不是模型的，所以拒绝的href停留在标记上并往返：打开文档永远不会编辑它。

留给你的是文本边缘和参考。两端的降价是主人的，而且 `doc.quillRef` 是 **文档的** Own -存储的文件命名为打开它的笔，因此ref→ `Quill` 上面的映射是它可以到达的模板的边界。

## 预览

`createPreview` 提供会话忽略的层：视口、DOM、DPR、点击映射。这是一种纯粹的观点：它从不呼唤 `session.apply`;您驱动编辑并将结果交给它 `ChangeSet`.

```ts
import { createPreview } from '@quillmark/svelte/preview';

const preview = createPreview(session, {
    container: document.querySelector('#preview')!,
    onPick: (at) => {
        /* a click resolved to an address, with a caret where the compile has one */
    },
    onError: (err) => {
        /* a page paint the backend refused; the preview shows its error state either way */
    }
});

// after an edit lands on `doc`, from any source:
preview.refresh(session.update(doc)); // repaint dirtyPages ∩ visible
```

苗条的, `<Preview {session} onPick={…} />` 暴露相同的动词(`refresh`， `scrollToField`， `focusPosition`， `endFollow`， `setZoom`)通过 `bind:this`.

`<Preview>` 结合 **一次**，在mount：交换 `session`， `margin`， `onPick`， `onError` 或者 `strings` 原地不改变屏幕和报告 `rebind-ignored` 通过 `onError` 在 `dev` 严重性，命名道具。重新挂载交换(`{#key session}`）; 驱动就地编辑 `refresh(change)`. `class` 和 `style` 是例外，登陆到根元素并保持存活。

## 可视化编辑器

`<VisualEditor>` 是一个文档上许多小型编辑器的联合组合：每个内容页都是ProseMirror散文表面，每个标量都是一个表单控件，是编辑器自己的卡片。它直接提交给 `doc` 句柄。

```svelte
<script lang="ts">
    import { VisualEditor } from '@quillmark/svelte/visual';
</script>

<VisualEditor
    {doc}
    {quill}
    onChange={(change) => {
        /* an edit LANDED; `change.source` is 'prose' | 'field' | 'structure' */
    }}
    onActiveLeafChange={(active) => {
        /* the active leaf: `active.field` (a DocPath), and `active.cardId` for its card */
    }}
    onCaretMove={(at) => {
        /* the caret moved to `at.field` (a DocPath) at `at.pos` (UTF-16) */
    }}
    onError={(err) => {
        /* a failure the editor recovered from; editing continues */
    }}
    diagnostics={external}
/>
```

换一种方式 `doc` 还有编辑 **re-keys本身**：每个叶子都重新装入新的句柄，id状态、提交-错误映射和活动地址重新装入。 `quill` 不是那个键的一部分-模式在每次派生时都会被重新读取，所以羽毛笔交换会自己重新投射；交换它 _没有_ 医生报告说 `rebind-ignored`.

### 从外面开车

`bind:this` 到达卡片头调用的相同动词，因此工具栏、命令调色板或快捷方式不需要第二个路径进入文档。每个人都通过报告 `onChange` 就像滴答声一样。

```svelte
<script lang="ts">
    let editor: ReturnType<typeof VisualEditor> | undefined = $state();
    let activeCard = $state<string | undefined>();
</script>

<VisualEditor
    bind:this={editor}
    {doc}
    {quill}
    onActiveLeafChange={(a) => (activeCard = a.cardId)}
/>

<button onclick={() => editor?.insertCard('indorsement')}>Add indorsement</button>
<button onclick={() => activeCard && editor?.moveCard(activeCard, -1)}>Move up</button>
<button onclick={() => editor?.focusField('main.subject')}>Jump to subject</button>
```

`insertCard` 把新卡递回去 `cardId`； `removeCard`， `moveCard` 和 `setKind` 拿一个。 `focusField` 到达任何已挂载的字段：一个散文页获取其视图的焦点，一个表单控件获取其标签上的点击所需要的相同切换，无论哪种方式，持有它的组都会首先显示。 `washField` 在一个字段上播放相同的到达洗涤，没有其他内容，主持人写道：焦点，滚动和封闭组保持原样，封闭组中的字段没有洗涤。卡钥匙或路径表面不支持是一个no-op报告 `target-unknown` 通过 `onError` 在 `dev` 严重性。

### 措辞

这个包裹是英文的，用一条缝代替。 `strings` 是键和 **部分**：设置你要翻译的内容，其余的保留在包中。几个关键是 **访问的名字** 而不是装饰（卡片控制，添加触发器），因此未翻译的表面将错误的语言读取到屏幕阅读器。 `DEFAULT_VISUAL_STRINGS` 是英文，导出后你可以对照它进行写作。 `<Preview>` 它有自己的三键 `strings` 当没有东西可画时，它所显示的状态。

```svelte
<VisualEditor
    {doc}
    {quill}
    strings={{
        cardDelete: 'Supprimer la carte',
        addCard: '+ Ajouter une carte'
    }}
    formatDiagnostic={(d) =>
        d.code === 'validation::type_mismatch' ? `${d.path} : type incorrect` : undefined}
/>
```

`formatDiagnostic` 得到全部 `Diagnostic` 并返回 `undefined` 来传递自己的信息。验证和编辑诊断信息 `code`， `path` 和 `args`;解析错误和渲染警告在英文文本中包含详细信息，并保持原样([VISUAL\_EDITOR.md](https://npmcn.com/package/%40quillmark/svelte/prose/canon/VISUAL_EDITOR.md)）。

### 字体

可以将一系列文本设置为字体族 **主人** 供应,拼写 `<qm-span family="…">` 在减价。使用字体文件(`.ttf`， `.otf`， `.ttc`， `.otc`)，并将它列出的脸交给编辑器：然后弹出窗口提供每个族的格式，编辑器将这些脸注册到页面中，这样运行就可以绘制预览设置的脸。

```ts
const carlito = new Uint8Array(await (await fetch('/fonts/Carlito-Regular.ttf')).arrayBuffer());
const engine = new Engine({ fonts: [carlito] });
```

```svelte
<script lang="ts">
    let fonts = $state<FontFace[]>([]);
    engine.fontFaces(quill).then((faces) => (fonts = faces));
</script>

<VisualEditor {doc} {quill} {fonts} />
```

[`@quillmark/fonts`](https://npmcn.com/package/%40quillmark/fonts) 是每个Quillmark客户端构建其引擎的库，因此一个家族的文档名称在每个文档中设置相同的方式；主机自己的文件跟踪它。 `fontFaces` 加载渲染构建，如 `open` 是的，所以先安装编辑器，让面孔到达它后面。文档已经设置了一个没有库的编辑，选择器提供了它，所以它可以被清除。

### 重新编译

`onChange` 信号是要重新编译的，它又覆盖了吗 **三条车道**：一个散文按键，一个标量写入，一个卡片操作。 `onCaretMove` 是一个 _选择_ 信号，而不是更改信号：它在一个空箭头键上触发，因此挂起的重新编译会在每个箭头键上重新编译。

```ts
let timer: ReturnType<typeof setTimeout> | undefined;

onChange: (change) => {
    // a structure op happens once per gesture, so it applies at once;
    // prose and field edits arrive per keystroke, so they debounce.
    if (change.source === 'structure') recompileNow();
    else {
        clearTimeout(timer);
        timer = setTimeout(recompileNow, 120);
    }
};

function recompileNow() {
    timer = undefined;
    const change = session.update(doc);
    preview.refresh(change);
    source.refresh();
    diagnostics = [...session.warnings]; // → the editor's `diagnostics` prop
}
```

`session.warnings` 是一个 **Svelte不跟踪的句柄上的getter**：每次应用时拉出它，否则编辑器会显示前一次编译的错误。

调度是主机策略，因此包不提供调度程序。义务：您自己的编辑（导入，撤销，直接） `doc` Write)通过相同的调用重新编译，因为没有轮询文档；编译器重新读取诊断；然后拆除计时器 **之前** 释放手柄。

## 插入式桥

那座桥住在 **消费者** 层和是可选的；编辑不知道预览，预览也不知道编辑。两个跳点都是传递：两个表面已经使用一个地址语法，即规范 `DocPath`.

```ts
// preview → editor: a click resolved to an address lands in the field it names.
onPick: (at) => visualEditor.setCaret(at);

// editor → preview: a caret move scrolls the preview to follow it,
// and a focus into a leaf with no caret to report ends the follow.
onCaretMove: preview.focusPosition;
onActiveLeafChange: preview.endFollow;
```

编辑器→预览跳转或两者都不跳转。控件报告它的焦点，没有插入符号——没有日期字段可以命名的偏移量——所以如果没有插入符号，预览会一直跟随焦点离开的叶子，并且每次重新编译都会将窗格拉回它。一个散文页用它的下一个插入符号重新开始跟随。为自己的chrome调用读取活动叶子的主机 `endFollow` 从它的处理程序。

pick带有一个插入符号，编译器在其中跟踪该点下的内容，而只有一个字段，它只跟踪位置——一个标量，印版打印而不跟踪。 `setCaret` 两者皆有：缺席 `pos` 揭示和聚焦领域，这是整个什么点击板上放置的墨水可以意味着。该地址可以命名一个数组元素(`main.keywords[0]`)，它落在那一行。

持有 `Addr` 它自己的地图 `fieldPathForAddr` (从 `@quillmark/svelte/core`）. playground应用的分窗格路由是完整的引用shell：一个会话，两个桥接方向，编辑后的预览，以及内联路由的诊断。

## 错误

每个表面 `onError`． 它向地面报告故障 **从**提交边界被拒绝，卡片操作被抛出，页面绘制失败，序列化被抛出。他们都没有停止编辑。没有电线，每个都落在控制台。

```ts
onError: (err) => {
    // err.code: 'commit-refused' | 'paint-failed' | … (see EditorErrorCode)
    // err.severity: 'error' (runtime) | 'dev' (a contract violation)
    // err.cause: whatever was thrown, unwrapped
    if (err.severity === 'error') telemetry.capture(err);
};
```

这不是诊断通道。一个 `Diagnostic` 是关于 **文档** 在它所属的领域里画画；一个 `EditorError` 是关于 **表面** 画不出任何东西。被拒绝的标量提交会产生这两种结果。

## 规范的减价

`doc.toMarkdown()` 返回文档的规范Quillmark标记，并将其重新解析为等号 `Document`． 一个只读镜像是在 `<pre>`，在每次编辑完成后重新运行，所以它不需要这个包的表面：

```ts
el.textContent = doc.toMarkdown();
```

## 主题

表面承载着一种中性的、可超越的视觉基线：一组 `--qm-*` 您在任何祖先上重写的CSS自定义属性。看到 [`THEMING.md`](https://npmcn.com/package/%40quillmark/svelte/THEMING.md).

## 开发

这个包是一个工作空间 [`quillmark-js`](https://npmcn.com/package/);门是根的，建筑就住在里面 [`prose/`](https://npmcn.com/package/%40quillmark/svelte/prose/).

```sh
npm run build -w packages/svelte   # svelte-package → dist/
npm run test  -w packages/svelte   # Vitest
npm run check -w packages/svelte   # svelte-check
npm run dev                        # the playground, from the root
```

## 常见问题

### @quillmark/svelte 是什么？

编辑器，实时预览和源表面在Quillmark WASM会话。

### 如何安装 @quillmark/svelte？

在项目目录运行 npm install @quillmark/svelte，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：svelte（^5\.33\.0）、@quillmark/wasm（\>=0\.134\.1-0 \<0\.135\.0-0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @quillmark/svelte 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @quillmark/svelte 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @quillmark/svelte 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=22。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @quillmark/svelte 可以商用吗？

@quillmark/svelte 使用 Apache-2\.0 许可。宽松许可：允许商业使用、修改和再分发，并附带明确的专利授权；分发时需保留许可与 NOTICE 文件，修改过的文件要注明改动。以上为概括，不构成法律意见，请以许可原文为准。

### @quillmark/svelte 还在维护吗？

最新版本 0\.30\.0 发布于 2026/11/02（1 天前）；至今共发布 48 个版本，其中过去 12 个月发布了 48 个；首次发布于 2026/08/07。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

