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

@fickit/transportpaint

v1.1.0

Published

TransportPaint 涂装设计器静态页面(自 buspaint 移植)

Readme

@fickit/transportpaint

TransportPaint 涂装设计器的前端包:5 个自包含单文件 HTML 页面(无构建步骤、无外部依赖),通过包内 bin 脚本复制到宿主应用的静态目录后,以 iframe 嵌入使用。

页面清单

| 文件 | 页面 | |---|---| | designer.html | 涂装设计器 | | my-designs.html | 我的设计 | | admin.html | 管理后台 | | roof-calibrator.html | 车顶校准器 | | template-generator.html | 模板生成器 |

另有共享引导脚本 tp-boot.js(各页面在 head 中以 <script src="tp-boot.js"></script> 加载),负责身份识别、API 请求头注入、全局 401 处理和页面间跳转。

基本集成

  1. 安装依赖:pnpm add @fickit/transportpaint(workspace 内为 workspace:*
  2. 在宿主 package.json 中添加复制脚本,并在 dev / build 前执行:
{
  "scripts": {
    "copy:transportpaint": "transportpaint-copy-pages public/transportpaint",
    "dev": "pnpm run copy:transportpaint && vite",
    "build": "pnpm run copy:transportpaint && vite build"
  }
}
  1. 复制后页面作为 public/ 静态资源随宿主分发,用 iframe 嵌入 {BASE_URL}transportpaint/{页面名}.html

页面内 API 基地址固定为绝对路径 /fickit/transportpaint/api,因此页面必须与后端同源(或由宿主反向代理该路径)。

完整的宿主包装组件示例见 example/TransportPaint.vue(取自生产宿主,覆盖了本文档全部通信机制;其中 @/providesinjectHttp() 是该宿主的 JWT 获取方式,集成时替换为自己的身份来源即可)。

宿主通信机制

页面以 iframe 嵌入时,tp-boot.js 通过 postMessage 与宿主窗口通信。所有消息的 targetOrigin / 校验 origin 均为 location.origin(同源部署前提下不会泄露给第三方);宿主侧也应校验 event.originevent.source 确为本页面 iframe。

身份下发(宿主 → 页面)

iframe 内的 fetch 默认不携带宿主的身份信息(如 JWT),需要宿主显式下发:

  1. 页面加载时,tp-boot.js 立即向宿主发送 fickit:auth-request 索要身份请求头;
  2. 宿主回复 fickit:auth
// 宿主侧
window.addEventListener('message', (event) => {
  if (event.origin !== location.origin) return
  if (event.source !== iframeEl.contentWindow) return
  if (event.data?.type === 'fickit:auth-request') {
    const headers = token ? { Authorization: `Bearer ${token}` } : {} // 未登录回复空对象
    ;(event.source as Window).postMessage({ type: 'fickit:auth', headers }, event.origin)
  }
})
// 建议在 iframe load 时也主动推一次同样的 fickit:auth,作为保险
  • 收到回复之前页面发出的 API 请求会被 tp-boot.js 挂起排队,收到后携带下发头放行;1.5 秒未收到回复则按未登录放行(兜底,避免宿主异常时页面卡死)。
  • 下发的请求头只保存在页面内存中,不写入任何存储。
  • 若页面收到的是空头对象,或未在 iframe 中运行,则回退到独立 demo 机制:读取 localStorage['fickit-demo-user'] 并注入 X-User-* 请求头(配合 FicKit demo 宿主使用)。

请求登录(页面 → 宿主)

API 请求返回 401,或页面主动调用 tpGoLogin() 时,页面向宿主发送 fickit:login。宿主应导航到自己的登录页/弹出登录框:

if (event.data?.type === 'fickit:login') {
  router.push({ name: 'login' })
}

页面间跳转(页面 → 宿主)

页面间的「新开标签页」跳转(如 my-designs 打开 designer 并载入指定设计)统一走 tpNav(page, query)

  • iframe 内:向宿主发送 fickit:nav{type:'fickit:nav', page:'designer', query:'local=3'})。宿主应把它映射到自己的路由并新开标签页,使新页面仍然是「宿主路由 + iframe」的形态,鉴权链路才得以延续;query 需透传到 iframe 地址。
  • 独立打开时(非 iframe):退化为直接 window.open('designer.html?local=3')
if (event.data?.type === 'fickit:nav') {
  const { page, query } = event.data
  const url = router.resolve({ name: tpRouteNames[page], query: Object.fromEntries(new URLSearchParams(query)) }).href
  window.open(url, '_blank')
}

注意:宿主的 iframe src 应把自身路由的 query 原样拼接到 html 地址上(如 designer.html?local=3),页面内部依赖该 query 载入对应设计。

消息类型汇总

| type | 方向 | 载荷 | 含义 | |---|---|---|---| | fickit:auth-request | 页面 → 宿主 | 无 | 页面启动,索要身份请求头 | | fickit:auth | 宿主 → 页面 | {headers: Record<string,string>} | 下发请求头(如 Authorization);空对象表示未登录 | | fickit:login | 页面 → 宿主 | 无 | 请求宿主弹出/跳转登录 | | fickit:nav | 页面 → 宿主 | {page: string, query: string} | 请求跳转到其他 tp 页面(新开标签页) |

目录结构

packages/@fickit/transportpaint/
├── pages/                  # 5 个页面 + tp-boot.js(被复制到宿主静态目录)
├── scripts/copy-pages.js   # bin: transportpaint-copy-pages <目标目录>
└── example/
    └── TransportPaint.vue  # 宿主包装组件参考实现(含全部通信机制)