The BundleHost
Area-by-area reference for the BundleHost — the single object a plugin receives at activation, carrying contribution registration, document access, selection, viewport, overlays, storage, diagnostics, and capability detection.
One parameter carries the whole platform. activate(host) receives a
BundleHost; this page is its complete surface at API v0.2. Every member
returns serializable data or a Disposable; expected failures resolve as
results, never throws.
host.manifest · host.log
Your own manifest (read-only) and a namespaced logger
(debug/info/warn/error, prefixed [<plugin-id>]). The logger shares its
sink with the diagnostics channel.
host.contribute
Every method enforces the namespace rule and tracks the registration for
structural teardown. All return Disposable.
| Method | Registers | Notes |
|---|---|---|
tool(c) | a tool-rail entry | ToolContribution: id, title, icon, shortcut, flyout group, rail section, cursor, and the gesture() handler factory the host mounts when the tool activates. Prefer the SDK's contributeTool, which also wires the activation command + guarded shortcut. |
panel(c) | a panel | PanelContribution: id, title, React component, default dock/group, icon. Panels compose the host UI kit so they read as native. |
command(c) | a command | id, title, category, handler(paged, payload?). Reachable from the command palette and keybindings. |
keybinding(c) | a key binding | { key: "cmd+shift+h", command, when? }. The when predicate receives live editor state (e.g. suppress single-key tool shortcuts while a text caret is active). |
overlay(c) | a canvas overlay | Renders into the shared camera-transformed SVG above the page. |
editContext(d) | — | Reserved. Throws PluginApiNotImplemented. |
objectType(d) | — | Reserved. Throws PluginApiNotImplemented. |
host.document
Read broadly; write through one door.
mutate(m: Mutation): Promise<MutationOutcome>
// → { applied: true, createdId, pageIds } | { applied: false, error }
undo(): Promise<void> redo(): Promise<void>
collection<T>(name): Promise<readonly T[]> // swatches, layers, styles, …
meta(): Promise<DocumentMeta> // pageCount, units, colorMode, …
pathAnchors(id): Promise<PathAnchorsResult | null> // Bézier anchor table
hitTest(pageId, [x, y], filter?): Promise<HitResult | null>
elementGeometry(ids): Promise<ElementGeometryItem[]>
tree(): Promise<SceneTreeNode[]>
onDidChange(l): Disposable
// fires { kind: "mutationApplied" | "undoApplied" | "redoApplied", pageIds }mutate is the write path. Every change lands on the editor's single
undoable Operation channel — plugin and native edits share one history. The
typed Mutation union (path topology, properties, swatches, gradients,
layers, pathfinder booleans, batches, …) is re-exported by
@paged-media/plugin-api; batches apply atomically and undo as one step.
Coordinate convention for pathAnchors and hitTest: anchor tables are in
the element's local frame (apply the result's itemTransform to reach
page-local space); hit-test points are page-local pt.
host.selection
get(): ElementId[]
set(ids, mode?): Promise<ElementId[]> // mode: "replace" (default) | …
onDidChange(l): Disposablehost.viewport
camera(): { scale, tx, ty } // snapshot, CSS px
pxToPt(px: number): number // screen px → document pt at current zoompxToPt is the constant-screen-tolerance idiom: a 6 px pick radius should
stay 6 px on screen at every zoom level.
host.overlay
setToolPreview(shape | null)
// shape: { pageId, points: [x,y][], close? } — polyline
// | { pageId, rect: [t,l,b,r] } — rubber-band rectThe in-progress gesture preview channel — page-local pt, rendered by the host above the canvas, excluded from export. Cubic previews are flattened to polylines at this surface today.
host.storage
Namespaced key-value persistence (paged.plugin.<id>.*), JSON values:
get<T>(key): T | undefined set(key, value)
delete(key) keys(): string[]host.diagnostics
The plugin→host problem channel — parse errors, unsupported-feature warnings:
set(key, Diagnostic[]) // { severity, message, source?, line?, column? }
clear(key?) get(key): Diagnostic[]
onDidChange(l): DisposableMirrored to the console in v0; the host's problems UI consumes the same store as it lands.
host.supports(feature)
Runtime capability detection, preferred over version sniffing for graceful degradation:
if (host.supports("document.hitTest@1")) { … }Feature strings have the form "area.member@major"; the implemented set is
exported as HOST_FEATURES from @paged-media/plugin-sdk, so code, tests,
and these docs cannot drift apart.
host.editor — the marked escape hatch
The raw editor handle, present at v0 by design (gesture handlers receive
it from the host's tool spine anyway). The rule: any use of host.editor not
reachable through a facade is an API gap by definition — record it, don't
normalize it. This member does not survive the future isolate boundary.
The manifest
Field-by-field reference for a Paged plugin's manifest.json — identity, apiVersion negotiation, the capability declaration, and the contributes block, including what is enforced today versus declared for the future.
Building a drawing tool
How to ship a canvas tool from a plugin — the GestureHandler contract, the gesture kit's page-anchored drag helpers, preview publishing, and committing one undoable mutation. The pattern behind Paged's own pen tool.