@paged-media/plugin-api 0.2.6-canary.0 → 0.2.9-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,4 @@
1
+ import type { AssetKind } from "./assets";
1
2
  /** Reverse-DNS plugin identity, e.g. `media.paged.draw`. Doubles as
2
3
  * the namespace prefix for every contribution id the bundle
3
4
  * registers (`media.paged.draw.tool.pen`). */
@@ -15,20 +16,144 @@ export interface PluginManifest {
15
16
  contributes?: PluginContributions;
16
17
  }
17
18
  export interface PluginCapabilities {
18
- /** read-broad / write-scoped is the intended default. */
19
+ /** read-broad / write-scoped is the intended default. Declaring
20
+ * `document` is the PREREQUISITE for the document surface: a bundle
21
+ * with no `document` capability cannot read OR write the document
22
+ * (the host gate throws). `read` gates the read doors
23
+ * (collection/meta/tree/pathAnchors/elementGeometry/getMetadata/
24
+ * onDidChange); `write` gates the write doors (mutate/undo/redo/
25
+ * setMetadata) AND `selection.set` (a document-level action). When
26
+ * enforcement is on, an absent sub-field denies that direction. */
19
27
  document?: {
20
28
  read?: "broad" | "scoped";
21
29
  write?: "broad" | "scoped";
22
30
  };
23
31
  /** Render-pipeline surfaces the bundle uses. v0: `overlay` means
24
- * the shared TS overlay signals (tool previews); `sceneLayer` and
25
- * a host-side `hitTest` service are reserved for the P2 channel. */
32
+ * the shared TS overlay signals (tool previews) AND
33
+ * `contribute.overlay`; `hitTest` gates `document.hitTest`;
34
+ * `sceneLayer` is reserved for the P2 channel. Declaring a surface
35
+ * is the prerequisite for the matching door (the host gate throws
36
+ * on an undeclared use). */
26
37
  rendering?: Array<"sceneLayer" | "overlay" | "hitTest">;
38
+ /** The bundle registers keybindings directly via
39
+ * `contribute.keybinding`. Keybindings have no id to list under
40
+ * `contributes`, so this boolean is their declaration. v0 first-
41
+ * party bundles let the HOST derive activation shortcuts from the
42
+ * tool registry (B-15), so this stays `false`/absent for them; a
43
+ * bundle that wires its OWN keybindings must declare it. */
44
+ keybindings?: boolean;
27
45
  /** Edit-context content types the bundle claims (P0 shell work —
28
46
  * reserved, not yet wired). */
29
47
  editContext?: string[];
30
- network?: boolean;
48
+ /**
49
+ * Asset-store reads the bundle uses (paged.web W-06). A closed array
50
+ * vocabulary: v1 has exactly `"fonts"` (gates `host.assets.getFontFace`
51
+ * — serving DOCUMENT font face bytes for `@font-face`). `"images"` is
52
+ * RESERVED for v2 and REJECTED by validation today (the door has no
53
+ * image read yet; accepting the declaration would claim a capability
54
+ * the host cannot honor). Declaring `"fonts"` is the prerequisite for
55
+ * the door (the host gate throws on an undeclared use). See
56
+ * DESIGN.md §13. */
57
+ assets?: AssetKind[];
58
+ /**
59
+ * Persistent BINARY storage the bundle uses (K-4 / S-08). The KV
60
+ * `host.storage` (localStorage JSON) is always available ungated; this
61
+ * capability gates the OPFS-backed `host.blob` byte store for payloads
62
+ * too large for KV (multi-MB workbook bytes, decode spill). Declaring
63
+ * `blob: true` is the prerequisite for every `host.blob` door (the host
64
+ * gate throws on an undeclared use). `quotaBytes`, when present,
65
+ * REQUESTS a ceiling — the host enforces the stricter of it and its
66
+ * hard per-plugin cap; `host.blob.usage()` reports the granted value.
67
+ */
68
+ storage?: StorageCapability;
69
+ /**
70
+ * Network reach the bundle declares (paged.data D-03; base-idea §11). The
71
+ * boolean form is the legacy shorthand (`true` = the bundle reaches the
72
+ * network, every origin still gated behind runtime consent; `false`/absent =
73
+ * no network). The object form declares a per-origin allow-list + a
74
+ * human-readable purpose the consent UI shows. Reach is NEVER silent: every
75
+ * origin is gated behind `host.network.requestConsent` (the visible
76
+ * data-source manifest), and a document does NOT fetch on open — external
77
+ * sources are inert until the user reviews + consents (base-idea §11). This
78
+ * is the OUTER bound; consent is the inner gate.
79
+ */
80
+ network?: boolean | NetworkCapability;
81
+ /**
82
+ * Data-provider roles (paged.data §7.1 / D-09): the neutral cross-plugin
83
+ * composition where one plugin PUBLISHES a resolved dataset and another
84
+ * CONSUMES it (e.g. a sheet sourced from a governed query) — they rendezvous
85
+ * ONLY at the core `host.dataProviders` registry, never by direct contact.
86
+ * `publish` = the categories this bundle may register providers in; `consume`
87
+ * = the categories it may discover + read. A bundle declaring neither role
88
+ * gets no surface.
89
+ */
90
+ dataProviders?: DataProvidersCapability;
31
91
  clipboard?: "none" | "vector" | "full";
92
+ /**
93
+ * Declared WebAssembly artifacts the bundle ships and loads at
94
+ * runtime (paged.web W-07 — e.g. a future HTML/CSS layout engine
95
+ * compiled to wasm). Capability-gated: a bundle CANNOT instantiate a
96
+ * module the host has not granted via the loader, and only the paths
97
+ * listed here are loadable (declared-only). See DESIGN.md §10 and
98
+ * `docs/wasm-packaging.md` for the budget rules and trust line.
99
+ *
100
+ * The wasm gets NO ambient authority: it has no direct engine/DOM/
101
+ * network handle and talks only through the bundle's already-gated
102
+ * JS. Threads/SharedArrayBuffer are OFF in v1.
103
+ */
104
+ wasm?: WasmArtifact[];
105
+ }
106
+ /** Persistent binary-storage declaration (K-4 / S-08). `blob` gates the
107
+ * OPFS-backed `host.blob` byte store; `quotaBytes` requests a ceiling
108
+ * (the host enforces the stricter of it and its hard per-plugin cap). */
109
+ export interface StorageCapability {
110
+ blob?: boolean;
111
+ quotaBytes?: number;
112
+ }
113
+ /** A structured network declaration (paged.data D-03; base-idea §11). The
114
+ * `origins` allow-list is the OUTER bound — the set of `scheme://host[:port]`
115
+ * the bundle may EVER request; runtime per-origin consent is the inner gate.
116
+ * The string `"consent"` means the bundle has no fixed list (author-supplied
117
+ * sources) — every reach requires runtime consent and none is pre-allowed. */
118
+ export interface NetworkCapability {
119
+ origins: string[] | "consent";
120
+ /** Human-readable reason shown in the consent UI / data-source manifest. */
121
+ purpose?: string;
122
+ }
123
+ /** Data-provider roles (paged.data §7.1 / D-09). Categories are a neutral, open
124
+ * string vocabulary (`"dataset"`, …) — discovery is BY CATEGORY, never by
125
+ * plugin identity. The PROVIDER and CONSUMER plugins each declare only their
126
+ * own role; they never name or import each other (§2.1). */
127
+ export interface DataProvidersCapability {
128
+ /** Categories this bundle MAY register providers in (the publish role). */
129
+ publish?: string[];
130
+ /** Categories this bundle MAY discover + read (the consume role). */
131
+ consume?: string[];
132
+ }
133
+ /** Purposes a bundle may declare for a shipped wasm module. A closed
134
+ * vocabulary (like `rendering`) so the host can reason about grants;
135
+ * unknown purposes are rejected at validation. v1:
136
+ * - `layout` — a foreign-document layout/measure engine (paged.web).
137
+ * - `codec` — encode/decode (image/font transforms).
138
+ * - `compute` — generic pure computation with no special host role. */
139
+ export type WasmPurpose = "layout" | "codec" | "compute";
140
+ /** One declared wasm artifact. `path` is bundle-relative (no leading
141
+ * slash, no `..`); `maxBytes`, when present, tightens — never widens —
142
+ * the host's per-artifact ceiling (see the budget table in
143
+ * `docs/wasm-packaging.md`). */
144
+ export interface WasmArtifact {
145
+ /** Logical name the bundle passes to the host loader
146
+ * (`loadBundleWasm(bundle, name)`). Unique within the manifest. */
147
+ name: string;
148
+ /** Bundle-relative path to the `.wasm` file. No leading `/`, no `..`
149
+ * segment (path-traversal is rejected at validation). */
150
+ path: string;
151
+ /** Why the bundle ships this module — gates what the host grants. */
152
+ purpose: WasmPurpose;
153
+ /** Optional per-artifact byte ceiling the bundle self-imposes. Must
154
+ * be ≤ the host's hard per-artifact ceiling; the loader enforces the
155
+ * stricter of the two. */
156
+ maxBytes?: number;
32
157
  }
