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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@m2lan/ai-chat-widget

v0.1.6

Published

可嵌入的 AI 对话组件,适配 AgentX 后端,任何 Vue 3 项目一行引入即可使用

Readme

@m2lan/ai-chat-widget

开箱即用的 AI 聊天 Widget,适用于任何 Vue 3 项目。支持流式响应、实体链接、图片/文件上传、会话管理、暗黑模式、国际化、消息反馈、Emoji 选择器、拖拽上传、图片预览。

安装

npm install @m2lan/ai-chat-widget

快速开始

<script setup>
import { AiChatWidget, createAgentxAdapter } from '@m2lan/ai-chat-widget';
import '@m2lan/ai-chat-widget/style.css';

const config = {
  api: createAgentxAdapter({
    baseUrl: '/dev-api',
    getHeaders: () => ({ Authorization: 'Bearer ' + token })
  }),
  assistantName: 'ERP 助手',
  quickActions: [
    { label: '新增客户', prompt: '帮我新增一个客户' }
  ]
};
</script>

<template>
  <AiChatWidget :config="config" />
</template>

也可以 headless 使用 — 只用 composables 不用 UI 组件,自己画界面。ChatApiAdapter 接口是唯一的 SPI,宿主实现这个接口就能对接任何后端。


ChatWidgetConfig 完整配置

| 配置 | 类型 | 默认值 | 说明 | |------|------|--------|------| | api | ChatApiAdapter | 必填 | API 适配器 | | entityRoutes | Record<string, string> | — | 实体链接路由映射 | | onEntityClick | (id, type) => void | — | 实体链接点击回调 | | assistantName | string | 'AI 助手' | 助手名称 | | assistantAvatar | string | — | 助手头像 URL | | welcomeMessage | string | — | 欢迎语 | | quickActions | QuickAction[] | — | 快捷操作按钮 | | streaming | boolean | true | 是否启用流式响应 | | storageKey | string \| false | 'agentx_sessions' | 会话持久化 key,false 禁用 | | height | string | '600px' | 抽屉高度 | | hideFab | boolean | false | 隐藏右下角悬浮按钮 | | theme | 'light' \| 'dark' \| 'auto' | 'auto' | 主题模式 | | locale | 'zh' \| 'en' | 'zh' | 语言 | | 附件上传 | | | | | enableImages | boolean | 自动检测 | 是否启用图片上传 | | enableFiles | boolean | 自动检测 | 是否启用文件上传 | | maxImages | number | 5 | 单次最多上传图片数 | | maxFiles | number | 5 | 单次最多上传文件数 | | maxFileSize | number | 52428800 (50MB) | 单个文件大小上限(字节) | | acceptFileTypes | string | .pdf,.doc,.docx,... | 文件选择器 accept 属性 | | maxMessageLength | number | 4000 | 消息最大字符数 | | maxRetries | number | 2 | 网络请求失败自动重试次数 | | 事件回调 | | | | | onMessageSent | (msg: ChatMessage) => void | — | 用户发送消息后触发 | | onMessageReceived | (msg: ChatMessage) => void | — | 收到 AI 回复后触发 | | onError | (error: string) => void | — | 发生错误时触发 | | onSessionChange | (sessionId: string \| null) => void | — | 切换/创建/删除会话时触发 | | onOpen | () => void | — | 打开面板时触发 | | onClose | () => void | — | 关闭面板时触发 | | onFeedback | (messageId, feedback) => void | — | 用户点击反馈按钮时触发 |


功能详解

暗黑模式

支持三种模式:light(浅色)、dark(深色)、auto(跟随系统 prefers-color-scheme)。

const config = {
  api: adapter,
  theme: 'dark', // 或 'light'、'auto'
};

所有样式通过 CSS 变量实现,宿主页面也可覆盖:

:root {
  --acw-primary: #409eff;
  --acw-bg: #fff;
  --acw-text: #303133;
  /* ...完整变量列表见 style.css */
}

国际化 (i18n)

内置中文 (zh) 和英文 (en) 两套翻译。

const config = {
  api: adapter,
  locale: 'en', // 切换为英文
};

消息反馈(点赞/点踩)

AI 回复的操作栏自动显示 👍👎 按钮。点击切换,再次点击取消。通过 onFeedback 回调收集用户满意度:

const config = {
  api: adapter,
  onFeedback: (messageId, feedback) => {
    // feedback: 'up' | 'down'
    analytics.track('message_feedback', { messageId, feedback });
  },
};

反馈数据存储在 ChatMessage.feedback 字段,随会话一起持久化。

事件回调系统

const config = {
  api: adapter,
  onMessageSent: (msg) => console.log('用户发送:', msg.content),
  onMessageReceived: (msg) => console.log('AI 回复:', msg.content),
  onError: (err) => console.error('错误:', err),
  onSessionChange: (id) => console.log('切换会话:', id),
  onOpen: () => console.log('面板打开'),
  onClose: () => console.log('面板关闭'),
};

拖拽上传

将文件拖拽到聊天区域即可上传。自动识别图片和文档类型,复用已有的上传逻辑。

Emoji 选择器

输入框工具栏左侧新增表情按钮,提供 4 个分类(表情、手势、物体、符号),点击插入到光标位置。

图片 Lightbox(预览)

点击消息中的图片弹出全屏预览,支持:

  • 左右箭头切换
  • 键盘 ESC 关闭
  • 多图计数器显示

打字动画

流式接收 AI 回复时,消息末尾显示闪烁光标 ▊,接收完成后自动消失。

消息分页

当单个会话消息数超过 50 条时,自动启用分页,只渲染最近的消息。顶部显示"加载更早消息"按钮,点击加载更多。


附件上传

ChatInput 支持两种附件类型,各自独立开关:

  • 📷 图片上传:支持点击上传和剪贴板粘贴,预览缩略图
  • 📎 文件上传:支持 PDF、Word、Excel、PPT、TXT、CSV,显示文件名和大小

前提条件:适配器需实现 uploadFile 方法,否则上传按钮自动隐藏。

interface ChatApiAdapter {
  uploadFile?(file: File): Promise<string>;
}

使用示例

// 默认行为:有 uploadFile 就自动启用
const config = { api: createAgentxAdapter({ baseUrl: '/dev-api', getHeaders }) };

// 只开图片,关闭文件上传
const config = { api: adapter, enableImages: true, enableFiles: false };

// 自定义文件限制
const config = {
  api: adapter,
  maxFiles: 3,
  maxFileSize: 10 * 1024 * 1024,
  acceptFileTypes: '.pdf,.docx',
};

适配器

AgentX 适配器(预置)

对接 AgentX 后端(agent-spring-boot-starter 端点):

import { createAgentxAdapter } from '@m2lan/ai-chat-widget';

const adapter = createAgentxAdapter({
  baseUrl: '/dev-api',
  getHeaders: () => ({ Authorization: 'Bearer ' + token }),
});

Chat Service 适配器(预置)

对接通用 Chat API(/api/v1/chat 端点 + SSE):

import { createChatServiceAdapter } from '@m2lan/ai-chat-widget';

const adapter = createChatServiceAdapter({
  baseUrl: '/api/v1',
  getHeaders: () => ({ Authorization: 'Bearer ' + token }),
});

自定义适配器

实现 ChatApiAdapter 接口即可对接任意后端:

import type { ChatApiAdapter } from '@m2lan/ai-chat-widget';

const adapter: ChatApiAdapter = {
  async sendMessage(req) {
    const res = await fetch('/api/chat', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ message: req.message, sessionId: req.sessionId }),
      signal: req.signal,
    });
    return res.json();
  },
  async sendMessageStream(req) {
    return fetch('/api/chat/stream', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ message: req.message, sessionId: req.sessionId }),
      signal: req.signal,
    });
  },
};

