feat: Add command documentation dialog
Introduce a modal dialog that displays documentation for all available markdown commands, accessible via a '?' button in the header.
This commit is contained in:
parent
f734da6acd
commit
110451ae04
29
src/App.tsx
29
src/App.tsx
|
|
@ -3,11 +3,17 @@ import { useLocation } from "@solidjs/router";
|
||||||
|
|
||||||
// 导入组件以注册自定义元素
|
// 导入组件以注册自定义元素
|
||||||
import "./components";
|
import "./components";
|
||||||
import { Article, MobileSidebar, DesktopSidebar } from "./components";
|
import {
|
||||||
|
Article,
|
||||||
|
MobileSidebar,
|
||||||
|
DesktopSidebar,
|
||||||
|
DocDialog,
|
||||||
|
} from "./components";
|
||||||
|
|
||||||
const App: Component = () => {
|
const App: Component = () => {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const [isSidebarOpen, setIsSidebarOpen] = createSignal(false);
|
const [isSidebarOpen, setIsSidebarOpen] = createSignal(false);
|
||||||
|
const [isDocOpen, setIsDocOpen] = createSignal(false);
|
||||||
|
|
||||||
const currentPath = createMemo(() => {
|
const currentPath = createMemo(() => {
|
||||||
// 根据路由加载对应的 markdown 文件
|
// 根据路由加载对应的 markdown 文件
|
||||||
|
|
@ -40,14 +46,23 @@ const App: Component = () => {
|
||||||
☰
|
☰
|
||||||
</button>
|
</button>
|
||||||
<h1 class="text-2xl font-bold text-gray-900">TTRPG Tools</h1>
|
<h1 class="text-2xl font-bold text-gray-900">TTRPG Tools</h1>
|
||||||
|
<div class="flex-1" />
|
||||||
|
<button
|
||||||
|
onClick={() => setIsDocOpen(true)}
|
||||||
|
class="w-8 h-8 rounded-full border border-gray-300 text-gray-500 hover:text-gray-700 hover:border-gray-400 flex items-center justify-center text-sm font-medium"
|
||||||
|
title="指令文档"
|
||||||
|
>
|
||||||
|
?
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
{/* fill th rest of the space*/}
|
{/* fill th rest of the space*/}
|
||||||
<div class="fixed top-16 left-0 right-0 bottom-0 overflow-auto">
|
<div class="fixed top-16 left-0 right-0 bottom-0 overflow-auto">
|
||||||
<main class="max-w-4xl mx-auto px-4 py-8 pt-4 md:ml-64 2xl:ml-auto flex justify-center items-center">
|
<main class="max-w-4xl mx-auto px-4 py-8 pt-4 md:ml-64 2xl:ml-auto flex justify-center items-center">
|
||||||
<Article class="prose-sm max-w-full flex-1" src={currentPath()} />
|
<Article class="prose-sm max-w-full flex-1" src={currentPath()} />
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
<DocDialog isOpen={isDocOpen()} onClose={() => setIsDocOpen(false)} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,170 @@
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
createSignal,
|
||||||
|
For,
|
||||||
|
Show,
|
||||||
|
onCleanup,
|
||||||
|
onMount,
|
||||||
|
} from "solid-js";
|
||||||
|
import { docEntries, type DocEntry } from "./doc-data";
|
||||||
|
|
||||||
|
export interface DocDialogProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DocDialog: Component<DocDialogProps> = (props) => {
|
||||||
|
const [selectedTag, setSelectedTag] = createSignal(docEntries[0]?.tag ?? "");
|
||||||
|
|
||||||
|
const selectedEntry = () =>
|
||||||
|
docEntries.find((e) => e.tag === selectedTag()) ?? docEntries[0];
|
||||||
|
|
||||||
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
|
if (e.key === "Escape") props.onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
document.addEventListener("keydown", handleKeyDown);
|
||||||
|
});
|
||||||
|
|
||||||
|
onCleanup(() => {
|
||||||
|
document.removeEventListener("keydown", handleKeyDown);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Show when={props.isOpen}>
|
||||||
|
<div
|
||||||
|
class="fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4"
|
||||||
|
onClick={(e) => {
|
||||||
|
if (e.target === e.currentTarget) props.onClose();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div class="bg-white rounded-lg shadow-2xl w-full max-w-4xl max-h-[85vh] flex flex-col overflow-hidden">
|
||||||
|
<div class="flex items-center justify-between px-6 py-4 border-b border-gray-200 shrink-0">
|
||||||
|
<h2 class="text-lg font-bold text-gray-900">指令组件文档</h2>
|
||||||
|
<button
|
||||||
|
onClick={props.onClose}
|
||||||
|
class="text-gray-400 hover:text-gray-600 text-xl leading-none p-1"
|
||||||
|
title="关闭"
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-1 min-h-0">
|
||||||
|
<nav class="w-48 shrink-0 border-r border-gray-200 overflow-y-auto p-3 bg-gray-50">
|
||||||
|
<For each={docEntries}>
|
||||||
|
{(entry) => (
|
||||||
|
<button
|
||||||
|
onClick={() => setSelectedTag(entry.tag)}
|
||||||
|
class={`w-full text-left px-3 py-2 rounded mb-1 text-sm transition-colors ${
|
||||||
|
selectedTag() === entry.tag
|
||||||
|
? "bg-blue-100 text-blue-800 font-medium"
|
||||||
|
: "text-gray-700 hover:bg-gray-100"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<span class="mr-2">{entry.icon}</span>
|
||||||
|
<span class="font-mono text-xs">{`:${entry.tag}`}</span>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="flex-1 overflow-y-auto p-6">
|
||||||
|
<Show when={selectedEntry()} keyed>
|
||||||
|
{(entry) => <DocContent entry={entry} />}
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Single entry documentation content */
|
||||||
|
const DocContent: Component<{ entry: DocEntry }> = (props) => {
|
||||||
|
const e = props.entry;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class="max-w-none">
|
||||||
|
<h2 class="text-2xl font-bold text-gray-900 mb-2">
|
||||||
|
{e.icon} {e.title}
|
||||||
|
<code class="ml-3 text-base font-mono bg-gray-100 px-2 py-0.5 rounded text-blue-700">
|
||||||
|
:{e.tag}
|
||||||
|
</code>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<p class="text-gray-600 mb-6">{e.description}</p>
|
||||||
|
|
||||||
|
<div class="mb-6">
|
||||||
|
<h3 class="text-sm font-semibold text-gray-500 uppercase mb-2">
|
||||||
|
基本语法
|
||||||
|
</h3>
|
||||||
|
<pre class="bg-gray-900 text-gray-100 px-4 py-3 rounded-lg text-sm overflow-x-auto">
|
||||||
|
<code>{e.syntax}</code>
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Show when={e.props.length > 0}>
|
||||||
|
<div class="mb-6">
|
||||||
|
<h3 class="text-sm font-semibold text-gray-500 uppercase mb-2">
|
||||||
|
属性
|
||||||
|
</h3>
|
||||||
|
<table class="w-full text-sm border-collapse">
|
||||||
|
<thead>
|
||||||
|
<tr class="border-b border-gray-200">
|
||||||
|
<th class="text-left py-2 pr-4 font-medium text-gray-600">
|
||||||
|
属性
|
||||||
|
</th>
|
||||||
|
<th class="text-left py-2 pr-4 font-medium text-gray-600">
|
||||||
|
类型
|
||||||
|
</th>
|
||||||
|
<th class="text-left py-2 pr-4 font-medium text-gray-600">
|
||||||
|
默认值
|
||||||
|
</th>
|
||||||
|
<th class="text-left py-2 font-medium text-gray-600">说明</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<For each={e.props}>
|
||||||
|
{(prop) => (
|
||||||
|
<tr class="border-b border-gray-100">
|
||||||
|
<td class="py-2 pr-4">
|
||||||
|
<code class="bg-gray-100 px-1.5 py-0.5 rounded text-xs text-pink-700">
|
||||||
|
{prop.name}
|
||||||
|
</code>
|
||||||
|
</td>
|
||||||
|
<td class="py-2 pr-4">
|
||||||
|
<code class="text-xs text-gray-600">{prop.type}</code>
|
||||||
|
</td>
|
||||||
|
<td class="py-2 pr-4 text-xs text-gray-400">
|
||||||
|
{prop.default ?? "—"}
|
||||||
|
</td>
|
||||||
|
<td class="py-2 text-sm text-gray-600">{prop.desc}</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<div class="mb-6">
|
||||||
|
<h3 class="text-sm font-semibold text-gray-500 uppercase mb-2">示例</h3>
|
||||||
|
<div class="bg-gray-50 border border-gray-200 rounded-lg p-4 text-sm leading-relaxed whitespace-pre-wrap font-mono text-gray-800">
|
||||||
|
{e.examples}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 class="text-sm font-semibold text-gray-500 uppercase mb-2">
|
||||||
|
使用场景
|
||||||
|
</h3>
|
||||||
|
<p class="text-sm text-gray-600">{e.usage}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DocDialog;
|
||||||
|
|
@ -0,0 +1,234 @@
|
||||||
|
export interface DocEntry {
|
||||||
|
tag: string;
|
||||||
|
icon: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
syntax: string;
|
||||||
|
props: { name: string; type: string; default?: string; desc: string }[];
|
||||||
|
examples: string;
|
||||||
|
usage: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const docEntries: DocEntry[] = [
|
||||||
|
{
|
||||||
|
tag: "md-dice",
|
||||||
|
icon: "🎲",
|
||||||
|
title: "骰子组件",
|
||||||
|
description: "点击文字执行掷骰,可用于属性检定、伤害投掷等场景。",
|
||||||
|
syntax: ':md-dice[2d6+d8]{key="attack"}',
|
||||||
|
props: [
|
||||||
|
{ name: "key", type: "string", desc: "URL 参数标识,结果记录到 ?dice-key=15" },
|
||||||
|
],
|
||||||
|
examples: `**攻击检定:** :md-dice[1d20+5]{key="attack"}
|
||||||
|
|
||||||
|
**伤害掷骰:** :md-dice[2d6+3]{key="damage"}
|
||||||
|
|
||||||
|
**优势检定:** :md-dice[2d20k1+5]{key="advantage"}
|
||||||
|
|
||||||
|
点击掷骰文字执行投掷,再次点击重置为公式。`,
|
||||||
|
usage: "用于技能检定、攻击命中、伤害计算等需要随机数的场景。",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: "md-table",
|
||||||
|
icon: "📊",
|
||||||
|
title: "表格组件",
|
||||||
|
description: "将 CSV 数据转换为可切换标签页的表格,支持随机抽取和变量引用。",
|
||||||
|
syntax: ':md-table[./data.csv]{roll=true remix=true}',
|
||||||
|
props: [
|
||||||
|
{ name: "roll", type: "boolean", desc: "显示随机切换按钮" },
|
||||||
|
{ name: "remix", type: "boolean", desc: "支持 {{prop}} 引用同行其他列" },
|
||||||
|
],
|
||||||
|
examples: `**基础表格:**
|
||||||
|
\`\`\`markdown
|
||||||
|
:md-table[./npcs.csv]
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
**带随机抽取:**
|
||||||
|
\`\`\`markdown
|
||||||
|
:md-table[./encounters.csv]{roll=true}
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
**随机 + 变量引用:**
|
||||||
|
\`\`\`markdown
|
||||||
|
:md-table[./quests.csv]{roll=true remix=true}
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
CSV 要求包含 label, body 列,可选 group 列分组。支持 YAML front matter 继承行属性。`,
|
||||||
|
usage: "用于 NPC 列表、遭遇表、宝物生成、随机事件等结构化数据。",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: "md-link",
|
||||||
|
icon: "🔗",
|
||||||
|
title: "链接组件",
|
||||||
|
description: "点击链接在当前页面内展开显示目标文章内容,支持章节定位。",
|
||||||
|
syntax: ':md-link[./rules.md#combat]',
|
||||||
|
props: [],
|
||||||
|
examples: `**展开完整文档:**
|
||||||
|
:md-link[./rules.md]
|
||||||
|
|
||||||
|
**展开特定章节:**
|
||||||
|
:md-link[./rules.md#combat]
|
||||||
|
|
||||||
|
点击链接即可内联展开目标内容,再次点击折叠。`,
|
||||||
|
usage: "用于引用规则书章节、怪物数据、快速预览等场景。",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: "md-pins",
|
||||||
|
icon: "📍",
|
||||||
|
title: "标记组件",
|
||||||
|
description: "在地图或图片上添加可编辑或固定的位置标记,支持字母或数字标签。",
|
||||||
|
syntax: ':md-pins[./images/map.png]{pins="A:30,40 B:10,30" fixed}',
|
||||||
|
props: [
|
||||||
|
{ name: "pins", type: "string", default: '""', desc: '标记列表,格式 "A:x,y B:x,y"' },
|
||||||
|
{ name: "fixed", type: "boolean", default: "false", desc: "固定模式(只读不可编辑)" },
|
||||||
|
{ name: "labelStart", type: "string", default: '"A"', desc: "标签起始值,支持字母或数字" },
|
||||||
|
],
|
||||||
|
examples: `**固定标记(只读):**
|
||||||
|
:md-pins[./images/battle-map.png]{pins="A:25,50 B:75,30" fixed}
|
||||||
|
|
||||||
|
**可编辑标记:**
|
||||||
|
:md-pins[./images/blank-map.png]
|
||||||
|
|
||||||
|
**数字标签:**
|
||||||
|
:md-pins[./images/dungeon.png]{labelStart="1"}
|
||||||
|
|
||||||
|
非 fixed 模式下点击图片添加标记,点击标记删除。`,
|
||||||
|
usage: "用于战斗地图标注、地牢房间标记、任务地点指示等。",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: "md-bg",
|
||||||
|
icon: "🖼️",
|
||||||
|
title: "背景组件",
|
||||||
|
description: "将图片设置为当前文章卡片的背景,支持多种适配方式。",
|
||||||
|
syntax: ':md-bg[./images/dungeon-bg.jpg]{fit="cover"}',
|
||||||
|
props: [
|
||||||
|
{ name: "fit", type: "cover | contain | fill | none | scale-down", default: "cover", desc: "背景适配方式" },
|
||||||
|
],
|
||||||
|
examples: `**设置背景图:**
|
||||||
|
:md-bg[./images/dungeon-bg.jpg]{fit="cover"}
|
||||||
|
|
||||||
|
背景图自动覆盖整个卡片区域,配合文章内容营造沉浸氛围。`,
|
||||||
|
usage: "用于营造场景氛围,如地牢、森林、城镇等不同环境的背景。",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: "md-font",
|
||||||
|
icon: "🔤",
|
||||||
|
title: "字体组件",
|
||||||
|
description: "设置整个文档的字体,支持 Google Fonts、emfont 和系统本地字体三种来源。",
|
||||||
|
syntax: ':md-font[Noto Sans SC]{source="google" weight="400"}',
|
||||||
|
props: [
|
||||||
|
{ name: "source", type: "google | emfont | local", default: "local", desc: "字体来源" },
|
||||||
|
{ name: "weight", type: "string", default: '"400"', desc: "字体粗细" },
|
||||||
|
],
|
||||||
|
examples: `**Google 字体:**
|
||||||
|
:md-font[Noto Sans SC]{source="google"}
|
||||||
|
|
||||||
|
**Emfont 字体:**
|
||||||
|
:md-font[Source Han Serif]{source="emfont" weight="700"}
|
||||||
|
|
||||||
|
**本地字体(默认):**
|
||||||
|
:md-font[STSong]
|
||||||
|
|
||||||
|
字体将应用到整个文档卡片。`,
|
||||||
|
usage: "用于切换文档的显示字体,适配不同风格需求。",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: "md-embed",
|
||||||
|
icon: "📄",
|
||||||
|
title: "嵌入组件",
|
||||||
|
description: "将另一个 Markdown 文件的内容内联嵌入到当前文档中。",
|
||||||
|
syntax: ':md-embed[./rules.md#combat]',
|
||||||
|
props: [],
|
||||||
|
examples: `**嵌入完整文档:**
|
||||||
|
:md-embed[./rules.md]
|
||||||
|
|
||||||
|
**嵌入指定章节:**
|
||||||
|
:md-embed[./rules.md#combat]
|
||||||
|
|
||||||
|
嵌入后内容直接在当前位置显示,包括其中的表格、骰子等组件也会正常渲染。`,
|
||||||
|
usage: "用于在文档中引用通用规则、重复使用的数据表格等。",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: "md-deck",
|
||||||
|
icon: "🃏",
|
||||||
|
title: "卡牌组件",
|
||||||
|
description: "将 CSV 数据渲染为卡牌布局,支持自定义网格和图层的排版。",
|
||||||
|
syntax: ':md-deck[./cards.csv]{grid="5x8" layers="title:1,1-5,1f8 body:1,5-8,8f3"}',
|
||||||
|
props: [
|
||||||
|
{ name: "grid", type: "string", desc: "卡牌布局,格式 行x列" },
|
||||||
|
{ name: "layers", type: "string", desc: "图层定义,格式 字段:行,列-列,字号" },
|
||||||
|
],
|
||||||
|
examples: `**基础卡牌:**
|
||||||
|
:md-deck[./spells.csv]{grid="3x3"}
|
||||||
|
|
||||||
|
**多层卡牌:**
|
||||||
|
:md-deck[./cards.csv]{grid="5x8" layers="title:1,1-5,1f8 body:1,5-8,8f3"}
|
||||||
|
|
||||||
|
CSV 包含 label 和显示字段列,通过图层定义控制各字段的位置和大小。`,
|
||||||
|
usage: "用于法术卡、物品卡、NPC 卡等可打印的卡牌排版。",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: "md-yarn-spinner",
|
||||||
|
icon: "🧶",
|
||||||
|
title: "叙事线组件",
|
||||||
|
description: "展示 Yarn Spinner 格式的分支叙事结构,支持对话选择和分支。",
|
||||||
|
syntax: ':md-yarn-spinner[./story.yarn]',
|
||||||
|
props: [],
|
||||||
|
examples: `**加载叙事文件:**
|
||||||
|
:md-yarn-spinner[./story.yarn]
|
||||||
|
|
||||||
|
Yarn Spinner 是用于游戏对话系统的格式,支持选项、条件分支和变量。`,
|
||||||
|
usage: "用于互动故事、分支对话、冒险剧本等。",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: "md-token",
|
||||||
|
icon: "🪙",
|
||||||
|
title: "代币组件",
|
||||||
|
description: "展示游戏代币或棋子的图片。",
|
||||||
|
syntax: ':md-token[./token.png]',
|
||||||
|
props: [],
|
||||||
|
examples: `**展示代币:**
|
||||||
|
:md-token[./goblin.png]
|
||||||
|
|
||||||
|
代币图片自动渲染为圆形,适合战棋或卡牌中的棋子展示。`,
|
||||||
|
usage: "用于展示怪物代币、角色棋子、道具图标等。",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: "md-token-viewer",
|
||||||
|
icon: "🎨",
|
||||||
|
title: "代币预览组件",
|
||||||
|
description: "使用 Three.js 在浏览器中 3D 渲染 3MF 格式的代币模型,支持旋转查看。",
|
||||||
|
syntax: ':md-token-viewer[./token.3mf]',
|
||||||
|
props: [],
|
||||||
|
examples: `**3D 预览模型:**
|
||||||
|
:md-token-viewer[./dragon.3mf]
|
||||||
|
|
||||||
|
支持鼠标拖拽旋转和自动旋转,从多角度查看模型细节。`,
|
||||||
|
usage: "用于 3D 打印前的代币模型预览。",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: "md-commander",
|
||||||
|
icon: "📋",
|
||||||
|
title: "命令追踪器",
|
||||||
|
description: "支持命令历史和游戏状态追踪,使用类 Emmet 语法创建追踪项。",
|
||||||
|
syntax: ':md-commander',
|
||||||
|
props: [],
|
||||||
|
examples: `**追踪 NPC 血量和防御:**
|
||||||
|
\`\`\`
|
||||||
|
track npc#john.dwarf.warrior[hp=4/4 ac=15 name="John"]
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
**语法规则:**
|
||||||
|
- \`#id\` 设置 ID
|
||||||
|
- \`.class\` 添加类别
|
||||||
|
- \`[attr=value]\` 设置属性
|
||||||
|
|
||||||
|
**属性类型:**
|
||||||
|
| 格式 | 显示 |
|
||||||
|
|---|---|
|
||||||
|
| x/y | 进度条 |
|
||||||
|
| 整数 | 计数器 |
|
||||||
|
| 文本 | 文本字段 |`,
|
||||||
|
usage: "用于追踪战斗中的 NPC 血量、AC、状态等游戏信息。",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
@ -18,6 +18,8 @@ export type { ArticleProps } from "./Article";
|
||||||
export { MobileSidebar, DesktopSidebar } from "./Sidebar";
|
export { MobileSidebar, DesktopSidebar } from "./Sidebar";
|
||||||
export type { SidebarProps } from "./Sidebar";
|
export type { SidebarProps } from "./Sidebar";
|
||||||
export { FileTreeNode, HeadingNode } from "./FileTree";
|
export { FileTreeNode, HeadingNode } from "./FileTree";
|
||||||
|
export { default as DocDialog } from "./DocDialog";
|
||||||
|
export type { DocDialogProps } from "./DocDialog";
|
||||||
|
|
||||||
// 导出数据类型
|
// 导出数据类型
|
||||||
export type { DiceProps } from "./md-dice";
|
export type { DiceProps } from "./md-dice";
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue