mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-25 10:56:48 -06:00
77b6f1fc12fd8bfa8498a859bee572bdb2c3ca3e
11
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
fb8ace5bf4 |
M1: full screen (#227) (#238)
View > Full screen (`view.fullscreen {on?}`, F11 on Windows and Linux,
Ctrl+Cmd+F on macOS) makes the window fill the screen, taskbar included,
and leaves it again. FigCraft's own menu item, translated in every catalog.
Tests: the key and the command toggle the window, junk parameters are
ignored; the shortcut table pins the new binding (49 pinned).
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: bflatastic <bombayapirat@gmail.com>
|
||
|
|
e01f4c1a43 | Docs: shortcut counts, draft coverage and the box-selection label after the polish pass | ||
|
|
29e31c2054 |
M1: rotation origin and place-with-Enter shortcuts
The last two documented shortcuts the parity table listed as missing, and a stale toast.
- **Alt+R: rotation origin** (both platforms). `transform.rotationOrigin {on?}` shows (or hides) a
handle at the selection's rotation origin: a ring with a cross through it, drawn in code. Drag it
to move the origin (it snaps to the selection box's corners, edge middles and centre within the
snap distance); double-click it to put it back at the centre; Escape hides it before it deselects,
and Alt+R again hides it too. `transform.setRotationOrigin {x, y} | {reset: true}` does the same
from the CLI, MCP and the control channel. The corner rotation handles, the rotation field
(`node.set {rotation}` on the selection: each layer's point under the origin stays put, so flipped
layers work too), Rotate 90/180 and `transform.rotate` without `cx`/`cy` turn about it. The
origin is stored on the document state per selection, in the first layer's own coordinates, so it
follows the layers as they move and turn; it is dropped when the selection changes and is not
saved in the file (the public documentation doesn't say it persists; pinned in the parity row).
- **Enter places an object with the chosen tool.** With a shape or frame tool chosen, Enter on the
canvas, or Enter again on the toolbar's chosen tool (after picking it from the toolbar with the
keyboard), places what a click with the tool makes (100x100; lines 100 long; sections 400x300, the
existing click sizes, now one `ToolId::click_size`) centred in the view, selected, as one undo
step; the canvas then takes the keyboard. New `tool.place {tool?, x?, y?}`; MCP's headless `key
Enter` does the same. The documented crosshair that the arrows move before placing is not built.
- **Keyboard box selection toast** (and its screen-reader text) now names both ways to size the box:
"Shift+arrows or Cmd+arrows" (Ctrl on Windows and Linux). The changed key is renamed in every
catalog with its translation updated.
- **Shortcuts panel labels:** a label too long for its column was measured in a smaller font than
it is drawn in, so long ones (several Spanish rows) ran into their keycaps; it is now measured in
its own font, ends with "..." before the keycaps and shows in full on hover. The new Tools row
reads "Place a shape or frame (its tool chosen)" with an Enter keycap.
- **Parity table:** the two rows move from Missing to bound (Alt+R -> `transform.rotationOrigin`;
Enter -> a Tools-tab gesture row, the keymap's Enter staying `select.children`); counts 129
documented, 128 bound, 1 moved, 0 missing, 2 kept by a platform, 48 pinned.
- New command labels in Spanish (`tool.place`, `transform.rotationOrigin`,
`transform.setRotationOrigin`), new UI strings in English and Spanish (es-419 overlays "Enter").
- Docs: ui-parity.md (selection and transform rows, shortcut counts, header, revision row),
mcp.md (the new commands).
|
||
|
|
8d5f8a0978 |
M1: keyboard shortcut parity suite and bindings
- A shortcut parity table (`crates/engine/src/shortcut_parity_tests.rs`): one row per shortcut in
the public documentation (help-centre feature articles: keyboard layouts, text properties,
alignment, frames, components, prototypes, plugins, keyboard navigation), with the macOS and the
Windows/Linux keys, what handles it here (keymap command, held key/gesture listed in the panel,
vector edit tool key, or missing), and a note in our own words. Rows the documentation is silent
on pin today's keys, so every default binding is in the table.
- 129 documented rows: 126 bound (110 before), 1 moved off a documented conflict (Run last
plugin on Windows: the docs also give Ctrl+Alt+P to pixel preview; the plugin keeps
Ctrl+Alt+Shift+P), 2 missing (moving the rotation origin with Alt+R; placing an object with
Enter after choosing a tool from the toolbar), 2 kept by a platform (Ctrl+Space with some CJK
input methods; F6 in browsers). 48 undocumented bindings pinned.
- Tests: every row fires its command on both platforms and sits in the right shortcuts-panel tab;
every default binding has a row; no binding uses a chord the OS keeps (Cmd+H/M/Q/Tab/Space,
Ctrl+Space, screenshot keys on macOS; Alt+Space/Tab/F4, Ctrl+Esc, Ctrl+Alt+Delete, Linux
workspace and VT keys); macOS keycaps show Cmd/Option glyphs only and Windows/Linux keycaps
Ctrl/Alt words only; vector edit tool keys; counts pinned for the docs.
- Newly bound: A (Frame tool), Shift+' (pixel grid; Cmd+' kept, the articles disagree),
Alt+Cmd+\ / Ctrl+Alt+\ (others' cursors in a live session), Ctrl+F6 (toolbar focus on
Windows/Linux), Shift+Cmd+U (link text; Cmd+K kept), Cmd/Ctrl+] and [ (list levels while
editing), Cmd/Ctrl+Backspace (ungroup), Alt+Space / Ctrl+Space (keyboard box selection; F8 kept,
Cmd/Ctrl+arrows draw the box out), Ctrl+Shift+A / Ctrl+Alt+Shift+A (suggest auto layout),
Ctrl+Alt+Cmd+K / Ctrl+Alt+Shift+K (go to main component).
- New commands: `view.multiplayerCursors {on?}` (View menu item, menu breadth 207/207),
`view.pan {dx, dy, big?}` (arrows pan the view by 20 screen px, 200 with Shift, when nothing is
selected; the canvas and MCP `key` both use it), `text.listIndent` / `text.listOutdent`.
- Behaviour: Control held while dragging suspends snapping on macOS too (Cmd/Ctrl already did);
the presenter takes Space and N for the next frame, C for comments (Shift+C kept) and F for full
screen, after the prototype's own key triggers; the paint bucket's tooltip shows Shift+B (B still
works).
- Keyboard layouts: shortcuts follow the character typed, named as on a US keyboard. A symbol
another layout types with Shift (`/` is Shift+7 on German keyboards) now counts without the
Shift (canvas, text editing and F6 navigation share one lookup). Characters egui has no key for
already arrive as the key at that position.
- Keyboard shortcuts panel: new rows for panning with the arrows, suspending snapping, moving along
the toolbar, and the presentation keys, in the tabs the documentation groups them in (en/es
strings).
- Docs: ui-parity.md gets a Keyboard shortcuts section with the counts; target-app-parity.md,
ROADMAP.md, gaps.md (gap 4 landed list), mcp.md, parity-checklist.md (regenerated).
|
||
|
|
1f6e75e0b6 |
M8: cursor chat and spotlight in live sessions
Two presence features for live sessions, with no accounts needed:
- **Cursor chat.** In a live session, `/` on the canvas opens a bubble in your colour at your pointer. What you type goes to everyone as you type, and others see it next to your cursor in place of your name tag. Enter (or clicking away) sends it and closes the box. On every screen it fades a few seconds after its last change (5 s, then a 1 s fade; with reduced motion it vanishes without fading). Escape takes it back at once and doesn't deselect. A box left open without typing closes by itself. Limits: 100 characters, control characters become spaces. Agents use `collab.cursorChat {text}`.
- **Spotlight.** Your avatar (top of the right panel) now has a menu with Spotlight me / Stop spotlighting (`collab.spotlight {on?}`). Everyone else follows your view and page, using the existing follow. People who join later are told by the server and follow too. Anyone who pans, zooms or presses Escape stops following, and a canvas banner ("Hana is spotlighting", Follow) lets them follow again. A newer spotlight takes over the old one. The presenter leaving or stopping ends it. The presenter sees a banner with Stop. After a reconnect, your spotlight goes out again.
- **Protocol: signals.** New `ClientMsg::Signal {kind, text}` and `ServerMsg::Signal {client_id, signal}` carry ephemeral presence. Signals never go into the document, the log or a saved state; the server keeps only who is spotlighting, for newcomers. The server cleans each signal: unknown kinds, malformed spotlights, a client's `ready` and anything over 1 KiB are dropped, and chat is capped and has control characters replaced. The app checks what arrives again before showing it. The hub rate-limits signals per connection (20 a second, bursts of 40) and drops the excess without closing the connection.
- **Compatibility, both directions.** A client that reads signals announces it with the word `signals` in its hello's `client_version`, and the server sends signals only to those clients, so older clients never see a message they can't decode. A server that takes signals answers with a `ready` signal after the welcome, and the client sends signals only after that, so older servers never get one; the app says the host runs an older version. Signals are kind plus text, so clients skip kinds added later and servers drop them.
- Engine: `Live` keeps `signals`, `chats`, `chat`, `spotlight` and `spotlighting`, all reported by `collab.peers`. UI: chat bubbles, the chat box, the spotlight banner (the existing canvas banner widget), the avatar menu and `ui.inspect` fields. Keymap: `/` maps to `collab.cursorChat` (Essential). Spanish labels for both commands, plus en/es UI strings.
- Docs: collab-protocol (messages, compatibility, Presence, security limits, agents), mcp.md, collaboration-parity rows, target-app-parity, ROADMAP, gaps, ui-parity.
|
||
|
|
2a54adb762 |
M4: shapes and vector behaviour suite and fixes
**A shapes and vector behaviour suite written from the public documentation, and fixes for every divergence it found.** Each documented rule (drawing with Shift and Alt, corner radius and which layers take one, point counts and star ratios, arcs, stroke joins and the miter angle, caps per end, boolean results and the look they take, flatten and outline stroke; entering and leaving vector edit mode, the pen, selecting points and segments, deleting vs. delete and heal, handle mirroring, the bend, paint bucket, cut, join and eraser tools, nudging and undo steps, what converting a shape keeps) is a table row or a step with what the rule implies and a one-line derivation; where the documentation is silent the row says so and pins today's behaviour.
**Fixes**
- **Rounded vector points were stored but never drawn**: a point's corner radius (edit mode's Corner radius field, `vector.edit.cornerRadius`) now rounds the corner between two straight segments on the canvas, in hit testing, booleans and every export (`VectorNetwork::rounded`, used by the fill and stroke paths); the radius shrinks to fit half of each segment, like a polygon's corners. A vector layer's own corner radius rounds every point. The points stay where they are in edit mode.
- **A boolean group's look follows its operation**: union, intersect and exclude take the top layer's fills, strokes and effects, subtract the bottom layer's (it was always the bottom one).
- **Alt draws a line or an arrow from its centre**, like the other shapes (it was ignored).
- **Stars keep to 3–60 points** (they went to 120; polygons keep 120, the documentation is silent); a junk `pointCount` is refused instead of becoming 5.
- **Delete and heal recomputes the curve**: the healed segment keeps its neighbours' tangents and passes through where the deleted point was (it reused the neighbours' handles, so healing over a circle cut deep inside it). Corners between straight segments heal straight, as before.
- **Shift-click on a selected point at a corner of the points' transform box takes it out of the selection** (it started a scale); a Shift-drag there still scales in proportion.
- **Outline stroke** turns the stroke's colour style into the outline's fill style, and the outline keeps the layer's opacity, blend, effects, constraints and visibility (they were lost).
- **The stroke panel's Miter angle shows degrees** (it showed the ratio 4 under that name; the default is ~28.96°); `node.set {miterAngle}` sets it and `get_node` reports a non-default join or angle.
- **Individual corners only for rectangles and frames**: polygons, stars and vectors have one radius (the toggle showed for them and set corners that weren't drawn).
- **Option/Alt+Shift+F flattens** (Cmd/Ctrl+E still does).
- **MCP `key` in vector edit mode reaches the edit tools first**, as the canvas does: tool letters, arrows, Delete / Shift+Delete, Cmd/Ctrl+J, Enter (Delete removed the whole layer; `X` was unknown).
**Missing documented behaviour, added**
- **Corner radius and star ratio handles on the canvas**: a handle inside each corner of a selected rectangle or frame, one inside a polygon's or star's top point, a ratio handle on a star's first inner point (`shape_handles.rs`). Dragging along a corner's bisector sets the radius of every corner (Alt: only that corner of a rectangle or frame, documentation silent), stopping where the corners meet; the ratio handle sets the inner radius. A readout shows the value while dragging; the handles hide under 48 px on screen, on locked layers, with several layers selected and in edit mode. One undo step per drag; `inspect_ui` lists them under `tool.shapeHandles`.
- **Count and Ratio fields** in the Design panel for polygons and stars (there was no way to change them in the UI).
- **Cut tool drags**: dragging across paths cuts them where the line crosses; parts that come apart move to layers of their own, each keeping its piece of the fill (`vector.edit.cut {from, to}`). A click still splits at a point or segment.
- **Paint bucket drags**: a drag fills or unfills every area it crosses the way the first one went (`vector.edit.paint {points}`), one undo step.
New strings go through `tr(...)` (the new fields reuse the existing Count / Ratio / Radius / Star / Polygon entries in `en.toml` and `es.toml`); the new fields have accessible names (an icon lead with its label). Each gesture and edit stays one undo step.
|
||
|
|
cc30a261e7 |
M1: layers panel keyboard (box selection, F2 rename, context menu) and stroke styles (#135)
## What
**Layers panel from the keyboard** (two of the three open items of area 3, plus long-list QA)
- **F2 renames** the selected layer (a second key for `object.rename`, next to Cmd/Ctrl+R) from the canvas, after a click on a row, or from a layer row that has the keyboard (F6 mode, where the canvas shortcuts are off, so the layers branch of `a11y::keys` runs it). When the rename ends the row gets the keyboard back (`a11y::refocus_row`); Escape in the field cancels and stays in the panel instead of jumping to the canvas.
- **Shift+F10 opens the context menu** (`view.contextMenu`, a new command that only asks the UI): at the focused layer row when the layers panel has the keyboard (F6 or a clicked row), else on the canvas just below the selection (the middle of the canvas with nothing selected). The menu takes the keyboard like the other menus opened from the keyboard (`a11y::popup`), and Escape gives it back to the row or the canvas. The Menu key can't be supported: egui 0.36 has no key for it.
- **Keyboard box selection** (`select.area`, F8): for keyboard-only users, a selection box appears around the selection (or in the middle of the view); the arrows move it, Shift+arrows move its far corner (10 screen pixels a press), Enter selects what it covers exactly like a marquee (Shift+Enter toggles, Cmd/Ctrl+Enter takes nested layers), Escape or a click drops it. A toast and the canvas's screen-reader description say the box's size and the keys. With a box (`{x, y, width, height, add?, deep?}`) the command selects directly, for agents. The marquee's rules (top-level layers, frames only when covered, the children of the frame the box starts in, the focus view, Shift toggling) moved into `Session::marquee_within` / `marquee_selection`, shared by the pointer marquee and the command.
- **Long lists (10k layers)**: a test that a layer near the bottom of a 10k-layer list is revealed (parents expanded, list scrolled) and that F6 lands the keyboard on its row and the arrows walk on from it; the UI bench (`perf_ui_frames`) gained rows for expanding every layer, arrow keys down the expanded list, revealing a layer at the bottom and collapsing everything. Release, 10k layers: first frame with every layer expanded 1.22 ms, an arrow key 1.04 ms, reveal 0.30 ms, collapse all 0.37 ms, idle 0.17 ms.
**Stroke styles** (area 6)
- What the public documentation describes for strokes was mostly already in: colour styles apply to strokes from the stroke section's style picker (`style.apply {target: "stroke"}`, linked, following `style.update`, one undo step, `style.detach {which: "stroke"}` keeping the colour), and the stroke weight binds to number variables. A test now covers that path.
- Missing piece, added: **each side's stroke weight binds to a number variable on its own** (`strokeTopWeight`, `strokeRightWeight`, `strokeBottomWeight`, `strokeLeftWeight`): `node.set` keys (setting or binding one side turns on individual strokes with the other sides at the weight), the stroke-weight variable scope, the stroke override group for instances, and REST `boundVariables` import. The Design panel's per-side fields are bindable, two to a row like the corner radii so a variable's name fits.
New strings go through `tr(...)` with `en.toml` / `es.toml` entries, and the two new commands have Spanish labels. Shortcuts are only in `keymap.rs`. Nothing redraws while idle (the box draws only when a key moves it). Selecting from the box leaves no undo step; each stroke edit is one.
## Verified
- **Desktop app** over the control channel (`target/debug/figcraft.exe` from this worktree's target dir, `--control <free port>`, separate `APPDATA`, keys sent as real `ui.input` key events, stopped by PID): F2 on a selected layer opens its name field in the layers panel (screenshot), typing and Enter rename it; F6 to the layers panel, Shift+F10 opens the menu at the Avatar row with Copy focused (screenshot), Escape gives the keyboard back to the row, F2 + typing + Enter renames it and the row keeps the keyboard ("Profile"); Shift+F10 on the canvas opens the menu for the selected frame with Copy focused (screenshot); F8 shows a box around the selected frame, Shift+arrows stretch it over a second layer (screenshot with the hint toast), Enter selects both; a rectangle with the colour style "Brand/Accent" on its stroke and its top weight bound to a number variable (screenshot: the style chip in the stroke section, the red stroke thicker at the top, the per-side field showing the variable).
- **MCP**: `figcraft-cli mcp` (headless): created a paint style, `style.apply {target: "stroke"}`, `object.rename`, bound `strokeLeftWeight` to a variable and ran `select.area` over the layer; `inspect_document` shows the name "Outlined card", `styles.stroke: "S:1"` with the style's colour on the strokes, `strokeSides [2, 2, 2, 6]`, `boundVariables {strokeLeftWeight}`, and one undo step per command.
- **Web**: `cargo xtask wasm` passes (part of the gate).
- **Performance**: release UI bench at 10k layers (numbers above).
- `cargo +1.99.0 xtask ci` passes (fmt, clippy, tests, layers, assets, wasm), after merging #131 and #132.
## Tests
- `ui-egui` `layers_keyboard_tests` (3): F2 from the canvas and from a focused row (the row gets the keyboard back, Escape cancels without leaving the panel); Shift+F10 at a focused row, on the canvas below the selection and after a clicked row, the menu taking and giving back the keyboard; F8: the box around the selection, the screen-reader description, Shift+arrows sizing, Enter selecting, arrows moving it without nudging layers, Escape, the middle of the view with nothing selected, a click dropping it.
- `ui-egui` `perf_tests::a_layer_revealed_at_the_bottom_of_10k_layers_gets_the_keyboard_on_its_row`, plus the new bench rows.
- `engine` `select_area_tests` (2): marquee rules through the command (negative sizes, frames, starting inside a frame, deep, add toggling, empty spots), the UI request with no box, bad boxes refused (the junk sweep covers both new commands too).
- `engine` `stroke_style_tests` (2): a colour style on strokes (link, undo, style edits, detach); stroke weight and per-side weights bound to variables following the frame's mode, `node.set` per-side keys, scopes and override groups.
## Not in this PR
- The Menu (application) key: egui 0.36 doesn't deliver it.
- Enter doesn't rename: on the canvas and a focused row it already selects children / activates the row.
- The keyboard box doesn't pan the view when it is moved off screen (zoom and pan keys still work while it shows).
- Shift+F10 does nothing while a toolbar or properties control has the keyboard.
- Progress docs: area 3 95%/78% → 98%/82%, 2–4 h → 1–2 h; area 6 90%/72% → 95%/75%, 1–3 h → 1–2 h; mainstream depth 67.8% → 68.1%; percentages unchanged at this rounding; full parity ~323–621 h → ~322–618 h (target-app-parity, ROADMAP); ui-parity rows (context menus, keyboard box selection, layers from the keyboard); `docs/mcp.md` documents `object.rename` without a name, `view.contextMenu`, `select.area` and stroke styles and variables.
|
||
|
|
10a9b7b165 |
M8: Windows QA fixes (#131)
Found and fixed in a Windows pass of everyday workflows: unsaved documents are now copied in the background (at most every 10 s) and offered back after a crash or forced quit (file.recover); Ctrl+arrow / Ctrl+Backspace / Ctrl+Delete text motion follows Windows and Linux conventions (keymap::text_motion); number-field scrubbing keeps its value; typing in a Tab-focused field works; default linear gradients and their handles behave; Escape closes dialogs without deselecting; clipped panel hints wrap; the unsaved-changes dialog is translated. |
||
|
|
041b3f4e5d |
M7: WASM plugins (#101)
Sandboxed WebAssembly plugins whose API is the command registry: - figcraft-plugin (new crate, L2): the figcraft-plugin.json manifest (menu commands with parameters, read/write access, command allowlist) and the sandbox: wasmi (pure Rust, no JIT, also in the web build) with fuel metering, a memory cap, no WASI and seven host functions (call, selection, reply_len, reply_read, log, notify, set_output). - Engine: plugin.list / plugin.import / plugin.remove / plugin.run, plugins.runLast (Alt+Cmd+P; Ctrl+Alt+Shift+P on Windows/Linux) and plugins.manage. A run is one undo step (Session::grouped, which command.batch now uses too) and is rolled back when it fails. Files, other documents, the undo history, dialogs and plugins are off limits; read-only plugins can't change the document. Installed plugins live in the preferences; development plugins re-read their module every run. - UI: installed plugins in the Plugins menu, a parameters form drawn from the manifest, Manage plugins, Plugins > Development > Import plugin from manifest. - plugins/: a no_std Rust SDK and two example plugins (Tidy names, Swatch grid), built for wasm32 by `cargo xtask plugins` (part of `cargo xtask wasm`). - Docs: docs/plugins.md (manifest, permissions, limits, ABI, SDK), docs/mcp.md, docs/control-protocol.md, README, ROADMAP, parity. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
c49a72ea88 |
M8: accessibility: screen reader names, keyboard navigation mode (#96)
* M8: accessibility: screen reader names, keyboard navigation mode
- Screen readers: the custom-painted controls report a role, name, value and
state to AccessKit (number and text fields, dropdowns, segmented and toggle
buttons, tools with their keys, layer rows as tree items, pages, tabs, menu
items, the canvas describing the selection), from the shared widget helpers
and a `Tip` trait for ad-hoc icon buttons. Regions are named containers.
- Keyboard navigation: F6 / Shift+F6 (view.nextRegion / view.previousRegion)
cycle the toolbar, layers, canvas and properties; Tab / Shift+Tab move within
a region and wrap; Enter or Space activate; arrows move along the toolbar and
through the layer rows; menus and flyouts opened from the keyboard take it and
give it back; Escape returns to the canvas. A focus ring (new `focus` token)
shows until the next pointer press. Tab on the canvas selects the next layer
again instead of moving egui's focus to the tab strip; Enter that starts a
field edit no longer ends it.
- Reduced motion: pref.reduceMotion and the system setting (Windows
UISettings, macOS, GNOME, prefers-reduced-motion) turn egui's animations off.
- Contrast: light secondary and accent text meet WCAG AA.
- ui.inspect reports focus {region, keyboard, control} and reduceMotion;
ui.set {focusRegion}.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M8: parity report command count after merging main
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M8: accessibility: regression test against nested egui context locks
A debug build's temporary logging called the context inside a context closure and hung; the shipped code never does. Document the rule in a11y.rs and run every accessibility path with the AccessKit tree on, so a nested call fails the test (debug builds panic after 10 s) instead of hanging.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
94aab893b0 |
M7: keyboard shortcut parity (#88)
One keymap (figcraft_engine::keymap) now holds every default shortcut, grouped like the public shortcut reference, with macOS and Windows/Linux keys where they differ and canvas / text-editing contexts. The menus, canvas, control channel, MCP key tool and Keyboard shortcuts panel all resolve keys there; the catalog keeps only the menu tree. - Fixed: Cmd/Ctrl+K made a link on the canvas (now Actions; link only while typing), Shift+1 and Ctrl+Shift+? never matched (key names are normalised), mask / bring to front / send to back / tidy up / layout grids on Windows and Linux, distribute on macOS, outlines (Shift+Cmd+O), pixel grid (Cmd+'), paint bucket B in vector edit mode. - Added: Cmd/Ctrl+/, + and - zoom, Cmd/Ctrl+0, hold Z zoom tool (view.zoomAt), two quick digits for exact opacity (object.opacityKeys), Alt+Shift+U/S/I/E booleans, Alt+/ and Shift+/ remove fill/stroke, pixel preview, Shift+E / Shift+D / Shift+Cmd+\ panel keys, Ctrl+Tab between files (doc.nextTab / doc.previousTab), Shift+I, Shift+Space preview, Home/End frames, Ctrl+C pick colour on macOS. - Keyboard shortcuts panel: docked at the bottom, a tab per group, search, keycaps per platform; tooltips show the platform's keys. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |