No description
  • TypeScript 90.7%
  • JavaScript 9.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Fraxured 5c44524f8a
Some checks failed
Verify and package desktop apps / desktop (Lumen-Linux, ubuntu-latest, package:linux) (push) Has been cancelled
Verify and package desktop apps / desktop (Lumen-Windows, windows-latest, package:win) (push) Has been cancelled
Remove duplicate desktop menu and prepare Lumen 0.20.1 release
2026-10-04 05:24:56 -04:00
.github/workflows Import Lumen 0.11.0 editor source 2026-10-02 10:48:55 -04:00
build Import Lumen 0.11.0 editor source 2026-10-02 10:48:55 -04:00
docs Remove duplicate desktop menu and prepare Lumen 0.20.1 release 2026-10-04 05:24:56 -04:00
electron Remove duplicate desktop menu and prepare Lumen 0.20.1 release 2026-10-04 05:24:56 -04:00
public feat: checkpoint verified editor batches through 0.18 2026-10-03 13:59:07 -04:00
scripts feat: complete batch 08 artwork, automation and output tools 2026-10-04 04:33:38 -04:00
src Remove duplicate desktop menu and prepare Lumen 0.20.1 release 2026-10-04 05:24:56 -04:00
tests Remove duplicate desktop menu and prepare Lumen 0.20.1 release 2026-10-04 05:24:56 -04:00
.gitattributes Import Lumen 0.11.0 editor source 2026-10-02 10:48:55 -04:00
.gitignore Import Lumen 0.11.0 editor source 2026-10-02 10:48:55 -04:00
AGENTS.md chore: archive stale artifacts and organize verification history 2026-10-04 04:47:21 -04:00
AUDIT.md fix: bound expanded smart sources and legacy filter work 2026-10-03 14:28:43 -04:00
DESIGN.md Import Lumen 0.11.0 editor source 2026-10-02 10:48:55 -04:00
index.html Harden file parsing and Electron security 2026-10-02 11:21:26 -04:00
package-lock.json Remove duplicate desktop menu and prepare Lumen 0.20.1 release 2026-10-04 05:24:56 -04:00
package.json Remove duplicate desktop menu and prepare Lumen 0.20.1 release 2026-10-04 05:24:56 -04:00
playwright.config.ts Import Lumen 0.11.0 editor source 2026-10-02 10:48:55 -04:00
README.md Remove duplicate desktop menu and prepare Lumen 0.20.1 release 2026-10-04 05:24:56 -04:00
tsconfig.json feat: checkpoint verified editor batches through 0.18 2026-10-03 13:59:07 -04:00
vite.config.ts feat: complete batch 08 artwork, automation and output tools 2026-10-04 04:33:38 -04:00
vitest.config.ts Import Lumen 0.11.0 editor source 2026-10-02 10:48:55 -04:00

Lumen

Lumen is a Windows and Linux image editor being built toward a Photoshop-like editing and export workflow. The goal is broad editing capability without a dependency on AI enhancement features. This repository contains the working foundation; it does not yet provide full Photoshop feature parity.

The editor runs locally in Electron, with a browser development mode. Editing uses Canvas 2D and local pixel processing. Native image decoding uses sharp in a separate codec process; on Linux export encoding runs in the main process, while other platforms encode in the codec process. Documents stay on your device.

Run and build

Use Node.js 22.12 or later and npm. The lockfile is shared across Windows and Linux.

npm ci
npm run desktop:dev

For a browser preview, use npm run dev and open http://127.0.0.1:5173. To run the production desktop app:

npm run build
npm run desktop

Build distributable installers on their target operating system:

npm run package:win
npm run package:linux

Windows packaging produces NSIS and portable executables; Linux packaging produces AppImage and Debian packages in release/. These development builds are unsigned. Linux desktop builds require the standard graphical libraries needed by Electron. A GitHub Actions matrix is configured to verify and package both platforms. The project homepage and release downloads are hosted on Forge.

Download the unsigned Windows x64 installer, portable EXE and Linux x64 AppImage from the Lumen 0.20.1 Forge release. SHA256SUMS records all three binary hashes. On Windows, run Lumen Setup 0.20.1.exe to install, or Lumen 0.20.1.exe for the portable build. On Linux:

chmod +x Lumen-0.20.1.AppImage
./Lumen-0.20.1.AppImage

If FUSE mounting is unavailable, run APPIMAGE_EXTRACT_AND_RUN=1 ./Lumen-0.20.1.AppImage. Version 0.20.1 removes the duplicate native menu row on Windows and Linux while retaining the editor menus and native shortcuts. Help provides Keyboard shortcuts and About Lumen. It includes the batch08 editing features and schema 15 lumen-production-v1 projects.

Release verification passed 1,699 unit tests, 42 affected Chromium cases, all 66 packaged Windows native workflows, and three linked-asset/directory/PDF capability workflows on each platform. Five focused shell workflows passed in the packaged Windows app and the actual AppImage. Both platforms produce the same ten compiled files; all 32 packaged application files match the tested inputs. NSIS and portable payloads were extracted and compared byte for byte with the unpacked Windows app. Linux execution used WSL Ubuntu 26.04, Xvfb and AppImage extract-and-run. NSIS installation, portable bootstrapper execution and normal FUSE mounting were not exercised. Earlier package verification is preserved in Verification history.

Current editing workflow

  • Multiple document tabs, undo/redo, document creation, drag-and-drop/open, and import as a layer.
  • Raster layers, editable text created in Lumen, visibility, locks, ordering, opacity, duplicate, merge down, and flatten.
  • Editable point text and paragraph boxes with character ranges, font size/color, bold/italic, tracking, underline/strikethrough, alignment, automatic/custom leading, paragraph spacing and vertical alignment. Four unmodified Noto Sans faces ship with the application for consistent rendering on both platforms.
  • Editable vector rectangles, rounded rectangles, ellipses, polygons, stars, lines, arrows and cubic pen paths. Polygon/star and line/arrow tools retain construction recipes editable through Configure shape; Convert to path deliberately removes the recipe. Fill/stroke colors, stroke width, caps, joins, fill rule, anchors and curve handles remain editable. A changed generic path edit materializes a recipe to a plain path in the same Undo step. Stroke path with brush paints a bounded pixel result while preserving the editable path. Existing legacy raster shapes retain their pixels.
  • Unite, subtract front paths, intersect and exclude selected eligible filled sibling paths. Results remain editable native paths; unsupported appearance, contour topology or precision ambiguity refuses atomically. These bounded set operations do not expand strokes or trace masked coverage.
  • Embedded smart objects with linked instances, editable source tabs, source replacement and independent copies. Each instance retains its own transform, fixed frame, mask, adjustments and styles; source edits publish a revision shared by linked instances.
  • Editable Smart Filter stacks on smart objects: Gaussian/Box/Surface blur, Unsharp mask, High pass, Median, Dust & Scratches, seeded uniform/Gaussian noise, Pixelate, Grayscale, Invert, Sepia, Posterize, Threshold, Motion blur, wrap/transparent Offset, RGB Minimum/Maximum, Find Edges, Emboss, Solarize, Color Halftone, Wave, Ripple, Twirl, Radial Blur, Lens Blur, Smart Sharpen, Reduce Noise, Diffuse, Displace, Pinch and Spherize. Reorder, duplicate, enable or delete filters; each has opacity and blending, and the stack has a shared painted mask with density and feather.
  • Nondestructive drop shadows, outer glows, inner shadows/glows, color, multi-stop gradient and Pattern overlays, Satin, Bevel & Emboss, and inside/center/outside strokes. Body fill opacity is separate from complete layer opacity, so effects can remain visible when the body is transparent. Styles work on leaves and isolated groups.
  • Nested layer groups with collapse/expand, pass-through or isolated blending, group opacity and masks, subtree duplication/deletion, grouping/ungrouping, and moving layers between groups while preserving their world transforms.
  • Clipping masks restrict a sibling chain to the nearest base layer below it. Base transparency and masks define the coverage; base opacity and blending apply to the combined chain.
  • Normal plus 15 Canvas blend modes: multiply, screen, overlay, darken, lighten, color dodge/burn, hard/soft light, difference, exclusion, hue, saturation, color, and luminosity.
  • Brush, Eraser and Clone with size, hardness, opacity, Flow, carried spacing, elliptical/textured tips, captured smoothing, seeded scatter and angle/roundness/size jitter, named presets and optional pen pressure for size/opacity; RGB Brush color jitter and RGB Brush/Clone blend modes; contiguous or global bucket fill, gradient and eyedropper.
  • Dodge and Burn brushes with exposure and shadows/midtones/highlights ranges; Sponge with saturate/desaturate modes. Retouch strokes support brush spacing, selections, RGB component targets and Undo. Dodge/Burn also edit mask and saved-alpha coverage.
  • Sampled Healing adapts nearby texture to the destination's surrounding lighting, with aligned/unaligned sampling, brush controls and selections. Choose current-layer, current-and-below or all-visible sampling. Preserve-alpha painting supports RGB components; repair-layer painting deposits corrections separately from the original photo.
  • Rectangular, elliptical, freehand/polygonal lasso and color-based selections, Grow/Shrink, Border, sampled Color Range and Quick Mask; replace/add/subtract/intersect combinations, inversion, feathering, selected-pixel copy/cut/paste, and selection-aware paint/fill/erase/drawing. Transform Selection moves, scales, rotates and skews selection coverage independently of image pixels.
  • Native OS image Clipboard copy/cut/paste, with captured delayed targets, write ordering and local-buffer fallback when no OS image is present.
  • Layer masks with white-to-reveal and black-to-hide painting, mask application/removal, and saved mask pixels.
  • Editable vector masks, one-line text on a path, stroke outlines, bounded path offsets and supported bundled-font text outlines. Artboards clip isolated root groups; slices export named document regions with transparent outside padding.
  • Layer Comps capture visibility, transforms and opacity/blending. Actions record supported numeric layer-property changes and replay explicit layer commands atomically. Desktop Directory Batch applies saved Actions to chosen files and writes a new ZIP.
  • Linked assets keep an embedded fallback and refresh through a native file grant; Relink renews access after reopening. Contact Sheets and Print Layout create editable page documents or PNG bundles; desktop Print Layout also writes raster-page PDFs at the chosen physical size.
  • Apply Image, Calculations, single-channel transforms and local spot-channel color/solidity with separate ink preview and raw grayscale plate export. Background Eraser matches captured RGB colors, and Clone supports source scale, rotation, flips and alignment. Color Lookup accepts bounded 3D .cube tables.
  • RGB component editing and saved alpha channels: brush, eraser, fill, gradient, cloning, copy/cut/paste, selections, Levels/Curves and color/spatial filters. Alpha channels use independent document-sized grayscale coverage and locks.
  • Move and multi-selected-layer Free Transform scale/rotation/skew handles, a draggable pivot, numeric transforms and horizontal/vertical flip; selected-layer alignment and distribution by centers or equal gaps; editable nonprinting document guides and optional snapping to guides, grid, canvas and layer bounds. Private axis-aligned Crop previews support free, ratio and exact-pixel framing with explicit Apply/Cancel. Image resizing, canvas resizing, zoom, pan and a canvas grid remain available.
  • Per-layer brightness, contrast, saturation, exposure, temperature, tint, highlights, shadows and gamma; grayscale, invert, sepia, blur and sharpen. Adjustment parameters remain editable in Lumen project files.
  • Editable Gradient Fill, Solid Color Fill and Pattern Fill layers; five gradient drawing modes, custom color/transparency stops and reversal.
  • Nondestructive Gradient Map, Vibrance, Color Balance, Channel Mixer, Posterize, Threshold, Black & White, Hue/Saturation, Selective Color and Photo Filter controls, with private previews and editable adjustment layers.
  • Editable RGB and individual-channel Levels and Curves on layers or adjustment nodes. Levels includes input/output endpoints, midtone gamma and histogram-based auto levels; Curves includes smooth/linear interpolation, draggable points, keyboard controls and presets. Both offer previews and retain their settings in the editable master.
  • Reusable adjustment layers apply those controls to the layers below them, with opacity, blending and painted masks. Isolated groups contain the adjustment scope; pass-through groups allow it to affect the outside backdrop. A clipped adjustment affects its clipping chain.
  • Named PNG/JPEG/WebP export presets, multi-size/format variant ZIPs, and selected layer/group PNG asset ZIPs with placement manifests. Presets stay outside project history; exports capture opening output and retain the editable master.
  • Keyboard shortcuts, native file dialogs and desktop menus, unsaved-close confirmation, and per-document IndexedDB crash recovery (see Crash recovery).

