Skip to main content

Canvas templates

A starting point a person can drop onto the canvas.

import { function canvasTemplate(template: Omit<CanvasTemplate, "kind">): CanvasTemplate
Declare a ready-made canvas element, filled in and named, for the insert menu. A template carries no renderer of its own. It names an existing `tag` and the attributes to stamp out with it, so one element can ship several starting points, sized and positioned, grouped and ordered in the picker. Introducing a tag is {@link canvasElement } . Offering a variant of a tag is this.
@categoryAuthoring
canvasTemplate
, function defineMod<const TMod extends ModDefinition>(definition: TMod & ValidateModHooks<TMod>): Mod
The 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.
@example```ts export default defineMod({ metadata: { displayName: 'Review', description: 'Review helpers for the current change.', }, commands: [ command({ id: 'review.open', title: 'Review: Open', run: openPanel }), slashCommand({ id: 'review', description: 'Review this change', run: review }), ], ai: { tools: [reviewTool], }, }); ```@categoryAuthoring
defineMod
} from '@modular/sdk';
export default
defineMod<{
    readonly metadata: {
        readonly displayName: "Shapes";
        readonly description: "Canvas work";
    };
    readonly canvas: readonly [CanvasTemplate];
}>(definition: {
    readonly metadata: {
        readonly displayName: "Shapes";
        readonly description: "Canvas work";
    };
    readonly canvas: readonly [CanvasTemplate];
}): Mod
The 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.
@example```ts export default defineMod({ metadata: { displayName: 'Review', description: 'Review helpers for the current change.', }, commands: [ command({ id: 'review.open', title: 'Review: Open', run: openPanel }), slashCommand({ id: 'review', description: 'Review this change', run: review }), ], ai: { tools: [reviewTool], }, }); ```@categoryAuthoring
defineMod
({
metadata: {
    readonly displayName: "Shapes";
    readonly description: "Canvas work";
}
metadata
: { displayName: "Shapes"displayName: 'Shapes', description: "Canvas work"description: 'Canvas work' },
canvas: readonly [CanvasTemplate]canvas: [ function canvasTemplate(template: Omit<CanvasTemplate, "kind">): CanvasTemplate
Declare a ready-made canvas element, filled in and named, for the insert menu. A template carries no renderer of its own. It names an existing `tag` and the attributes to stamp out with it, so one element can ship several starting points, sized and positioned, grouped and ordered in the picker. Introducing a tag is {@link canvasElement } . Offering a variant of a tag is this.
@categoryAuthoring
canvasTemplate
({
id: stringid: 'shapes.sticky', label: stringlabel: 'Sticky note', element: CanvasTemplateElementelement: { CanvasTemplateElement.tag: stringtag: 'shapes-note', CanvasTemplateElement.attrs: Readonly<Record<string, CanvasTemplateAttrValue>>attrs: { colour: stringcolour: 'amber', text: stringtext: '' }, }, }), ], });

element is the element to place and the attributes to place it with, not an element id. A template is a preset, so the attribute values are the preset.

Members

CanvasTemplateid, label, element, group, icon, order, kind.

element points at a canvas element by id. A template does not define behaviour; it names an element and gives a person a labelled way to reach it.

group and order are purely presentational — they decide where the template sits in the picker. label is what a person reads, unlike the element's name.

Templates and elements are two jobs

An element says this kind of thing can exist. A template says here is a convenient one to start from. Shipping an element with no template means the capability exists but nothing offers it; shipping several templates over one element is normal.