with_native_host draws native windows as their own egui frames with a
desktop rect, focus, close requests and the commands they receive. Tests
cover each way into a native window, closing, stable ids, place and
monitor, restore, the bar drag, hiding and forwarded keys.
With native-windows on a backend with viewports, each floating window
whose panels allow it is native: in-window floats open over their place
in the app window, and restored windows go back to their monitor or onto
the main window's. Windows stay on top and hide while the app is
inactive. A thin bar with title and close replaces the OS title bar and
moves the window; closing the window closes its panels. Keys a native
window leaves unused come back in DockOutput::forwarded_events, and
DockArea::monitors takes the monitor list. Move Panel to New Window goes,
since every float is native.
Floating::id names a window across panel moves: fresh from Floating::new
and on load, unique within a loaded layout, kept by a whole window that
moves and by the receiver of a merge. Floating::monitor and monitor_size
record where a native window is; Action::SetMonitor sets them. Equality
ignores the id.
Floating::stack builds a window from groups stacked top to bottom, so an
app's own stacked layouts migrate without building the split chain. The
four-way handle no longer takes keyboard focus: it has no label or key
action, and the two dividers it joins take arrow keys.
Drags the resize grip of sample()'s floating group: a movable group emits MoveFloating,
one with a protected member emits none. Fails without the surface.rs check (CodeRabbit
review nitpick).
A NaN input gives a NaN fraction, which the active-dimension check dropped, returning a
finite node value. Keep any nonzero fraction, NaN included (CodeRabbit review).
Follow-up to the hostile-input fixes in #196, covering findings that PR does not:
- ISO BMFF metadata: all udta/meta entries share a 4 MiB budget charged before a keys name
is copied, so many ilst items naming one long key cannot multiply it (CodeRabbit CWE-400,
isobmff/demux.rs:939)
- ISO BMFF stbl: reserve only the samples the chunk map can place, fallibly, instead of the
declared stsz count (CodeRabbit CWE-789, isobmff/demux.rs:767)
- Matroska: a header-stripping prefix over 256 bytes is treated as unsupported compression
rather than copied in front of every frame; Track::sample_size no longer allocates the
prefix (CodeRabbit CWE-409, matroska/track.rs:257)
- Matroska muxer: per-track cue flags use a cluster number, so starting a cluster no longer
clears every declared track (CodeRabbit CWE-407, matroska/mux.rs:402)
- craft-ui shown_names resolves label collisions with hash-map counts and a worklist, so a
crafted cascade is linear instead of cubic (CodeRabbit CWE-407, ui/src/printing.rs:32)
- parse_ppd caps group, option and choice labels at 256 characters before each option copies
its group (CodeRabbit CWE-400, print/src/lib.rs:232)
- printers_parsed tracks seen destinations in a HashSet (CodeRabbit CWE-407, lib.rs:417)
- sheets::Reader::rect checks an inline array's length before reading it and borrows direct
objects instead of deep-cloning the inherited MediaBox/CropBox/Rotate for every page
(CodeRabbit CWE-400, print/src/sheets.rs:774)
MoveFloating checked only the panel it named, but changes the rectangle shared by that
panel's whole floating group, so naming a movable sibling moved a panel whose policy forbids
it. The model now refuses the action unless every member is movable, and the floating
window's resize grip no longer emits it for such a group.
CodeRabbit CWE-863, crates/ui/src/docking/model.rs:392
deserialize_sections tried each top-level key against a document holding every key accepted
before it, cloning and deserializing the growing document per key. A config.toml with many
unknown keys took quadratic time (20k keys: >2 minutes, now <1 s). Each key is now checked
against the defaults plus that key, and accepted keys are merged once.
CodeRabbit CWE-407, crates/settings/src/document.rs:47
- Clut::multilinear enumerates corners only for dimensions between two grid points, so
singleton dimensions no longer multiply per-pixel work (CodeRabbit CWE-407, clut.rs:238)
- Transform::from_pipeline finds a sampled curve's flat ends once per curve via
Curve::inverse64_many instead of rescanning the table for each of the 4096 output entries
(CodeRabbit CWE-407, curve.rs:276)
- redact matches secrets in the original text only, so markers are never searched again
(CodeRabbit CWE-400, secret.rs:47)
- render_text/render_value share one MAX_RENDERED_BYTES budget across repeated slots
(CodeRabbit CWE-400, spec/template.rs:122)
- Connection::validate checks model-ID uniqueness with a set instead of a pairwise scan
(CodeRabbit CWE-407, connection.rs:205)
- Disclosure::kinds counts parameters as text, so a request carrying only params still needs
an agreement (CodeRabbit CWE-863, consent.rs:123)
The property test starts with a split floating window beside Info's,
drags tabs and drag bars into, out of and between windows, floats some
with Ctrl/Cmd, and checks after every drop that no panel is lost, every
group in every window keeps its minimum and every divider can travel
both ways.
With side_edge_zones, a drop on a group fills the zone the pointer is in
(a side's trapezoid, or the center rectangle with the tab strip) and
outlines the rect the drop gives what moves. zone_shape gives a zone's
outline; DockDriver::filled_polygon checks it. The dock-and-canvas
arrangement keeps its filled half and insertion lines.
Floating::root replaces the stack of groups. A floating window takes
splits in any direction, tab groups and accordions; its groups split,
join and resize like frames, with the Sizer, four-way handles
(Divider::WindowSplit, Divider::WindowIntersection,
Action::ResizeWindowSplit) and the room check. Tabs and drag bars inside
it show the targets of every window and dock; Undock Panel Group floats
one group of a window that has several. Floating::minimized replaces
per-group minimizing in a window.
Saved layouts keep loading: a stored stack of groups becomes a vertical
split sized by the groups' heights. Placement::Group on a floating anchor
splits its group above or below. A window moved onto a dock keeps its
groups as stacked groups.
Two DockStyle settings, on by default so the dock-and-canvas arrangement
keeps its behaviour. Without float_on_empty_drop, a tab or group released
over no target inside the app window (the gap between groups, a menu bar,
a status bar) cancels the drag; Ctrl/Cmd or a release outside the window
floats it. Without title_bar_docks, dragging a floating window's title bar
only moves the window.
Remove `Profile`. What it switched is now a `DockStyle` setting with the
dock-and-canvas behaviour as the default: `side_edge_zones`,
`new_dock_zones`, `maximize_item`, `close_others_item`,
`command_press_floats`, `float_hint`, and plain bools for
`wheel_selects_tabs` and `middle_click_closes_tabs`. The undock and
Close Panel Group labels go; an app sets `float_label`,
`float_group_label` and `close_group_label` instead.
Remove the size machinery that made frames stick: `DockContent::bare`,
`FixedSlot` and the split that took a parent's or sibling's half beside
a fixed-size frame, and the gap snapping to the nearest frame. A tab
group takes the largest maximum of its panels, so a panel without one
lifts the cap of the group it joins. A dragged divider stores a ratio
whatever mode it was saved with.
Sizes follow the reference rules: a divider resizes only the groups
that touch it and stops at their minimums; a four-way handle where a
divider ends on another moves both (`Divider::Intersection`); a split or
outer-edge drop that would leave a group without `min_pane` is not
offered, and a group too small to halve takes the drop as a tab.
Tests: one per rule, the stuck cases from the FilmCraft map, and a
property test over random drops checking minimums and divider travel.
Add opt-in browser download helpers that own detached links and object URLs, clean up failed or abandoned preparation, and register delayed URL cleanup before issuing a click. Applications retain filename, MIME type, pacing, and user-activation policy.
Include browser lifecycle and failure-path tests, public API documentation, and Sonar test-source classification. All 32 PR checks passed; targeted WASM Clippy and formatting passed in local validation.
* hayro-jbig2: cap long-form referred-to-segment count
A crafted JBIG2 segment header can claim a 29-bit referred-to-segment count;
Vec::with_capacity reserved ~2 GiB from that field before a single
referred-to number was read, and the resulting allocation failure aborts
the process in a way catch_unwind cannot contain (fatal on wasm32, where
memory is capped well below 2 GiB). A ~100 KB Flate-compressed PDF was
enough to reach it inside the 256 MiB stream budget.
Real files refer to a handful of segments, so the count is now capped at
u16::MAX; larger claims fail as InvalidReferredCount before any
retention-flag bytes are consumed. Regression test included.
Updates SOURCE.json's local hash for src/segment.rs and records the patch
in vendor/README.craft-reader.md, following the vendored tree's provenance
practice.
Co-Authored-By: Claude Code <noreply@anthropic.com>
* hayro-jbig2: record git-blob sha256 for patched segment.rs
The previous entry hashed the Windows working-tree checkout (CRLF) of
src/segment.rs instead of the LF content git stores, so every other
manifest entry's convention (git-blob content hash) was broken by this
one file. Re-hash against `git show HEAD:vendor/hayro-jbig2/src/segment.rs`
as the vendored manifests' other 30 entries do.
Co-Authored-By: Claude Code <noreply@anthropic.com>
---------
Co-authored-by: Claude Code <noreply@anthropic.com>
Pin third-party actions to full-length commit SHAs (with version comments)
instead of mutable tags/branches to prevent supply-chain tampering via
retagged releases. Resolves SonarCloud rule githubactions:S7637.
Resolve and validate command-line path arguments before use, enforce HTTPS at
download sinks, and confirm path containment. Hardens local codegen/fetch tools
against path-traversal and command-argument-injection findings.
Calls storytold/craft-common-actions/.github/workflows/sonarcloud.yml@main
with secrets: inherit. Adds SonarCloud badge to README where missing and
removes the redundant sonar-project.properties (config now passed by the
reusable workflow).
While a drag offers a docking target, a hint under the pointer reads
"Hold Ctrl to float" ("Hold ⌘ to float" on macOS), from the
localizable float_hint_label (key dock.floatHint).
A side split replaced the anchor frame in place, so a split beside a
fixed-width frame such as a tool strip took the frame's fixed slot:
the two frames shared its width, and the parent's divider resized both.
The frame now keeps its size: on its outer side the new split wraps the
parent, on its inner side it halves the parent's other child. The drop
preview shows the half the new frame takes.
A side zone is a quarter of the frame deep, at least 32 pt and at most
half the frame, so a 64 pt tool strip offers its left and right edges
past the outer edge band. A pointer in the gap between frames resolves
to the nearest frame's edge instead of floating.
DockArea::float_bounds sets where floating windows, flyouts and drag ghosts may go; the default is the whole viewport. An app with its own title bar under a transparent macOS title bar passes the viewport below it, so a floating window can't be dropped, dragged or restored where pressing it moves the app window.
* feat(window): shared crate deciding who draws the window frame
Add craft-window, which decides whether the desktop or the app draws the
window frame (titlebar, borders, window buttons) and applies it to an
egui ViewportBuilder. This fixes the drift where some Craft apps drew
their own titlebar on KDE while others kept KWin's.
The rule: macOS, Windows and X11 keep the desktop frame; a Wayland
compositor with zxdg_decoration_manager_v1 (KDE, Sway, Hyprland, COSMIC)
keeps its frame; one without it (GNOME) leaves the frame to the app.
<PREFIX>_TITLEBAR=app|system|desktop overrides. When the app draws the
frame, the decision carries the window-button Layout from GNOME's
button-layout so the app's header bar matches the desktop's order.
Decision logic only; drawing the buttons stays in each app. Extracted and
generalized from pdfcraft's decorations.rs. forbid(unsafe_code); default
build has no egui dependency (egui feature adds decorate()).
* style(window): rustfmt the Wayland probe and Dispatch impl
cargo fmt --check failed CI: collapse the thread-spawn builder chain and
the Dispatch::event signature onto the lines rustfmt wants (verified with
rustfmt 1.98 against the workspace rustfmt.toml).
* fix(window): address review — WAYLAND_SOCKET, bounded gsettings, checkout creds
- Skip the Wayland probe when WAYLAND_SOCKET is set: connect_to_env would
take ownership of the inherited fd and unset the variable, closing the
descriptor winit needs. Keep the desktop frame instead.
- Run the gsettings button-layout lookup behind the same 500 ms bounded
worker as the Wayland probe, so a stalled gsettings can't block startup.
Factored the worker into a shared bounded() helper.
- window.yml: set persist-credentials: false on checkout so the token
isn't left in .git/config while cargo runs dependency build scripts.
- README: document the socket guard and the bounded gsettings lookup.
Verified with rustfmt 1.98 and clippy -D warnings (both feature sets).
* fix(window): reap the gsettings child on timeout; document probe tradeoffs
Follow-up to review: the button-layout lookup now spawns gsettings with
piped output, reads it on a worker, and kills+reaps the child whether it
finishes or times out — so a stalled gsettings leaves neither a blocked
process nor a parked worker (it is a one-shot startup call).
Also document, on the Wayland probe, that an inherited WAYLAND_SOCKET
(rare; normal launches get WAYLAND_DISPLAY) deliberately falls back to the
desktop frame to protect winit's connection — the same frame an app that
doesn't probe would get, a safe default rather than a regression.
Verified: rustfmt 1.98 clean, clippy -D warnings clean (default + egui),
tests pass.