Commit Graph
405 Commits
Author SHA1 Message Date
IvanandClaude Opus 5.5 0d5b7f6b39 refactor(app): parse the desktop command line with clap (#7071)
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>
2026-10-24 19:45:27 +03:00
IvanandClaude Opus 5.5 483b01b562 test(app): pin --version and the automation root variables (#7071)
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>
2026-10-24 19:45:27 +03:00
IvanandClaude Opus 5.5 2bba1c13fe refactor(app): move the launch argument parser into launch_args (#7071)
The hand-written argument loop, its LaunchArgs, USAGE and helpers
(parse_control_port, usage_exit_code, print_stdout) and their unit
tests move unchanged from main.rs into a new launch_args module; only
the module doc, the `mod`/`use` lines and pub(crate) visibility are
new. main.rs keeps the environment defaults and the exits.

This is the step before the clap port, so that one reads as a rewrite
of launch_args.rs rather than a large main.rs deletion. Check with
`git show --color-moved=dimmed-zebra`.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 19:45:27 +03:00
IvanandClaude Opus 5.5 23548ee92a fix(cli): check the token pair only where a token is read (#7071)
With the environment fallbacks, clap's conflicts_with on the token pair
made a stale exported PHOTOCRAFT_CONTROL_TOKEN and
PHOTOCRAFT_CONTROL_TOKEN_FILE a usage error for every mcp and serve, though
headless mcp and stdio serve read no token. As on main, the pair is now
refused only by serve on TCP and mcp bridging, still as clap's either/or
usage error naming each half as it was given.

An automation root on the command line with PHOTOCRAFT_CONTROL_PORT set
is a usage error too: the variable would switch mcp to bridging, which
drops the roots, with only a note on stderr.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 19:45:26 +03:00
18f9f11de4 feat(cli): environment fallbacks for mcp and serve (#2491, #7071)
The desktop app's --control already reads PHOTOCRAFT_CONTROL_PORT, and
the CLI read the token variables at run time. Now every fallback is
declared with `env =` on its flag (clap's `env` workspace feature, no
new crate in Cargo.lock):

- --control-token and --control-token-file in ControlArgs fall back to
  PHOTOCRAFT_CONTROL_TOKEN and PHOTOCRAFT_CONTROL_TOKEN_FILE, as
  security::token_inputs did at run time. The CLI no longer calls
  token_inputs, so the variables are read in one place; the desktop
  app still uses it until step 5. Help names the token's variable but
  never shows its value (hide_env_values). A token from the
  environment together with the other flag is now clap's either/or
  usage error (exit 2) rather than the automation crate's (exit 1).
- `serve` takes PHOTOCRAFT_CONTROL_PORT as --port and `mcp` as the
  bridge port when the flag is absent, as #2495 proposed. The variable
  must be a port: `mcp` refuses 127.0.0.1:7878 or :7878 from it, as the
  desktop app's --control would; only --bridge takes host:port.
- The automation roots stay flag-only for the CLI: a
  PHOTOCRAFT_AUTOMATION_*_ROOT exported for the desktop app must not
  widen headless file access (#1551).
- A flag wins, and an empty or blank variable counts as unset
  (blank_env_is_unset; clap would hand the empty value to the parser),
  so stdio and headless stay the default.
- A bad value or conflict from a variable is a usage error naming the
  variable instead of the flag it stands in for (name_env_vars).
- The port variable changes what the command does, so when it (not the
  flag) turns on TCP or bridging, the command says so on stderr and
  names the way back (ArgMatches::value_source tells which).

bin() in the tests removes the token and port variables, so an
exported one can't change a test. New tests:
control_port_env_switches_modes_with_a_notice_and_flags_win (ported
from #2495, also checking the bad values) and
control_token_env_vars_are_the_flags_fallbacks.

Co-authored-by: ahmedhosnypro <ahhosnyas@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 19:45:26 +03:00
IvanandClaude Opus 5.5 2f13a1564c refactor(cli): share ControlArgs and AutomationArgs, typed --bridge (#7071)
Step 4 of the issue: the control token flags and the automation roots
move to photocraft-automation's new args module as two clap structs,
which mcp and serve flatten and the desktop app can flatten in step 5
without moving them again. clap is a plain dependency of the crate.

- ControlArgs holds --control-token and --control-token-file;
  AutomationArgs holds the roots, and AutomationArgs::workspace opens
  them (replacing the CLI's automation_workspace).
- The port value parser is photocraft_automation::args::control_port,
  with its unit tests, for serve --port and --bridge now and the
  desktop app's --control later. It ignores surrounding spaces and
  refuses anything else with a typed NotAPort.
- --bridge parses to Bridge::Port or Bridge::Addr: it takes a bare
  port, meaning 127.0.0.1:<port>, or any value containing a ':', which
  the bridge client then validates. Anything else is a usage error
  (exit 2) instead of a bridge error.
- 127.0.0.1 addresses are built from Ipv4Addr::LOCALHOST.

mcp_bridge_takes_a_port_or_an_address is new: `--bridge nope` is a
usage error, and `--bridge 1` bridges to 127.0.0.1:1, which a tool
call names when nothing listens there.

No new environment behaviour: the token variables are still read at
run time through security::token_inputs, and the roots stay flag-only.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 19:45:26 +03:00
IvanandClaude Opus 5.5 f540d3f5e3 refactor(cli): parse photocraft-cli values into types at the boundary (#7071)
Step 3 of the issue: handlers get values that are already checked, so
a bad one is a clap usage error naming its flag (exit 2) and the
"bad --x" errors (exit 1) are gone.

- --quality is a u8 in 1..=100; --tga-bits, --dpx-bits and
  --dds-compression parse to photocraft_io's TgaBits, DpxBits and
  DdsCompression through their from_bits/from_name, and --tiff-compression
  and --jpeg-format to photocraft_codecs' TiffCompression and JpegFormat,
  refusing a bad one with a typed ValueError; serve --port is a u16. export_opts becomes `impl From<&ExportArgs> for ExportOptions`,
  which can't fail.
- run's --new and --params parse to serde_json::Value, so JSON that
  doesn't parse is a usage error; whether it is an object is still the
  command's to say (exit 1). The --cmd/--params pairing keeps using
  ArgMatches::indices_of, now over typed Step values.
- Paths are PathBufs (convert, info, run, batch, the control token
  file and automation roots), and run hands the OsStrings to clap
  instead of refusing non-Unicode up front: a Latin-1 file name works
  as a path (#1108), and one given as text is clap's usage error.
  load_actions_file takes a &Path, and photocraft_engine's same_file
  takes any AsRef<Path>, so convert's #7437 check compares the PathBufs.
- Rules live in the types: run needs exactly one of <FILE> or --new
  (ArgGroup "source") and a --cmd or --out ("work"); batch's --actions,
  --in and --out are required; --control-token conflicts with
  --control-token-file. The runtime checks and their messages are
  deleted. The automation crate's own token check stays, as it also
  sees PHOTOCRAFT_CONTROL_TOKEN(_FILE).

Tests changed for this: run_errors, quality_outside_1_to_100_is_refused,
dpx_converts_both_ways_with_the_bits_asked_for,
dds_converts_both_ways_with_the_compression_asked_for,
tga_bits_choose_the_targa_depth, jpeg_format_chooses_the_scans
(exit 2, the error names the flag) and
the #1108 test, now
a_non_unicode_path_is_taken_and_non_unicode_text_is_a_usage_error:
convert reads and writes such a file name end to end, while info opens
through the automation layer, whose paths must still be UTF-8, and
refuses it (exit 1). either_or_flags_are_usage_errors is new.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 19:45:26 +03:00
IvanandClaude Opus 5.5 a6a82b0dca docs(cli): describe repeated flags, dash values and --version (#7071)
The CLI page said parsing reports malformed flags and JSON. Name what
the step 0 decisions made a usage error: a single-value flag given twice
(--cmd and --params repeat), how to pass a value starting with '-', and
that `version` is only --version/-V. Say why --quality allows negative
numbers.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 19:45:26 +03:00
IvanandClaude Opus 5.5 ef5688fec3 refactor(cli): one run taking any OsString arguments (#7071)
run took &[String] and run_os &[OsString], and run_os only checked
each argument was Unicode before calling run. A single run generic over
Into<OsString> (as clap's try_get_matches_from is) does that check once
and serves main's args_os and the tests' &str slices alike.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 19:45:26 +03:00
IvanandClaude Opus 5.5 da11e381ba refactor(cli): reject repeated flags, use clap's --version (#7071)
The behaviour changes decided in step 0 of the issue:

- A repeated single-value flag is a usage error (exit 2) instead of
  the last one winning: args_override_self is gone, and with it the
  argument scan that found repeats for mcp and serve (#8035). --cmd and
  --params stay repeatable.
- A value starting with '-' was already written --out=-image.png or
  ./-image.png since the clap port (a flag can't be a value since
  #8035); this pins it, and that `--filter -1` still works and
  `--quality -1` is still refused by its range.
- The bare `version` subcommand is gone; clap's version flag prints
  the same `photocraft-cli <long version>` line for --version and now
  -V too. `help <subcommand>` stays.

clap's version wants a &'static str, so build_info gains
long_version_static, long_version worked out once in a OnceLock, next
to long_version. The desktop app will need the same in step 5.

flag_value_forms_and_repeats and top_level_help_version_and_no_arguments
now pin the new behaviour; a_value_starting_with_a_dash_is_written_with_equals
is new.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 19:45:26 +03:00
IvanandClaude Opus 5.5 1d6d1da0a9 refactor(cli): drop duplicate --in-place help and a panicking matches lookup (#7071)
batch --help printed the "--out that is the --in folder is refused"
sentence twice, in the subcommand's long help and on --in-place. Keep
it on the flag, where -h shows it too.

steps() read --cmd/--params with get_many, which panics on a type
mismatch, against the crate's never-panic rule. Use try_get_many and
treat an error as no values; behaviour is otherwise the same.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 19:45:26 +03:00
IvanandClaude Opus 5.5 3765fcba15 docs(cli): note help-subcommand exit codes and clarify mcp/serve and batch help (#7071)
Behaviour change from the clap port, not listed in its commit: `help`
followed by an unknown name or extra arguments is a usage error
(exit 2); before, `help …` always printed the whole USAGE with exit 0.
usage_and_unknown_command now pins `help frobnicate` exiting 2 with
the error on stderr.

The --control-token-file help says it is used instead of
--control-token, as the old USAGE showed them as either/or. The
--in-place help carries the "--out equal to --in is refused" sentence,
so `batch -h` shows it and not only `batch --help`.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 19:45:26 +03:00
IvanandClaude Opus 5.5 43476581ea refactor(cli): parse photocraft-cli arguments with clap derive (#7071)
Replace the hand-rolled parser (USAGE, SUBCOMMANDS, Args::get/has) with
clap derive types in args.rs: a Cli parser, one Args struct per
subcommand, and flattened ExportArgs (convert, run, batch) and
ControlArgs (mcp, serve). The USAGE prose moves into doc comments, so
-h and --help carry it. Handlers keep their bodies; only their argument
type and the lines reading flags change.

Behaviour is kept: the last of a repeated flag wins (args_override_self),
values stay strings checked by the handlers with the same messages and
exit 1, a value flag never takes the next flag as its value, and help
there (`--out --help`) is still help, --version/version are checked
before clap, help goes to stdout with exit 0 and usage errors to stderr
with exit 2. run pairs each --params with the --cmd before it by putting
both back in command-line order with ArgMatches::indices_of.

mcp and serve keep #8035's launch checks: no positional arguments, no
repeated or empty options, --control-token or --control-token-file but
not both, a token flag only with --bridge or --port, and no automation
roots with --bridge. clap gives the first four (a repeat is found by
scanning the arguments while args_override_self stays on); a small
check after parsing gives the last two, with #8035's messages.

Behaviour changes (allowed by the issue's step 2):
- Help and error text is clap's: per-subcommand help instead of the one
  USAGE page, "unexpected argument '--fromat' found" instead of
  "unknown flag --fromat for batch", and so on.
- A wrong positional count is a usage error (exit 2) instead of a
  handler error (exit 1): convert with one or three paths, info with
  none, droplet with no inputs, run with two files.
- A positional starting with '-' (other than "-") needs a '--' before
  it, and '--' now ends the flags instead of being an unknown flag.
- A value starting with a single '-' is written --out=-image.png (or
  ./-image.png), as one starting with '--' already was; --quality and
  --filter keep negative numbers (allow_negative_numbers).
- `help <subcommand>` prints that subcommand's help.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 19:45:26 +03:00
IvanandClaude Opus 5.5 3651f6c228 test(cli): name the fields of test run results (#7071)
invoke returned (code, stdout, stderr) and ok (stdout, stderr), so each
call site had to remember the order. Return Invoke and Printed structs
instead.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 19:43:56 +03:00
IvanandClaude Opus 5.5 f59f1c347f refactor(cli): typed errors with thiserror instead of String (#7071)
Every failure was formatted into a String where it happened, so callers
could only print it. Subcommands now return a CliError, the actions
loaders an ActionsError and the hand-written parser a UsageError; each
variant prints exactly the text the String did, and the engine and
automation errors pass through unchanged.

Also: print_json takes a JsonStyle instead of a bool, the engine's
missing_font_warnings replaces the CLI's copy (same text), batch warns
through warn_all, run drops a second, no-op insert into its reported
font set, and the src tests share one Invoke helper.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 19:33:49 +03:00
IvanandClaude Sonnet 5.5 3d64c717b6 refactor(cli): split photocraft-cli into modules, no behaviour change (#7071)
lib.rs held the whole CLI in one 754-line file. Split it into args,
output, document, actions, batch and automation modules so the argument
parser can be replaced with clap in a separate, reviewable change.

Function bodies, doc comments and tests move unchanged. The only edits
are mod/use lines, pub(crate) visibility on items now shared across
modules, and the imports the moved test modules need. The public API
(run, run_os, parse_actions, load_actions, load_actions_file, Steps,
USAGE) is re-exported from lib.rs.

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-24 19:33:19 +03:00
David Mann 8c04b175ec Merge remote-tracking branch 'origin/main' into integration-batch-7
# Conflicts:
#	crates/automation/src/rpc.rs
#	crates/automation/src/server.rs
#	crates/gpu/src/health.rs
#	crates/text/src/tests.rs
#	crates/ui-egui/src/i18n/zh-hans.tsv
#	crates/ui-egui/src/panels.rs
#	docs/control-protocol.md
#	docs/ui-parity.md
2026-10-24 00:23:15 +02:00
ZuujisandGitHub 151eae66be fix(app): no unused screen picker code on FreeBSD (#8469)
FreeBSD (and every other OS without a capture backend) compiles the
screen colour picker's stub capture, which never reads monitor placements
or builds images. The build warned about the unused ScreenImage import,
the never-read Placement fields, find_monitor and count. Compile those
only where a capture backend or the tests use them.

Seen in the FreeBSD aarch64 CI build; checked with `cargo check -p
photocraft --all-targets` on FreeBSD 15.1 aarch64 (no warnings) and macOS.
2026-10-23 17:39:19 -04:00
aiongg 7aaa59712a Merge main into integration-batch-7 2026-10-23 20:37:05 +00:00
96042b8e7a fix(text): bound untrusted font and PSD text processing (#8768)
* fix(text): bound font paths and imported text processing

Reject encoded path tricks and cap resource-heavy font and PSD text processing to keep untrusted inputs bounded.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>

* fix(text): reject percent-encoded DEL in served paths; satisfy clippy chunks_exact lint

---------

Co-authored-by: Codebuff <noreply@codebuff.com>
2026-10-23 09:40:04 -04:00
David Mann 14d68f972b Merge PR #8035: CLI: reject ignored or conflicting mcp/serve launch options 2026-10-22 23:40:13 +02:00
David Mann 83b1850539 Merge PR #7455: photocraft-cli run: refuse an --out that is the input file unless --in-place (#7437) 2026-10-22 23:40:13 +02:00
David MannandClaude Opus 5.5 f56147655d Merge PR #5485: Add opt-out startup release checks and manual update status
Conflicts resolved:
- crates/ui-egui/src/i18n/hu.tsv: kept base's Bevel & Emboss keys, then the PR's update-check keys
- crates/ui-egui/src/i18n/ru.tsv: kept base's Bevel & Emboss keys, then the PR's update-check keys
- crates/ui-egui/src/i18n/sr.tsv: kept base's added sections, then the PR's update-check keys
- crates/ui-egui/src/i18n/tr.tsv: kept base's Bevel & Emboss keys, then the PR's update-check keys
- crates/ui-egui/src/menus.rs: kept base's saved-workspace entries, then the PR's Help › Check for Updates reordering
- docs/gaps.md: base status line with the PR's G17 clause prepended; PR revision row above base's rows
- docs/target-app-parity.md: base status line (updated 2026-10-22) with the PR's clause prepended; PR revision row above base's rows
- docs/scorecard.md: generated, took b7-base's version

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 23:38:46 +02:00
Khawar Zamman Wani 0b72779f3d Merge remote-tracking branch 'origin/main' into claude/run-out-refuses-input-7437 2026-10-22 23:27:01 +05:30
David Mann 9487065314 Integration: merge main (one test binary per crate, #8644)
# Conflicts:
#	docs/file-format-parity.md
#	docs/gaps.md
#	docs/scorecard.md
#	scorecard/file_compat.toml
2026-10-22 19:55:06 +02:00
Dylan M. TaylorandGitHub 6649b78526 Use chrono instead of hand-rolled UTC conversion (#8234)
* Use chrono for UTC/calendar conversion instead of hand-rolled copies

Replaces the civil_from_days copies behind the app's log timestamp, the
Measurement Log's date/time, and the perf report date with chrono as a
workspace dependency (reusing the version already in the tree). Output formats
are unchanged and pinned by the existing tests. AGENTS.md now says time goes
through chrono.

Follow-up to storytold/cadcraft#1785

* Never panic on a clock outside chrono's range; keep 5-digit years unprefixed

chrono's From<SystemTime> unwraps internally, so a clock past year 262143
panicked. logging::timestamp and analysis_cmds::now_iso now read the duration
since the epoch and go through the fallible DateTime::from_timestamp, reading
an out-of-range or pre-epoch clock as the epoch as the old saturating code
did. All three ISO helpers (timestamp, now_iso, perf::iso_utc) write the year
with {:04} rather than %Y, which prefixes a '+' past year 9999.

Tests cover the far-future and year-10000 cases for timestamp and iso_utc.

* Don't build an unrepresentable SystemTime in the far-future test

UNIX_EPOCH + Duration::from_secs(u64::MAX / 2) overflows Windows'
SystemTime and panicked before timestamp() ran, failing the Windows
test job. Build it with checked_add and only assert where it exists.

* time: preserve old out-of-range date conversion (no epoch fallback)

`DateTime::from_timestamp(...).unwrap_or_default()` silently reset any
instant past chrono's year range to 1970-01-01, changing output the old
hand-rolled Howard-Hinnant code converted exactly (e.g. 10_000_000_000_000 s
was 318857-05-20 17:46:40, not the epoch). Restore the old conversion domain
with a cycle-based helper that routes the calendar through chrono (the
calendar repeats every 400 years = 146_097 days, so month/day/time come from
chrono and only the year is carried in i128, keeping every i64 exact).

- apps/photocraft/src/logging.rs: `timestamp` via `utc_parts`; keep the
  pre-1970 epoch clamp; u64 secs past i64::MAX saturate, not epoch.
- crates/engine/src/analysis_cmds.rs: extract pure `iso_from_unix` (cycle
  based) from `now_iso` so the conversion is tested directly.
- xtask/src/perf.rs: `iso_utc` cycle-based, accepts the full u64 range the
  old code did.

Pinned old outputs in tests:
  logging u64::MAX/2 s -> 292277026596-12-04T15:30:07.000Z (was epoch),
          10_000_000_000_000 s -> 318857-05-20T17:46:40.000Z
  iso_from_unix 10_000_000_000_000 -> 318857-05-20T17:46:40Z,
          -10_000_000_000_000 -> -314918-08-13T06:13:20Z, i64 extremes
  iso_utc u64::MAX -> 584554051223-11-09T07:00:15Z (was epoch),
          10_000_000_000_000 -> 318857-05-20T17:46:40Z

* Don't build a year-318857 SystemTime unchecked in the logging test

Windows' SystemTime only reaches about year 30828, so
UNIX_EPOCH + Duration::from_secs(10_000_000_000_000) panicked before
timestamp() ran and failed the Windows test job. Build it with
checked_add and assert where the platform can hold it. The year-10000
case fits everywhere and stays unconditional.

* Name the 400-year range extension as the one exception to the time rule

The rule said never to hand-roll calendar math, but the range fix
(shifting by whole 400-year Gregorian cycles so dates past chrono's year
range keep their old output) is cycle arithmetic, so the rule and the code
disagreed. The rule now names that exception: the library still computes
month, day and time, and only whole cycles are carried in the year.

* Say the library computes month and day; time-of-day is plain arithmetic

Narrows the range-extension wording: the date library computes the
calendar fields, and splitting seconds into hours, minutes and seconds
is ordinary arithmetic, which is what the code does.
2026-10-22 13:54:10 -04:00
David MannandClaude Opus 5.5 f5533e5d82 Integration: merge main into batch 6
Main merged #5940, #6472, #7838 and #7922 directly. Kept from the batch:
the snapshot/History-follow identity at the history limit (#5940,
c7ef3676a; main still had the 3-tuple HistorySpot) and the strokeless
shape at a 0 px width step (#6472, 962539607; main still added the
stroke). Main already carries the other two fix-ups (34c9d0ea4, dff8bfd0e).

Overlaps with main resolved:
- #5842 vs main's #5568: one path, apply_color, tool-gated as #5842
  has it, with #5568's float fill, kept fill opacity and lock skip;
  shape_fill_ui's duplicate foreground_changed/gesture dropped; #5568's
  tests run with a shape tool.
- #6230 vs main's #5180: one Type tool path click (path_target), no
  second type.attachToPath; vertical type does not start type on a path,
  as main has it; main's path_at/path_cursor and canvas arm dropped.
- #5452 moved the shape swatch to shape_paint_ui.rs: main's hex field
  (#6201) ported there.
- type_extra_cmds::with_text takes main's dpi form, pub(crate) for
  type_area_cmds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 19:20:26 +02:00
Khawar Zamman Wani 0ccd1f2642 Merge remote-tracking branch 'origin/main' into claude/run-out-refuses-input-7437
# Conflicts:
#	apps/photocraft-cli/src/lib.rs
#	apps/photocraft-cli/tests/cli.rs
2026-10-22 22:31:14 +05:30
aiongg a6428d3dfb Build each crate's integration tests as one binary
Every tests/*.rs file is its own executable that links the whole crate
graph. Move each crate's test files into tests/it/ as modules of one
tests/it/main.rs; helper modules under tests/common move with them.

These keep their own binary (own process):
- engine panic_hunt: a process-wide panic hook counts every panic.
- engine text_gamma: changes the process-wide text gamma.
- engine smart_filter_stages, mask_cache_purge: assert on global caches.
- codecs pnm_peak_memory: reads the process's peak RSS.
- photocraft mac_menu_appkit: harness = false.
2026-10-22 16:55:34 +00:00
81925d7769 Integration: Raw Defaults resolution on Revert, Batch, Stack, headless open and CLI convert (#7811)
The resolution preference reached File › Open, but the other ways a raw
becomes a document of its own still opened it at 72 ppi: File › Revert
(a raw opened at 240 dropped to 72), Batch / Image Processor / Lens
Correction batch (a raw saved as PSD came out at 72 while Open + Save
gave 240), Load Files into Stack, the headless MCP `doc.open`, and the
CLI (`convert` at 72 while `run`/`info`, which open headless, now take
the default 300). They now apply the same `raw_defaults` rule; Batch
passes the user's value into its scratch sessions.

Tests that fail without this change (72 vs 240/300):
revert_batch_and_stack_open_a_raw_at_the_raw_defaults_resolution
(engine, one assertion per path), doc_open_gives_a_raw_the_raw_defaults_resolution
(automation), convert_opens_a_raw_at_the_raw_defaults_resolution (CLI).

Co-authored-by: Korg1ch <57859048+Korg1ch@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 15:42:42 +00:00
aiongg 9cb9535a95 Integration: #4315 DICOM gets its own File > Open filter 2026-10-22 15:39:30 +00:00
aiongg d87a169738 Integration: cargo fmt 2026-10-22 15:38:48 +00:00
aiongg 576ba0e02b Integration: track t12
# Conflicts:
#	contributors/people.toml
#	crates/compose/src/lib.rs
#	crates/compose/src/tests.rs
#	crates/engine/src/print_cmds.rs
#	crates/engine/src/print_cmds/tests.rs
#	docs/target-app-parity.md
2026-10-22 15:38:33 +00:00
aiongg 5857afd121 Integration: track t11
# Conflicts:
#	apps/photocraft-cli/src/lib.rs
#	crates/ui-egui/src/i18n/cs.tsv
#	crates/ui-egui/src/i18n/el.tsv
#	crates/ui-egui/src/i18n/es.tsv
#	crates/ui-egui/src/i18n/fr.tsv
#	crates/ui-egui/src/i18n/hu.tsv
#	crates/ui-egui/src/i18n/id.tsv
#	crates/ui-egui/src/i18n/ja.tsv
#	crates/ui-egui/src/i18n/ko.tsv
#	crates/ui-egui/src/i18n/nl.tsv
#	crates/ui-egui/src/i18n/pl.tsv
#	crates/ui-egui/src/i18n/pt-br.tsv
#	crates/ui-egui/src/i18n/ru.tsv
#	crates/ui-egui/src/i18n/sr.tsv
#	crates/ui-egui/src/i18n/tr.tsv
#	crates/ui-egui/src/i18n/uk.tsv
#	crates/ui-egui/src/i18n/vi.tsv
#	crates/ui-egui/src/i18n/zh-hans.tsv
#	crates/ui-egui/src/i18n/zh-hant.tsv
#	docs/gaps.md
2026-10-22 15:38:31 +00:00
aiongg 71991b0a03 Integration: track t10
# Conflicts:
#	contributors/people.toml
#	crates/engine/src/prefs.rs
#	crates/ui-egui/src/layer_style/contour.rs
#	docs/ui-parity.md
2026-10-22 15:38:28 +00:00
aiongg 1dc684a2b6 Integration: track t09
# Conflicts:
#	contributors/people.toml
#	crates/engine/src/atn_cmds/tests.rs
#	crates/engine/src/file_cmds.rs
#	crates/engine/src/jobs.rs
#	docs/gaps.md
#	docs/target-app-parity.md
2026-10-22 15:38:26 +00:00
aiongg 4d1b922ddf Integration: track t02
# Conflicts:
#	contributors/people.toml
#	crates/engine/src/file_cmds/tests.rs
#	crates/engine/src/print_cmds/tests.rs
#	crates/ui-egui/src/i18n/cs.tsv
#	crates/ui-egui/src/i18n/de.tsv
#	crates/ui-egui/src/i18n/el.tsv
#	crates/ui-egui/src/i18n/es.tsv
#	crates/ui-egui/src/i18n/fr.tsv
#	crates/ui-egui/src/i18n/hu.tsv
#	crates/ui-egui/src/i18n/id.tsv
#	crates/ui-egui/src/i18n/it.tsv
#	crates/ui-egui/src/i18n/ja.tsv
#	crates/ui-egui/src/i18n/ko.tsv
#	crates/ui-egui/src/i18n/nl.tsv
#	crates/ui-egui/src/i18n/pl.tsv
#	crates/ui-egui/src/i18n/pt-br.tsv
#	crates/ui-egui/src/i18n/ru.tsv
#	crates/ui-egui/src/i18n/sr.tsv
#	crates/ui-egui/src/i18n/tr.tsv
#	crates/ui-egui/src/i18n/uk.tsv
#	crates/ui-egui/src/i18n/vi.tsv
#	crates/ui-egui/src/i18n/zh-hans.tsv
#	crates/ui-egui/src/i18n/zh-hant.tsv
2026-10-22 15:38:14 +00:00
aiongg 82cf3c828c Integration: track t01
# Conflicts:
#	crates/ui-egui/src/i18n/cs.tsv
#	crates/ui-egui/src/i18n/de.tsv
#	crates/ui-egui/src/i18n/el.tsv
#	crates/ui-egui/src/i18n/es.tsv
#	crates/ui-egui/src/i18n/fr.tsv
#	crates/ui-egui/src/i18n/hu.tsv
#	crates/ui-egui/src/i18n/id.tsv
#	crates/ui-egui/src/i18n/it.tsv
#	crates/ui-egui/src/i18n/ja.tsv
#	crates/ui-egui/src/i18n/ko.tsv
#	crates/ui-egui/src/i18n/nl.tsv
#	crates/ui-egui/src/i18n/pl.tsv
#	crates/ui-egui/src/i18n/pt-br.tsv
#	crates/ui-egui/src/i18n/ru.tsv
#	crates/ui-egui/src/i18n/sr.tsv
#	crates/ui-egui/src/i18n/tr.tsv
#	crates/ui-egui/src/i18n/vi.tsv
#	crates/ui-egui/src/i18n/zh-hans.tsv
#	crates/ui-egui/src/i18n/zh-hant.tsv
#	docs/file-format-parity.md
#	docs/gaps.md
2026-10-22 15:38:14 +00:00
aiongg 0e80156397 Integration: track t16
Resolutions: CLI usage, flag lists and flag parsing carry both --tiff-compression (t15) and
--jpeg-format (t16); file.saveACopy, batch, imageProcessor and layersToFiles param specs carry
both tiffCompression and jpegFormat; file_cmds keeps tiff_compression_param and
jpeg_format_param; atn_cmds keeps flag_set/applies_at_canvas_bounds (t15) and UNHOLDABLE (t16),
edit.transform uses the pixel_target guard (t15) with layer_ref(p)? (t16); ui-egui open paths
build ImportOptions (t16) and apply raw_defaults (t15); docs headers/rows unioned; atn tests keep
both sides; tr.tsv "Keep Using CPU" kept once.
2026-10-22 15:38:13 +00:00
aiongg 3d1a4d6679 Integration: track t15
# Conflicts:
#	crates/engine/src/atn_cmds/tests.rs
#	crates/ui-egui/src/i18n/cs.tsv
#	crates/ui-egui/src/i18n/de.tsv
#	crates/ui-egui/src/i18n/el.tsv
#	crates/ui-egui/src/i18n/es.tsv
#	crates/ui-egui/src/i18n/fr.tsv
#	crates/ui-egui/src/i18n/hu.tsv
#	crates/ui-egui/src/i18n/id.tsv
#	crates/ui-egui/src/i18n/it.tsv
#	crates/ui-egui/src/i18n/ja.tsv
#	crates/ui-egui/src/i18n/ko.tsv
#	crates/ui-egui/src/i18n/nl.tsv
#	crates/ui-egui/src/i18n/pl.tsv
#	crates/ui-egui/src/i18n/pt-br.tsv
#	crates/ui-egui/src/i18n/ru.tsv
#	crates/ui-egui/src/i18n/sr.tsv
#	crates/ui-egui/src/i18n/tr.tsv
#	crates/ui-egui/src/i18n/uk.tsv
#	crates/ui-egui/src/i18n/vi.tsv
#	crates/ui-egui/src/i18n/zh-hans.tsv
#	crates/ui-egui/src/i18n/zh-hant.tsv
#	docs/file-format-parity.md
#	docs/ui-parity.md
2026-10-22 15:38:12 +00:00
aiongg a7c80a83d2 Merge PR #6791: Intel Iris Xe on DX12: no more red blocks in brush previews and tool icons (#6790) 2026-10-22 15:37:46 +00:00
6b551c11e8 Integration: MCP ui_set doc, no update check in Flatpak, DIST-220-1 partial (#5485)
- ui.set gained updatesOpen, but the MCP ui_set description didn't name it, so
  automation's ui_set_description_names_every_accepted_field failed on main.
- The Flatpak grants no network on purpose and `flatpak update` owns updates there,
  yet every launch started a check that could only fail and Help > Check for Updates
  stayed enabled. Flatpak builds now get no update transport (the item is disabled,
  as in the web build); flatpak_builds_have_no_update_transport pins it.
- The PR changed scorecard row DIST-220-1's target from "periodic" to "startup" and
  marked it done. The target is back as written and the row is partial: one check
  per launch, no periodic recheck while the app stays open. gaps.md G17 and the
  parity Platforms row say the same; docs/scorecard.md regenerated.
- Merge leftovers: a doubled Services::inbox doc line, and the Serbian
  release-check rows moved below main's newer rows to the end of sr.tsv.

Co-authored-by: ZhuanTou33212 <285931757+ZhuanTou33212@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 17:01:33 +02:00
6f56a4d750 Integration: merge main, resolve conflicts (#5485)
Main moved about 1,860 commits since the PR's last merge. i18n catalogs keep both
sides' rows; control.rs keeps updatesOpen next to main's strokeTrace (39 fields);
Services keeps check_updates next to main's place_inbox; Cargo.lock is main's,
re-resolved for ureq and semver; docs/gaps.md, docs/target-app-parity.md and
docs/scorecard.md are main's (the PR's entries come back in the next commit).

Co-authored-by: ZhuanTou33212 <285931757+ZhuanTou33212@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 16:36:42 +02:00
David MannandClaude Opus 5.5 7a54fa9c77 Integration: merge main into batch 6
Conflicts, both sides kept: main's .act swatches next to the batch's .asl
styles (Open filters, preset extensions), main's Render smart filters next
to the batch's Distort ones (53 mapped), parity doc status lines.
docs/scorecard.md regenerated.

Follow-ups the merge needed: the Distort smart filter readers pass their
enum type to main's stricter enum_name (the type their writers use), and
the CMYK effect tests set main's new StrokeFx overprint and Bevel
gloss_anti_alias.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 12:09:25 +02:00
aiongg 1033aab750 Integration: cargo fmt 2026-10-22 02:56:39 +00:00
aiongg 882cbe1c2c Merge PR #7240: Save As on Linux: the one file type offered gives the name its extension 2026-10-22 02:36:51 +00:00
aiongg dfde38919e Merge PR #7883: Save As JPEG: JPEG Options dialog (quality 0–12, Baseline / Optimized / Progressive) (FILE-215-19)
crates/ui-egui/src/lib.rs: replay_save doc comment names both Export As and JPEG; replay_save uses the PR's FormatChoices::from_step, after main's export_dialog::replay branch.
docs/file-format-parity.md: header keeps main's text plus the JPEG Options item (last updated 2026-10-19); TIFF row is main's, JPEG row is the PR's; change-log rows once each, newest first.
docs/gaps.md: header keeps main's text plus the G10 item (last updated 2026-10-19).
2026-10-22 02:27:46 +00:00
aiongg 7a25b3f38b Merge PR #7099: test(cli): pin photocraft-cli argument parsing before the clap port (#7071) 2026-10-22 02:27:40 +00:00
aiongg a77b078589 Merge PR #7417: Camera Raw: choose the demosaic method when a raw opens
crates/io/src/lib.rs: kept main's import_psd (and its call in import_stages); import_stages takes the PR's ImportOptions argument.
crates/io/src/raw.rs: the PR's demosaic summary on main's developed_document(name, bytes, ...) call.
docs/scorecard.md: ours (regenerated at combine).
2026-10-22 02:25:41 +00:00
aiongg 28a1ee7b6e Merge PR #6468: fix(photocraft): --control 0 logs the port actually bound (#6427) 2026-10-22 02:24:53 +00:00