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

in-page-playable-page

v0.1.8

Published

仓库 `in-page-playable` 的 **页内单例** 子目录:每 `window` 一个纯 JS 实例,`installPlayable` / `handle`,组合 `in-page-sense` / `in-page-bot` / `in-page-bom`。

Readme

in-page-playable-page

仓库 in-page-playable页内单例 子目录:每 window 一个纯 JS 实例,installPlayable / handle,组合 in-page-sense / in-page-bot / in-page-bom

与 React 无关。信封字段以仓库根协议为准,本目录不另写 cmd 表。

| 文档 | 用途 | |------|------| | 需求拟定 | 本包产品基线 | | 07 与插件交换 | 目标 page 的 postMessage;插件只传话 | | 根协议 | 跨子目录信封真源 | | AGENTS.md | 本包 Agent 入口 |

状态

  • P0 + P1 已实现hello / snapshot / run / escape、单例、门闩、attachWindowBridge(down/up 携 seq echo)
  • 三原子库已接线installPlayable 默认 createSense / createBot / createBomislandSlots / captureScreenshot 转交 sense
  • playground:npm run playground(顶层 + shell.html iframe 夹具;打点符合 sense 合同)
  • 协议类型由本包导出,供同仓 MCP import type

给 MCP 的类型入口

import type {
  IPlayableDownlink,
  IPlayableUplink,
  TPlayableErrorCode,
} from "in-page-playable-page";
// 源码侧:in-page-playable-page/src/index.ts
// 构建后:in-page-playable-page/dist/index.d.ts

字段语义仍以根 docs/AI与人类/协议/ 为准;本包类型是同一份的实现侧导出。

脚本

npm install
npm run typecheck
npm run lint
npm run test
npm run build
npm run playground

Quick Start

本包提供页内单例(每个 window/document 一个实例),用来在已打开的页面上:

  1. 接收信封(hello / snapshot / run / escape
  2. 现读菜单(in-page-sense)与必要时点击(in-page-bot)/刷新(in-page-bom
  3. 按协议返回上行信封(ok:false + error.code 诚实失败)

1) 安装并注入单例

宿主只需在目标页面里调用一次(例如 content script / iframe app 内):

import { installPlayable } from "in-page-playable-page";

const playable = installPlayable({
  root: document, // 或 iframe 的 contentDocument
  window, // 默认是 root 所属 window / globalThis
  allowedOrigins: [window.location.origin], // 如需启用 attachWindowBridge
  // islandSlots: { ... }, // 如有 island 合成 id(可选)
  // captureScreenshot: async (scope) => ({ mime: "image/png", width, height, bytesBase64 }),
});

installPlayable 会把该 window 标记为已安装(window.__IN_PAGE_PLAYABLE_V1__),但它不是 API、也不会提供可直接调用的全局入口。

2) 直接调用 handle(playground/宿主联调常用)

import { installPlayable } from "in-page-playable-page";
import type { IPlayableDownlink } from "in-page-playable-page";

const playable = installPlayable({ root: document, window });

const requestId = "req-1";
const down: IPlayableDownlink = {
  pageId: null,
  requestId,
  cmd: "hello",
  payload: {},
};

const up = await playable.handle(down);
// up.ok === true 时,up.payload.pageId 为新生成的页内 pageId

3) 通过 postMessage 走插件通道(对打插件时常用)

插件脚本无法直接 import 并调用 handle,约定使用目标 page 的 window.postMessage。 在该 page 安装完成后显式开启监听:

playable.attachWindowBridge();

桥会只接受同一 window 自语(event.source === window)且允许的 origin,并在收到有效 downlink 运输封装后回传 uplink 运输封装。

信封字段(cmd/requestId/pageId/payload)不在运输包装里做语义改写。

DOM 标记契约(与 in-page-sense 对齐)

编排层本质上依赖 in-page-sense 的“现读菜单 + resolve 规则”。你需要在页面里按 in-page-sense 标记点位:

  1. 页标题:放一个(或多个,但取第一个非空)data-e2e-pagetitle
  2. 可点击节点(playables):对可操作控件标记
    • data-e2e-kind="playable"
    • data-e2e-id="..."(菜单 id,用于 run)
    • data-e2e-event="click|input|drag|scroll"
    • data-e2e-title="..."(菜单标题)
    • data-e2e-desc="..."(菜单说明)

