
Tool Ui Vue
- 3 installs
- 4 repo stars
- Updated July 28, 2026
- lionad-morotar/tool-ui-vue
Helps with ai & agent building tasks.
About
tool-ui-vue is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- tool-ui-vue
- AI & Agent Building
- AI-coding skill
Tool Ui Vue by the numbers
- 3 all-time installs (skills.sh)
- Ranked #13,657 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/lionad-morotar/tool-ui-vue --skill tool-ui-vueAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 3 |
|---|---|
| repo stars | ★ 4 |
| Last updated | July 28, 2026 |
| Repository | lionad-morotar/tool-ui-vue ↗ |
What it does
Helps with ai & agent building tasks.
Files
tool-ui-vue Assistant
You are an expert on the tool-ui-vue (VTU) Vue 3 component library — a Vue 3 + Zod + Tailwind CSS v4 component toolkit for agent tool-call UIs.
Stack: Vue 3 + TypeScript + Zod + Tailwind CSS v4
Monorepo layers:
@lionad/vtu-components— 27 tool-specific components (charts, maps, social posts, code blocks, etc.) + 内嵌 core primitives、i18n、design tokens@lionad/vtu-renderer— JSON-driven component rendering@lionad/vtu-server— MCP server exposing component data
核心规则(始终遵循)
1. 组件根元素使用 cmpt- kebab-case 标识(如 cmpt-data-table),带 data-tool-ui-id 属性 2. 数据验证通过 Serializable Zod schemas(JSON-safe),运行时通过 Props 接口(含 css + 回调) 3. 样式覆盖通过 css prop(Tailwind 类字符串),不修改组件源码 4. 默认中文 locale(zh-CN),组件导入时自动注册;通过 registerEnglish() 切换英文 5. Headless 架构 — 组件逻辑在 states/ composable 中,index.vue 仅绑定视图
如何使用本 skill
按需加载参考文件,不要一次全部读取。根据用户问题查阅路由表中对应的文件。
参考文件
快速参考
references/components.md— 组件索引(27 个组件,按类别分组)
指南
references/guidelines/conventions.md— 命名约定、目录结构、TypeScript 模式、CSS prop 系统references/guidelines/schema-contracts.md— Zod schema 模式、Contract 系统、Serializable vs Props、Action/Decision 模式references/guidelines/theming.md— Token 系统、CSS 变量、dark mode、css prop 覆盖references/guidelines/i18n.md— LocaleProvider、useI18n、registerEnglish、组件级 i18n
配方
references/recipes/component-usage.md— 安装、基本用法、事件处理、peer 依赖references/recipes/headless-states.md— 使用 states/ composables 自定义 UIreferences/recipes/json-rendering.md— Renderer 包、Catalog + Registry、错误边界references/recipes/mcp-server.md— MCP Server 安装(Claude Code / Cursor / Windsurf)
路由表
| 用户问题 | 加载文件 |
|---|---|
| "有哪些组件?/ 用哪个组件?" | references/components.md |
| "怎么命名?/ 文件结构?" | references/guidelines/conventions.md |
| "怎么定义 schema?/ parse 和 safeParse?" | references/guidelines/schema-contracts.md |
| "怎么改颜色?/ dark mode?" | references/guidelines/theming.md |
| "怎么切换语言?" | references/guidelines/i18n.md |
| "怎么安装?/ 怎么用?" | references/recipes/component-usage.md |
| "想自定义 UI / 只用逻辑" | references/recipes/headless-states.md |
| "JSON 渲染 / 动态渲染" | references/recipes/json-rendering.md |
| "样式坏了" | references/guidelines/theming.md + references/recipes/component-usage.md 中的故障排除 |
| "LLM/AI 集成" | references/guidelines/schema-contracts.md + references/recipes/json-rendering.md |
| "MCP server / 安装 server" | references/recipes/mcp-server.md |
安装
pnpm add @lionad/vtu-components在入口 CSS 文件中,添加在 @import "tailwindcss" 之后:
@import "@lionad/vtu-components/style.css";不使用 Tailwind 的项目暂不支持(组件依赖 Tailwind utility classes 生成布局样式)。
快速开始
<script setup lang="ts">
import { CodeBlock } from '@lionad/vtu-components'
import type { SerializableCodeBlock } from '@lionad/vtu-components'
const data: SerializableCodeBlock = {
code: "console.log('hello')",
language: 'javascript',
filename: 'hello.js',
lineNumbers: true,
}
</script>
<template>
<CodeBlock v-bind="data" />
</template>{
"name": "@lionad/vtu-skill",
"version": "0.1.2",
"description": "Claude skill for tool-ui-vue component library",
"license": "MIT",
"type": "module",
"files": [
"SKILL.md",
"references/"
],
"main": "SKILL.md",
"scripts": {},
"packageManager": "pnpm@10.33.0"
}
组件索引
所有组件通过 @lionad/vtu-components 导出。每个组件导出:组件、Props 类型、Serializable Schema、parse/safeParse 函数。
详细的 props 和 slots 请使用 MCP server 查询(pnpm add @lionad/vtu-server,然后 npx vtu-mcp-server)。本文件帮助你找到正确的组件名。
数据展示(Data Display)
| Component | 用途 |
|---|---|
Chart | 柱状/折线数据可视化,基于 chart.js |
DataTable | 可排序、可筛选的数据表格,支持列分类和格式化 |
StatsDisplay | 统计数值展示,支持 sparkline 和差值显示 |
WeatherWidget | 天气预报卡片,含 WebGL 动画天气效果 |
代码与终端(Code & Terminal)
| Component | 用途 |
|---|---|
CodeBlock | 语法高亮代码块,基于 Shiki |
CodeDiff | 并排/统一模式的代码 diff 视图 |
Terminal | 终端输出模拟,支持 ANSI 着色 |
媒体(Media)
| Component | 用途 |
|---|---|
Audio | 音频播放器,含播放控制和进度条 |
Image | 响应式图片,支持宽高比和填充模式 |
ImageGallery | 图片画廊,含网格布局和灯箱浏览 |
ItemCarousel | 水平滚动轮播,展示项目卡片列表 |
Video | 视频播放器,含播放控制和全屏 |
社交(Social)
| Component | 用途 |
|---|---|
ApprovalCard | 审批决策卡片,含确认/取消操作和元数据展示 |
Citation | 单条引用/参考链接,含弹出详情 |
CitationList | 引用列表,含折叠/展开行为 |
InstagramPost | Instagram 帖子预览卡片 |
LinkedInPost | LinkedIn 帖子预览卡片 |
LinkPreview | 富文本链接预览卡片 |
MessageDraft | 邮件或 Slack 消息草稿预览(discriminated union) |
XPost | X/Twitter 帖子预览卡片,支持引用帖子 |
联系信息(Contact)
| Component | 用途 |
|---|---|
ContactCard | 联系方式卡片,支持电话、邮箱、地址、WhatsApp、微信、网址等,含一键复制和链接跳转 |
表单与输入(Forms & Input)
| Component | 用途 |
|---|---|
OptionList | 可选择列表,支持单选/多选 |
ParameterSlider | 数值参数滑块,含拖拽和刻度 |
PreferencesPanel | 偏好配置面板,含开关/选择项 |
工作流(Workflow)
| Component | 用途 |
|---|---|
GeoMap | 交互式地图,支持标记、路线和聚合(基于 Leaflet) |
Plan | 计划/待办列表,含状态跟踪和进度庆祝 |
ProgressTracker | 多步骤进度追踪器 |
QuestionFlow | 交互式问答流程,支持渐进/预填/回执三种模式 |
OrderSummary | 订单摘要,含商品列表和价格明细 |
核心基础组件(Core Primitives)
| Component | 用途 |
|---|---|
Button | 按钮,支持变体(default/destructive/secondary/ghost/outline) |
Card / CardHeader / CardTitle / CardDescription / CardContent / CardFooter | 卡片容器及子组件 |
Badge | 标签,支持变体 |
CopyButton | 复制到剪贴板按钮 |
核心组件由 @lionad/vtu-components 重导出。约定
目录结构
每个组件遵循统一目录结构:
<component-name>/
├── index.ts # 公开导出(组件、类型、schema、parser)
├── index.vue # 主组件 UI(纯视图绑定)
├── schema.ts # Zod schemas、类型、Props 接口
├── states/ # Headless 状态逻辑(composable)
│ └── index.ts # 聚合导出
├── cmpts/ # 子组件
├── composables/ # 可复用 composable
├── i18n/ # 国际化
│ ├── zh-CN.ts
│ └── en.ts
└── __tests__/ # 测试
└── index.test.ts命名规则
| 类别 | 格式 | 示例 |
|---|---|---|
| 组件目录 | kebab-case | data-table/, code-block/ |
| 组件导出 | PascalCase | DataTable, CodeBlock |
defineOptions.name | cmpt- + kebab-case | cmpt-data-table |
| Composable | use 前缀 | useDataTable, useSort |
| Provider | provide 前缀 | provideImageGallery |
| 事件处理函数 | handle 前缀 | handleClick |
类型命名模式
每个组件通常导出以下类型集合:
// Props 接口(运行时)
export interface DataTableProps { ... }
// Serializable 类型(JSON-safe)
export type SerializableDataTable = z.infer<typeof SerializableDataTableSchema>
// Zod Schema
export const SerializableDataTableSchema = z.object({ ... })
// Parser 函数
export const parseSerializableDataTable: (input: unknown) => DataTable
export const safeParseSerializableDataTable: (input: unknown) => DataTable | null命名规律:
- Props 接口:
XxxProps - Serializable 类型:
SerializableXxx - Zod Schema:
SerializableXxxSchema - Parser:
parseSerializableXxx/safeParseSerializableXxx
TypeScript
- 严格模式:
strict: true、noUnusedLocals、noUnusedParameters - 始终使用
<script setup lang="ts"> - Props 通过
defineProps<XxxProps>()定义 - Emits 通过
defineEmits<{}>()定义 - 强制 type-only imports
模板约定
<template>
<div
data-tool-ui-id="cmpt-data-table"
:data-slot="css?.root ? undefined : undefined"
:class="cn('...', css?.root)"
>
...
</div>
</template>关键规则:
- 模板中使用 kebab-case 组件名:
<data-table>而非<DataTable> - Tailwind 类通过
cn()合并(clsx+tailwind-merge) - 根元素带
data-tool-ui-id="cmpt-xxx"属性 - 子区域可带
data-slot属性标识
CSS Prop 系统
每个组件支持 css prop,允许外部通过 Tailwind 类字符串覆盖内部样式:
// schema.ts 中定义
export const DataTableCssSchema = z.object({
root: z.string().optional(),
header: z.string().optional(),
row: z.string().optional(),
cell: z.string().optional(),
})
// Props 中使用
export interface DataTableProps {
css?: z.infer<typeof DataTableCssSchema>
}使用方式:
<DataTable v-bind="data" :css="{ root: 'rounded-xl shadow-lg', header: 'bg-gray-100' }" />Import 排序
1. builtin + external(vue, zod) 2. 内部(@/, ~/, @lionad/vtu-*) 3. 父级/兄弟/当前目录 4. type imports
同组内按字母排序。
ESLint
- 单引号
- 未使用变量以
_前缀 - BEM 风格 Tailwind 类排序(通过自定义
eslint-plugin-v-tw-merge)
国际化(i18n)
概述
tool-ui-vue 内置中文(zh-CN)和英文(en)两套 locale。默认使用中文,组件导入时自动注册 zh-CN 消息(零配置)。
LocaleProvider
包裹应用根组件提供 i18n 上下文:
<script setup lang="ts">
import { LocaleProvider } from '@lionad/vtu-components'
</script>
<template>
<LocaleProvider>
<YourApp />
</LocaleProvider>
</template>useI18n
在组件内获取翻译函数:
import { useI18n } from '@lionad/vtu-components'
const { t, locale, setLocale } = useI18n()
// t() 返回 ComputedRef<string>
const label = t('data-table.sort')API:
| 返回值 | 类型 | 说明 |
|---|---|---|
t | (key, params?) => ComputedRef<string> | 翻译函数,支持 {param} 插值 |
locale | ComputedRef<string> | 当前 locale |
setLocale | (locale: string) => void | 切换 locale |
切换到英文
使用 registerEnglish() 便捷函数:
<script setup lang="ts">
import { LocaleProvider } from '@lionad/vtu-components'
import { registerEnglish, enAll } from '@lionad/vtu-components/i18n'
registerEnglish() // 切换 locale 为 'en' 并加载英文消息
</script>
<template>
<LocaleProvider :messages="enAll" locale="en">
<YourApp />
</LocaleProvider>
</template>registerEnglish() 由 @lionad/vtu-components/i18n 导出,等价于:
import { setLocale, setMessages } from '@lionad/vtu-components'
import { enAll } from '@lionad/vtu-components/i18n'
setLocale('en')
setMessages(enAll)组件级 i18n
每个组件在 i18n/ 目录下有独立的 locale 文件:
data-table/
├── i18n/
│ ├── zh-CN.ts # 中文消息
│ └── en.ts # 英文消息组件内使用 useI18n() 获取翻译:
const { t } = useI18n<DataTableMessages>()
const sortLabel = t('data-table.sort')聚合 i18n 导出
@lionad/vtu-components/i18n 聚合所有组件的 locale 消息:
import { zhCNAll, enAll, registerEnglish } from '@lionad/vtu-components/i18n'zhCNAll— 合并所有组件的中文消息enAll— 合并所有组件的英文消息registerEnglish()— 便捷函数,切换到英文
添加新 Locale
1. 为每个组件创建 i18n/<locale>.ts 文件 2. 参照 zh-CN.ts 的 key 结构填写翻译 3. 在 @lionad/vtu-components/i18n 中聚合导出 4. 通过 setMessages() 和 setLocale() 应用
Story 中的 i18n
Histoire story 中使用 useStoryLocale 模式提供双语切换。
无 LocaleProvider 的行为
如果未配置 <LocaleProvider>,useI18n() 会自动降级使用内置 zh-CN 消息,并在开发环境输出 console.warn。
类型
i18n 系统提供完整的类型推导:
DeepKeyPath<T>— 深层 key path 的字符串字面量类型DeepValueOf<T, P>— 指定路径的值类型ParamValue— 插值参数类型I18nContext<T>— 注入上下文类型I18nReturn<T>—useI18n返回类型
Schema 与契约(Contract)
defineToolUiContract
每个组件通过 defineToolUiContract<T>() 创建标准化契约对象:
// @lionad/vtu-components 中的 contract.ts
export interface ToolUiContract<T> {
schema: z.ZodType<T>
parse: (input: unknown) => T // 无效时抛出异常
safeParse: (input: unknown) => T | null // 无效时返回 null
}
export function defineToolUiContract<T>(
componentName: string,
schema: z.ZodType<T>,
): ToolUiContract<T>Serializable vs Props
核心区分:Serializable schema 是 JSON-safe 的,用于 LLM 工具调用数据传输。Props 是运行时完整接口。
| Serializable Schema | Props Interface | |
|---|---|---|
| 用途 | LLM JSON 输出验证、API 传输 | Vue 组件运行时 |
| 包含 css | 否 | 是 |
| 包含回调 | 否(onXxx) | 是 |
| 命名 | SerializableXxxSchema | XxxProps |
| 来源 | Zod schema 定义 | 基于 schema 扩展 |
示例(以 OptionList 为代表):
// JSON-safe,用于 AI/LLM pipeline
export const SerializableOptionListSchema = z.object({
title: z.string(),
options: z.array(OptionListOptionSchema),
// 无 css、无 onXxx
})
// 运行时完整接口
export interface OptionListProps {
title: string
options: OptionListOption[]
css?: { root?: string; option?: string }
onSelect?: (selection: OptionListSelection) => void
}parse vs safeParse
// 抛出异常 — 用于受控上下文(服务端验证、开发调试)
const data = parseSerializableDataTable(input)
// 返回 null — 用于流式/工具调用上下文(数据可能不完整)
const data = safeParseSerializableDataTable(input)
if (!data) return nullsafeParse 适用于 assistant-ui 的 render 函数,因为 args 是流式传入的,在工具调用完成前可能不完整。
Action 模式
ActionSchema
定义操作按钮:
const ActionSchema = z.object({
id: z.string().min(1),
label: z.string().min(1),
sentence: z.string().optional(), // 操作后助手 narration
variant: z.enum(['default', 'destructive', 'secondary', 'ghost', 'outline']).optional(),
icon: z.string().optional(), // Lucide 图标名
loading: z.boolean().optional(),
disabled: z.boolean().optional(),
})DecisionResultSchema
有后果的决策产生结构化结果:
const DecisionResultSchema = z.object({
kind: z.literal('decision'),
version: z.literal(1),
decisionId: z.string().min(1),
actionId: z.string().min(1),
actionLabel: z.string().min(1),
at: z.iso.datetime(),
payload: z.record(z.string(), z.unknown()).optional(),
})工厂函数:
import { createDecisionResult } from '@lionad/vtu-components'
const result = createDecisionResult({
decisionId: 'approval-001',
action: { id: 'confirm', label: '确认' },
payload: { reason: '预算内' },
})ActionsConfig
批量操作配置:
// Serializable 版本(JSON-safe)
const SerializableActionsConfigSchema = z.object({
items: z.array(SerializableActionSchema).min(1),
align: z.enum(['left', 'center', 'right']).optional(),
confirmTimeout: z.number().positive().optional(),
})ToolUISurface 基础 Schema
所有工具 UI 的基础结构:
const ToolUISurfaceSchema = z.object({
id: ToolUIIdSchema, // 稳定唯一标识
role: ToolUIRoleSchema.optional(), // information | decision | control | state | composite
receipt: ToolUIReceiptSchema.optional(), // 结果回执
})ToolUIId 建议格式:{component-type}-{semantic-identifier},如 "data-table-expenses-q3"。
Receipt 模式
操作完成后生成的持久化摘要:
const ToolUIReceiptSchema = z.object({
outcome: z.enum(['success', 'partial', 'failed', 'cancelled']),
summary: z.string().min(1),
identifiers: z.record(z.string(), z.string()).optional(),
at: z.iso.datetime(),
})使用场景:审批卡片确认后、偏好面板提交后等需要"结果快照"的场景。
Schema-first 开发流程
1. 在 schema.ts 中定义 SerializableXxxSchema 2. 用 z.infer 生成 TypeScript 类型 3. 创建 XxxProps 接口(扩展 Serializable 类型 + css + 回调) 4. 调用 defineToolUiContract() 创建 parse/safeParse 5. 实现 index.vue(视图)和 states/(逻辑)
主题与样式
Token 系统
主题通过 @lionad/vtu-components/style.css 提供,包含 CSS 自定义属性和 @source 指令:
@source "."— 自动触发 Tailwind v4 扫描组件 JS bundle 中的 class 名:root { }— 无 Tailwind 环境的兜底@theme { }— Tailwind CSS v4 主题注册
导入方式:
@import "@lionad/vtu-components/style.css";颜色 Token
所有颜色提供 light/dark 两套值:
| Token | 用途 |
|---|---|
--color-background | 页面背景 |
--color-foreground | 主要文字 |
--color-primary | 主要操作色 |
--color-primary-foreground | 主要操作上的文字 |
--color-secondary | 次要区域 |
--color-secondary-foreground | 次要区域文字 |
--color-destructive | 危险/删除操作 |
--color-destructive-foreground | 危险操作上的文字 |
--color-muted | 弱化背景 |
--color-muted-foreground | 弱化文字 |
--color-accent | 强调背景 |
--color-accent-foreground | 强调文字 |
--color-card | 卡片背景 |
--color-card-foreground | 卡片文字 |
--color-popover | 弹出层背景 |
--color-popover-foreground | 弹出层文字 |
--color-border | 边框 |
--color-input | 输入框边框 |
--color-ring | focus ring |
Tailwind v4 中使用:bg-primary、text-muted-foreground、border-border 等。
其他 Token
圆角
--radius-sm (0.125rem) → --radius-3xl (1.5rem)
间距
--spacing-1 (0.25rem) → --spacing-24 (6rem)
阴影
--shadow-xs → --shadow-xl
Dark Mode
通过 data-theme="dark" 属性切换:
document.documentElement.setAttribute('data-theme', 'dark')dark mode 会覆盖所有颜色和阴影 token。颜色模式 key 为 vtu-color-mode。
css Prop 覆盖
每个组件支持 css prop,通过 Tailwind 类字符串覆盖内部样式:
interface DataTableCss {
root?: string // 根容器
header?: string // 表头
row?: string // 行
cell?: string // 单元格
}使用示例:
<DataTable
v-bind="data"
:css="{ root: 'rounded-xl shadow-lg', header: 'bg-muted' }"
/>组件内部通过 cn() 合并默认类和覆盖类:
<div :class="cn('rounded-md border', css?.root)">自定义 Token
在项目 CSS 中覆盖变量即可:
:root {
--color-primary: hsl(220 80% 50%);
--color-primary-foreground: hsl(0 0% 100%);
}如果使用 Tailwind v4 的 @theme,在自定义 CSS 中重新声明同名变量即可覆盖。
组件使用
安装
pnpm add @lionad/vtu-components导入样式:
@import "@lionad/vtu-components/style.css";基本用法
使用 Serializable 数据
通过 v-bind 将 Serializable 数据传递给组件:
<script setup lang="ts">
import { CodeBlock } from '@lionad/vtu-components'
import type { SerializableCodeBlock } from '@lionad/vtu-components'
const data: SerializableCodeBlock = {
code: "console.log('hello')",
language: 'javascript',
filename: 'hello.js',
lineNumbers: true,
}
</script>
<template>
<CodeBlock v-bind="data" />
</template>使用 Props
直接传递 typed props:
<script setup lang="ts">
import { OptionList } from '@lionad/vtu-components'
</script>
<template>
<OptionList
title="选择部署目标"
:options="[
{ id: '1', label: '生产环境', value: 'prod' },
{ id: '2', label: '测试环境', value: 'staging' },
]"
@select="handleSelect"
/>
</template>事件处理
组件通过 onXxx 回调通知父组件:
<template>
<ApprovalCard
v-bind="data"
@decision="handleDecision"
/>
</template>
<script setup lang="ts">
import { createDecisionResult } from '@lionad/vtu-components'
function handleDecision(action: { id: string; label: string }) {
const result = createDecisionResult({
decisionId: data.id,
action,
})
// 发送到后端...
}
</script>css Prop 定制
不修改源码,通过 Tailwind 类覆盖样式:
<template>
<ItemCarousel
v-bind="data"
:css="{
root: 'rounded-2xl shadow-xl',
card: 'hover:scale-105 transition-transform',
}"
/>
</template>每个组件的 css 支持的 key 不同,常见 key:root、header、content、footer、item/card/row。
组件导出结构
从 @lionad/vtu-components 导入:
// 组件
import { DataTable } from '@lionad/vtu-components'
// 类型
import type { DataTableProps, SerializableDataTable } from '@lionad/vtu-components'
// Schema
import { SerializableDataTableSchema } from '@lionad/vtu-components'
// Parser
import { parseSerializableDataTable, safeParseSerializableDataTable } from '@lionad/vtu-components'或从根包聚合导入:
import { DataTable } from 'tool-ui-vue'Peer 依赖注意
部分组件有额外 peer 依赖:
| 组件 | 依赖 |
|---|---|
GeoMap | leaflet, @vue-leaflet/vue-leaflet |
Chart | chart.js, vue-chartjs |
CodeBlock / CodeDiff | shiki(动态 import) |
Terminal | ansi-to-html |
样式故障排除
1. 确认 @import "@lionad/vtu-components/style.css" 已添加在 @import "tailwindcss" 之后 2. 确认 Tailwind v4 扫描到组件源码(style.css 内置 @source "." 指令,无需手动配置) 3. 确认 data-theme="dark" 已设置(如需 dark mode)
Headless States
概述
tool-ui-vue 采用 Headless 架构:每个组件的业务逻辑抽离到 states/ 目录的 composable 中,index.vue 仅负责视图绑定。
这意味着你可以直接使用 states composable 构建自定义 UI,而不使用默认组件。
架构
组件目录/
├── states/ # 纯逻辑(composable)
│ ├── index.ts # 聚合导出
│ └── useXxx.ts # 具体 composable
├── index.vue # 默认 UI(调用 states)
└── schema.ts # 数据契约index.vue 的典型结构:
<script setup lang="ts">
import { useDataTable, type DataTableEmit, type DataTableState } from './states'
const props = defineProps<DataTableProps>()
const emit = defineEmits<DataTableEmit>()
const { sortedData, sortField, sortDirection, toggleSort } = useDataTable(props, emit)
</script>
<template>
<!-- 纯视图绑定 -->
</template>可用 States
DataTable
import { useDataTable, useSort, useFormat, useLayout } from '@lionad/vtu-components/data-table/states'
// useDataTable — 主 composable
// 返回:sortedData, sortField, sortDirection, toggleSort 等
// useSort — 排序逻辑
// useFormat — 格式化逻辑
// useLayout — 列分类与布局OptionList
import { useOptionList } from '@lionad/vtu-components/option-list/states'
// 返回:selectedIds, isSelected, toggle, selectAll, clearSelection 等QuestionFlow
import { useQuestionFlow } from '@lionad/vtu-components/question-flow/states'
// 支持三种模式:progressive(渐进)、upfront(预填)、receipt(回执)
// 返回:currentStep, answers, progress, next, back, submit 等Audio / Video
import { useAudio } from '@lionad/vtu-components/audio/states'
import { useVideo } from '@lionad/vtu-components/video/states'
// 共享 composable:
// usePlayback — 播放控制(play/pause/seek/volume)
// useEvents — 媒体事件处理其他 States
| 组件 | Composable | 主要功能 |
|---|---|---|
Chart | useChart | tooltip 状态、数据映射 |
CodeBlock | useCodeBlock | Shiki 加载、行号计算 |
CodeDiff | useCodeDiff | diff 算法、主题 |
GeoMap | useGeoMap | 地图状态、标记管理 |
ImageGallery | useImageGallery, useGallery | 灯箱、选中索引 |
ItemCarousel | useItemCarousel | 滚动位置、活跃索引 |
ParameterSlider | useSlider, useDrag, useLayout, useVisual | 拖拽、刻度、视觉反馈 |
Plan | usePlan | 展开/折叠、进度计算、庆祝动画 |
PreferencesPanel | usePreferencesPanel | 偏好值管理、dirty 状态 |
ProgressTracker | useProgressTracker | 步骤进度、回执状态 |
WeatherWidget | useWeatherWidget | 天气主题、效果参数、玻璃样式 |
InstagramPost | useInstagramPost | 展开/截断状态 |
LinkedInPost | useLinkedinPost | 展开/截断、格式化计数 |
XPost | useXPost | 格式化计数、相对时间 |
自定义 UI 示例
使用 useOptionList 构建自定义选项 UI:
<script setup lang="ts">
import { useOptionList, type OptionListEmit } from '@lionad/vtu-components/option-list/states'
import type { OptionListProps } from '@lionad/vtu-components'
const props = defineProps<OptionListProps>()
const emit = defineEmits<OptionListEmit>()
const { selectedIds, isSelected, toggle } = useOptionList(props, emit)
</script>
<template>
<div class="flex flex-wrap gap-2">
<button
v-for="option in props.options"
:key="option.id"
:class="isSelected(option.id) ? 'bg-primary text-white' : 'bg-muted'"
@click="toggle(option)"
>
{{ option.label }}
</button>
</div>
</template>设计原则
1. States 是纯逻辑 — 不包含任何模板、样式或 DOM 操作 2. 视图自由 — 你可以用任何 UI 框架或样式方案渲染 3. 类型安全 — 所有 composable 都有完整的 TypeScript 类型导出 4. 可测试 — 纯逻辑更容易编写单元测试,无需挂载 Vue 组件
JSON 渲染
概述
@lionad/vtu-renderer 包提供 JSON 驱动的组件渲染能力。基于 @json-render/core 和 @json-render/vue,将 JSON spec 动态渲染为 Vue 组件。
适用场景:
- LLM 工具调用返回组件数据,前端动态渲染
- 低代码平台通过 JSON schema 驱动 UI
- 需要运行时决定渲染哪些组件的场景
VtuRenderer
主渲染组件:
<script setup lang="ts">
import { VtuRenderer } from '@lionad/vtu-renderer'
import type { Spec } from '@json-render/core'
const spec: Spec = {
component: 'CodeBlock',
props: {
code: "console.log('hello')",
language: 'javascript',
filename: 'hello.js',
},
}
</script>
<template>
<VtuRenderer :spec="spec" />
</template>Props
| Prop | 类型 | 说明 |
|---|---|---|
spec | Spec | 渲染规格(组件名 + props) |
handlers | Record<string, (...args) => unknown> | 自定义动作处理器 |
initialState | Record<string, unknown> | 初始状态 |
Catalog
Catalog 注册所有组件的 Serializable Schema:
import { catalog, type AppCatalog } from '@lionad/vtu-renderer'
// catalog 包含 27 个组件的 schema 定义
// 每个 entry: { props: SerializableXxxSchema, slots: [], description: string }Catalog 用于:
- 验证 JSON spec 的 props 是否合法
- 为 LLM 提供组件 schema 信息(通过 MCP server)
- 自动补全和类型检查
Registry
Registry 将组件名映射到 Vue 组件渲染器:
import { registry } from '@lionad/vtu-renderer'
// 内部使用 defineRegistry(catalog, { components }) 创建
// 每个组件通过 createRenderer(Component) 包装
// 再通过 withErrorBoundary() 包裹,提供错误降级错误边界
每个组件渲染器都被 withErrorBoundary HOC 包裹:
import { withErrorBoundary } from '@lionad/vtu-renderer'
import ErrorBoundary from './error-boundary.vue'
// 如果组件渲染出错,ErrorBoundary 会捕获并显示降级 UI这确保单个组件的错误不会影响整个渲染树。
完整示例
<script setup lang="ts">
import { VtuRenderer } from '@lionad/vtu-renderer'
import type { Spec } from '@json-render/core'
import { ref } from 'vue'
const specs = ref<Spec[]>([
{
component: 'WeatherWidget',
props: {
id: 'weather-hz',
location: { name: '杭州' },
current: { temperature: 22, conditionCode: 'clear' },
},
},
{
component: 'DataTable',
props: {
id: 'expenses-q3',
title: 'Q3 支出',
columns: [
{ key: 'name', label: '名称' },
{ key: 'amount', label: '金额' },
],
data: [
{ name: '服务器', amount: '¥12,000' },
{ name: '域名', amount: '¥200' },
],
},
},
])
function handleAction(action: string, ...args: unknown[]) {
console.log('Action:', action, args)
}
</script>
<template>
<div class="flex flex-col gap-4">
<VtuRenderer
v-for="(spec, i) in specs"
:key="i"
:spec="spec"
:handlers="{ submit: handleAction }"
/>
</div>
</template>包依赖
pnpm add @lionad/vtu-renderer @json-render/core @json-render/vue@lionad/vtu-components 是唯一需要安装的包,包含所有组件、类型和主题 tokens。MCP Server 安装
@lionad/vtu-server 提供 MCP (Model Context Protocol) Server,让 AI 编码助手(Claude Code、Cursor 等)能查询 VTU 组件文档、示例和 schema。
Claude Code
claude mcp add --transport stdio vtu npx vtu-mcp-serverClaude Desktop
编辑 claude_desktop_config.json:
{
"mcpServers": {
"tool-ui-vue": {
"command": "npx",
"args": ["vtu-mcp-server"]
}
}
}Cursor / Windsurf 等编辑器
在 MCP 配置中添加:
{
"mcpServers": {
"tool-ui-vue": {
"command": "npx",
"args": ["vtu-mcp-server"]
}
}
}Server 能力
安装后,AI 助手可使用以下能力:
| 工具 | 用途 |
|---|---|
search_components | 按名称/类别搜索组件 |
get_component | 获取组件完整文档 |
search_documentation | 搜索文档页面 |
search_icons | 搜索可用图标 |
list_examples | 列出所有示例 |
以及 resource://vtu/components、resource://vtu/composables 等资源端点。