33
158
  export interface PluginContributions {
34
159
  /** Tool ids the bundle registers. Must be namespaced by `id`. */
@@ -57,4 +182,12 @@ export interface PluginContributions {
57
182
  type: string;
58
183
  bakedFallback: "group" | "rectangle" | "raster";
59
184
  }>;
185
+ /** Importer ids the bundle registers (K-2 / S-06). Must be namespaced
186
+ * by `id`. The rich `ImporterContribution` (extensions, MIME, the
187
+ * `import()` callback) is handed in at `host.contribute.importer(...)`
188
+ * — the manifest only DECLARES which ids may register. */
189
+ importers?: string[];
190
+ /** Exporter ids the bundle registers (K-2 / S-06). Must be namespaced
191
+ * by `id`. */
192
+ exporters?: string[];
60
193
  }
@@ -1 +1 @@
1
- export type { ElementId, PageId, NodeId, NodeSpec, Mutation, Operation, PropertyPath, Value, PathAnchorSpec, PathAnchorTriple, PathAnchorsResult, PathPointAddress, PathPointRole, PathfinderKind, HitFilter, HitResult, CollectionName, DocumentMeta, ElementGeometryItem, SceneTreeNode, SelectionMode, ContentSelection, MainToWorker, MainToWorkerKind, WorkerToMain, GestureType, GestureHandle, GestureModifiers, SwatchSpec, GradientSpec, GradientStopSpec, SwatchSummary, GradientSummary, LayerSummary, } from "./wire";
1
+ export type { ElementId, PageId, NodeId, NodeSpec, Mutation, Operation, PropertyPath, Value, PathAnchorSpec, PathAnchorTriple, PathAnchorsResult, PathPointAddress, PathPointRole, PathfinderKind, HitFilter, HitResult, CollectionName, DocumentMeta, ElementGeometryItem, SceneTreeNode, SelectionMode, ContentSelection, MainToWorker, MainToWorkerKind, WorkerToMain, SceneLayer, SceneItem, ScenePathSeg, ScenePaint, GestureType, GestureHandle, GestureModifiers, SwatchSpec, GradientSpec, GradientStopSpec, SwatchSummary, GradientSummary, LayerSummary, } from "./wire";
@@ -0,0 +1,92 @@
1
+ import type { ComponentType } from "react";
2
+ import type { BindingsSurface } from "./host";
3
+ import type { PropertyPath, Value } from "./wire";
4
+ export type WidgetValueBinding = {
5
+ kind: "literal";
6
+ value: Value;
7
+ } | {
8
+ kind: "selectionProperty";
9
+ /** Which selection surface to address (defaults to `"element"`). */
10
+ scope?: "element" | "content";
11
+ /** The PropertyPath the widget reads from + commits to. */
12
+ path: PropertyPath;
13
+ /** Optional unit coercion on read + write (`pt` / `px` / `%`). */
14
+ coerce?: "pt" | "px" | "%";
15
+ };
16
+ export interface BindingRef {
17
+ /** The published binding name to look up (`host.bindings.publish`). */
18
+ bind: string;
19
+ /** Invert the looked-up boolean. The only transform — NOT a DSL. */
20
+ negate?: boolean;
21
+ }
22
+ /** A visibility / enablement gate: either a static literal or a
23
+ * lookup of a published plugin binding. Absent = always shown /
24
+ * enabled. A non-boolean published value is coerced with `Boolean()`
25
+ * (and a missing name reads as `false` — a visible seam, never a
26
+ * throw). */
27
+ export type SchemaGate = boolean | BindingRef;
28
+ export interface PanelSchemaRow {
29
+ /** Catalog widget id (a curated primitive-leaf id the host
30
+ * registered). An unknown id renders a visible "unknown widget"
31
+ * placeholder, not a throw. */
32
+ widget: string;
33
+ /** Static props forwarded to the leaf (label, options, placeholder,
34
+ * …). Must match the leaf's declared `PropSchema`; unknown keys are
35
+ * ignored by the leaf. */
36
+ props?: Record<string, unknown>;
37
+ /** The widget's primary value binding — the §11.5 ceiling
38
+ * (`literal | selectionProperty`). Absent = a layout-only / display
39
+ * leaf (label, section). */
40
+ value?: WidgetValueBinding;
41
+ /** Show the row only when this gate is truthy. Absent = always. */
42
+ visible?: SchemaGate;
43
+ /** Enable the row's control only when this gate is truthy. Absent =
44
+ * always enabled (subject to the widget's own no-write-path
45
+ * disable). */
46
+ enabled?: SchemaGate;
47
+ }
48
+ export interface PanelSchemaSection {
49
+ /** Section title (the kicker / disclosure header). Absent = an
50
+ * untitled flat group. */
51
+ title?: string;
52
+ /** Render as a collapsible disclosure (the catalog section's
53
+ * `collapsible` chrome). */
54
+ collapsible?: boolean;
55
+ rows: PanelSchemaRow[];
56
+ /** Hide the entire section (title + rows) on this gate. */
57
+ visible?: SchemaGate;
58
+ }
59
+ export interface PanelSchema {
60
+ /** Stable id, `<namespace>.<panel>` — namespace-checked at register
61
+ * exactly like a React panel. */
62
+ id: string;
63
+ title: string;
64
+ icon?: string;
65
+ defaultDock?: "left" | "right" | "top" | "bottom" | "center";
66
+ defaultGroup?: string;
67
+ sections: PanelSchemaSection[];
68
+ }
69
+ /** A panel contribution that is a declarative SCHEMA rather than an
70
+ * expert-leaf React component. The host renders it from the catalog
71
+ * and subscribes to the bundle's published bindings. Carries no
72
+ * React — the isolate-ready panel form. */
73
+ export interface SchemaPanelContribution {
74
+ id: string;
75
+ title: string;
76
+ icon?: string;
77
+ defaultDock?: "left" | "right" | "top" | "bottom" | "center";
78
+ defaultGroup?: string;
79
+ /** The declarative body (sections/rows/widgets from the catalog). */
80
+ schema: PanelSchema;
81
+ closable?: boolean;
82
+ movable?: boolean;
83
+ }
84
+ export interface SchemaPanelRendererProps {
85
+ schema: PanelSchema;
86
+ /** The bundle's published-bindings surface — the renderer subscribes
87
+ * to it so `visible`/`enabled` gates react to plugin state. */
88
+ bindings: BindingsSurface;
89
+ }
90
+ /** The host-injected schema-panel renderer (React). Resolves a schema
91
+ * through the host's catalog + the bundle's bindings. */
92
+ export type SchemaPanelRenderer = ComponentType<SchemaPanelRendererProps>;
@@ -0,0 +1,48 @@
1
+ import type { ComponentType } from "react";
2
+ /** Languages the host code editor highlights. `text` = no
3
+ * highlighting (line numbers + gutter only). Additive: a host MAY
4
+ * accept more, but only these are contract-guaranteed. */
5
+ export type CodeEditorLanguage = "html" | "css" | "text";
6
+ /** A per-line marker rendered in the editor's diagnostics gutter and,
7
+ * where the host supports it, as an inline squiggle. Structurally a
8
+ * subset of `Diagnostic` (host.ts) — `line` is 1-based. */
9
+ export interface CodeEditorDiagnostic {
10
+ severity: "error" | "warning" | "info";
11
+ message: string;
12
+ /** 1-based line the marker attaches to. Out-of-range = clamped. */
13
+ line: number;
14
+ }
15
+ /**
16
+ * Props the host code-editor widget accepts. Controlled: the bundle
17
+ * owns `value` and applies `onChange`. The widget never mutates the
18
+ * document — it is a pure text surface (the panel persists through
19
+ * `host.document`/`host.storage` as it already does for a textarea).
20
+ */
21
+ export interface CodeEditorProps {
22
+ value: string;
23
+ onChange(next: string): void;
24
+ /** Syntax-highlight language; default `"text"`. */
25
+ language?: CodeEditorLanguage;
26
+ /** Per-line markers (squiggles + gutter dots). */
27
+ diagnostics?: readonly CodeEditorDiagnostic[];
28
+ /** Non-editable view (still highlighted + line-numbered). */
29
+ readOnly?: boolean;
30
+ /** Min editor height in CSS px (the widget grows with content). */
31
+ minHeight?: number;
32
+ /** Accessible label / test hook passthrough (`data-*` is host'd). */
33
+ ariaLabel?: string;
34
+ }
35
+ /**
36
+ * The widget catalog the host injects. Members are React component
37
+ * TYPES (the knowingly-non-clonable exit, same class as panel
38
+ * components — DESIGN.md §6): they live in the host's UI package and
39
+ * are handed to in-process bundles. Across the future isolate boundary
40
+ * a widget is addressed by a serializable descriptor instead; that is
41
+ * a second `WidgetSurface` implementation, not a contract change.
42
+ */
43
+ export interface WidgetSurface {
44
+ /** The line-numbered, syntax-highlighting, diagnostics-gutter source
45
+ * editor. Always present: a plain-textarea fallback stands in when
46
+ * the host app injects no real widget catalog. */
47
+ readonly CodeEditor: ComponentType<CodeEditorProps>;
48
+ }