Port behavior and regression coverage into current modules; preserve current upstream behavior.
Prebound the listener inside current authorized control_setup before Linux display and GPU initialization, retaining integration preferences, workspace grants, authentication and single-instance routing. Preserved actual port logging, added occupied-port/prebound-dispatch regressions and bounded child cleanup to current process test harness.
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Register starter actions and bounded background-blur command in current modules; preserve persistent migration marker and portable JSON defaults compatibility.
serve_fonts asks for the interface language's served script font once per
language, but the font manifest loads asynchronously: when the first frame
ran before it arrived, no fallback was registered yet, the request found
nothing, and an Arabic interface on an English browser stayed without its
font until the language changed. The manifest load now asks for the
interface language's font along with the browser languages'.
Review finding on #10006.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The web build has no system fonts and embeds no craft fonts, so an Arabic
(or Japanese, Chinese, Korean, Hindi) interface had no font for its labels:
served fonts were registered only in the document text engine. A served
script fallback now also joins the end of every egui font family, and the
font stack theme::install_fonts rebuilds on a language change includes it
again. The web shell asks for the interface language's script font, not
only the browser languages'.
Review finding on #10006.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CodeRabbit on #9775: the Linux portal swaps the extension for the picked type, but macOS and rfd keep the suggested name, so an opened image.pgm could still be saved as image.pgm holding P6 data. The suggested name itself now takes .pnm for any Netpbm subtype; the services test fails without it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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>
CodeRabbit on #9775: save_filters adds an unlisted suggested extension as its own type, so Save As on an opened .pgm offered .pgm again, though the encoder writes P6 for an RGB document. A suggested .pbm/.pgm/.ppm/.pam/.pfm now starts on the Netpbm type as .pnm; the services test checks every subtype name and fails without this.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
CodeRabbit on #9775: the encoder picks P5/P6/P7/PFM from the image, not the name, so a .pbm, .pgm or .pfm picked in Save As held other data (an RGB document as P6 in a .pbm). Only the neutral .pnm is offered; File > Open still reads every Netpbm extension.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Picking Netpbm for a bare name (Untitled) took the row's first
extension, .pbm, though the codec writes P6/P7/PF from the image, never
a 1-bit PBM: an RGB document saved as x.pbm holding P6 data. The row now
starts with .pnm. File › Open didn't list .pnm or the new .dib alias, so
a file Save As had just written was hidden in the Open dialog; both are
now in All Formats and their own type. The test checks every new type
and alias is offered, the Netpbm row's first extension, that Open lists
every extension Save As writes, and what Save As writes to .pnm (P6) and
.hdr (#?RADIANCE), as asked in review.
Co-authored-by: buivanvu94 <125793064+buivanvu94@users.noreply.github.com>
Co-authored-by: tanish2k09 <13985398+tanish2k09@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Remove blank lines between doc comments or #[test] attributes and the item (S8855, S8856)
- type_tool: drop the redundant else after continue in the IME event arm (S8862)
- ui_state: pass OsStr::to_os_string instead of an equivalent closure (S1612)
- macos packaging: [[ ]] for the attempt test (S7688); bind validate_staple's argument to a local (S7679)
* chore: resolve SonarCloud maintainability issues
* fix: format imports and fix effect_entry argument type
* fix: restore test closure and wildcard import that broke compilation
* refactor: split composite_to_surface to reduce cognitive complexity
`main` returns `eframe::run_native`'s error, and a desktop launch has no console —
release builds are GUI-subsystem on Windows, a Dock launch has no terminal on
macOS — so the error went nowhere and PhotoCraft "just closed" (#7643).
The error is now logged, and when nothing else can see it it is also shown in one
native `rfd::MessageDialog` with the renderer that was tried, the GPU-startup
fallback reason, the version and the log file to attach. A terminal launch keeps
printing to standard error, a `--control` launch keeps its exit status and a
headless Linux launch (no DISPLAY, no WAYLAND_DISPLAY) keeps the error on stderr,
so none of them gets a modal dialog.
`AppLogger` remembers the file `attach_dir` opened (`path()`): the logger only
held the `File`, and the dialog has to name the file a reporter should attach.
* feat(updates): startup update prompt modal and direct self-updating
Release checks and the install go through craft-update's Updater: the engine's Help ▸ Check for Updates command keeps its status machine, its transport asks craft-update instead of a private GitHub client, and a found release opens the startup prompt. An install holds back while documents are unsaved, and a finished update closes through the guarded close path. craft-update stays out of the wasm build.
Ref: storytold/craft-libs#319
* fix(updates): address review feedback
Wake the UI when a manual check is queued from a menu callback; the update prompt owns the keyboard while it is open (LightCraft); Download opens through the desktop URL hook (FilmCraft); the Help menu dispatches through menus::invoke (FigCraft); print jobs and comment drafts hold an install back (WordCraft); Check for Updates is a registered command (GridCraft); an install holds back while documents are unsaved.
Conflicts: the batch's wheel reads (#5547 Animated Zoom) keep their extra
argument with main's ids (`Id::unique`, `ui.scope_id()`); crop presets keep
#6620's `apply_choice` with main's accessible name; Bevel & Emboss keeps
#9370's per-mode id with main's name argument; the Stroke field keeps
#7203's gradients with main's name; tone.rs keeps #9139's move of the
Histogram panel to histogram_panel.rs (main only renamed ids there);
translation catalogs as unions.
Not flagged by git, the batch's new code follows egui 0.37 too: dropdowns
take an accessible name (Histogram Channel/Source #9139, tool Mode #5504,
Art History Style #1610, New Snapshot From #3567), MouseWheel events carry
`source`, `WidgetType` is `Role` (ColorButton is ColorWell, #7203),
`Id::new`/`Ui::id` are `Id::unique`/`Ui::scope_id`.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* egui037: name each control for what it sets, and order the eager fonts for the UI
CodeRabbit review follow-ups on the migration: the Artboard X/Y fields, the CAF
dropdowns, the channel Solidity field, the GPU backend/adapter dropdowns, the
corner radius fields and the canvas extension colour all had an accessible name
that named another control (or was shared by four). The eager craft-fonts
registration now puts the UI Japanese face first, as the lazy loader does, so
Japanese UI is not drawn in the manifest's Mincho face and shared Han characters
do not take Japanese forms.
Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
* egui037: rustfmt the eager craft-font order
---------
Co-authored-by: Dylan M. Taylor <dylan@dylanmtaylor.com>
Co-authored-by: Codebuff <noreply@codebuff.com>
Photoshop reads and writes IFF; PhotoCraft opened Amiga IFF/ILBM files
but had no writer. Saving as .iff now writes a FORM ILBM with a BMHD and
a ByteRun1-compressed BODY of 24 bitplanes (red, green and blue, least
significant bit first), one plane row at a time. An image with alpha
also gets a mask plane (masking 1): alpha of 128 or more is opaque, and
when any pixel is not fully opaque the export warns that alpha is
reduced to on/off transparency. Gray is expanded to RGB, 16-bit and
float images are reduced to 8 bits, and images wider or taller than
65535 pixels are refused.
The reader also opens 24-bitplane ILBMs, so a saved file reopens with
the same pixels. A transparent color index in a 24-bitplane file is
refused as unsupported.
IFF leaves ASYMMETRIC_EXCEPTIONS, Save As offers Amiga IFF, and the
codecs README, file-format parity and G10 are updated.
* fix(engine): defined and imported patterns survive a restart (#9317)
The pattern library lived only in memory: patterns defined with Edit >
Define Pattern or imported from a .pat file were gone after a restart,
while their Patterns panel folders (saved with the preferences) still
named them. Photoshop keeps them.
The preset store now writes the user patterns (the library minus the
built-ins, in order) to patterns.pat, Photoshop's own pattern file
format, whenever their ids, names, sizes or order change, and loads
them when the store attaches. Like actions.json, a file that fails to
load is warned about and never overwritten. The web store accepts the
new record.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Patterns: persist same-id pixel replacements, retry a failed removal
CodeRabbit (discussion_r4369506324, r4369506326):
- an imported .pat whose id is already in the library replaces its
pixels, which the id/name/size key didn't show, so the old pixels came
back after a restart; PatternLibrary::pixels_rev is bumped on such a
replacement and is part of the key;
- a failed patterns.pat removal recorded the empty library as saved and
was never retried, so the deleted pattern came back; it now stays
pending, as a failed write does.
Tests: a_same_id_pattern_replacement_survives_a_restart,
a_failed_patterns_removal_is_retried.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Patterns: persist replaced built-ins and ASL replacements
CodeRabbit (discussion_r4369536978, r4369536981):
- a .pat import that replaced a built-in pattern by id was filtered out
as a built-in and never saved; only built-ins exactly as shipped
(is_pristine_builtin) are skipped now, and on load a saved built-in
takes the shipped one's place unless it changed before attachment;
- style.presets.importAsl replaced patterns by id without bumping
pixels_rev; both imports now go through PatternLibrary::upsert.
Tests: an_imported_builtin_replacement_survives_a_restart,
an_asl_pattern_replacement_bumps_pixels_rev.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* .pat: refuse names and counts the reader won't load, on write too
CodeRabbit (review 5641404908, outside-diff, two findings): now that the
preset store writes the user patterns, a file its own loader rejects or
truncates loses patterns at the next start.
- a name of 65,536+ UTF-16 units (with its NUL) was written although the
reader refuses it; write_pattern refuses it now;
- more than 100,000 patterns were written, and the reader kept the first
100,000 without a word; both now refuse counts over MAX_PAT_PATTERNS.
Tests: a_name_the_reader_refuses_is_refused_on_write,
more_patterns_than_a_pat_file_holds_are_refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Patterns: an early same-id import with other pixels is saved
CodeRabbit (review 5641454449, outside-diff, preset_store.rs:1191-1202):
a pattern imported before the store finished loading, with the id, name
and size of a saved one but other pixels, was appended (no pixels_rev
bump), won the merge, and then matched the loaded file's key, so it was
never written and a restart brought the old pixels back. The merge now
bumps pixels_rev when the copy it keeps differs from the saved one.
Test: an_early_same_id_import_wins_and_is_saved.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Merged with our fix-up d5139d177 (pr-1199-fixup), which merged main as of 8f09d122d.
Conflicts with main since then and with batch 8:
- photocraft-cli: main split the CLI into clap modules (#7071); the model wiring
(local_headless, configure_cli_models, batch's shared backend, droplet) is re-applied there.
- Cargo.lock regenerated: crates/ml pins ureq =3.4.2, so the app's ureq goes 3.5.0 -> 3.4.2.
- cutout_cmds: main's subject area (#8235) kept for the classical path beside the model path.
- file_cmds batch: main's per-input closure takes `s` later, so the backend and
integrations prefs are read before the closure instead of inside it.
- prefs_ui: model-choice filter with #4573's pref_choice_label; Integrations stays visible
(#4097) with the model choices only where models run; the translation-coverage test main
deleted (c3aa3f9e5) stays deleted; old section_fields test calls take PreferenceFieldOptions.
- Semantic clashes git did not flag: #4097's Integrations test literals in
apps/photocraft take ..Default::default() for the new model fields; main's and #4573's
section_fields test calls pass PreferenceFieldOptions.
- engine lib.rs, prefs.rs, smartselect tests, snapshot example, jobs_ui_tests, people.toml,
docs and i18n catalogs merged as unions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
panels.rs: the PR's snapshot rows next to main's History Brush source
and scroll-to-current; ui-parity.md: main's status line.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- prefs_ui `unimplemented_preferences_are_hidden_from_the_dialog` still asserted the
Integrations section is hidden, so test (ubuntu/windows) failed (FredApps' review).
It now asserts both rows are visible; scratchDisks stays hidden.
- `integrations()` parsed the whole preferences file strictly, so one wrong-typed value
anywhere (`{"interface":{"enableAnimations":"nope"},"integrations":{"allowAgentControl":false}}`)
reset Allow Agent Control to on while the dialog (lenient load, #6611) showed it off.
The section is now read on its own, and field by field when it doesn't read as a whole
(a list like `[false]` reads as a whole, as in the app); a test compares it with the
app's own load.
- Both rows are read only at launch: they say "Applies at next launch." like System Title Bar.
- Removed the stale "not by these yet" comment in HIDDEN_UNTIL_IMPLEMENTED.
Co-authored-by: Erica Franco <20589100+Erica4fr@users.noreply.github.com>
Co-authored-by: FredApps <153173854+FredApps@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.
Reconcile main 486afd82cc while preserving reviewed lifecycle, capture, IME and settings migrations. Include canonical dependency pins and the generated lockfile.
All nine CI stages passed: 11,580 passing test executions, zero failures and 91 ignored across 93 summaries. Native/browser acceptance and paired app performance remain open.
The checks parse() runs after clap built their messages from a const
and two format! calls. ServerOptionError holds them as thiserror
variants beside ValueError, each with the clap ErrorKind it reports
as: the PHOTOCRAFT_CONTROL_PORT host:port refusal, bridge roots, and a
token flag without its transport. The output is unchanged.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The review round of the #7071 stack, which #9167 merged without
(it took the pre-review heads):
- A bare --help or -h before `--` is help after any parse error, not
only after a missing value: `mcp stray.psd --help` and
`serve --port abc --port 1 -h` print the subcommand's help.
- mcp and serve --help carry #8035's rules: what the automation roots
grant, that bridge roots are refused, and that the token flags need
--bridge or --port (or PHOTOCRAFT_CONTROL_PORT).
- check_server takes the subcommand and whether a transport is set;
the bridge-roots check moves next to the other mcp checks.
- The server tests match clap's "a value is required for" text, and
mcp.md in the book and docs names PHOTOCRAFT_CONTROL_PORT.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Add the man-and-completions example, which writes photocraft-cli's
completions for bash, zsh, fish, elvish and PowerShell (clap_complete)
and its man pages (clap_mangen: photocraft-cli.1 and one per
subcommand, such as photocraft-cli-convert.1) into a directory:
cargo run -p photocraft-cli --example man-and-completions -- <out-dir>
clap_complete and clap_mangen are dev-dependencies, so the binary is
unchanged. clap_mangen gets its env feature, so the pages name each
flag's environment variable as --help does; clap already has env, so
Cargo.lock gains no package for it. The library exposes only
`command()`, the clap definition; the args module stays private.
A test runs Command::debug_assert and checks that every shell's
completions and every page generate. Another repeats the example's two
generate_to calls and pins the exact file list: the five completion
files under the names the packages install, photocraft-cli.1, one page
per subcommand, and no pages for the help subcommand.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Step 5 of the issue: launch_args' hand-written loop, USAGE and
LaunchArgs become a clap derive, which flattens photocraft-automation's
ControlArgs and AutomationArgs. `photocraft --help` comes from the doc
comments; `--version` prints the same `photocraft <long version>` line
(build_info::long_version_static), and `-V` now does too. --control
uses photocraft_automation::args::control_port instead of a local copy.
The cases the loop handled by hand are kept:
- An old macOS Finder's -psn_... argument is dropped before clap,
unless it comes after `--`, where it is a file name.
- The per-process options for the single-instance hand-off come from
ArgMatches::value_source, in a fixed order. A PerProcess enum names
each (and PHOTOCRAFT_GPU_ADAPTER): id() gives a flag's clap id,
Display the flag or the variable, so single_instance's log line reads
as before. As since #7242, a flag's variable counts as the flag: one
clap reads (PHOTOCRAFT_IN_WINDOW_MENUS when it turns the menus on, an
automation root that isn't blank) is counted here, as clap read it;
env_launch_options keeps #7242's rules for the three read elsewhere
(present mode, frame latency, stroke trace) and returns PerProcess.
- A file name that isn't valid Unicode is still reported in a notice
instead of being opened (#1108); file_names returns
FileNames { openable, unreadable }.
- Windows release builds still have no console: help, version and
usage errors show only when redirected, as since #6435. clap's
Error::exit, like print_stdout, never panics on a closed stream.
The variables are now declared on their flags: PHOTOCRAFT_CONTROL_PORT
on --control, the token variables through ControlArgs (so
security::token_inputs, which nothing else used, is removed), and
PHOTOCRAFT_AUTOMATION_*_ROOT added on the desktop's Command only, so
photocraft-cli still ignores them (#1551).
Behaviour changes, as step 0 of the issue decided:
- A bad or missing --present-mode, --frame-latency or --stroke-trace
value is a usage error (exit 2) instead of a warning. Their
variables stay lenient (logged and ignored); presentation's value
checks serve both, and the flags refuse a value with a ValueError.
- A repeated flag is a usage error, and a value can't start with `-`
unless it is a negative number (write --opt=-value).
- clap reports the first usage error, not all of them. On an unknown
argument clap resolves the pending option value but drops its error,
so `--control nope --safegpu` names `--safegpu`.
- An empty or blank variable counts as unset; before, only an empty
PHOTOCRAFT_CONTROL_PORT did, and an empty token or root was passed
on as given. PHOTOCRAFT_IN_WINDOW_MENUS also reads false, no and
off as off (FalseyValueParser). A variable that isn't valid Unicode
where text is expected is a usage error instead of being ignored.
- With --control (or its variable), a token together with a token
file, from flags or variables, is clap's either/or usage error at
parse time, before the server's own check. Without it no token is
read, so a stale exported pair stops nothing, as before.
- With --control given, PHOTOCRAFT_CONTROL_PORT is not read, so a
bad value there is no longer reported too.
The CPU-compatibility relaunch puts --safe-gpu before the original
arguments, so a `--` among them can't turn it into a file name.
The launch_args unit tests now check clap's errors instead of the error
list. The process tests put a late usage error (--control 0 with the
token pair) where they used a bad port, so stderr still shows how the
other arguments were taken, and cover the variables.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Two process tests of today's behaviour that the clap port keeps:
`--version` prints `photocraft <long version>` and stops reading the
arguments, and PHOTOCRAFT_AUTOMATION_READ_ROOT / _WRITE_ROOT reach the
control server's workspace like their flags. The process tests return
a Run struct instead of tuples, and control_exit_code gains run_with
to set variables.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>