# @feedlog/widget 中文文档

规范地址：https://npmcn.com/package/%40feedlog/widget/

版本：0\.2\.0
npm 元数据更新时间：2026-10-16T07:19:38.283Z
中文译文更新时间：2026-11-14T13:17:50.028Z
软件包许可：MIT

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

原始 npm 页面：https://www.npmjs.com/package/%40feedlog/widget
原文对照：https://npmcn.com/package/%40feedlog/widget/?lang=original
项目主页：https://github.com/linkcraftstudio/feedlog-widget#readme
源码仓库：https://github.com/linkcraftstudio/feedlog-widget

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

- 包体积（解压后）：65 kB，5 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=18
- 运行依赖：0 个
- 首次发布：2026-07-28T01:41:24.972Z
- 最新版本发布：2026-10-16T07:19:38.021Z
- 近 30 天下载量：2489（2026-10-15 至 2026-11-13，npm 官方下载统计）
- 已发布版本数：9（近 12 个月 9 个）

## 中文简介

零运行时间依赖的 FeedLog 可嵌入反馈部件 .

## 安装

```sh
npm install @feedlog/widget
```

也可以使用：`pnpm add @feedlog/widget`、`yarn add @feedlog/widget`、`bun add @feedlog/widget`

## README

# @ feedlog/ 部件

