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

@jason-huang/infinite-canvas-agent

v0.1.1

Published

Framework-neutral infinite canvas runtime, bounded media cache, and host-owned Agent command contracts.

Readme

English | 简体中文

Infinite Canvas Agent

一个框架无关的 TypeScript 基础库,提供无限画布 Document、视口工作集、解码资源缓存、 Undo/Redo、持久化和宿主 Agent 接入方法。

它不包含应用壳、UI 框架、业务 Schema、模型 Provider、Prompt 或凭证管理。已有 Canvas、Konva、 React、Vue 项目可以只接入真正需要的一层。

npm install @jason-huang/infinite-canvas-agent

能解决什么

  • Document 可以很大,Renderer 只挂载“视口 + overscan”节点。
  • 通过七个方法的 RendererPort 保留现有 Konva/Canvas/WebGL 渲染器。
  • 使用 lease、并发加载去重、LRU 和硬字节预算限制解码图片与纹理。
  • 人工、脚本和宿主 Agent 共用 revision-checked CommandBus。
  • 持久化 Document、同 revision 的 Undo/Redo History 和资产 Blob。
  • 同时支持 ESM、CommonJS,并只安装需要的可选框架适配器。

架构

宿主 UI / 人工输入 / 宿主 Agent
                    |
                    v
             CanvasCommand[]
                    |
               CommandBus ------ Undo / Redo
                    |
               revision 事务
                    v
              DocumentStore ----- Persistence
                    |
               SpatialIndex
                    |
 Camera -> Bounds -> WorkingSet
                    |
             ResourcePool / LOD
                    |
               RendererPort
            /          |          \
       Canvas2D      Konva      自定义 Renderer

四条核心不变量:

  1. DocumentStore 是持久作品真相。
  2. 离开视口只 dehydrate 运行时资源,不删除 Document 节点。
  3. 运行时容量由 WorkingSet 和明确资源预算约束。
  4. 所有持久修改都是原子的 revision transaction。

Subpath 与可选依赖

| Import | 能力 | 可选 peer | | --- | --- | --- | | 包根路径 | Core、CommandBus、Cache、Integration、Persistence | 无 | | /core | Document、Camera、Index、WorkingSet、Runtime 契约 | 无 | | /integration | createCanvasRuntime 组合工厂 | 无 | | /cache | ResourcePool、图片 LOD、AssetStore | 无 | | /agent | Commands、有界上下文、宿主 Policy、远程计划解析 | 无 | | /canvas2d | Canvas2D Renderer | 无 | | /konva | Konva Renderer | konva >=10 <11 | | /rbush | RBush SpatialIndex | rbush >=4 <5 | | /persistence | 持久化端口与内存实现 | 无 | | /indexeddb | IndexedDB 文档/历史/资产实现 | 无 | | /react | React 生命周期绑定 | react >=18 <20 | | /vue | Vue 生命周期绑定 | vue >=3.3 <4 |

最小 Canvas2D 接入

import { createCanvasRuntime } from '@jason-huang/infinite-canvas-agent'
import { Canvas2DRenderer } from '@jason-huang/infinite-canvas-agent/canvas2d'

const kit = createCanvasRuntime({
  renderer: new Canvas2DRenderer(canvas, { showGrid: true }),
  runtime: { overscanPixels: 240, exitGraceMs: 180 },
})

kit.commands.execute({
  actionId: crypto.randomUUID(),
  label: 'Create card',
  expectedRevision: kit.document.revision,
  commands: [{
    type: 'create',
    node: {
      id: 'card-1',
      kind: 'card',
      bounds: { minX: -120, minY: -60, maxX: 120, maxY: 60 },
      payload: { label: 'Hello canvas', fill: '#5b5bd6' },
    },
  }],
})

kit.refresh(camera, { width: canvas.clientWidth, height: canvas.clientHeight })

// 页面或组件销毁时调用。
kit.destroy()

宿主继续负责输入事件、Camera、选区、路由和业务节点 Schema。

保留现有 Renderer

interface RendererPort<Node extends CanvasNode> {
  setView(view: ViewState): void
  hydrate(node: Node): void
  update(node: Node): void
  dehydrate(id: string): void
  draw(frame: RenderFrame<Node>): void
  getObjectCount(): number
  destroy(): void
}

hydrate 只创建当前需要的对象;dehydrate 释放场景与媒体资源,但不删除 Document 节点。 大文档可以传入 RBushSpatialIndex。

只接入缓存

const pool = new ResourcePool<ImageBitmap>(128 * 1024 * 1024)

const lease = await pool.acquire('asset-42@1024', async () => {
  const bitmap = await loadBitmap()
  return {
    value: bitmap,
    bytes: bitmap.width * bitmap.height * 4,
    dispose: (value) => value.close(),
  }
})

lease.release()
pool.evictUnused()

同 key 并发 acquire 只运行一个 loader。有 active lease 的资源不能被淘汰;无法腾出空间时会 dispose 新资源并抛出 ResourceBudgetExceededError。

持久化

const persistence = new IndexedDbPersistence<MyNode>({ databaseName: 'my-canvas' })
const saved = await persistence.loadWorkspace(documentId)

const kit = createCanvasRuntime<MyNode>({
  renderer,
  initialSnapshot: saved?.document,
  initialHistory: saved?.history,
})

await persistence.saveWorkspace(
  documentId,
  kit.document.snapshot(),
  kit.commands.exportHistory(),
)

History 只能在完全相同的 Document revision 恢复。Renderer、解码资源和索引会重新构建。

宿主 Agent 接入

包只提供接入方法,不内置 Agent:

const snapshot = kit.document.snapshot()
const context = buildAgentContext({
  revision: snapshot.revision,
  nodes: snapshot.nodes,
  selectedIds,
  visibleIds,
  maximumNodes: 60,
})

const providerOutput = await myBackend.plan({
  prompt,
  nodes: context.nodes.map((node) => compactNodeForRemoteAgent(node)),
})

const plan = parseRemoteAgentPlan<MyNode>({
  actionId: crypto.randomUUID(),
  expectedRevision: snapshot.revision,
  nodes: snapshot.nodes,
  content: providerOutput,
  policy: {
    allowedKinds: ['card', 'connector'],
    allowedPatchKeys: ['bounds', 'payload', 'zIndex'],
    validateNode: assertProductNode,
  },
})

kit.commands.execute(plan)

宿主负责 Provider、Prompt、API Key、业务权限和节点校验。所有远程响应都视为不可信输入, 服务端仍需重新鉴权。过期 Agent 响应会被相同的 revision 门禁拒绝。

SDK 文档

包保证

  • Node.js >=20 用于构建和工具链。
  • 发布 ESM、CommonJS 与 TypeScript declarations。
  • Optional peer 不进入无关 subpath。
  • Tarball 使用严格文件白名单,并扫描凭证与私有路径。
  • 临时空消费者验证 TypeScript、ESM、CJS 和 optional peers。

License

MIT