# @12-apps/chat 中文文档

规范地址：https://npmcn.com/package/%4012-apps/chat/

版本：1\.3\.0
npm 元数据更新时间：2026-10-10T00:34:13.487Z
中文译文更新时间：2026-11-05T19:57:40.289Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%4012-apps/chat
原文对照：https://npmcn.com/package/%4012-apps/chat/?lang=original
项目主页：https://github.com/12-apps/shared-packages#readme
源码仓库：https://github.com/12-apps/shared-packages

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：171 kB，48 个文件
- 模块格式：ES Module
- TypeScript 类型：未声明
- Node.js 要求：\>=22\.0\.0
- 运行依赖：0 个
- peer 依赖：react ^18\.0\.0 \|\| ^19\.0\.0，@12-apps/ui \>=6\.70\.0，react-native \>=0\.79\.0，@12-apps/wiring \>=1\.17\.0
- 首次发布：2026-10-02T10:12:55.507Z
- 最新版本发布：2026-10-10T00:34:13.162Z
- 近 30 天下载量：32654（2026-10-04 至 2026-11-02，npm 官方下载统计）
- 已发布版本数：6（近 12 个月 6 个）

## 中文简介

双方对一出作品的对话线索,每出作品仅由主机选择的角色标签显示:框架中立的 HTTP 路由,每个角色写出窗口,一个联系-信息过滤器和快速回复,加上由@12-apps/ui p所建的一出线程屏幕

## 安装

```sh
npm install @12-apps/chat
```

也可以使用：`pnpm add @12-apps/chat`、`yarn add @12-apps/chat`、`bun add @12-apps/chat`

## README

# @ 12- apps/ 聊天

双方就一项工作进行对话,其中每一项 派对是别人知道的 **只能通过它的标签**无身份证明,姓名 或联系细节穿过电线。 每个观众登上一次航道 同一线程屏幕在网络 SPA 和 React Industrial app 中制作.

## 盒子里装的是什么

| 子路径 | 什麽 |
| --- | --- |
| `./server` | `createApiChat(config) → { routes }`:框架中立 `GET /`, (中文(简体) ), `POST /messages`, (中文(简体) ), `POST /read`。 。 。 . `createChatReads` 为主机自己的列表提供未读的徽章. |
| `./client` | `createChatClient` 财务报告和审定财务报表 `useChatThread`,这两个平台都是免费的:为主机绘制自己的UI. |
| `./react` | `createWebChat(config) → { ChatThread }`:网络表面. |
| `./native` | `createNativeChat(config) → { ChatThread }`:反应原生地表. |
| `./manifest*` | 用于服务器、网络和本地运行时间的线条清单. |
| `prisma/` | `chat.prisma` (单位:千美元)`ChatThread`, (中文(简体) ), `ChatMessage`, (中文(简体) ), `ChatReadMarker`加上其迁移. `pnpm --filter @12-apps/chat prisma:sync` 将部分复制到主机计划文件夹. |

## 主持人决定谁在线上

```ts
const { routes } = createApiChat({
  db: () => getPrismaClient(),
  authorize: async (request) => {
    // Is this caller a party to the thread the URL names? Under which role?
    // May that role still write? Answer null for "no such thread for you" (404).
    return { tenantId, threadKey: `job:${request.params.id}`, role: "client",
             authorId: user.id, readerId: user.id, canWrite: true };
  },
  roles: {
    client: { label: "Client", shared: false, maxLength: 500, freeText: true,
              blockContact: [], quickReplies: [], rateLimit: { max: 20, windowMs: 60_000 } },
    agent:  { label: "Agent", shared: false, maxLength: 160, freeText: true,
              blockContact: ["phone", "email", "url", "handle"],
              quickReplies: [{ key: "arrived", text: "I have arrived." }],
              rateLimit: { max: 6, windowMs: 60_000 } },
    team:   { label: "Team", shared: true, maxLength: 1000, freeText: true,
              blockContact: [], quickReplies: [], rateLimit: { max: 30, windowMs: 60_000 } },
  },
  copy: EN_US_CHAT_SERVER_COPY,          // or a per-request resolver
  contactVocabulary: { numberWords: { nine: "9" }, atWords: ["at"] },
  onMessage: async (event) => { /* notify, publish a live hint */ },
  onRefused: async (event) => { /* who keeps trying to pass contact info */ },
  onError: (error, { hook }) => logger.error(`chat ${hook} failed`, error),
});
```

