diff --git a/CLAUDE.md b/CLAUDE.md index c9fe78c..28864da 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -54,6 +54,7 @@ When smoke fails, read the **first** `FAIL` line, because later failures often c - The core state: - `S` holds settings and the document (tool, colours, sizes, zoom, file handle and name, `pristine`). - `drag` is the in-progress pointer gesture, keyed by `drag.t`: `pencil`, `brush`, `move`, `select`, `canvas`, `pan`, `objmove`, `objsize` (the Move tool, V, or a shape tool on its own selected shape), and so on. + - `objmove` and `objsize` snap (`objDragUpdate()` in `pointer.js`): on the first real move, `drag.snap` gets the lines (`Core.snapLines`) of the frames (`frameOf()`, cached per object in `scene.js`) of the objects in view (`visible()`) that aren't being dragged, plus the canvas; each update snaps the raw pointer delta afresh (`Core.snapMove` / `snapResize`) and keeps `drag.guides`, which `render()` places as `#guides` lines. `drag.free` (⌘, Ctrl elsewhere) turns it off. - `sel` is the selection; `sel.float` is a lifted or pasted canvas that hasn't been committed yet. - `textAt` is non-null while the text box is open. - `objs` is the list of objects, bottom to top: shapes (line, rect, ellipse), strokes (pencil, eraser = a colour-2 pencil, brush; in Transparent a stroke clears what is under it, with `destination-out`), text, image (a committed paste or lift) and fill (a frozen bitmap of what a fill changed); see `Core.objBox` for each kind's data. Every tool that marks the picture adds one **on top** (since the switch, 2026-10-17). A stroke is drawn live as the scene's top object (`setLive()`) through the same tile repaint, and joins on release without a repaint; a pencil or eraser stroke (whole-pixel rects, `Core.stampRects`) is drawn a piece at a time onto a canvas per tile it is in (`liveCv`), not composed again each frame, which a brush (one anti-aliased path per tile) still is. A lift leaves a colour-2 patch (a filled rect object; in Transparent it clears) and commits an image object, one undo step; Delete adds a patch. The fill reads what is seen (`composite()`) in its worker. Only whole-image ops, New/Open and ⌘E change the base. They are kept each with a fractional z-key `o.z` (`Core.zBetween`) that sorts like the list; they are drawn into `#image`'s cache over the base. `objSel` is the selected one's id; `multi` holds the ids when several are selected (Move tool box drag or ⇧-click), and `selList()` gives the selection either way. Every change goes through `install()`, which keeps the scene's index and repaint in step. Objects are immutable (edits make a new one with the same id) and become pixels only through `flattenObjs()` (⌘E). Crop, resize, canvas size, rotate and flip carry them along instead (`docOp(next, map)` with `Core.moveObj` / `scaleObj` / `transformObj`; one left wholly outside is dropped). Save, copy and the picker use `composite()`. See `docs/objects.md`. diff --git a/README.md b/README.md index 2d37e9f..4eeb022 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ In Chrome you can install it as an app (install icon in the address bar). It the ## Features - Pencil, brush (smoothed), eraser, fill, colour picker, text -- Every mark (strokes, shapes, text, fills, pastes) stays an editable object until you flatten it (⌘E); PNG saves keep them +- Every mark (strokes, shapes, text, fills, pastes) stays an editable object until you flatten it (⌘E); PNG saves keep them. Moving or resizing one snaps it to the others and to the canvas - Select, crop, resize, canvas size, rotate, flip - Open, paste or drop images, including TIFF; ⌘S saves back to the file you opened - Transparency: PNGs open and save with it. The checkered Transparent swatch erases: with color 2 set to it, the eraser, Delete, moving a selection and a bigger canvas leave transparent pixels, and pencil, brush and fill drawing with it clear @@ -45,6 +45,7 @@ Press `?` in the app for the full list. ⌘ is Ctrl and ⌥ is Alt on Windows/Li | ⌘⇧X | Crop to selection | | ⌘D, ⌘] ⌘[, ⌘E | Objects (every stroke, shape, text, fill and paste stays movable; new ones go on top; PNG saves keep them): duplicate, bring forward / send backward (⌘⇧] ⌘⇧[: to front / back; also buttons in the Move tool's bar), flatten into pixels (with the Move tool and a selection: just those, into image objects). The Fill tool inside a closed shape sets that shape's fill | | ⌥+drag (Move tool) | Duplicate an object by dragging a copy off it | +| ⌘ while dragging an object | Moving or resizing an object snaps its edges and centre to other objects' and the canvas's (magenta guides show what lines up); hold ⌘ to place it freely | | ⌥+drag (Eraser) | Delete every object the drag crosses (⌥ alone outlines the one under the pointer) | | Tab / Shift+Tab (Move tool, canvas focused) | Select the next / previous object in drawing order; past the last one, focus moves on | | Shift+R | Resize image (resample) | diff --git a/docs/objects.md b/docs/objects.md index d496db8..c02e3c0 100644 --- a/docs/objects.md +++ b/docs/objects.md @@ -95,7 +95,7 @@ Everything is a node, including brush strokes (vector paths) and pasted bitmaps 9. **Text as an object:** not done. `commitText()` would create a TEXT object (string, font, size, colour, position) instead of baking, and clicking it with the text tool would reopen the box. The baseline maths (`textBaseline()`) carries over unchanged. 10. **Pasted images as objects:** not done. This would reuse `sel.float`'s canvas as an IMAGE object, so a paste could be moved and scaled later, which is the most Figma-like win for screenshots. 11. **Persistence chunk:** dropped (decided: objects are gone on reopen). -12. **Rotation, groups, multi-select, snapping, layers list:** not done. Multi-select (⇧-click, marquee over objects) is the next most useful step after text. +12. **Rotation, groups, layers list:** not done. Multi-select (⇧-click, a box drag with the Move tool) came with total objects, and snapping to other objects and the canvas, with guides, on 2026-10-21 (task.md, "Shapes (objects)"). 13. **`task.md`:** "selection resize handles" is listed as out of scope for polish. Objects reopen it as feature work. 14. **Keyboard access to objects:** done. With the Move tool and the canvas focused (`#viewport` is a Tab stop), Tab / ⇧Tab select the next / previous shape in drawing order, and a screen reader hears its kind, size, position and index. Past either end the shape is deselected and the key moves focus on as usual, so focus is never trapped. diff --git a/src/index.html b/src/index.html index f1b10e4..a44dfed 100644 --- a/src/index.html +++ b/src/index.html @@ -110,7 +110,7 @@ - Click an object · drag empty canvas to box-select · Space-drag pans + Click an object · drag empty canvas to box-select · ⌘ held: no snapping · Space-drag pans Drag to select · drag inside to move Paints with color 2 · clears if it’s Transparent Click: color 1 · right-click: color 2 @@ -130,7 +130,7 @@