Commit Graph
313 Commits
Author SHA1 Message Date
Don Omondi b8ec5f4b23 Type: Alt + numeric-keypad codes type their character on Windows; type.insert takes a code (#5108) 2026-11-07 00:17:15 +07:00
dotCooCoo b8155698b5 Remove temporary test files after failures 2026-11-05 23:25:05 +07:00
Locme a3ec061995 .ai import: 6D Art Pen brushes open live, and Windows reads pen tilt and rotation (#372)
Squashed from #5815 onto main.
2026-11-05 23:25:05 +07:00
dotCooCoo 877b3de3ad Tests: the read_file_head test creates its file exclusively and removes it on failure 2026-11-05 21:18:44 +07:00
dotCooCoo 2cd68fbdcb Actions: loading an action set checks its size before reading it
Load Actions… read the whole file before checking the 8 MiB limit, so
picking a very large file could take a lot of memory before it was
refused. A new read_head service reads at most a given number of bytes;
the desktop app provides it, and loading an action set reads one byte
past 8 MiB with it. A larger file is refused without reading the rest.
Hosts without the service keep reading the whole file. Automation
roots apply to read_head as they do to read.
2026-11-05 20:55:58 +07:00
KnottyDyes c9588457b9 Export: raster output defaults to the document resolution 2026-11-05 19:02:23 +07:00
FredAppsandClaude Opus 5.5 c1f7e4ed6d #1280: PathData::bounds closes a NaN anchor as kurbo does; the perf row names what it times
Follow-up to CodeRabbit's review of #5778.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 17:17:36 +07:00
FredAppsandClaude Opus 5.5 0a8566e1b0 perf: the import comes first
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 16:49:06 +07:00
a33d83d84f #1280: perf --deep measures pointing; PathData::bounds builds no path
First slice of https://github.com/storytold/vectorcraft/pull/1281, split
as the review asked. `--deep` gains rows for hovering, a hit test that
misses, marquee, smart guide targets and shift-click on a large document,
so the later slices have a baseline.

PathData::bounds built a kurbo path on every call. It now measures the
segments in place, with the same guards, and a proptest compares the
result bit for bit with what the path gave.

Co-Authored-By: arhammusheer <arhammusheer@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 16:49:06 +07:00
bflatastic 7b608b2dab Control server: close connections gracefully, so a reply is not lost to a reset
A connection ended (a junk line, an idle or stalled peer, the app going away) by dropping its socket. On Windows, a reset can follow and discard replies the peer has not read yet, which made tcp_connection_is_closed_after_junk fail at random in CI with ConnectionReset. serve_socket now shuts the write side down and reads what the peer still sends until it closes, for at most a second and 1 MiB, before dropping the socket. The test goes through serve_socket, the path the app uses.
2026-11-05 07:13:06 +07:00
dotCooCoo 59a3cad1dc Type: Paste keeps a copy the system clipboard refused
When a Copy in the Type tool cannot be written to the system clipboard
(another app holds it open, or a format fails after the clipboard was
emptied), Paste into type or a text field now inserts that copy, with
its style in type, instead of the text the clipboard kept. This covers
Edit > Paste, the paste key, and a paste that arrives while the copy is
still being written. When another app copies something, before or after
the failed write, Paste takes that.

The new SystemClipboard::change_count reports the clipboard's change
count (Windows' sequence number). Where a platform has none, a change
of the clipboard's text marks a new copy.
2026-11-05 07:13:03 +07:00
dotCooCoo 8555ac42c4 UI-1661: Use Windows clipboard ownership for Type paste 2026-11-05 04:11:40 +07:00
dotCooCoo a2947df1fa File dialogs: Open and Place list files with capitalized extensions on Linux
GTK and the desktop portal match a filter's *.ext patterns case-sensitively,
so on Linux and the BSDs files such as image.DIB, photo.PNG or art.Svg did not
show under any Open or Place filter, "All readable files" and "All placeable
files" included. On those systems each open and place filter now also lists
every extension with each letter as a [xX] pair, the form the portal's
FileChooser documentation recommends. Save dialogs and the other platforms
keep the plain extensions.
2026-11-04 22:57:50 +07:00
Don Omondi 609528220b Fix #5160: a slow first frame is waited for, not blamed on the graphics adapter
Squashed from #5635 onto main.
2026-11-04 22:29:32 +07:00
bflatastic 292b385a66 Tests: the font menu and idle-socket tests don't fail on a busy machine
type_font_menu_lists_every_available_family compared two reads of Type › Font while a test running alongside could add a font in between, so the menu was rebuilt with one more family. It now judges a run in which the font generation held still.

idle_expiry_closes_without_writing_a_line took a connection reset as a failure; on Windows the server's close of an idle connection can reach the client as one.
2026-11-04 21:46:50 +07:00
byoungyoonandClaude Opus 5.5 c82a1eb0e8 Fix #5555: Cmd+V pastes once on the web
eframe's web runner reports a Cmd+V press both as the key press and, with the
clipboard's text, as a Paste event; the app pasted on each (egui's window
integration sends only the Paste). The web shell now drops the paste key's
press when a Paste is in the same frame's input; its release stays for
PasteChord, and a Cmd+V with no text on the clipboard keeps its press.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 16:33:24 +07:00
bflatastic 1e3553843d Single instance: the stale-endpoint test connects to no freed port
The test bound a port, freed it and then connected to it as a gone app.
Tests run in parallel, so another test's listener could take that port
meanwhile and accept this connection in place of its own client's,
whose connection was then reset when the listener closed: the cause of
the intermittent tcp_connection_is_closed_after_junk failures on
Windows. The gone app is now a listener that closes without a greeting.
2026-11-04 16:08:43 +07:00
bflatastic 821757acef Control server: the junk-closes test leaves no unread input
On Windows, closing a socket with unread input resets the connection, and
the reset can discard the reply before the client reads it, so the test
failed under load. The request after junk is still covered by the
in-memory tests.
2026-11-04 09:43:38 +07:00
dotCooCoo 04e154510a Edit Original: read PhotoCraft's App Paths entry as UTF-16 on Windows
The lookup ran reg.exe and decoded its output as UTF-8, but reg.exe
writes in the console's code page, so a registered path with characters
outside ASCII came back wrong, and characters outside that code page
came back as "?". The path is now read with RegGetValueW.
2026-11-04 08:12:40 +07:00
Dylan M. Taylor 9b10a40709 fix: Templates folder needs write access; portal saves keep exact file names 2026-11-03 22:17:45 +07:00
Dylan M. Taylor c879ff369b fix(app): keep preference copies, dialog passwords and migrated folders private 2026-11-03 22:17:45 +07:00
Dylan M. Taylor 8e595afdf0 fix(app): open and paste read only regular files 2026-11-03 22:17:45 +07:00
bflatastic f3a9114e55 Edit Original: the PhotoCraft lookup test holds where PhotoCraft is installed 2026-11-03 19:58:28 +07:00
ruchirguitarandClaude Opus 5.5 46f1ddf170 Fix #5236: find PhotoCraft where its Windows installer puts it
Per CodeRabbit on #5327: on Windows Edit Original looked for photocraft.exe
only on the PATH, but PhotoCraft's MSI installs it to ArtCraft\PhotoCraft
under %LOCALAPPDATA%\Programs (per user, the default) or Program Files (for
everyone), neither on the PATH. Look first at the path the installer
registers under App Paths (HKCU, then HKLM; `reg query`, as i18n reads the
locale, which also covers a folder chosen in setup), then those two default
folders, then the PATH. Each candidate must still be an .exe.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 19:57:04 +07:00
ruchirguitarandClaude Opus 5.5 d7aa1c561f Fix #5236: Edit Original is one checkbox, Use System Defaults for Edit Original
Per review on #5327: Preferences › File Handling › Edit Original With (an app
path and Choose…) becomes the checkbox Use System Defaults for Edit Original,
on by default. Off, Edit Original opens a raster image in the app that made
it, PhotoCraft, looked for only where it installs; any other file, or no
PhotoCraft, goes to the system's default app. The preference is a boolean, so
prefs.set over control or MCP can no longer name an executable. The label has
a row in all 26 catalogs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 19:57:04 +07:00
ruchirguitarandClaude Opus 5.5 79c8d1a7e0 Fix #5236: cap the wait for open -a at two seconds
A stalled `open -a` no longer holds the window: Edit Original waits at most
two seconds for its exit status (enough to report an app it can't open),
then leaves the launch running and reaps it on a thread of its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 19:57:04 +07:00
ruchirguitarandClaude Opus 5.5 7c5670b797 Fix #5236: report a failed open -a, and keep Other Application while its path is cleared
macOS: wait for `open -a` (it returns once Launch Services has the
request) and report its error, such as a folder named .app that isn't a
bundle; other platforms still start the editor without waiting. A saved
or typed path also sets the dialog's Other Application choice, so
clearing it to type another keeps the field.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 19:57:04 +07:00
ruchirguitarandClaude Opus 5.5 d11764c117 Fix #5236: Edit Original opens linked files in a chosen app
Preferences › File Handling › Edit Original With picks the app Links
panel › Edit Original opens a linked file in: System Default (as before),
PhotoCraft (found in /Applications or ~/Applications on macOS, on the
PATH elsewhere) or Other Application, with a path field and Choose….

The desktop host's new `open_file_with` service starts only an
application (an .app bundle on macOS, an .exe on Windows, an executable
file elsewhere), with the path as its only argument and never through a
shell. Edit Original still refuses control requests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 19:57:04 +07:00
dotCooCoo b1c98821d6 Clipboard: a bitmap pasted from another app is decoded off the UI thread
Squashed from #4277 onto main.
2026-11-02 14:33:02 +07:00
dotCooCoo e8e09af782 Brushes: Calligraphic and Scatter values follow pen tilt, bearing and rotation
Squashed from #5031 onto main.
2026-11-02 14:33:02 +07:00
dotCooCoo 90179904d8 Actions: the saved current set follows its set when windows merge
When two windows save the preferences, the current set's index and the
list of sets can come from different windows. The index now points at
the same set in the merged list, found by its contents or its name, and
no set is current when the merge cannot tell which set it was. Recording
into, renaming or deleting the current set, or deleting a set before it,
does not override a set the other window made current.
2026-11-02 14:32:50 +07:00
bflatastic 51dcb94661 Fix #4967: a drawing tablet's pen draws on a Wayland session by default
On a Wayland session Linux Display Server's default, Automatic, opened
the window on native Wayland. winit 0.30 doesn't bind the Wayland tablet
protocol, and KWin (since Plasma 6.3) and Mutter turn no tablet input
into pointer input, so the pen moved only the compositor's cursor and
the app got no motion or presses. Only X11 (XWayland) When a Pen Is
Attached, which users had to find and choose, sent the window through
XWayland, where the pen works as a mouse with its pressure.

Automatic now opens the window through XWayland when a pen is attached
as the app starts, as PhotoCraft does by default.
VECTORCRAFT_NATIVE_WAYLAND=1 keeps the window on Wayland, like
PhotoCraft's PHOTOCRAFT_NATIVE_WAYLAND. The X11 choice is unchanged.
2026-11-01 05:32:30 +07:00
dotCooCoo cbde11d593 Fix #2105: the window asks the GPU for no more than it offers
eframe's default device descriptor asks for wgpu's default limits (WebGL2
defaults on OpenGL) with 8192 px textures, whatever the adapter offers. A GPU
short of any one of them, such as a Raspberry Pi 5's V3D 7.1 (15 inter-stage
shader variables on Vulkan, 4096 px textures on OpenGL), failed to create a
device, and the window fell back to the software rasterizer.

The device now asks for eframe's limits lowered to the adapter's wherever the
adapter offers less, and for the adapter's own texture side, which still
raises it above 8192 px when the adapter allows more (#1459).
2026-11-01 04:53:40 +07:00
dotCooCoo dfd07552c8 Cursors: Caps Lock gives the drawing tools the crosshair
While Caps Lock is on, the tools whose pointer General > Use Precise
Cursors changes show the crosshair, and the saved preference does not
change. The desktop app reads the lock state on macOS, Windows and
Linux X11 and passes each change to the new view.capsLock command. A
change to view.capsLock or usePreciseCursors through app.run refreshes
the canvas pointer. The view.preciseCursors command is labeled Use
Precise Cursors, so command search finds it by the preference's name.
2026-11-01 01:59:54 +07:00
bflatastic 9b17e38a5c Fix #4892: a file opened while VectorCraft runs opens in that window 2026-10-31 23:11:00 +07:00
bflatastic da9eaaacf8 Windows cores: test the affinity mask in-process
A test that spawned the test binary with `start /affinity 1` ran while the control server's socket tests held connections; the child inherited them and reset one (idle_expiry_closes_without_writing_a_line failed every full run). The one-core worker count stays covered by `automatic_threads`.
2026-10-31 19:43:13 +07:00
dotCooCoo 4e785b40ce Part of #4516: Windows renders start workers only for the cores the process may use
The renderer sized its worker pool from available_parallelism, which on
Windows counts every processor in the machine, also when the process's
affinity mask allows fewer. A process allowed one core started 8 workers,
and the renderer's flush spun on that core while the workers got no time,
so renders crawled or stalled.

The desktop app and vectorcraft-cli now read the affinity mask at startup
(src/windows_cores.rs, shared) and pass the count to the renderer with
vectorcraft_render::set_usable_cores. With one usable core the renderer
renders on the calling thread.
2026-10-31 18:44:55 +07:00
dotCooCoo de72fc3998 Clipboard: TEXT_ONLY is built on Linux only
The constant is used only by the Linux Wayland arms, so on macOS it is
dead code and `cargo clippy -p vectorcraft --all-targets -- -D warnings`
fails with "constant `TEXT_ONLY` is never used". Gate it with
`#[cfg(target_os = "linux")]`, as the `Wayland` variant beside it is.
2026-10-31 06:28:35 +07:00
dotCooCoo 6a034c77c3 Transform tools: backquote drag transforms only the pattern
Holding backquote while dragging with the Selection tool (move, bounding-box
scale or rotate) or the Rotate, Reflect, Scale or Shear tool applies the drag
to the selected objects' pattern fills and strokes and leaves their geometry
in place. The key state at release decides the result: the key may be pressed
after the mouse button goes down, and releasing it before the button comes up
commits an ordinary transform. Objects without pattern paint do not move.

PointerEvent gains a `backquote` flag (false when absent). The desktop canvas
reads it from the held key on every pointer event, and `ui.pointer` and the
`pointer_gesture` tool accept it per event or as a default for the gesture.
2026-10-31 03:34:55 +07:00
wjrichardsandClaude Opus 5.5 9b3b662f5d Linux clipboard: the read worker holds the window as long as its clipboard
The worker's closure named loader.cb, and a closure moves only the fields it names, so
the worker's window clone was dropped when TextClipboard::new returned. A worker held
by an owner that never answers could then outlive the window. The closure now calls
Loader::load, which moves the whole Loader.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 17:12:17 +07:00
wjrichardsandClaude Opus 5.5 7a86f94282 Linux clipboard: a Wayland core-protocol read waits 2 s at most
smithay-clipboard's load() waits for the selection owner's data with no limit, and
Paste and the "is the clipboard still ours" check read on the UI thread, so an owner
that never sends its data froze the window. Reads now run on a worker thread with a
second clipboard on the window's connection and are waited for 2 s at most. While a
read that timed out is unanswered, later reads return at once instead of queueing
behind it, and its late answer is dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 17:12:17 +07:00
wjrichardsandClaude Opus 5.5 dd934dfd4e Linux clipboard: copy text through the core Wayland protocol
Where arboard can't open (GNOME has no data-control protocol, and the Flatpak on a
Wayland session has no X11), Copy and Paste use the compositor's core data-device
protocol on the window's own connection through smithay-clipboard, already in the
build through egui-winit: text only, a copy's text or its SVG markup. A bitmap-only
copy reports that this clipboard carries text only and leaves it as it was.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 17:12:17 +07:00
Dylan M. Taylor a24d811c7b Update prompt shows the app icon and accent color instead of a letter (#4671)
UPDATE_APP (native builds; craft-update is a native-only dependency) carries our 128 px icon and accent (#e8573f, its colour in ArtCraft Launcher). The updater, the self-update
helper and craft-update's startup prompt all get them through crate::UPDATE_APP; the
prompt draws the icon, the accent and "by ArtCraft" (craft-update's default developer).

craft-update is pinned to storytold/craft-libs main at 1065653e7 (#434).
Clippy fixes for Rust 1.99 in untouched code (same logic):
- crates/svg/src/import/fx.rs
- crates/tools/src/text/tool.rs
2026-10-30 01:27:35 -04:00
Don Omondi 9095cdf926 Fix #4625: UI settings survive a relaunch and a second window
Two ways the next launch came back with settings from before:

- The workspace, panels, toolbars and other settings kept only in the UI
  state were written only on quitting (Preferences OK already saved at
  once, #1747). A session ended any other way lost them. The app now
  saves within 2 seconds of a change, and only when something changed.
- Each launch is its own process, so two windows are common (opening a
  second file from the file manager). A window wrote back the whole
  state it started from when it quit, so the one quitting last put back
  the settings it read at its start, even with nothing changed in it.
  A window now saves only what it changed since it last read or saved,
  merged into the file as it is: each UI field, and each engine
  preference on its own. A setting both windows changed takes the last
  save.

The status line, open dialog and other fields a start resets neither
trigger a save nor replace another window's.
2026-10-30 04:47:12 +07:00
wjrichardsandClaude Opus 5.5 c988af83b5 Linux clipboard: stop the retry storm
The Flatpak on GNOME has no X11 (fallback-x11 on a Wayland session) and GNOME offers
no data-control protocol, so arboard::Clipboard::new() failed every time. The handle
was kept only on success, so the background Paste check reconnected to the compositor
and logged a warning every 250 ms: 267k warnings in 18 h, the 16 MB log full in 3 h.

- A clipboard that can't be opened is tried again after 1 s, doubling to a minute.
  A Copy or Paste of the user's retries at most once a second.
- The outage is logged once (WARN) and its end once (INFO), across the UI's and the
  background check's handles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 02:19:33 +07:00
VoiceLessQ ac3982c68a Windows: keep OpenGL for a GPU without Direct3D 12 and for a named adapter (#4150)
Follow-up to #4600, from its review. A start left OpenGL out whenever
Direct3D 12 listed an adapter, and WARP, its software adapter, is
always listed. So a GPU without a Direct3D 12 driver, which drew
through OpenGL before, now drew on the CPU. An adapter that
WGPU_ADAPTER_NAME named was left out too when it was an OpenGL one.

OpenGL is now listed when Direct3D 12 lists no hardware adapter and
when WGPU_ADAPTER_NAME is set. The Direct3D 12 check takes the shader
compiler the window does (WGPU_DX12_COMPILER, else FXC).
2026-10-29 23:14:58 +07:00
VoiceLessQ 2f9e72b419 Fix #4150: Windows doesn't start OpenGL unless a start needs it
wgpu's OpenGL backend on Windows keeps a hidden window on a helper
thread that pumps no messages (gfx-rs/wgpu#10504). Windows hands an
input-language change to every UI thread of the process and waits for
each, so switching the keyboard to another layout hung the whole app,
even while it drew with Direct3D 12. OpenGL was started on every
Windows start, as the fallback.

A Windows start now lists Direct3D 12 alone. OpenGL is still listed
where it is the fallback: after a start crashed on an adapter or while
listing them (#2243), after a restart that left an adapter out, and when
Direct3D 12 lists no adapter at all (Windows 7). WGPU_BACKEND still
picks the backends as before.
2026-10-29 21:50:02 +07:00
G.J.R. Timmer 11f19da6dd Web build: show the app icon as the tab and home-screen icon
index.html had no icon link, so browsers requested /favicon.ico and got a 404. Trunk now copies the existing app icon (SVG, 32 px and 256 px PNG) from assets/app-icon into icons/ and index.html links them as icon and apple-touch-icon. No new artwork.
2026-10-29 20:36:09 +07:00
dotCooCoo f55cbf313c Fix #2249: macOS converts the canvas from sRGB to the display's profile
The window's CAMetalLayer had no color space: wgpu 30.0.1 sets it to
nil for an sRGB surface each time it configures the surface, and Core
Animation does not color-match a layer without one. On a wide-gamut
display #FF0000 showed as the display's own red, (255, 0, 0) in a
capture where Preview shows the same sRGB red as (234, 51, 35).

A paint callback that draws nothing runs after egui configures the
surface and before the frame is presented. It sets sRGB on each
window's Metal layer that has no color space and leaves any other color
space as it is. wgpu's own fix (gfx-rs/wgpu#10286) is in no release yet.

Fixes #3161.
2026-10-29 14:36:37 +07:00
dotCooCoo 59711d61f3 macOS: the app builds and passes clippy again after main.rs was split
`cargo build -p vectorcraft` on macOS stops with nine E0433 errors:
app.rs and host_services.rs use `mac_menu`, `mac_window`,
`open_documents`, `gpu`, `mac_save_format`, `mac_tablet` and
`mac_screen_color` without importing them since those items moved out
of main.rs. The two files now import them under the same cfg as the
code that uses them.

`cargo clippy -p vectorcraft --all-targets -- -D warnings` on macOS then
stops at the wildcard import in clipboard.rs's macOS module, which now
names the two items it uses.

Other platforms compile the same code as before.
2026-10-29 14:31:10 +07:00