172 Commits
Author SHA1 Message Date
Raghav Krishna aa12046847 Crop: Straighten gets its label and a spirit-level glyph (#10888) 2026-11-10 17:47:27 +05:30
TriwarrandFredApps 7c64653d5c Add toolbar side toggle (#11193)
* Add a toolbar side toggle and dock-edge resizing

- Let the toolbox sit on the canvas' left or right, persisted in the panel
  state and switchable from the toolbar header and preferences.
- In the compact arrangement, stack dock, rail and toolbox on the dock's
  physical edge so the toolbox cannot cover the rail.
- Make the dock panel's inner edge the single resize handle: it drives the
  panel width and the dock total (split size and column widths) in the same
  frame, so the rail and dock content stay bound with no reserved room and
  no second handle. The docking layout's own column-edge handle is covered;
  the bar is the theme separator at rest and the accent on hover/drag.
- Persist the layout, keep the split size in sync, handle RTL, and test the
  toolbar side state.

* Toolbar side: dock-edge only with a lone canvas; test every theme

- Use the between-dock-and-canvas layout only when nothing is docked beside the canvas: that view drops the canvas from craft-ui's split, so with a panel there the standard layout keeps it visible and dockable.
- Add canvas_alone_layout/canvas_alone and a unit test for a lone canvas versus a split canvas side.
- Add a kittest that toggles the toolbar Left/Right across every ThemeKind::ALL entry, asserting the panel lands on the requested physical side.
- No dependency changes: Cargo.toml and Cargo.lock are identical to main, so the repository's exact pins are untouched.

* Toolbar side: keep the canvas a drop target during dock drags

While a panel is being dragged, draw the standard layout even with the toolbar at the dock edge. It keeps the canvas in craft-ui's split, so a panel can still be dropped beside it; after the drop the canvas is no longer alone and the standard layout stays. The between-dock-and-canvas layout resumes for a lone canvas once the drag ends.

* Toolbar side: stable drag layout, unwind-safe dock flag, clippy

- Restore the dock-only thread-local through a drop guard, so an unwinding show does not leave a later standard layout canvas-less.
- Do not switch the dock layout mid-drag: craft-ui records its areas on the press, and changing the shape cancels the drag. The layout stays stable; a panel beside the canvas still selects the standard layout before the drag begins.
- Fix the CI clippy errors: explicit auto-deref in end_dock_node/resize_end_dock and field reassignment in the toolbar_side test.

* Toolbar side: anchor the Edit Toolbar flyout; borrow the canvas layout

- more_slot_flyout takes the resolved ToolbarSide instead of the text direction, so a right-hand toolbar in LTR (and a left-hand toolbar in RTL) anchors the flyout away from the canvas like the tool flyouts.
- canvas_alone borrows the saved layout and treats the native-dock legacy layout as canvas-only, avoiding a per-frame clone and traversal.

* Toolbar side: only a horizontal canvas split uses the dock-edge layout

A saved workspace can put the canvas above another panel in a vertical root split; canvas_alone_layout now requires SplitAxis::Horizontal so that shape keeps the standard layout. The guard test covers the vertical case.

* Toolbar side: document the functions this PR adds

Covers ToolbarSide::resolve/other, canvas_alone_layout, the DockOnlyGuard drop, and the two new tests. Docstring coverage of the PR's own additions is now complete; no behaviour change.

* docs: refresh the scorecard after merging main

The merge left docs/scorecard.md stale against the combined settings table; regenerated with cargo xtask scorecard, which is what the gate's scorecard step checks.

* Toolbar side: honor the workspace lock, empty docks and the end-dock shape

- The dock-edge resize handle is inert while the workspace is locked (hover sense, no highlight) and set_end_dock_total refuses to write then; the drag test covers a locked workspace.
- An empty dock takes only the rail's width instead of a clamped blank column; dock_is_empty reports it and a test empties the dock.
- canvas_alone_layout now requires the second child to be the Dock itself, and end_dock_node/resize_end_dock follow horizontal splits only, so a layout the handle cannot resize keeps the standard layout instead of snapping back.
- Tests: DockOnlyGuard restores the flag on drop and unwinding; the theme test clicks the toolbar's move button instead of setting state; a dock-edge harness drags the edge, checks it persists and that a locked workspace refuses it; axis tests and a two-column ui.set {dockWidth} test.
- The flyout indicator follows the resolved toolbar side, the rail keeps its chrome fill and 4/8 inset with the separator inside it, ATTRIBUTION counts the new icon (116), and docs/control-protocol.md describes dockWidth as the whole end dock.

* Toolbar side: floating windows on empty docks, iconic widths, dockWidth columns

- An empty dock still runs the docking pass on the full remaining area, so Studio's floating Properties window keeps rendering with or without the rail; the dock column itself takes no width. A Studio regression builds an empty dock with a floating Properties group and asserts the pass runs and the window is drawn.
- Dock width now uses each column's displayed width (iconic_width while collapsed to icons), and resizing/scaling keeps iconic widths in step. A collapse-to-icons regression checks a 36-point column not reserving 290.
- ui.set {dockWidth} scales every column to the requested total directly instead of resizing only the first column and then finding the sum already equal. The two-column test also covers a target smaller than the other column's expanded width (250 from [300, 180]) and asserts per-column widths.

* Toolbar side: iconic widths, edge release and modal handling, side persistence

- An iconic-only dock keeps its narrow displayed width instead of being raised to the expanded
  minimum, and the dock-edge panel records its laid-out width for a geometry regression.
- The release frame applies its final pointer position before dropping the drag, so the dock keeps
  the width the user released at.
- The dock-edge handle is inert (and any stored drag is dropped) while a modal dialog is open.
- set_display_width changes iconic_width while iconic and leaves the columns expanded width for
  expanding again; the regression resizes an iconic column and checks the restore.
- Clicking the toolbar side persists the panels snapshot immediately through persist_panels, even
  when the dock is not drawn, while Remember Workspace Changes is on.
- The locked-drag test compares against the pre-drag width instead of only settled equality.

* Toolbar side: defer persistence while moving, translate the side labels

- persist_panels defers while the dock persistence pass sees a native window move/resize (the
  same native_moved signal), so a click during an OS resize cannot save intermediate geometry;
  it requests the usual 100 ms repaint and leaves the dock pass to save when the geometry is
  stable.
- The toolbar-side tooltip/accessible labels now pass both literals directly to tl!, and all 34
  registered catalogs carry translations for Move Toolbar to the Right/Left.

* Toolbar side: keep in-progress dock geometry out of toolbar saves

- native_moved caches its answer for the frame, so the dock pass and persist_panels see the same
  movement result even though only the first call observes the change.
- persist_panels defers while a dock-edge drag is stored, a pointer is down or a native window
  moved; it stores a pending flag and requests the usual 100 ms repaint.
- persist_pending runs in the frame tick (before preferences save) and retries the deferred save
  once geometry is stable, so the toolbar side is not lost even when the dock was not drawn.
- The dock persistence pass also skips while the dock-edge drag start is stored, covering a
  synthetic/accessibility click with no pointer button down.

* Toolbar side: keep a same-width dockWidth request a no-op

The dockWidth rewrite scaled and committed the legacy layout even when the requested width
already matched, so a later `ui.set {dockTabs}` took the customized-workspace path and expanded
the Properties group. Restore the early return for an unchanged displayed width and cover it:
setting the current dock width keeps `app.ui.docking` None.

Fixes the `a_new_adjustment_layer_brings_properties_to_the_front` failure on CI.

* Toolbar side: recheck native bounds, expire stale dock-edge drags

- native_moved keeps movement already seen in the frame but compares the bounds again on every
  call, so a later dock save is not fooled by the early persist_pending check caching false
  before the docking UI updates a floating window.
- dock_panel records the frame it rendered; persist_pending drops a stored dock-edge drag start
  once the panel stopped rendering (Shift+Tab hides the dock mid-drag), so deferred saves resume.
- The same-width dockWidth test also selects a tab in a collapsed group and asserts the tab,
  the collapsed state and that no workspace was customized.

* Toolbar side: draw the release width, clear drags when the dock empties

- The release frame draws the panel at the final pointer width just stored, instead of the width
  read before the drag ended, so the panel and toolbar match the saved dock width.
- The empty-dock path clears a stored dock-edge drag start, so a dock that empties mid-drag cannot
  leave deferred saves blocked.
- Tests: nested DockOnlyGuard (flag found set stays set), and a queued `ui.set {dockWidth}`
  through the rendered dock pass checking a two-column dock totals 420 points.

* Toolbar side: cancel the dock-edge drag when resizing is unavailable

- A disabled parent UI (screen picking) never delivers drag_stopped, so dock_panel now clears a
  stored dock-edge drag whenever resizing is unavailable: locked, modal, empty dock or disabled
  UI. The same condition makes the handle hover-only, so it cannot start a new drag while
  disabled.
- Regression: start a resize, disable the UI, release, re-enable and move the pointer; the drag
  is cleared, the dock does not jump and persistence can resume.

* Toolbar side: bound the dock-edge layout and enforce column minima

- The dock-edge panel leaves the toolbar and the 200-point canvas minimum room, so a narrow
  window shrinks the dock instead of the canvas.
- The empty dock runs the docking pass in a child ui that does not consume the parent layout, so
  the toolbar and canvas keep their space.
- Column scaling keeps the 220-point panel minimum: minima are allocated first, the rest is shared
  proportionally, and requests below the sum of minima stop there.
- The dock metrics (empty, iconic-only, width) come from one layout snapshot per frame.
- Removed dead empty-dock branches, narrowed the content-edge interceptor to one point either
  side, corrected the toolbar target comment, used the drag press origin, and documented the
  Side::End assumption.
- Tests: narrow-window canvas minimum, empty dock composed with toolbar and canvas, minimum-aware
  two-column widths, and the empty compose regression.

* Toolbar side: bound the dock-edge layout, enforce panel minima, match iconic widths

- The dock-edge panel leaves the toolbar and the 200-point canvas minimum room, so a narrow
  window shrinks the dock instead of the canvas.
- The empty dock runs the docking pass in a child ui that does not consume the parent layout, so
  the toolbar and canvas keep their space.
- Column scaling allocates each column's 220-point panel minimum first and shares the rest
  proportionally; requests below the sum of minima stop there.
- Iconic columns use craft-ui's renderer rule: iconic_width.unwrap_or(36).clamp(36, 240), including
  the real collapse case that leaves iconic_width unset.
- Dock metrics come from one layout snapshot per frame; dead empty-dock branches are gone; the
  content-edge interceptor is one point either side; the toolbar target comment is corrected and
  the Side::End assumption documented.
- Tests: narrow-window canvas minimum, empty dock composed with toolbar/canvas, minimum-aware
  two-column widths, collapse via Action::SetIconic leaving iconic_width unset, and the 36..240
  clamp. The press-origin change was reverted: Sense::drag starts immediately.

* Toolbar side: distribute refused width, draw actual widths, invalidate stale drags

- scale_columns moves the total toward the request in passes, freezing columns that hit a bound
  (iconic 36..240, expanded minimum 220), so width refused by the iconic cap reaches columns that
  can still grow.
- set_end_dock_total returns the width actually stored; the dock panel uses that width during the
  drag, on release and after, instead of forcing the requested or minimum width.
- The custom drag stores docking_generation; a workspace replacement clears the stale gesture
  before any width mutation.
- Clamp bounds are normalized against the available maximum (including iconic columns) and
  resizing is disabled when the window cannot fit the column minima, so narrow layouts cannot
  panic.
- Tests: iconic-cap surplus redistribution, actual-vs-requested panel geometry during and after a
  drag, workspace replacement during a drag, and a 300pt window resize without panic.

* Fix dock-edge resize bounds and cancellation

* Preserve explicit dock width across legacy migration

* Document dock code, dedupe widths, harden toolbar_side load

- Add doc comments to the functions and tests this PR touches (Docstring Coverage).
- Share the toolbar width (toolbar_width/toolbar_columns), the rail widths and
  CANVAS_MIN_WIDTH instead of repeating the values; no values change.
- Document dockWidth as the requested model width, which a narrow window may draw
  narrower in either layout, and test model 520 / drawn <= 364 / canvas >= 200.
- Document that a dockWidth change is shared equally among columns within their
  minimums and iconic caps; tie ICONIC_WIDTH/ICONIC_MAX_WIDTH to craft-ui's
  DockStyle defaults (36/240) in a test.
- Replace the DOCK_ONLY thread-local and its guard with show_layout(dock_only).
- Move the test-only probe writes behind panel_docking::record, with no-op stubs
  outside tests.
- Read an unknown or malformed saved toolbar_side as Start instead of dropping
  the whole panel state; ui.set still refuses it.

* Test visible dock chrome in narrow windows; name panel body minimum

- The narrow-window dockWidth test now checks the docking controls craft-ui drew:
  hit_rects are full widget rects (before clipping), so the visible part of each,
  intersected with the dock, must stay inside the dock and off the canvas and
  the toolbox, with at least one control visible.
- Name the 200-point guard for drawing a panel's body PANEL_BODY_MIN_WIDTH,
  separate from CANVAS_MIN_WIDTH; no value change.

* test(ui-egui): assert dock controls are clipped at the panel edge

The narrow-window test intersected each hit rect with the dock and then asserted the
intersection lies inside the dock, which always holds. Check each dock control's clipped
interact_rect against the dock instead, and add a squeezed-column test where a control's
full rect runs past the panel edge, so the clip is actually exercised.

* ci: retrigger pull_request workflows after resolving the merge conflict

No changes. GitHub did not queue Actions for 3652ede, the push that
resolved the conflict with main.

* test(ui-egui): assert saves resume after a disabled UI cancels a dock-edge drag

The test's doc says saves must resume once a disabled UI drops the held drag, but nothing
checked it. Assert that the held drag is not saved, that its width is saved as soon as the
drag is cancelled (while the UI is still disabled), and that a width change made after
re-enabling reaches the saved layout too.

* test(ui-egui): cancel a dock-edge drag on a window shrink and check the dockWidth column split

constrained_width_does_not_panic_while_resizing now starts the drag in a wide window and
shrinks the window below the column minima while it is held: the drag must be cancelled and
the dock must stay within the window. Both ui_set_dock_width tests now check how the change is
split between the columns, not only the total.

* test(ui-egui): tighten dock tests per CodeRabbit review 5824274070

- disabling_the_ui_cancels_a_dock_edge_drag: assert the drag is cancelled on the first disabled
  frame, before the pointer is released.
- all_iconic_totals_stop_at_both_bounds_without_losing_expanded_widths: check the stored column
  widths sum to the expected total after each request.
- explicit_width_request_preserves_queue_and_native_state_until_invalid_workspace_recovery: add
  a changed-width (520) case next to the no-op one.
- the_toolbar_side_toggle_works_in_every_interface_theme: with the toolbar on the right, a grouped
  tool's flyout opens to the left of its button.

* test(ui-egui): assert stored widths and native rect per CodeRabbit review 5824824938

- constrained_width_does_not_panic_while_resizing: the stored column widths captured before the
  window shrinks are unchanged after the cancel and after the release.
- explicit_width_request_preserves_queue_and_native_state_until_invalid_workspace_recovery: the
  native state keeps the exact rectangle the test saved until recovery, not just some state.

* Keep the toolbar side when a workspace saved none

A workspace saved before the toolbar could move has no panels.toolbar_side,
so applying it reset a right-hand toolbar to Start. Keep the current side
when the saved value is missing or unreadable; a valid saved side still wins.

* Cover a saved start side and the other panel flags on workspace apply

---------

Co-authored-by: FredApps <FredApps@users.noreply.github.com>
2026-11-09 15:51:29 +01:00
rmsff 08b1149175 fix(ui): vertical text gets Top / Center / Bottom alignment buttons (#12059)
The Type options bar always showed Left / Center / Right. As in the
reference application, for vertical text it now shows "Top align text",
"Center text" and "Bottom align text" with vertical glyphs: when the
edited or selected text layer is vertical, or with no text targeted
when the Vertical Type tool is active. The buttons still set left /
center / right and the lit one follows the targeted paragraph. The Type
Mask tools keep today's buttons (not measured).

The orientation is read from the target's own document, as the
paragraph style is, so a text edit left in a background document is
not judged by a same-id layer of the active one.

The three glyphs (vertical-align-{top,middle,bottom}.svg) are original
work drawn on Lucide's grid and stroke, with their ATTRIBUTION.md row;
they are kept out of the Lucide row's wildcard.
2026-11-08 15:55:59 +03:00
Triwarr ca92d936c0 PDF: import raster pages through craft-pdf instead of PDFium
- craft-pdf (the shared Rust/Hayro reader in craft-libs, rev c5a1cbf) renders each page; no shared library is loaded or shipped, and every desktop platform works.
- Raster pages only: editable text/vector import and the pdf_text/pdf_vectors options are removed (they can return later through craft-pdf's interpret device). Page geometry, artboards, page range, budgets, cancellation and warnings are kept; the effective DPI now respects craft-pdf's 8192 px side and 64M px page limits.
- Drops pdfium-render, the PDFium download in CI, the build-script copy and the packaging/licence staging; records the Hayro/fonts/CMaps/ICC assets in ATTRIBUTION.md and NOTICE.
- \.ai\ files starting with %PDF- now take the PDF importer, matching the comment.
- Attribution, architecture and file-format-parity docs updated.
2026-11-07 19:41:22 +01:00
David Mann e6ba4aca42 Merge pull request #5845 from devius/ka-language
i18n: Georgian (ka) interface language
2026-11-07 19:19:01 +01:00
Sven Carstensen 2928191557 Merge main and retain guide keyboard and exact-position fixes 2026-11-07 11:54:29 +01:00
Davit Matchakhelidze 20bbd27c84 Merge upstream main into ka-language 2026-11-07 02:53:58 +04:00
David Mann 6b0f7762e1 Merge pull request #11671 from davidmann4/integration-batch-20
Integration batch 20: 7 i18n PRs (Thai, Hebrew, Urdu, Tamil, Azerbaijani, Romanian, Korean)
2026-11-06 22:49:42 +01:00
Sven Carstensen 779a27e7fb Limit guide dialog changes to keyboard editing and exact positions 2026-11-06 20:14:37 +01:00
David MannandClaude Opus 5.5 c7b5373ba8 Crop: Contextual Task Bar under the crop frame, as a reusable task-bar widget (#10890)
The Crop tool gets the reference application's contextual task bar: a small floating
bar centred 11 pt under the crop frame with a grip, the preset dropdown (the options
bar's own, so both stay in sync), Straighten, a ⋯ menu (Hide bar, Reset bar position,
Pin bar position) and, while a crop is pending, Cancel and Done. It follows the frame,
stays inside the canvas, and stays put once dragged or pinned. Window › Contextual Task
Bar (after Tools, as in the reference) shows and hides it.

The bar itself is a reusable widget (widgets/task_bar.rs): position rule, grip, ⋯ menu
and the hide/pin/place state in Panels (kept with the panel layout across restarts,
read by ui.inspect › taskBar, set by ui.set {panels}). A context supplies only its
controls and where ⋯ goes; the Crop contents are tools/crop_task_bar.rs. Generative
Expand stays out until #41.

Also: Lucide pin and rotate-ccw icons, six new strings in all 27 catalogues, parity
661/661 (FLOOR raised), control-protocol docs. Three tests about the options bar's
Ratio dropdown now hide the task bar, which shows the same dropdown.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 17:50:20 +01:00
f236f0f6a4 Integration: Thai test compiles; Tamil catalog rows, plurals and attribution (#10241, #10359)
#10241: `th.0.plural(5)` calls a function-pointer field with method syntax
(E0599), so the ui-egui test target did not build; call it as `(th.0.plural)(5)`
(CodeRabbit). Add the ATTRIBUTION.md row for th.tsv and Thai to the licence
header (review ask).

#10359, from the review asks on the PR: two rows used spaces instead of a TAB
(Layer Mask Display Options..., Proportional Lining) and two had an empty
fourth column; "Scratch: {size}" translated the placeholder, so it rendered
literally; "Zoom In (hold, drag to scrub)" was split into a second "Zoom In"
key; "Print Settings..." lost its trailing ellipsis to a space. Tamil counts
take one|other, so the 28 two-form @plural rows now use plural_one_other; the
entry moves to the end of the registry. Adds the ATTRIBUTION.md, licence and
docs/localization.md rows, and tamil_tags_plurals_and_placeholders, which fails
on the old plural rule and placeholder.

Co-authored-by: Thanawatttt <89511939+Thanawatttt@users.noreply.github.com>
Co-authored-by: NewsBaba <346041388+newsbaba24x7@users.noreply.github.com>
Co-authored-by: Tanish Manku <13985398+tanish2k09@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 17:32:39 +01:00
David Mann c9e48117dd Merge PR #10555: i18n: add Azerbaijani UI language
# Conflicts:
#	crates/ui-egui/src/i18n/LICENSE-translations.txt
#	docs/localization.md
2026-11-06 17:23:00 +01:00
David Mann bcf818cca4 Merge PR #10547: i18n: add Romanian (ro) UI translation
# Conflicts:
#	ATTRIBUTION.md
#	crates/ui-egui/src/i18n.rs
#	crates/ui-egui/src/i18n/LICENSE-translations.txt
#	docs/localization.md
2026-11-06 17:22:40 +01:00
David Mann 50d7ff9722 Merge PR #10361: i18n: add Urdu (ur) UI translation
# Conflicts:
#	ATTRIBUTION.md
#	crates/ui-egui/src/i18n.rs
#	crates/ui-egui/src/i18n/LICENSE-translations.txt
#	crates/ui-egui/src/text/direction/tests.rs
#	docs/localization.md
2026-11-06 17:22:15 +01:00
Davit MatchakhelidzeandClaude Sonnet 5.5 dd10f81580 Drop crates/text font changes (now in #8812); add ka.tsv ATTRIBUTION row
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
2026-11-06 11:57:49 +04:00
tronicumandClaude Opus 5.5 653a177e6f Romanian: complete the catalog, credit and disclose the translation
- Translate the 118 strings added on main since the catalog was finished
  (Romanian now covers 2,345 of 2,345 keys).
- Register Bosnian and Romanian together in LANGUAGES (29 entries after the merge).
- Add ATTRIBUTION.md, LICENSE-translations.txt and docs/localization.md rows
  (plurals one / few / other), and note in the catalog header that it is
  machine-translated and not yet reviewed by a native speaker.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 07:07:10 +01:00
tronicumandClaude Opus 5.5 7649a728e4 Hebrew: complete the catalog, credit and disclose the translation
- Translate the 118 strings added on main since the catalog was finished
  (Hebrew now covers 2,345 of 2,345 keys).
- Register Bosnian and Hebrew together in LANGUAGES (29 entries after the merge).
- Add ATTRIBUTION.md, LICENSE-translations.txt and docs/localization.md rows,
  and note in the catalog header that it is machine-translated and not yet
  reviewed by a native speaker.
- Restore the lost backslash in the header's escapes line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 07:07:10 +01:00
tronicumandClaude Opus 5.5 8f342c5939 Urdu: complete the catalog, credit and disclose the translation
- Translate the 118 strings added on main since the catalog was finished
  (Urdu now covers 2,345 of 2,345 keys).
- Register Bosnian and Urdu together in LANGUAGES (29 entries after the merge).
- Add ATTRIBUTION.md, LICENSE-translations.txt and docs/localization.md rows,
  and note in the catalog header that it is machine-translated and not yet
  reviewed by a native speaker.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 07:01:02 +01:00
Taxperia 25ed8e5cb8 Merge upstream main and verify Azerbaijani command translations 2026-11-05 19:49:51 +03:00
SvenCarstensen cf69b5fd0b Merge branch 'main' into codex/guide-position-units 2026-11-05 17:19:54 +01:00
davidvhkandFredApps f1137013a2 feat(paint): add History Brush opacity/flow controls, source selection, and custom icon (#4446)
* feat(paint): add History Brush opacity/flow controls, custom icon, and options bar source label

- Options bar and keyboard shortcuts (digits 0-9 and Shift+digits) for History Brush opacity and flow %
- Options bar displays translated source state or snapshot name with missing-source hint when deleted
- Dedicated clean-room history-brush icon combining Lucide brush with counter-clockwise rewind arc
- Preserved JetBrainsMono attribution in ATTRIBUTION.md and added history-brush icon attribution
- Add contributor credit in people.toml

* History Brush options bar: rustfmt the source label

* Opacity keys: 00 gives Clone Stamp and History Brush their 1% minimum

Their Opacity fields and the retouch command start at 1%, so 00 set a 0%
the stroke then painted at 1% (CodeRabbit). The Brush still goes to 0%.

---------

Co-authored-by: FredApps <FredApps@users.noreply.github.com>
2026-11-05 05:29:57 +01:00
a331258dff bs: Add Bosnian (bs) to the interface languages (#10290)
* Add Bosnian (bs) to the interface languages

bs.tsv: 4,552 rows, Latin script, Ijekavian, the same keys as the newest
catalogs. Wording shared with storytold/craft-i18n locales/bs/; the 306
strings craft-i18n doesn't have yet are new. Every Bosnian locale,
Cyrillic included, resolves to it, as for Serbian; plurals reuse the
one/few/other rule of plural_russian.

Locale and plural tests, live locale case, localization docs and an
ATTRIBUTION row (AI-assisted, not yet reviewed by a native speaker).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013Tayw8nmxoLLmKv13jK93z

* Add Bosnian to the translation license notice; document the locale test

LICENSE-translations.txt now lists the Bosnian catalog and its
provenance (shared with storytold/craft-i18n locales/bs/, AI-assisted,
not yet native-reviewed), as ATTRIBUTION.md points to it. The touched
live locale test gets a doc comment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013Tayw8nmxoLLmKv13jK93z

* bs: wording from the craft-i18n catalog review

The Bosnian catalog review (storytold/craft-i18n#72) asked for one word
per concept; this catalog follows: pan is "pomjeranje" (not
"pomicanje"), a spreadsheet is "proračunska tabela", Greek & Cyrillic
reads "Grčko pismo i ćirilica". Placeholders and ellipses are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013Tayw8nmxoLLmKv13jK93z

* bs: translate the 30 strings added on main; match Camera Raw labels

Main gained Path operations, the SVG smart object editor, Esc/Return pen
status messages and more since this catalog was written, so the
every-language tests (esc_ends_a_refused_draft_and_return_keeps_it) failed
on bs. Five cameraRaw-context entries (White Balance: As Shot, Tone
Histogram, Show Selected Region, Opened {name}, Loaded Camera Raw settings
from {file}) differed from the plain entries the panel actually draws, which
failed the Camera Raw localization test; they now match.

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: FredApps <FredApps@users.noreply.github.com>
2026-11-05 05:16:02 +01:00
Raghav Krishna 1d86b32bab Crop: give each overlay its own glyph in the menus and on the options button
Six original icons in the house style, one per crop overlay, drawn in the
crop-mark frame around the guide they name. They sit between the check mark
and the label in the options-bar overlay menu and in the canvas Crop menu, and
the options-bar button paints the overlay in use.

Tests: crates/ui-egui/tests/it/crop_overlay_icons.rs (#10889).
2026-11-03 15:52:40 +05:30
David Mann 5bc852379a Merge PR #10743: Read Photoshop Actions version 12 descriptors
With our fix-up 1f524c378b on top of the author's head 713aa92ba5. Resolved with main: the version check sits in main's parse_layout (it merged cleanly); doc comments combined; atn_contract no longer expects version 12 to be refused and pins that an empty version 12 set parses; one ATTRIBUTION row for all ATN fixtures; the fixture README states which files were encoded how; development.md no longer counts the fixtures.
2026-11-02 02:13:56 +01:00
1f524c378b Integration: track the v12 .atn fixtures and sniff v12 in the CLI (#10743)
The three version 12 fixtures were tracked inside crates/psd/fuzz/corpus,
which crates/psd/fuzz/.gitignore ignores and cargo-fuzz rewrites (cmin) or
deletes; the psd and engine test binaries include_bytes! them, so cleaning
the corpus broke the build. They move to crates/psd/tests/fixtures/atn with
a README (sizes, purpose, sha256; the seeds note folded in) and an
ATTRIBUTION.md row; the fuzzer takes the folder as an extra corpus dir.

photocraft-cli recognised an extensionless Actions file only by version
word 16, so a version 12 set without .atn (classic Mac OS Photoshop 5)
went down the JSON path and failed with a UTF-8 error. It now accepts 12
and 16, the versions photocraft-psd reads. Input: v12-commands.atn copied
to "Legacy Actions" and passed to load_actions_file.

Co-authored-by: yoriswind <164435053+yoriswind@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 02:09:41 +01:00
4b053e15ae Integration: keep the ATN contract tests, drop the extra workflow (#7561)
The contract tests no longer compiled on main (AtnItem::dictionary is now
Vec<u8>, so it is compared with b"Future event"), sat outside main's
tests/it layout, and the new atn.yml added six legs (two macOS) to every
PR against ci.yml's policy. Move the tests into tests/it so the existing
CI runs them, drop atn.yml and docs/atn-testing.md, keep main's ci.yml and
fuzz target, and note the committed fixtures in ATTRIBUTION.md.

Co-authored-by: denstu473 <63150090+denstu473@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 14:25:51 +01:00
David Mann 893d12bee2 Merge main into #7561 2026-11-01 14:19:49 +01:00
Taxperia 081dd26d57 i18n: limit language contribution to Azerbaijani 2026-11-01 11:27:12 +03:00
SvenCarstensen 9c7590105c Merge branch 'main' into codex/guide-position-units 2026-10-31 20:48:47 +01:00
Taxperia 5d54be710c i18n: add Azerbaijani and Romanian UI languages 2026-10-31 22:26:12 +03:00
David Mann e5b4ea6781 Merge main at e7b7cf11e into integration batch 10
i18n catalogs: main's new rows (#10328) appended after the batch's, as a union.
2026-10-31 02:05:31 +01:00
David MannandClaude Opus 5.5 d9cb0edd8d Liquify: no Twirl Counterclockwise tool, Alt with Twirl Clockwise does it
The reference application's Liquify has Twirl Clockwise only; holding
Alt (Option on macOS) while using it twirls counterclockwise. PhotoCraft
had an extra Twirl Counterclockwise tool on the strip, and #9598 had just
given it a mirrored icon.

Twirl Counterclockwise leaves the tool strip (LiquifyTool::ALL, 11 to
10 tools) and the rotate-ccw icon #9598 added goes with it (asset,
icon_data entry, ATTRIBUTION count back to 113). Alt already reversed
Twirl Clockwise at stroke-down; that stays. The TwirlCcw variant stays as
a stroke's direction: Alt-strokes record `twirlCcw`, and filter.liquify
strokes and older saved strokes naming it still replay as Twirl
Clockwise reversed (the command doc says so). Selecting it as the tool
(control channel `tool: "twirlCcw"`, or a preference saved by an older
version) is refused with an error that says to choose twirlCw and hold
Alt; the rest of the settings still apply and the dialog opens.

Tests: the_tool_strip_has_no_twirl_counterclockwise (no such tool, label
or missing icon on the strip, C is Twirl Clockwise, `twirlCcw` as the
tool is an Err and leaves the tool, an old saved preference naming it
opens); alt_with_twirl_clockwise_twirls_counterclockwise (the field
displacement a dab leaves right of the centre points up without Alt and
down, mirrored, with Alt). twirl_counterclockwise_has_its_own_mirrored_icon
and the twirlCcw row of alt_reverses_twirl_and_swaps_pucker_and_bloat
are removed. The i18n labels for Twirl Counterclockwise stay (the
variant's label).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 16:34:03 +01:00
David Mann c5ad3ed53b Merge PR #9274: Support editing selected color channel sets
# Conflicts:
#	docs/gaps.md
#	docs/ui-parity.md
2026-10-30 14:28:46 +01:00
David Mann b2db24400e Merge main at b1cfff9d3 into integration batch 10
Conflicts resolved as unions of both sides: codecs re-exports (PngRows + TiffBands), smart_cmds source imports
(main's source_surface_from_buffer and cache API with #8571's svg_source_image_in/convert_svg_source; main's
duplicate rendering import dropped), io flat.rs test modules (#8536's banded TIFF tests and main's PNG ones),
menus/invoke.rs (#8571's smart SVG intercept and main's layer.selectUnderCursor), state.rs (#8311's
type_box_overflow and main's type_insertion_style), the canvas_geom test keeps the is_finite guard, docs
status lines keep both change lists, i18n catalogs keep both sides' rows (hi.tsv: main's wording where both
added the same string).
2026-10-30 14:28:02 +01:00
David Mann f5ae6d5276 Merge main into pr-9274-fixup
# Conflicts:
#	crates/ui-egui/src/canvas.rs
#	crates/ui-egui/src/dialogs/adjust_editors/curves.rs
#	docs/gaps.md
#	docs/target-app-parity.md
#	docs/ui-parity.md
2026-10-30 13:16:39 +01:00
David Mann fea300dd4c Merge origin/main into integration batch 10 2026-10-30 01:47:01 +01:00
FredApps 0cb4ec56d3 Merge current main and complete retained Pen messages in Nordic catalogs 2026-10-30 00:48:24 +01:00
c0fb32db24 Finnish, Danish and Norwegian Bokmål interface languages, combined from the community catalogs
Finnish (fi) combines #5666 and #6961, Danish (da) is based on #7267 and
completed for the current UI, Norwegian Bokmål (nb) combines #8072 and
#9025. Each catalog covers the same 4,552 keys as the Swedish one, with one
term per concept. `no` and `nn` locales resolve to Bokmål; `fi-FI` to
Finnish while `sv-FI` stays Swedish.

Co-authored-by: Janiz-zzz <267742026+Janiz-zzz@users.noreply.github.com>
Co-authored-by: metuuu <25014162+metuuu@users.noreply.github.com>
Co-authored-by: pechnikov2026-star <266495960+pechnikov2026-star@users.noreply.github.com>
Co-authored-by: nikolainyegaard <6809008+nikolainyegaard@users.noreply.github.com>
Co-authored-by: Bolla76 <200539769+Bolla76@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 19:34:10 +01:00
David Mann afacb057f7 Merge origin/main into pr-8571-fixup
Conflicts: svg.rs (main's bufread inflate and shared render helper, kept
with the PR's bounded raster path), psd_import.rs (the PR's vector
preview moved into main's smart_content), menus/invoke.rs (the SVG
intercept moved into main's split invoke_unguarded), rendering.rs and
the ui-egui import lists (main's explicit imports), Cargo.lock, docs
status lines (unions).
2026-10-29 16:38:24 +01:00
David Mann 601f2ed7f9 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	crates/ui-egui/src/i18n.rs
#	crates/ui-egui/src/i18n/LICENSE-translations.txt
#	crates/ui-egui/src/i18n/live_tests.rs
#	crates/ui-egui/src/i18n/system.rs
#	docs/localization-parity.md
2026-10-29 15:41:15 +01:00
FredApps 93f11e42aa Adapt Frequency Separation module registration and finish icon attribution reconciliation 2026-10-29 04:22:09 +01:00
FredApps 7f1f867b1d Merge approved PhotoCraft PR #9598: fix(ui): Liquify's Twirl Counterclockwise gets a mirrored twirl icon
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Preserve current attribution exclusions and credit the mirrored Lucide Twirl Counterclockwise icon; keep both Liquify regressions.
2026-10-29 04:21:48 +01:00
FredApps 86fcbf26ee Merge approved PhotoCraft PR #7648: Pen: Path Operations menu, as in Photoshop (#1397) 2026-10-29 03:29:31 +01:00
Vlad Grankovsky 94c1ddcb2f Merge origin/main into codex/multi-channel-editing
Integrate main 69b8fd9c1, retaining multi-channel selection and editing through extracted channel-panel, Move-pointer and tone-editor helpers. Adapt explicit imports and preserve progress-document histories.
2026-10-29 00:37:26 +01:00
David MannandClaude Opus 5.5 95b1109d9c Swedish catalog: name both translators in ATTRIBUTION.md and cover sv in the license grant
Review findings on #10021.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 00:11:13 +01:00
5ab3025bcf Swedish interface language, combined from the community catalogs
Adds Swedish (sv, Svenska) as an interface language. The catalog combines
the open Swedish catalogs of #5985 and #9536: each row takes the best of
their translations, with one Swedish term per concept across the UI and
the reference application's established Swedish terms where they are
unambiguous, and the strings added since are filled in. 4,537 rows, every
measured key; plural rule one|other.

Where the rows come from: 2,710 both catalogs agreed on, 979 from #5985,
655 from #9536, 193 written new; 177 of the taken rows
were edited for consistency.

The tests that used sv-SE as an unsupported locale now use sw-KE and
fi-FI, and sv-SE / sv_FI.UTF-8 are positive cases. macOS lists sv in
CFBundleLocalizations.

Fixes #10020.

Co-authored-by: kler <966132+kler@users.noreply.github.com>
Co-authored-by: davvarn <1687962+davvarn@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 22:35:59 +01:00
bd9590d060 Arabic interface language, combined from the community catalogs
Adds Arabic (ar, العربية) as an interface language. The catalog combines
the open Arabic catalogs of #2505, #4520, #4569, #5593, #5915, #6092 and
#6287: each row takes the best of their translations, with one Arabic
term per concept across the UI, and the strings added since are filled
in. Modern Standard Arabic, Western digits, the six CLDR plural forms
(zero|one|two|few|many|other).

The two tests that used ar-SA as an unsupported locale now use sw-KE, and
the direction test expects Arabic to be the one right-to-left catalog.

Co-authored-by: azizr12 <163084230+azizr12@users.noreply.github.com>
Co-authored-by: hicbest <213873494+hicbest@users.noreply.github.com>
Co-authored-by: hasan-sdaif <306196098+hasan-sdaif@users.noreply.github.com>
Co-authored-by: NibrasAz7 <34162952+NibrasAz7@users.noreply.github.com>
Co-authored-by: vfggf95565 <74471233+vfggf95565@users.noreply.github.com>
Co-authored-by: syrrevshin4202-prog <243624842+syrrevshin4202-prog@users.noreply.github.com>
Co-authored-by: test-bit-sys <336050374+test-bit-sys@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 21:22:26 +01:00
SvenCarstensen e7acfbfc52 Merge branch 'main' into codex/guide-position-units 2026-10-28 08:40:49 +01:00
Khawar Zamman Wani 8b835a103c Merge origin/main into pen-path-operations 2026-10-28 09:22:55 +05:30
SvenCarstensen 9ef07f4bd9 Document attribution for guide unit screenshots 2026-10-27 17:42:43 +01:00