The test compared the pasted shape's connection sites with the original's
through zip, which stops at the shorter list. It now checks that both have
the same number of sites first.
The test compared each shape's connection sites before and after through
zip, which stops at the shorter list, so a shape that lost sites would still
pass. It now checks the counts first. The case with three nested groups also
checks that the freed shapes are selected.
The test compared the copy's connection sites with the original's through
zip, which stops at the shorter list. It now checks that both have the same
number of sites first.
Found while testing crates/model/src/edit.rs:~505 (change_case_parts, sentence branch)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Found while testing crates/engine/src/cmd/text/caret.rs:60 (delete_dir, forward) with crates/model/src/edit.rs:423 (char_move)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
edit.copy and edit.cut put a shape inside a group on the clipboard with
its box in the group's own space. Pasted, it landed where it would be
drawn without the group's move, stretch, turn or flip: a shape from a
group moved 200 pt to the right pasted 200 pt left of where it was
drawn, and one from a group stretched to twice its width pasted half as
wide.
The copied shape now carries the box it is drawn in on the slide, mapped
out through each group around it as Ungroup maps a freed shape. It
pastes at the slide's top level where it was drawn, offset as any paste
on the same slide is. Cut still deletes the shape from its group.
A turned shape in a group stretched more one way than the other is drawn
skewed, which a box cannot hold; it pastes with the unskewed box Ungroup
gives it. Copying with a text caret is unchanged.
Test: a_shape_copied_from_a_group_pastes_where_it_was_drawn.
arrange.ungroup looked only at the slide's top-level list, so a group
inside another group answered `{"ids": []}`, stayed grouped, and the
selection was emptied.
Such a group's children now take its place among the outer group's
children, with their boxes mapped into the outer group's space by
child_to_parent, as for a top-level group, so they are drawn where they
were. They are selected and returned. A shape freed from a named group
and then from a named group around it is listed once.
Test: ungrouping_a_nested_group_frees_its_shapes_into_the_outer_group.
edit.duplicate took its copies from the slide's top-level list only, so
duplicating a shape inside a group answered `{"ids": []}` and changed
nothing.
A shape inside a group is now copied into that group, after its other
children, and the copy is selected and returned. The duplicate offset is
converted into the group's space, so the copy is drawn as far from the
original on the slide as a top-level copy is, however the groups around
it are moved, stretched or turned. Duplicating a moved copy again
repeats the move (#966), measured on the slide. A shape whose group is
duplicated in the same call is copied only with the group's copy.
Test: duplicating_a_shape_in_a_group_copies_it_into_the_group.
shape.merge worked on the slide's top-level list. Given shapes inside a
group, it merged them from their boxes in the group's own space, left
them in the group and added the result to the slide's top level, so in a
moved or resized group the merged shape was drawn away from the shapes
it came from, which stayed drawn as well.
A call whose shapes include one inside a group is now refused with the
shape's name and a request to ungroup it first, and the slide is left as
it was. Merge Shapes is unavailable while a shape inside a group is
selected; a call that names top-level shapes in `ids` still runs
whatever is selected, as it does when nothing is selected.
Test: a_shape_in_a_group_is_refused.
Found while testing crates/engine/src/cmd/format/paragraph.rs:123-129
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Found while testing crates/engine/src/cmd/shape/appearance.rs:8 (adjust, index via usize_param); same pattern in quick_style at :559
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The web shell queues the page's calls as control requests (the channel the desktop control server uses) and answers them on the next frame. The API is installed in every mode; opening, saving, the file name and the unsaved flag stay with the host messages.
On Windows a folder can be set to tell case apart, and then IN beside the root in passed as inside
it, because names were compared with to_lowercase. Both paths have the spelling the file system
gives them by then, so another spelling of the root still matches with an exact compare. A missing
name keeps the spelling it was given, but it can only match a root that exists, and resolve has
already found that one in its real spelling.
is_device_name also missed COM¹, COM², COM³ and LPT¹, LPT², LPT³, which Windows reserves too.
It also cut the name only at a period, so COM1: and NUL:x were not seen as device names.
About > Contributors still showed the snapshot taken for v0.4.0, because
nothing regenerated contributors/contributors.json before a release.
Opt in to the shared refresh from craft-common-actions: a read-only
credits job regenerates the snapshot for the release commit, and the
shared release workflow installs it before every source build. The
snapshot is not committed back.
wasm32 cannot allocate a Vec of 2 GiB (isize::MAX is one byte less), so
the open check that only rejected a larger file trapped on that boundary.
Refuse 2 GiB as well, and say so in the failure the host receives.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Show: Presenter View only with a second display
With one display the presenter window opened on the same screen as the show. Under Hyprland's
focus-follows-mouse, whichever window the pointer was over took full screen, and the hidden one
stalled every frame: the show lagged, dropped key presses or froze.
One display now shows only the slides, as in PowerPoint. The host counts displays through winit's
monitor list when a show starts; hosts that can't tell keep Presenter View.
* Show: count displays by MonitorHandle
Wayland lists one output twice under the same wl_output id, so comparing handles counts it once, and two separate displays with the same name and geometry still count as two.
* Show: docs for Presenter View with a second display
slideshow-parity and hardware-parity note that on Linux one display shows only the slides, with status lines and revision history; ROADMAP progress log. display_count collects into a BTreeSet (MonitorHandle's Ord matches its Eq).
* Show: docs state the one-display rule exactly
Presenter View stays off when winit lists exactly one display; if it lists none, the setting is kept. The host callback covers Unix other than macOS, so the docs say Linux and the BSDs.
* Apply review fixes
- docs/slideshow-parity.md
---------
Co-authored-by: aiongg <67543387+aiongg@users.noreply.github.com>
The transports, the message types and the origin checks come from the shared crate (storytold/craft-libs#420). `data-host="page"` on the canvas now hosts DeckCraft in a page's own document, as in the other apps; the parent bridge keeps its behaviour.