# 模板目录

从公共模板清单浏览 25 个真实示例，打开独立副本继续编辑。

当前提供 **25 个模板**：Mermaid 10 个、PlantUML 11 个、Graphviz / DOT 4 个。模板是语法起点，手写源码不受下拉列表中的图形种类限制。

下面的目录直接调用 `textDiagramTemplates(locale)`，与三个编辑器同源。选择后查看实际源码，点击“试用这个模板”打开独立副本，可预览与下载；这个临时副本不会写进在线工作台的本地保存记录。

### templates/catalog.vue

```vue
<script setup lang="ts">
import { computed, onBeforeUnmount, ref } from "vue";
import { QuireButton, QuireSelect } from "@quirekit/ui/vue";
import { createTextDiagram, textDiagramTemplates } from "@quirekit/text-diagram";
import { openTextDiagramDialog, type TextDiagramDialogHandle } from "@quirekit/text-diagram-editor";
import "@quirekit/text-diagram-editor/styles.css";
import { createDiagramSession } from "../recipes/session";
import { exampleText } from "../shared/locale";
import { dialogTheme } from "../shared/theme";

const props = defineProps<{ /** Names and source labels of the unmodified template catalog. */ locale?: string }>();
const t = computed(() => exampleText(props.locale));
const language = ref("mermaid"), selected = ref<string>();
const languages = [{ value: "mermaid", label: "Mermaid" }, { value: "plantuml", label: "PlantUML" }, { value: "dot", label: "Graphviz / DOT" }];
const templates = computed(() => textDiagramTemplates(props.locale).filter(item => item.language === language.value));
const active = computed(() => templates.value.find(item => item.id === selected.value) ?? templates.value[0]!);
const items = computed(() => templates.value.map(item => ({ value: item.id, label: item.label })));
const lifetime = new AbortController();
let dialog: TextDiagramDialogHandle | undefined;

/** Opens a disposable editable copy; the library's template data remains immutable. */
function open(): void {
  dialog?.close();
  const sample = active.value, session = createDiagramSession(createTextDiagram(`template-${sample.id}`, sample.language, sample.source));
  dialog = openTextDiagramDialog({ request: session.capture(), ownerDocument: document, locale: props.locale, theme: dialogTheme(document), signal: lifetime.signal });
}
onBeforeUnmount(() => lifetime.abort());
</script>
<template>
  <section class="template-catalog" :aria-label="t.templates" data-template-catalog data-qk-not-prose>
    <div class="template-catalog__controls">
      <QuireSelect v-model="language" :items="languages" :label="t.language" width="170px" />
      <QuireSelect :model-value="active.id" :items="items" :label="t.template" width="190px" @update:model-value="selected = $event" />
      <QuireButton variant="outline" @click="open">{{ t.try }}</QuireButton>
    </div>
    <p>{{ t.selected }}</p>
    <ul><li v-for="item in templates" :key="item.id">{{ item.label }}</li></ul>
    <pre :aria-label="t.original">{{ active.source }}</pre>
  </section>
</template>
<style scoped>
.template-catalog { display:grid;gap:16px;min-width:0;padding:20px; }
.template-catalog__controls { display:flex;gap:12px;align-items:center;flex-wrap:wrap; }
p { margin:0;font-size:13px;color:var(--qk-color-text-secondary); }
ul { margin:0;display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:8px;font-size:13px;padding-inline-start:20px; }
pre { margin:0;padding:16px;max-height:380px;overflow:auto;background:var(--qk-color-surface-subtle);font-size:12px; }
</style>

```

### recipes/session.ts

```ts
import { readTextDiagram, serializeTextDiagram, type TextDiagramEmbed } from "@quirekit/text-diagram";
import type { DiagramSession } from "../shared/types";

/**
 * Creates a single-object host with compare-and-save semantics.
 * @param initial - Validated persisted object or new draft.
 * @param persist - Optional synchronous storage write; throwing leaves the baseline unchanged.
 * @returns A host whose requests cannot overwrite a newer accepted revision.
 */
export function createDiagramSession(initial: TextDiagramEmbed, persist?: (next: TextDiagramEmbed) => void): DiagramSession {
  let current = readTextDiagram(initial);
  return {
    /** @returns The immutable accepted revision. */
    read() { return current; },
    /** @returns A detached baseline with explicit commit and release callbacks. */
    capture() {
      const baseline = current, expected = serializeTextDiagram(baseline);
      let released = false;
      return {
        document: baseline,
        /** @param proposal - Source and preview proposed by the workbench. @returns Atomic host acceptance. */
        commit(proposal) {
          if (released) return "missing";
          if (serializeTextDiagram(current) !== expected) return "conflict";
          const next = readTextDiagram(proposal);
          if (next.id !== baseline.id || next.document.revision !== baseline.document.revision + 1 || !next.preview) {
            throw new TypeError("Invalid diagram save");
          }
          // Storage must succeed before publishing the new revision to the UI.
          persist?.(next);
          current = next;
          return "saved";
        },
        /** Closes this target without changing the accepted object. */
        release() { released = true; },
      };
    },
  };
}

```

