* 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>
36 KiB
Attribution
Every non-code asset in this repository, with its author, source and license. PhotoCraft's own
code and original assets are MIT OR Apache-2.0 (see LICENSE-MIT,
LICENSE-APACHE and NOTICE). When you add an asset, add a row here
in the same change; third-party assets must be permissively licensed and keep their license file
next to them.
Bundled in the app
| Path | Title | Author | Source | License |
|---|---|---|---|---|
crates/codecs/src/codecs/xpm/x11_colors.rs |
X11 rgb.txt color-name lookup table (normalized X.Org RGB values) |
X.Org Foundation and X11 contributors | https://cgit.freedesktop.org/xorg/app/rgb/tree/rgb.txt | MIT (X.Org app/rgb) |
crates/ui-egui/src/i18n/el.tsv |
Greek UI translations | PhotoCraft contributors | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, LICENSE-translations.txt |
crates/ui-egui/src/i18n/de.tsv |
German UI translations | PhotoCraft contributors | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, LICENSE-translations.txt |
crates/ui-egui/src/i18n/es.tsv |
Spanish UI translations | @gorevyoneticisi; AI contributor: OpenAI Codex | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, LICENSE-translations.txt |
crates/ui-egui/src/i18n/it.tsv |
Italian UI translations | PhotoCraft contributors | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, LICENSE-translations.txt |
crates/ui-egui/src/i18n/ko.tsv |
Korean UI translations | PhotoCraft contributors | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, LICENSE-translations.txt |
crates/ui-egui/src/i18n/nl.tsv |
Dutch UI translations | PhotoCraft contributors | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, LICENSE-translations.txt |
crates/ui-egui/src/i18n/pl.tsv |
Polish UI translations | PhotoCraft contributors | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, LICENSE-translations.txt |
crates/ui-egui/src/i18n/pt-br.tsv |
Brazilian Portuguese UI translations | PhotoCraft contributors | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, LICENSE-translations.txt |
crates/ui-egui/src/i18n/uk.tsv |
Ukrainian UI translations | PhotoCraft contributors | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, LICENSE-translations.txt |
crates/ui-egui/src/i18n/cs.tsv |
Czech UI translations | PhotoCraft contributors | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, LICENSE-translations.txt |
crates/ui-egui/src/i18n/hu.tsv |
Hungarian UI translations | PhotoCraft contributors | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, LICENSE-translations.txt |
crates/ui-egui/src/i18n/vi.tsv |
Vietnamese UI translations | PhotoCraft contributors | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, LICENSE-translations.txt |
crates/ui-egui/src/i18n/ar.tsv |
Arabic UI translations | PhotoCraft contributors | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, LICENSE-translations.txt |
crates/ui-egui/src/i18n/tr.tsv |
Turkish UI translations | PhotoCraft contributors | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, LICENSE-translations.txt |
crates/ui-egui/src/i18n/az.tsv |
Azerbaijani (Latin script) UI translations | PhotoCraft contributors; AI contributor: OpenAI Codex | PhotoCraft English labels; Google Translate drafts with edited core terminology, contexts and plurals | MIT OR Apache-2.0, LICENSE-translations.txt |
crates/ui-egui/src/i18n/bs.tsv |
Bosnian (Latin, Ijekavian) UI translations | Nine (@nine-codes) with Claude Code, AI-assisted; not yet reviewed by a native speaker | Original translations of PhotoCraft's English labels, shared with storytold/craft-i18n locales/bs/ |
MIT OR Apache-2.0, LICENSE-translations.txt |
crates/ui-egui/src/i18n/sr.tsv |
Serbian (Latin, Ekavian) UI translations | PhotoCraft contributors | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, LICENSE-translations.txt |
crates/ui-egui/src/i18n/ru.tsv |
Russian UI translations | PhotoCraft contributors | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, LICENSE-translations.txt |
crates/ui-egui/src/i18n/sv.tsv |
Swedish UI translations | @kler, @davvarn and PhotoCraft contributors | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, LICENSE-translations.txt |
crates/ui-egui/src/i18n/fi.tsv |
Finnish UI translations | @Janiz-zzz, @metuuu and PhotoCraft contributors | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, LICENSE-translations.txt |
crates/ui-egui/src/i18n/da.tsv |
Danish UI translations | @pechnikov2026-star and PhotoCraft contributors | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, LICENSE-translations.txt |
crates/ui-egui/src/i18n/nb.tsv |
Norwegian Bokmål UI translations | @nikolainyegaard, @Bolla76 and PhotoCraft contributors | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, LICENSE-translations.txt |
crates/ui-egui/src/i18n/he.tsv |
Hebrew UI translations | @tronicum with Claude Code, AI-assisted (machine-translated); not yet reviewed by a native speaker | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, LICENSE-translations.txt |
crates/ui-egui/src/i18n/ur.tsv |
Urdu UI translations | @tronicum with Claude Code, AI-assisted (machine-translated); not yet reviewed by a native speaker | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, LICENSE-translations.txt |
crates/ui-egui/src/i18n/ro.tsv |
Romanian UI translations | @tronicum with Claude Code, AI-assisted (machine-translated); not yet reviewed by a native speaker | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, LICENSE-translations.txt |
crates/ui-egui/src/i18n/th.tsv |
Thai UI translations | @Thanawatttt and PhotoCraft contributors, with opencode (DeepSeek) as an AI contributor | Machine-assisted first draft from PhotoCraft's English labels, reviewed for UI terminology | MIT OR Apache-2.0, LICENSE-translations.txt |
crates/ui-egui/src/i18n/ta.tsv |
Tamil UI translations | @newsbaba24x7 and PhotoCraft contributors | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, LICENSE-translations.txt |
crates/ui-egui/src/i18n/ka.tsv |
Georgian UI translations | @devius with Claude Code, AI-assisted | Original translations of PhotoCraft's English labels | MIT OR Apache-2.0, LICENSE-translations.txt |
epaint_default_fonts::EGUI_ICONS (build input; deflated into OUT_DIR/egui_fonts/egui-icons.ttf.deflate, registered by crates/ui-egui/src/egui_fonts.rs) |
egui-icons, five-glyph subset of emoji-icon-font | John Slegers | egui fork at 6d518fc, original; egui subset, unmodified here | MIT; Copyright (c) 2014 John Slegers; full upstream notice in NOTICE |
assets/fonts/Inter-Regular.ttf, Inter-Medium.ttf, Inter-SemiBold.ttf |
Inter 4.001 (UI font) | The Inter Project Authors (Rasmus Andersson) | https://github.com/rsms/inter | SIL OFL 1.1, assets/fonts/OFL-Inter.txt |
assets/fonts/JetBrainsMono-Regular.ttf |
JetBrains Mono 2.305 (numeric font) | The JetBrains Mono Project Authors | https://github.com/JetBrains/JetBrainsMono | SIL OFL 1.1, assets/fonts/OFL-JetBrainsMono.txt |
assets/icons/*.svg except slice-knife.svg, slice-select.svg, font-similar.svg, font-variable.svg, sampler.svg, artboard.svg, perspective-crop.svg, frame-placeholder.svg, ellipse-dashed.svg, gradient.svg, type-mask.svg, type-mask-vertical.svg, text-warp.svg, clip-below.svg, clip-release.svg, layer-mask.svg, adjustment-layer.svg, brush-replace.svg, history-brush.svg, quick-selection.svg, eraser-background.svg, eraser-magic.svg, lasso-magnetic.svg, direct-select.svg, convert-point.svg, pen-add-anchor.svg, pen-delete-anchor.svg, pen-freeform.svg, pen-curvature.svg, bandage-sparkle.svg, painting-symmetry.svg, symmetry-*.svg, path-op-*.svg and marquee-{row,column}.svg, align-edge-*.svg and distribute-*.svg, crop-overlay-*.svg, vertical-align-*.svg (116 files; star-filled.svg is star.svg with a solid fill) |
Lucide icons | Lucide Icons and Contributors | https://github.com/lucide-icons/lucide (icons/<name>.svg; align-* are now named text-align-* upstream) |
ISC, assets/icons/LICENSE-lucide.txt |
assets/icons/{check,chevron-down,chevron-left,chevron-right,chevron-up,chevrons-left,chevrons-right,circle,clock,compass,info,link,link-2,lock,minus,monitor,moon,move,navigation,plus,search,square,trash,triangle,type,x,zoom-in}.svg |
Lucide icons derived from Feather (subset of the row above) | Cole Bemis (Feather), Lucide Contributors | https://github.com/feathericons/feather via Lucide | MIT (Feather) and ISC (Lucide), assets/icons/LICENSE-lucide.txt |
assets/icons/slice-knife.svg |
Slice tool glyph | PhotoCraft contributors | Original work | MIT OR Apache-2.0 |
assets/icons/slice-select.svg |
Slice Select tool glyph | PhotoCraft contributors | Original work | MIT OR Apache-2.0 |
assets/icons/crop-overlay-*.svg |
Crop overlay glyphs: Rule of Thirds, Grid, Diagonal, Triangle, Golden Ratio and Golden Spiral inside the crop-mark frame | PhotoCraft contributors | Original work | MIT OR Apache-2.0 |
assets/icons/sampler.svg |
Color Sampler tool glyph: a target reticle marking a sampled pixel | PhotoCraft contributors | Original work | MIT OR Apache-2.0 |
assets/icons/font-similar.svg, assets/icons/font-variable.svg |
Font menu filter glyphs: two waves (Show Similar Fonts) and a T over a slider (Show Variable Fonts), drawn on the 24 px icon grid | PhotoCraft contributors | Original work | MIT OR Apache-2.0 |
assets/icons/convert-point.svg |
Convert Point tool glyph: an angle with a square anchor on its apex, drawn on Lucide's 24 px grid and stroke | PhotoCraft contributors | Original work | MIT OR Apache-2.0 |
assets/icons/artboard.svg |
Artboard tool glyph: a board with a tick at each end of its edges | PhotoCraft contributors | Original work | MIT OR Apache-2.0 |
assets/icons/perspective-crop.svg |
Perspective Crop tool glyph: a crop frame in perspective with its grid and crop-mark ticks | PhotoCraft contributors | Original work | MIT OR Apache-2.0 |
assets/icons/type-mask.svg, assets/icons/type-mask-vertical.svg |
Horizontal and Vertical Type Mask tool glyphs: a T drawn as marching ants (the vertical one beside a downward arrow), on Lucide's 24 px grid and stroke | PhotoCraft contributors | Original work | MIT OR Apache-2.0 |
assets/icons/path-op-{new-layer,combine,subtract,intersect,exclude,merge}.svg |
Path Operations glyphs (Pen options bar): a single square for New Layer, and two overlapping squares showing the area each operation keeps (union, back minus front, overlap, everything but the overlap, the merged outline), drawn on Lucide's 24 px grid and stroke | PhotoCraft contributors | Original work | MIT OR Apache-2.0 |
assets/icons/text-warp.svg |
Create Warped Text glyph (Type tool options bar): a T above a bowed arc, drawn on Lucide's 24 px grid and stroke | PhotoCraft contributors | Original work | MIT OR Apache-2.0 |
assets/icons/marquee-row.svg, assets/icons/marquee-column.svg |
Single Row and Single Column Marquee tool glyphs: a dashed row (column) between two edge bars, drawn on Lucide's 24 px grid and stroke | PhotoCraft contributors | Original work | MIT OR Apache-2.0 |
assets/icons/clip-below.svg, assets/icons/clip-release.svg |
Clipping-mask cursors (⌥ over the line between two layers): a hooked arrow onto the layer below; the same with a small "no" badge to release | PhotoCraft contributors | Original work | MIT OR Apache-2.0 |
assets/icons/layer-mask.svg, assets/icons/adjustment-layer.svg |
Layers panel footer glyphs: Add a mask (a frame around a solid disc) and New Fill or Adjustment Layer (a circle, one half solid), drawn on Lucide's 24 px grid and stroke | PhotoCraft contributors | Original work | MIT OR Apache-2.0 |
assets/icons/vertical-align-{top,middle,bottom}.svg |
Type options-bar alignment glyphs for vertical text: three columns hanging from a top line, centred on a middle line, or standing on a bottom line, drawn on Lucide's 24 px grid and stroke | PhotoCraft contributors | Original work | MIT OR Apache-2.0 |
assets/icons/align-edge-*.svg, assets/icons/distribute-*.svg |
Move tool options-bar glyphs: Align (six edges/centres) and Distribute (edges, centres, spacing), a guide line or edge lines with bars, drawn on Lucide's 24 px grid and stroke | PhotoCraft contributors | Original work | MIT OR Apache-2.0 |
assets/icons/ellipse-dashed.svg |
Elliptical Marquee tool glyph: a dashed horizontal ellipse, drawn on Lucide's 24 px grid and stroke | PhotoCraft contributors | Original work | MIT OR Apache-2.0 |
assets/icons/frame-placeholder.svg |
Frame tool glyph: an empty placeholder frame, crossed corner to corner | PhotoCraft contributors | Original work | MIT OR Apache-2.0 |
assets/icons/gradient.svg |
Gradient tool glyph: a rounded rectangle filled with a left-to-right linear ramp, drawn on Lucide's 24 px grid and stroke | PhotoCraft contributors | Original work | MIT OR Apache-2.0 |
assets/icons/eraser-background.svg, assets/icons/eraser-magic.svg |
Background Eraser and Magic Eraser tool glyphs: Lucide eraser plus added marks |
Lucide Icons and Contributors; marks by PhotoCraft contributors | Derived from https://github.com/lucide-icons/lucide icons/eraser.svg |
ISC, assets/icons/LICENSE-lucide.txt |
assets/icons/brush-replace.svg |
Color Replacement tool glyph: Lucide brush plus two swap arrows |
Lucide Icons and Contributors; arrows by PhotoCraft contributors | Derived from https://github.com/lucide-icons/lucide icons/brush.svg |
ISC, assets/icons/LICENSE-lucide.txt |
assets/icons/history-brush.svg |
History Brush tool glyph: Lucide brush plus counter-clockwise history rewind arrow |
Lucide Icons and Contributors; arrow by PhotoCraft contributors | Derived from https://github.com/lucide-icons/lucide icons/brush.svg |
ISC, assets/icons/LICENSE-lucide.txt |
assets/icons/quick-selection.svg |
Quick Selection tool glyph: Lucide brush, scaled to 70%, inside a dashed upright rounded "C" selection |
Lucide Icons and Contributors; selection by PhotoCraft contributors | Derived from https://github.com/lucide-icons/lucide icons/brush.svg |
ISC, assets/icons/LICENSE-lucide.txt |
assets/icons/bandage-sparkle.svg |
Remove tool glyph: Lucide bandage moved down, plus a sparkle mark |
Lucide Icons and Contributors; mark by PhotoCraft contributors | Derived from https://github.com/lucide-icons/lucide icons/bandage.svg |
ISC, assets/icons/LICENSE-lucide.txt |
assets/icons/direct-select.svg |
Direct Selection tool glyph: Lucide mouse-pointer-2 plus an anchor square |
Lucide Icons and Contributors; square by PhotoCraft contributors | Derived from https://github.com/lucide-icons/lucide icons/mouse-pointer-2.svg |
ISC, assets/icons/LICENSE-lucide.txt |
assets/icons/pen-freeform.svg, assets/icons/pen-curvature.svg |
Freeform Pen and Curvature Pen tool glyphs: Lucide pen-tool scaled down, plus a freehand squiggle or a curve through two points |
Lucide Icons and Contributors; marks by PhotoCraft contributors | Derived from https://github.com/lucide-icons/lucide icons/pen-tool.svg |
ISC, assets/icons/LICENSE-lucide.txt |
assets/icons/pen-add-anchor.svg, assets/icons/pen-delete-anchor.svg |
Add Anchor Point and Delete Anchor Point tool glyphs: Lucide pen-tool plus a + or − mark |
Lucide Icons and Contributors; marks by PhotoCraft contributors | Derived from https://github.com/lucide-icons/lucide icons/pen-tool.svg |
ISC, assets/icons/LICENSE-lucide.txt |
assets/icons/painting-symmetry.svg |
Painting symmetry glyph: Lucide heart with a central axis |
Lucide Icons and Contributors; adapted by @shorbank | Derived from https://github.com/lucide-icons/lucide icons/heart.svg |
ISC, assets/icons/LICENSE-lucide.txt |
assets/icons/symmetry-{vertical,horizontal,dual,diagonal}.svg |
Painting symmetry axis glyphs | @shorbank | Original work; supplied SVG line geometry centred on a common square canvas, stroke adjusted to match the painting-symmetry glyph | MIT OR Apache-2.0, assets/icons/LICENSE-symmetry-axes.txt |
assets/icons/lasso-magnetic.svg |
Magnetic Lasso tool glyph, adapted from "Magnetic Lasso Tool" (redrawn on the 24 px toolbar grid) | Lil' Seal (Noun Project); adapted by PhotoCraft contributors | https://thenounproject.com/icon/magnetic-lasso-tool-177513/ | CC BY 3.0, assets/icons/LICENSE-noun-magnetic-lasso.txt |
assets/dict/en_US-scowl-50.txt.gz |
English word list for Check Spelling (SCOWL 2020.12.07, size 50, en_US; 72,403 words) | Kevin Atkinson and the SCOWL contributors | http://wordlist.aspell.net/ | SCOWL license (permissive, keep the notice), assets/dict/LICENSE-SCOWL.txt, which also has the build recipe |
crates/cms/profiles/photocraft-coated-cmyk.icc |
PhotoCraft Coated CMYK (synthetic) ICC profile | PhotoCraft contributors (generated by photocraft-cms, src/synth.rs) |
Original work, see crates/cms/README.md |
CC0-1.0 |
assets/app-icon/ (all files) |
PhotoCraft app icon (nine-tailed kitsune) | The project owner (drawn in ArtCraft, vectorised) | Original work, see assets/app-icon/README.md |
MIT OR Apache-2.0, assets/app-icon/LICENSE.txt |
packaging/macos/dmg/ (all files) |
PhotoCraft DMG window background and layout | @XusBadia, from the PhotoCraft app icon | Original work, see packaging/macos/dmg/README.md |
MIT OR Apache-2.0, like the code |
crates/ui-egui/src/theme.rs (Solarized Dark palette) |
Solarized colour scheme used by the Solarized Dark theme | Ethan Schoonover | https://ethanschoonover.com/solarized/ | MIT, LICENSE-solarized.txt |
crates/ui-egui/src/theme.rs (Breeze Light and Breeze Dark themes) |
The Breeze Light and Breeze Dark themes are original colour values on PhotoCraft's Studio layout, chosen to look close to KDE Plasma's Breeze (cool blue-greys, blue accent, 5 px frame radius) so the app blends into a Plasma desktop. None of Breeze's colour values, files or code are copied; the values are a few levels away from its schemes and tuned for text contrast | PhotoCraft contributors | Inspired by the look of KDE Breeze, https://invent.kde.org/plasma/breeze (its colour schemes are LGPL-2.0-or-later) | Original work |
crates/ui-egui/src/theme.rs (Soft Dark and Soft Light themes) |
Original colour values and radii for two low-contrast themes on PhotoCraft's Studio layout; no third-party palette, file or code is used | PhotoCraft contributors | Original | Original work |
crates/algo/data/libfacedetection.bin (face detection weights, read by crates/algo/src/faces.rs, whose network code is a port of the library's) |
libfacedetection CNN (YuNet design) weights and inference code, weights converted to half precision | Shiqi Yu | ShiqiYu/libfacedetection (src/facedetectcnn-data.cpp, facedetectcnn.cpp, facedetectcnn-model.cpp) |
BSD-3-Clause, LICENSE-libfacedetection.txt |
Optional build input: craft-fonts (not files in this repo)
Builds made with CRAFT_FONTS_DIR (all official releases) embed fonts from
storytold/craft-fonts at a pinned commit; no font file
from it is committed here. Per-file authors, sources and licences:
craft-fonts ATTRIBUTION.md.
| Font (embedded at build time) | Use | License |
|---|---|---|
| BIZ UDPGothic Regular, Bold (Morisawa) | Japanese UI and sans Type fallback (desktop) | SIL OFL 1.1, shipped as OFL-biz-ud-pgothic.txt |
| Shippori Mincho Regular (FONTDASU) | Japanese serif Type fallback (desktop) | SIL OFL 1.1, shipped as OFL-shippori-mincho.txt |
| BIZ UDMincho Regular (Morisawa) | Japanese serif Type fallback (desktop) | SIL OFL 1.1, shipped as OFL-biz-ud-mincho.txt |
The other built-in ICC profiles and the generated LUT looks are produced by code at runtime
(crates/cms/src/builtin.rs, crates/cms/src/lutfile.rs) and dedicated to the public domain
(CC0-1.0); they are not separate files.
Documentation and README
| Path | Title | Author | Source | License |
|---|---|---|---|---|
docs/brand/ (all files) |
ArtCraft name, wordmark and mark | ArtCraft Team | getartcraft.com | Not open source; trademarks of the ArtCraft Team, docs/brand/LICENSE-brand.txt |
docs/images/photocraft-*.jpg |
PhotoCraft screenshots | PhotoCraft contributors (UI) | Rendered offscreen with the snapshot example |
MIT OR Apache-2.0 (UI); the artwork in each is public domain, listed below |
docs/images/preferences-apply-*.png |
Preferences before and after adding Apply (no artwork) | PhotoCraft contributors | PhotoCraft control-channel capture and offscreen snapshot example |
MIT OR Apache-2.0 |
docs/images/guide-position-*-tab.png |
New Guide before and after focusing Position with Tab (no artwork) | PhotoCraft contributors | Offscreen snapshot example with ui.key Tab |
MIT OR Apache-2.0 |
docs/images/embedded-vector-editor-en.png |
Embedded AI/PDF/EPS conversion warning and editor chooser (no artwork) | Don Omondi | Offscreen smart_svg_ui::tests::capture_embedded_vector_conversion_prompt with authored bytes and mock services |
MIT OR Apache-2.0 |
docs/images/embedded-vector-tabs-linux.png |
Shared VectorCraft window after a portal editor save and ordinary document launch | Don Omondi | Local Linux GUI acceptance with authored orange rectangle/blue circle SVGs; no private artwork | MIT OR Apache-2.0 |
docs/images/gradient-overlay-method-*.png |
Gradient Overlay Method selector, before and after (synthetic gradient) | @oRE-o | Rendered offscreen with the snapshot example; no external artwork |
MIT OR Apache-2.0 |
docs/images/cpu-pixel-alignment/*.png |
CPU canvas pixel alignment, before and after #8321 | PhotoCraft contributors | Original synthetic noise rendered with the offscreen snapshot example; see the adjacent README |
MIT OR Apache-2.0, adjacent LICENSE-MIT and LICENSE-APACHE |
docs/images/multi-channel-editing/{curves,curves-choices,levels,drawing,mask-view-before,mask-view-after}.png |
Selected colour channel editing (synthetic gradient) | @hidoba | Original synthetic gradient rendered with the offscreen snapshot example; adjacent JSON scripts |
MIT OR Apache-2.0, adjacent LICENSE-MIT and LICENSE-APACHE |
docs/images/multi-channel-editing/great-wave.jpg, rgb.png, cmyk.png, lab.png, gallery-channel-preview-{before,after}.png |
The Great Wave off Kanagawa, c. 1830–1832, and PhotoCraft channel-selection captures | Katsushika Hokusai (artwork); PhotoCraft contributors (UI) | Wikimedia Commons, Metropolitan Museum reproduction; adjacent JSON capture scripts | Public domain artwork (PD-Art), adjacent LICENSE-PUBLIC-DOMAIN.txt; UI/scripts MIT OR Apache-2.0 |
Artwork shown in the screenshots (all public domain, via Wikimedia Commons; details in
docs/images/SOURCES.md):
| Screenshot | Artwork | Source |
|---|---|---|
photocraft-demo.jpg |
The Great Wave off Kanagawa, Katsushika Hokusai, c. 1831 | Commons |
photocraft-layer-styles.jpg |
Earthrise, William Anders, Apollo 8, 1968 (NASA) | Commons |
photocraft-masks.jpg |
Girl with a Pearl Earring, Johannes Vermeer, c. 1665 | Commons |
photocraft-vector.jpg |
Water Lilies, Claude Monet, 1906 | Commons |
photocraft-filters.jpg |
The Starry Night, Vincent van Gogh, 1889 | Commons |
photocraft-adjustments.jpg |
Impression, Sunrise, Claude Monet, 1872 | Commons |
photocraft-transform.jpg |
The Tetons and the Snake River, Ansel Adams, 1942 (U.S. National Archives) | Commons |
photocraft-type.jpg |
Among the Sierra Nevada, California, Albert Bierstadt, 1868 | Commons |
photocraft-export-light.jpg |
The Kiss, Gustav Klimt, 1907–1908 | Commons |
Test data (not committed, not shipped)
corpus/ is gitignored and no test fixtures are committed to the repository, except the small
synthetic parser fixtures listed under Synthetic parser test fixtures below.
cargo xtask corpus --all fetches every corpus at the pinned commits in xtask/src/corpus_pins.rs
and verifies each file against the sha256 lists in xtask/*.sha256, with the upstream licence
next to the files:
| Path (fetched) | Title | Author | Source | License |
|---|---|---|---|---|
corpus/photoshop/ (258 PSDs) |
Photoshop oracle corpus: smart filters, layer-style effects, type, adjustments in every mode and depth | PhotoCraft contributors (authored with Adobe Photoshop 2026 by a script) | https://github.com/storytold/photocraft-corpus (photoshop/, with its generator and README) |
MIT OR Apache-2.0 |
corpus/gimp/ (28 files, 251 KB) |
GIMP XCF oracles: 40×30 procedural layers written by GIMP 3.2.6 through the corpus generator tools/gimp-xcf/xcf_fixtures.py, each with GIMP's merged rendering as a PNG |
PhotoCraft contributors (authored with GIMP 3.2.6 by a script) | https://github.com/storytold/photocraft-corpus (gimp/, with its generator and README) |
MIT OR Apache-2.0 |
corpus/psd-tools/ (309 files) |
psd-tools test set | Kota Yamaguchi and contributors | psd-tools tests/psd_files |
MIT, Copyright (c) 2019 Kota Yamaguchi |
corpus/psd/ (170 files) |
Small selection of the psd-tools and ag-psd test files | Kota Yamaguchi; Agamnentzar | psd-tools (above) and ag-psd test/ |
MIT (both) |
corpus/heif/ (9 files, 0.1 MB) |
HEIC/HEIF test files: heic-rs/ checkerboards, RGB strips and a grid-tiled photo with EXIF and XMP (synthetic pixels encoded by macOS sips, each .ref.png Apple's decode); pillow-heif/ the 10-bit RGBA RGBA_10__29x100.heif and its source RGBA_16__29x100.png |
Thomas Braun (heic-rs); Pillow-Heif contributors | heic-rs tests/fixtures, pillow-heif tests/images |
MIT OR Apache-2.0 (heic-rs); BSD-3-Clause (pillow-heif) |
corpus/exr/ (5 files, 2.3 MB) |
The deep OpenEXR test images 11, 42, 64, multivariate and objectid.deep.exr |
Contributors to the OpenEXR Project | openexr src/test/bin/test_images |
BSD-3-Clause |
corpus/affinity/ (60 files) |
Public Affinity documents and PNG references: vector-art/ (9 documents), afdesignload/ (9), jac21/ (2), asset-store-template/ (1), and affinity-samples/ (18 CC0 documents plus exported references) |
samuel-etver; Nick Beeuwsaert; Jeremy Cantu; DAIKI (eviltwo); satoshoe-dev | vector-art simple/, AFDesignLoad testDesigns/, Jac21/Branding Logos/JC/DesignerFiles/Affinity, AssetStoreTemplate AssetStoreTemplate/, affinity-samples |
CC0-1.0 (vector-art and affinity-samples); MIT, Copyright (c) 2015 Nick Beeuwsaert; MIT, Copyright (c) 2018 Jeremy Cantu; MIT, Copyright (c) 2024 DAIKI |
corpus/atn/ (16 files, 0.9 MB) |
Photoshop Actions sets, one folder per repository: sbaril/ (Frequency Separation, Firefly, Dimension, Harmonize, Photo, Quick Object Extraction, Generative Upscale), bjango/ (Bjango Actions, Adjustment Actions, Scale Actions), bkwld/ (Save layers for web), atntools/ (test.atn), trimmer/ (Trimmer), alpha-utility/ (AlphaUtilityAdditions), gif-creator/ (BannerActions) |
Stéphane Baril; Bjango and Marc Edwards; BKWLD; Maurits Rijk; damsmcfly; UCLA Game Lab; overbyte | sbaril/Photoshop-Scripts, bjango/Bjango-Actions, BKWLD/photoshop-actions, mrijk/atntools, damsmcfly/photoshop-cleaner-trimmer, uclagamelab/AlphaUtilityAdditions, overbyte/photoshop-gif-creator-script, pinned in xtask/src/corpus_pins.rs | MIT (sbaril, BKWLD, atntools, gif-creator), BSD (bjango, per Help/License.md), BSD-2-Clause (alpha-utility), Unlicense (trimmer); each licence file sits next to its files |
corpus/pngsuite/ |
PngSuite | Willem van Schaik | http://www.schaik.com/pngsuite/ | Public domain |
corpus/pixls/ (11 files) |
Real camera raws, one per decode path: Canon PowerShot SX50 HS (CR2 and its DNG conversion), Nikon D3 uncompressed NEF and Z6 paired 12/14-bit lossy and packed NEFs, Sony DSC-RX0 cRAW ARW, Panasonic DC-G9 high-res RW2, Olympus E-1 GRBG ORF, plus the E-5 packed ORF as the documented known-unsupported case. Per file (each CC0, per its database entry): IMG_4059.CR2, CRW_4061.DNG, JD1_8203.NEF, DSC00009.ARW, P1000475.RW2, E_1__C106743_gredos.ORF, _7061961_copy.ORF, DSC_0750.NEF, DSC_0751.NEF, DSC_0753.NEF, DSC_0754.NEF | The photographers of the pixls.us raw samples database | raw.pixls.us data/, fetched per xtask/src/corpus_pins.rs |
CC0 / public domain, per file as linked |
Files copied into corpus/ by hand (tiff, exr, raw) must be MIT, BSD or CC0.
Synthetic parser test fixtures
| Path | Title | Author | Source | License |
|---|---|---|---|---|
crates/psd/tests/fixtures/atn/*.atn |
Independently encoded ATN version-16 and version-12 parser fixtures | PhotoCraft contributors (version-12 files: @yoriswind) | Original synthetic test data; wire layout documented in crates/psd/src/atn.rs; provenance in the adjacent README |
MIT OR Apache-2.0 |
Embedded-vector editor source
The external SVG watcher, engine/UI handoff and bounded SVG rendering in this change
are adapted from Skyfuller1303’s MIT OR Apache-2.0 implementation in
PhotoCraft #3409, commit
897f20072762fdf0eb688adcdb8d8cda7b38ce66. Don Omondi’s embedded AI/PDF/EPS
extension and current-main integration build on that source. Clipboard and unrelated
Shape Tools changes from that branch are not included.
Assets bundled by the PDF reader dependency (craft-pdf)
PhotoCraft imports PDF pages through craft-pdf, pinned to the craft-libs revision
c5a1cbfb5f9049f9270955caffb5bee5997aebf7. The renderer vendors Hayro and the assets below;
they ship inside the dependency rather than being copied into this repository. PDFium is no
longer used or shipped.
| Crate / file | Author | Source | Licence | Use |
|---|---|---|---|---|
hayro-interpret 0.7.0 / assets/Foxit*.pfb (13 standard-14 font files) |
Foxit Software Inc. / PDFium Authors | https://github.com/storytold/craft-libs/tree/c5a1cbfb5f9049f9270955caffb5bee5997aebf7/vendor/hayro-interpret/assets | BSD-3-Clause | Renders non-embedded standard-14 PDF fonts |
hayro-interpret 0.7.0 / assets/CGATS001Compat-v2-micro.icc |
Clinton Ingram (saucecontrol/Compact-ICC-Profiles) | https://github.com/storytold/craft-libs/tree/c5a1cbfb5f9049f9270955caffb5bee5997aebf7/vendor/hayro-interpret/assets | CC0-1.0 | Default CMYK colour conversion |
hayro-cmap 0.1.0 / assets/cmaps.brotli |
Adobe | https://github.com/storytold/craft-libs/tree/c5a1cbfb5f9049f9270955caffb5bee5997aebf7/vendor/hayro-cmap/assets | BSD-3-Clause | Character-code mapping for CJK PDFs |