- Make parseGamutFile support multiple BEGIN_DATA blocks, inline #
comments, out-of-bounds vertex warnings, and user-visible parser
warnings (returned in { vertices, faces, warnings }).
- Harden profcheck report parsing: prefer the last JSON object with
avg_de/max_de/rms_de keys, then fall back to a wider set of regex
patterns for legacy plain-text output.
- Add parser warnings to the process log when metrics cannot be found.
- Fix loadGamutMesh call signature and add try/catch around gamut load.
- Add manual gamut parser tests in src/js/gamut_viewer.test.js.
- Update AGENTS.md, README.md, and ROADMAP.md.
Generated with Devin
7.7 KiB
7.7 KiB
ICCery Agent Notes
Stage 5 Verification / Profcheck
profcheckoutput is parsed from both JSON summaries (preferred) and legacy plain-text report formats.- If no delta-E values can be detected, the report cards show 0.00 and a warning is appended to the process log.
- The
.gamfile for the 3D viewer is parsed usingparseGamutFile, which supports multipleBEGIN_DATAblocks (some Argyll files use a separate block per surface section), inline#comments, and out-of-bounds vertex warnings. - Manual parser tests live in
src/js/gamut_viewer.test.js.
3D Gamut Viewer
- The viewer renders the measured/derived
.gamvolume and an optional sRGB reference wireframe in CIELAB. - Layer controls (profile, sRGB, axes) each have visibility toggles and opacity sliders.
- Click Reset View or press R to return the camera to its default position.
- Full JSDoc is provided on the public API in
src/js/gamut_viewer.js.
Stage 4 colprof Options
colprofoptions are exposed in the Stage 4 UI with contextual tooltips:- FWA / OBA Compensation (
-f): choose D50, None, D65, or a custom.spspectrum file. - Standard Illuminant (
-i) and Observer (-o): override default D50 / 1931 2° for CIE colourimetric calculations. - Viewing Conditions (
-cinput,-doutput): set CIE viewing-condition transforms for the profile table and output intent.
- FWA / OBA Compensation (
- The backend
build_colprof_argsincommands.rsmaps these UI values tocolprofCLI flags. Unit tests cover all combinations.
Stage 3 Swatch Grid
- Swatch patches render a 135° diagonal split: top-left is the intended/expected colour, bottom-right is the measured colour.
- Argyll
chartreademitsis_padfor boundary/spacer patches. White reference patches (e.g.-ewhite steps) may also carryis_pad, but they have validexpected.Labor non-zerodevicedata. The guard skips only pads with no measurement and all-zero device values. - Row/patch order from
chartread(rows A→Z, patches 1→N within each row) is rendered left-to-right / top-to-bottom to match theprinttargoutput. - Tooltip shows intended Lab (or device %), measured Lab, and ΔE₀₀ with a Good/Warning/Bad classification.
UI Button Conventions
The frontend uses a tiered button sizing system defined in src/styles/main.css. Prefer these utility classes over inline style attributes.
| Class | Size | Use for |
|---|---|---|
.btn-sm |
28px | Toolbar actions, header icons, minor toggles |
.btn-md (default for button.secondary/button.danger) |
36px | Standard dialog/form actions and browse buttons |
.btn-lg |
40px | Primary stage actions (Generate, Create Layout, Create Profile, Verify, etc.) |
.btn-icon-sq |
36×36px | Square icon-only buttons (refresh, settings, etc.) |
.icon-btn |
28×28px | Small header icon-only buttons (settings, about, save preset, manage presets) |
.btn-properties |
36px | Printer driver preferences button |
- Primary action buttons use
button.primaryplus.btn-lgfor major stage actions. - Danger actions use
button.danger(36px). - All action rows use one of:
.stage-actions,.modal-actions,.chartread-actions,.print-actions-row,.btn-row,.btn-row-sm,.btn-row-end, or.input-row-sm. - Avoid inline
styleon<button>elements or their immediate parent rows.
Settings & Preferences
- Settings are persisted to
settings.jsonin the app data directory and include the Stage 3 ΔE₀₀ traffic-light thresholds. - Valid threshold values must be non-negative and
delta_e_good_max < delta_e_warning_max; both the frontend and backend enforce this. - Saving settings dispatches a
settings-savedcustom event so live components (e.g. the swatch grid) can re-classify on the fly.
Build Commands
- Rust backend:
cd src-tauri && CARGO_INCREMENTAL=0 cargo check(the project lives on a network filesystem that doesn't support file locking, soCARGO_INCREMENTAL=0is required) - Rust tests:
cd src-tauri && CARGO_INCREMENTAL=0 cargo test - Frontend:
cd src-tauri && npm run build(ornpm run devfor development)
Architecture Overview
- Backend: Rust + Tauri v2 (
src-tauri/) - Frontend: Vanilla JS modules (
src/js/), HTML (src/index.html) - Print subsystem: Platform-specific code under
src-tauri/src/print/mod.rs: Shared types (PrintOptions,Printer,PrinterCapabilities,PrinterDevModeStore)macos.rs: macOS-specificlpspooling and nativeNSPrintPanelintegrationunix.rs: Generic Unix/CUPS utilities (printer enumeration, PPD parsing,lpargs)windows.rs: Windows-specific printing via Win32 API and DEVMODE
Cross-Platform Printer Field Notes
When adding fields to Printer in src-tauri/src/print/mod.rs, update every platform-specific constructor in src-tauri/src/print/windows.rs, src-tauri/src/print/macos.rs, and src-tauri/src/print/unix.rs to avoid build regressions on any target. Use ..Default::default() where possible, or explicitly provide values (e.g. display_name: None on Windows).
macOS Print Properties (Issue #188)
The "Preferences" button opens the native macOS NSPrintPanel (not CUPS web UI or System Settings).
- The CUPS destination ID is bound to the panel via Core Printing
PMPrinterCreateFromPrinterIDandPMSessionSetCurrentPMPrinter - A
Printer.display_name(from CUPSprinter-info) is cached at enumeration as a fallback forNSPrinter::printerWithName - Pre-configured with both
AP_ColorMatchingMode=AP_ApplicationColorMatchingandAP.ColorMatchingMode=AP_ApplicationColorMatching(dot-notation) as a locked PMPrintSettings value and in theNSPrintInfojob ticket - Uses the private Core Printing
PMSessionSetColorMatchingMode/PMSessionSetColorMatchingModeLock/PMSessionSetColorMatchingModeNoLockSPI (resolved at runtime viadlsym) to gray out and lock the Color Matching controls; all three symbols use the 2-argument(PMPrintSession, *const CFString)signature;PMSessionSetColorMatchingModeLocksets and locks in one call;NoLocksets the mode without locking; falls back to the publicPMPrintSettingsSetValuesetting if the SPI is absent - Pre-selects the driver-specific "no color adjustment" PPD option (Canon
CNIJIntent2=4, EpsonEPIJ_CMat=3, etc.) in the native panel and on thelpcommand line - Captures user's media type / quality selections as a CUPS options string with
PMPrintSettingsToOptions - Returns a
PrintPropertiesResultwith the effectiveselected_printerand capturedPrintOptions - Cancellation is returned as
None, not an error - Captured options are stored in frontend
capturedCupsOptionsmap and passed viaPrintOptions.cups_options build_lp_argsinmacos.rsalways adds both-o AP_ColorMatchingMode=AP_ApplicationColorMatchingand-o AP.ColorMatchingMode=AP_ApplicationColorMatching, and forwards captured options- Only
AP_ApplicationColorMatchingandApplicationColorMatchingare passed to the private SPI;AP_ColorSyncMatchingandAP_VendorColorMatchingare intentionally avoided because they would enable color management on profiling targets
Key Dependencies (macOS only)
objc20.6 — MainThreadMarker, rcobjc2-app-kit0.3.2 — NSPrintPanel, NSPrintInfo, NSPrinterobjc2-foundation0.3.2 — NSStringobjc2-core-foundation0.3.2 — CFString, CFTypeobjc2-application-services0.3.2 — PMCore (PMPrintSettings, PMPrinter, PMSession, etc.)
PPD Option Detection
- Epson media type key:
EPIJ_Medi(in addition toCNIJMediaType,MediaType,StpMediaType) - Epson color bypass:
EPIJ_CMat=3(Off / No Color Adjustment) - Canon color bypass:
CNIJIntent2=4orCNIJIntent=4 - Gutenprint:
StpColorCorrection=Uncorrected