Port behavior and regression coverage into current modules; preserve current upstream behavior.
Ported hanging punctuation into current line placement and kerning modules, preserving forced breaks, bounded edge-tip row merging, justified-last-line behavior, area exclusions and independent Roman orientation shaping; kept PSD/engine/UI regression coverage.
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Persist and restore Color panel options through the current dock/startup modules and regenerate the scorecard from current source.
Conflicts in perf/budgets.toml and docs/scorecard.md: both sides added perf rows (P80 here, P81 and P82 from #9942). Kept all three; scorecard regenerated with cargo xtask scorecard.
Track-only rows that check they hit the whole-document-redraw path and
print the refreshed pixel count: undo of a brush dab on a 16-bit
15000x10000 full-canvas layer (reps capped at 5) and a fill of a 2 %
selection on a 24 MP full-canvas layer.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Track-only row, the slow-frame recorder's worst case. A 24 MP photo plus
three full-canvas blended copies (Multiply, Screen, Overlay); each sample
toggles the top copy and times one harness.step(), so every frame
recomposites all 24 MP on the CPU (the photo's tiles are never uniform,
which keeps the reduced render's one-colour shortcut out of the way).
The row errors unless each frame recorded a CPU refresh covering at least
95 % of the canvas, so it can't silently take a fast path. 10 samples.
Conflicts with main's batch #9880:
- psd file.rs / layer.rs: #5287 (PSB by encoded size, RLE tables widened on
promotion) against #6583's lazy reader. Main's new widening and length code
read `.data`, which #6583 leaves empty while the bytes stay in the source;
it now goes through `encoded()` / `encoded_len()` (also the auto-merged
lines in `rle_widening`, `encoded_len` and `LayerRecord::write`), and the
lazy reader sets `write_as_psb: false`. New test
`a_lazy_psd_promotes_to_the_same_psb_as_an_eager_one`: a lazily read PSD
writes the same PSB bytes as an eager one; it fails with a `.data` read.
- direct_select.rs / vector_ui.rs: #9321's Type Path and #6353's hidden
Work Path both kept.
- io tests main.rs: both modules. scorecard.md regenerated (12 of 181);
ui-parity.md and gaps.md follow it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Character section: a style picked under a family's ">" with type layers
selected set the layers but not the style for new type (the options bar
did both). Pick "Inter > Thin" in Properties > Character: new type stayed
Regular.
- Faux bold: a pointed tip moved r/sin(half its angle) along its bisector,
uncapped (3.9 r at 30 degrees, an A apex grew a spike), and tips sharper
than about 20 degrees did not move at all. Every tip now moves at most
2r, continuously; inner corners (the crotch of a V) keep their exact
miter.
- Faux bold: each point searched past runs of coincident points for its
neighbours, O(n^2); a contour of 50 000 copies of one point took ~8 s per
glyph. Neighbours now come from one O(n) pass over the runs.
- prefs.reset {"path":"type"} and Preferences > Type > Reset Section wiped
the favourite fonts; they are kept now (type.favoriteFonts by name, or
Reset All, still clear them).
- An expanded family whose own row scrolled out of view lost its style
rows, so the list below jumped up.
- Show Similar Fonts re-sorted around each font an arrow step applied, so
Down went back and forth between two fonts; the list now compares with
the font current when the filter was turned on (or the menu opened).
- Docs: an uninstalled favourite is kept but not listed; FAUX_BOLD_RADIUS
and the raster budget describe the thickened outline; the unused
"Recent font list" rows are gone; docs/scorecard.md regenerated (the
favourite fonts are one more setting).
Co-authored-by: D-monized <341904702+D-monized@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The gate's scorecard check failed on #9606: the new remembered Color
panel option is one more preference (179). Regenerated with
`cargo xtask scorecard`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
P25 broke at 299 ms once on the contended runner (2026-10-18). It now uses
the p95 metric with the same 200 ms budget; with n <= 20 the p95 rank equals
the max, so on the runner (n = 8) this has no effect until reps >= 21.
P16 stays unenforced: the new --from enforce report over the 2026-10-18..25
nightlies lists it as flaky (p95 103.7 ms on 2026-10-18).
The 2026-10-05 baseline was recorded at load 224.9 on 14 cores; the scorecard now
says so without changing a number, shows the baseline's run count, and replaces
the claim that the nightly checks regressions with the machine class it actually
runs on.
davidmann4's round-3 review of #8347: a marks row the box had to split
out of a run folded row after row into one line running past the edge by
almost the whole run, and a row merged into a justified paragraph's last
line kept the row above's spread instead of the last-line align.
- A marks-only row steps back only while it is at most two clusters (the
tip that hangs at the edge); a row above that already ends in a
hangable mark takes no more.
- A justified paragraph's last row keeps its own align (centered, right
or left): the placed clusters already carry the spread, and the merge
only moves x1/range.end.
- hanging_marks_of_a_split_run_keep_their_breaks pins that a run keeps
its rows and no line outgrows the box by more than two marks;
hanging_marks_stay_off_a_justified_paragraphs_last_line pins the
JustifyCenter/Right/All last lines. The old merged-row test now pins
the tip (a_stepped_mark_row_keeps_its_clusters_and_glyphs_in_step).
The summary row still said 9/13/5 after an earlier batch PR moved FILE-215-11 to partial,
and #3297 moves FILE-215-3 to done: scorecard/file_compat.toml now counts 10 done,
14 partial, 3 missing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged with our fix-up pr-3297-fixup (1de1e9eb58) on top of the author's head 053e1e0d03.
Conflicts resolved: channel_cmds.rs routed() keeps the PR's PAINT_IDS list plus the batch's
paint.artHistoryBrush; paint_cmds.rs Paint Bucket params keep the batch's "mode" and the PR's
"filterMask" target; docs/scorecard.md takes the batch's numbers with FILE-215-3 done.
Main moved the shape options bar and paint swatch out of vector_ui.rs into
shape_options.rs and shape_paint_ui.rs (#5452). Carry the PR's gradient paint
over: PaintChange::Gradient, paint_param and the swatch's Gradient tab live in
shape_paint_ui.rs, the tool gradients and set_tool_paint in shape_options.rs,
and shape_gradient_ui.rs is its own module. Both sides' new tests are kept.
Co-authored-by: Denis <179751626+deniskhud@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#6923 develops the Camera Raw dialog's preview on the GPU whenever no spatial stage is set,
from `per_pixel`'s stages. #5128 adds Calibration (primaries and Shadows tint) to the CPU
develop between white balance and Dehaze, which those stages don't run, so with both merged
a Calibration-only change previewed unchanged on the GPU while Open applied it. `per_pixel`
now returns None when Calibration is set, so the dialog develops on the CPU then. The
white and black points stay as they were, so the dialog's points cache is still right once
Calibration goes back to neutral. The hardware parity doc and BUG-204-3's note now say so.
Co-authored-by: Fabio Lissi <3223836+FabioLissi@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Open wrote PhotoCraft's crs: subset over any existing sidecar, on by
default: a Lightroom or Bridge IMG_0001.xmp lost its crop, masks, rating
and keywords, even when the raw was only opened with Open in Camera Raw
off (FredApps' review). Now a sidecar is only written when the settings
changed, never over one whose x:xmptk isn't PhotoCraft's (a notice says
it was kept, camera_raw_xmp::is_photocraft_xmp), and never for a control
or MCP request, which has no authority to write beside the user's files.
A write error goes to a notice instead of a status line the develop
overwrote at once.
The Camera Raw Defaults' colour space, 8-bit and smart-object steps ran
before the Camera Raw step, so with the default Adobe RGB every raw opened
with the dialog's settings applied to other pixels than it previewed. They
now follow the step (also when a background re-develop lands), as
Camera Raw converts to the workflow space last. Main already sets the raw
resolution at import (#7811), so the PR's Image Size step is dropped.
Two raws opened back to back with raw-stage edits: the second one's
background re-develop replaced the first one's pending entry, so the first
never got its Camera Raw step, conversions or unmodified mark. Pending
re-develops are now a list keyed by job. Sidecars are read only from
regular files of at most MAX_XMP_BYTES (a pipe or a huge file named
IMG.xmp no longer blocks or fills memory), and a conversion that fails
after the Camera Raw step is reported instead of leaving the dialog open
for a second Open that would apply the step twice.
Tests: a_foreign_sidecar_is_never_overwritten,
a_rating_only_sidecar_is_left_alone,
opening_without_the_dialog_does_not_rewrite_the_sidecar,
a_control_request_never_writes_the_sidecar,
a_sidecar_write_error_stays_reported,
the_camera_raw_step_runs_before_the_workflow_conversion (inline and
background), two_raws_opened_back_to_back_both_finish_their_open,
sidecars_are_read_from_small_regular_files_only,
is_photocraft_xmp_reads_the_toolkit_marker; the existing
sidecar round trip now uses a PhotoCraft-written fixture.
Co-authored-by: Fabio Lissi <3223836+FabioLissi@users.noreply.github.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/file-format-parity.md: main's PSD row with the round-trip count
258/258. docs/scorecard.md: regenerated with `cargo xtask scorecard`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolves the conflicts with main's area type (TextShape::Area): EngineData keeps main's area arm beside the path comment, and a Txt2 frame of an area layer becomes a text box over the path's bounds, as its EngineData describes it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Tolerance defaulted to 50 and averaged alpha into the difference, so an
ordinary edit (e.g. #c03030 -> #d04848) was never painted back, and a stroke
that wrote nothing still left an empty History step. Photoshop's default is
0; the difference is now the larger of the mean colour change and the
alpha change (the Eraser keeps colour and lowers alpha), and a stroke that
writes nothing is an error instead of an empty step.
- Each tile copied a source window of tile +- halo per dab: a leftward stroke
or a curl across x = 64 read empty pixels there (seam), and a size-1000 click
copied ~5e9 floats (11.9 s). The rotated read stays inside the dab, so it now
samples the source directly; bilinear reads at pixel centres, so an
unrotated dab copies the history pixel instead of a 2x2 blend.
- The brush used its own history lookup (state 0 by default): it now takes
the History Brush's source (history.setSource, snapshots, refusal after a
canvas size change).
- TOOL-213-20 is partial (8 of 10 styles, Area scales spacing); docs follow.
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts with #5547 (Animated Zoom) resolved in wheel_nav.rs, canvas.rs and
panels.rs: the Navigator and canvas pass both Animated Zoom and
!animations_off; with Animation Effects off, the sideways part of an
animated wheel zoom pans by the frame's own events folded by their
modifiers (#4573's unsmoothed scroll) instead of the plain-event raw.x,
so Ctrl + wheel still pans sideways there. #4573's modifier test runs
with Animated Zoom off and on.
Translation catalogs keep main's strings plus the Art History Brush ones
(fr: main already has Area; hi: added the brush strings). docs/scorecard.md
regenerated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Photoshop's documented hang list carries weights: period, comma, quote,
apostrophe and hyphen hang 100%, colon, semicolon, ellipsis and dash hang
50%; ! and ? are not in the list (davidmann4). The drawn positions never
change: the weights only come out of the composed width.
- A marks-only row after a forced break keeps its row: the merge-back must
not cross the author's break (CodeRabbit).
- The merge-back reads its pair style from the mapped byte; the box slack
and the justify-all path take the shaped advance scaled once and the
(already scaled) trim on top, so horizontal scale no longer scales the
trim twice; area type keeps its planned rows (hanging applies to point
and box lines only).
- The Styles editor row label goes through tl!, the dock comment loses its
(#...), space-before keeps upstream's arrow order, tests get abs() and
exact centering assertions; hi.tsv carries the label.
Conflicts resolved by keeping both sides:
- ui-egui control.rs: UI_SET_FIELDS has main's updatesOpen and the
histogramPanel, histogramRange and histogramRefresh fields (42).
- automation server.rs: the ui_set description names all of them.
- docs/ui-parity.md: main's header, with the Histogram panel entry
added to its change list.
- docs/scorecard.md: regenerated with `cargo xtask scorecard`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Compact, Expanded and All Channels views; the channel is kept across views.
- Channels per colour mode, plus Luminosity and Colors (overlaid, mixed where
channels overlap); Show Channels in Color and Show Statistics in the panel menu.
- Statistics for the level under the pointer or a dragged range: Mean, Std Dev,
Median, Pixels, Level, Count, Percentile, Cache Level.
- Source (Entire Image, Selected Layer, Adjustment Composite) for multilayer
documents.
- Adjustment previews counted live, with the original outlined over them.
- Cached-data warning and Uncached Refresh; Preferences > Performance > Cache
Levels (1-8) caps the cache level.
- ui.set histogramPanel / histogramRange / histogramRefresh, ui.inspect histogram.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The row's own note says the window's Marquee tool and the Stamp's Heal modes
are still missing, so it stays partial; the scorecard is regenerated to match.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>