宿主集成

把 SlexKit 接入 Markdown renderers、chat hosts、document viewers 和 custom platforms 的方法。

核心概念

Artifact

Artifact 是属于同一文档、消息或笔记的一组 Slex blocks。宿主用 artifactId 标识 artifact,并把相关 blocks 分组到同一个 runtime domain。

Trusted mode 下,runtime 会用 artifact ID 前缀化每个 block 的 namespace,避免跨文档状态污染。disposeArtifact() 会释放该 artifact 下的 namespace stores。

Secure mode 下,同一个 artifact 的所有 fences 会合并进一个 sandbox iframe。Runtime 使用 artifact slots,把渲染高度同步回原 Markdown placeholder containers。

Block

Block 是单个 Slex block,即一个可渲染单元。它包含:

Cleanup

每次 block mount 都返回 cleanup function。Block 被移除时宿主必须调用它。Artifact-level cleanup 调用 disposeArtifact(artifactId);全局清理调用 disposeAll()

MarkdownRuntimeHost

SlexKitMarkdownRuntimeHost 是 Markdown 宿主推荐 API。它处理 mode selection、artifact management 和 block lifecycle。

import {
  createSlexKitMarkdownRuntimeHost,
  getSlexKitMarkdownRuntimeHost,
  installSlexKitMarkdownRuntimeHost
} from "slexkit";

Interface

type SlexKitMarkdownRuntimeHost = {
  configure(options: Partial<SlexKitMarkdownRuntimeOptions>): void;
  getMode(): "trusted" | "secure";
  mountBlock(block: SlexKitMarkdownBlock): () => void;
  disposeBlock(container: HTMLElement): void;
  disposeArtifact(artifactId: string): void;
  disposeAll(): void;
};

SlexKitMarkdownBlock 包含 artifactIdblockIdsourcecontainerstateOnlythemedirlabelsexecutionModeSlexKitMarkdownRuntimeOptions 包含 modepolicyhostAdaptersecureFramethemedirlabelsexecutionMode

Global singleton

模块提供一个 global singleton:

const runtime = installSlexKitMarkdownRuntimeHost({
  mode: "secure",
  policy: { execution: { maxUnresponsiveMs: 30000 } },
  secureFrame: { runtimeUrl: "/slexkit.runtime.js" }
});

const runtime = getSlexKitMarkdownRuntimeHost();

当整个应用共享一套 runtime configuration 时使用 singleton。不同 host contexts 需要不同 policies 时,应创建独立 host。

Trusted mode integration

Trusted mode 在宿主页 realm 运行 Slex source,用于本地文档、应用生成内容或已审查 source。

const runtime = createSlexKitMarkdownRuntimeHost({
  mode: "trusted",
  theme: "host-shadcn"
});

const cleanup = runtime.mountBlock({
  artifactId: "doc-1",
  source: fenceSource,
  container: fenceContainer
});

runtime.disposeBlock(fenceContainer);
runtime.disposeArtifact("doc-1");
runtime.disposeAll();

Trusted mode 会自动用 artifact ID scope namespaces,例如 <artifactId>::<namespace>,避免不同文档污染彼此状态。

State-only blocks(无 layout,只有 g 更新)会自动通过 ingest() 导入。后续同 artifact 的 renderable blocks 可以读取这些状态。

Secure mode integration

Secure mode 在 sandbox iframe 中运行 Slex source,用于不可信或 agent-generated Markdown。

const runtime = createSlexKitMarkdownRuntimeHost({
  mode: "secure",
  policy: {
    execution: { maxUnresponsiveMs: 30000 }
  },
  secureFrame: {
    runtimeUrl: "/slexkit.runtime.js"
  },
  theme: "host-shadcn"
});

const cleanup = runtime.mountBlock({
  artifactId: "agent-msg-1",
  source: agentGeneratedDsl,
  container: fenceContainer
});

省略的 capability policies 默认拒绝访问。只有宿主明确启用时才添加 networktimeranimationcanvas policy objects。

Artifact slot 桥接

同一 secure artifact 中的多个 blocks 共享一个 sandbox iframe。第一个 block 作为 iframe anchor,其他 blocks 作为 slots;slot containers 接收来自 sandbox 的 position 和 height 更新。

<div id="fence-1"><!-- anchor: iframe rendered here --></div>
<div id="fence-2"><!-- slot: height synced from iframe --></div>
<div id="fence-3"><!-- slot: height synced from iframe --></div>

这样 fence 之间可以共享状态,同时所有执行仍限制在同一个 sandbox 中。

runtimeUrl requirements

runtimeUrl 必须把 SlexKit runtime 作为 ES module 提供,并返回:

Access-Control-Allow-Origin: *
Content-Type: text/javascript

Build output 包含 dist/runtime.jsslex copy-runtime 命令默认复制到 public/slexkit.runtime.js,便于保持 secure-frame URL 稳定。

Svelte custom Markdown host

SlexKit 官网使用 Svelte 和自定义 Markdown renderer,而不是一个独立发布的 Svelte Markdown adapter。对外 API 是 createSlexKitMarkdownRuntimeHost;Svelte 部分属于宿主代码。

import { mount, unmount } from "svelte";
import { createSlexKitMarkdownRuntimeHost } from "slexkit";
import MarkdownRenderer from "./MarkdownRenderer.svelte";

export function renderMarkdown(content, container, options = {}) {
  const runtimeHost = options.slexkitRuntimeHost ?? createSlexKitMarkdownRuntimeHost({
    mode: options.runtimeMode ?? "trusted",
    theme: "host-shadcn"
  });

  const app = mount(MarkdownRenderer, {
    target: container,
    props: {
      content,
      runtimeHost,
      artifactId: options.artifactId,
      slexkitRenderMode: options.slexkitRenderMode ?? "component"
    }
  });

  return () => unmount(app);
}

