English | 简体中文
本文是公开 API 的结构化速查。行为语义与设计原因请继续阅读对应专题文档。
1. 安装与运行环境
pnpm add @jason-huang/infinite-canvas-agent
- Node.js:
>=20(构建、SSR 和工具链)。 - 浏览器 API:Canvas2D/IndexedDB 适配器需要相应浏览器能力。
- 输出:ESM + CommonJS +
.d.ts。 sideEffects: false,建议使用 subpath import 控制模块图。
ESM:
import { createCanvasRuntime } from '@jason-huang/infinite-canvas-agent'
CommonJS:
const { createCanvasRuntime } = require('@jason-huang/infinite-canvas-agent')
2. Subpath 与可选 peer
| Import path | 主要导出 | 额外 peer |
|---|---|---|
| 包根路径 | core、agent、cache、integration、persistence 的公共导出 | 无 |
/core |
model、camera、document、spatial index、working set、runtime | 无 |
/integration |
createCanvasRuntime, CanvasRuntimeKit |
无 |
/cache |
ResourcePool, LOD helpers, AssetStore |
无 |
/agent |
commands、CommandBus、context、PlannerPort、remote parser | 无 |
/canvas2d |
Canvas2DRenderer |
无 |
/konva |
KonvaRenderer |
konva >=10 <11 |
/rbush |
RBushSpatialIndex |
rbush >=4 <5 |
/persistence |
persistence ports、memory implementation | 无 |
/indexeddb |
IndexedDbPersistence |
无 |
/react |
React useCanvasRuntime |
react >=18 <20 |
/vue |
Vue useCanvasRuntime |
vue >=3.3 <4 |
只有导入对应 subpath 才需要可选 peer。例如 cache-only 项目不需要安装 React、Vue、Konva 或 RBush。
3. Core model
Types
type NodeId = string
type JsonPrimitive = boolean | number | string | null
type JsonValue = JsonPrimitive | JsonValue[] | { [key: string]: JsonValue }
type JsonObject = { [key: string]: JsonValue }
interface Bounds { minX: number; minY: number; maxX: number; maxY: number }
interface Camera { centerX: number; centerY: number; zoom: number }
interface Viewport { width: number; height: number }
interface ViewState { camera; viewport; exactBounds; expandedBounds }
interface CanvasNode<Payload extends JsonObject = JsonObject> {
id: string
kind: string
bounds: Bounds
rotation?: number
zIndex?: number
assetIds?: string[]
payload: Payload
}
Functions
| Function | Result / failure |
|---|---|
assertBounds(bounds) |
非有限数或反转 bounds 时抛错 |
assertNode(node) |
校验 id、kind、bounds |
cloneNode(node) |
structuredClone |
intersects(left, right) |
bounds 是否相交,接触边界视为相交 |
clampZoom(zoom, min?, max?) |
默认 clamp 到 0.02..5 |
visibleWorldBounds(camera, viewport) |
精确可视世界 bounds |
expandBounds(bounds, distance) |
四边外扩 |
createViewState(camera, viewport, options?) |
clamp camera + exact/expanded bounds |
worldToViewport(point, view) |
世界坐标转 CSS viewport 坐标 |
viewportToWorld(point, view) |
viewport 坐标转世界坐标 |
4. Document
DocumentStore<Node>
| Member | 说明 |
|---|---|
revision |
当前非负整数 revision |
get(id) |
返回节点克隆或 undefined |
values() |
返回全部节点克隆 |
snapshot() |
{ revision, nodes } |
commit(input) |
revision + before 校验的原子提交 |
subscribe(listener) |
返回 unsubscribe |
MemoryDocumentStore(initialNodes?, initialRevision?) 是默认内存实现。构造时拒绝重复 id。
提交输入与输出:
interface NodeChange<Node> { id: string; before: Node | null; after: Node | null }
interface CommitInput<Node> {
transactionId: string
label: string
expectedRevision: number
changes: NodeChange<Node>[]
}
interface DocumentCommit<Node> {
transactionId: string
label: string
baseRevision: number
revision: number
changes: NodeChange<Node>[]
committedAt: number
}
5. Spatial index 与 WorkingSet
SpatialIndex<Node>
load, upsert, remove, search, clear, size。内置实现:
LinearSpatialIndex:无 peer,O(n) search;RBushSpatialIndex:/rbush,适合大文档。
WorkingSet
new WorkingSet({ exitGraceMs?: number }) // 默认 180
workingSet.reconcile(candidates, pinned, now?)
workingSet.remove(id)
workingSet.clear()
workingSet.values()
reconcile 返回:enter, stay, pendingExit, exit, active。
6. Renderer 与 Runtime
Renderer ports
| Port | 新增能力 |
|---|---|
RendererPort |
setView/hydrate/update/dehydrate/draw/count/destroy |
PreviewRendererPort |
setPreview(node?) |
PngExportRendererPort |
exportPng(): Promise<Blob> |
InteractiveRendererPort |
同时包含 preview + PNG |
内置 Canvas2DRenderer 和 KonvaRenderer 都实现 InteractiveRendererPort。
Canvas2D options
| Option | 默认值 |
|---|---|
devicePixelRatio |
globalThis.devicePixelRatio ?? 1 |
background |
#f8f8fc |
showGrid |
false |
gridColor |
#d8dbe6 |
axisColor |
#b7bdca |
selectionColor |
#ff7a45 |
style |
defaultNodeStyle |
Konva options
与 Canvas2D 的背景/网格/样式选项相同;另有 exportPixelRatio,默认 DPR。
CanvasRuntime<Node>
const runtime = new CanvasRuntime(document, spatialIndex, renderer, {
overscanPixels?: number
minimumZoom?: number
maximumZoom?: number
exitGraceMs?: number
})
runtime.setSelection(ids)
runtime.setPinned(ids)
runtime.refresh(camera, viewport, now?)
runtime.destroy()
createCanvasRuntime
推荐使用的组合工厂。输入可包含:
renderer:必填;document或initialSnapshot:二选一;initialHistory:必须与 Document revision 相同;spatialIndex:默认 Linear;runtime:Camera/WorkingSet 参数;history:CommandBus 历史预算。
返回 CanvasRuntimeKit:document, spatialIndex, renderer, runtime, commands,
refresh, destroy。
7. Commands 与 Agent
Command types
type CanvasCommand<Node> =
| { type: 'create'; node: Node }
| { type: 'update'; id: string; patch: Partial<Omit<Node, 'id'>> }
| { type: 'delete'; id: string }
interface AgentPlan<Node> {
actionId: string
label: string
expectedRevision: number
commands: CanvasCommand<Node>[]
}
applyCommandsToSnapshot(initialNodes, commands) 在克隆 Map 上暂存命令,返回 before/after Map,
不修改输入数组。
CommandBus
const bus = new CommandBus(document, {
maxHistoryTransactions?: number // 默认 100
maxHistoryBytes?: number // 默认 8 MiB
})
bus.execute(plan)
bus.undo()
bus.redo()
bus.inspectHistory()
bus.exportHistory()
bus.restoreHistory(snapshot)
Agent helpers
| API | 作用 |
|---|---|
buildAgentContext(input) |
按 selected/visible/recent/remaining 构建有界上下文 |
compactNodeForRemoteAgent(node, options?) |
明确裁剪允许发送的 payload |
parseRemoteAgentPlan(input) |
JSON、schema、大小、policy、最终快照校验 |
PlannerPort |
宿主 Planner 抽象,不包含实现 |
RemoteAgentPlanPolicy |
kinds、patch keys、坐标、size、payload、points、业务校验 |
详细门禁值见 Agent Integration。
8. Cache 与 Asset
ResourcePool<Resource>
const pool = new ResourcePool<Resource>(budgetBytes)
const lease = await pool.acquire(key, loader)
lease.release()
pool.evictUnused(targetBytes?)
pool.inspect()
pool.destroy()
loader 返回 { value, bytes, dispose(value) }。Stats 字段:entries、bytes、budgetBytes、
activeLeases、pendingLoads、hits、misses、evictions、rejections。
LOD
DEFAULT_IMAGE_TIERS = [128, 256, 512, 1024, 2048, 4096]selectImageTier(projectedMaxEdge, options?)imageResourceKey(assetId, tier)- 默认 downshift ratio
0.45
Asset
AssetVariantAssetManifestAssetStoreMemoryAssetStore
9. Persistence
| API | 说明 |
|---|---|
DocumentPersistence |
load/save snapshot + append commit |
CanvasWorkspacePersistence |
同 revision 保存/恢复 Document + History |
MemoryDocumentPersistence |
测试和无刷新内存实现,可 inspect journal |
IndexedDbPersistence |
/indexeddb,兼具 workspace 和 asset store |
IndexedDbPersistence options:databaseName 默认 infinite-canvas-agent,version 默认 2。
10. React / Vue lifecycle binding
React:
const runtimeRef = useCanvasRuntime(factory, dependencies)
// event handler 中读取 runtimeRef.current
effect cleanup 会 destroy 当次创建的 kit。factory 应在 DOM ref 可用后返回 kit 或 null。
Vue:
const runtime = useCanvasRuntime(factory)
// 读取 runtime.value
onMounted 创建,onBeforeUnmount destroy。两个 binding 都不管理业务 state、camera 或输入事件。
11. 公共错误
| Error | code |
触发条件 | 推荐处理 |
|---|---|---|---|
RevisionConflictError |
revision-conflict |
expected revision 或 before 过期 | 重读、重算、再提交 |
ActionIdConflictError |
action-id-conflict |
同 action id 被不同计划复用 | 修复调用方 id 生命周期 |
ResourceBudgetExceededError |
resource-budget-exceeded |
无可淘汰空间或单资源过大 | 降 LOD/释放 lease/拒绝挂载 |
其他参数/schema 错误使用 TypeError、RangeError 或普通 Error。不要靠错误 message 做程序
分支;优先使用公共 error class 或 code。
12. 类型扩展示例
interface CardPayload extends JsonObject {
schemaVersion: 1
title: string
color: string
}
interface CardNode extends CanvasNode<CardPayload> {
kind: 'card'
}
const kit = createCanvasRuntime<CardNode>({ renderer })
当项目有多种节点时,用联合类型并在 Renderer/业务 validator 中按 kind 穷举。核心包不会
注册全局节点类型,也不会阻止应用添加自定义字段之外的 JSON payload。