Headless 使用

不用 UI 组件,只用 composables 自己画界面:

import { createSessionManager, useChat, useStream, renderMessageContent } from '@m2lan/ai-chat-widget';

const sm = createSessionManager({ storageKey: 'my_sessions' });
const chat = useChat({ api: adapter }, sm);

// 发送消息
await chat.send('你好');

// 当前会话
console.log(sm.activeSession.value?.messages);

实体链接

AI 回复中的 [客户名称](entity:customer:123) 格式会自动渲染为可点击的实体标签。

配置路由映射:

const config = {
  api: adapter,
  entityRoutes: {
    customer: '/system/customer/detail',
    order: '/system/order/detail',
  },
};

或使用回调:

const config = {
  api: adapter,
  onEntityClick: (id, type) => router.push(`/${type}/${id}`),
};

导出清单

| 导出 | 类型 | 说明 | | --- | --- | --- | | AiChatWidget | 组件 | 主组件(悬浮按钮 + 抽屉面板) | | MessageBubble | 组件 | 消息气泡 | | ChatInput | 组件 | 输入组件 | | ErrorBoundary | 组件 | 错误边界 | | EmojiPicker | 组件 | Emoji 选择器 | | ImageLightbox | 组件 | 图片预览 | | createSessionManager | 函数 | 会话管理 composable | | useChat | 函数 | 聊天逻辑 composable | | useStream | 函数 | SSE 流式解析 composable | | useVirtualScroll | 函数 | 消息分页 composable | | renderMessageContent | 函数 | Markdown 渲染 + XSS 防护 | | sanitizeHtml | 函数 | HTML 消毒 | | createEntityClickHandler | 函数 | 实体链接点击处理 | | copyToClipboard | 函数 | 复制到剪贴板 | | provideConfig / useConfig | 函数 | provide/inject 配置注入 | | useT | 函数 | 国际化翻译函数 | | createAgentxAdapter | 函数 | AgentX 适配器 | | createChatServiceAdapter | 函数 | Chat Service 适配器 |


本地开发

# 构建 widget
cd ai-chat-widget && npm run build

# 在消费方 package.json 中引用本地路径
# "@m2lan/ai-chat-widget": "file:../ai-chat-widget"

# 重新安装
cd wl95-home-ui && npm install

目录结构

ai-chat-widget/
├── src/
│   ├── adapters/
│   │   ├── agentx.ts              # AgentX 后端预置适配器
│   │   ├── chat.ts                # Chat Service 适配器
│   │   └── erp-agent.ts           # ERP Agent 适配器
│   ├── components/
│   │   ├── AiChatWidget.vue       # 主组件(悬浮按钮+抽屉+侧边栏)
│   │   ├── ChatInput.vue          # 输入组件(图片/文件/Emoji)
│   │   ├── MessageBubble.vue      # 消息气泡(反馈/打字动画/Lightbox)
│   │   ├── ErrorBoundary.vue      # 错误边界
│   │   ├── EmojiPicker.vue        # Emoji 选择器
│   │   └── ImageLightbox.vue      # 图片预览
│   ├── composables/
│   │   ├── useChat.ts             # 核心聊天逻辑(同步+流式+重试+中断)
│   │   ├── useEntityLink.ts       # 实体链接渲染 + XSS 防护 + 代码块增强
│   │   ├── useSession.ts          # 会话管理 + localStorage 持久化
│   │   ├── useStream.ts           # SSE 流式解析(兼容多种后端格式)
│   │   └── useVirtualScroll.ts    # 消息分页
│   ├── config.ts                  # provide/inject 配置注入
│   ├── i18n.ts                    # 国际化
│   ├── types.ts                   # 所有类型定义 + ChatApiAdapter SPI
│   ├── style.css                  # 全量样式(acw- 前缀,暗黑模式支持)
│   └── index.ts                   # 包导出入口
└── package.json