The readout was a moving average of 1/dt over any two consecutive frames.
An idle window wakes now and then (a timer, a control request, a repaint
after input) and each wake-up draws two frames about 4 ms apart, the second
one not waiting for vsync because the swapchain has a free image. Every
wake-up pushed the average up by 0.1 x 240 and the idle gap pulled it down
by only 0.1 x 2, so at rest it climbed past the display's refresh rate
(105 fps on a 30 Hz monitor that was drawing 4 frames a second). As soon as
the pointer moved and frames became vsync-paced, it fell back to the real
rate, which looked like a slowdown.
- frame_rate: frames counted over the last second of continuous drawing.
A gap more than 100 ms longer than the previous frame's own work means
the window was idle and starts a new count. The readout changes only
after half a second of drawing, so a wake-up's frames keep the last
rate. Slow frames still read slow, because their gap is their own work.
- chrome: "– fps" until the window has drawn continuously, instead of 0.
Measured on an Apple M4 driving a 30 Hz 4K display: drawing continuously
reads 30; at rest with a wake-up every 0.5 s it stays 30 (before: 45 -> 105).
In a clip group or a clipping layer the bottom child is the clipping path.
Object › Arrange moved the selected children with no regard for that slot:
Send to Back (and Send Backward from just above it) put the art under the
clipping path, so the art became the clipping path, lost its role as art,
and the group drew nothing. Bring Forward / to Front on the clipping path
itself would likewise have made art the clip. Layers-panel moves already
keep the clipping path at the bottom (layer.rs, `index.max(1)`); arrange
didn't.
- object.rs `arrange`: in a parent that clips, the clipping path is left
where it is and the art is arranged within the slots above it (Back goes
to just above the clipping path, Backward stops there).
- tests: Back, Backward, several objects, and Forward/Front on the
clipping path in a clip group (with a render check); Send to Back in a
clipping layer.
Include guide visibility in effect raster cache inputs, skip hidden guide paths in outline members, and clear selected guide paths when hiding guides. Add regression coverage for cached effects and selection overlays.\n\nThe cached-effect regression test is adapted from the overlapping #3264 implementation.\n\nCo-authored-by: Mahtab <smmalam9@gmail.com>
Address CodeRabbit review on #3307: the new `Seq::Erase` arm discarded
the `tool_key` Result, dropping an erase error silently, whereas the
old `route_type_input`/`text_key` path reported it via `app.status`.
Mirror that behaviour so a failed Backspace/Delete still surfaces in
the status bar. Behaviour otherwise unchanged; fmt/clippy clean and the
three IME tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
craft-docpath follows a stored path only on this computer or on the
share the document is on, but its check is lexical and cannot see
mapped drives. fileio::document_path therefore followed a link stored
as Y:\img\logo.png, where Y: is mapped to \\server\other, from any
document: opening the document, links.check, Link Info and the
linked-file watch read or stamped the file on that server.
On Windows, document_path now reads the final path of the root folder
of a link's drive (std::fs::canonicalize). Windows opens that root
folder on the drive's server to answer, so the check still contacts
the server once per lookup; no file on it is read, stamped or watched.
When the final path names a share, the link is checked against the
document's path on its share, and a link on another share is refused
as on another computer, as a link stored with that share's own path
is. Links on this computer's drives and on the document's share are
followed as before, and a link on the document's own drive is not
looked up. A document opened as \\?\Z:\... is looked up on its share
as Z:\... is. Other systems are unchanged.
Docs: docs/mcp.md, docs/target-app-parity.md.
Preview in Browser wrote each preview into `VectorCraft Save for Web`,
and before each preview it removed every file and folder there that
was an hour old or older. Where the temporary folder is the shared
/tmp, another user could make that folder first, writable by everyone,
and the app wrote its previews inside it, in folders that user could
read and replace. Where the system follows other users' links in /tmp,
the folder could also be a link, and the app removed the old entries
of the folder it named.
A preview now uses that folder only when it is a folder and not a
link, and on Linux, macOS and FreeBSD only when the current user owns
it and no other user can write to it. On those systems the folder and
each preview's folder are made with mode 0700. The cleanup removes
only preview folders, on those systems only those the current user
owns. When the shared folder can't be used, the preview goes to a new
folder in the temporary folder. A later preview removes such folders
once they are an hour old.
Docs: docs/target-app-parity.md.
Hook IMEs recompose by injecting Backspace key events and VK_PACKET
characters, which egui surfaces as interleaved Event::Text and
Event::Key(Backspace) in one frame. type_text handled text/IME in one
pass and left Backspace to a later pass, so within a frame all text was
inserted before any delete and the diacritics were eaten (tiếng -> tieng).
Take Text, IME and plain Backspace/Delete in a single event-ordered pass,
removing handled keys so route_type_input does not repeat them. Modified
keys and composing frames are unchanged. Adds two canvas IME tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The version 7 .ai writer wrote the %_Bs color stops of every gradient
from ramp point 100 down to 0. The Adobe Illustrator File Format
Specification version 7.0 lists a linear gradient's stops in that order
(section 1.4.3). Its radial definition, "White & Purple Radial", lists
them from 10 to 100 (section 3.5.2). As reported on #2807, the
reference app reads both stops of #2687's radial gradient, written from
100 down to 0, at ramp point 100 and draws the circle in #1b2a6b. It
reads a radial gradient with three stops written that way in reverse
order.
The writer now lists a radial gradient's stops from the first to the
last. Each stop is written with its own midpoint to the next stop, and
section 3.5.2 says the last stop's midpoint is ignored. Linear
gradients are unchanged.
The Offset Path dialog remembers the values it was last confirmed
with (#1152), but the Preview checkbox was not among the kept
fields, so every open started unchecked and Preview had to be
ticked manually each time. Preview now rides along with offset,
joins and miterLimit in the fields kept on OK, as Simplify's
retained fields already keep theirs.
Unchanged: a very first open (nothing kept yet) still starts
unchecked, and Cancel keeps nothing.
Adds a regression test beside offset_path_remembers_its_last_values:
open, tick Preview, confirm, reopen - the checkbox is still on.
Verified by reading the flow on current main (menus.rs opens on
app.ui.offset_path when it is an object, d.bool("preview") defaults
to false when the field is absent, form::preview_result ticks the
checkbox into d.fields) and by tracing the existing #1152 test;
no Rust toolchain on this machine, so the test was not executed
here - saying so plainly for the reviewer.
Co-authored-by: rahul05ranjan <11793455+rahul05ranjan@users.noreply.github.com>
The desktop app reads the User Defined swatch and graphic style
libraries while it creates its window: set_user_dir runs
Libraries::rescan, which read and parsed every file in the folder
outside the panic guard that commands have. A parser bug that panicked
on one file stopped the app before its window appeared, at every
launch while the file stayed in the folder.
rescan now reads and parses each file inside guard::catch_panic and
skips a file whose reading panics, as it skips one that can't be read.
The other libraries are listed.
Found while testing crates/color/src/palette_io.rs:373
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Found while testing crates/tools/src/xform/measure.rs:20 (shown at :66, reported at :80)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Found while testing crates/doc/src/xml_id.rs:80 (strip_unique), triggered by from_object_name at :32
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Adds .coderabbit.yaml so CodeRabbit no longer reviews or comments on PRs opened by dependabot[bot].
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>