Split print session (and preset sheet state) out of TargetWorkflowViewModel #85

Closed
opened 2026-09-10 18:23:15 +01:00 by gronod · 1 comment
Owner

Summary

TargetWorkflowViewModel (719 lines) is the composition root and Stage 1 form and Stage 2 form and CUPS print session and preset CRUD, while also owning measurement, profile, and calibration. Print-queue state sitting next to customPatchCount makes Stage 2 and Stage 1 PRs collide and is why print notices were a second banner system.

Spec refs

  • Issues 12–15, 17 (print panel)
  • Issue 11 (presets)
  • Sources/ICCery/TargetWorkflowViewModel.swift
  • Sources/ICCery/RootView.swift / Stage2View.swift

Scope

In

  1. PrintSessionViewModel (Sources/ICCery/Print/PrintSessionViewModel.swift):

    • printers, selectedPrinter, printerCaps, tray/media/orientation
    • capturedCupsOptions
    • printNotice: Notice? (from the notice-unification ticket)
    • isPrinting, printTask
    • refreshPrinters, reloadSelectedCapabilities, openPrinterPreferences, printAllPages, printPage, spool
    • Writes wizard.printerName after a successful spool (Stage 5 history #95)
  2. Optional but recommended in the same PR if it stays mechanical: PresetSession holding presets, selectedPresetID, save/manage sheet flags, import/export. applyPreset / saveCurrentAsPreset can remain on the target VM because they touch Stage 1/2 form fields — or they take the form snapshot as input. Do not move targen form fields into the preset type.

  3. TargetWorkflowViewModel keeps:

    • Stage 1/2 form + buildTargenConfig / buildPrinttargConfig
    • generateTarget, createLayout, resume/import
    • ownership of wizard + measurement + profile + calibration
    • print: PrintSessionViewModel property
  4. Stage2View print panel / gallery Print buttons bind to workflow.print.

  5. RootView preset sheets can bind to workflow or workflow.presets without reaching into print state.

Out

  • Changing lp argv or ColorSync suppression.
  • Merging measurement/profile/calibration into one VM.
  • Changing accessibility ids (printerSelect, btnPrintAll, btnPrintPage-N, rawPrintPanel, …).

Full solution

  • AppEnvironment.cupsService is injected into PrintSessionViewModel; do not construct a second CupsService.
  • printAllPages still stops on first failure.
  • Strong printTask reference stays so unstructured Task is not deallocated mid-spool.
  • onChange(of: selectedPrinter) still clears tray/media and reloads caps.

Rewrite invariants

  • Never -o raw on macOS.
  • Both AP_ColorMatchingMode and AP.ColorMatchingMode still come from LpArgs / ColorMatchingAttempts, not from this VM.
  • Cancelled NSPrintPanel → info notice, cache untouched.

Dependencies

Blocks-on: notice-unification ticket (Notice must exist for print chrome before the type moves).
Unblocks: none.

Test

  • Tests/ICCeryUITests/Milestone3UITests.swift is the gate:
    • refresh printers / default selection
    • preferences cancel → info
    • preferences OK captures options
    • print all / print page disable rules
    • ICCERY_TEST_LP_ARGV still records both AP_* keys
  • If you extract spool into a testable type, add a Core-level test that wizard.printerName is assigned after success (can be a small fake CupsService).
  • Milestone 2 tests must still generate + layout without constructing print state beyond init.

Acceptance criteria

  • TargetWorkflowViewModel no longer contains CUPS/spool stored properties.
  • Stage 2 identifiers unchanged.
  • Milestone 3 UI tests green.
  • Milestone 2 UI tests green.
## Summary `TargetWorkflowViewModel` (719 lines) is the composition root **and** Stage 1 form **and** Stage 2 form **and** CUPS print session **and** preset CRUD, while also owning `measurement`, `profile`, and `calibration`. Print-queue state sitting next to `customPatchCount` makes Stage 2 and Stage 1 PRs collide and is why print notices were a second banner system. ## Spec refs - Issues 12–15, 17 (print panel) - Issue 11 (presets) - `Sources/ICCery/TargetWorkflowViewModel.swift` - `Sources/ICCery/RootView.swift` / `Stage2View.swift` ## Scope **In** 1. `PrintSessionViewModel` (`Sources/ICCery/Print/PrintSessionViewModel.swift`): - `printers`, `selectedPrinter`, `printerCaps`, tray/media/orientation - `capturedCupsOptions` - `printNotice: Notice?` (from the notice-unification ticket) - `isPrinting`, `printTask` - `refreshPrinters`, `reloadSelectedCapabilities`, `openPrinterPreferences`, `printAllPages`, `printPage`, `spool` - Writes `wizard.printerName` after a successful spool (Stage 5 history #95) 2. Optional but recommended in the same PR if it stays mechanical: `PresetSession` holding `presets`, `selectedPresetID`, save/manage sheet flags, import/export. `applyPreset` / `saveCurrentAsPreset` can remain on the target VM because they touch Stage 1/2 form fields — or they take the form snapshot as input. Do not move targen form fields into the preset type. 3. `TargetWorkflowViewModel` keeps: - Stage 1/2 form + `buildTargenConfig` / `buildPrinttargConfig` - `generateTarget`, `createLayout`, resume/import - ownership of wizard + measurement + profile + calibration - `print: PrintSessionViewModel` property 4. `Stage2View` print panel / gallery Print buttons bind to `workflow.print`. 5. `RootView` preset sheets can bind to `workflow` or `workflow.presets` without reaching into print state. **Out** - Changing `lp` argv or ColorSync suppression. - Merging measurement/profile/calibration into one VM. - Changing accessibility ids (`printerSelect`, `btnPrintAll`, `btnPrintPage-N`, `rawPrintPanel`, …). ## Full solution - `AppEnvironment.cupsService` is injected into `PrintSessionViewModel`; do not construct a second `CupsService`. - `printAllPages` still stops on first failure. - Strong `printTask` reference stays so unstructured `Task` is not deallocated mid-spool. - `onChange(of: selectedPrinter)` still clears tray/media and reloads caps. ## Rewrite invariants - Never `-o raw` on macOS. - Both `AP_ColorMatchingMode` and `AP.ColorMatchingMode` still come from `LpArgs` / `ColorMatchingAttempts`, not from this VM. - Cancelled NSPrintPanel → info notice, cache untouched. ## Dependencies Blocks-on: notice-unification ticket (`Notice` must exist for print chrome before the type moves). Unblocks: none. ## Test - `Tests/ICCeryUITests/Milestone3UITests.swift` is the gate: - refresh printers / default selection - preferences cancel → info - preferences OK captures options - print all / print page disable rules - `ICCERY_TEST_LP_ARGV` still records both AP_* keys - If you extract spool into a testable type, add a Core-level test that `wizard.printerName` is assigned after success (can be a small fake CupsService). - Milestone 2 tests must still generate + layout without constructing print state beyond init. ## Acceptance criteria - [ ] `TargetWorkflowViewModel` no longer contains CUPS/spool stored properties. - [ ] Stage 2 identifiers unchanged. - [ ] Milestone 3 UI tests green. - [ ] Milestone 2 UI tests green.
gronod added this to the M7 — Deduplicate & consolidate (develop) milestone 2026-09-10 18:23:15 +01:00
gronod self-assigned this 2026-09-10 18:23:15 +01:00
Author
Owner

Closing #85. Implementation landed via stacked commit d4261ba (PR #87). Verified that PrintSessionViewModel owns print session state, TargetWorkflowViewModel is decoupled from spooling, Stage2View bindings remain intact, and UI test coverage passed in baseline verification at 0332a2bb4f. Acceptance criteria fully satisfied.

Closing #85. Implementation landed via stacked commit d4261ba (PR #87). Verified that PrintSessionViewModel owns print session state, TargetWorkflowViewModel is decoupled from spooling, Stage2View bindings remain intact, and UI test coverage passed in baseline verification at 0332a2bb4f26f7f88e7ff8e36b084a145f69b01c. Acceptance criteria fully satisfied.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: gronod/iccery-v2-mac#85