13 KiB
13 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 & Test Commands
- Rust backend check:
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 unit tests:
cd src-tauri && CARGO_INCREMENTAL=0 cargo test - Frontend test suites:
- Verification & drift tests:
node src/js/profcheck.test.js(21 tests) - Chartread classifier & XY table tests:
node src/js/chartread.test.js(39 tests) - Gamut viewer tests:
node src/js/gamut_viewer.test.js - Browser devtools console:
import('./profcheck.test.js').then(m => m.runAll())
- Verification & drift tests:
- Frontend development server:
npm run tauri dev - Production package build:
npm run tauri build
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
Verification History & Printer Drift Tracking (#95)
- Historical verification runs are stored in
verification_history.jsonin the app data directory. - Record schema (
VerificationRecordinsrc-tauri/src/quality_store.rs):id: unique record identifier in the formatvr-<epoch_millis>-<seq>.profile_name: target profile filename.printer_name: device name captured at print spooling (wizardState.printerName), or "Unknown".avg_de,max_de,rms_de: CIEDE2000 metrics fromprofcheck(using-uJSON summary).patch_count: number of test patches evaluated.status: classified status using ICCery verification bands (issue #95):< 1.0: "Excellent" (badge-excellent)< 2.0: "Good" (badge-good)< 3.5: "Acceptable" (badge-acceptable)>= 3.5: "Warning" (badge-poor)
timestamp: ISO-8601 UTC string.
- Max capacity is 1,000 records; oldest records evicted on overflow.
- Atomic file writes (
.tmpwrite followed byrename) prevent data corruption. - Tauri IPC command casing:
- Nested struct fields (
VerificationRecord) serialize withsnake_case. - Top-level Tauri command arguments use
camelCase(e.g.savePath,record,profileName).
- Nested struct fields (
- Drift history UI in Stage 5 features an interactive SVG trend chart with ICCery verification reference bands, consecutive-breach alert card (requires
\ge 2consecutive runs\ge 3.5on distinct calendar days or\ge 1hour apart), and RFC-4180 compliant CSV export.
Stage 3 XY Automated Scanning Tables (#93)
- Supports automated XY scanning tables (GretagMacbeth SpectroScan, X-Rite i1iO) in Stage 3
chartread. - Hardware detection in
instlistflags devices matching/spectro\s?scan|i1io/iwithdata-xy="1"and· XY Tablelabel suffix. - Runtime auto-detection activates when any XY-specific prompt is classified from
chartreadstdout (supporting i1iO units reporting as i1Pro). - XY State Machine additions:
STATE.TABLE_PLACE_SHEET: Prompts user to place sheet on table; button displays "✓ Sheet Placed — Continue".STATE.TABLE_ALIGN: Prompts user to align measurement head with target fiducial patches (locate patch <ID> with sight); button displays "✓ Aligned — Continue".
- Two-line prompt handling & sticky state:
- Argyll
chartread.csplits XY prompts across two lines (prompt line followed byhit return to continue...). - While in
TABLE_PLACE_SHEETorTABLE_ALIGN, subsequent continuation lines remain sticky in that table state, preserving the custom button label and preventing regression to genericPROMPT_CONTINUE.
- Argyll
- Button behaviors:
btnAccept: inTABLE_*states, sends\nwithout forcingSTATE.READING; the state machine advances naturally when Argyll emits the next prompt.btnCancel: inTABLE_*states or when an XY table is active, sendsq\nfirst to allow the hardware to park its measurement head gracefully before terminating the process.
- Multi-sheet and final sheet notice:
- Multi-sheet targets are measured within a single
chartreadprocess lifecycle; sheet changes transition throughTABLE_PLACE_SHEETwithout opening the Stage 3 multi-pass averaging panel. Please remove last sheet from tableis emitted by Argyll right before writing.ti3and exiting; it is classified as an info-only notice (isRemoveSheetNotice: true) and does not prompt for user input.
- Multi-sheet targets are measured within a single
- Testing:
- Pure line classification unit tests live in
src/js/chartread.test.js(executable directly in Node or browser console). - Unix/macOS mock script
src-tauri/argyll/mocks/chartread.mocksupports--xyflag (orMOCK_XY_TABLE=1) with blockingreadcalls simulating calibration, sheet placement, fiducial alignment, and scanning.
- Pure line classification unit tests live in
Stage 3 i1Pro 2 LED Status Feedback (#204)
- Supports the
-Y lswitch introduced in the ICCery ArgyllCMS fork to drive the dual RGB ring LEDs of the X-Rite i1Pro 2 (Rev E) for real-time visual status feedback during strip measurement:- Flashing White: Awaiting baseline calibration on white tile.
- Flashing Blue: Ready for row swipe / awaiting strip read.
- Flashing Red: Strip scan error / misread.
- Flashing Green: Strip scan successfully captured.
- Controlled via
enable_i1pro2_leds: boolinAppSettings(persisted insettings.json), exposed under Settings → Instrument & Measurement Preferences. - Defaults to
falseensuring 100% out-of-the-box compatibility with stock upstream ArgyllCMS binaries. - Subprocess error diagnostics in
chartread.jscapturelastStderrLinefromprocess:stderr, auto-expanding the Process Output<details>panel with the stderr explanation if an unpatched binary rejects-Y l.
CI & Cross-Compilation
- Release packaging workflows live under
.gitea/workflows/(build-macos.yml,build-linux.yml,build-windows.yml). - Tag release builds focus exclusively on packaging via
npm run tauri buildwithout redundant debug-profile test compilations. - Local/CI cross-compilation test execution for Apple Silicon (
aarch64-apple-darwin) on Intel hosts must usecargo test --no-run --target aarch64-apple-darwinto avoid executing ARM64 binaries on an x86_64 CPU (Bad CPU type in executable (os error 86)).