宿主拥有自己的 Markdown AST 或 Svelte renderer 时,可以把这个作为实现参考。不要把 site/markdown/* 当成单独版本化的包 API。

Streamdown / React integration

@slexkit/streamdown 提供 React/Streamdown custom renderer:

import { Streamdown } from "streamdown";
import { slexkitRenderer } from "@slexkit/streamdown";
import "@slexkit/theme-shadcn/style.css";
import "@slexkit/streamdown/style.css";

export function Message({ markdown }: { markdown: string }) {
  return (
    <Streamdown plugins={{ renderers: [slexkitRenderer] }}>
      {markdown}
    </Streamdown>
  );
}

该 renderer 处理 slex fences,支持 trusted 和 secure runtime modes,也可以委托给 shared Markdown runtime host instance。

流式输出时,trusted renderer 默认使用 streaming="repair":完整可解析源码会直接挂载;只缺 EOF 闭合符的前缀会以 preview 模式挂载;无法确定的前缀继续显示 placeholder;明确语法错误仍显示 diagnostics。streaming="stable" 只渲染已经可解析的前缀,streaming={false} 会等待 closing fence。Secure runtime 和 secure runtime host 不会在宿主 realm 执行 repaired prefix。

Tiptap integration

@slexkit/tiptap 提供 framework-free Tiptap CodeBlock extension:

import StarterKit from "@tiptap/starter-kit";
import { Markdown } from "@tiptap/markdown";
import { createSlexKitTiptapExtension } from "@slexkit/tiptap";
import "@slexkit/theme-shadcn/style.css";
import "@slexkit/tiptap/style.css";

const extensions = [
  StarterKit.configure({ codeBlock: false }),
  Markdown,
  createSlexKitTiptapExtension({ artifactId: "doc-1" })
];

这个 adapter 只接管 language === "slex"codeBlock node。非 Slex code block 保持普通 Tiptap code block;通过 Tiptap Markdown extension 导入/导出时,标准 <div class="slex-prerender-placeholder" data-slex-source="5q+P5LiqIGVkaXRvciDlrp7kvovpu5jorqTmi6XmnInkuIDkuKogdHJ1c3RlZCBNYXJrZG93biBydW50aW1lIGhvc3TjgILor6UgZWRpdG9yIOS4reeahCBibG9jayDlhbHkuqsgYXJ0aWZhY3QgSUTvvIzlm6DmraQgc3RhdGUtb25seSBmZW5jZSDlj6/ku6XkuLrlkI7nu63lj6/muLLmn5MgZmVuY2Ug5o+Q5L6b54q25oCB44CC5a6/5Li76ZyA6KaB5pi+5byP55Sf5ZG95ZGo5pyf5o6n5Yi25pe277yM5Y+v5Lul5Lyg5YWlIGBydW50aW1lSG9zdGAg5oiWIGBhcnRpZmFjdElkYOOAggoKIyMgT2JzaWRpYW4gaW50ZWdyYXRpb24KCuWumOaWuSBPYnNpZGlhbiDmj5Lku7bkvY3kuo4gPGh0dHBzOi8vZ2l0aHViLmNvbS9zbGV4a2l0L29ic2lkaWFuLXNsZXhraXQ+77yM5a6D5rOo5YaMIGBzbGV4YCBmZW5jZWQgY29kZSBibG9jayBwcm9jZXNzb3LvvJo="><pre><code class="language-slex">每个 editor 实例默认拥有一个 trusted Markdown runtime host。该 editor 中的 block 共享 artifact ID,因此 state-only fence 可以为后续可渲染 fence 提供状态。宿主需要显式生命周期控制时,可以传入runtimeHostartifactId`。

Obsidian integration

官方 Obsidian 插件位于 <https://github.com/slexkit/obsidian-slexkit>,它注册 slex fenced code block processor:

ts registerMarkdownCodeBlockProcessor("slex", (source, el, ctx) => { ... });


Adapter 只在 reading mode 渲染,不写回 vault。同一 note 内的 blocks 共享 trusted artifact runtime。

Obsidian adapter 使用 trusted mode,因为它渲染用户本地 vault 内容。不要用它隔离不可信或 agent-generated Markdown。

## Writing a custom host adapter

### 1. Detect fence language

只处理语言标记为 `slex` 的 fences。不要扫描 plain JavaScript、JSON 或 untagged code blocks。

### 2. Create a runtime host

```ts
import { createSlexKitMarkdownRuntimeHost } from "slexkit";

const runtime = createSlexKitMarkdownRuntimeHost({
  mode: "trusted",
  theme: "host-shadcn"
});

3. Mount blocks

为每个 fence 创建 container,并挂载:

function processFence(source: string, fenceIndex: number) {
  const container = document.createElement("div");
  const cleanup = runtime.mountBlock({
    artifactId: "message-42",
    source,
    container
  });

  return cleanup;
}

4. Manage lifecycle

runtime.disposeBlock(container);
runtime.disposeArtifact("message-42");
runtime.disposeAll();

5. Handle secure mode

使用 secure mode 时,确保 slexkit.runtime.js 作为 public ES module 提供,并配置 secureFrame.runtimeUrl

Fallback 渲染

支持 SlexKit 的宿主应在 DOM 中保留 raw fence content 或 plain text fallback,以便不支持 SlexKit 的环境可读。Runtime 会替换 container children,因此 fallback 只在 mount 前或 disposal 后可见。