Stage 1 targen: typed TargenConfig + argv builder (-v -d 2/4, -f, -e, -B, optional advanced flags, CMYK-only -l, never -u), ArgyllRunner over ProcessManager, Stage1View with disk-gated Generate. Issue 8: Open Existing resume — .ti1 → Stage 2, .ti2 → Stage 3 shell with persistent "Resumed from .ti2" banner, sibling-.ti1 gate. Stage 2 printtarg: PrinttargConfig/args (-v -u, -i, -p, -R 1 default, -r raster, -t/-T DPI), Stage2View with instrument/page/DPI/label controls, CM warning, stubbed print panel. Issue 10: pretty-manifest JSON parsing, host-side TIFF→PNG gallery previews, per-page stubbed print, failure stays on stage. Issue 11: typed ProfilingPreset schema with legacy CustomPreset migration, four built-ins, save/manage/import/export UI, DPI binding. Tests: 124 Core tests (Swift Testing) + 11 Milestone2UITests (XCTest) with committed fixture sidecars — all green on arm64+x86_64 universal build. No hardware, no downloads. Also: ProcessError LocalizedError conformance; accessibilityElement .contain on container identifiers so child AX ids survive. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
93 lines
4.0 KiB
Swift
93 lines
4.0 KiB
Swift
import SwiftUI
|
|
import ICCeryCore
|
|
|
|
/// `#savePresetDialog` — save the live Stage 1/2 form as a custom
|
|
/// preset (issue #11). Names/descriptions render via `Text` only (#114).
|
|
struct SavePresetDialog: View {
|
|
@Bindable var workflow: TargetWorkflowViewModel
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: 14) {
|
|
Text("Save Preset").font(.title3).foregroundStyle(Theme.text)
|
|
TextField("Name", text: $workflow.savePresetName)
|
|
.textFieldStyle(.roundedBorder)
|
|
.accessibilityIdentifier("savePresetName")
|
|
TextField("Description (optional)", text: $workflow.savePresetDesc)
|
|
.textFieldStyle(.roundedBorder)
|
|
.accessibilityIdentifier("savePresetDesc")
|
|
HStack {
|
|
Spacer()
|
|
Button("Cancel") { workflow.showingSavePreset = false }
|
|
.accessibilityIdentifier("btnCloseSavePresetDialog")
|
|
Button("Save") { workflow.saveCurrentAsPreset() }
|
|
.accessibilityIdentifier("btnConfirmSavePreset")
|
|
.disabled(workflow.savePresetName
|
|
.trimmingCharacters(in: .whitespaces).isEmpty)
|
|
}
|
|
}
|
|
.padding(20)
|
|
.frame(width: 380)
|
|
.background(Theme.background)
|
|
.accessibilityElement(children: .contain)
|
|
.accessibilityIdentifier("savePresetDialog")
|
|
}
|
|
}
|
|
|
|
/// `#managePresetsDialog` — list, delete (custom only), import, export.
|
|
struct ManagePresetsDialog: View {
|
|
@Bindable var workflow: TargetWorkflowViewModel
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: 12) {
|
|
Text("Manage Presets").font(.title3).foregroundStyle(Theme.text)
|
|
List {
|
|
ForEach(workflow.presets) { preset in
|
|
HStack {
|
|
VStack(alignment: .leading, spacing: 2) {
|
|
Text(preset.name).foregroundStyle(Theme.text)
|
|
if !preset.description.isEmpty {
|
|
Text(preset.description)
|
|
.font(.caption).foregroundStyle(.secondary)
|
|
}
|
|
}
|
|
Spacer()
|
|
if PresetCatalog.isBuiltIn(preset.id) {
|
|
Text("Built-in")
|
|
.font(.caption).foregroundStyle(.secondary)
|
|
} else {
|
|
Button("Export") { workflow.exportPreset(preset) }
|
|
.accessibilityIdentifier(
|
|
"btnExportPreset-\(preset.id)")
|
|
Button("Delete", role: .destructive) {
|
|
workflow.deletePreset(preset)
|
|
}
|
|
.accessibilityIdentifier("btnDeletePreset-\(preset.id)")
|
|
}
|
|
}
|
|
.accessibilityElement(children: .contain)
|
|
.accessibilityIdentifier("presetRow-\(preset.id)")
|
|
}
|
|
}
|
|
.accessibilityIdentifier("managePresetsList")
|
|
.frame(minHeight: 240)
|
|
HStack {
|
|
Button("Import…") { workflow.importPreset() }
|
|
.accessibilityIdentifier("btnImportPreset")
|
|
if let selected = workflow.selectedPreset,
|
|
!PresetCatalog.isBuiltIn(selected.id) {
|
|
Button("Export Active") { workflow.exportPreset(selected) }
|
|
.accessibilityIdentifier("btnExportActivePreset")
|
|
}
|
|
Spacer()
|
|
Button("Close") { workflow.showingManagePresets = false }
|
|
.accessibilityIdentifier("btnCloseManagePresetsDialog")
|
|
}
|
|
}
|
|
.padding(20)
|
|
.frame(width: 480)
|
|
.background(Theme.background)
|
|
.accessibilityElement(children: .contain)
|
|
.accessibilityIdentifier("managePresetsDialog")
|
|
}
|
|
}
|