# 快速开始

用公开包入口创建第一个编辑器，完成编辑、读取与清理。

这一页把 Markdown 编辑区放入你的页面：先创建实例，输入内容，再读取原文并在退出时释放。

## 1. 准备依赖

当前开发版通过工作区包或本地打包产物消费，尚未承诺公共 npm 稳定发布。在包含 MSFE 包的 pnpm 工作区中，为你的应用加入依赖：

```sh
pnpm add '@quirekit/msfe-core@workspace:*' '@quirekit/msfe-starter-kit@workspace:*'
```

Vue 应用再添加 `@quirekit/msfe-vue`；React 应用使用 `@quirekit/msfe-react`。按应用已有的框架版本安装相应 peer dependencies。

本地 tarball 应通过包管理器安装，并完整保留 `dist` 和 `types` 发布目录。StarterKit 的类型入口会带入它安装的插件命令声明，使用 `editor.commands.toggleBold()` 等方法不需要再为类型单独导入插件。

普通安装包与本仓库开发环境的依赖配置可能不同；当前发布准备尚未完成，长期运行和图表行为请以所取得版本的支持范围为准。

## 2. 创建实例

提供一个已挂载的 DOM 元素。`markdown` 是初始文本，`extensions` 决定安装哪些能力。

```ts
import { createMsfeEditor } from '@quirekit/msfe-core'
import { StarterKit } from '@quirekit/msfe-starter-kit'

const parent = document.querySelector<HTMLElement>('#editor')!
const editor = createMsfeEditor({
  parent,
  markdown: '# 你好，Markdown\n\n开始写作。',
  mode: 'live',
  extensions: [StarterKit],
})

// 在保存动作中读取；无需每次输入都序列化全文。
const markdown = editor.getMarkdown()

// 宿主离开页面时清理。
// editor.destroy()
```

## 3. 加入 Vue 与操作按钮

下面增加模式切换、撤销和读取按钮。先输入，再切换源码与实时模式；点击“读取 Markdown”查看当前原文。

源码里的 `basic/editor.vue` 负责界面和实例，`shared/appearance.ts` 配置外观，`shared/messages.ts` 提供示例文案，`shared/types.ts` 定义语言类型。只需要查看某一部分时，在源码页签选择对应文件即可。

### basic/editor.vue

```vue
<script setup lang="ts">
import { computed, ref, shallowRef } from "vue";
import { useMsfeEditor, VueNodeViewHost } from "@quirekit/msfe-vue";
import { StarterKit } from "@quirekit/msfe-starter-kit";
import { QuireButton, QuireSegmented, QuireProseSource } from "@quirekit/ui/vue";
import { Appearance } from "../shared/appearance";
import { exampleMessages } from "../shared/messages";
import type { ExampleLocale } from "../shared/types";

const props = withDefaults(defineProps<{ /** Language supplied by the application. */ locale?: ExampleLocale }>(), { locale: "zh-CN" });
const text = computed(() => exampleMessages[props.locale]);
const initial = text.value.initial;
const parent = shallowRef<HTMLElement | null>(null);
const mode = ref("live");
const markdown = ref<string>();
const items = computed(() => [{ value: "live", label: text.value.live }, { value: "source", label: text.value.source }, { value: "locked", label: text.value.locked }]);

// One editor instance owns source, selection and history for all three modes.
const { editor, host } = useMsfeEditor(parent, () => ({ markdown: initial, mode: "live",
  extensions: [StarterKit, Appearance], label: text.value.editor }));

/** @param value - Mode selected in the shared control. */
function changeMode(value: string | null | undefined): void {
  if (value === "source" || value === "live" || value === "locked") { mode.value = value; editor.value?.setMode(value); }
}
/** Serializes only for an explicit save/read action; typing never mirrors the entire source. */
function readMarkdown(): void { markdown.value = editor.value?.getMarkdown(); }
/** Starts a fresh document and history after the user explicitly resets this example. */
function reset(): void { editor.value?.replaceDocument(initial, "example-reset"); markdown.value = undefined; }
</script>
<template>
  <div class="grid gap-4 p-qk-surface">
    <div class="flex flex-wrap items-center justify-between gap-3">
      <QuireSegmented :model-value="mode" :items="items" :label="text.modes" size="sm" @update:model-value="changeMode" />
      <div class="flex gap-2">
        <QuireButton size="sm" variant="ghost" :disabled="mode === 'locked'" @click="editor?.undo()">{{ text.undo }}</QuireButton>
        <QuireButton size="sm" variant="ghost" @click="reset">{{ text.reset }}</QuireButton>
        <QuireButton size="sm" variant="outline" @click="readMarkdown">{{ text.readMarkdown }}</QuireButton>
      </div>
    </div>
    <div ref="parent" class="overflow-hidden rounded-qk-control border border-qk-border-subtle" />
    <VueNodeViewHost :host="host" />
    <QuireProseSource v-if="markdown !== undefined" :code="markdown" language="markdown" filename="document.md" :max-height="260" />
  </div>
</template>

```

### shared/appearance.ts

