@jason-huang/infinite-canvas-agent
v0.1.1
Published
Framework-neutral infinite canvas runtime, bounded media cache, and host-owned Agent command contracts.
Maintainers
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四条核心不变量:
- DocumentStore 是持久作品真相。
- 离开视口只 dehydrate 运行时资源,不删除 Document 节点。
- 运行时容量由 WorkingSet 和明确资源预算约束。
- 所有持久修改都是原子的 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
