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

oh-my-pet

v0.1.2

Published

Low-intrusion, extensible pixel pets for any web page

Readme

oh-my-pet

English | 简体中文

一个低侵入、可扩展的像素网页宠物库。宠物生活在网页元素的边缘上:在卡片顶上走、跳、爬墙、飞,挖个洞钻进去再从别处冒出来;可以戳、拖起来扔,也会搬东西、说话、彼此串门。

  • 只读取几何位置和你主动声明的 data-pet-* 属性,不读页面文字,不修改页面元素
  • Shadow DOM 隔离样式,兼容严格 CSP;零网络请求
  • 命中区之外,页面原有的滚动、点击、键盘行为完全不受影响;手指在宠物身上滑动照常滚动页面,长按才拎起来
  • 一切能力都是插件:移动方式、离散移动的变体、动作、场景、插件都能由第三方扩展
  • 体积小、开销低:核心压缩并 gzip 后约 26KB,加上全部内置插件约 31KB;<script> 直接引入的整包(含 Mochi 和全部插件)gzip 后约 34KB。空闲时主线程每秒不到 1ms

安装

npm i oh-my-pet oh-my-pet-pack-mochi

oh-my-pet-pack-mochi 是默认角色 Mochi;自己做角色包用命令行工具 oh-my-pet-cli。

最快的用法

<script src="https://unpkg.com/oh-my-pet/dist/oh-my-pet.iife.js" async></script>
<oh-my-pet characters="mochi" activity="calm"></oh-my-pet>

<article data-pet-surface="top left right">宠物可以站在这张卡片上,也能爬两侧</article>

这个整包已经带了默认角色 Mochi 和全部内置插件。地址栏加上 ?pets-debug 可以看到调试面板:所有的边、规划的路线、每只宠物的状态和需求。

在构建工具里用

import { createPets } from "oh-my-pet";
import { cursor, drag, items, persist, quietControl, scroll, speech } from "oh-my-pet/plugins";
import mochi from "oh-my-pet-pack-mochi";

const stage = createPets({
  characters: [mochi],
  plugins: [drag(), cursor(), scroll(), speech(), items(), persist(), quietControl()],
  locale: "zh",
  scale: 3
});

const pet = stage.pet("mochi");
await pet?.goTo({ tag: "featured" });
await pet?.say("你好");

createPets 根据传入的插件推导类型:没装 speech 时,pet.say 在 TypeScript 里就不存在。在 React 里,放进一个客户端组件的 useEffect,清理时调用 stage.destroy()。

告诉宠物页面上有什么

| 属性 | 含义 | | --- | --- | | data-pet-surface="top left right" | 元素的哪些边可以站或爬,默认 top | | data-pet-tags="featured ai" | 给行为和场景用的标签 | | data-pet-item="vue" | 可以被拿起、搬运的物品 | | data-pet-avoid | 宠物不进入、气泡不遮挡的区域 |

不方便改 HTML 时,用 createPets({ surfaces, items, avoid }) 传选择器。

宿主 API

stage.add(pack, { id: "mochi-2", palette: "sakura" });
stage.pet("mochi")?.do("sleep");
stage.pet("mochi")?.appear({ tag: "featured" }, "dig");
stage.itemVisual("vue", "<svg …>…</svg>");
stage.setActivity("reactive"); // 只回应访客,不主动活动
stage.pause();
stage.resume();
await stage.quiet(true); // 全部退场
stage.destroy();

stage.on("deliver", ({ item, target }) => {
  // 宠物把物品搬到了 target 上;后果由你决定
});

所有让宠物做事的请求都按优先级调度:拖拽 > 宿主指令 > 场景 > 反应 > 自主行为。被抢占的任务以 OhMyPetError(code: "aborted")失败,任务名下的移动、动画、台词、物品占用都会统一清理。

场景

stage.scene("greet", {
  cast: ["mochi", "sakura"],
  async run({ cast, world, parallel }) {
    const featured = world.edges.find({ tag: "featured" });
    if (!featured) return;
    await parallel(
      (b) => b.cast.mochi.goTo(featured),
      (b) => b.cast.sakura.goTo({ edge: featured.id, s: featured.length - 30 })
    );
    await cast.mochi.say("看这个!");
  }
});

await stage.play("greet", { queue: true });

开始前一次性占用全部参与者;宿主指令或拖拽抢占了任意一只,整个场景取消。parallel 任一分支失败会取消其余分支。

角色包

角色包是纯数据。源格式是一个 TypeScript 模块:

import type { PackSource } from "oh-my-pet/pack";

const pack: PackSource = {
  meta: { id: "blob", name: "Blob", formatVersion: 1 },
  palettes: { default: { ".": null, k: "#1c2536", w: "#ffffff" } },
  sprites: { idle_0: { anchor: [3, 4], rows: ["..kk..", ".kwwk.", "kwwwwk", ".kkkk."] } },
  hitbox: [-3, -4, 6, 4],
  clips: {
    idle: { frames: [{ sprite: "idle_0", ms: 500 }], loop: true }
    // walk、fall、land、drag、sleep、talk 也是必备的
  },
  traversals: { walk: { speed: 30 }, drop: true }
};

export default pack;
oh-my-pet-cli pack check src/blob.ts
oh-my-pet-cli pack build src/blob.ts -o dist/blob.js
oh-my-pet-cli pack preview src/blob.ts
oh-my-pet-cli pack import-aseprite blob.json -o src/blob.ts

在 Vite 里也可以直接 import pack from "./blob.ts?pack",配合 oh-my-pet/vite 里的 ohMyPetPacks() 插件,改了源文件会热更新。用 TypeScript 时,在 tsconfig 的 compilerOptions.types 里加上 "oh-my-pet/client",?pack 导入就有类型。

协议

MIT