```ts
import { Extension } from "@quirekit/msfe-core";
import { EditorView } from "@quirekit/cm-view";
import { Prec } from "@quirekit/cm-state";
import { HighlightStyle, syntaxHighlighting } from "@quirekit/cm-language";
import { tags } from "@lezer/highlight";
import { themeVariableReferences as token } from "@quirekit/ui";

/** Consumer-owned theme; source semantics stay in the installed feature packages. */
export const Appearance = Extension.create({ name: "documentationAppearance",
  /** @returns Scoped typography, surfaces and syntax colors from the host design system. */
  addCodeMirrorExtensions() {
    return Prec.high([
      EditorView.theme({
        "&": { ...token.typography.body, color: token.color.text.primary, backgroundColor: token.color.surface.page, minHeight: "180px" },
        ".cm-scroller": { fontFamily: "inherit", lineHeight: "1.8" },
        ".cm-content": { padding: token.spacing.surface, caretColor: token.color.text.primary },
        ".cm-line": { padding: "0" },
        ".cm-gutters": { color: token.color.text.tertiary, backgroundColor: token.color.surface.subtle, border: "none" },
        ".cm-activeLine, .cm-activeLineGutter": { backgroundColor: "transparent" },
        ".cm-selectionBackground, &.cm-focused .cm-selectionBackground": { backgroundColor: token.color.selection.document },
        ".cm-cursor": { borderLeftColor: token.color.text.primary },
        ".msfe-h1": { ...token.typography.pageTitle },
        ".msfe-h2, .msfe-h3": { ...token.typography.sectionTitle },
        ".msfe-heading": { paddingBlock: token.spacing.inline },
        ".msfe-link": { color: token.color.text.accent },
        ".msfe-code-line, .msfe-inline-code": { ...token.typography.code },
        ".consumer-status": { verticalAlign: "middle" },
        "&.cm-focused": { outline: "none" },
      }),
      syntaxHighlighting(HighlightStyle.define([
        { tag: tags.heading, color: token.color.text.primary, fontWeight: "600" },
        { tag: tags.strong, fontWeight: "700" },
        { tag: tags.emphasis, fontStyle: "italic" },
        { tag: [tags.link, tags.url], color: token.color.text.accent },
        { tag: tags.processingInstruction, color: token.color.text.tertiary },
        { tag: tags.monospace, color: token.color.text.secondary },
      ])),
    ]);
  },
});

```

### shared/messages.ts

```ts
const chinese = {
  editor: "Markdown 示例编辑器", modes: "编辑器模式", source: "源码", live: "实时预览", locked: "只读",
  undo: "撤销", redo: "重做", reset: "重置", readMarkdown: "读取 Markdown", readingPreview: "阅读预览", insertStatus: "插入状态", done: "已完成", todo: "待处理",
  nativeEditor: "原生 DOM 示例编辑器", statusEditor: "自定义状态编辑器",
  initial: "## 把想法写下来\n\n在这里编辑 **Markdown**，也可以切换到源码。\n\n- [x] 保留 Markdown 原文\n- [ ] 试试修改这一行\n",
  nativeInitial: "## 原生 DOM 接入\n\n这个实例直接使用 `createMsfeEditor()`。\n",
  statusInitial: "## 自定义状态\n\n设计评审：[[todo]]\n\n点击状态按钮，然后读取 Markdown 或撤销。\n",
};
const english: { [Key in keyof typeof chinese]: typeof chinese[Key] } = {
  editor: "Markdown example editor", modes: "Editor mode", source: "Source", live: "Live", locked: "Locked",
  undo: "Undo", redo: "Redo", reset: "Reset", readMarkdown: "Read Markdown", readingPreview: "Reading preview", insertStatus: "Insert status", done: "Done", todo: "Pending",
  nativeEditor: "Native DOM example editor", statusEditor: "Custom status editor",
  initial: "## Write your ideas\n\nEdit **Markdown** here, or switch to source.\n\n- [x] Preserve the Markdown source\n- [ ] Try editing this line\n",
  nativeInitial: "## Native DOM integration\n\nThis instance uses `createMsfeEditor()` directly.\n",
  statusInitial: "## Custom status\n\nDesign review: [[todo]]\n\nClick the status, then read the Markdown or undo.\n",
};
export const exampleMessages = { "zh-CN": chinese, en: english };

```

### shared/types.ts

```ts
/** Example host language; independent of the documentation router. */
export type ExampleLocale = "zh-CN" | "en";

```

## 接下来

阅读 [Vue 接入](/guide/vue)或 [React 接入](/guide/react)，了解框架生命周期与自定义组件。需要接入现有存储时，继续看[读取与保存](/guide/saving)。

## 完成接入前检查

给 parent 实际宽度和合理最小高度；给编辑区提供 label；把清理接到路由/组件生命周期。默认功能皮肤不等于你的产品主题，按 [主题教程](/customization/theme) 接入设计系统。

工作区代码直接引入时注意 CodeMirror state/view 的单实例依赖。生产应用不要从 `packages/.../src` 私有路径导入；使用公共包入口和自己的构建产物。本站源码 alias 只是仓库开发环境配置。

接入完成后验证输入、模式切换、只读、保存和卸载。然后依据 [功能目录](/features/catalog) 选择可选功能，而不是在第一步复制整个示例应用。