### shared/types.ts

```ts
import type { TextDiagramEditRequest, TextDiagramEmbed, TextDiagramSnapshot, TextDiagramRaster } from "@quirekit/text-diagram";

/** Minimal synchronous host; durable storage belongs to its caller. */
export interface DiagramSession {
  /** @returns The last accepted immutable object. */
  read(): TextDiagramEmbed;
  /** @returns A new edit request capturing the current baseline. */
  capture(): TextDiagramEditRequest;
}

/** Two output formats generated from exactly the same source. */
export interface DiagramFiles {
  /** Sanitized vector snapshot. */
  readonly vector: TextDiagramSnapshot;
  /** Bounded PNG projection. */
  readonly raster: TextDiagramRaster;
}

```

### shared/locale.ts

```ts
/** @param locale - Host interface language. @returns Demonstration labels; source is never translated. */
export function exampleText(locale = "zh-CN") {
  return locale.startsWith("en") ? {
    saved: "Saved in this browser", restored: "Restored the last saved diagram", cancelled: "Draft cancelled; showing the last saved version",
    unsaved: "No saved diagram yet", storage: "The saved diagram could not be read. You can edit a new draft; saving will replace that local entry.",
    persistence: "Save diagram stores the source and PNG in this browser. Unsaved edits are discarded when you leave this page.",
    result: "Last saved Markdown", json: "Download saved JSON", revision: "Revision", open: "Open diagram editor",
    close: "Editor closed", memory: "This dialog example keeps accepted changes until you leave the page.",
    templates: "Template catalog", language: "Language", template: "Template", try: "Try this template",
    original: "Template source", selected: "The dialog uses a detached copy; the template stays unchanged.",
  } : {
    saved: "已保存到当前浏览器", restored: "已恢复上次保存的图形", cancelled: "草稿已取消，已回到上次保存的版本",
    unsaved: "尚未保存图形", storage: "无法读取已保存的图形。可以编辑新草稿；保存将替换这一条本地记录。",
    persistence: "“保存图形”将源码与 PNG 保存在当前浏览器。离开本页会丢弃尚未保存的修改。",
    result: "上次保存的 Markdown", json: "下载已保存 JSON", revision: "修订号", open: "打开绘图编辑器",
    close: "编辑器已关闭", memory: "此弹窗示例在离开页面前保留已保存的修改。",
    templates: "模板目录", language: "绘图语言", template: "模板", try: "试用这个模板",
    original: "模板源码", selected: "弹窗使用独立副本，不会修改原模板。",
  };
}

```

### shared/theme.ts

```ts
import { createTheme, type QuireKitTheme } from "@quirekit/ui";

/** @param owner - Document with a host-controlled CSS color scheme. @returns Theme snapshot for an independent dialog root. */
export function dialogTheme(owner: Document): QuireKitTheme {
  const scheme = owner.defaultView!.getComputedStyle(owner.documentElement).colorScheme;
  return createTheme({ scheme: scheme === "dark" ? "dark" : "light" });
}

```

## 模板内容

- Mermaid：流程、时序、思维导图、类、状态、实体关系、甘特、饼图、用户旅程、Git 分支。
- PlantUML：时序、类、活动、用例、组件、状态、部署、对象、实体关系、思维导图、工作分解。
- DOT：有向、无向、分组架构、依赖关系。

`id` 是稳定标识，`label` 和源码示例中的人类文本跟随请求的语言。界面切换语言时，已经打开的草稿不会自动改写。共享工作台仅在用户明确点击“应用模板”后替换源码，替换可以撤销。

## 语法与引擎

更多语法可查阅 [Mermaid 文档](https://mermaid.js.org/intro/)、[PlantUML 文档](https://plantuml.com/)及 [Graphviz 文档](https://graphviz.org/documentation/)。上游支持的所有功能不等于此浏览器构建全部开放，尤其是外部资源与 PlantUML 可选库；本站边界以[支持范围](/reference/support)为准。
