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

@yeez-tech/chat-tool

v0.3.3

Published

Embeddable OpenIM chat window for Vue apps, powered by ChatUI and OpenIM WASM SDK

Readme

@yeez-tech/chat-tool

可嵌入 Vue 应用的 OpenIM 聊天能力,UI 基于 ChatUI,IM 基于 OpenIM WASM SDK。

集成方式对齐 dianshu / Rocket.Chat:在 App 根节点挂载一次 全局聊天壳,业务页通过 useOpenIMContact() 打开会话(如「联系卖家」),而不是每个页面各自 mount 聊天组件。

示例窗口

安装

npm install @yeez-tech/chat-tool vue @openim/wasm-client-sdk

0.2.0 起@openim/wasm-client-sdkpeerDependency,本包不再把 SDK 打进 chat-tool.mjs;宿主须自行安装 SDK,并部署浏览器静态资源(对齐 OpenIM Browser Quickstart)。

宿主必须提供的静态资源

| 文件 | 建议路径 | 说明 | | ---- | -------- | ---- | | wasm_exec.js | /wasm_exec.js | 在 index.html / Nuxt app.head<script src="/wasm_exec.js"> 先于聊天组件加载 | | worker.js | /@openim/wasm-client-sdk/lib/worker.js | SDK 在 Vite/Nuxt dev 下会把预构建路径改写为此目录,见下方同步脚本 | | worker-legacy.js | 同上目录 | 不支持 Module Worker 的浏览器 | | openIM.wasm | /openIM.wasm 或 CDN | 可通过 core-wasm-path 覆盖 | | sql-wasm.wasm | /sql-wasm.wasm 或 CDN | 可通过 sql-wasm-path 覆盖 |

一键从 node_modules 同步到宿主 public/不修改 OpenIM SDK 源码):

node node_modules/@yeez-tech/chat-tool/scripts/sync-openim-static.mjs public

生产构建(Nuxt/Vite)还可把 worker.js 复制到客户端 chunk 同级目录(/_nuxt/worker.js),可使用包内 Vite 插件。

默认(推荐 SSR 主站同源):不传插件参数、不传组件 props 时,Worker 仍走 public/@openim/...(与 sync-openim-static.mjs 一致)。

Nuxt 打包到 _nuxt/(可选,需与 openimWorkerCopyPlugin 成对使用):

openimViteSdkPatchPlugin({ workerBasePath: 'bundled' }),
openimWorkerCopyPlugin(),

'bundled' 会根据 Vite base + build.assetsDir 自动解析(Nuxt 默认 /_nuxt/)。也可在组件上传 worker-base-path 覆盖。

// nuxt.config.ts → vite.plugins
import {
  openimViteSdkPatchPlugin,
  openimWorkerCopyPlugin,
} from "@yeez-tech/chat-tool/scripts/openim-vite-plugin.mjs";
import { chatToolSafariRegexpPlugin } from "@yeez-tech/chat-tool/scripts/chat-tool-safari-regexp-plugin.mjs";

export default defineNuxtConfig({
  vite: {
    plugins: [
      openimViteSdkPatchPlugin(), // 默认 public/@openim;bundled 模式见上
      chatToolSafariRegexpPlugin(),
      openimWorkerCopyPlugin(),
    ],
    optimizeDeps: {
      exclude: ["@yeez-tech/chat-tool"],
      include: ["@openim/wasm-client-sdk"],
    },
  },
  app: {
    head: {
      script: [{ src: "/wasm_exec.js" }],
    },
  },
});

Vue CLI / Webpack 宿主(如典枢 website)请使用 openim-webpack-worker-loader.cjs打包时 patch SDK(不修改 node_modules),并复制 worker 到 public/@openim/wasm-client-sdk/lib/0.2.2 起 loader 使用 CommonJS patch(修复生产构建 ERR_REQUIRE_ESM);可通过 loader options.workerBasePath 或组件 worker-base-path 覆盖 Worker 目录。详见下方 Safari 兼容

ChatUI 工具栏/导航图标已随包内置(ensureChatuiIcons),宿主无需再引入 CDN 图标脚本。

Nuxt 等使用 vite-plugin-node-polyfills 的宿主:包内会在初始化前自动补齐 process.pid 等字段(ensureGoProcessShim),无需再写 openim-process-shim 一类插件。

Safari 兼容(含 Safari 16.1 / iOS 16.x)

Safari 上常见问题来自三层,0.2.0 分别在包内与宿主侧处理:

| 层级 | 现象 | 处理方式 | | ---- | ---- | -------- | | ChatUI bundle | 页面白屏 / Invalid regular expression: invalid group specifier name | 本包 构建时 自动 patch(见下) | | OpenIM WASM | commonEventFunc is not a function、WASM 不启动 | 宿主静态资源 + Webpack loader / CDN MIME | | 宿主业务代码 | 首页 SSR 脚本解析失败 | 宿主自行避免 lookbehind 等 Safari 不支持的语法(与 chat-tool 无关) |

1. ChatUI RegExp 探针(本包构建时自动处理)

@chatui/core 依赖链会在模块加载阶段探测 (?<a>b) 命名捕获;Safari < 16.4(含 16.1)会直接抛 SyntaxError,导致聊天 chunk 无法加载。

npm run build 末尾会自动运行 scripts/sanitize-regexp-ncg.mjs,改写 dist/chat-tool.mjs / dist/chat-tool.cjs

  • 将命名捕获探针 stub 为 true
  • 注入 __chatToolRegExpFails,避免 Safari 上 feature 检测本身抛错
  • patch 函数追加在 bundle 末尾(不插在 import 前,避免 Safari ESM 报错)

Nuxt 开发模式若使用 file:../chatui-openim-demo 且 dist 未及时重建,可在 nuxt.config.ts 启用 dev 兜底插件(见上方示例中的 chatToolSafariRegexpPlugin())。

2. OpenIM WASM / Worker(宿主集成)

Safari 还需注意 OpenIM SDK 运行时行为(不修改 @openim/wasm-client-sdk 磁盘源码):

| 问题 | Safari 行为 | 宿主应对 | | ---- | ----------- | -------- | | Module Worker | 部分版本不稳定 | Safari 使用 worker-legacy.jssync-openim-static.mjs 已同步) | | Nuxt cdnURL 跨域 | Failed to construct 'Worker'(CDN /_nuxt/worker.*.js ≠ 页面域名) | 必须 openimViteSdkPatchPlugin(),Worker 走同源 /@openim/wasm-client-sdk/lib/ | | instantiateStreaming | CDN / MIME 不符时易失败 | Webpack 宿主用 loader 改为 arrayBuffer + instantiate | | WASM 404 / MIME | commonEventFunc is not a function | CDN 返回 200Content-Type: application/wasm(勿用 SPA fallback 返回 HTML) | | Webpack 5 打包 worker | Worker 内 require is not defined | 使用 openim-webpack-worker-loader.cjs 改为从 public/@openim/wasm-client-sdk/lib/ 加载 |

Vue CLI 示例(vue.config.jschainWebpack):

const openimSdkEntry = path.resolve(
  __dirname,
  "node_modules/@openim/wasm-client-sdk/lib/index.es.js"
);
const openimWebpackWorkerLoader = path.resolve(
  __dirname,
  "node_modules/@yeez-tech/chat-tool/scripts/openim-webpack-worker-loader.cjs"
);

config.module
  .rule("openim-sdk-webpack-worker")
  .test(/index\.es\.js$/)
  .include.add(openimSdkEntry)
  .end()
  .use("openim-webpack-worker-loader")
  .loader(openimWebpackWorkerLoader);

config.resolve.alias
  .set(
    "@yeez-tech/chat-tool$",
    path.resolve(__dirname, "node_modules/@yeez-tech/chat-tool/dist/chat-tool.mjs")
  )
  .set("@openim/wasm-client-sdk$", openimSdkEntry);

loader 还会在打包时修正 sqlWasmPath 初始化顺序,并导出 __chatToolResetSDKSingleton(WASM 加载失败后可重置单例重试)。

WASM 路径: 生产环境建议通过 props / 环境变量指向 CDN,例如:

VUE_APP_OPENIM_CORE_WASM_PATH=https://cdn.example.com/web/im.wasm
VUE_APP_OPENIM_SQL_WASM_PATH=https://cdn.example.com/web/sql.wasm

Network 面板应能看到 im.wasm(约 34MB)返回 200 且 Type 为 wasm;若出现 ERR_CONTENT_LENGTH_MISMATCH,清除站点 Cache Storage 后重试。

3. 宿主自检清单

  • [ ] postinstall 或 CI 中执行 sync-openim-static.mjs public
  • [ ] index.html / Nuxt app.head 已加载 /wasm_exec.js(先于聊天组件)
  • [ ] Nuxt:openimViteSdkPatchPlugincdnURL 跨域必填)+ chatToolSafariRegexpPlugin;Vite optimizeDeps.exclude@yeez-tech/chat-tool
  • [ ] Webpack:openim-webpack-worker-loader + @yeez-tech/chat-tool alias 到 dist/chat-tool.mjs
  • [ ] WASM CDN Content-Type: application/wasm
  • [ ] 联系卖家等业务入口传递 uniqueUserId(OpenIM userID 与店铺 ID 映射)

推荐集成(dianshu 模式)

1. App 根节点挂载一次

<!-- App.vue -->
<script setup lang="ts">
import { OpenIMChatApp } from "@yeez-tech/chat-tool";
import "@yeez-tech/chat-tool/style.css";

const apiAddr = "...";
const wsAddr = "...";
const userID = "...";
const token = "...";
</script>

<template>
  <router-view />
  <OpenIMChatApp
    :apiAddr="apiAddr"
    :wsAddr="wsAddr"
    :userID="userID"
    :token="token"
    :platformID="5"
    auto-login
  />
</template>

OpenIMChatApp = provide 聊天控制能力 + 浮层壳 + 内部 OpenIMChat(对标 dianshu 的 provide(chatControls) + FloatingCustomerService)。

2. 业务页「联系卖家」

<script setup lang="ts">
import { useOpenIMContact } from "@yeez-tech/chat-tool";

const { handleContact } = useOpenIMContact();

async function onContactSeller() {
  // 卖家 OpenIM userID(由业务后端映射,对标 dianshu 的 createUser → roomId)
  await handleContact({ userID: sellerOpenIMUserId });
}
</script>

<template>
  <button type="button" @click="onContactSeller">联系卖家</button>
</template>

| dianshu | @yeez-tech/chat-tool | | ------------------------------------ | ------------------------------------------- | | handleContact({ createUser }) | handleContact({ userID }) | | POST /chat/conversationroomId | SDK getOneConversationconversationID | | iframe go /direct/{roomId} | 内部切会话 + 拉历史 | | FloatingCustomerService 全局单例 | OpenIMChatApp 全局单例 |

其他入口

const { openInbox, openChat, closeChat } = useOpenIMContact();

openInbox(); // 打开会话列表(对标「我的消息」)
openChat(); // 仅显示浮层
closeChat(); // 关闭浮层

补充说明(弹窗壳 shellMode="popup"):

  • openInbox():标准会话模式,显示左侧会话列表 + 右侧聊天区。
  • handleContact({ userID | groupID }):单会话模式,仅显示目标会话聊天区(隐藏左侧会话列表),适合“联系卖家”入口。
  • 可在两种入口之间切换:调用 openInbox() 会回到标准会话模式。

架构

App.vue
  └── <OpenIMChatApp />          ← provide(openIMChatControlsKey)
        └── OpenIMChatHost       ← 壳层(popup 浮窗 / fullpage 全页面)
              └── OpenIMChat     ← React + ChatUI + WASM SDK

商品页 / 订单页
  └── useOpenIMContact()
        └── openConversation({ userID })  → SDK + 切换会话

宿主接入注意(Nuxt / SSR)

  • 全屏聊天页shell-mode="fullpage"):使用默认 layoutauto)即可;layout="wide",否则窄屏无法切换列表/会话分屏。
  • 输入区变高:需设置 composer-height / compact-composer-height(外框);仅改 *-textarea-min-height 可能无可见效果(见下方输入区高度说明)。
  • 表情面板:点击笑脸应在输入框上方弹出 emoji 网格并插入文本(非 sendFace 直发);若只见空白条请升级到 ≥ 0.3.3
  • 弹窗(联系卖家 / 客服):OpenIMChatHost 会自动维护 host-chat-open;宿主需 auto-login 且在 userID + token 就绪后再挂载组件(避免空凭证触发设置面板)。
  • 过早点击「联系卖家」:组件会在登录 / WASM 就绪后自动重试打开会话,并显示加载蒙层,无需宿主额外处理。