`db` 可能归还客户或承诺. `onMessage` 财务报告和审定财务报表 `onRefused` 在背景中运行:不等待推就发送答案 提供商,并报告投出或拒绝的钩子 `onError`永远 给发送者.

所有这一切都属于主机: 写出窗口, 一个呼叫者所扮演的角色, 标签,和快速回复的词。 软件包拥有其余:

-   角色的内容规则;
-   联系人- info过滤器,它也读取主机的躲避字;
-   每作者的费率限额;
-   电线上的信息看起来像什么.

### 电线

每2个回答 `{ data }`数字 : `GET /` 给 `{ thread, messages, unread }`, (中文(简体) ), `POST /messages` 给 `{ message }` (201个)和 `POST /read` 给 `{ unread }`拒绝答复 `{ error, message }`时 `error` 这是 软件包的代码 (`contact_info`, (中文(简体) ), `closed`, (中文(简体) ), `rate_limited`,(......)和 `message` 实值 主持人的句子。 作者只表现为 角色的标签.

`POST /read` 带 `{ upTo }`,则 `createdAt` 最新讯息 读者实际上被显示。 标记从不向后移动,从不过去 所以在屏幕装入后传到的讯息 仍然没有被读取 阅读 永不创建线程行; 第一个消息会.

### 联系人过滤器

它在Unicode 正常化后读取文本:全宽和其他标注 数字,隐形字符,看起来像西里尔字母和希腊字母,a 逆向跑 从主机的拼出数字 `contactVocabulary`电话号码是16个字符中的8位数, 不管他们分开。 时钟时间,货币符号后的金额,以及 匹配主机的横跨 `neutralPatterns` (地址单位如 `apto 1204`,最多每个4位数字)先被预留,因此地址与 公寓号码; 预留跨度仍为至少一个 数字. 只有在作为信使写作的价格时,才留出一笔金额 1,最多4个整数`85,90`, (中文(简体) ), `1.250,00`),这样 `$54321`, (中文(简体) ), `$98765,43` 财务报告和审定财务报表 `$8.765.432` 没有。 CEP,全日期,或 单位拒绝前的5位数街道号码;即有文件证明的街道号码 价钱.

在信件中,它只读取一个相接的链条:同一个作者最近写的 免费文本( e)`contactLookback`,默认在 10 以内的最后 12 个消息 分钟)加入草案,而每条消息结束于下一个开始之处 数字、点或符号。 每个消息中一个数字 一号; `Apto 1204` 接下来是 `Chego 19:30` 是两行。 一个作者 发送到线程在每个过程中一次检查一个, 所以并行 请求不能通过对对方缺席的规则(若干请求) 复制品仍可互出). Bidi 控件在消息发出前被移除 因此屏幕显示过滤器读取的内容.

每一次尝试都花一个限速档, 包括一个被拒绝的,所以测试 过滤器已耗尽。 这是一种威慑,而不是一种保证:a 确定通过数字的人总是可以找到编码,没有过滤器 读取 `onRefused` 东道主如何看待谁一直在尝试。 已知错过, 让一条普通行穿过的代价: 数字的块(`98765 e 4321`),9个或10个位数的平分数 以一个地址单位或一个价格计算(`98 765 ap 4321`, (中文(简体) ), `9876 $5.432` a 包括: 第一组内的空间正是街道号码的写法), 单位或价格后面的两块(`apto 98765 apto 4321`),链断了 写着别的东西和照片 带有区域代码的移动 (偶数) 无法隐藏在一码后。 一个希望 `arroba` 要计数 向社会手柄增加 `extraPatterns`。 。 。 .

### 保留

信件保存 `author_id` 为节制,包船不删除 或匿名API:保留(LGPD,GDPR)是主机. 这个 `chat_messages (tenant_id, created_at)` 索引用于主机的扫描. `created_at` 来源于应用程序的时钟, 和相同的信息 毫秒由id订购.

### 主机自身查询索引

`chat_threads (tenant_id, last_message_at)` 1.1.0,移徙 `20261003120000_add_chat_thread_activity_index`) 服务于主机徽章 将线程数起自某一刻起的活动(`tenant_id = ? AND last_message_at >= ?`) (中文(简体) ). 复制软件包的移取主机 与它的下个同步; 它是添加剂, 所以它不需要释放的舞蹈.

