@pinwren/widget

中文文档
0.4.0发布于 2026/11/02MITTypeScriptES Module零依赖

Pinwren feedback widget: Figma-style pinned comments and annotated bug reports for any website

原始使用文档
文档目录9 节
  1. Install
  2. Script tag (any site)
  3. npm
  4. API
  5. Verified reporters
  6. Visibility
  7. Session replay
  8. Hosting the files
  9. Licence

@pinwren/widget

The Pinwren feedback widget: Figma-style comments pinned to any element of your site, and annotated bug reports with console, network and (optionally) session replay — sent to your self-hosted or cloud Pinwren server. MIT licensed.

It runs in a closed Shadow DOM, never throws into your page, and loads its heavy parts (screenshot engine, annotation editor, side panel, replay recorder, translations) only when a visitor needs them.

Install

Script tag (any site)

<script src="https://feedback.example.com/widget.js" data-project="pk_live_xxx" async></script>

feedback.example.com is your Pinwren server; the project key comes from Project › Install in the dashboard. The page's origin must be in the project's allowed origins.

AttributeMeaning
data-projectPublic project key (required)
data-endpointAPI origin, when it differs from where widget.js is served (e.g. a CDN)
data-localeUI language (en, es, fr, de, pt-BR, it, ja, hi, nl, zh, ru)
data-accentLauncher colour, hex
data-themelight or dark; otherwise the visitor's system setting
data-url-patternPage scope, e.g. /product/:id — pins are shared across every page matching it
data-force-showShow the widget even where the project's visibility rules would hide it
data-nonceCSP nonce for the scripts the widget adds
data-allow-replay-in-privacy-modeKeep session replay on in privacy mode (you are responsible for consent)

npm

npm install @pinwren/widget
import { identify, init, on, open } from "@pinwren/widget";

init({ projectKey: "pk_live_xxx", endpoint: "https://feedback.example.com" });
identify({ id: "user_42", name: "Maria Lopez", email: "[email protected]" }, { signature });
on("submit", ({ number }) => console.log(`Feedback #${number} sent`));
open("bug");

The npm entry is a ~1 KB ESM loader with TypeScript types. init() adds <endpoint>/widget.js — the build your server ships, so the widget always matches your server's version — and queues every call made before it has loaded. It is safe to import during server-side rendering: nothing touches window until you call a function in the browser. For React and Next.js use @pinwren/react.

API

CallWhat it does
init(options)Start the widget (once per page). Options: projectKey, endpoint, accent, locale, urlPattern, forceShow, allowReplayInPrivacyMode, nonce
open(mode?)Open the menu, or "comment", "bug", "panel" or "page" (a comment on the whole page) directly
close()Close whatever is open
identify(user, { signature? })Pre-fill the reporter from your app; null forgets them
setPageScope({ urlPattern })Change the page scope (null clears it); the pins on the page are re-listed
setMetadata(object)Context (plain JSON object, ≤ 14 KB) sent with every report and comment; null clears it
show() / hide()Show or hide the widget, overriding the project's visibility mode
on(event, handler)open, close, submit ({ id, number, kind }), error ({ message }), ready. Returns unsubscribe
destroy()Remove everything: UI, listeners, console/network wrappers, session replay

With the script tag, the same functions are on window.Pinwren; calls made before the script has loaded can be queued as window.Pinwren = { q: [["identify", user], ["open", "bug"]] }.

Verified reporters

Compute signature on your server — hex HMAC-SHA256 of the user's id with the project's identity secret (dashboard: project settings › identity) — and pass it to identify(). The dashboard then marks that reporter as verified. Never ship the secret to the browser.

// Node, on your server
import { createHmac } from "node:crypto";
const signature = createHmac("sha256", process.env.PINWREN_IDENTITY_SECRET!).update(user.id).digest("hex");

Visibility

Set in the dashboard (Widget › Visibility); forceShow overrides both:

  • Environments → non-production only hides the widget on production hosts. Not production: localhost, *.localhost, loopback and private-network IPs (127.x, 10.x, 192.168.x, 172.16–31.x, ::1), the suffixes .local, .test, .internal, and staging, stage, preview or dev subdomains (dev.example.com, preview-42.example.com — but example.dev is production). Everything else is production.
  • Minimum viewport width hides it on narrower windows (it comes back when the window grows).

Session replay

When the project turns it on (Capture › Session replay), the widget records the last 30–120 s of the page with rrweb and attaches it to bug reports. The recording stays in the page's memory and is only sent with a report; the reporter sees it in "What we'll send" and can leave it out. Every input is masked; elements with the class pinwren-block or data-pinwren-ignore are not recorded; pinwren-mask / data-pinwren-mask (and the project's mask selectors) have their text replaced. Privacy mode turns replay off unless the page sets allowReplayInPrivacyMode.

Hosting the files

dist/ holds widget.js plus content-hashed chunks (widget-panel.<hash>.js, widget-capture.<hash>.js, …) listed in dist/manifest.json. Serve widget.js with a short cache (Cache-Control: public, max-age=300) and the hashed files with Cache-Control: public, max-age=31536000, immutable. Chunks load from the directory widget.js was served from, so a CDN copy of dist/ works with data-endpoint pointing at your server.

Content Security Policy: allow your Pinwren origin in script-src, connect-src and img-src (plus data: and blob: in img-src for screenshots).

Licence

MIT. Bundled third-party code (SnapDOM, finder, rrweb) is listed in dist/THIRD_PARTY_NOTICES.md.

版本与发布记录

@pinwren/widget 共发布 6 个版本,首次发布于 2026/10/24;过去 12 个月发布了 6 个版本。

版本发布日期距今
0.4.0latest2026/11/021 天前
0.3.02026/10/268 天前
0.2.02026/10/268 天前
0.1.12026/10/2410 天前
0.1.02026/10/2410 天前
0.0.0-stage2026/10/2410 天前

关于 @pinwren/widget 的常见问题

@pinwren/widget 是什么?

Pinwren反馈小部件:figma风格的固定评论和任何网站的注释错误报告

如何安装 @pinwren/widget?

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

@pinwren/widget 支持 TypeScript 吗?

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

@pinwren/widget 支持 ESM 还是 CommonJS?

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

@pinwren/widget 可以商用吗?

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

@pinwren/widget 还在维护吗?

最新版本 0.4.0 发布于 2026/11/02(1 天前);至今共发布 6 个版本,其中过去 12 个月发布了 6 个;首次发布于 2026/10/24。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。