Open the included “Blue hour study” sample from the welcome screen to try the layer workflow.

Choose Brush (B), Eraser (E) or Clone stamp (S). Spacing is a percentage of the opening brush diameter and continues across pointer events. A stroke uses its maximum dab coverage at each pixel, so overlapping dabs keep the chosen opacity; a second stroke builds coverage. Brush settings, color, target, transform, effective selection and Clone source pixels are captured on pointerdown. Clone uses a fixed document-space source offset through transformed layers. Each changed stroke creates one Undo step. Escape or pointer cancellation restores its opening state; zero-opacity and unchanged strokes add no history.

Enable Size pressure or Opacity pressure to use a pen's pressure; Minimum sets the smallest diameter as a percentage. These options start disabled. Mouse painting uses full pressure. Supported pen eraser-end events erase without changing the selected tool. Coalesced pointer samples retain curved paths, and an unrelated finger or pointer cannot change or end the captured gesture. Flow controls buildup within a stroke: 100% retains maximum-dab coverage, lower Flow builds coverage across dabs, and 0% adds no pixels or history. Brush options include angle/roundness and distance smoothing; Brush presets… creates, updates, deletes, imports and exports local recipes outside document history. Open Edit → Brush settings… for texture scale/rotation, RGB mode, scatter, seeded angle/roundness/size jitter and hue/saturation/brightness jitter. Recipes and texture assets are captured at stroke start. Version 2 preset files accept version 1 migration. Tilt remains future work.

Double-click a layer name, press F2, or choose Layer → Rename layer to edit it inline. Enter commits one Undo step; Escape cancels. Names preserve Unicode and the opening layer/document target, including when blur selects another layer or tab. Renaming changes metadata only and is allowed on locked layers.

Choose Move (V), then click Free transform in the options bar, choose Edit → Free transform, or press Ctrl+T. Drag a corner or edge to scale selected layers; drag the round handle above the frame to rotate. One selected root uses its local axes; multiple topmost selected roots share a document-space frame and affine change. The opposite corner or edge stays fixed. Hold Shift for uniform scaling and aspect preservation, or for 15° rotation steps; hold Alt while scaling to keep the chosen pivot fixed. Drag the crosshair to move that pivot without adding history; choose Skew to use the tangential edge handles. Each changed drag creates one Undo step. Escape or pointer cancellation restores an active drag; Escape while the handles are idle hides them. If a menu is open, Escape closes it first. Returning exactly to the opening transform creates no Undo step and preserves Redo.

Choose the complete RGB view and the layer body. The layer and its ancestor groups must be unlocked. Handles transform selected raster, vector, text, smart-object or group roots; a selected descendant moves once with its selected ancestor. Editable geometry, source contents, masks and native body pixels remain available. Finish a Pen draft with Enter, or discard it with Escape, before requesting Free transform. An unfinished Pen draft is retained when Free transform is refused. Very small frames prioritize the southeast corner; zoom in or use the numeric dialog when handles overlap.

Edit → Transform layer and the Transform layer options button retain the numeric dialog for X/Y, horizontal/vertical scale, rotation, horizontal/vertical skew and flips. Focus a transform handle and press Enter to open that dialog. Its existing correction, cancellation and signed Flip controls remain available.

Layer opacity and X/Y, vector geometry/colors, layer effects and shared Smart Filter mask density/feather preview continuously and create one Undo step when a changed edit ends. Enter or leaving a number field commits; Escape cancels, and blank or unfinished number fields restore their opening value. A held adjustment key or pointer gesture and a color picker's preview stream each form one edit. Returning to the opening value keeps Redo. Selection gestures and commands have their own Undo steps, so undoing a selection leaves preceding paint intact; repeating the same selection keeps Redo.

Hold Ctrl (or Command) while scrolling over the canvas to zoom around the pointer; ordinary scrolling pans, and Hand (H) drags the workspace. Zoom uses the wheel distance and stays between 2% and 1600%; Fit centers the image. The fixed rulers follow document coordinates throughout the supported image size. A stroke or active path drag keeps its opening coordinates through a viewport resize, and view changes are blocked until the gesture ends. Zoom and pan create no Undo steps. Wheel zoom and view commands preserve idle Pen drafts; property edits retain their normal Enter/blur and canvas gesture settlement behavior. At 100%, one document pixel occupies one CSS pixel; the display keeps pixels crisp when zoom multiplied by device pixel ratio exceeds one. The underlying canvas and exports keep their native document dimensions. Alt and AltGraph keys do not trigger ordinary tools or color/brush shortcuts; Ctrl+Alt+G and Alt pointer sampling/handle/selection modifiers remain available.

Choose Dodge (O), then use Tool in the options bar to choose Dodge, Burn or Sponge. Shift+O cycles them; O returns to the last retouch tool. Size, hardness, opacity and spacing control the brush. Dodge/Burn exposure adjusts raw pixel brightness in the chosen tonal range; Sponge amount changes color saturation. Tonal ranges use the original RGB luminance, including when editing only selected components. Settings, target, transform and selection are captured when a stroke starts. A stroke uses its maximum brush coverage at each pixel, so overlapping dabs do not repeatedly darken or lighten that pixel; a subsequent stroke builds the effect. Each changed stroke creates one Undo step, and Escape or pointer cancellation restores it.

Retouch preserves transparency and untouched RGB components. Existing controls on raster layers stay editable; painting a text/vector body first rasterizes its appearance and Undo restores it. Smart-object bodies require rasterization or editing their source. Dodge/Burn can lighten or darken mask and saved-alpha coverage; Sponge requires an RGB pixel target. These are Lumen's bounded exponential exposure and luminance-based saturation tools; Photoshop Protect Tones, Vibrance and grayscale Sponge contrast are not implemented. Localized strokes keep bounded scratch tiles rather than copying the entire layer for each pointer event.

Choose Healing (J) with the shortcut, or choose Healing from Tool in the Clone stamp options. Sample chooses Current layer, Current & below or All visible. Alt-click clean texture, then paint over a blemish or scratch. Aligned keeps the source offset across strokes; turn it off to restart at the sampled point on each stroke. Reset source clears the sample. The source cross shows where texture is sampled. Size, hardness, opacity and spacing control the brush; 0% opacity changes no pixels and creates no Undo step. Source pixels, destination pixels, brush settings, transform and selection are captured at the start, including when the sampled and repaired areas overlap.

Choose New repair layer in the Healing options or Layer menu to keep the original photo intact. It adds a blank Normal root layer above the selected root and its complete clipping chain, selects it, and sets Current & below sampling with the active layer excluded. Paint → Repair layer deposits corrected RGB and transparency on a visible root raster layer with Normal blending, 100% opacity and no active mask, clipping, adjustments or effects. A layer with clipped followers also cannot receive repair coverage; New repair layer places the repair above the complete chain. You can transform that layer or adjust its appearance after painting. Returning to repair painting requires these neutral appearance settings again. Paint → Preserve alpha retains existing target transparency and supports selected RGB components, including with composite texture sampling.

Sampling options → Exclude active leaves opening repairs out of the sampled texture. Ignore adjustments skips owner adjustment nodes, including those in groups, while keeping ordinary leaf controls and the rendered contents of embedded smart objects. An ignored adjustment that anchors a clipping chain becomes an identity adjustment so its nonignored clipped siblings still use the backdrop alpha. Current layer retains native-body sampling and disables these composite flags. Composite sample anchors remain valid when changing the active layer in the same document; current-layer samples belong to their original layer. Locked source photos can be sampled without unlocking them; the repair target must be writable.

Healing keeps sampled texture while solving a spatial correction against the surrounding destination colors. Neighbor connections cross scratch-tile boundaries, so a stroke has one connected solution. A selection excludes colors outside its boundary and gates the final blend once. Leave some valid selected pixels around the painted area to anchor the correction; a region without a surrounding anchor is rejected. Changed strokes create one Undo step. Escape or pointer cancellation restores the opening document, including while the background calculation finishes; composition commands wait until it finishes. Uncommitted previews do not enter recovery.

Preserve-alpha painting keeps alpha and unselected components; repair painting adds coverage using source-over compositing. Sampled texture and destination lighting are independent: composite repair lighting comes from the opening visible stack through the target's insertion point, even when sampling all visible layers or ignoring adjustments. Higher layers remain outside that destination backdrop. Undefined transparent source/backdrop pixels are skipped. Ordinary raster controls remain editable in Preserve alpha mode. A text/vector body is staged as a raster appearance and installed only when pixels change; Undo restores it. Smart-object bodies require rasterization or editing their source to write pixels, but can supply sampled texture to a separate repair layer. Masks and saved alpha channels remain outside Healing's write scope. The harmonic correction is Lumen's implementation; Photoshop's exact Healing algorithm and diffusion controls are not claimed. Each repair is limited to 65,536 active native pixels, a 1024-pixel bounding side and 64 MiB of accounted patch/solver/regional-render buffers. Regional sampling retains filter support and complete transformed primitive coverage where cropped Canvas drawing would change edge pixels. A small brush can still require broad support from transformed artwork. Native point text retains its original viewport because font substitution can extend ink beyond saved bounds; native paragraph text retains its clipping box. Support exceeding the budget is rejected before regional allocation. A captured opening composition, document-sized selection snapshot and temporary feather/inversion construction buffers, existing history, UI rendering and worker/runtime overhead are separate costs. The regional renderer keeps original document and native filter/style sampling coordinates; the ordinary editing display still uses full-document rendering.

Choose Pen (P) and click to place anchors, or drag while placing an anchor to create cubic curve handles. Shift constrains the angle; Alt while dragging separates the incoming handle. Click the first anchor to close the path, press Enter to finish an open path, and press Escape to discard the draft. Undo/redo removes and restores draft anchors. A committed subpath needs at least two anchors; each vector layer can contain at most 4096 anchors across its subpaths.

Choose Direct select (A) to select a path and drag its anchors or handles. Shift constrains movement and Alt separates linked handles. Double-click a segment to insert an anchor. Handle-free line subdivision keeps native line commands; cubic subdivision retains its curve controls. Exact Undo/cancel restores the opening geometry and cache. Arbitrary affine Canvas antialiasing after subdivision is not guaranteed byte-identical. The vector properties panel changes fill, stroke, caps, joins and fill rule; it also opens/closes paths, adds subpaths, deletes anchors, and changes anchors between smooth and corner modes. Primitive shapes can be converted to paths, and a path can become a raster selection.

Saving, exporting, changing tools or switching documents finishes a valid Pen draft first; a draft with fewer than two anchors is discarded. If finishing fails because of a lock or size limit, the draft remains available and the pending action stops. Unfinished drafts are marked as unsaved, but are not included in recovery snapshots until committed. Finish a path before relying on recovery. Painting and other destructive pixel tools rasterize editable vectors; undo restores the vector geometry.

Image resize scales the root transforms across the document. Exact affine matrices preserve rotated and sheared layers without rasterizing their editable content. Reparenting similarly preserves world placement. Structural masks retain their original local pixel domain when document dimensions change.

Use the layer checkboxes, Ctrl-click, or Shift-click to select multiple layers, then choose Layer → Group layers. Grouping requires consecutive siblings and complete clipping chains to preserve stacking and blending. Ungrouping a plain pass-through folder retains editable children; shared opacity, masks, isolation or effects can require an explicitly labelled rasterization. Backdrop-dependent cases that cannot preserve their appearance are rejected with an actionable message. Undo restores the original folder and contents.

Choose File → Place embedded to place an image or Lumen project as a smart object, or Layer → Convert to smart object to embed an existing layer/group. Ordinary duplicate instances share their source. Edit smart object opens an editable source tab; Update source publishes its changes to linked instances, while Save source copy writes a separate project. Saving from a source tab updates and saves the owning composition. Reload source loads the latest revision, and a stale revision is rejected without discarding the draft. Replace smart object updates the linked contents while preserving instance frames and placement. New independent copy creates a separate source graph. These sources are embedded in the project. External linking is available through Place linked, Refresh, Relink and Embed; Photoshop smart-object editability remains unsupported.

Embedded sources containing blur or sharpen controls, including nested sources or adjustment nodes, currently render at their original source pixel resolution before mapping into the instance. This preserves the source viewport and filter kernel, but enlarging that instance can lose fine vector detail. Unfiltered vector sources that fit their source viewport render directly at the final output resolution. Overflowing unfiltered scenes use a once-clipped viewport sampled within the 8192-pixel/32-million-pixel limits; large enlargements that reach that sampling cap can also lose fine vector detail. The editable source geometry and controls remain in the .lumen master.

Choose Layer → Convert for Smart Filters for an ordinary layer, or use an existing smart object. Open Smart filters in Properties, choose a type and press Add. The dialog previews the composition; Cancel discards the draft and Apply creates one Undo step. The top row applies last, after the rows below it. Reopening a filter changes its parameters, opacity or blend mode. Stack visibility, individual visibility, ordering, duplication and deletion remain editable. Copy smart filters captures settings and mask; Paste gives another instance its own settings and mask, resampling mask coverage to its frame when needed. Linked instances share embedded contents while their filters remain independent.

Adding the first filter while a selection exists creates a shared mask from that selection. Add filter mask from selection creates or replaces it later. White reveals filtering and black restores the original mapped source; click the mask thumbnail to paint, fill, clone, use Dodge/Burn or correct its coverage with Levels/Curves. A separate layer mask still controls the complete layer. Density 0 reveals all filtering; feather softens the mask edge in frame pixels. Mask coverage follows the instance transform. Noise uses a saved seed, so opening, undoing or rendering another region retains the same pattern.

Filter radii and Pixelate cells use the smart object's fixed frame pixels before its world transform. Active stacks rasterize that frame, so enlarging filtered vector/text contents can lose fine detail; disabled or Normal identity stacks retain the direct unfiltered rendering path. Each stack supports 32 filters and the complete project/source graph 256. Local filter scratch is bounded and preflighted; a stack whose required support exceeds the available rendering budget is rejected atomically. Full-frame outputs, saved masks, history, UI and runtime memory are separate costs. .lumen projects retain editable stacks and read valid older schemas. PSD/SVG export bakes filtered smart scopes once and labels them; editable Photoshop Smart Filter interchange and Adobe's exact filter algorithms are not implemented.

Choose Text (T) and click the canvas to create text, or select a text layer and choose Layer → Edit text. Point text grows with its content; a paragraph box wraps and clips content to its width and height. Overset text stays in the editable master and reappears when the box is enlarged. Text defaults apply to characters without overrides. Choose Selected characters, select text, change the desired controls, and choose Apply style to selection to preserve other character settings. Ranges follow whole Unicode graphemes, including combining characters and emoji sequences. Clear character styles returns the text to its defaults.

The dialog previews the composition; Cancel discards the draft and Apply creates one undo step. Sizes and custom leading use local pixels; tracking uses thousandths of an em. Automatic leading uses 125% of the largest size on the incoming line. Unfiltered text renders at output resolution, including inside supported smart sources, so enlarging an export retains glyph outlines. Blur/sharpen on a text layer use local cache pixels. Painting text pixels rasterizes the layer; mask painting retains its editable text. Editing masked text preserves existing mask coverage at its logical position and reveals newly added regions.

Noto Sans provides the shipped Latin, Greek and Cyrillic glyphs. Other scripts, emoji and other selected families can use fonts available on the current computer; their appearance can differ on another machine. Native PSD/SVG eligibility checks the actual bundled glyph map rather than relying only on the family name. Font provenance and the SIL Open Font License are stored in src/assets/fonts/; the license also ships with the application and font-bearing SVG exports.