## 屏幕

```tsx
const { ChatThread } = createWebChat({ fetch: credentialedFetch, copy: EN_US_CHAT_UI_COPY, formatTime });
<ChatThread endpoint={`/api/jobs/${id}/chat`} refreshSignal={liveTick} onUnreadChange={setBadge} />
```

该线条本身 读到它显示的最新信息 `onUnreadChange` 然后得到服务器的剩余数(通常为0). 过 `autoMarkRead={false}` 仅用于未读数.

`autoMarkRead="visible"` 是为一线可挂出视线的 抽屉下半部分: 装入未读信件等待信件列表 显示在屏幕上,然后被标记为读取;在屏幕上找到未读的负载 列表已同时在屏幕标记上。 在网络上,名单被监视 a 一个 `IntersectionObserver` (在屏幕上从第一个像素开始显示);一个没有 一分一分,作为 `true` 已经 在本地,一个挂载列表算 屏幕。 jsdom stubs 的主机测试 `IntersectionObserver` 来驾驶它. 一个主机绘制自己的列表 `useChatThread({ autoMarkRead: "visible" })` 通过报告 `controls.onVisibleChange(visible)`。 。 。 .

仅读线程( E)`canWrite: false`没有信件只显示关闭的 通知:没有写出第一个信件的空行邀请,也没有列表.

自己发的短信和别人的留言 都从表面上说:读者的 自己的泡泡是主题的一丁点 `palette.primary.main` 光线为8% 模式,16%在黑暗中),其他人坐在纸张表面. 标签是身体 文字本身的锡和纸上的次级文本; 主要颜色本身 在4.5:1下,它本身的锡,所以它不用于文字。 气泡最多为线宽的80%(60个) 字符,在网络上).

无障碍:清单是一个礼貌的活区(第1段)`aria-live` 在网上, `accessibilityLiveRegion` 因此,一个到达的消息被宣布. 在网络上点击一个发送按钮的手聚焦到球场上 角色的 `maxLength` 坐在这 `<input>` 自己来.

布局 :

