Canvas elements
A kind of thing that can exist on the canvas.
import { function canvasElement(element: Omit<CanvasElement, "kind">): CanvasElementDeclare a new kind of element the user can place on a document canvas. Renderer-native, like `editorExtension`. The module runs in the host's own renderer and shares its `lexical` and `react` instances through a live handle rather than over RPC, so the element is a real part of the canvas rather than an embedded webview. That is the line between this and `view`/`widget`, which are sandboxed and have no Node access. The `module` must default-export a `defineCanvasElement({...})` value, and its `schema.tag` has to match the `tag` you pass here. The loader compares the two by name and refuses to register the element if they differ. Use `canvasElement` to introduce a tag. Use {@link canvasTemplate } to offer a preset of a tag that already exists.canvasElement, function defineMod<const TMod extends ModDefinition>(definition: TMod & ValidateModHooks<TMod>): ModThe entry point of the SDK. A mod's module default-exports one `defineMod` call and nothing else. The returned value is data. It registers nothing, starts nothing, and touches no host state. The host folds it twice. At build time it runs the module in a throwaway sandbox and reads the value to derive the serializable manifest. At runtime it folds the same value again to wire your handlers into their lanes: ext-host for commands, tools, effects, and hooks; the sandboxed renderer for views and widgets. That double fold is why the module must be pure. It is re-executed to produce the manifest, so anything at the top level, a file read, a network call, a timer, a `console` line, runs during a build with no host attached. Put behavior inside handlers: `run`, `activate`, `handle`. The `ValidateModHooks` constraint makes the call fail to typecheck when a `hook.beforeAction` targets an action that is neither a known host action nor `command:` plus one of this mod's own command ids.defineMod } from '@modular/sdk'; export defaultdefineMod<{ readonly metadata: { readonly displayName: "Shapes"; readonly description: "Canvas work"; }; readonly canvas: readonly [CanvasElement]; }>(definition: { readonly metadata: { readonly displayName: "Shapes"; readonly description: "Canvas work"; }; readonly canvas: readonly [CanvasElement]; }): ModThe entry point of the SDK. A mod's module default-exports one `defineMod` call and nothing else. The returned value is data. It registers nothing, starts nothing, and touches no host state. The host folds it twice. At build time it runs the module in a throwaway sandbox and reads the value to derive the serializable manifest. At runtime it folds the same value again to wire your handlers into their lanes: ext-host for commands, tools, effects, and hooks; the sandboxed renderer for views and widgets. That double fold is why the module must be pure. It is re-executed to produce the manifest, so anything at the top level, a file read, a network call, a timer, a `console` line, runs during a build with no host attached. Put behavior inside handlers: `run`, `activate`, `handle`. The `ValidateModHooks` constraint makes the call fail to typecheck when a `hook.beforeAction` targets an action that is neither a known host action nor `command:` plus one of this mod's own command ids.defineMod({metadata: { readonly displayName: "Shapes"; readonly description: "Canvas work"; }metadata: { displayName: "Shapes"displayName: 'Shapes', description: "Canvas work"description: 'Canvas work' }, canvas: readonly [CanvasElement]canvas: [ function canvasElement(element: Omit<CanvasElement, "kind">): CanvasElementDeclare a new kind of element the user can place on a document canvas. Renderer-native, like `editorExtension`. The module runs in the host's own renderer and shares its `lexical` and `react` instances through a live handle rather than over RPC, so the element is a real part of the canvas rather than an embedded webview. That is the line between this and `view`/`widget`, which are sandboxed and have no Node access. The `module` must default-export a `defineCanvasElement({...})` value, and its `schema.tag` has to match the `tag` you pass here. The loader compares the two by name and refuses to register the element if they differ. Use `canvasElement` to introduce a tag. Use {@link canvasTemplate } to offer a preset of a tag that already exists.canvasElement({ id: stringid: 'shapes.note', name: stringname: 'Note', tag: stringtag: 'shapes-note', module: stringmodule: './canvas/note', }), ], });
tag is the custom-element name the canvas places, and module is the
renderer-native module that defines it.
Members
CanvasElement — id, name,
description, tag, module, tool, kind.
| Member | What it decides |
|---|---|
tag | the element's tag on the canvas |
module | where its implementation is loaded from |
tool | the tool a person uses to place one |
Like an editor extension, the implementation is named by
module rather than passed as a function — canvas code loads with the canvas.
tool is what connects the element to the person: without it, the element type
exists but nothing in the interface creates one.
The canvas entry point
Canvas-side types come from @modular/sdk/canvas — 41 exports. Browse them at
@modular/sdk/canvas.
To ship a pre-arranged group of elements, see canvas templates.