request, execute, commands and screenshot move onto the shared crate (storytold/craft-libs#413): a timeout on every call, the screenshot's PNG also in data, and docInspect and uiInspect beside them. A call the page gave up on gives up its request if the app has not started it, so the app skips it. openFile, captureDownloads, files, readFile and inspect stay VectorCraft's own, on the same object.
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.
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>
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.
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.
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.
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.
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>
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.
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.
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.
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>
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>
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>
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>
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>
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.
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.
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).
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.
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`.
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.
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.
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.
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>
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>
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>
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
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.
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>
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).
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.
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.
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.