Converting a group with descendant vector, group, smart-object or adjustment-layer blur/sharpen controls is rejected because moving those document-pixel filters into a source can change their appearance. Rasterize the group first, or rasterize/merge its affected children before converting it. Ordinary raster-layer filters use local cache pixels and remain supported during conversion. Placing and editing existing filtered embedded sources remains supported.

Open Layer effects in the properties panel to enable and edit individual styles, their color/opacity and sizes. Layer fill opacity reduces the body while shadows, glow, color overlays and strokes use its original masked coverage; complete layer opacity affects the combined result. Masks apply before styles so effects can extend beyond the masked body. Pass-through folders and adjustment nodes do not support styles; set a folder to an isolated blend mode first. Shadow offsets are limited to ±1024 local pixels, and blur, spread and stroke widths to 256. Rasterizing a styled or smart layer bakes its appearance; Undo restores the editable content. Pattern Overlay, Satin and Bevel & Emboss have separate configuration dialogs from the Layer menu or effects panel. Bevel offers Inner, Outer, Emboss, Pillow and Stroke styles; Stroke relief needs an enabled nonzero Stroke. Use Layer → New Pattern Fill… or Edit Pattern Fill… for a native repeated RGBA tile, or import/capture an image tile; its frame, scale, rotation and offsets stay editable.

Choose Filter → Levels… (Ctrl+L) or Filter → Curves… (Ctrl+M) to edit the selected layer, or add a Levels adjustment layer or Curves adjustment layer from the Layer menu to correct its backdrop. Change between RGB, Red, Green and Blue without losing channel settings. Levels stretches the input range, changes midtone gamma, and maps the output endpoints; output endpoints can also be inverted. Auto levels clips 0.5% of visible histogram coverage at each end. Transparent pixels are excluded and partial alpha contributes its coverage. The RGB histogram averages the three channel distributions; Curves shows its input after existing Levels.

Click the Curves graph to add a point, drag it or use arrow keys to move it (Shift moves ten values), and press Delete to remove it. Curves supports 2–19 ordered points per channel with input/output values from 0 to 255. Smooth mode uses a natural cubic curve; Linear connects points directly. Values outside the endpoint inputs stay at their endpoint outputs. Preview changes compares against the current composition, and Cancel leaves it unchanged. Apply creates one undo step. Tone corrections preserve alpha and run after the scalar color controls, before blur/sharpen, masks and layer effects.

Choose Channels in the right sidebar to select Red, Green or Blue, or include multiple components with their checkboxes. Pixel edits affect those components on the active pixel layer while preserving its other components and transparency. A single component displays in grayscale; Show channels in color isolates its color. RGB returns to ordinary composite editing. The view is session state and never changes the image sent to exports. Choose RGB before moving a layer; text and vector creation return to the composite view. Destructive edits rasterize supported editable text/vectors; smart objects require rasterization or editing their source.

New channel creates black or white coverage. Saved alpha channels use document coordinates and their own locks, independently of layer locks. White selects and black hides; erasing or clearing paints black. Save selection captures effective feathering and inversion once, into a new channel or combined with an existing channel. Load selection replaces, adds, subtracts or intersects coverage; RGB component sources also multiply by composite transparency. Editing alpha coverage is independent of image opacity. Duplicate, rename, delete and painting participate in history. Crop, image resize, canvas size, rotation and flipping transform all saved channels, including locked channels, with the document.

When a component or alpha channel is selected, adjustments, Levels/Curves and filters apply to its pixels within the selection and create one undo step. Previews compare the draft against current pixels. These corrections are destructive; the ordinary RGB view retains editable layer adjustments. Alpha Levels/Curves use the master scalar curve. Embedded source channels are retained through source editing, publication, independent copies and recovery.

Files and compatibility

Format Open Save/export What is retained
.lumen Browser and desktop Browser download or native save Native Pattern Fill, Pattern Overlay/Satin/Bevel, optical Smart Filter metadata/private maps, saved alpha coverage/names/locks, embedded source graph/revisions and smart frames, editable layer effects/fill opacity, Levels/Curves channels and points, vector geometry/style, hierarchy/clipping, layer/mask pixels, text settings, affine transforms, opacity/blending, adjustments, DPI and selection
PNG Browser and desktop Browser and desktop Flattened appearance, alpha, export dimensions and DPI
JPEG Browser and desktop Browser and desktop Flattened appearance, chosen background, dimensions, quality and DPI
WebP Browser and desktop Browser and desktop Flattened appearance, alpha, dimensions and quality; native export writes resolution metadata
TIFF / AVIF Native desktop decoder Native desktop encoder Flattened appearance, dimensions, quality and DPI where supported by the format/decoder
GIF / BMP Browser and desktop No animated export First/static image frame
PSD Browser and desktop Layered PSD with supported editable text and adjustments Saved root alpha planes/names/IDs, supported groups/clipping, raster pixels/masks, visibility/opacity/blending, bundled-font point/paragraph text and character runs, native invert/Levels/smooth Curves adjustments and DPI; unsupported typography, smart objects, styles and custom adjustment scopes use labelled raster baking
PSB Bounded 8-bit RGB input in browser and desktop No PSB export Supported raster/layer metadata and saved alpha planes/names/IDs; existing document/allocation limits apply
SVG No editable SVG import Browser download or native save Unaffected paths/style, bundled-font text and decorations, transforms, ordinary groups, opacity and visibility; unsupported text, raster content, smart objects, styles and affected scopes use embedded PNGs

PNG export preserves full 8-bit color and alpha with lossless compression. The quality setting applies to formats with lossy compression; native PNG does not quantize the image to a color palette.

Lumen projects now write version 15 with the lumen-production-v1 encoding profile and read valid versions 1–15. Version 15 stores vector masks, text baselines, outline clipping, artboards/slices, linked-source descriptors, Layer Comps, Actions, spot metadata and Color Lookup. It refuses unsupported typography, warp and lock records from the parallel development branch instead of discarding them. Version 14 added Pattern Fill/Overlay, Satin/Bevel and eight optical Smart Filters with captured maps. Version 13 added shape recipes; version 12 added ten Smart Filters; version 11 added guides. Older schemas reject owned newer metadata even when undefined or disabled. Geometry, source graph, count and allocation validation covers the complete incoming table, including orphan sources, before image decode; saving prunes unreachable sources only after validation. Legacy text keeps its cache until explicitly edited and old rasterized shapes remain rasterized. Source documents reference the owner's table and cannot own nested source tables. Export presets and brush libraries remain separate from project/recovery history.

Validation also rejects duplicate layer identifiers within a document, missing/non-group parents, invalid transforms and hierarchies deeper than 32 parent levels before restoring pixel data. Vector caches must match independently computed curve/stroke bounds; paths are limited to 4096 anchors and finite coordinates within one million pixels. Style colors must be durable plain CSS colors, with finite bounded controls. The aggregate layer/mask allocation and node limits include the composition and every embedded source.

Per-layer scalar adjustments in project files use the editor's ranges: exposure ±4, gamma 0.1–3, brightness/contrast/saturation/temperature/tint/highlights/shadows ±100, blur 0–100 and sharpen 0–500. Version 8 projects with values outside these ranges are rejected; versions 1–7 still accept ±1000 and are clamped into range when opened. Layers, selections and selection points are rebuilt from the known schema fields, so unknown keys in a file are dropped rather than restored and saved again.

Documents allow up to 12 saved alpha channels, with at most 64 across the root and complete embedded source table. Their canvases count toward the existing 128 million pixel budget and history memory limit. Channels require unique IDs within their document, trimmed names of at most 100 characters, exact document dimensions and opaque grayscale pixels. Metadata and combined allocation are validated before decoding; decoded channel pixels are checked before installing the project. Duplicate names are allowed.

PSD interchange supports 8-bit RGB/grayscale input. The header and layer-record count are checked before the PSD parser runs: files with more than 256 layer records (128 nodes plus group dividers) are rejected. ZIP-compressed layer and mask channels are inflated by Lumen with an exact output cap for their layer or mask rectangle. Native text caches created on import count toward the 128 million pixel project budget; text that would exceed it stays raster content labelled “(rasterized text: native text cache memory limit)”. Supported group hierarchy, pass-through/isolation, clipping, opacity and raster masks are retained. Known bundled-font text can become editable when its typography is supported and a bounded rendering comparison agrees with the cached pixels and placement. Unsupported fonts, glyphs or typography retain labelled cached raster content; vector layers and smart objects also use their cached rendering. Native invert adjustments and supported RGB Levels/smooth Curves adjustment records map to editable Lumen nodes, including all four channels. Grayscale tone adjustments, advanced mask/blending controls and unsupported adjustment types open the cached composite as an explicitly named raster layer; a missing composite produces an import error.

PSD export retains supported bundled-font point/paragraph text, character runs, local pixel sizes, tracking, decorations, manual leading and paragraph spacing as native text metadata. Overflow, nonzero paragraph padding, non-top vertical alignment, variable automatic leading, unsupported fonts/glyphs and unsupported appearance or paragraph controls use labelled raster fallback. Text content and authored trailing blank paragraphs remain in the Lumen master. Changing document DPI does not rescale local font-size metadata. PSD references fonts by name; the receiving computer needs matching faces for text rerendering. Adobe Photoshop rendering of these new files remains unverified.

Saved alpha planes in 8-bit RGB PSD and PSB files are imported with their names and numeric IDs, separately from merged image transparency. Raw, RLE, ZIP and ZIP with 8-bit row prediction are supported within the existing size/memory limits. Layerless compressed documents use the same channel decoder. Layered PSD export writes real saved alpha planes and standard name/ID/display resources while retaining supported editable layer/text/tone records. PSD saves the root document's channels; embedded source channels and channel locks remain in the .lumen master. Non-RGB saved planes, spot-color channels, ambiguous transparency identifiers and unsupported display records produce explicit diagnostics.

Other PSD leaves render at document resolution, baking editable Lumen vectors, per-layer controls and world transforms into raster layers. Export retains ordinary group hierarchy and masks, and writes a current composite image. Smart objects and styled nodes are labelled raster layers; their mask and style extents are baked once, with overall opacity/blending applied once. A smart/styled clipping chain can require baking the lower sibling scope while preserving higher unaffected layers. Export does not claim editable Photoshop vectors, smart objects or styles. Invert-only adjustment nodes and pure Levels or smooth Curves adjustment nodes use real PSD metadata; Levels gamma must be representable to two decimal places. Combined Levels/Curves, linear curves and other custom adjustment stacks bake the affected lower sibling scope once as “Rasterized adjustment scope,” retaining higher unaffected siblings. Isolated groups contain this baking; backdrop adjustments in pass-through groups can require baking the containing lower scope. Save .lumen as the editable master. These tone records are verified against the file format and independent channel/LUT tests; a native Photoshop runtime comparison is still needed. Sophisticated Photoshop masking/blending and unsupported object features remain fidelity limitations; PSD interchange is not a lossless Photoshop round trip. See the ag-psd format limitations.

Choose File → Export SVG for a self-contained SVG document. Ordinary vector leaves export as path commands with cubic handles and editable fill/stroke. Supported text exports as positioned text runs with embedded exact Noto Sans font bytes and rectangular underline/strikethrough decorations; paragraph clipping retains the visible appearance. Invisible overset runs are omitted, and excessive text element counts or decoration geometry use labelled raster fallback. Raster layers and unsupported text use embedded PNGs. Group transforms, opacity, visibility, local raster masks and ordinary clipping chains are retained. Smart objects, styled nodes and their affected clipping chains use labelled raster images including their mask/style appearance once. Adjustment nodes and non-normal blend scopes similarly bake once while retaining unaffected higher vector nodes as paths. Per-vector color/filter controls bake that leaf's appearance. There is no duplicate adjustment, style or mask metadata applied to baked pixels. Characters that XML does not allow (control characters, U+FFFE/U+FFFF and unpaired surrogates) are replaced with U+FFFD. SVG output contains no scripts or external resources; the desktop save capability accepts a restricted, bounded vocabulary and only the exact licensed font bytes. Keep the .lumen master for continued editing of any baked scope.

SVG path edges can receive different antialiasing from Canvas and SVG rendering backends. The Windows accelerated Canvas comparison showed bounded edge differences while preserving exact cubic geometry, uniform interiors and covered bounds. Software-rendered Windows and the tested Linux environment matched the SVG fixture pixels exactly. The native tests check coverage and centroid as well as geometry, rather than accepting arbitrary image differences.

The editing canvas uses 8-bit RGBA through Canvas 2D. There is no full ICC color-management, CMYK/Lab editing, 16/32-bit workflow, RAW development, color-managed printing or soft proofing yet. Print Layout exports raster-page PDFs rather than editable PDF artwork. Local spot designations and their raw plate exports do not provide native Photoshop spot-color interchange. Browser WebP export does not write explicit DPI metadata. Animated GIFs are opened as a static frame.

Desktop open accepts .lumen, .psd, .psb, .bmp and natively decoded .png, .jpg/.jpeg, .webp, .tif/.tiff, .avif and .gif files of at most 256 MB. A native image's leading signature and its decoded format must both match the extension, so a renamed file (for example a TIFF or SVG saved as .png) is rejected, as are truncated or corrupt pixel data and unsupported types. Only the PNG, JPEG, WebP, TIFF, HEIF/AVIF and GIF loaders are enabled; SVG, PDF and all other decoder loaders are unavailable. BMP headers are checked in the main process and the image is decoded by the renderer. The browser preview checks a dropped file's size before reading it.

Images are limited to 8192 pixels per side and 32 million pixels total. Projects accept up to 128 total nodes across the root and all sources, 128 million combined layer/mask/channel/captured-filter-map pixels and a 256 MB serialized file. Groups and adjustment layers use tiny placeholder canvases; their masks occupy real pixels. Smart caches occupy their fixed frame dimensions. PSD export rasterizes transformed leaves and effects to document bounds and limits combined export buffers to 128 million pixels; off-canvas content remains intact in .lumen masters. Rendering bounds each node's body, styles, masks and clipping chain to its own rectangle. Display updates still composite the full document, so deep or large stacks can be expensive. History shares immutable canvases and copies only the target before pixel edits; dirty-rectangle display rendering and history stored as changed tiles remain future work.

Security, recovery and performance

Security model

  • Renderer: sandboxed, with context isolation and no Node integration. Navigation, new windows, webviews and permission requests are denied. The preload exposes a narrow set of validated file operations, never generic filesystem or IPC access. Every handler checks that the request comes from the application page. Saves to an existing path only go to paths chosen in a native dialog. Packaged builds ignore --dev, so they never load the development server. A single-instance lock focuses the existing window instead of starting a second editor on the same profile.
  • Codec process: opened images are decoded in an Electron utility process named “Lumen image codec”. On Windows it uses native sharp for both decoding and export encoding, and the main process never loads sharp. On Linux the codec process uses sharp's WebAssembly build, because native sharp crashes in an Electron utility process there (electron#46323); Linux exports, whose input is Lumen's own rendered PNG, are encoded natively in the main process. WebAssembly decoding is roughly 1.5–2.5× slower than native and uses more memory, but keeps any decoder memory corruption inside the WebAssembly sandbox. Requests are sent in 16 MB chunks and have timeouts; the process restarts after a crash or timeout. Its replies are size-bounded and checked again: the decoded output must be a well-formed PNG within the dimension limits, and encoded output must match the requested format. The renderer sees only Lumen's own error messages, never decoder output. This is process isolation, not an operating-system sandbox.
  • Content Security Policy: one shared policy in electron/csp.cjs. The production build injects the strict policy into index.html (default-src 'none', no plugins, frames or base URL, same-origin scripts and workers). Response headers carry the same policy. Inline style attributes remain allowed.
  • Validation before decode: project files, recovery entries and PSD/PSB structures are checked for size, counts, hierarchy, dimensions and pixel budgets before any image data is decoded. Desktop file reads use one capped file handle and reject files that grow while being read. Saving a project checks only a bounded header and trailer in the main process instead of parsing the complete JSON there.
  • Bounded PSD decoding: the header and layer-record count are checked before the PSD parser runs. ZIP-compressed layer, mask, composite and saved-alpha data are inflated with exact output caps, never by the parser's uncapped inflate. Restored layers, selections and points are rebuilt from known schema fields.

PSD files are still sent to the renderer as base64 strings. Every declared PSD/PSB section, block, channel and merged-row length is checked against its parent and the end of the file before parsing; lengths inside a few rare embedded payloads (annotation sounds, linked files, filter-effect masks) are not yet bounded. Builds are unsigned, and there is no auto-update.

Crash recovery

Each open composition has its own IndexedDB recovery entry, keyed by a stable per-tab ID. An embedded-source editing tab is stored with the composition that owns it. Autosave runs about 1.2 seconds after the last edit. It serializes an immutable snapshot asynchronously, one canvas at a time. It waits until strokes, unfinished pen paths and open editing dialogs are finished. Saving or closing a document clears only that document's entry, and a closed document is not offered again. On the next launch every recoverable document is listed for Restore or Discard. Entries that no longer pass validation, for example after an update tightens a rule, are listed separately and kept until you choose Discard unreadable. If storage fails, an Autosave unavailable notice explains why. Recovery entries pass the same validation as opened files. The older single latest entry is still read.

Performance and benchmarks

The compositor has a no-pixel dry pass (validateDocumentRender) for edit validation, renders each node within its rectangle, and uses fast paths for full-weight interpolation, identity scalar adjustments and opaque adjustment nodes. Color parsing and text layout are memoized. Smart-filter tiles are larger when the dry pass allows it. Effect blur processes alpha only, flood fill does not allocate per pixel, and temporary canvases are released. In the app, live display paints are coalesced to one per animation frame. Hover and pen overlays do not re-render the editor, panels are memoized, and dialog previews are frame-coalesced. The eyedropper reads the display canvas, and the Healing worker is reused between jobs.

Undo history is copy-on-write: snapshots share canvases with each other and with the open document, and a canvas kept by history is never modified in place. A stroke copies only the layer, mask or channel it paints; metadata-only changes copy nothing. History is limited to 50 steps and 256 MiB per document plus 1 GiB across all open documents, counting each distinct retained canvas and immutable filter map once plus owned retained metadata. Brush and clone strokes capture the effective selection once per stroke and update only the 64-pixel tiles each dab reaches.

The compositor and history optimizations are checked against a 160-scene golden harness and the complete pixel suites for byte-identical rendered output. The new stroke engine intentionally changes event-boundary beading and overlap opacity; separate independent pixel references and optimized/unoptimized comparisons check its behavior. The following timings are historical 0.11.x measurements in Playwright Chromium on the development Windows machine (wall-clock medians; machine-specific), not new 0.12 measurements:

Operation Before After
Full render, 4000×3000 document with six layers (styled, masked, text, Levels) 1144 ms 130 ms
moveLayer / layer effect / Levels update on that document ~1.1 s ~0.1 ms
updateSmartFilterStack (blur + unsharp mask on a smart object) 721 ms 5 ms
50 brush pointer moves, 4000×3000, 20 px feathered selection ~50 s ~50 ms
50 brush pointer moves, 4000×3000, no selection (settled) ~700 ms ~40 ms
Undo snapshot per stroke, four full-size layers (median) 33 ms 6 ms
Undo steps kept after 20 strokes on different full-size layers 1 5

The benchmarks and the golden harness are skipped unless enabled:

# Engine benchmarks (logs LUMEN_BENCH / LUMEN_BENCH2 JSON lines)
LUMEN_BENCH=1 npx playwright test tests/perf-bench.spec.ts
# Compare another engine module, e.g. a saved copy of an older engine
LUMEN_BENCH=1 LUMEN_BENCH_ENGINE=/src/engine-old.ts npx playwright test tests/perf-bench.spec.ts
# App-level brush/interaction benchmark
LUMEN_BENCH=1 npx playwright test tests/interaction-perf.spec.ts
# Golden harness: the first run (or LUMEN_GOLDEN_MODE=record) records digests, later runs compare
LUMEN_GOLDEN=golden.json npx playwright test tests/render-golden.spec.ts
LUMEN_GOLDEN=golden.json LUMEN_GOLDEN_SEEDS=3,17,42 npx playwright test tests/render-golden.spec.ts

Golden digests depend on the platform and GPU, so record and compare them on the same machine. LUMEN_GOLDEN_SCENES changes the scene count (default 160). setCompositorOptimizations(false) in src/engine.ts turns off the bounded-rendering paths, so tests can compare them with the unbounded compositor.

Verification

Desktop release 0.20.1 verification

The menu patch passed production typecheck/build, all 1,699 units across 109 files and 42 affected browser cases with no failures, skips, retries or flaky cases. The earlier complete batch08 browser and benchmark results below remain attached to 0.20.0. The new desktop helper is node tests/desktop-shell-menu.cjs --output=<fresh-absolute-directory>; set LUMEN_EXECUTABLE to the packaged executable or AppImage. It checks native-menu visibility through Alt, Alt+F, resize and a retained native fullscreen-role round trip, Help/About before document creation, one-step duplicate/Undo, focused text handling and literal PNG/project reopen pixels. Fullscreen-role invocation does not establish physical OS F11 key dispatch.

Local raw receipts are retained under release/development-release-0.20.1/: windows-unit-02.json, windows-browser-03-reviewed.json, windows-package-native-reviewed-01.json, root-windows-shell-role-03/acceptance.json, and linux-run-05/terminal.json. The original failed preparation and harness invocations remain failed; successful recovery receipts name their actual scopes. Only the test helper and documentation changed after the complete 490-file source guards closed: the committed helper is exactly the candidate exercised on both platforms, and every packaged application/compiled file remains unchanged. Release publication separately verifies all four public downloads against SHA256.

Batch08 (0.20.0) source verification

Batch08 (0.20.0) is the latest verified source milestone after the current Windows suites and affected Linux checks below completed with their closing source and dependency guards. The current writer is schema 15 with the exact lumen-production-v1 profile. Packaged distributables remain 0.12.5; these source checks do not retest or rename the delivered packages.

Batch08 adds twenty distinct local editing and output capabilities for the Photoshop-like Windows/Linux editor, without AI enhancement dependencies.

# Distinct addition Supported scope
1 Editable vector layer masks Owned filled paths combine with raster masks, with density, inversion and bounded feathering.
2 Text on editable paths Eligible pinned-font, whole-run shaped text follows an owned baseline; unsupported shaping refuses.
3 Outline Stroke Eligible stroke paints become actual editable filled geometry while original paints remain ordered.
4 Offset Path Eligible closed paths receive signed inset/outset geometry; unsupported topology refuses atomically.
5 Text to Outlines Eligible glyphs and decorations use actual pinned-font contours and retain ordered paints and clipping.
6 Linked smart assets Place linked, Refresh, Relink and Embed use opaque native grants and immutable embedded fallbacks.
7 Artboards Named isolated root-group frames clip editable artwork and support per-board outputs.
8 Slices Named rectangular regions export cropped PNG bundles with names, dimensions and DPI; no placement coordinates are claimed.
9 Layer Comps Bounded named presentation states apply over current artwork without restoring old pixels.
10 Recorded Actions Settled supported properties record and replay with explicit targets and atomic refusal; no arbitrary scripts.
11 Directory batch processing Up to 32 selected native inputs receive a captured supported Action and separate bounded output files.
12 Contact sheets Captured photos use validated fit, rotation, labels, margins and grid placements.
13 Print Layout and PDF Editable bounded page placements export up to 16 native raster-page PDF pages; no press color management claim.
14 Apply Image Captured source imagery combines into an eligible pixel or coverage target with selection and alpha applied once.
15 Calculations Captured channels combine into a new saved coverage result under complete scene admission.
16 Individual channel transforms Supported adjustments operate on one captured component or saved coverage channel.
17 Local spot channels Independent ink designation, transient preview and raw separation PNGs; no native PSD spot designation.
18 Background Eraser Captured sampling and selection remove matching background coverage through frozen baseline arithmetic.
19 Affine Clone sampling Captured scale, rotation and mirroring sample through the source affine frame; identity keeps legacy arithmetic.
20 Color Lookup Bounded 3D Cube data uses red-fastest trilinear encoded-RGB interpolation and retains alpha.

All new paths, contours, captured recipes, metadata, native grants and private outputs retain complete source/resource/history admission. Unsupported shaping and vector topology refuse atomically. Slices provide cropped-region PNG bundles and a names/dimensions/DPI manifest; placement coordinates are not exported. Spot channels provide local ink designation, preview and raw separation PNGs; native PSD spot designation is unsupported. Print Layout exports raster-page PDF artwork without press color management. The batch08 native capability helper covers Linked assets, Directory grants and PDF; the mapped public App cases supply editing UI coverage for all twenty features.

Evidence lane Actual current source outcome
Windows TypeScript / production build Typecheck03 and build02 exited 0 with stable opening/closing inputs. Typecheck, build.
Full Windows units All 1,699 assertions in 109 files passed, actual exit 0 with stable collected inputs. Report.
Full Windows Chromium / Canvas / UI All 1,257 current cases passed once with zero failures, skips, retries, flaky outcomes or global errors. This includes all six benchmarks, all 94 cases in 13 new specs and the actual public App mapping for each twenty-feature addition. Final audit.
Immutable compositor golden Actual comparison retained 3,791 entries / 160 scenes / 60 expected refusals, exactly matching SHA256 45553143a3b0e381a455d2a6b197129e89240adeb7e3f8382602eee93b824274. Actual golden.
Windows production native regressions Actual exit 0 and 66 distinct workflows, including optional 8000×4000 AVIF export/reopen. Retained twelve helper sets / 111 files, nine legacy diagnostics and 21 restored historical text outputs. Native review.
Windows batch08 native capabilities Separate actual exit 0 for Linked assets, Directory grants and native physical PDF output: three workflows / nine outputs and five fixture inputs. This does not establish native editing UI coverage of all twenty features. Actual execution.
Affected WSL / Canvas Fresh Linux CI/type/build and selected 333 unit assertions in 26 files passed. All 470 Canvas/UI cases in 30 files passed once, including all thirteen new specs / 94 cases; this is an affected scope, not the full Linux suite. Factual final receipt.
Linux native / PDF pixels Three narrow native workflows retained nine outputs / five fixture inputs. Independent Poppler raster checks covered two 612×792 pt PDF pages and eight literal regions / 8,192 pixels, with RGB error at most one and exact alpha. This numeric check is separate from visual inspection and editing UI coverage. PDF oracle.
Complete source / compiled assets / dependencies The tested hold contains 487 inputs / 10,434,056 bytes; ten compiled files bind the same source and actual type/build, match Linux, and remain unchanged at closing. Complete Windows 18,810-file dependency/Node/lockfile and Linux dependency inventories closed with zero changes. Source, compiled inventory, overall closure.

Original failures remain retained with their actual outcomes: Windows current-all-01 recorded 1,255 passes / two failures across 1,257 cases, and WSL05 recorded 469 passes / one failure across 470 selected cases. The complete source guards closed on each original invocation; later corrected runs do not turn them into successful runs. Windows factual original, WSL factual original. Earlier failed/interrupted invocation records and all historical verification text below remain unchanged for the batch08 commit.

Legacy document/layer/revision values retain their original opaque identifier alphabet, including newline, tab, NUL and reserved-looking value strings; source-table keys retain their separate reserved-key rules. Spot ink designation is independent of saved channel labels through Rename/Duplicate, no-op Redo and project reopen. Two obsolete brush cases were consolidated only after their unique assertions were retained in stronger existing coverage; no net runtime saving is claimed. Passing these suites does not establish full Photoshop parity, Adobe interchange fidelity or comprehensive physical OS/tablet/IME interoperability.

Historical source and package verification is preserved in Verification history. Actual artifact cleanup and recovery mappings are recorded in Artifact cleanup.

npx playwright install chromium
npm run verify

On a clean Linux CI machine, install browser system libraries with npx playwright install --with-deps chromium. Verification covers TypeScript, production bundling, pure pixel and project validation tests, and real Chromium canvas/UI tests. Native desktop tests exercise Electron file dialogs, security boundaries, and actual sharp encoders:

