@lcap/spec-ui
v0.0.1
Published
Vue3 UI component library
Maintainers
Keywords
Readme
Spec UI
Vue3 UI 组件库,基于 Vite + TypeScript 构建。
特性
- 🚀 基于 Vite + TypeScript
- 📦 按组件目录分 chunk 构建,支持按需加载
- 🧪 完整的测试支持
- 🎨 完整的示例和开发调试环境
- 📄 内置 PDF 预览组件
- 🔍 内置 Diff 查看器组件(基于 @git-diff-view)
- ✍️ 内置 Markdown 编辑器组件(基于 Tiptap)
- 📝 支持多文档编辑器和懒加载
- 💬 完整的聊天界面组件库
安装
pnpm add @lcap/spec-ui注意: 如果使用 TypeScript,还需要安装 Vue 3 和 Element Plus 作为 peer dependencies。
使用
PDF 预览组件
<template>
<PdfViewer
:source="pdfSource"
:page="currentPage"
:scale="scale"
:rotation="rotation"
:show-toolbar="true"
@page-change="handlePageChange"
@scale-change="handleScaleChange"
@rotation-change="handleRotationChange"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { PdfViewer } from '@lcap/spec-ui';
const pdfSource = ref('https://example.com/document.pdf');
const currentPage = ref(1);
const scale = ref(1);
const rotation = ref<0 | 90 | 180 | 270>(0);
const handlePageChange = (page: number) => {
currentPage.value = page;
};
const handleScaleChange = (newScale: number) => {
scale.value = newScale;
};
const handleRotationChange = (newRotation: number) => {
rotation.value = newRotation as 0 | 90 | 180 | 270;
};
</script>PDF 预览组件 Props
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| source | string \| File \| ArrayBuffer | - | PDF 文件源,可以是 URL、File 对象或 ArrayBuffer |
| page | number | 1 | 页面编号(从 1 开始) |
| scale | number | 1 | 缩放比例 |
| rotation | number | 0 | 旋转角度(0, 90, 180, 270) |
| showToolbar | boolean | true | 是否显示工具栏 |
| enableTextSelection | boolean | true | 是否启用文本选择 |
| class | string | - | 自定义类名 |
| style | string \| Record<string, any> | - | 自定义样式 |
PDF 预览组件 Events
| 事件名 | 参数 | 说明 |
|--------|------|------|
| page-change | page: number | 页码改变时触发 |
| scale-change | scale: number | 缩放比例改变时触发 |
| rotation-change | rotation: number | 旋转角度改变时触发 |
Diff 查看器组件
DiffViewer 组件用于显示代码差异对比,支持分屏和统一两种显示模式。
<template>
<DiffViewer
:diff-data="diffData"
:diff-view-font-size="14"
:diff-view-mode="'split'"
:diff-view-highlight="true"
:diff-view-add-widget="false"
:diff-view-wrap="false"
:diff-view-theme="'light'"
@add-widget-click="handleAddWidgetClick"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { DiffViewer } from '@lcap/spec-ui';
import type { DiffData, AddWidgetClickEvent } from '@lcap/spec-ui';
import { DiffModeEnum } from '@git-diff-view/vue';
const diffData = ref<DiffData>({
oldFile: {
fileName: 'src/index.ts',
fileLang: 'typescript',
content: 'export const oldCode = "old";',
},
newFile: {
fileName: 'src/index.ts',
fileLang: 'typescript',
content: 'export const newCode = "new";\nexport const addedCode = "added";',
},
});
const handleAddWidgetClick = (event: AddWidgetClickEvent) => {
console.log('Add widget clicked:', event);
};
</script>DiffViewer 组件 Props
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| diffData | DiffData | - | Diff 数据,包含 oldFile 和 newFile |
| diffViewFontSize | number | 14 | 字体大小 |
| diffViewMode | 'split' \| 'unified' | 'split' | 显示模式,split 为分屏模式,unified 为统一模式 |
| diffViewHighlight | boolean | true | 是否高亮显示差异 |
| diffViewAddWidget | boolean | false | 是否显示添加小部件 |
| diffViewWrap | boolean | false | 是否自动换行 |
| diffViewTheme | 'light' \| 'dark' | 'light' | 主题 |
| extendData | Record<string, any> | - | 扩展数据 |
| class | string | - | 自定义类名 |
| style | string \| Record<string, any> | - | 自定义样式 |
DiffData 类型
interface FileData {
fileName?: string; // 文件名
fileLang?: string; // 文件语言(用于语法高亮)
content: string; // 文件内容
}
interface DiffData {
oldFile?: FileData; // 旧文件数据
newFile?: FileData; // 新文件数据
}DiffViewer 组件 Events
| 事件名 | 参数 | 说明 |
|--------|------|------|
| add-widget-click | event: AddWidgetClickEvent | 点击添加小部件时触发 |
AddWidgetClickEvent 类型
interface AddWidgetClickEvent {
side: 'old' | 'new'; // 所在侧:old 或 new
lineNumber: number; // 行号
}Markdown 编辑器组件
<template>
<div>
<MarkdownEditorToolbar :editor="editorInstance" />
<MarkdownEditor
v-model="markdownContent"
:marked-options="{ gfm: true, breaks: true }"
:readonly="false"
@ready="handleEditorReady"
@change="handleContentChange"
@focus="handleFocus"
@blur="handleBlur"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { MarkdownEditor, MarkdownEditorToolbar } from '@lcap/spec-ui';
import type { Editor } from '@tiptap/core';
const markdownContent = ref('# Hello World\n\nThis is a markdown editor.');
const editorInstance = ref<Editor | null>(null);
const handleEditorReady = (editor: Editor) => {
editorInstance.value = editor;
};
const handleContentChange = (content: { markdown: string; json: unknown }) => {
console.log('Content changed:', content.markdown);
};
const handleFocus = () => {
console.log('Editor focused');
};
const handleBlur = () => {
console.log('Editor blurred');
};
</script>Markdown 编辑器组件 Props
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| modelValue | string | - | 初始内容(Markdown 格式),支持 v-model |
| editorOptions | Partial<EditorOptions> | - | Tiptap 编辑器配置选项 |
| markedOptions | MarkedOptions | - | MarkedJS 配置选项(见下方说明) |
| placeholder | string | - | 占位符文本 |
| readonly | boolean | false | 是否只读 |
| class | string | - | 自定义类名 |
| style | string \| Record<string, string \| number> | - | 自定义样式 |
Markdown 编辑器组件 Events
| 事件名 | 参数 | 说明 |
|--------|------|------|
| update:modelValue | value: string | 内容更新时触发(支持 v-model) |
| ready | editor: Editor | 编辑器创建完成时触发 |
| change | content: { markdown: string; json: unknown } | 内容变化时触发 |
| focus | - | 编辑器获得焦点时触发 |
| blur | - | 编辑器失去焦点时触发 |
MarkedOptions 配置
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| breaks | boolean | false | 将换行符 \n 转换为 <br> |
| pedantic | boolean | false | 严格的 Markdown 模式 |
| smartypants | boolean | false | 智能引号和破折号 |
| gfm | boolean | false | 启用 GitHub 风格的 Markdown(GFM),支持表格、任务列表等 |
MarkdownEditorToolbar 组件
工具栏组件提供丰富的格式化工具,包括:
- 文本格式:粗体、斜体、下划线、删除线
- 标题:H1-H4 标题
- 列表:有序列表、无序列表
- 其他:引用、行内代码、表格、链接、图片、水平线
- 操作:撤销、重做、清除格式
<template>
<MarkdownEditorToolbar :editor="editorInstance" />
</template>
<script setup lang="ts">
import { MarkdownEditorToolbar } from '@lcap/spec-ui';
import type { Editor } from '@tiptap/core';
const editorInstance = ref<Editor | null>(null);
</script>MultiMarkdownEditor 组件
多文档编辑器组件,支持懒加载和工具栏集成:
<template>
<MultiMarkdownEditor
:documents="documents"
:min-height="'400px'"
:marked-options="{ gfm: true }"
:readonly="false"
@change="handleDocumentChange"
>
<template #header>
<div class="custom-header">自定义头部内容</div>
</template>
<template #placeholder="{ doc }">
<div class="custom-placeholder">加载中: {{ doc.key }}</div>
</template>
</MultiMarkdownEditor>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { MultiMarkdownEditor } from '@lcap/spec-ui';
const documents = ref([
{ key: 'doc-1', content: '# Document 1\n\nContent 1' },
{ key: 'doc-2', content: '# Document 2\n\nContent 2' },
{ key: 'doc-3', content: '# Document 3\n\nContent 3' },
]);
const handleDocumentChange = (event: { key: string; content: string }) => {
console.log(`Document ${event.key} changed:`, event.content);
};
</script>MultiMarkdownEditor 组件 Props
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| documents | Array<{ key: string; content: string }> | - | 文档列表 |
| minHeight | string | '400px' | 每个文档的最小高度(占位符高度) |
| markedOptions | MarkedOptions | - | MarkedJS 配置选项 |
| editorOptions | Partial<EditorOptions> | - | Tiptap 编辑器配置选项 |
| readonly | boolean | false | 是否只读 |
| documentClass | string | - | 文档容器的自定义类名 |
| documentStyle | string \| Record<string, string \| number> | - | 文档容器的自定义样式 |
MultiMarkdownEditor 组件 Events
| 事件名 | 参数 | 说明 |
|--------|------|------|
| change | event: { key: string; content: string } | 文档内容变化时触发 |
MultiMarkdownEditor 组件 Slots
| 插槽名 | 作用域 | 说明 |
|--------|--------|------|
| header | - | 自定义头部内容,显示在工具栏下方 |
| placeholder | { doc: { key: string; content: string } } | 自定义占位符内容,显示在文档加载前 |
Chat 聊天组件
Chat 组件库提供了一套完整的聊天界面组件,包括聊天容器、消息气泡、输入框、附件管理等。
Chat 主容器组件
Chat 组件是聊天界面的主容器,提供了头部、内容和底部三个区域的插槽。
<template>
<Chat title="AI 助手">
<template #meta>
<IconAiLogo />
<span>自定义标题</span>
</template>
<template #actions>
<IconButton>
<IconMoreList />
</IconButton>
</template>
<template #header>
<Sessions
:items="sessions"
:active-id="activeSessionId"
@select="handleSessionSelect"
@close="handleSessionClose"
/>
</template>
<div class="chat-content">
<BubbleList :items="messages" />
</div>
<template #footer>
<Sender
v-model="inputMessage"
:loading="sending"
@submit="handleSendMessage"
@paste-file="handlePasteFile"
>
<template #header>
<Attachments
v-model:opened="attachmentsOpened"
:attachments="attachments"
@remove="handleRemoveAttachment"
/>
</template>
<template #actions>
<Actions :items="actionItems" @click="handleActionClick" />
</template>
</Sender>
</template>
</Chat>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import {
Chat,
Sessions,
BubbleList,
Sender,
Attachments,
Actions,
IconButton
} from '@lcap/spec-ui';
import { IconAiLogo, IconMoreList } from '@lcap/spec-ui/icons';
const activeSessionId = ref('session-1');
const inputMessage = ref('');
const sending = ref(false);
const attachmentsOpened = ref(false);
const attachments = ref([]);
const sessions = ref([
{ id: 'session-1', name: '会话 1', status: 'normal' },
{ id: 'session-2', name: '会话 2', status: 'working' },
]);
const messages = ref([
{ role: 'user', id: 'msg-1', content: 'Hello' },
{ role: 'assistant', id: 'msg-2', content: 'Hi there!' },
]);
const handleSendMessage = (message: string) => {
console.log('发送消息:', message);
};
const handlePasteFile = (firstFile: File, files: FileList) => {
console.log('粘贴文件:', firstFile, files);
};
const handleSessionSelect = (id: string) => {
activeSessionId.value = id;
};
const handleSessionClose = (id: string) => {
console.log('关闭会话:', id);
};
const handleRemoveAttachment = (attachment: any) => {
console.log('移除附件:', attachment);
};
const handleActionClick = (key: string) => {
console.log('点击操作:', key);
};
const actionItems = [
{ label: '操作1', key: 'action1' },
{ label: '操作2', key: 'action2' },
];
</script>Chat 组件 Props
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| title | string | 'CodeWave智能AI助手' | 聊天标题 |
Chat 组件 Slots
| 插槽名 | 说明 | |--------|------| | meta | 自定义头部元信息(默认显示 AI Logo 和标题) | | actions | 自定义头部操作按钮 | | header | 自定义头部内容(通常用于放置 Sessions 组件) | | default | 聊天内容区域(通常用于放置 BubbleList 组件) | | footer | 底部内容(通常用于放置 Sender 组件) |
Sessions 会话列表组件
Sessions 组件用于显示和管理聊天会话列表。
<template>
<Sessions
:items="sessions"
:active-id="activeSessionId"
@select="handleSelect"
@close="handleClose"
/>
</template>
<script setup lang="ts">
import { Sessions } from '@lcap/spec-ui';
const sessions = ref([
{ id: 'session-1', name: '会话 1', status: 'normal' },
{ id: 'session-2', name: '会话 2', status: 'working' },
{ id: 'session-3', name: '会话 3', status: 'waiting' },
]);
const activeSessionId = ref('session-1');
</script>Sessions 组件 Props
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| items | Array<{ id: string; name: string; status: 'normal' \| 'working' \| 'waiting' }> | [] | 会话列表 |
| activeId | string | - | 当前激活的会话 ID |
| class | ClassValue | - | 自定义类名 |
| style | string \| Record<string, any> | - | 自定义样式 |
Sessions 组件 Events
| 事件名 | 参数 | 说明 |
|--------|------|------|
| select | id: string | 选择会话时触发 |
| close | id: string | 关闭会话时触发 |
Bubble 消息气泡组件
Bubble 组件用于显示单条消息,支持用户和助手两种布局。
<template>
<!-- 用户消息(右侧) -->
<Bubble placement="end">
<template #avatar>
<IconUser />
</template>
<div>这是用户消息</div>
</Bubble>
<!-- 助手消息(左侧) -->
<Bubble placement="start">
<template #avatar>
<IconAiLogo />
</template>
<template #header>
<span>AI 助手</span>
</template>
<div>这是助手消息</div>
<template #footer>
<span>2024-01-01 12:00</span>
</template>
</Bubble>
</template>
<script setup lang="ts">
import { Bubble } from '@lcap/spec-ui';
</script>Bubble 组件 Props
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| placement | 'start' \| 'end' | 'start' | 消息位置,start 为左侧(助手),end 为右侧(用户) |
| class | unknown | - | 自定义类名 |
Bubble 组件 Slots
| 插槽名 | 说明 | |--------|------| | avatar | 头像区域 | | header | 消息头部(如发送者名称) | | default | 消息内容 | | footer | 消息底部(如时间戳) |
BubbleList 消息列表组件
BubbleList 组件用于显示多条消息的列表,支持自动滚动到底部。
<template>
<!-- 默认渲染模式 -->
<BubbleList :items="messages">
<template #avatar="{ item }">
<IconAiLogo v-if="item.role === 'assistant'" />
<IconUser v-else />
</template>
<template #default="{ item }">
<div>{{ item.content }}</div>
</template>
</BubbleList>
<!-- 自定义渲染模式 -->
<BubbleList :items="messages" :custom-render="true">
<template #default="{ item, index }">
<div :class="item.role === 'user' ? 'user-msg' : 'assistant-msg'">
{{ item.content }}
</div>
</template>
<template #last>
<div class="loading-indicator">正在输入...</div>
</template>
</BubbleList>
</template>
<script setup lang="ts">
import { BubbleList } from '@lcap/spec-ui';
const messages = ref([
{ role: 'user', id: 'msg-1', content: 'Hello' },
{ role: 'assistant', id: 'msg-2', content: 'Hi there!' },
]);
</script>BubbleList 组件 Props
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| items | Array<{ role: 'user' \| 'assistant'; id: string } & Record<string, any>> | - | 消息列表 |
| customRender | boolean | false | 是否使用自定义渲染模式,为 true 时不会自动渲染 Bubble 组件,而是通过插槽自定义渲染 |
BubbleList 组件 Slots
| 插槽名 | 作用域 | 说明 |
|--------|--------|------|
| avatar | { item: MessageItem } | 头像区域(仅在默认渲染模式下有效) |
| header | { item: MessageItem } | 消息头部(仅在默认渲染模式下有效) |
| default | { item: MessageItem; index: number } | 消息内容,在自定义渲染模式下用于完全自定义消息渲染 |
| footer | { item: MessageItem } | 消息底部(仅在默认渲染模式下有效) |
| last | - | 列表底部内容,通常用于显示加载指示器等 |
注意: 当 customRender 为 true 时,组件不会自动渲染 Bubble 组件,你需要通过 default 插槽完全自定义每条消息的渲染。此时 avatar、header、footer 插槽不会生效。
Sender 消息输入组件
Sender 组件提供了消息输入框和发送功能。
<template>
<Sender
v-model="message"
:loading="sending"
:disabled="disabled"
submit-type="enter"
placeholder="请输入消息"
@submit="handleSubmit"
@paste-file="handlePasteFile"
>
<template #header>
<Attachments :attachments="attachments" />
</template>
<template #actions>
<Actions :items="actionItems" @click="handleActionClick" />
</template>
</Sender>
</template>
<script setup lang="ts">
import { Sender, Attachments, Actions } from '@lcap/spec-ui';
const message = ref('');
const sending = ref(false);
const disabled = ref(false);
</script>Sender 组件 Props
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| modelValue | string | - | 输入内容,支持 v-model |
| submitType | 'enter' \| 'shift-enter' | 'enter' | 提交方式,enter 为回车提交,shift-enter 为 Shift+Enter 提交 |
| placeholder | string | '请输入内容' | 占位符文本 |
| disabled | boolean | false | 是否禁用 |
| loading | boolean | false | 是否加载中 |
| rows | number | 3 | 文本域行数 |
| autosize | boolean \| { minRows: number; maxRows: number } | { minRows: 3, maxRows: 6 } | 是否自动调整高度 |
| class | unknown | - | 自定义类名 |
| style | unknown | - | 自定义样式 |
Sender 组件 Events
| 事件名 | 参数 | 说明 |
|--------|------|------|
| submit | message: string | 提交消息时触发 |
| cancel | - | 取消时触发 |
| change | message: string | 内容变化时触发 |
| update:modelValue | message: string | 内容更新时触发(支持 v-model) |
| pasteFile | firstFile: File, files: FileList | 粘贴文件时触发 |
| focus | event: FocusEvent | 获得焦点时触发 |
| blur | event: FocusEvent | 失去焦点时触发 |
Sender 组件 Slots
| 插槽名 | 说明 | |--------|------| | header | 输入框上方内容(通常用于放置 Attachments 组件) | | footer | 输入框下方内容 | | actions | 操作按钮区域(通常用于放置 Actions 组件) |
Attachments 附件组件
Attachments 组件用于显示和管理附件列表。
<template>
<Attachments
v-model:opened="opened"
:attachments="attachments"
@remove="handleRemove"
/>
</template>
<script setup lang="ts">
import { Attachments } from '@lcap/spec-ui';
const opened = ref(false);
const attachments = ref([
{
key: '1',
url: 'https://example.com/image.jpg',
mimeType: 'image/jpeg',
name: 'image.jpg',
size: 1024000,
},
{
key: '2',
mimeType: 'application/pdf',
name: 'document.pdf',
size: 2048000,
},
]);
</script>Attachments 组件 Props
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| opened | boolean | false | 是否展开,支持 v-model:opened |
| attachments | Array<Attachment> | [] | 附件列表 |
| containerClass | ClassValue | - | 容器自定义类名 |
| class | ClassValue | - | 自定义类名 |
| style | unknown | - | 自定义样式 |
Attachment 类型
interface Attachment {
key?: string | number; // 唯一标识
url?: string; // 文件 URL(用于预览)
mimeType: string; // MIME 类型
name: string; // 文件名
size: number; // 文件大小(字节)
}Attachments 组件 Events
| 事件名 | 参数 | 说明 |
|--------|------|------|
| remove | attachment: Attachment | 移除附件时触发 |
| update:opened | opened: boolean | 展开状态更新时触发(支持 v-model:opened) |
Actions 操作按钮组件
Actions 组件用于显示操作按钮列表。
<template>
<Actions
:items="actionItems"
direction="horizontal"
@click="handleClick"
/>
</template>
<script setup lang="ts">
import { Actions } from '@lcap/spec-ui';
import { IconAdd, IconDelete } from '@lcap/spec-ui/icons';
const actionItems = [
{ label: '添加', key: 'add', icon: IconAdd },
{ label: '删除', key: 'delete', icon: IconDelete },
];
</script>Actions 组件 Props
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| items | Array<{ label: string; icon?: Component; key: string }> | [] | 操作项列表 |
| direction | 'vertical' \| 'horizontal' | 'horizontal' | 排列方向 |
| class | ClassValue | - | 自定义类名 |
| style | unknown | - | 自定义样式 |
Actions 组件 Events
| 事件名 | 参数 | 说明 |
|--------|------|------|
| click | key: string, event: MouseEvent | 点击操作项时触发 |
IconButton 图标按钮组件
IconButton 组件用于显示图标按钮。
<template>
<IconButton
:size="24"
:active="isActive"
:disabled="isDisabled"
tooltip="提示文本"
tooltip-placement="top"
>
<IconMoreList />
</IconButton>
</template>
<script setup lang="ts">
import { IconButton } from '@lcap/spec-ui';
import { IconMoreList } from '@lcap/spec-ui/icons';
</script>IconButton 组件 Props
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| size | number | 24 | 按钮大小 |
| active | boolean | false | 是否激活/选中状态 |
| disabled | boolean | false | 是否禁用 |
| tooltip | string | - | Tooltip 提示文本 |
| tooltipPlacement | 'top' \| 'bottom' \| 'left' \| 'right' \| 'top-start' \| 'top-end' \| 'bottom-start' \| 'bottom-end' \| 'left-start' \| 'left-end' \| 'right-start' \| 'right-end' | 'top' | Tooltip 显示位置 |
AttachContainer 附件容器组件
AttachContainer 组件是附件的容器组件,提供统一的样式和布局。
<template>
<AttachContainer :visible="visible" :radius="8">
<template #content>
<!-- 附件内容 -->
</template>
<!-- 其他内容 -->
</AttachContainer>
</template>
<script setup lang="ts">
import { AttachContainer } from '@lcap/spec-ui';
</script>AttachContainer 组件 Props
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| radius | number | - | 圆角半径 |
| containerClass | ClassValue | - | 容器自定义类名 |
| visible | boolean | - | 是否可见 |
AttachContainer 组件 Slots
| 插槽名 | 说明 | |--------|------| | content | 附件内容区域 | | default | 其他内容 |
开发
安装依赖
pnpm install启动开发服务器
pnpm dev开发服务器会在 http://localhost:3000 启动,自动打开浏览器。
运行测试
# 运行所有测试
pnpm test
# 监听模式运行测试
pnpm test:watch构建
pnpm build构建产物会输出到 dist 目录,按组件目录分 chunk:
dist/
├── index.mjs # 主入口(ES Module)
├── index.d.ts # 类型定义
├── index.css # 样式文件
├── components/
│ ├── pdf-viewer/
│ │ ├── index.mjs # PDF 预览组件入口
│ │ └── index.d.ts # 类型定义
│ ├── markdown-editor/
│ │ ├── index.mjs # Markdown 编辑器组件入口
│ │ └── index.d.ts # 类型定义
│ └── diff-viewer/
│ ├── index.mjs # Diff 查看器组件入口
│ └── index.d.ts # 类型定义
└── chunks/ # 其他 chunk类型检查
pnpm type-check项目结构
spec-ui/
├── src/
│ ├── components/ # 组件目录
│ │ ├── pdf-viewer/ # PDF 预览组件
│ │ │ ├── PdfViewer.vue
│ │ │ ├── index.ts
│ │ │ └── types.ts
│ │ ├── markdown-editor/ # Markdown 编辑器组件
│ │ │ ├── MarkdownEditor.vue
│ │ │ ├── MarkdownEditorToolbar.vue
│ │ │ ├── MultiMarkdownEditor.vue
│ │ │ ├── types.ts
│ │ │ ├── tools/ # 工具栏工具组件
│ │ │ └── index.ts
│ │ └── diff-viewer/ # Diff 查看器组件
│ │ ├── DiffViewer.vue
│ │ ├── index.ts
│ │ └── types.ts
│ │ └── chat/ # Chat 聊天组件
│ │ ├── Chat.vue
│ │ ├── Sessions.vue
│ │ ├── Bubble.vue
│ │ ├── BubbleList.vue
│ │ ├── Sender.vue
│ │ ├── Attachments.vue
│ │ ├── AttachContainer.vue
│ │ ├── Actions.vue
│ │ ├── IconButton.vue
│ │ ├── SendButton.vue
│ │ ├── types.ts
│ │ └── index.ts
│ │ └── icons/ # 图标组件
│ │ ├── icon-*.vue # 各种图标组件
│ │ └── index.ts
│ ├── styles/ # 样式文件
│ │ └── variables.css # CSS 变量
│ └── index.ts # 主入口
├── examples/ # 示例和开发调试环境
│ ├── index.html
│ ├── main.ts
│ ├── App.vue
│ └── components/
│ ├── chat/
│ │ └── ChatDemo.vue
│ ├── diff-viewer/
│ │ └── DiffViewerDemo.vue
│ ├── markdown-editor/
│ │ ├── MarkdownEditorDemo.vue
│ │ └── MultiMarkdownEditorDemo.vue
│ └── pdf-viewer/
│ └── PdfViewerDemo.vue
├── tests/ # 测试文件
│ ├── setup.ts # 测试设置文件
│ └── components/
│ ├── pdf-viewer.test.ts
│ ├── diff-viewer.test.ts
│ └── multi-markdown-editor.test.ts
├── scripts/ # 构建脚本
│ └── generate-component-dts.js # 生成组件类型定义脚本
├── vite.config.ts # Vite 配置
├── vitest.config.ts # Vitest 配置
└── package.json按需引入
组件库支持按需引入,每个组件都会被打包成独立的 chunk:
// 引入整个组件库
import {
PdfViewer,
DiffViewer,
MarkdownEditor,
MarkdownEditorToolbar,
MultiMarkdownEditor,
Chat,
Sessions,
Bubble,
BubbleList,
Sender,
Attachments,
Actions,
IconButton
} from '@lcap/spec-ui';
// 引入样式文件
import '@lcap/spec-ui/index.css';
// 或者按组件引入
import { PdfViewer } from '@lcap/spec-ui/pdf-viewer';
import {
MarkdownEditor,
MarkdownEditorToolbar,
MultiMarkdownEditor
} from '@lcap/spec-ui/markdown-editor';
import { DiffViewer } from '@lcap/spec-ui/diff-viewer';
// 引入图标组件
import { IconAiLogo, IconSend, IconAdd } from '@lcap/spec-ui/icons';注意: 目前 chat 组件暂不支持单独的导出路径,请从主入口引入。
License
MIT
