# 文档结构

使用 Heading、Paragraph 和 List 组织内容。

适用范围：本页通用结构用于 Classic 产品中立模型与 Block。Classic Word 使用自己的段落、标题和编号模型；Block 的 Word 表格、编号需独立适配。见[编辑器适用范围](/plugins/compatibility)。

标题与列表是文档节点，不是给一段 DOM 加上临时样式。本页使用 StarterKit 中的标准结构扩展（MIT）。单独安装时使用 `@quirekit/extension-heading`、`@quirekit/extension-paragraph` 和 `@quirekit/extension-list`，保留对应 Schema 所需的节点。

## 配置与命令

```ts
StarterKit.configure({ heading: { levels: [1, 2, 3] } })
editor.chain().focus().toggleHeading({ level: 2 }).run()
editor.chain().focus().toggleBulletList().run()
```

## 实际效果

将光标放入正文，点击「二级标题」或「项目列表」。输入新内容、按 Enter 继续结构，再用撤销恢复。下载 JSON 可看到真实节点变化。

### plugins/structure.vue

```vue
<script setup lang="ts">
import CoreEditor from "../core/editor.vue";
defineProps<{ /** Page language. */ locale: "zh-CN" | "en" }>();
</script>
<template><CoreEditor :locale="locale" tools="structure" /></template>

```

### core/editor.vue

```vue
<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, ref, shallowRef } from "vue";
import type { Editor } from "@quirekit/core";
import { QuireButton } from "@quirekit/ui/vue";
import { createExampleEditor } from "./create";
import { downloadJson } from "../shared/download";
import "@quirekit/pm-view/style/prosemirror.css";

const props = withDefaults(defineProps<{
  /** Initial copy and control language. */ locale?: "zh-CN" | "en";
  /** Visible controls for the capability being demonstrated. */ tools?: "formatting" | "structure";
}>(), { locale: "zh-CN" });
const host = ref<HTMLElement>();
const instance = shallowRef<Editor>();
const copy = computed(() => props.locale === "en"
  ? { bold: "Bold", undo: "Undo", save: "Download JSON", label: "Editable document" }
  : { bold: "加粗", undo: "撤销", save: "下载 JSON", label: "可编辑文档" });
onMounted(() => { if (host.value) instance.value = createExampleEditor(host.value, props.locale); });
onBeforeUnmount(() => instance.value?.destroy());
/** Downloads the actual editor state instead of a second copy maintained by the UI. */
function download(): void { if (instance.value) downloadJson(instance.value.getJSON(), "quirekit-document.json"); }
</script>
<template>
  <div class="core-example">
    <div class="core-example-tools">
      <QuireButton variant="outline" @click="instance?.chain().focus().toggleBold().run()">{{ copy.bold }}</QuireButton>
      <template v-if="tools === 'formatting'">
        <QuireButton variant="outline" @click="instance?.chain().focus().toggleItalic().run()">{{ locale === 'en' ? 'Italic' : '斜体' }}</QuireButton>
        <QuireButton variant="outline" @click="instance?.chain().focus().toggleUnderline().run()">{{ locale === 'en' ? 'Underline' : '下划线' }}</QuireButton>
      </template>
      <template v-if="tools === 'structure'">
        <QuireButton variant="outline" @click="instance?.chain().focus().toggleHeading({ level: 2 }).run()">{{ locale === 'en' ? 'Heading 2' : '二级标题' }}</QuireButton>
        <QuireButton variant="outline" @click="instance?.chain().focus().toggleBulletList().run()">{{ locale === 'en' ? 'Bullet list' : '项目列表' }}</QuireButton>
      </template>
      <QuireButton variant="outline" @click="instance?.chain().focus().undo().run()">{{ copy.undo }}</QuireButton>
      <QuireButton variant="outline" @click="download">{{ copy.save }}</QuireButton>
    </div>
    <div ref="host" class="core-example-document" role="group" :aria-label="copy.label" />
  </div>
</template>
<style scoped>
.core-example { padding: 20px; background: var(--qk-color-surface-page); }
.core-example-tools { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 20px; }
.core-example-document { border: 1px solid var(--qk-color-border-default); border-radius: 6px; padding: 20px; text-align: start; }
.core-example-document :deep(.ProseMirror) { min-height: 180px; line-height: 1.8; }
/* A writing surface expresses focus through its caret and selection, without a second frame. */
.core-example-document :deep(.ProseMirror:focus), .core-example-document :deep(.ProseMirror:focus-visible) { outline: none; box-shadow: none; }
.core-example-document :deep(h2) { font-size: 22px; margin: 0 0 12px; }
.core-example-document :deep(p) { margin: 8px 0; }
</style>

```

### core/create.ts

```ts
import { Editor } from "@quirekit/core";
import { StarterKit } from "@quirekit/starter-kit";
import { DomViewExtension } from "@quirekit/dom-view";
import { writingShortcuts } from "./shortcuts";

/**
 * Mounts a minimal editor with the Tiptap-style Core API and an explicit native DOM view.
 * @param element - Browser-owned mount target.
 * @param locale - Language for initial sample copy only.
 * @returns The caller-owned editor; destroy it when its host unmounts.
 */
export function createExampleEditor(element: HTMLElement, locale: "zh-CN" | "en"): Editor {
  return new Editor({
    element,
    mode: "dom",
    extensions: [StarterKit, DomViewExtension, writingShortcuts],
    content: { type: "doc", content: [
      { type: "heading", attrs: { level: 2 }, content: [{ type: "text", text: locale === "en" ? "Start with your words." : "从你写下的文字开始。" }] },
      { type: "paragraph", content: [{ type: "text", text: locale === "en" ? "Select some text, make it bold, or add a new paragraph. This is a real QuireKit Core editor." : "选择一段文字，加粗，或另起一段。这里运行的是真实的 QuireKit Core 编辑器。" }] },
    ] },
  });
}

```

### core/shortcuts.ts

```ts
import { Extension } from "@quirekit/core";

/** A host-authored behavior extension using the same typed command registry as the toolbar. */
export const writingShortcuts = Extension.create({
  name: "writingShortcuts",
  /** @returns A keyboard binding that participates in the editor's existing history. */
  addKeyboardShortcuts() {
    return { "Mod-Shift-b": () => this.editor?.commands.toggleBold() ?? false };
  },
});

```

### shared/download.ts

```ts
/**
 * Downloads canonical JSON through a short-lived browser URL.
 * @param document - Serializable canonical content.
 * @param name - Download filename chosen by the host.
 */
export function downloadJson(document: unknown, name: string): void {
  const url = URL.createObjectURL(new Blob([JSON.stringify(document, null, 2)], { type: "application/json" }));
  const link = window.document.createElement("a");
  link.href = url;
  link.download = name;
  link.click();
  window.setTimeout(() => URL.revokeObjectURL(url), 1000);
}

```

Word 的编号定义、段落样式、分页与 OOXML 往返由 Word 包处理，不应把通用列表演示当作这些能力的等价物，见 [Word 能力](/plugins/word)。

## 独立插件参考

[Heading](/plugins/reference/heading)、[Paragraph](/plugins/reference/paragraph)、[ListKit](/plugins/reference/list-kit)。其他能力见[全部插件](/plugins)。