npm run test:desktop
# Headless Linux:
xvfb-run -a npm run test:desktop

Ten-feature development batch (0.13.0)

This batch implements editable Gradient Fill layers; gradient drawing with linear, radial, angle, reflected and diamond modes and reusable color stops; editable Solid Color Fill layers; and seven nondestructive color adjustments: Gradient Map, Vibrance, Color Balance, Channel Mixer, Posterize, Threshold and Black & White. Create fills and adjustment layers from Layer, then edit them through Properties. Drafts preview privately and apply as one Undo step. Selection-derived masks, hierarchy locks, clipping, layer opacity and embedded sources retain their existing rules.

Projects now write schema 9 and continue reading versions 1–8. New fill/color metadata is strictly validated before image decoding. The editable Lumen master retains these controls; PSD and SVG bake unsupported scopes while preserving unaffected higher native paths and text. Gradient color interpolation uses premultiplied encoded sRGB. Color correction formulas are Lumen behavior; Adobe byte identity is not claimed. Brush Flow from the preceding development work is also present.

Ten-feature development batch (0.14.0)

The next batch adds a named brush preset library with Lumen JSON import/export, captured distance-based smoothing and elliptical tips with angle/roundness; multi-layer Free Transform, a draggable pivot and dedicated/numeric skew; selection Grow/Shrink, Border, sampled Color Range and Quick Mask painting.

Brush recipes are local tool settings outside project history and use .lumen-brushes files. Circular tips and disabled smoothing retain the previous captured brush arithmetic. Multi-layer transforms apply one document-space affine change to topmost selected roots, retaining editable geometry and masks. Pivot changes are transient. Selection morphology uses a square pixel neighborhood; Color Range uses RGB distance with alpha coverage. Quick Mask uses a private grayscale plane and separate red overlay, so canonical RGB pixels, clipboard and exports remain unchanged. Brush and Eraser edit coverage; leave Quick Mask to use image-pixel tools. Selection scratch and complete source-table allocation are checked before new planes are created. These controls retain Lumen behavior rather than claiming Adobe algorithm identity.

Twenty-feature development batch (0.19.0, verified source)

The seventh batch adds twenty distinct local editing capabilities. The original Photoshop-like editing/export scope remains open; this batch adds no AI enhancement dependency. Radial Blur's two methods count as one capability, and the separate Bevel & Emboss style is distinct from the existing Emboss Smart Filter.

# Distinct addition Editing behavior
1 Native OS image Clipboard Copy/Cut publish an image; Paste prioritizes a present OS image and uses the local pixel buffer only when no OS image is present.
2 Textured brush A captured grayscale tile modulates nib coverage with editable scale and rotation.
3 RGB brush blend Normal, Multiply, Screen, Overlay, Darken and Lighten operate through the captured paint writer.
4 Brush scatter Seeded perpendicular or two-axis displacement expands dab support before painting.
5 Seeded angle jitter Each carried dab ordinal determines its angle independently of event partitioning.
6 Roundness jitter Captured per-dab roundness varies the elliptical tip.
7 Size jitter Captured per-dab size variation retains pressure and carried spacing.
8 Color jitter Independent hue, saturation and brightness lanes vary RGB Brush color while retaining foreground alpha.
9 Pattern Fill layer An editable RGBA tile repeats through its own frame, scale, rotation and offsets.
10 Pattern Overlay style A repeated RGBA pattern follows the original layer lattice and masked body coverage.
11 Satin style Two offset blurred coverage fields supply color, opacity, contour and inversion controls.
12 Bevel & Emboss style Coverage-height lighting supports Inner, Outer, Emboss, Pillow and Stroke relief, three techniques, depth, direction, size/soften and highlight/shadow controls.
13 Radial Blur Spin and Zoom are two modes of one filter, with center, integration samples and image-edge choices.
14 Lens Blur Finite aperture blades/rotation/curvature, highlight controls and optional captured depth/focal controls.
15 Smart Sharpen Bounded regularized Richardson–Lucy restoration with Gaussian, Lens or Motion PSF, noise suppression and tonal fades.
16 Reduce Noise Luminance/chroma reduction, detail preservation and optional detail sharpening.
17 Diffuse Seeded Normal/Darken/Lighten modes and bounded anisotropic diffusion.
18 Displace map A captured R/G/alpha map drives two-axis displacement with stretch/tile placement and image-edge choices.
19 Pinch Signed radial pinch/bulge with an editable center and radius.
20 Spherize Signed spherical mapping with Normal, Horizontal and Vertical modes.

All eight filters remain editable entries in the existing smart-object stack: original source/frame sampling, ordering, entry opacity/blend and the shared mask apply through the established compositor. Their dialogs capture private settings/maps until Apply. Changed edits create one Undo step; admitted semantic no-ops preserve exact metadata/cache, token and Redo. Pattern Fill is a native frame-sized body; RGB body painting stages a raster appearance and installs it only when pixels change, while mask painting retains the native pattern. Pattern Overlay, Satin and Bevel & Emboss retain original frame/world coordinates and apply masks/fill/overall opacity in the existing style order. Stroke relief requires enabled, nonzero Stroke coverage.

Brush/Eraser/Clone and Stroke path capture texture and shape variation with the opening recipe; Quick Mask uses its separate coverage API. Color jitter applies to RGB Brush paint, while Clone, Eraser, masks and channels keep their neutral color behavior. RGB blend applies to RGB Brush/Clone targets. Normal keeps the established arithmetic; non-Normal body painting uses source-over alpha composition, including transparent pixels, and component edits retain other components and alpha. Separate Retouch and Healing APIs retain their existing recipes. Version 2 .lumen-brushes libraries own texture assets and all recipe fields, accept version 1 migration, and stay outside project/history/recovery.

Clipboard Copy/Cut freeze canonical document-space pixels and order writes before asynchronous encoding. Cut removes pixels only after a successful write and a fresh check of the captured document/token/physical target/selection; a failed or stale completion leaves the captured pixels intact. Paste resolves captured IDs after Undo and respects active modal/gesture FIFO gates. A present but invalid OS image reports its failure; local-buffer fallback is only for no image. Component, layer-mask, Smart Filter mask and saved-channel routes retain their existing coverage/selection rules. Images cap 8192 pixels per side, 16 million pixels and 64 MiB encoded; OS acquisition caps 16 items/64 types per item. Native encoded writes and renderer retained decoded writes use separate 128-entry/64 MiB admission queues before decode/capture, with an enqueue recheck.

Resource limits are separate: brush lazy tiles, texture/variation captures and transient dab/stamp scratch share 64 MiB, with 65,536 dabs per event. Selection/Clone backing canvases, rich-body baking, COW/history and UI/runtime costs are additional. Brush libraries allow 128 presets/256 KiB UTF-8 JSON, 32 texture assets, 64×64 pixels per texture and 65,536 total grayscale texels. Pattern RGBA tiles allow 256×256 pixels, imported PNG tiles cap 1 MiB, and distinct owned tile records in the complete root/source table cap 16 MiB; equal immutable base64 text on different records is charged separately. Pattern and surface sampling each have a 64 MiB scratch fence; Pattern Overlay additionally caps 16 million sampler visits (four per output pixel), and surface evaluation has a 640 million work-unit fence.

Captured optical maps retain private scalar depth or R/G/alpha displacement planes behind immutable tokens. The complete root/source table allows at most 256 distinct map tokens and 64 MiB of retained map bytes, including headers; map pixels also count with canvases/masks/channels toward the existing 128 million pixel document fence. Optical sampling separately allows 64 MiB scratch, 16 million sample visits per request and 640 million whole-render operation units, with at most 2,048 dependency pieces. Canonical map JSON has separate 256 Mi-character, 640 million codec work-unit and 384 MiB conservative encoding-scratch fences. Complete incoming metadata, orphan records, owned newer fields, maps, counts and allocation are validated before decoding/encoding. History counts retained immutable map identities and owned Pattern metadata in its existing 50-step/256 MiB per-session and 1 GiB global budgets. These conservative charges are not total process-memory measurements.

Projects write schema 14 and read valid versions 1–14. Schema 14 gates Pattern bodies, Pattern Overlay/Satin/Bevel and all eight new Smart Filter kinds, including disabled or orphan records. Unsupported PSD/SVG scopes bake and label their appearance once while the Lumen master and unaffected higher native paths/text stay editable. Source replacement validates the complete prospective graph, prunes unreachable sources, then admits final retained allocation before COW/publication; immutable captured maps stay shared and Pattern records stay owned.

Core corrections after 0.18.0

Expanded smart-source trees now refuse above 4096 layer occurrences per root/source, including hidden and orphan records and repeated references. Stored-node, depth and allocation limits remain independent. Prospective duplication, independent copies, placement, replacement and source publication are checked before history freezes live canvases. Active legacy Smart Filter stacks now enter the existing 640-million-unit whole-render admission before pixel work; disabled/Normal identity stages retain their bypass, and the original kernel arithmetic is unchanged. These changes close audit findings A-S12 and A-S13.

Ten-feature development batch (0.18.0, verified source)

Version 0.18 adds Unite, Subtract, Intersect and Exclude filled paths; persistent polygon, star and line/arrow recipes; named export presets; variant ZIPs; and layer/group asset ZIPs. Select eligible consecutive sibling paths and use the Layer Boolean command. The lowest sibling is Subtract's subject; the active selected layer supplies the surviving ID/style/frame. Boolean curves are approximated within 1/64 document pixel in world space, then saved as editable line-anchor paths. Unsupported appearance/topology and unequal coordinates that would be merged by backend precision snapping refuse atomically rather than silently changing thin geometry. The initial scope excludes stroke expansion, masked coverage and crossing/touching Nonzero compounds.

