Affinity SDK — Quick Reference Cheat Sheet
Keep this file open while coding. One page covering the patterns you'll use every session.
Boilerplate — every script starts here
'use strict';
const { Document } = require('/document.js');
const doc = Document.current;
if (!doc) { console.log('No document open.'); }
else {
// your code here
}
Imports by task
// Document access
const { Document, FileExportOptions, FileExportArea, DocumentPromises } = require('/document.js');
const { app } = require('/application.js');
// Making changes
const { DocumentCommand, CompoundCommandBuilder, AddChildNodesCommandBuilder } = require('/commands.js');
// Selections
const { Selection, TextSelection } = require('/selections.js');
// Colours and fills
const { RGBA8, Colour } = require('/colours.js');
const { FillDescriptor, SolidFill } = require('/fills.js');
// Strokes
const { LineStyle, LineStyleMask } = require('/linestyle.js');
// Geometry
const { Transform, CurveBuilder, PolyCurve } = require('/geometry.js');
// Text
const { StoryBuilder } = require('/storybuilder.js');
const { StoryDelta } = require('/storydelta.js');
const { GlyphAttStringType } = require('/glyphatts.js');
// Dialogs
const { Dialog, DialogResult, DialogSpatialAnchor, SpatialAnchor } = require('/dialog.js');
// Units
const { UnitType } = require('/units.js');
// Collections
const { Collection } = require('/collection.js');
// Node tree
const Nodes = require('/nodes.js');
// Files
const { File } = require('/fs.js');
const { Buffer } = require('/buffer.js');
const { HttpRequest, RequestMethod } = require('/network.js');
// System / logging / config
const { OS } = require('/os.js');
const { Environment } = require('/environment.js');
const { LogFile, LogLevel } = require('/logging.js');
const { ConfigurationItem } = require('/configuration.js');
Programmatic selection
const { Selection } = require('/selections.js');
const { DocumentCommand } = require('/commands.js');
// Select a single node
const sel = Selection.create(doc, targetNode);
doc.executeCommand(DocumentCommand.createSetSelection(sel));
// Select multiple nodes
const multiSel = Selection.create(doc, node1, node2, node3);
doc.executeCommand(DocumentCommand.createSetSelection(multiSel));
The command loop
const sel = doc.selection;
const compound = CompoundCommandBuilder.create();
for (const node of sel.nodes) {
const nodeSel = Selection.create(doc, node);
compound.addCommand( DocumentCommand.createXxx(nodeSel, ...) );
}
doc.executeCommand(compound.createCommand());
Colours
const red = RGBA8(220, 50, 50, 255); // r, g, b, a — all 0-255
const clear = RGBA8(0, 0, 0, 0);
const fill = FillDescriptor.createSolid(red);
const noFill = FillDescriptor.createNone();
Apply fill / stroke / opacity
// Via DocumentCommand — selection first, then fill
doc.executeCommand(DocumentCommand.createSetBrushFill(sel, fill)); // interior
doc.executeCommand(DocumentCommand.createSetPenFill(sel, fill)); // stroke
// Via doc convenience methods — accept Colour, SolidFill, or FillDescriptor
doc.setBrushFillDescriptor(colour, sel);
doc.setPenFillDescriptor(fill, sel);
// Opacity and blend mode
doc.setOpacity(0.5, sel); // layer opacity 0.0–1.0
doc.setBrushFillOpacity(0.7, sel); // fill opacity
doc.setLineFillOpacity(0.5, sel); // stroke opacity
// Line style shorthands
doc.setLineWeight(4, sel); // pixels
doc.setLineWeightPts(2, sel); // points
doc.setLineCap(cap, sel);
doc.setLineJoin(join, sel);
// Common doc convenience methods
doc.undo(); doc.redo();
doc.selectAll();
doc.deleteSelection(sel);
doc.showSelection(sel); doc.hideSelection(sel);
doc.lockSelection(sel); doc.unlockSelection(sel);
doc.convertToCurves(sel);
doc.rasteriseObjects(sel);
doc.flatten(); doc.mergeVisible();
Text replacement
// end - 1 excludes the hidden story terminator
doc.executeCommand(DocumentCommand.createSetCurrentSpread(doc.currentSpread));
const si = node.storyInterface;
const range = si.storyRange;
const textSel = TextSelection.create([{ begin: range.begin, end: range.end - 1 }]);
const selection = Selection.create(doc, node); // NOT Selection.createEmpty()
selection.addSubSelectionForNode(node, textSel);
doc.executeCommand(DocumentCommand.createSetText(selection, 'New text'));
Dialog
const dlg = Dialog.create('My Tool');
const col = dlg.addColumn();
const grp = col.addGroup('Options');
// Attach controls to dlg for clean access in helper functions
dlg.enable = grp.addCheckBox('Enable', false);
dlg.format = grp.addComboBox('Format', ['PNG', 'PDF'], 0);
dlg.colour = grp.addColourPicker('Colour');
dlg.size = grp.addUnitValueEditor('Size', UnitType.Pixel, UnitType.Millimetre, 100); // store px, display mm
dlg.count = grp.addUnitValueEditor('Count', UnitType.Number, UnitType.Number, 6, 1, 100); // unitless integer
dlg.anchor = grp.addSpatialAnchor('Anchor', SpatialAnchor.None); // spatial anchor picker
// Reactive — per-control (no argument)
dlg.enable.onValueChangedHandler = () => { dlg.size.isEnabled = dlg.enable.value; };
// Reactive — dialog-level (receives changed control)
dlg.onControlValueChangedHandler = (ctrl) => { /* ctrl === dlg.enable etc. */ };
// Simple run
const result = dlg.runModal();
if (result == DialogResult.Ok) { // note: Ok not OK (title-case)
console.log(dlg.enable.value, dlg.format.selectedIndex, dlg.colour.value);
}
// Live preview loop
dlg.onControlValueChangedHandler = () => doc.executeCommand(cmd, true); // true = preview
while (dlg.runModal() == DialogResult.Ok) {
if (!valid) { alert('Fix this.'); continue; }
doc.executeCommand(cmd); break;
}
doc.clearPreviews(); // ALWAYS — even on cancel
Export
const desktop = app.userDesktopPath;
const opts = FileExportOptions.createWithPresetName('PNG');
const area = FileExportArea.createForWholeDocument();
const records = doc.export(`${desktop}/output.png`, opts, area, null);
console.log(records.all[0]?.isSuccess ? 'OK' : 'FAIL');
Layer effects (shadow example)
// sel must be Selection.create(doc, node) — not doc.selection
// Effect commands must be executed individually — CompoundCommandBuilder is a silent no-op for effects
const idx = 0;
doc.executeCommand(DocumentCommand.createSetOuterShadowLayerEffectColour(sel, RGBA8(0,0,0,180), idx, true));
doc.executeCommand(DocumentCommand.createSetOuterShadowLayerEffectOffsetAngle(sel, 6, 315, idx, true));
doc.executeCommand(DocumentCommand.createSetOuterShadowLayerEffectRadius(sel, idx, 8, true));
doc.executeCommand(DocumentCommand.createSetOuterShadowLayerEffectOpacity(sel, 0.7, idx, true)); // opacity first, then index
// true = enableIfDisabled — adds the effect if not already present
Outline with gradient/pattern fill
// Outline accepts a FillDescriptor (not just solid colour)
const { Gradient } = require('/colours.js');
const { GradientFill, FillDescriptor } = require('/fills.js');
const gradient = Gradient.create([
{ colour: RGBA8(255, 0, 0, 255), position: 0 },
{ colour: RGBA8(0, 0, 255, 255), position: 1 },
]);
const fill = FillDescriptor.create(GradientFill.create(gradient, 0), true);
doc.executeCommand(DocumentCommand.createSetOutlineLayerEffectFill(sel, fill, 0, true));
Async pipeline
async function main() {
const docP = await DocumentPromises.getCurrent();
if (!docP) { console.log('No doc.'); return; }
try {
await docP.executeCommand(someCmd);
// mustSaveAs = true for new documents — use saveAs(path) instead of save()
if (docP.document.mustSaveAs) {
const { app } = require('/application.js');
await docP.saveAs(app.userDesktopPath + '/output.af');
} else {
await docP.save();
}
} catch (err) {
console.log('Error:', err);
}
}
main(); // always invoke the async entry point
Additional APIs
Pixel read/write
const { PixelReaderWriterRGBA8 } = require('/pixelaccessor.js');
const bitmap = node.rasterInterface.createCompatibleBitmap(true);
const rw = PixelReaderWriterRGBA8.create(bitmap);
const px = rw.readPixel(x, y); // { r, g, b, alpha } — NOT { r, g, b, a }
rw.writePixel(x, y, { r, g, b, alpha }); // key is 'alpha' on write too
// // using 'a:' silently writes 255 — no error!
doc.executeCommand(DocumentCommand.createReplaceBitmap(sel, bitmap));
Font enumeration
const { Font, FontFamily } = require('/fonts.js');
const families = FontFamily.all; // all installed families
const fonts = Font.all; // all individual fonts
// Get a specific font via FontFamily
const arial = FontFamily.all.find(f => f.name === 'Arial');
const f = arial?.fontCount > 0 ? arial.getFont(0) : null;
if (f) console.log(f.isSerif, f.isMonospaced, f.postscriptName);
Custom node tags
const { PredefinedTagKey } = require('/taginterface.js');
// Write a tag (persists in file)
DocumentCommand.createSetTagValueForKey(sel, 'my-key', 'value');
DocumentCommand.createSetTagValueForPredefinedKey(sel, PredefinedTagKey.ScriptLabel, 'label');
// Read tags back via tagInterface
if (node.tagInterface) {
console.log(node.tagInterface.hasKey('my-key'));
console.log(node.tagInterface.getValueForKey('my-key'));
}
Opacity commands
DocumentCommand.createSetOpacity(sel, 0.5) // layer opacity
DocumentCommand.createSetBrushFillOpacity(sel, 0.8) // fill opacity
DocumentCommand.createSetLineFillOpacity(sel, 0.6) // stroke opacity
ContainerNode (named layer group)
const { ContainerNodeDefinition } = require('/nodes.js');
const { AddChildNodesCommandBuilder } = require('/commands.js');
const def = ContainerNodeDefinition.create('My Group'); // named container
const builder = AddChildNodesCommandBuilder.create();
builder.addContainerNode(def);
doc.executeCommand(builder.createCommand(true));
doc.addNode(def); // shorthand for single-node add (no builder needed)
Node convenience methods
node.userDescription = 'Renamed'; // set layer name directly
node.lock(); // make non-editable
node.unlock();
const copy = node.duplicate(); // returns new node (lands on top)
node.delete(); // remove node
if (node.isColouredLogicalNode) {
console.log('Colour label:', node.layerColour); // read-only
}
Application settings
const { app } = require('/application.js');
app.settings.undoLimit // read-only
app.settings.loadPSDWithEditableText // read/write
app.settings.loadPSDWithEditableText = true
Document save and close
const { PackageResourcesPolicy } = require('/document.js');
doc.save(); // save in-place
doc.saveAs('/path/to/file.af');
doc.saveAsPackage('/path/pkg', PackageResourcesPolicy.CopyLinks);
// doc.close(); // close (no save prompt — save first; handle invalid after)
Macro commands
DocumentCommand.createStartRecordingMacro()
DocumentCommand.createStopRecordingMacro()
DocumentCommand.createReplayMacro()
DocumentCommand.createImportMacro(desktop + '/macro.afmacro')
Raster selection commands
DocumentCommand.createRasterSelectAll()
DocumentCommand.createRasterDeselect()
DocumentCommand.createRasterInvertSelection()
// Renamed from rasterSelectPolygon
DocumentCommand.createSetRasterSelectionFromPolygon(polygon, operation, antialias, feather)
// Selection from object bounds
DocumentCommand.createSetRasterSelectionFromObject(sel, operation, antialias, feather)
// Refine existing raster selection
DocumentCommand.createGrowShrinkRasterSelection(10, false) // grow by 10px (circular kernel)
DocumentCommand.createFeatherRasterSelection(5) // soften edge by 5px
DocumentCommand.createSmoothRasterSelection(3) // smooth boundary by 3px
const { RasterSelectionOutlineAlignment } = require('/commands.js');
DocumentCommand.createOutlineRasterSelection(4,
RasterSelectionOutlineAlignment.Centre, false) // outline at radius 4px
// Doc convenience methods — same signatures without create*
doc.growShrinkRasterSelection(8, true); // grow, circular kernel
doc.featherRasterSelection(5);
doc.smoothRasterSelection(3);
doc.outlineRasterSelection(4,
RasterSelectionOutlineAlignment.Inside, false);
Drawing scale
const { DrawingScale } = require('/drawingscale.js');
const scale = DrawingScale.createFromIntegers(1, 100, true); // 1:100
createSetEditable — argument order
// Selection is first argument, editable flag is second
DocumentCommand.createSetEditable(selection, false) // lock
DocumentCommand.createSetEditable(selection, true) // unlock
Document convenience shorthands
// Text operations
doc.setText('Hello', sel); // replace text in selection
doc.formatText(delta, sel); // apply formatting to selection
doc.insertGlyph(0x2665, sel); // insert ♥ glyph
// Undo/redo
doc.undo();
doc.redo();
// Stroke enhancements
doc.setDashPhase(0, sel); // dash phase
doc.setBalancedDashes(true, sel); // balance dashes at corners
// Dash pattern variants
doc.setDashPattern([8, 4], sel); // regular dash
doc.setDashPattern([8, 4, 2, 4], sel); // dash-dot-dot
Document line style shorthands
// These doc.setXxx() shorthands are simpler than building commands manually
doc.setLineWeightPts(2, sel); // set weight in points (auto-converts to px)
doc.setLineCap(2, sel); // 0=Butt, 1=Square, 2=Round
doc.setLineJoin(2, sel); // 0/1=Miter, 2=Round, 3=Bevel
doc.setLineType(LineType.Dash, sel); // set line type
doc.setDashPattern([8, 4], sel); // set dash pattern
doc.setStrokeAlignment(StrokeAlignment.Centre, sel); // stroke alignment
StoryDelta convenience factories
const { StoryDelta } = require('/storydelta.js');
const { FontWeight } = require('/fonts.js');
// Simple bold + italic
const bold = StoryDelta.createWeight(FontWeight.Bold);
const italic = StoryDelta.createItalic(true);
const combined = StoryDelta.createComposite([bold, italic]);
doc.formatText(combined); // applies to current selection — no Selection object needed
Geometry utilities
const { unionRects, TransformBuilder, Polygon, Curve, CubicBezier, Point } = require('/geometry.js');
const combined = unionRects(rect1, rect2); // merge two bounding boxes
// Polygon convenience factories
const tri = Polygon.createTriangle(Point.zero, new Point(100, 0), new Point(50, 80));
const dia = Polygon.createDiamond(new Point(0, 0), new Point(100, 100));
const loz = Polygon.createLozenge(new Point(0, 50), new Point(100, 50), 20);
// Precision unit circle (for measuring/fitting)
const circle = Curve.createPrecisionUnitCircle();
// Transform properties
const tx = node.baseToSpreadTransform;
console.log(tx.xAxis, tx.yAxis, tx.origin);
const inv = tx.inverted;
// Chainable transform methods
const moved = Transform.translationMatrix(100, 0); // translate
const scaled = Transform.scaleMatrix(2, 2, point); // scale about point
const added = moved.translate(50, 0).scale(1.5, 1.5, origin); // chainable
// around() replaces about()
const rot = Transform.rotationMatrix(45, Transform.Degrees); // 45deg about origin
const rot2 = Transform.rotationMatrix(45, Transform.Degrees, center); // about center
// Static multiply, decompose
const combined2 = Transform.multiply(txA, txB); // combine transforms
const [sc, tr, rot3] = Transform.decompose(combined2); // decompose
// TransformBuilder fluent builder
const builder = TransformBuilder.create();
const built = builder
.setXAxis(1, 0)
.setYAxis(0, 1)
.setOrigin(100, 100)
.build();
// CubicBezier prototype methods
const bez = new CubicBezier();
const pt = bez.evaluate(0.5); // point at t=0.5
const { left, right } = bez.split(0.5); // split into two beziers
const tangent = bez.getTangent(0.5); // tangent vector
const normal = bez.getNormal(0.5); // normal vector
const curv = bez.getCurvature(0.5); // curvature value
const closest = bez.getClosestPoint(pt); // nearest point on curve
const len = bez.length; // total arc length
HalftoneFilter
const { HalftoneFilter } = require('/layereffects.js');
// Direct apply (no CompoundCommandBuilder — silent no-op for layer effects)
doc.executeCommand(DocumentCommand.createSetHalftoneParameters(
sel, HalftoneFilter.Screen, HalftoneFilter.Sine, 0.5, 45, 0.1
));
// Or use live HalftoneFilterRasterNode
const { HalftoneFilterRasterNodeDefinition, HalftoneScreenType, HalftoneDotType } = require('/nodes.js');
const def = HalftoneFilterRasterNodeDefinition.createDefault();
DialogButton and DialogColumnStack
// Button — use addButton() on a group
const btn = grp.addButton('Click Me');
btn.alignment = HorizontalAlignment.Left;
btn.onClickHandler = () => { console.log('Clicked!'); };
// Column stack layout
const stack = grp.addColumnStack();
const leftCol = stack.addColumn();
const rightCol = stack.addColumn();
// Detect if dialog is running modal (property on Dialog instance)
if (dlg.isRunningModal) { console.log('Dialog is modal.'); }
// Toggle item visibility (method on Dialog instance)
dlg.setItemsVisibility([label.itemID], []); // show label
dlg.setItemsVisibility([], [label.itemID]); // hide label
// Fill/stroke editor
const { DialogFillEditor, DialogStrokeEditor } = require('/dialog.js');
const fillEditor = DialogFillEditor.create();
const strokeEditor = DialogStrokeEditor.create();
// Font picker
dlg.font = grp.addFontPicker('Font'); // DialogFontPicker — .font, .fontFamily, .fontCollection
// Find a control by ID (returns typed DialogControl or null)
const ctrl = dlg.findControl('my-control-id');
if (ctrl) ctrl.isEnabled = false;
Fills — selection first
const desc = FillDescriptor.create(SolidFill, { r: 255, g: 0, b: 0, alpha: 255 });
DocumentCommand.createSetBrushFill(sel, desc); // selection first, then fill
DocumentCommand.createSetPenFill(sel, desc);
// Hatch fill
const hatchCmd = DocumentCommand.createSetHatchFillAttributes(sel, /* builder */);
TableTextNode
const { TableTextNodeDefinition } = require('/nodes.js');
const def = TableTextNodeDefinition.create({ x: 0, y: 0, width: 300, height: 200 }, { width: 3, height: 3 });
doc.addNode(def);
TableSubSelection — read table cell/edge state
const { TableSubSelection } = require('/selections.js');
// After selecting a TableTextNode, check for table sub-selection
const item = doc.selection.items[0];
const sub = item.getSubSelectionOfType(SubSelectionType.Table);
if (sub) {
const ts = TableSubSelection.fromSubSelection(sub);
console.log(ts.anchor, ts.caret, ts.isRectangle);
ts.enumerateCells((row, col, rect) => { /* ... */ });
}
NodeChildren.all — recursive descendants
// node.children.all returns ALL descendants (not just direct children)
for (const child of node.children.all) {
if (child.isRasterNode) console.log('Found raster:', child.userDescription);
}
GlyphAtts / ParagraphAtts
const { GlyphAtts, GlyphAttStringType, GlyphAttDoubleType } = require('/glyphatts.js');
const { ParagraphAtts } = require('/paragraphatts.js');
const ga = GlyphAtts.create();
ga.setStringValue(GlyphAttStringType.StyleName, 'Heading');
ga.setDoubleValue(GlyphAttDoubleType.Height, 24);
const pa = ParagraphAtts.create();
pa.isPreventWidows = true;
Attribute runs on Story
for (const run of story.glyphAttRuns) {
console.log(run.begin, run.end, run.glyphAtts);
}
// Also: story.paragraphAttRuns — yields {paragraphAtts, begin, end}
// Also: story.attRuns — yields {glyphAtts, paragraphAtts, begin, end}
Fluent node-definition builders
const def = PolyCurveNodeDefinition.createDefault()
.setUserDescription('My Shape')
.setTransform(xf);
Colour → FillDescriptor auto-convert
// Fill/line methods accept Colour directly
def.addBrushFillDescriptor(RGBA8(200, 80, 40, 255));
node.spreadBaseBox
const box = node.spreadBaseBox; // equivalent to getSpreadBaseBox(true)
Spread / artboard document properties
const { ArtboardDocumentProperties } = require('/documentproperties.js');
const props = ArtboardDocumentProperties.create().setUseMargin(true).setMargin(margin);
doc.setArtboardDocumentProperties(artboard, props);
doc.setArtboardSizeWithAnchor(artboard, 400, 300, SpatialAnchor.Centre);
Boolean operations & shape division
// Combine/divide overlapping vector nodes (needs 2+ selected)
doc.boolOpUnion(); // or DocumentCommand.createBoolOpUnion(doc.selection)
doc.boolOpSubtract(); // top shape cuts the shape(s) below
doc.boolOpIntersect(); // keep only the overlap
doc.boolOpXor(); // keep everything except the overlap
doc.divideShapes(); // split into pieces along intersections
Text frame threading
// Thread text from one frame into another (no doc convenience method)
doc.executeCommand(DocumentCommand.createLinkTextFrame(srcFrame, destFrame));
doc.executeCommand(DocumentCommand.createUnlinkTextFrame(srcFrame));
// srcFrame.textFrameInterface.isMultiFrameTextFlow → true when threaded
Raster auto-fixes & fill
doc.rasterAutoLevels(); // one-click tonal correction on selection
doc.rasterAutoContrast();
doc.rasterAutoColours();
doc.rasterAutoWhiteBalance();
doc.rasterFill(); // fill raster selection with current colour
System / logging / config
OS.platform; // "Darwin"
Environment.permissions; // { fileSystem, network, genAI }
Environment.fileSystemRoots; // ["/Users/you/Desktop"]
const log = LogFile.create(`${app.userDesktopPath}/run.log`, LogLevel.Info, true);
log.start(); log.flush(); log.stop();
const cfg = ConfigurationItem.createFromJsonString('{"width": 1200}');
cfg.value.width; // 1200
Per-node export config, glyphs, units & hatch
// Per-node export settings — config must already exist (set once via UI Export persona)
const { ExportScale, ExportSize } = require('/exportconfig.js');
const cfg = node.exportableInterface.exportConfig;
cfg.formats[0].appendSize(ExportSize.createWithExportScale(ExportScale.createWithMultiplier(2)));
doc.executeCommand(DocumentCommand.createSetExportConfig(sel, cfg));
// Glyphs — typed character model (code point, not a string)
const { CharGlyph } = require('/glyphs.js');
const g = CharGlyph.create(0x2665);
// story.glyphs / story.charGlyphs / story.hardBreakGlyphs — pre-filtered collections
// DPI-based unit conversion
const pxPerPt = doc.unitValueConverter.getConversionFactor(UnitType.Point, UnitType.Pixel);
// Hatch pattern construction
const { HatchPattern, HatchLine } = require('/hatch.js');
const pat = HatchPattern.createEmpty();
pat.appendHatchLine(HatchLine.create({ x: 0, y: 0 }, Math.PI / 4, 8, [4, 2]));
Critical rules
| Rule | Why |
|---|---|
require('/module.js') not ./module | SDK module registry, not file system |
doc.title not doc.name | .name is undefined |
RGBA8() not Colour.fromRGBA() | .fromRGBA doesn't exist |
doc.executeCommand(cmd, true) for preview, doc.clearPreviews() after loop | Without clearPreviews(), cancelled previews stay on canvas permanently |
| Previews don't stack — rebuild in one command each update | SDK replaces previous preview; layering commands doesn't accumulate |
result == DialogResult.Ok | Use title-case Ok not OK; loose equality == works correctly |
range.end - 1 for text selections | Excludes the hidden story terminator |
Selection.create(doc, node) for text | Selection.createEmpty() causes silent failure |
Read doc.selection BEFORE setCurrentSpread | Spread change clears selection |
| Compound commands for multi-step changes | One undo step, not N separate entries |
| Desktop + granted folders for file access | Other paths → PERMISSION_DENIED |
No toLocaleString() | Not supported — use plain number or String(n) |
Check String(ri.format) === 'RGBA8' before PixelReaderWriterRGBA8.create() | Format mismatch throws INVALID_ARGS — format is an enum object, use String() to compare |
bitmap.format returns a plain integer not a name | Use RasterFormat.keys/values to look up — see lesson 15 loading example |
res.response.statusCode == 200 not res.statusCode | statusCode is on res.response, not res directly; body is res.response.content |
create*Filter not createSet*FilterParameters to apply a new filter | createSet* is a silent no-op if no filter exists yet |
No CompoundCommandBuilder for effect commands | Silent no-op — execute each effect command individually |
setTimeout(delay, callback) not setTimeout(callback, delay) | SDK order is opposite to Node/browser |
main() call at bottom of async scripts | Async functions don't auto-run |
Transform.around() — use around(), not about() | about() does not exist |
createSetRasterSelectionFromPolygon() for polygon selections | Prefer this over rasterSelectPolygon() |
require('/vectorbrush') fails — that module does not exist | Use /pathbrush for vector brushes, /rasterbrush for pixel brushes |
ExportConfig has no constructor | Obtain via node.exportableInterface.exportConfig — set once via the UI Export persona |
createSetEditable(sel, bool) — selection first | Selection is the first argument |
createSetBrushFill(sel, fill) — selection first | Selection is the first argument, then the fill |
dialog.setIsEnabledBy*() / setIsDisabledBy*() are deprecated no-ops | Drive dependent state from value-change handlers; use setItemsVisibility() instead |
TableTextNodeDefinition.create(rect, {width, height}) not create(rect, width, height) | Grid dimensions are an object |
formatText across cell separators only partially applies | Format each cell individually in a loop |
| Dialog values are read-only after build phase | Must set values before runModal() |
setCurrentSpread clears selection | Read doc.selection first |
node.spreadBaseBox equivalent to getSpreadBaseBox(true) | Convenience property, defaults includeClips to true |
RasterSelectionOutlineAlignment — Centre, Inside, Outside | Required for createOutlineRasterSelection |
Fill/line methods on definitions accept Colour directly | A solid fill is created automatically |
| Node-definition setters are chainable | .setUserDescription(), .setTransform(), etc. return this |
DialogControl.setIsEnabled() works correctly | Updates isEnabled properly |
| Handlers fire on user interaction only | Programmatic value changes do not trigger onValueChangedHandler |
| Geometry structs have proper wrapper types | Rectangle, Point, Vector, etc. are documented types, not anonymous objects |