-   **网络 :** 留言就坐在这里 `ScrollArea` (最多60%的取景点) 高高的),它保持了最新的视野. `ScrollArea` 需求 `ResizeObserver`, (中文(简体) ), 因此,主机测试 在jsdom 悬浮它.
-   **原生 :** 线条是一条"ui" `Screen` 键盘和它自己的 `ScrollView`。将其挂入一个有界限高度的母体(一个弹性一视图,或 `Screen scroll={false}`永远不要在另一个ScrollView。 过 `keyboardOffset` (dp) 当一头相上而坐.

`createNativeChat` 使用相同的配置。 两个表面都取出线程 `@12-apps/ui` 原始人只使用他们的跨平台道具,所以反应 原生的捆绑会把每个原始人 解决成其原生的双胞胎 `react-native` 出口条件。 每个平台的不同之处是 内部 `ChatPlatform`一个平台的造型

| 插槽 | 接收 | 网络 | 本地语言 |
| --- | --- | --- | --- |
| `Frame` | 列表和写入区域 | (单位:千美元) `Stack` | 键盘避免 `Screen` |
| `MessageList` | 喷出出出水泡, `label`, (中文(简体) ), `scrollKey`, (中文(简体) ), `onVisibleChange?` | `ScrollArea` + 键 `IntersectionObserver` | `ScrollView`,在挂载时可见 |
| `Bubble` | `{ mine, children }` | 欧 `Box` 与 `sx` (图, `min(80%, 60ch)`(中文(简体) ) | 欧 `Box` 与 `style` (图, `80%`(中文(简体) ) |
| `Composer` | 控制字段及其发送动作 | `Input` + 输入 | `Input` + 键盘发送键 |
| `QuickReplies` | 芯片的密钥和标签,标题 | 欧 `Chip`s, 包装 | 欧 `Chip`s或药丸; 包装,或一条侧行 |
| `Notice` | 发送失败的行、 语气和外观 | 欧 `Alert`,或图标 + 标题 | 相同,按 `onNoticeHeight` |

软件包没有打开套接字 。 弹出 `refreshSignal` 当你的直播频道说 线条移动.

### 主机选项

两个表层都把这些放在配置上,都是可选的;一个没有它们的配置 和以前一样画线条 列出值以外的值抛出 `ChatConfigError` 当地表被建造的时候.

| 配置 | 默认 | 变化是什么 |
| --- | --- | --- |
| `quickReplyLabel(key, text)` | 服务器的句子 | 芯片的标签。 对于比较简短的回答名称的芯片:一盘水龙头仍然发送出"关键",因此线程存储了完整的句子. 返回时 `text` 对于一个你不知道的钥匙. |
| `quickRepliesHeading` | `"visible"` | `"label"` 滴数 `copy.quickReplies` 从屏幕上保留它作为行的可访问名称. |
| `emptyDescription` | `"always"` | `"withQuickReplies"` 显示 `copy.emptyDescription` 仅在下面绘制芯片时,用于指向芯片的描述. |
| `sendFailureMessage(failure)` | 服务器句子, 否则 `copy.sendFailed` | 发送失败的行显示; 返回默认无效 . `failure.code` 是软件包的( a)`contact_info`(中文(简体) ). |
| `sendFailureNotice` | `"alert"` | `"compact"`:一个图标,在作曲家上最多两行,一旦编辑草稿就消失;a `contact_info` 拒绝读作警告, 任何其它的都是错误. `"alert"` 留在芯片上直到下一个发送. |
| `sendWhenEmpty` | `"disabled"` | `"inert"` 保留空白字段中所绘制的发送按钮;空白的发送不起作用. |

`createNativeChat` 并且需要两个只画出原生行 :

| 配置 | 默认 | 变化是什么 |
| --- | --- | --- |
| `quickReplyLook` | `"chip"` | `"pill"`: 中性填充药丸并有粗体标签,至少 `NATIVE_CHAT_METRICS.quickReplyMinHeight` (40)dp高,取而代之的是i's 32-tall 概述芯片. |
| `quickReplyScrollBelowWidth` | 无( 总是包) | 在这个窗口宽度下方,在dp中,芯片坐落在一行滚动的侧道上,而不是包接. |

芯片坐着 `NATIVE_CHAT_METRICS.quickReplyGap` (8)dp 分道扬镳.

`ChatThread` 3个道具 用于一个主机 大小的房间周围 线程( 带键盘的小电话) :

-   `foldQuickReplies` 把筹码放出去 草稿和名单都留下.
-   `onNoticeHeight(height)` 发送失败通知的高度为 dp 每个布局,当没有绘制时,则0(除去后,线程转动) 只读,未挂载). 它排除了通知上的空白: `theme.spacing(0.5)` 在作曲家 在紧凑的外表, `theme.spacing(2)` 在警戒状态.
-   `onComposerFocusChange(focused)` 报告田间采取和丢失 重点,以及 `false` 当它消失的时候,它集中, 所以一个宿主隐藏它 铬,而有人 类型永远不会隐藏.

## 复制

每个句子都需要配置,没有默认. `PT_BR_*` 财务报告和审定财务报表 `EN_US_*` 为航道打包船(`CHAT_SERVER_COPY`)和屏幕 (单位:千美元)`CHAT_UI_COPY`;通过一手。 角色标签是角色配置的一部分, 所以每个表面都显示一个角色的同名.

## 常见问题

### @12-apps/chat 是什么？

双方对一出作品的对话线索,每出作品仅由主机选择的角色标签显示:框架中立的 HTTP 路由,每个角色写出窗口,一个联系-信息过滤器和快速回复,加上由@12-apps/ui p所建的一出线程屏幕

### 如何安装 @12-apps/chat？

在项目目录运行 npm install @12-apps/chat，或使用 pnpm add / yarn add / bun add。

### @12-apps/chat 支持 TypeScript 吗？

该包的 package\.json 没有声明类型入口。需要类型提示时，可以在 npm 上查找社区维护的 @types/12-apps\_\_chat 是否存在。

### @12-apps/chat 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @12-apps/chat 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=22\.0\.0。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @12-apps/chat 可以商用吗？

@12-apps/chat 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @12-apps/chat 的下载量有多少？

根据 npm 官方下载统计，@12-apps/chat 在 2026/10/04 – 2026/11/02 这 30 天内被下载 32,654 次，日均约 1,088 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @12-apps/chat 还在维护吗？

最新版本 1\.3\.0 发布于 2026/10/10（28 天前）；至今共发布 6 个版本，其中过去 12 个月发布了 6 个；首次发布于 2026/10/02。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

