Forge
Edit images with pro tools that feel simple.
Try it: open a photo and fix it up with pro tools.
Automation
FORGE โ agent-driving prompt (API v1 ยท verified against the live build)
Forge is a web design tool at https://apps.29court.com/forge: Photoshop-style raster painting + Figma-style vector, auto layout, components/variants, and styles in one layer tree. Drive it from any agent context by opening the URL and calling the in-page API: window.__forge.
Doc: 1920x1080, origin top-left, all coordinates in document pixels. Export renders the doc page rect. Measurement is stroke-inclusive (a 2px stroke adds 2px to w/h). Layer order is Figma order: last child renders on top.
API โ window.__forge (every function below is real and verified):
state() -> {tool,zoom,layers,selection,kinds}
layers() -> [{id,name,kind}] โ every layer incl. nested; kind is raster|vector|text|frame
selectOnly(id) โ select one layer; select(ids) -> [ids] โ select one id or an array
setTool(name) โ move|brush|eraser|select|rect|ellipse|line|pen|text|frame|hand|zoom
undo() / redo()
moveLayerTo(srcId,targetId) โ reorder; pass a FRAME id as target to nest into it (world transform preserved)
createLayer(kind,opts) -> id โ kind: raster|vector|text|frame|adjustment; opts {name,x,y,w,h,fill,text,adjType}
deleteLayer(id) -> bool
setProps(id,props) -> bool โ {name,fill,stroke,strokeWidth,opacity,x,y,w,h,fontSize,color,text,visible,locked,blendMode}
paint(x1,y1,x2,y2,color) -> layerId โ brush stroke in doc coords
addRect(x,y,w,h,fill) -> layerId โ vector rectangle
addEllipse(x,y,w,h,fill) -> layerId โ vector ellipse (bezier)
addLine(x1,y1,x2,y2,stroke) -> layerId โ vector line, stroke-only
addText(str,x,y) -> layerId โ text layer
addFrame(w,h) -> layerId โ frame, centered in viewport (pin with setProps x/y)
makeComponent() -> componentId โ turns the SELECTED frame into a component
placeInstance() -> instanceId โ instance of the selected (or last) component
setOverride(instId,childId,props) -> bool โ childId is the MASTER's child id; overrides are per-variant
alignLayers(ids,mode) -> bool โ mode l|c|r|t|m|b; needs 2+ unlocked layers
distributeLayers(ids,horiz) -> bool โ needs 3+ unlocked layers
snapshot() -> JSON-safe deep copy of the doc (raster canvases become {__canvas,w,h,dataURL} โ can be large)
exportDataURL() -> PNG data URL of the full document (note: skips global adjustments; the UI Export button applies them)
doc() โ live document object (advanced: full layer tree, override maps)
hitAt(clientX,clientY) -> {layer,kind,childId,instFrame}|null
refresh() โ re-render panels
Layout:
setLayout(frameId,{mode:'none'|'vertical'|'horizontal',gap,padding,align:'start'|'center'|'end'|'stretch',justify:'start'|'center'|'end'|'between',hugW,hugH}) -> bool
setLayoutChild(layerId,{fill,align}) -> bool โ fill: take remaining main-axis space; align overrides frame align
relayout(frameId) -> bool
childCenters(frameId) -> [{id,name,kind,x,y,w,h}] โ doc coords, after reflow
frameSize(frameId) -> {w,h}
Variants:
addVariant(masterId?) -> newMasterId โ clones the master as a new variant (auto-creates a State property)
addVariantProperty(name,masterId?)
setInstanceVariant(instId,props) -> masterId โ e.g. {State:'Variant 2'}
Styles:
createStyle('color'|'text'|'effect',layerId?) -> styleId โ captures the layer's current value
applyStyle(layerId,kind,styleId) -> bool
editStyle(id,patch) -> bool โ {value} for color; text props for text; propagates to every linked layer
deleteStyle(id) -> bool
styles() -> {colors:[{id,name,value}],texts:[...],effects:[...]}
Example tasks:
1. Auto-layout card: f=addFrame(900,700); setProps(f,{x:100,y:100}); setLayout(f,{mode:'vertical',gap:16,padding:24,align:'stretch'}); r1=addRect(0,0,200,80,"#e5484d"); r2=addRect(0,0,200,80,"#2f9e44"); moveLayerTo(r1,f); moveLayerTo(r2,f); childCenters(f); exportDataURL().
2. Button component with variants + styles: f=addFrame(400,200); r=addRect(60,60,280,80,"#409eff"); t=addText("Click me",90,140); moveLayerTo(r,f); moveLayerTo(t,f); selectOnly(f); makeComponent(); v2=addVariant(f); setProps(childCenters(v2)[0].id,{fill:"#e5484d"}); selectOnly(f); inst=placeInstance(); setInstanceVariant(inst,{State:'Variant 2'}); st=createStyle('color',r); editStyle(st,{value:"#00c853"}); exportDataURL().
3. Align + inspect: a=addRect(300,500,60,60,"#111"); b=addRect(450,520,60,60,"#222"); c=addRect(700,540,60,60,"#333"); alignLayers([a,b,c],'t'); distributeLayers([a,b,c],true); snapshot(); undo().
Gotchas: addFrame centers in the viewport โ pin with setProps(id,{x,y}). moveLayerTo takes layer IDS, not indexes. setOverride's childId comes from childCenters(masterId), not from the instance. Switching an instance's variant restores that variant's own overrides. createStyle captures current props โ set the fill BEFORE creating the style. All mutating calls push history, so undo() always bails you out.
Use with AI
Forge: ask any AI about this app (copy into any chat) You're helping me use Forge at https://apps.29court.com/forge. Edit images with pro tools that feel simple.. It exposes the window.__forge agent API (full prompt in the Automation section). Help me: understand what it can do, get a task done in it step by step, and troubleshoot when something looks wrong. If it exposes an agent API or AI actions, show me how to use them.
AI actions: None declared
Source
TBD (source not yet declared)
License
TBD (not yet declared)