Use Configure shape to edit a polygon/star or line/arrow recipe. Changed anchor/handle edits and committed Pen subpaths materialize it to a plain path; Convert to path explicitly removes only the recipe. Opening, rejected previews, Cancel and exact return preserve the opening metadata, mask/cache and Redo. Native handle-free line subdivision now keeps line commands; strict grid/native/masked fixtures retain exact pixels, but arbitrary affine antialiasing after subdivision is not a universal byte-identity claim. Undo restores the exact opening state. Schema 13 writes recipes throughout root/source tables and reads valid versions 1–13; PSD/SVG use canonical paths and existing labelled fallback, with no Adobe editable-recipe claim.

File → Export presets manages PNG/JPEG/WebP settings in separate local storage or .lumen-exports files. Accepted imported selection hydrates its name and all settings, including same-ID imports with incomplete number fields; rejected values remain editable. File → Export variants creates enabled format/size rows and saves one ZIP. File → Export layer assets emits selected artwork scopes as PNGs with placement metadata. Selected descendants collapse under selected ancestors, and clipping chains remain complete. Inherited masks/opacity/styles/isolation or clipped-ancestor backdrop dependence requires selecting the enclosing scope. Trim stays inside the document viewport; all-transparent trim is a retryable no-output result. Full document canvas can deliberately emit transparent PNGs.

Exports admit the complete master/source table and dry render/resize work before freezing output. Presets allow 32 records/256 KiB UTF-8. Bundles allow 32 total rows (including disabled/empty records), at most 32 images plus a manifest, and a complete stored ZIP ≤64 MiB including headers/CRC/manifest. Each output is ≤8192 per side/32 million pixels; enabled output pixels total ≤128 million. Separate limits are 64 MiB distinct native input PNGs, 256 MiB conservative wire text, 64 million additional backing pixels, 64 MiB preparation scratch and 640 million work units. These conservative charges do not measure total process memory. Shared saving ownership, fixed validated bundle IPC, real PNG/JPEG/WebP encoding, one native chooser after ZIP preparation and atomic replacement keep the master unchanged; browser downloads work directly under the CSP.

Ten-feature development batch (0.17.0, verified source)

The fifth batch adds ten editable Smart Filters: Box Blur, Surface Blur, Dust & Scratches, Find Edges, Emboss, Solarize, Color Halftone, Wave, Ripple and Twirl. They retain the original source and use the existing ordered stack, entry opacity/blending and shared mask. Each dialog has private preview, Reset, Apply and Cancel; Wave also offers seed regeneration. Apply records one changed edit; admitted unchanged edits retain the authored metadata, token and Redo.

Box Blur uses a premultiplied square average. Surface Blur gates neighboring samples by RGB distance, and Dust & Scratches gates the existing alpha-weighted median candidate. Find Edges uses luminance Sobel edges; Emboss uses directional bilinear luminance samples; Solarize inverts components above its threshold. Color Halftone uses frame-anchored CMYK screens and cell means. Wave uses seeded inverse displacement, Ripple uses radial displacement, and Twirl uses radius-weighted inverse rotation. Warp edge choices resolve individual bilinear taps in the original frame. These are defined Lumen formulas, with no Adobe byte-identity claim.

Projects write version 12 and read valid versions 1–12. Older schemas reject the ten newer kinds even when disabled or hidden in orphan sources. Complete metadata, count, dimensions and allocation checks precede decoding. New kernels additionally admit synchronous work before output allocation/clear: 16 million visits per request and 640 million operation units for a complete render. Dependency, buffer and whole-document budgets remain separate; these are conservative accounting limits rather than total process-memory measurements. Refused requests leave an editable dialog for correction.

Regional rendering now preserves the original device origin for non-grid affine bodies when the ordinary document viewport requires it. The anchored backing prefix is charged before allocation; exact-grid and quarter-turn samples remain local. Tight dependency/culling geometry remains separate from the backing rectangle. Smart preview refresh validates the complete canonical owner before rendering its fixed-frame scene, including nested source publication. Exact regression coverage includes empty/filtered nested occurrences, masks, both compositor modes, quota rejection and a 67-node canonical owner. Custom effect viewports and arbitrary final sampling transforms are outside those new exact-oracle claims.

Ten-feature development batch (0.16.0, verified source)

Version 0.16 adds selected-layer alignment and center/equal-gap distribution; document guides and captured snapping; polygonal lasso, Transform Selection and Crop framing; editable polygon/star and line/arrow shapes; and brush strokes along selected vector subpaths. Alignment and distribution preserve layer hierarchy, affine shear and native bodies. Guides are nonprinting overlays with ruler dragging, numeric placement, layouts and locks. Crop supports Free, ratio presets/custom ratios and Exact pixels modes with handles, thirds and Apply/Cancel.

Polygonal lasso keeps its clicks private until a valid close or Enter, supports Backspace and Escape, and combines selections by replace/add/subtract/intersect. Transform Selection moves, scales or rotates the selection without altering layer pixels. Exact simple geometry remains editable; feathered/inverted or general affine coverage is sampled once from the finite opening document plane, with zero coverage outside it. Crop remains an axis-aligned private frame until Apply; Exact pixels selects crop dimensions without resampling. Full-document identity Crop preserves the original selection and Redo.

Lumen writes project version 11 and reads valid versions 1–11. Version 11 adds owned guide records to root and source documents. Older schemas reject the guide field even when empty or undefined. Guides do not print or alter exported pixels. Polygon/star and line/arrow tools create the existing editable vector path geometry; path brush strokes create pixels on the chosen raster, mask or saved alpha target while retaining the vector master.

Use Move (V), select the layers and expand Align / distribute in the options bar. Choose Alignment reference (Selected bounds or Canvas) and Distribution spacing (Centers or Equal gaps), then apply the requested command. In View, New guide… and Guide layout… create document guides; Snap enables the targets chosen in Snap settings…. Guides stay outside image pixels and exports.

Choose Polygonal lasso and click its vertices, then press Enter to close the selection. Use Select → Transform selection… to change its geometry without moving image pixels. Crop (C) keeps its frame private until Apply. For drawing, choose Polygon / Star or Line / Arrow; with an editable path selected, expand its vector controls and choose Stroke path with brush….

Crop stays axis-aligned; arbitrary-angle straightening and perspective Crop remain open. Guides use horizontal/vertical lines, and shape creation parameters save as ordinary editable paths. Stroke Path remains a bounded pixel operation. No 0.16 packages, Adobe rendering parity or physical OS/tablet/IME interoperability claim follows.

Ten-feature development batch (0.15.0, verified source)

The third batch adds Hue/Saturation, nine-range relative/absolute Selective Color and Photo Filter; Inner Shadow, edge/center Inner Glow and editable Gradient Overlay; Motion blur, wrap/transparent Offset and RGB Minimum/Maximum Smart Filters.

Projects write schema 10 and read valid versions 1–10. New metadata is rejected in older schemas before image decoding, including disabled records and hidden source entries. Overlay stops share the complete-table 4096-stop budget. Offset gathers separated wrap dependencies through preceding filters without treating a seam as a full-frame allocation. Minimum/Maximum use square RGB neighborhoods and retain alpha. Interior styles retain the original masked layer coverage, follow local geometry and remain visible at zero body Fill. The gradient editor owns private draft metadata and previews until Apply. Unsupported PSD/SVG scopes bake with labels while the Lumen master retains editable settings. These formulas do not claim Adobe byte identity.

Remaining work toward the full editor

The original scope remains the destination. Hierarchy, clipping, adjustment layers, editable vectors/text, embedded smart sources, basic nondestructive styles and RGB/saved alpha editing provide a foundation for larger compositions. Further milestones include font discovery/import, language-aware typography, hyphenation, per-paragraph styles, broader path-text shaping, vector topology operations and additional parametric shapes; portable linked-file resolution and a larger style/filter library; Healing diffusion controls and content-aware tools that do not depend on hosted AI; broader channel transforms and native PSD/press spot-color interchange; color management and higher bit depths; RAW support; editable Photoshop vector/smart/style/adjustment interchange and stronger format fidelity; scalable tiled rendering and disk-backed history; richer directory and combined asset/variant export workflows; color-managed printing; accessibility polish; and sustained Windows/Linux installer and performance validation.

Further transform work includes distort, perspective and warp, arbitrary-angle Crop straightening and perspective Crop. Current selected-layer scale/rotation/skew controls preserve editable geometry and exact affine shear. Guides currently remain horizontal or vertical; angled guides remain future work.

Performance work still open: tiled, dirty-rect rendering with per-node caching instead of a whole-document recomposite per display update; moving PSD decode/encode and heavy filters to workers; transferring PSD bytes without base64 IPC strings; loading fonts as file assets instead of inlined base64; and disk-backed history with dirty-tile snapshots inside a layer. Version 0.12 adds accessible desktop layer rename and pressure size/opacity for Brush, Eraser and Clone; version 0.14 adds brush presets, smoothing and elliptical tips; version 0.19 adds captured texture, blending, scatter and jitter. Tilt remains future work. The remediation table in AUDIT.md tracks each finding.

These are substantial features. Completion means testing the editing and export workflows across both operating systems, with explicit coverage of document fidelity and compatibility, rather than treating this initial feature set as Photoshop parity.