Learning in public — this reference is being written in the open. Unfinished pages are excluded from search engines.
paged.IDML Reference
Plugin SDK

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.

Tier: ProProIIIreference

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.

MethodRegistersNotes
tool(c)a tool-rail entryToolContribution: 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 panelPanelContribution: id, title, React component, default dock/group, icon. Panels compose the host UI kit so they read as native.
command(c)a commandid, 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 overlayRenders 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): Disposable

host.viewport

camera(): { scale, tx, ty }   // snapshot, CSS px
pxToPt(px: number): number    // screen px → document pt at current zoom

pxToPt 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 rect

The 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): Disposable

Mirrored 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.

On this page