npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@lcap/spec-ui

v0.0.1

Published

Vue3 UI component library

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 | - | 列表底部内容,通常用于显示加载指示器等 |

注意:customRendertrue 时,组件不会自动渲染 Bubble 组件,你需要通过 default 插槽完全自定义每条消息的渲染。此时 avatarheaderfooter 插槽不会生效。

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