Dottie can run user plugins written in JavaScript. A plugin can be a loose .js command or a package folder containing plugin.json and a JavaScript entry point.
By default, Dottie loads plugins from its own container folder—nothing to configure. Choose Plugins > Open Plugins Folder… to reveal that folder in the Finder, and Plugins > Reload Plugins to re-scan it after you add or edit files.
Commands, validators, and tools appear in Plugins. Plugin importers and exporters appear in the corresponding File menus. Dottie bundles isometric projections, configurable Shadow and Extrude effects, and palette-range remapping.
To try it: make a rectangular selection with the Select Rectangle tool, then choose Plugins > Selection > Make Selection Isometric Up-Right (or Up-Left). Press Command+Z to undo it in one step.
A command calls dottie.command({ name, menu, run }) or the compatible dottie.plugin(…). Packages may also register dottie.tool, dottie.importer, dottie.exporter, or dottie.validator. Every callback receives a context object containing only host-mediated operations.
Context properties:
ctx.canvas — { width, height } of the current document.
ctx.selection.bounds — { x, y, w, h } of the selection bounding box, or null if nothing is selected.
ctx.selection.mask — mask[x][y] booleans, indexed relative to the bounding box origin.
ctx.selection.colors — colors[x][y] strings in "#RRGGBBAA" format (or null for transparent/empty pixels), bounds-relative.
ctx.colors — the current primary/secondary colours (also named foreground/background).
ctx.palette — palette colours, exact and nearest lookup, plus capability-controlled add/update/remove/move operations.
ctx.layers, ctx.frames, and ctx.tags — fresh snapshots and zero-based host operations for document structure, properties, pixels, timing, and animation ranges. The same objects are grouped under ctx.document, which also exposes the document mode and note plus canvas resize and scale operations.
ctx.preferences — persistent values scoped to the package ID.
ctx.files — native open/save panels and read/write methods limited to the paths selected in those panels.
Returning changes:
Call ctx.emptyResult() to obtain a result builder. Use .select(x, y) to mark canvas pixels as selected and .paint(x, y, "#RRGGBBAA") to write a pixel colour (both take canvas coordinates). Pass the finished builder to ctx.commit(result)—this clears the original pixels, paints the new ones, and updates the selection, all as a single undo step.
For selection-only changes (no pixel edits), use ctx.emptyMask() to build a new mask and ctx.selection.replace(mask) to apply it.
Wrap several host changes in ctx.transaction("Name", function () { … }) to create one undo step. If JavaScript throws, Dottie restores the complete before-state.
Native parameters and background work:
ctx.dialog({ title, fields }) renders integer, choice, colour, boolean, and text controls in declaration order. Cancel returns null.
ctx.jobs.start(worker, completion, progress) gives the worker an immutable JSON-compatible document snapshot on a separate JavaScript context. The control argument provides isCancelled() and progress(amount, message); cancel using ctx.jobs.cancel(id). Apply results from the main-thread completion inside a transaction.
Package manifest:
{
"id": "com.example.my-plugin",
"name": "My Plugin",
"version": "1.0",
"apiVersion": 2,
"entry": "main.js",
"capabilities": ["document.read", "document.write", "preferences"]
}
Supported capabilities are document.read, document.write, palette.write, preferences, files.open, files.save, validators, and tools. Unsupported API versions, unknown capabilities, duplicate IDs, and entry paths outside the package are rejected and listed by Plugins > Show Plugin Errors….
The isometric example below shows the full pattern:
// Make Selection Isometric — the named edge stays put and the tilt rises away from it.
function isometric(ctx, anchorRight) {
var b = ctx.selection.bounds;
if (!b) return; // nothing selected → no-op
var mask = ctx.selection.mask; // mask[x][y], bounds-relative
var cols = ctx.selection.colors; // "#RRGGBBAA" or null
var out = ctx.emptyResult(); // builder for the new content
for (var x = 0; x < b.w; x++) {
var dist = anchorRight ? (b.w - 1 - x) : x; // columns from the fixed edge
var shift = -(dist >> 1); // 2px columns, 1px up each
for (var y = 0; y < b.h; y++) {
if (mask[x][y]) {
var nx = b.x + x, ny = b.y + y + shift;
out.select(nx, ny); // selection follows the tilt
if (cols[x][y]) out.paint(nx, ny, cols[x][y]);
}
}
}
ctx.commit(out); // apply as one undo step
}
dottie.plugin({ name: "Make Selection Isometric Up-Right", menu: "Selection",
run: function (ctx) { isometric(ctx, false); } });
dottie.plugin({ name: "Make Selection Isometric Up-Left", menu: "Selection",
run: function (ctx) { isometric(ctx, true); } });
Plugins have no network or ambient file access. Packaged plugins receive only declared capabilities; file reads and writes additionally require a path chosen by the user in a native panel.