Launch OpenGPEX

Tools & Interactions

A reference to the v2 toolset, interaction overlays, and how tools plug into the editor's command and signal system.


Sidebar Drawers

Drawer Description
Layers Z-order, visibility, opacity, blend modes, masks, merge, fragment marking, Merge Back
Craft Brush, Eraser, Marker, Mosaic panels — Brush and Eraser bind an AA switch two-way to hardness; Marker carries a plain AA toggle; Mosaic exposes S/M/L/XL brush+block size presets
Adjustment Color grading: basic sliders, curves, levels, channel mixer, color balance
Image Info Dimensions, DPI, ICC profile, bit depth, EXIF, export configuration
Animated Images Multi-frame GIF playback and export (Canvas 2D overlay beside the WebGPU stage)
AI Tools Client-side background removal, upscale, segmentation

Tool-Level Anti-Aliasing

AA is a per-tool property, not a global canvas switch. Eraser, Marker, and Brush each hold an independent toggle; on the selection side only tools whose strategy declares supportsAntiAlias (Ellipse (Path), Lasso, SAM) expose it — Rect and Wand are fixed hard-edge:

Tool AA ON AA OFF
Eraser / Restore Forced hardness = 100 Dropping hardness below 100 forces AA off
Brush Soft tips require AA (hardness < 100 forces AA on) Forces hardness = 100
Marker Panel toggle, default on SDF zero-crossing hard cutoff
Selections 1px sub-pixel coverage ramp Pixel-center binary test (same staircase line)

Text Tool

Text uses a box layout model with three modes:

Mode Created by Behavior
auto_width Single click (point text) Width hugs content; dynamic anchor compensation keeps the left/center/right edge visually fixed
auto_height Horizontal-only drag (total travel ≥ 5px, ΔY < 5px) Fixed width, natural word wrap
fixed Two-axis drag (ΔY ≥ 5px) Fixed box; after commit, wrapped lines falling below the box height are dropped at layout time (computeTextLayout with clipToBox) — no overflow badge. During live editing overflow stays visible (Figma-style)

Interaction highlights:

  • Double-click any text layer with the Move tool to edit in place — no tool switch needed
  • Escape commits and exits (Figma semantics: save, never destructively discard); Cmd/Ctrl+Enter commits explicitly; clicking outside commits via two-stage click arbitration
  • IME composition (isComposing || keyCode === 229) is fully guarded against commit/exit side effects
  • Cmd/Ctrl + drag during editing moves the layer as an independent immediately-committed undo transaction
  • Handles are hidden while editing; outside editing, the LayerOverlay draws screen-constant handles (10px handles, 24px rotation offset) regardless of zoom

Transform / Layer Overlay

Selection geometry is owned by a generic LayerOverlay with a usage-contributor registry: plugins declare intent via registerLayerOverlayUsage({ id, resolve }), and LayerOverlay aggregates gizmo types and hover suppression. Overlay hiding is driven by a camera-motion test (draft camera vs committed camera), not an interacting flag — so layer move/resize/rotate keeps handles visible while pan/zoom hides them.

Input Action
Drag corner / edge Scale (Shift = proportional)
Drag rotation handle Free rotate (Shift = snap 15°)
Arrow keys Nudge 1px (Shift = 10px)
Border band drag (text) 12% edge band pans the layer; interior click enters editing

Selection (Clip Mode)

Selections compile to polygon ring sets. The marching-ants display derives from the persisted polygon rings via a pure scanline staircase transform (polygonToStairedPathD) — AA ON and AA OFF share the exact same staircase; only the GPU coverage evaluation differs.

Shortcut Action
Tab / Shift+Tab Cycle tool (Rect → Ellipse (Path) → Lasso → Wand → SAM)
A×2 Toggle anti-alias
Enter / Escape Enter bakes the peel (exchange layer merged into host) and exits clip mode; Escape exits clip mode too — any dirty peel is merged first, never discarded
Cmd+J / Cmd+Shift+J Layer via copy / Layer via cut (fragment with hole mask)
Cmd+Shift+A Select from alpha

Wand output (flood → close → marching squares → simplify) is marked antiAliased: false and merges into the generic selection pipeline with no special-casing.


Smart Guides

v2 layers a "soft snapping" engine on top of world-space AABB alignment:

  • Velocity escape: an EMA-smoothed cursor speed (τ = 45ms) maps to a threshold factor; fast flicks bypass snapping entirely and clear guides
  • Sticky hysteresis: locked guides get a 0.55 diff discount — a 6px bite / ~11px release band prevents flickering between near-equivalent lines
  • Settings expose three sensitivity presets (Gentle 4px / Standard 6px / Strong 10px); Alt (or Shift) temporarily bypasses snapping per-event

Viewport Navigation

Input Action
Drag on canvas (pan mode) / middle drag Pan
Mouse scroll / Alt+scroll / trackpad pinch Zoom (cursor-centered)
Trackpad two-finger scroll / Shift+scroll / Ctrl+scroll (mouse) Pan
Space Toggle Pan/Clip mode
Ctrl+1 / Ctrl+2 Fit to viewport / 100%
Shift+H / Shift+V Flip viewport horizontally / vertically

The ViewPass sampler adapts automatically: nearest-neighbor at scale ≥ 1 (crisp pixel editing), bilinear below 1. Text and vector content additionally quantize zoom into density bands {1, 1.5, 2, 3, 4} so staying inside a band never triggers recomposite.


Command and Signal Flow

Tools do not write pixels directly. Interactions dispatch commands through the editor store (each undoable operation is a transaction, e.g. applyBitmapMaskBatch wraps erase + hole-plugging in a single undo unit), and fast per-frame feedback flows through volatile transients (e.g. fast.setTransient('smartguides', …) at 60Hz) that bypass the reducer entirely. Errors surface only through the EditorHUD toast channel.


Last updated: 2026-10-12