主机侧 SDK 用于 [种子日志](https://github.com/linkcraftstudio/feedlog) 反馈部件。 它使一个启动器和一个iframe中托管反馈接口,零运行时依赖性以及约11个KB gziped的无害环境管理捆绑.

## 安装

```bash
pnpm add @feedlog/widget
```

## 使用量

```ts
import { createWidget } from '@feedlog/widget'

createWidget({
  baseUrl: 'https://acme.feedlog.ai',
  auth: {
    getToken: async () => {
      const res = await fetch('/api/feedlog-token')
      if (!res.ok) throw new Error('Could not fetch the feedback token')
      return (await res.json()).token ?? null
    },
    login: () => new Promise<void>((resolve) => {
      openYourLoginModal({ onClose: resolve })
    }),
  },
  theme: 'auto',
})
```

`openYourLoginModal` 是您的应用程序的功能。 内容 `onClose` 操作员必须在每个出口运行,包括取消。 返回一个承诺,只有当它代表整个互动,而不仅仅是打开模式.

没有用户系统的产品可以省略 `auth`数字 :

```ts
createWidget({ baseUrl: 'https://acme.feedlog.ai' })
```

启用 **来宾职位** 在 FeedLog 中允许这些访客不签名就提交. 由于访客发送已禁用且没有主机认证, 部件无法满足登录请求 .

## 公共 API

套件输出了四个函数 。 全部返回 `void`; 没有实例或 `destroy` 方法.

| 函数 | 效果 |
| --- | --- |
| `createWidget(options: WidgetOptions)` | 每页初始化一次 。 重复的电话发出警告并被忽略. |
| `openWidget()` | 打开反馈面板而不改变发射装置状态或提示登录. |
| `closeWidget()` | 关闭面板而不破坏面板、清理其草稿或将焦点移出主机对话框. |
| `updateWidget(options: WidgetDisplayOptions)` | 更新主题, 启动器位置/ 状态或堆放顺序 。 它不打开或关闭面板. |

打电话 `createWidget` 首先在正常的整合。 控制不需要等待初始化或iframe准备:最后的开放/关闭指令被保留并按字段显示更新合并. 初始化前的呼叫也被保留,但从未初始化SDK本身. 服务器端的电话什么都不做。 被禁用的工作空间不会产生任何效果,即使之后 `openWidget()`。 。 。 .

### 初始化选项

| 选项 | 说明 |
| --- | --- |
| `baseUrl` | 需要 FeedLog 工作空间 URL . |
| `auth` | 可选主机身份整合. |
| `auth.getToken` | 要求为 `auth`数字 : `() => Promise<string | null>`返回已签名的 JWT 或 `null` 签字后 暂时失败,不会让访客退出. |
| `auth.login` | 可选 `() => void | Promise<void>`打开你的签名UI。 身份在返回或定居后再次检查. |
| `theme` | `light`, (中文(简体) ), `dark` 或者说 `auto` (默认). `auto` 操作系统. |
| `launcher` | 以下列举的可选领域. 丢失的放置场使用仪表板默认. |
| `zIndex` | 0至2147483647的整数;默认值 **第40条**适用于发射装置和相板. |
| `embedPath` | 默认为 `/widget/embed`。一个自定义建筑的同源绝对路径嵌入;它不能指另一个来源. |
| `onUnreadChange` | 可选 `(count: number) => void` 对于自定义条目的未读指标. |

`baseUrl`, (中文(简体) ), `auth`, (中文(简体) ), `embedPath` 财务报告和审定财务报表 `onUnreadChange` 选项。 通话 `createWidget` 也不再更新它们.

### 运行时间更新

```ts
import { createWidget, openWidget, closeWidget, updateWidget } from '@feedlog/widget'

createWidget({
  baseUrl: 'https://acme.feedlog.ai',
  launcher: { state: 'hidden' },
})

document.querySelector('#help')?.addEventListener('click', openWidget)

updateWidget({ theme: 'dark' })
updateWidget({ zIndex: 40 })
updateWidget({ launcher: { alignment: 'left', bottomOffset: 80 } })
updateWidget({ launcher: { state: 'tab' } })
updateWidget({ launcher: { state: 'button' } })

// A host modal needs the panel closed; the launcher is controlled separately.
closeWidget()
updateWidget({ launcher: { state: 'hidden' } })
```

`WidgetDisplayOptions` 仅接受 `theme`, (中文(简体) ), `zIndex` 财务报告和审定财务报表 `launcher`。更新合并各个字段,包括其中的字段 `launcher`一、导 言 `undefined` 字段保留其现有价值。 无效提供的显示值 `TypeError`。更新不会改变仪表板设置.

启动器选项 :

| 外地 | 数值 |
| --- | --- |
| `alignment` | `left` 或者说 `right`默认为 `right`。 。 。 . |
| `bottomOffset` | 非负式安全整数以像素表示. 默认为 `20`。 。 。 . |
| `sideOffset` | 非负式安全整数以像素表示. 默认为 `20`。 。 。 . |
| `state` | `button`, (中文(简体) ), `tab` 或者说 `hidden`。初始化时省略,恢复访客保存的状态,否则显示按钮. |

没有固定的冲抵限额。 在不改变配置值的情况下被限制在视图端口,并自动添加设备安全区设置. 桌面和移动共享相同的配置.

在初始化时,被保存的访客安置比SDK和仪表板默认优先. 明确的运行时位置更新可以替换保存拖放并保存其他当前放置字段. 状态,主题和z-index更新使位置保持完整. 明确状态更新超越了访客的偏好,独立于仪表板的访客关闭政策.

### 主题和堆叠顺序

`updateWidget({ theme })` 更改发射装置、面板外壳和iframe内容而无需重新装入帧。 它保留了对话,起草,滚动位置和开放/封闭状态. 带有自己的主题切换的应用程序应该通过 `light` 或者说 `dark`; SDK不检查主机的CSS课.

Iframe发布现场主题支持广告. 随着一个更古老或自定义的嵌入到缺少它的地方,当前 shell 和 iframe 主题被保留,而 SDK 记录一个警告. 所请求的主题将适用于随后创建的框架;SDK从不重装活动的框架,只是为了改变其主题.

默认 Z 索引是 **第40条**,在诸如 `50`。主机堆放上下文仍然决定最后顺序。 覆盖 `zIndex` 初始化时或通过初始化 `updateWidget` 当应用程序的分层尺度不同时.

## 启动器交互

访客可以用鼠标,笔或触摸来拖动按钮或帮助标签. 移动超过8px开始拖动;释放到最近的左/右边缘. 箭头键还会移动焦点发射器;出逃或被中断的手势会取消拖放. 拖曳不会打开面板或更改发射器形式 .

按钮保留了20px边距并加上拖放后的安全区域. 概述的“帮助”标签在取景口边缘上被冲出,而不论侧面是否抵消。 它的可见大小为22×56px,至少有一个44px命中目标. 形状过渡超过220ms,尊重减动偏好. 单击标签可恢复按钮并打开反馈.

拖曳后,发射器使用与更接近的上下边缘的固定距离. 折叠并恢复 维护垂直锚。 移动浏览器工具栏和键盘高度变化不重新计算所保存的拖放比例;旋转设备或调整桌面窗口的大小可以恢复其相对位置. 反馈面板仍然分别适应可见的取景口.

板子关闭行为 `none`, (中文(简体) ), `collapse` (违约)或 `hide`。按钮的相近控制在桌面悬浮/焦点上可见,在触摸设备上总是可见。 当关闭被关闭,面板被打开,或者发射装置被倒塌或被拖走时,它就不存在了. 反馈面板内部没有发射装置控制.

按钮/ tab 状态并拖曳位置使用 `sessionStorage`,范围为主机和 FeedLog URL。 他们活下来了 重新装入同一个标签。 隐藏状态只持续当前页面并清除所保存的标签状态 。 初始化为 `state: 'hidden'` 明确将发射器再次隐藏在每一个负载上。 存储受限制的浏览器只保留内存中的偏好. 盘子 `none` 在下一个负载上重置旧访问者崩溃偏好,但明确提供的SDK状态仍然适用.

在桌面上,在至少480px适合时,面板更倾向于在发射器上方打开,再放在下方,再放在旁边。 它更喜欢内向的一面,在需要的时候尝试另一面,否则就适合在取景口内,而不会将对话挤入狭长的一带. 它保持了空间允许的16px发射机间隙和12px取景外接安全区域,最高可达680px高. 小屏幕使用全屏面板. 隐藏发射器为自定义主机条目打开的面板保留了锚.

## 主机登录生命周期

SDK电话 `auth.login()` 只有在明确签名和沉默之后 `getToken()` 检查。 返回的"承诺"隐藏了面板和发射器,直到互动结束,身份再次被检查. 拒绝也结束了等待,不自动重试登录. 没有强制暂停.

不保证的返回使部件保持可见,并立即再次检查身份。 不需要填写回调或单独的填写方法。 使用承诺自动避免模式, 或协调主机的z索引本身。 静态认证不隐藏部件 .

使用相容的iframe,身份更改保留了同一访客的视图,对话,草稿,上传的附件引用和滚动位置. 国家不能由不同的注册账户继承。 全页重定向使用5分钟回程标记和1个使用会话存储快照;存储限制会阻碍草稿在整个导航中恢复. 会话符号只在 Iframe URL 碎片中行走 .

## 认证

FeedLog 重新使用您产品的现有身份 **产品SSO**。您的后端已经知道是谁签名的; 部件只需要一个短暂的、签名的特征断言 .

`auth.getToken` 必须返回 **JWT 由您的后端签名** 带着你的一个半兽人的秘密(HS256),或者 `null` 当没有人签字。 在 FeedLog 下生成 SSO 机密 **开发者 — SSO**同样的秘密已经赋予了产品 SSO的交接权, 所以大多数集成器重用它们已经拥有的代码.

法定和任择索赔:

| 索赔 | 需求 | 页:1 |
| --- | --- | --- |
| `email` | 对 | 身份钥匙. |
| `exp` | 对 | 过期. 出站时间不得超过24小时;一个小时是好的默认时间. |
| `name` | 没有 | 显示名称 . |
| `picture` | 没有 | 阿凡达URL. |

SDK 将此 JWT 交换为 FeedLog 会话符, 每封电子邮件缓存它 `localStorage`,然后通过 URL 片段将其交给 Iframe。 签名必须在您的服务器上进行—— 永远不要将 SSO 秘密传送给浏览器 .

## 浏览器支持

在所有当前常绿浏览器(Chrome,Firefox,Safari,Edge)中工作. 建筑目标为ES2020 并依赖于Shadow DOM, `fetch`,和 Web 存储——支持的浏览器不需要多填充.

## 类型

货船 `.d.ts` 声明. `WidgetOptions`, (中文(简体) ), `WidgetDisplayOptions`, (中文(简体) ), `LauncherOptions`, (中文(简体) ), `LauncherPlacement`, (中文(简体) ), `LauncherState`, (中文(简体) ), `WidgetAuth`,以及 `WidgetTheme` 从软件包根导出,并附带四个函数.

## 如何运作

SDK只做无法从跨源iframe内部完成的事情:使发射器和未读徽章,拥有面板和iframe寿命周期,呼叫您 `getToken` 编号 : `login`,并接收iframe的信息。 其他一切——反馈UI和所有商业API的调用——发生在FeedLog托管的iframe内部.

几个值得了解的变量,如果您正在读取源或调试:

-   **两端都验证出源和源.** 版本一信息带有准备状态,主题更新,页面上下文和临时恢复状态. 发射装置状态仍在SDK中. 未知类型会被忽略用于转发相容性;一个旧的iframe不会收到不支持的恢复数据.
-   **会话符只在 URL 片段中行走** (单位:千美元)`#token=…`),从未在查询字符串中和从未结束 `postMessage`。碎片不发送到服务器上,所以该符号不包含访问日志和 `Referer` 头; Iframe 把它从装入的地址栏上擦去. 新会话到达iframe的唯一方式是用新的片段重建.
-   **信使缓存通过电子邮件键入 .** A 类 `null` 从 `getToken()` 立即清除缓存,一个不同的电子邮件丢弃它——因此共享的计算机不会将一个人的会话泄露给下一个人. A 类 `401` 在放弃之前 触发一次无声的再交换.
-   **`auth-requested` 总是重复 `getToken()` 安静的先** (用户可能在另一个标签上签名). 仅 `reason: 'user'` 可能会升级为 `auth.login()`· ; `reason: 'expired'` 从来没有打开一个弹出自己.
-   **嵌入的 URL 携带 `?origin=<your page's origin>`。 。 。 .** Iframe把它当作 `postMessage` 目标来源,它无法可靠地从中获取(Firefox没有 `location.ancestorOrigins`,而裁判员政策可以剥去裁判员. 伪造它无益:如果真正的母源不匹配,浏览器拒绝发送消息.

## 捐款

```bash
pnpm install
pnpm build        # tsup -> dist/index.js + dist/index.d.ts
pnpm typecheck
pnpm test
pnpm size         # report the bundle size
```

来源布局 :

```
src/
  index.ts          public controls, single-call initialization and pending instructions
  display.ts        display defaults, partial updates and option validation
  position.ts       visitor docking and viewport-constrained panel placement
  widget.ts         orchestration: boot, open/close, iframe lifecycle, postMessage dispatch
  auth.ts           the auth flow, with single-flight de-duplication
  session-cache.ts  session-token cache in localStorage, keyed by email
  unread.ts         badge count (unread endpoint + 60s sessionStorage cache)
  api.ts            fetch wrappers for the three widget endpoints
  jwt.ts            reads the email claim from a JWT (base64 decode only, no verification)
  login-marker.ts   sessionStorage marker for redirect-style login (~5 min expiry)
  storage.ts        localStorage / sessionStorage with an in-memory fallback
  ui.ts             launcher / badge / panel / loading, all inside a shadow root
  types.ts          public types + the postMessage protocol types
```

## 测试环境

`playground/demo-host.html` 是测试环境:一个单一的自成一体的文件——一个将部件嵌入的实事求是的(小说)SaaS登陆页,其中有一个模拟的登录和可拖动的控制面板,用于 `baseUrl`,嵌入路径,Org SSO秘密,和主题. SDK作为全球建筑的内入 `build:demo`。 。 。 .

**Demo控制 → 发射器转换** 提供 **显示已崩溃的标签**, (中文(简体) ), **显示启动器**,以及 **左停靠/ 右停靠** 尝试过渡。 将标签从边上拖出以查看其浮动形状,然后放出以停靠. 概述的"帮助"分页为22×56px,不包括安全区垫地,其内侧有至少44px宽的命中目标.

```bash
pnpm demo          # build the inline SDK once, then serve at http://localhost:5173/demo-host.html
pnpm dev:demo      # same server, plus re-inline the SDK on every src/ change (refresh the browser)
```

二者在开始时打印 URL; `PORT=xxxx` 超过端口。 使用 `demo` 只要点击它, `dev:demo` 一边在SDK本身上行走.

指着面板 `baseUrl` 运行中的 FeedLog 实例( 默认) `http://localhost:3000`并粘贴Org的SSO秘密。 页面在每页的 JWT 客户端上签名 `getToken()` ——所以它代表着没有自己一个客户后端的客户,一个坚持不变的秘密永远不会消失. 面板配置( baseUrl, 嵌入路径, 秘密, 主题, 登录状态) 坚持跨重载, 由页面来源键入 .

永久航行 **帮助** 菜单调用 `openWidget()` 打开反馈或 `updateWidget({ launcher: { state: 'button' } })` 以恢复浮动按钮。 发射机的关闭按钮遵循了仪表板的关闭行为.

> **客户端签名仅仅是一个演示快捷键 .** 在制作中,SSO的秘密生活于 **** 服务器和标志 JWTs 在那里——它绝不能到达浏览器. 面板的字段模型是开发者配置的值;它模拟的签名是您后端的工作.

交出来 **网页**没有 `file://`(单位:千美元) `file://` 页面报告a `null` 源,它打破了部件的跨源后Message + CORS(登陆页、登录和面板仍在工作,但使用通知跳过活的部件).

活的部件需要一个可达到的 FeedLog 后端 —— 没有捆绑的模拟器, 所以在 at `baseUrl`。 。 。 .

## 许可证

[麻省理工学院](https://npmcn.com/package/%40feedlog/widget/LICENSE)

## 代理页面上下文

对于每个消息,信任的iframe通过请求ID请求页面上下文. SDK以当前路径名,文档响应. 标题和元描述(截至2000/500/2000字符)。 查询参数、碎片和页面正文不包括在内。 双方检查消息的来源和来源. 这在没有额外选择的情况下可以跨越SPA导航. 更古老的 SDK 发行版仍然可以无页面上下文地聊天.

SDK对iframe的响应 `ready` 一个版本的事件 -- 1个 `init` 包含信件 `payload.capabilities.pageContext: true`。iframe仅在此声明之后请求页面上下文。 更古老的 SDKs 省略了它,所以 Iframe 跳过收藏而不等待;一个声明的能力仍然有500ms的响应超时.

## 移动面板行为

在宽至520px的屏幕上,打开全屏面板会隐藏发射装置. 关闭面板恢复其先前的按钮/tab/隐藏状态. 通过面板关闭 UI 返回焦点到可见的启动器或主机条目; 程序 `closeWidget()` 离开主机焦点。 装入和出错屏幕包括一个独立的关闭按钮. 更宽的屏幕保留着一个浮出水面的面板,其高度符合可用的取景口;一个短的取景口单独不会触发全屏模式.

## 常见问题

### @feedlog/widget 是什么？

零运行时间依赖的 FeedLog 可嵌入反馈部件 \.

### 如何安装 @feedlog/widget？

在项目目录运行 npm install @feedlog/widget，或使用 pnpm add / yarn add / bun add。

### @feedlog/widget 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @feedlog/widget 支持 ESM 还是 CommonJS？

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

### @feedlog/widget 需要什么版本的 Node\.js？

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

### @feedlog/widget 可以商用吗？

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

### @feedlog/widget 的下载量有多少？

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

### @feedlog/widget 还在维护吗？

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

