//! The PhotoCraft engine façade: open documents, history, and the command registry. //! //! Every user-visible action is a command with a stable id (Photoshop-style, such as //! `layer.newAdjustmentLayer.invert`) and JSON parameters. Every frontend goes through the same //! [`Session::execute`] entry point: the egui UI, the CLI, the remote-control channel and the MCP //! server. This is what makes the UI swappable and the app fully scriptable. #![forbid(unsafe_code)] #![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)] mod actions_record; pub mod adjust_params; mod allocation; pub mod build_info; mod canvas_geom; mod channel_clip; mod clip_group; pub mod color_channels; mod crop_fill; pub mod display_color; pub mod doc_path; pub mod filters; pub mod filters_ext; mod group_options; pub mod guide_layout; pub mod hidden_target; pub mod inspect; pub mod installed_profiles; pub mod jobs; pub mod kys; pub mod layer_place; pub mod layer_style; pub mod link_watch; pub mod malloc_tuning; pub mod memory; pub mod memory_gate; pub mod new_layer; mod param_check; mod pixels; pub mod prefs; pub mod preset_store; pub mod presets; pub mod proof_sim; mod scratch; pub mod select_mask_edits; pub mod shell_commands; pub mod shortcut_sets; pub mod snap; pub use variables_cmds::{export_data_sets_with, import_data_sets_text}; pub use video_cmds::render_video_with; use std::sync::Arc; use photocraft_doc::{Document, LayerId}; use photocraft_ops::{History, LayerTarget}; use serde_json::Value; pub use commands::{CommandSpec, command_specs}; pub use photocraft_doc as doc; pub use photocraft_paint as paint; pub use photocraft_paint::BrushSettings; pub mod cmd; mod doc_state; mod error; mod session; pub use cmd as commands; pub use cmd::actions_cmds; pub use cmd::actions_edit_cmds; pub use cmd::adjust_cmds; pub use cmd::align_cmds; pub use cmd::analysis_cmds; pub use cmd::artboard_cmds; pub use cmd::atn_cmds; pub use cmd::automate_cmds; pub use cmd::background_cmds; pub use cmd::brush_cmds; pub use cmd::brush_key_cmds; pub use cmd::brush_preset_cmds; pub use cmd::channel_cmds; pub use cmd::color_cmds; pub use cmd::color_to_alpha_cmds; pub use cmd::comps_cmds; pub use cmd::contour_lib_cmds; pub use cmd::cutout_cmds; pub use cmd::distort_cmds; pub use cmd::document_preset_cmds; pub use cmd::edit_cmds; pub use cmd::edit_menu_cmds; pub use cmd::eraser_cmds; pub use cmd::export_preset_cmds; pub use cmd::exr_cmds; pub use cmd::extra_cmds; pub use cmd::face_cmds; pub use cmd::file_cmds; pub use cmd::fill_cmds; pub use cmd::fill_key_cmds; pub use cmd::float_cmds; pub use cmd::font_library_cmds; pub use cmd::frame_cmds; pub use cmd::fx_view_cmds; pub use cmd::fx_visibility_cmds; pub use cmd::gallery_cmds; pub use cmd::gradient_fill_cmds; pub use cmd::group_view_cmds; pub use cmd::history_cmds; pub use cmd::history_menu_cmds; pub use cmd::history_options_cmds; pub use cmd::history_select_cmds; pub use cmd::history_source_cmds; pub use cmd::image_cmds; pub use cmd::layer_copy_cmds; pub use cmd::layer_label_cmds; pub use cmd::layer_list_cmds; pub use cmd::layer_mask_props_cmds; pub use cmd::layer_menu_cmds; pub use cmd::layer_multi_cmds; pub use cmd::layer_nav_cmds; pub use cmd::layer_xmp_cmds; pub use cmd::lens_cmds; pub use cmd::magnetic_cmds; pub use cmd::mask_view_cmds; use cmd::migrate_cmds; pub use cmd::mode_cmds; pub use cmd::multichannel_cmds; pub use cmd::notes_cmds; pub use cmd::paint_cmds; use cmd::path_edit_cmds; pub use cmd::pattern_cmds; pub use cmd::pattern_fill_cmds; pub use cmd::perspective_crop_cmds; pub use cmd::photo_cmds; pub use cmd::pick_cmds; pub use cmd::plugin_cmds; pub use cmd::preset_import_cmds; pub use cmd::print_cmds; pub use cmd::rasterize_style_cmds; pub use cmd::redeye_cmds; pub use cmd::render_cmds; pub use cmd::retouch_cmds; pub use cmd::sample_cmds; pub use cmd::sampler_cmds; pub use cmd::select_extra_cmds; pub use cmd::selection_cmds; pub use cmd::slice_cmds; pub use cmd::smart_cmds; pub use cmd::smart_svg_cmds; pub use cmd::smartselect_cmds; pub use cmd::solid_fill_cmds; pub use cmd::stamp_cmds; pub use cmd::style_asl_cmds; pub use cmd::swatch_cmds; pub use cmd::symmetry_cmds; use cmd::timeline_cmds; pub use cmd::transform_cmds; use cmd::trap_cmds; pub use cmd::type_area_cmds; pub use cmd::type_caret_cmds; pub use cmd::type_cmds; pub use cmd::type_extra_cmds; pub use cmd::type_mask_cmds; pub use cmd::type_spell_cmds; pub use cmd::type_styles_cmds; pub use cmd::updates_cmds; use cmd::variables_cmds; pub use cmd::vector_cmds; use cmd::video_cmds; pub use cmd::vp_cmds; pub use cmd::warp_cmds; pub use cmd::web_cmds; use cmd::wia_cmds; pub use doc_state::{CopiedStyle, DocState, PRECHECKED, ToolState}; pub use error::{EngineError, Result}; pub use session::move_item; use session::restore_target; pub(crate) use session::{carry_visibility, fix_selection, visibility_changes}; /// A type layer for [`Session::refresh_type_layers`]: in the open document, or in its first /// History snapshot (the document as opened) when `first_snapshot`. The caller picks each against /// that version of the layer, which may differ from the open one or be gone from it. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct TypeLayerRef { pub doc: photocraft_doc::DocId, pub layer: LayerId, pub first_snapshot: bool, } /// The pixels of a raster layer (typically one a command just created), as an error instead /// of a panic if the layer has none. pub(crate) fn pixels_mut(l: &mut photocraft_doc::Layer) -> Result<&mut photocraft_raster::Surface> { let id = l.id; l.surface_mut().ok_or_else(|| EngineError::Other(format!("layer {id:?} has no pixels"))) } /// The active document's active layer, for `enabled` predicates. pub(crate) fn active_layer_of(s: &Session) -> std::result::Result<&photocraft_doc::Layer, String> { let d = s.active().ok_or("no document open")?; d.active_layer.and_then(|id| d.doc.layer(id)).ok_or_else(|| "no active layer".into()) } #[derive(Default)] pub struct Session { docs: Vec, /// Override Open belongs only to one Batch scratch session, never to caller preferences. pub(crate) batch_open: Option, /// Import diagnostics retained by a Batch scratch that suppresses only profile prompts. pub(crate) batch_open_notices: Option>, /// Frozen RAW workflow settings for one noninteractive Batch input. pub(crate) batch_raw_open: Option, /// Admitted document identities in opening order, independent of visual-tab reordering. open_document_order: Vec, active: Option, /// Monotonic counter, bumped whenever a different document becomes active (including a /// switch away and back inside one pass). UI work that must not cross documents (control /// screenshots, automation input) binds to it. active_generation: u64, /// The document id [`Self::active_generation`] was last bumped for. active_doc_for_generation: Option, /// Change marker: advances at least once whenever the active document or layer changes through /// the `Session` API (commands via `execute`, `start` and finished jobs, `select_layer`, /// `restore_layer_selection`, `layer_multi_cmds::set_selection`, `set_active_layer`, undo / redo / /// rollback, document switches). Compare two readings for equality to know whether anything /// changed in between; it is not an exact count of operations (one operation may advance it /// more than once). Writing `DocState::active_layer` directly bypasses it. active_layer_changes: u64, pub tools: ToolState, /// Log of executed commands (for action recording and debugging). pub journal: Vec<(String, Value)>, /// Text layer changed by the last journal entry, for folding Type tool keystrokes. typed_layer: Option<(photocraft_doc::DocId, LayerId)>, /// Coalescing key of the command being executed. coalesce_request: Option, /// The outer action's original document/history states, independent of command transactions. action_history: Option, /// Weak leases expire when every copy of a suspended continuation is abandoned. paused_action_history: Vec>, /// Tests observe the actual scratch-session boundary without retaining it or changing output. #[cfg(test)] batch_history_observer: Option, /// Pixels copied with Edit › Copy / Cut (shared by all documents, like Photoshop). pub clipboard: Option, /// Layer › Layer Style › Copy Layer Style: the effects and the Blending Options /// ([`CopiedStyle`]: blend mode, fill opacity, Advanced Blending, Blend If, channels). pub style_clipboard: Option, /// Shape path context menu: copied vector fill and stroke styles. pub path_fill_clipboard: Option, pub path_stroke_clipboard: Option, /// Colour management: proofing state, monitor profile, display transforms. pub color: color_cmds::ColorState, /// Open Edit Contents documents and the smart objects they update. pub smart_links: Vec, /// Type › Save Default Type Styles: character and paragraph style new type layers start from. pub type_defaults: Option<(photocraft_doc::text::CharStyle, photocraft_doc::text::ParagraphStyle)>, /// Quick Mask Options (colour, opacity, colour indicates) used when entering Quick Mask. pub quick_mask_options: channel_cmds::QuickMaskOptions, /// Colour channel the running command may change (a single colour channel is targeted). color_restrict: Option, /// Smart object whose filter mask the running command edits (`"target":"filterMask"`): each /// edit re-renders it, in the same history step. filter_mask_layer: Option, /// Edit › Preferences, keyboard shortcuts, menu and toolbar customisation (see `prefs`). pub prefs: prefs::PrefsStore, /// Release checks use an injected platform transport; no network I/O in the engine. pub updates: updates_cmds::Updates, /// Edit menu state: Fade source, custom shape library (see `edit_menu_cmds`). pub edit_state: edit_menu_cmds::EditState, /// The pattern library (Window › Patterns; see `pattern.*`). pub patterns: pattern_cmds::PatternLibrary, /// Image › Analysis: Measurement Log and Select Data Points (see `analysis_cmds`). pub analysis: analysis_cmds::AnalysisState, /// Window › Gradients, Patterns (groups), Styles, Shapes, Tool Presets and Clone Source. pub presets: presets::PresetState, /// File menu state: Lock Slices, Image Assets, last Print / Save for Web settings, script /// event log (see `automate_cmds`). pub file_menu: automate_cmds::FileMenuState, /// Persistent brush preset store (desktop only; `None` keeps presets session-only, as in /// headless and test sessions). See `preset_store`. The same store holds the Actions list. pub preset_store: Option, /// Window › Actions. The list persists with the preset store when one is attached. pub actions: actions_cmds::ActionState, /// Gate for every command the session runs, including the ones a command runs on its own /// behalf and `actions.play` steps. Untrusted sessions (MCP, the control channel) install /// the same check a top-level request sees. `None` runs everything, which is what a local /// UI and `photocraft-cli run` do. pub authorize: Option Result<()>>, /// A further route for File › Print when neither `lp` nor a CUPS server over IPP is /// reachable (the XDG Print portal in a Flatpak); see `print_cmds::Spooler`. pub print_spooler: Option, /// The printers `file.printers`, `file.print.printers` and the Print dialog list in place of /// the system's (tests); see `print_cmds::PrinterSource`. `None` asks the spooler. pub printer_source: Option, /// Background jobs (see [`jobs`]). jobs: jobs::Jobs, /// Color Sampler points dropped by the last canvas-size change (crop, Canvas Size, Image Size, /// rotation), for the status message. Read with [`Session::take_samplers_removed`]. samplers_removed: usize, /// The canvas-geometry map the next [`Session::edit`] applies to the Color Sampler points (crop, /// Canvas Size, Image Size, rotation set it just before their edit). Consumed by the edit. sampler_map: Option, /// The same edit's map in edge coordinates, for the painting symmetry (Image Rotation maps the /// samplers' pixel centres instead). Consumed by the edit. symmetry_map: Option, /// Each open document's linked smart objects' files: size and modification time when last /// seen ([`link_watch`]). pub link_stamps: std::collections::HashMap<(u64, String), link_watch::Stamp>, /// The stamps being taken on a worker thread ([`Session::start_link_scan`]). pub link_scan: Option, /// Each open document's linked smart objects' files that were not there when last looked at /// ([`Session::missing_links`]). link_missing: std::collections::HashSet<(u64, String)>, /// Select Subject's last result, reused while the pixels are unchanged (#2865). subject_cache: smartselect_cmds::SubjectCache, /// Object Selection's Object Finder results (#4087), shared by the hover preview and the /// click that commits it; valid while the pixels are unchanged. pub object_finder: smartselect_cmds::ObjectFinderCache, /// A finished live stroke offered to the `paint.stroke` / `paint.pencil` that commits it /// ([`Session::offer_live_stroke`]). pub(crate) live_commit: Option, /// Documents a background open decoded but held back until the user answers the profile /// prompt ([`ColorState::open_question`](color_cmds::ColorState::open_question)): `(id, /// document, source read-only)`. The shell takes them with [`Session::take_held_open`]. held_opens: Vec<(u64, Document, bool)>, next_held_open: u64, pub(crate) pending_preflights: Vec, next_pending_preflight: u64, /// Shared native memory supervision, owned and stopped by the last session using it. scratch_runtime: Option, /// [`photocraft_raster::spill::epoch`] when History's byte limits were last applied: a /// scratch disk that pauses or resumes changes the limit ([`Session::sync_scratch_state`]). scratch_epoch: u64, /// The document before the latest [`Session::edit`] of the running command, which /// [`Session::hint_damage`] checks a hint against. Set by `edit`, taken by `hint_damage`, /// dropped when the command ends. Debug and test builds only. #[cfg(any(test, debug_assertions))] hint_before: Option>, /// Damage hints that missed a tile their edit changed ([`Session::hint_damage`]): each one a /// command reporting too narrow a redraw area (#4053). Counted in debug and test builds only; /// tests read it. pub damage_misses: usize, } #[cfg(test)] mod brush_pressure_size_default_tests; #[cfg(test)] mod embed_all_linked_tests; #[cfg(test)] mod fill_layer_mode_tests; #[cfg(test)] mod mixer_indexed_bitmap_tests; #[cfg(test)] mod pattern_mode_tests; #[cfg(test)] mod second_keys_zoom_actions_tests; #[cfg(test)] mod tests; #[cfg(test)] mod fx_mode_tests; #[cfg(test)] mod brush_clear_background_tests; pub mod command_hints;