Props(传给 OpenIMChatApp / OpenIMChat

Vue 模板请使用 camelCase 对应的 kebab-case(如 compactComposerHeightcompact-composer-height)。
连接类 prop 请用 :userID:platformID 等,写成 :user-id / :platform-id(Vue 会映射成错误的属性名)。

连接与登录

| Prop | 类型 | 默认 | 说明 | | ---- | ---- | ---- | ---- | | apiAddr | string | — | OpenIM HTTP API 根地址 | | wsAddr | string | — | OpenIM WebSocket 地址 | | userID | string | — | 当前用户 OpenIM userID | | token | string | — | 登录 token(由业务鉴权接口下发) | | platformID | number | 5 | 平台 ID(Web 一般为 5) | | autoLogin | boolean | false | 有 userID + token 时自动调用 SDK login | | persistConfig | boolean | true | 是否将连接设置写入 localStorage | | storageKey | string | openim-chatui-config | persistConfig 为 true 时的存储键 | | debug | boolean | false | 开启 OpenIM WASM SDK 调试日志 |

壳层与布局

| Prop | 类型 | 默认 | 说明 | | ---- | ---- | ---- | ---- | | shellMode | 'popup' \| 'fullpage' | popup | popup:全局浮窗;fullpage:占满父容器(如 /chat 新标签页) | | layout | 'auto' \| 'compact' \| 'wide' | auto | 见下方 布局说明 | | compactBreakpoint | number | 768 | layout='auto'shellMode='fullpage' 时,容器宽度 ≤ 此值则切换为紧凑分屏 | | hostChatOpen | boolean | — | 是否应对外同步会话/历史:fullpage 应为 true;popup 在浮层打开时为 true。使用 OpenIMChatHost 时会自动注入,一般无需手写 | | initialPeerUserID | string | — | 登录并拉取列表后,自动打开指定对方 userID 的会话(常用于全屏页深链);fullpage 仍保留左侧列表 |

弹窗尺寸(仅 shellMode='popup'

| Prop | 类型 | 默认 | 说明 | | ---- | ---- | ---- | ---- | | chatWindowWidth | number | 800 | 浮窗宽度(px),由 OpenIMChatHost 读取 | | chatWindowMinWidth | number | 400 | 浮窗最小宽度(px);拖拽缩放下限与 CSS min-width | | chatWindowHeight | number | 600 | 浮窗高度(px) | | chatMinHeight | number | 700 | 弹窗壳 min-height(px) | | compactChatMinHeight | number | — | 紧凑布局壳最小高度;未传时回退 chatMinHeight |

输入区高度

通过 CSS 变量注入,无需 :deep() 覆盖样式。默认值见包内 DEFAULT_* 常量。

| Prop | 类型 | CSS 默认 | 说明 | | ---- | ---- | -------- | ---- | | composerHeight | number | 140 | 宽屏布局下整条输入区外框高度(px) | | compactComposerHeight | number | 100 | 紧凑布局下输入区外框高度(px);未传时回退 composerHeight | | composerTextareaMinHeight | number | 80 | 宽屏下文本框 min-height(px) | | compactComposerTextareaMinHeight | number | 50 | 紧凑布局下文本框 min-height(px);未传时回退 composerTextareaMinHeight |

注意: 外框 .im-composer 使用固定 height,仅增大 *TextareaMinHeight 而外框高度不变时,视觉上可能无变化。需要整块输入区变高时,请同时(或至少)设置 composerHeight / compactComposerHeight

WASM 与对象存储

| Prop | 类型 | 默认 | 说明 | | ---- | ---- | ---- | ---- | | coreWasmPath | string | /openIM.wasm | 主 WASM 路径(常用 CDN URL) | | sqlWasmPath | string | /sql-wasm.wasm | SQL WASM 路径 | | wasmExecPath | string | /wasm_exec.js | Go WASM runtime 脚本路径 | | workerBasePath | string | /@openim/wasm-client-sdk/lib/ | Worker 目录(可传 /_nuxt/ 等);运行时覆盖打包默认值 | | objectStorageProxy | object | — | 内网 MinIO / OpenIM 对象 URL 改写;需配合 installOpenIMObjectStorageFetchProxy() 做上传代理 |

objectStorageProxy 字段:internalOriginpublicOriginurlRewriteRules(见 OpenIMObjectStorageProxyConfig 类型)。

布局(layout

shellMode='fullpage' 时生效;popup 浮窗始终为桌面双栏,不随容器变窄切换分屏。

| 值 | 行为 | | -- | ---- | | auto推荐) | 根据 组件容器宽度ResizeObserver,非 window.innerWidth)与 compactBreakpoint 自动切换宽屏双栏 / 紧凑「列表 ⇄ 会话」分屏 | | compact | 始终紧凑分屏 | | wide | 始终桌面双栏;窄屏下左右栏会被挤在一起,不适合移动端全屏聊天页 |

全屏聊天页请使用 shell-mode="fullpage"不要layout="wide"(或显式省略 layout,使用默认 auto)。

autoLogin 与加载态

autoLogin 为 true 且当前应同步数据时(fullpagepopup 已打开),在 会话列表首次拉取完成前 会显示全区域加载蒙层(转圈 +「加载中…」),避免 WASM 初始化 / 登录期间闪出「请先登录」「暂无对话」等占位 UI。

蒙层在以下情况隐藏:列表拉取成功(含空列表)、目标会话已打开、同步流程结束,或登录明确失败(connection === 'failed')。

联系卖家若在 autoLogin / WASM 尚未完成时触发,会进入待打开队列并在登录完成后自动重试,期间保持蒙层(单会话模式且尚未选中 peer 时同样显示蒙层)。

Events

| 事件 | 载荷 | 说明 | | ---- | ---- | ---- | | login-start | — | 开始调用 SDK login | | login-success | — | IM 登录成功 | | login-fail | error | IM 登录失败 | | connect-success | — | WebSocket 连接成功 | | connect-fail | error | 连接失败 | | token-expired | — | Token 无效或过期 | | unread-change | { total } | 全部会话未读之和;宿主用于消息入口角标 | | new-message | { peerKey, conversationID?, userID?, groupID?, sendID?, isSelf } | 收到他人消息;宿主自行 toast / 桌面通知 | | visit-seller-profile | sellerID | 弹窗会话栏「店铺」图标点击;由宿主跳转卖家主页 | | close | — | 弹窗壳点击关闭(shellMode='popup') |

高级导出

import {
  OpenIMChat, // 仅内核,自行 provide + 壳
  OpenIMChatHost,
  useOpenIMChatControls,
  openIMChatControlsKey,
  initIMSDK,
  getIMSDK,
  installOpenIMObjectStorageFetchProxy,
  DEFAULT_COMPOSER_HEIGHT,
  DEFAULT_CHAT_WINDOW_WIDTH,
} from "@yeez-tech/chat-tool";

若不用 OpenIMChatApp,需自行:

provide(openIMChatControlsKey, useOpenIMChatControls());

并挂载 OpenIMChatHost

本地 Demo

npm i
npm run dev   # predev 会自动 sync OpenIM 静态资源到 public/

.env 示例:

VITE_API_URL=http://your-openim-host:10002
VITE_WS_URL=ws://your-openim-host:10001
VITE_OPENIM_USER_ID=
VITE_OPENIM_TOKEN=
VITE_DEMO_SELLER_USER_ID=   # 演示「联系卖家」的对方 userID

Demo 页:OpenIMChatApp 全局挂载 + 按钮调用 useOpenIMContact(),与宿主集成方式一致。

npm run build    # 构建 npm 包 → dist/

功能

  • 会话列表、文本/图片/文件/名片/表情、历史消息、单聊已读
  • 容器宽度 ResizeObserver 紧凑布局(非 window.innerWidth
  • autoLogin 下首次进入的加载蒙层,直至会话列表就绪
  • 可配置的弹窗尺寸与输入区高度(props,见上表)
  • Safari < 16.4:构建时 RegExp 探针 sanitize;Webpack 宿主可选 loader 处理 WASM / worker

版本记录

| 版本 | 说明 | | ---- | ---- | | 0.3.3 | 修复固定高度布局下表情面板被 overflow: hidden 裁切导致空白的问题;表情层挂载到 im-composer 顶层 | | 0.3.2 | 修复滚动时输入区底部白条(ChatFooter 背景与 padding-bottom) | | 0.3.1 | 新增 chatWindowMinWidth,弹窗窄屏不再被硬编码 400px min-width 撑破 | | 0.3.0 | 推荐宿主用固定 composer-height + MessageContainer flex:1 控制消息区高度(替代易失效的 ratio 方案) | | 0.2.8+ | 稳定 OpenIMComposer 组件;表情插入输入框;隐藏 ChatUI 重复 .Composer | | 0.2.2 | Webpack worker loader(CJS patch);workerBasePath 可配置 | | 0.2.0 | @openim/wasm-client-sdk 改为 peerDependency;静态资源同步脚本 |

发布

npm run build
npm pack   # 生成 yeez-tech-chat-tool-0.3.3.tgz,可供 file: 或私有源安装
npm publish --access public

License

MIT