示例:

<div hidden data-e2e-pagetitle="ready-list"></div>

<button
  data-e2e-kind="playable"
  data-e2e-id="btn-save"
  data-e2e-event="click"
  data-e2e-title="Save"
  data-e2e-desc="Save the current list"
>
  Save
</button>

Island 合成 id(可选)

若页面里存在复合体(data-e2e-kind="island"),可由宿主注入 islandSlots 让 sense 把岛展开成合成 playable id:

合成 id 形状:{islandId}:{slotName}(例如 list-pagination:next

宿主需要提供:

  • 岛:data-e2e-kind="island" data-e2e-id="list-pagination"
  • installPlayable({ islandSlots }):描述通过 aria-labelrole-name 寻址岛内 slot 元素

协议字段语义仍以根协议真源为准;本包只做编排与门闩,不另写 cmd 表。

生命周期与失败语义(协议 00–03 对齐)

每个 window/document 的公开生命周期:

  • installed:已 install,但未 hello 成功(pageId === null
  • live:hello 成功后写入 pageId(允许 snapshot/run/escape
  • retiredescape.reload 触发后退役(同一 pageId 的旧请求拒绝)

hello

  • 成功:mint 新 pageId,进入 live
  • 重复 hello:复用旧 pageId(不换代)

snapshot

  • 只读:现读 sense snapshot
  • 输出 payload.snapshot(sense 回包)与 payload.escapes(由本页 snapshot 派生)

run

  • 门闩:必须 live 且 envelope 的 pageId 等于当前 pageId
  • id 校验:
    • "" 或 generic ref(如 g0)→ NOT_IN_MENU,零副作用
    • 现读菜单中不存在该 id → NOT_IN_MENU
  • 命中后:交给 in-page-bot 只进行 click(targetElement)

escape(V1 仅支持 reload)

  • 现读重校:必须由本页 sense snapshot 判定 reload 仍允许 → 否则 ESCAPE_NOT_ALLOWED
  • 调用 in-page-bom.run({ type: "reload" })
    • bom 返回 retired:true 才真正把本 pageId 标成 retired
    • bom 返回 ok:falseretired:false 时:本包不假退役,并回 DISPATCH_FAILED

因此该组件的失败路径在 state machine 上是“可解释、可复验”的(不依赖 Agent 记忆)。

API 说明

installPlayable(options?)

每个 window 一个实例;二次 install 不会换 pageId

function installPlayable(options?: IInstallPlayableOptions): IPlayable;

IInstallPlayableOptions

  • root?: Document | ShadowRoot | Element:sense 的扫描根(默认 document
  • window?: Window:绑定浏览上下文(默认 root 所属 window / globalThis
  • islandSlots?: IIslandSlotTable:岛展开槽位表(来自 in-page-sense
  • captureScreenshot?: (scope: Element) => Promise<unknown>:可选截图注入(对齐 sense)
  • allowedOrigins?: string[]:仅用于桥监听 origin 白名单
  • ports?: IPlayablePorts仅测试/联调注入,生产默认使用真三库

IPlayable

  • pageId: string | null
  • phase: "installed" | "live" | "retired"
  • handle(request: IPlayableDownlink): Promise<IPlayableUplink>
  • attachWindowBridge(): void

类型与协议真源

信封字段与失败码的真源在仓库根:

  • docs/AI与人类/协议/(见本目录 README 顶部链接)

本包导出与协议对齐的 TypeScript 类型,供同仓 MCP/插件 import type 使用:

import type { IPlayableDownlink, IPlayableUplink, TPlayableErrorCode } from "in-page-playable-page";

导出项(概览)

  • installPlayable / resetPlayableForTests
  • deriveEscapes / isEscapeAllowed
  • attachWindowBridge:通过 playable.attachWindowBridge() 启用

测试辅助(resetPlayableForTests / detachWindowBridgeForTests)不建议在生产逻辑里使用。