Found while testing apps/vectorcraft/src/gpu.rs:313 (pci_ids)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The type parity row no longer says the NumLock-on path was tried on
Windows: neither path has been run on a Windows machine yet. The comment
on the NumLock-off egui keys says Clear comes as the top-row 5, and the
test comment names the keys 0151 and 0234 actually use.
Follow-up to #5939. With NumLock off the keypad sends Insert, End, arrows,
Clear, Home and Page Up/Down; Windows still composes the code from them,
but the app also moved the caret, so the character landed elsewhere. With
Alt held, those keys without the extended-key flag now count as digits and
are taken out of the frame; the separate navigation keys keep their meaning.
File › Open and File › Place read HEIC and HEIF still images (.heic,
.heif) through heic-rs 0.1.4, the pure-Rust decoder photocraft-heif
uses, and store the pixels as PNG. Single pictures and grids of tiles,
8 to 10 bits, with or without an alpha image, are read with the
container's rotation, mirror and crop applied. A grid without a color
box of its own takes its tiles'. When neither the image nor its tiles
have an nclx box, the HEVC stream's video signal type gives the
samples' matrix and range. An RGB ICC profile, or Display P3 with the
sRGB curve named by an nclx box or, without a color box, by the
stream, is converted to sRGB. The EXIF resolution tags size the image;
without them it is 72 ppi. When the container's rotations turn the
image an odd number of quarter turns, the resolution's axes swap with
the pixels'.
Image sequences, images that aren't HEVC-coded, grids of cropped tiles
and images past the pixel allowance are refused with a message before
the pixels are decoded. The allowance counts each coded picture at the
size its sequence parameter sets give it.
The decoder is in the engine's `heif` feature, which is on by default
and listed by the desktop, CLI and web apps. A build without it still
recognizes the files and refuses them with a message.
On Linux, when the desktop draws the window's frame (KDE, Sway,
Hyprland, COSMIC and X11 sessions), the app bar painted the brand mark
under the desktop's title bar unless System Title Bar was on. The mark
followed the live preference instead of the frame the window has.
The desktop app now records the frame it opened the window with
(VectorcraftApp::desktop_frame), and system_title_bar reads it. A
change to System Title Bar during a session no longer shows or hides
the mark; it changes with the frame at the next start. Embedders
without a window of their own keep the mark.
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.
+[NSSavePanel savePanel] returned nil inside VectorCraft on macOS 15.7 and
objc2 panicked; build the panel with init (and Option fallbacks) so Save As
shows again, and do not hand a refused panel to rfd.
Co-authored-by: Cursor <cursoragent@cursor.com>
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.