Repository navigation
Project
A project is one comic chapter: an ordered list of input pages, the slices produced from them, and
the render baselines that let the next run tell what changed. It is an entity — it holds state and
answers questions about itself; the operations that act on it from outside live on
Infrastructure::ProjectEditor.
The invariant at the end is filed here rather than with the matching algorithm because it constrains the project, not the matcher.
uid : uid // local id (e.g. "file-<hex>"), minted if missing/duplicate on load
filePath : string // absolute path on disk
order : int // 0-indexed strip position — the render builds the virtual strip in this
// order (via ProjectItem::inputsInOrder()), not in the stored-vector order.
// Reordering rewrites only this field (Infrastructure::ProjectEditor); the
// input vector is never physically moved.
status : enum // PENDING | PROCESSED | SKIPPED | ERROR
errorMessage : string // empty unless status == ERROR or SKIPPED
Note:
thumbnailPathis not persisted. The GUI derives it on demand viaThumbnailCache::thumbnailPath(page.filePath)— the path is deterministic and does not need to be stored in the workspace JSON.
A project is an ordered set of source images that are processed together as a single virtual strip, sharing one output profile and a curated list of canvas profiles.
uid : uid // stable local identifier
name : string // user-facing label, e.g. "Chapter 01"
inputDirectory : string // folder inputs were last scanned from (not authoritative — each
// input carries its own path); CLI project-match key + GUI dialog default
outputDirectory : string // where output slices are written
outputProfileId : uid // references OutputProfile.id in the parent Workspace
// exactly one output profile per project
canvasProfileIds : uid[] // ordered list of CanvasProfile.id values
// order = priority (first match wins)
// invariant: no two entries may refer to profiles
// whose canvasSize is identical (conflict guard)
pages : PageItem[] // ordered input images
processedFiles : ProcessedFileRecord[]
colourCorrection : ColourCorrection // optional per-page grade (page domain); disabled by default
stripOverlays : StripOverlay[] // optional text/bubble overlays (strip domain); a lib-owned inventory
processingSignature : string // fingerprint of the two steps at last render (staleness axis, §7)
stripDirty : bool
Overlays are a lib-owned inventory, parallel to input files. The consumer creates the RGBA bitmap;
the library inventories it: ProjectItem::addOverlay(path, x, y, blend) mints the ovl-… uid, hashes
the bitmap and dedups identical content by sha256, removeOverlay(uid) drops one. The field is
private, read through getStripOverlays() (const + mutable, like getInputImages()). Files are
referenced by path and never copied — the same self-containment level as input files. Colour correction
carries no binary artifact (pure numbers), so it is fully contained in the workspace JSON.
Why a list, not a single id?
An artist may work with multiple canvas heights within one chapter (e.g. a normal
page at 10 240 px and a splash at 20 480 px, both 1 600 px wide). The list lets
the project accept both, while the order encodes which profile takes precedence when
two profiles match the same width but could theoretically both match. The conflict
guard (§7.5) prevents two profiles with identical dimensions from being registered,
so priority only matters for width-only ambiguity during the search (see §7.5).
outputProfileId is a direct, stable reference. If the referenced profile is
deleted from the workspace the project is considered misconfigured and the CLI / GUI
must report an error before processing begins.
inputFilePath : string // absolute path
sha256 : string // hex digest at time of last processing
lastProcessed : datetime // ISO 8601
contributesTo : string[] // output filenames that contain pixels from this input
Within a single project, canvasProfileIds must never contain two profiles whose
canvasSize (width × height) is identical. If it did, the algorithm above would
always select the first one and the second would be unreachable — a silently wasted
entry.
The conflict is detected at the moment a profile is added to a project, not deferred to serialisation. The library exposes a dedicated mutation method that checks for a collision and returns a structured error identifying the conflicting profile — so the CLI can suggest a removal command and the GUI can highlight the offending entry.
See also: Home · Workspace · Project · Render · Preview · Canvas-Profiles · Processing-Steps · Components · CLI · Platform · Roadmap · Diagrams