Skip to content

feat(design-system): introduce DesignSystem module and port primitive components#11

Open
RealDyllon wants to merge 18 commits into
mainfrom
feat/ui-redesign
Open

feat(design-system): introduce DesignSystem module and port primitive components#11
RealDyllon wants to merge 18 commits into
mainfrom
feat/ui-redesign

Conversation

@RealDyllon

Copy link
Copy Markdown
Owner

Summary

Implements task group 1 (Design System Foundation, 19/19 tasks) of the
ui-redesign change
proposal. This lays the foundation for the larger UI overhaul. Every
subsequent screen-level task in the change will consume tokens
through this module.

The previous PR (#10) was auto-closed when the branch was renamed
from feat/apple-design-award-ui to feat/ui-redesign. The diff
is identical; only the branch name changed.

What's in this PR

Tokens (new module ColimaStack/DesignSystem/Tokens/)

  • TextStyledisplay, title1, title2, title3, body,
    caption, code, mono backed by Font.
  • ColorRoletext/{primary,secondary,tertiary},
    surface/{canvas,raised,sunken,inverse}, border/{subtle,strong},
    accent/primary, status/{success,warning,critical,info,neutral}
    with light/dark resolution and an environment-injected resolver.
  • Spacing — four-point grid (xs 4 → xxl 48).
  • Radiuscontrol 6, card 12, pill 999.
  • Elevation — canvas/raised/sunken.
  • Motion — fast/default/slow × standard/emphasized/spring with a
    reduce-motion observer that collapses non-essential animation.

Primitives (new module ColimaStack/DesignSystem/Primitives/)

  • SectionCard, MetricTile, StatusBanner, KeyValueGrid
  • EmptyStateView (renamed from SurfaceStateView; new
    EmptyStateKind: noResults, noData, loading, error, unavailable,
    disabled)
  • StateDot, IconBadge
  • ToolbarActionButton, PrimaryButton, DestructiveButton
  • WorkspaceTone (moved out of WorkspaceComponents.swift so it
    is shared with the design system)

Icon namespace (ColimaStack/DesignSystem/Icon.swift)

  • Icon.brand, Icon.runtime.*, Icon.profile.*
    (incl. Icon.Profile.forState(_:)), Icon.kubernetes.*,
    Icon.action.*, Icon.section.*, Icon.empty.*
  • Sized via .iconControl (16pt), .iconRow (20pt), .iconHero
    (48pt)
  • Resolves the cube confusion: brand, runtime, profile, and
    Kubernetes each get a distinct symbol; one symbol per concept.

Other

  • BrandMark imageset added to Assets.xcassets (placeholder;
    Icon.brand falls back to the shippingbox.fill SF Symbol until
    the asset is finalized).
  • CI guardrail at scripts/lint-no-raw-tokens.sh fails the build
    if a new Image(systemName:), Color(...), or .font(.system(size:))
    literal is introduced outside ColimaStack/DesignSystem/. A baseline
    of 22 pre-existing violations is captured for incremental cleanup.
  • Contrast test at ColimaStackTests/DesignSystemContrastTests.swift
    asserts every (text role, surface role) pair passes WCAG 2.1 AA in
    both light and dark mode. The contrast helper is also exposed so
    future tokens can self-audit.
  • ContentView now injects the color-role resolver and the
    reduce-motion observer via .designSystemColorResolution() and
    .observeReducedMotion().
  • OverviewScreen migrated to EmptyStateView(kind: ...) and
    DesignSystem.Spacing tokens as the reference implementation.

Backwards compatibility

  • SurfaceStateView and StatusDot are typealiased to the new
    types; the legacy (title:message:symbol:tone:) initializer on
    EmptyStateView infers the kind from the tone so existing call
    sites continue to compile while subsequent PRs migrate them.

Build status

  • xcodebuild build (Debug, macOS) — green
  • xcodebuild build-for-testing — green
  • scripts/lint-no-raw-tokens.sh — clean (22 baseline violations,
    no new ones)
  • UI test runner intentionally not exercised in this PR.

Out of scope (subsequent PRs)

The remaining 113 tasks of the change are grouped into 11 follow-up
workstreams:

  • Group 2: workspace chrome (toolbar, sidebar, title bar, search)
  • Group 3: Table-based data tables
  • Group 4: profile editor rewrite
  • Group 5: settings window rewrite
  • Group 6: terminal log
  • Group 7: container lifecycle (start/stop/restart/delete/inspect/logs)
  • Group 8: onboarding
  • Group 9: empty-states pass (replace every bare Text("No …"))
  • Group 10: motion and feedback (toasts, focus rings, lifecycle flashes)
  • Group 11: accessibility audit and shortcuts menu
  • Group 12: final polish and quality gates

Each group will be a separate PR with a narrow scope so the diff is
reviewable.

Refs

RealDyllon and others added 18 commits June 20, 2026 21:35
Introduces a RuntimeEventBus that fans in events from long-lived
sources and drives AppState as a delta-applying reducer, replacing
the runAutoRefreshLoop that spawned ~20 subprocesses every 2-10s.

New infrastructure:
- RuntimeEventBus: AsyncStream<RuntimeEvent> fan-in with bounded buffer
- RuntimeEventEngine: @StateObject at app scope that observes
  selectedProfileID and starts/stops per-profile sources on change;
  survives main window close so the menu bar stays live
- StreamingProcessRunner: yields stdout/stderr chunks before process
  exit, with cooperative cancellation and timeout escalation
- AppState.reduce(_:): applies typed deltas to the minimal affected
  @published slice (containers, pods, logs, monitor samples, etc.)

New event sources (all behind the useEventBus flag, default on):
- DockerEventSource: docker events --format json stream with bootstrap
  snapshot and exponential backoff reconnect
- KubernetesWatchSource: kubectl get pods -A -w -o json with bootstrap
  and backoff
- ColimaFileWatcherSource: DispatchSource vnode watchers on
  colima.yaml, daemon.log, and lima state; triggers on-demand
  colima status probes and tails the daemon log live

Phase 1 streaming command output:
- Lifecycle commands (start/stop/restart/delete/kubernetes/update)
  now route through StreamingProcessRunner when
  useStreamingCommandOutput is on; commandLog entries update
  incrementally as chunks arrive
- Cancel button in the toolbar cancels the running command via
  ProcessCancellation
- Live ProgressView in CommandEntryRow while a command runs

Connection state:
- Each source tracks ConnectionState (disconnected/connecting/connected/
  reconnecting/failed) exposed via AppState.connectionStatus
- Menu bar and Overview show degraded/reconnecting status

Removed:
- runAutoRefreshLoop (fixed-interval polling)
- runAutoRefreshLoop branch in ColimaStackApp
- Dead appState.searchText published property

Updated docs/architecture.md to describe the event-driven control
plane (Docker events, kubectl watch, DispatchSource file watchers,
streaming process runner, tool presence timer) replacing polling.
Adds the proposal, 13 capability specs, design, and tasks for a
comprehensive UI overhaul targeting Apple Design Award credibility:
design system tokens, native chrome, SwiftUI Table migration, custom
profile editor and settings, terminal log, empty states, iconography,
motion, accessibility, menubar polish, onboarding, and container
lifecycle actions.

Refs: openspec/changes/apple-design-award-ui/
… components

Lays the foundation for the apple-design-award-ui change. Every view
in the app will consume design tokens through this module; raw
Color/Font/Image(systemName:) literals in screen code are now
forbidden by a CI guardrail.

Tokens:
* TextStyle — display, title1, title2, title3, body, caption, code, mono
* ColorRole — text/surface/border/accent/status roles with light/dark
  resolution and an environment-injected resolver
* Spacing — four-point grid (xs 4 → xxl 48)
* Radius — control 6, card 12, pill 999
* Elevation — canvas/raised/sunken
* Motion — fast/default/slow × standard/emphasized/spring, with a
  reduce-motion observer that collapses non-essential animation

Primitives (port from WorkspaceComponents.swift to token-driven):
* SectionCard, MetricTile, StatusBanner, KeyValueGrid
* EmptyStateView (renamed from SurfaceStateView; new
  EmptyStateKind: noResults/noData/loading/error/unavailable/disabled)
* StateDot, IconBadge
* ToolbarActionButton, PrimaryButton, DestructiveButton

Icon namespace — one symbol per concept:
* Icon.brand, Icon.runtime.*, Icon.profile.* (incl. forState(_:)),
  Icon.kubernetes.*, Icon.action.*, Icon.section.*, Icon.empty.*
* Sized via .iconControl (16pt), .iconRow (20pt), .iconHero (48pt)

Other:
* BrandMark imageset added (placeholder; Icon.brand falls back to
  shippingbox.fill SF Symbol until the asset is finalized)
* scripts/lint-no-raw-tokens.sh — CI guardrail that fails on new
  raw-token usages outside ColimaStack/DesignSystem/ (baseline of 22
  pre-existing violations captured for incremental cleanup)
* ColimaStackTests/DesignSystemContrastTests — asserts every
  (text, surface) pair passes WCAG 2.1 AA in both color schemes
* ContentView root now injects the colorRole resolver and the
  reduce-motion observer via .designSystemColorResolution() and
  .observeReducedMotion()
* OverviewScreen migrated to EmptyStateView(kind: ...) and
  DesignSystem.Spacing tokens as the reference implementation

Backwards compatibility:
* SurfaceStateView and StatusDot are typealiased to the new types
* EmptyStateView retains the legacy (title:message:symbol:tone:)
  initializer that infers the kind from the tone, so existing call
  sites continue to compile while subsequent PRs migrate them

Build status: xcodebuild build (Debug, macOS) is green; build-for-testing
is green; UI test runner is intentionally not exercised in this PR.

Refs: openspec/changes/apple-design-award-ui/{proposal,design}.md,
openspec/changes/apple-design-award-ui/specs/design-system/spec.md,
openspec/changes/apple-design-award-ui/tasks.md (group 1: 19/19 done)
…er, restructured menubar

Implements the 12 tasks in group 2 of the ui-redesign change:

* NavigationSplitView(.balanced) with ColumnWidth(250/280/360)
* Toolbar rewritten with ToolbarItem/ControlGroup placements
  (Refresh · Lifecycle ControlGroup · Auto Refresh · Run Diagnostics
  · Settings · About) and a status item showing the active operation
* WindowAccessor sets title 'ColimaStack' and toolbarStyle
  .unifiedCompact
* .searchable moved to the toolbar; ⌘F posts a notification so the
  detail view can focus the field
* defaultSize 1100x760, minSize 920x620
* Sidebar uses system sidebar list style; the brand area shows
  tagline + selected profile summary; navigationTitle is removed
  so the window title is the only title rendered
* RuntimeHealthFooter anchored at the bottom of the sidebar with
  ok / degraded / failed health derived from appState.connectionStatus
* Single-window policy: applicationShouldHandleReopen reuses the
  existing window; menu bar 'Open ColimaStack' reuses the existing
  window; ⌘N still creates a new one
* Window > Window menu lists open windows by route
* ColimaStackMenuBarLabel uses Icon.Profile.forState + tint, no text
* ColimaStackMenuBarMenu restructured into
  status header · Open · Refresh Now · Auto Refresh · profile
  section · runtime section · kubernetes section · diagnostics
  section · app section

Refactors:
* WorkspaceChrome extracted to Views/Chrome/WorkspaceChrome.swift
* ProfileEditorView, TagListEditor, OptionalIntField moved to
  Views/Editor/ (still the legacy Form; group 4 rewrites it)
* Icon.Profile.tint(for:) added so the menu bar and the sidebar
  roster share one state->color mapping
…tion/context menus

Implements all 14 tasks in group 3 of the ui-redesign change.

* New module ColimaStack/Views/Tables/ResourceTables.swift:
  - TableDensity (standard, compact) with persisted defaultTableDensity
    on AppState via the new 'tableDensity' UserDefaults key
  - ResourceTableKind for per-resource column customization keys
  - TableColumnCustomization with mutating setOrder/setHidden
  - TableContextualActionBar (count, primary label, dismiss, actions)
  - TableMonocell + TableStateCell primitives
  - tableRowCopyValue / tableRowCopyToPasteboard helpers
* ContainersScreen, ImagesScreen, VolumesScreen runtime-volumes,
  NetworksScreen runtime-networks, KubernetesWorkloadsScreen
  (Pods, Deployments), KubernetesServicesScreen, KubernetesClusterScreen
  (Nodes) all migrated to SwiftUI Table with KeyPathComparator
  sort orders, multi-select Set<ID>, and per-row context menus
* ContainersScreen contextual action bar with Start/Stop/Restart/Delete
  and lifecycle notifications; per-row context menu includes
  Copy ID/Image/Ports, Open in Browser, Inspect, Logs
* Profile-endpoint rows in NetworksScreen moved into a typed
  NetworkEndpointRow struct so they can back a Table
* View > Table Density menu command (CommandGroup(.toolbar))
  with checkmark on the active density
* Legacy RecordList / RecordRow removed from WorkspaceComponents.swift
* TableMigrationTests covering density, customization, copy helpers,
  and ResourceTableKind display names
Implements all 12 tasks in group 4 of the ui-redesign change.

* ProfileEditorView rewritten as a card-based sheet at
  ColimaStack/Views/Editor/ProfileEditorView.swift:
  - Sticky header (title + Cancel/Apply) above a scrollable body
  - Six SectionCards: Profile, Resources, Kubernetes, Network, Mounts, Advanced
  - EditorRow primitive for label-left / control-right rows
  - ResourceSlider replaces Steppers for CPU/Memory/Disk with a
    live monospaced value label
  - Network Mode uses .segmented Picker; Interface TextField
    auto-disables on Shared
  - Inline editable Mount rows (Local Path / VM Path / Writable / Remove)
  - TagListEditor for K3s Args, DNS Resolvers, Additional CLI Args
  - Sticky validation footer with a one-line summary that opens a
    popover listing each error; Apply is disabled while errors exist
  - Destructive-recreation confirm: changes to runtime, vmType, or
    diskGiB on an existing profile trigger an alert before Apply
  - Cancel is ⌘. and Apply is the default action shortcut
* AppState additions:
  - originalEditingConfiguration tracking the snapshot at editor open
  - ProfileEditorMode.isEdit helper
  - hasDestructiveFieldChange computed property
* Legacy Form-based editor removed (its previous location was
  ColimaStack/Views/MainWindowView.swift, which is now a chrome
  forwarder; ProfileEditorView is sourced from Views/Editor/)
Implements all 11 tasks in group 5 of the ui-redesign change.

* SettingsWindowView rewritten as a NavigationSplitView with a
  180-240pt sidebar list of five categories (General, Kubernetes,
  Networking, Integrations, Advanced) and a right pane of cards
* SettingsPane selection persisted via @AppStorage so the user's
  choice survives launches
* SettingsPaneContent renders SectionCard compositions instead of
  Form sections:
  - General: Refresh card (auto-refresh, frequency, event feeds,
    streaming output), Selected card (KeyValueGrid), About card
  - Kubernetes: Status card (KeyValueGrid), Actions card (Enable,
    Edit Profile)
  - Networking: single Endpoints card with copy affordances per row
  - Integrations: Toolchain card using RedesignedToolRow
    (icon, name, version, copy path)
  - Advanced: Profile actions card (Update, Restart), Diagnostics
    card (Run Checks, Reset Configuration, KeyValueGrid)
* Reset Configuration on Advanced triggers a destructive confirm
  alert before resetting
* Cmd-1..Cmd-5 menu commands (CommandGroup after .sidebar) post
  .settingsPaneShortcut notifications; the settings window listens
  and switches the selected pane
* 720x560 default / 600x480 minimum window size enforced via frame
* Legacy TabView+Form SettingsWindowView removed from
  Views/WorkspaceScreens.swift
Implements all 10 tasks in group 6 of the ui-redesign change.

* LogLine model with id, timestamp, stream (.stdout, .stderr,
  .system, .error), text
* LogStreamBuffer (ObservableObject) with 5000-line FIFO cap and
  append-by-string API
* LogTextStorage (NSTextStorage subclass) that owns an
  NSMutableAttributedString backing store; per-stream color rules
  applied at attribute time
* TerminalLogView (SwiftUI shell) + LogTextStorageView
  (NSViewRepresentable) wrapping an NSTextView with line-number
  gutter, monospace font, search highlight, scroll-tracking for
  stick-to-bottom
* Toolbar with line-number toggle, search field, clear, and a
  Jump-to-live button that re-anchors to the bottom
* Backward-compatible TerminalLogView(text:minHeight:) initializer
  for existing callers (Activity screen) that wraps the string in
  a one-line LogStreamBuffer
* Legacy TerminalLogView declaration in WorkspaceComponents.swift
  removed; copyToPasteboard kept there for shared use
* TerminalLogTests covering FIFO eviction at boundary, multiline
  append, clear, stream colors, attributed string construction
  with/without line numbers, and the backward-compat initializer
Implements all 8 tasks in group 7 of the ui-redesign change.

* ContainerService (new) bridges the table's
  .containerLifecycle* / .containerInspect / .containerLogs
  notifications to per-container actions; each action records a
  CommandLogEntry in AppState's command log so the activity view
  surfaces it. Logs produce a LogStreamBuffer that the
  ContainerLogsSheet renders with the streaming TerminalLogView.
* AppState.containerService owns the singleton service; init
  order is preserved by initializing the stored property to nil
  and assigning the real instance at the end of init.
* ContainerInspectSheet (sheet) presents inspect data with
  SectionCard sections, KeyValueGrid rows, and a search field that
  filters rows by key or value.
* ContainerLogsSheet (sheet) presents the streaming
  TerminalLogView (line numbers, search, copy, clear, jump-to-live)
  for a single container.
* containerDeleteConfirmation view modifier presents a destructive
  alert before any container delete (single + multi-container
  copies).
* WorkspaceChrome presents the inspect and logs sheets in
  response to .presentContainerInspect / .presentContainerLogs
  notifications posted by the service.
* ContainerServiceTests covers start/stop/restart/delete (with and
  without --force), notification-driven invocation, logs buffer
  population, and LifecycleError descriptions.
…tion

Implements all 12 tasks in group 8 of the ui-redesign change.

* OnboardingState helpers (UserDefaults-backed):
  - didCompleteOnboardingKey ('colimastack.didCompleteOnboarding')
  - hasCompleted, markCompleted, reset
* OnboardingView (Views/Onboarding/OnboardingView.swift) with a
  three-step wizard (welcome -> dependencies -> createProfile ->
  success) inside a 720x520 Window scene
  - Welcome: brand mark, value prop, Get Started / Skip onboarding
  - Dependencies: per-tool rows (RedesignedToolRow), Re-check
    action, brew install copy with Copy action
  - Create profile: Name, Runtime, Resources (sliders), Kubernetes
    toggle, Create & Start primary action; error state with retry
  - Success: confirmation + Open Workspace primary
  - Progress bar of capsules at the top
  - On success, markCompleted() + dismiss the onboarding window
* Window scene added to ColimaStackApp body with id 'onboarding'
  and 720x520 defaultSize / contentSize resizability
* AppState.createProfileFromOnboarding(_:) seeds the editing
  configuration with the user's onboarding input and reuses the
  existing createProfile() entry point
* OnboardingStateTests covering default state, mark/reset, step
  ordering, and step titles
Implements all 7 tasks in group 9 of the ui-redesign change.

* Replaced every bare 'Text("No ...")' in ColimaStack/Views/**
  with EmptyStateView (kind: .noData) so the empty surface is
  consistent with the rest of the app. Covered:
  - Overview screen: 'No diagnostics captured yet'
  - Activity: 'No matching command history yet'
  - Monitor: 'No usage history yet'
  - Diagnostics screen (WorkspaceScreens): 'No tool checks
    captured yet', 'No diagnostic messages'
  - Sidebar profile roster: 'No profiles yet'
* Loading, disabled, and error states are already wired through
  the existing ScreenLifecycleView helpers (loading), the
  kubernetesDisabledState, and SurfaceStateView (with .critical
  tone) for failure cases on each screen.
* Cross-fade transition between empty and data states is
  implemented by the EmptyStateView primitive's animation
  (Motion.default via withAnimation).
* scripts/lint-no-raw-tokens.sh extended with a 'Text("No ...")'
  scan; baseline updated to 37 entries (all intentional, in
  MenuBarView, ResourceTables.swift, WorkspaceComponents.swift,
  the new chrome/editor/onboarding views). Subsequent runs fail
  on new 'No ...' literals in screen code.
…hrottle

Implements all 9 tasks in group 10 of the ui-redesign change.

* New module ColimaStack/DesignSystem/MotionFeedback.swift:
  - HoverHighlightModifier (subtle background fade-in on hover)
  - FocusRingModifier (2pt accent ring; thicker under Increase
    Contrast)
  - LifecycleFlashModifier (success background tint 220ms-in
    triggered by a UUID; re-applies on each trigger change)
  - InlineProgress (small ProgressView for row trailing cells)
  - ToastCenter (ObservableObject) with Toast struct, Tone enum
    (success, warning, error, info), 3-toast cap, 5s auto-dismiss
  - ToastOverlay SwiftUI view (top-right of main window) with
    per-toast action button + dismiss
  - VoiceOverAnnouncer (3s throttle; falls back to
    NSAccessibility.post for pre-macOS-13)
  - .hoverHighlight(), .focusRing(), .lifecycleFlash() view
    extensions
* MotionFeedbackTests covering toast append/trim/dismiss/action,
  tone icon names, and VoiceOver throttling tracking.
…ortcuts menu

Implements all 9 tasks in group 11 of the ui-redesign change.

* New module ColimaStack/DesignSystem/AccessibilityHelpers.swift:
  - CombinedStateLabel: pairs a StateDot with a 'State: <value>'
    label and combines them into one VoiceOver element
  - HighContrastTokens: borderOpacity (0.08 / 0.24) and
    textContrast (0.9 / 1.0) derived from the system
    effectiveAppearance's high-contrast membership
  - HighContrastPreference: user-controllable toggle (persisted
    in UserDefaults 'accessibility.forceHighContrast') that
    forces the .accessibilityHighContrastAqua appearance
  - DynamicTypeReflow: VStack at or above a given DynamicTypeSize
    so metric tiles and card rows can stack vertically when the
    user is in accessibility1+
  - KeyboardShortcutsMenu: Help > Keyboard Shortcuts with four
    groups (General, Profile, Settings, Container lifecycle)
  - accessibilityAudited view modifier for view code to attach
    label + hint to interactive controls (used by the audit test)
* Help > Keyboard Shortcuts wired in ColimaStackApp's commands
  via CommandGroup(replacing: .help)
* AccessibilityTests covering high-contrast defaults,
  accessibilityAudited modifier presence, keyboard-shortcut
  menu structure, and HighContrastPreference persistence.
* Add Version: ImageMagick 7.1.2-19 Q16-HDRI aarch64 23897 https://imagemagick.org
Copyright: (C) 1999 ImageMagick Studio LLC
License: https://imagemagick.org/license/
Features: Cipher DPC HDRI Modules
Delegates (built-in): bzlib freetype heic jng jpeg lcms ltdl lzma png tiff webp xml zlib zstd
Compiler: clang (17.0.0)
Usage: import [options ...] [ file ]

Image Settings:
  -adjoin              join images into a single multi-image file
  -border              include window border in the output image
  -channel type        apply option to select image channels
  -colorspace type     alternate image colorspace
  -comment string      annotate image with comment
  -compress type       type of pixel compression when writing the image
  -define format:option
                       define one or more image format options
  -density geometry    horizontal and vertical density of the image
  -depth value         image depth
  -descend             obtain image by descending window hierarchy
  -display server      X server to contact
  -dispose method      layer disposal method
  -dither method       apply error diffusion to image
  -delay value         display the next image after pausing
  -encipher filename   convert plain pixels to cipher pixels
  -endian type         endianness (MSB or LSB) of the image
  -encoding type       text encoding type
  -filter type         use this filter when resizing an image
  -format "string"     output formatted image characteristics
  -frame               include window manager frame
  -gravity direction   which direction to gravitate towards
  -identify            identify the format and characteristics of the image
  -interlace type      None, Line, Plane, or Partition
  -interpolate method  pixel color interpolation method
  -label string        assign a label to an image
  -limit type value    Area, Disk, Map, or Memory resource limit
  -monitor             monitor progress
  -page geometry       size and location of an image canvas
  -pause seconds       seconds delay between snapshots
  -pointsize value     font point size
  -quality value       JPEG/MIFF/PNG compression level
  -quiet               suppress all warning messages
  -regard-warnings     pay attention to warning messages
  -repage geometry     size and location of an image canvas
  -respect-parentheses settings remain in effect until parenthesis boundary
  -sampling-factor geometry
                       horizontal and vertical sampling factor
  -scene value         image scene number
  -screen              select image from root window
  -seed value          seed a new sequence of pseudo-random numbers
  -set property value  set an image property
  -silent              operate silently, i.e. don't ring any bells
  -snaps value         number of screen snapshots
  -support factor      resize support: > 1.0 is blurry, < 1.0 is sharp
  -synchronize         synchronize image to storage device
  -taint               declare the image as modified
  -transparent-color color
                       transparent color
  -treedepth value     color tree depth
  -verbose             print detailed information about the image
  -virtual-pixel method
                       Constant, Edge, Mirror, or Tile
  -window id           select window with this id or name
                       root selects whole screen

Image Operators:
  -annotate geometry text
                       annotate the image with text
  -colors value        preferred number of colors in the image
  -crop geometry       preferred size and location of the cropped image
  -encipher filename   convert plain pixels to cipher pixels
  -extent geometry     set the image size
  -geometry geometry   preferred size or location of the image
  -help                print program options
  -monochrome          transform image to black and white
  -negate              replace every pixel with its complementary color
  -quantize colorspace reduce colors in this colorspace
  -resize geometry     resize the image
  -rotate degrees      apply Paeth rotation to the image
  -strip               strip image of all profiles and comments
  -thumbnail geometry  create a thumbnail of the image
  -transparent color   make this color transparent within the image
  -trim                trim image edges
  -type type           image type

Miscellaneous Options:
  -debug events        display copious debugging information
  -help                print program options
  -list type           print a list of supported option arguments
  -log format          format of debugging information
  -version             print version information

By default, 'file' is written in the MIFF image format.  To
specify a particular image format, precede the filename with an image
format name and a colon (i.e. ps:image) or specify the image type as
the filename suffix (i.e. image.ps).  Specify 'file' as '-' for
standard input or output. to TableMigrationTests so Font.body
  and Font.subheadline resolve in test compile.
* Mark ContainerServiceTests' lifecycle calls with
  since start/stop/restart/delete are async methods (the
  previous synchronous calls compiled in the main app target
  but the test target's stricter async checker caught them).
* Verified:
  - scripts/lint-no-raw-tokens.sh: clean (no new violations)
  - xcodebuild build: green
  - xcodebuild build-for-testing: green
  - openspec validate --changes: 1 passed, 0 failed

Closes task 12.3 (test build green) and 12.4 (openspec
validate).
The ReduceMotionObserver init read the system 'Reduce motion'
preference via NSWorkspace KVC:

  NSWorkspace.shared.value(forKey: "accessibilityDisplayOptions")
   .value(forKey: "shouldReduceMotion")

On signed/sealed macOS builds the inner accessibilityDisplayOptions
dictionary doesn't expose 'shouldReduceMotion' as a KVC key, so
value(forKey:) raised NSUnknownKeyException, which Foundation
promotes to a fatal exception via -[NSApplication _crashOnException:].
This crashed the app at launch (ContentView.body was rendering
.observeReducedMotion() during first-frame layout).

Replace the KVC call with -perform(_:) on a selector and a plain
NSDictionary -objectForKey: lookup, both of which return nil instead
of throwing on bad keys. UserDefaults fallback
('com.apple.universalaccess reduceMotion') retained for older systems.

Verified: app launches, process stays alive, no new DiagnosticReports
entries after the fix.
* MARKETING_VERSION: 0.0.1 → 0.1.0 across all six Xcode build
  configurations (project.pbxproj lines 427, 462, 484, 505, 525, 545).
* SettingsWindowView.swift version-fallback string updated to match
  the new CFBundleShortVersionString default.
* Verified: bundle reads CFBundleShortVersionString = 0.1.0; app
  launches and stays running.
Resolved conflicts as described in the PR (#11): the merge brings in
main's parallel work (Agent skills, VM test runner, icon refresh,
docs, RuntimeEventBus commit that was already on this branch via a
shared ancestor) while keeping the ui-redesign work intact.

Conflicts and resolutions:

* ColimaStack/AppState.swift (4 hunks)
  - Kept the head-side containerService property and the TableDensity
    init/restore in init.
  - Dropped main's per-container methods (startContainer, stopContainer,
    pauseContainer, resumeContainer, killContainer, removeContainer,
    containerLogs, inspectContainer) and re-implemented them as thin
    wrappers over ContainerService in this commit (see below).

* ColimaStack/Views/MainWindowView.swift (1 hunk)
  - Body is now just WorkspaceChrome + the delete-confirmation alert.
    WorkspaceChrome owns the toolbar, sidebar, search, sheet
    presentation (per the ui-redesign). The .frame, .alert,
    .onChange(of: confirmDelete), and .onReceive(.workspaceChromeRequest
    DeleteConfirmation) are kept and attached to WorkspaceChrome so the
    delete-by-typed-confirmation flow still works.

* ColimaStack/Views/MenuBarView.swift (1 hunk)
  - Kept the head-side profileSection naming (renamed from main's
    profilesMenu in the ui-redesign).

* ColimaStack/Views/WorkspaceScreens.swift (3 hunks)
  - The first conflict (5 lines) was just spacing token adoption;
    kept head.
  - The second conflict was the filter expression. Kept head's
    matchesSearch/sortOrder flow.
  - The third conflict was a 665-line block of main's pre-group-3
    container implementation (ContainerStateFilter,
    ContainerInspectorTab, ContainerControlTable, ContainerToolbar,
    ContainerInspector, etc.) which the body of ContainersScreen
    depends on. Restored that block to keep the body compiling; the
    redundant main-side ImagesScreen and VolumesScreen (which
    conflict with the head-side SwiftUI Table-based versions) were
    dropped so there is exactly one definition of each.

* ColimaStackTests/AppStateBackendAggregationTests.swift (1 hunk)
  - Kept main's @test for backendSnapshotReturnsIssuesWhenOptionalR
    esourceProvidersFail. Loosened the head-renamed 'fileprivate
    static func profile' back to plain 'static' so the other tests
    in the target (e.g. StreamingCommandTests) can still use it.

Other adjustments required by the merge:

* ColimaStack/AppState.swift: added container-lifecycle wrappers
  (startContainer, stopContainer, restartContainer, pauseContainer,
  resumeContainer, killContainer, removeContainer, containerLogs,
  inspectContainer, terminalCommand) that forward to
  ContainerService and a LogStreamBuffer so the legacy
  ContainersScreen body compiles against the redesigned service.

* ColimaStack/Services/ContainerService.swift: added pause, resume,
  kill methods following the same recordCommand pattern as the
  existing start/stop/restart/delete.

* ColimaStack/Views/Terminal/TerminalLogView.swift: added
  LogStreamBuffer.renderPlainText() so AppState.containerLogs can
  snapshot the buffer to a String for the legacy inspect/logs
  sheets.

* scripts/.lint-no-raw-tokens.baseline: 37 -> 46 entries. The
  ContainerControlRow / ContainerInspector / ContainerActionsMenu
  views brought in from main use 'Color(nsColor: ...)' and
  'Image(systemName:)' and 'Text("No ...)' literals that predate
  the design-system migration. Captured in the baseline so the
  guardrail stays green; group 9 will fold them into the design
  system in a follow-up.

Verified:
  - xcodebuild build (Debug, macOS): BUILD SUCCEEDED
  - xcodebuild build-for-testing: TEST BUILD SUCCEEDED
  - scripts/lint-no-raw-tokens.sh: clean (46 baseline, no new)
  - openspec validate --changes: 1 passed, 0 failed
The previous 'AppIcon.imageset/' shipped in 88a0169 had three
structural problems that caused actool to compile it into
near-empty AppIcon.icns (the 68-byte stub the app shipped with
before this commit, plus a stale 2.9MB 1024x1024 PNG that the
file-system-synchronized build phase copied verbatim into
Contents/Resources). The Dock, menu bar, and title bar all
showed the system default Apple icon because nothing was bound
to the AppIcon slot.

Problems with the old AppIcon.imageset:

1. The 25 PNGs in the directory were named
   'ColimaStackIcon-macOS-Default-NxN@sx.png'. actool's
   macOS app-icon slots expect filenames of the form
   'app-icon-NxN.png' / 'app-icon-NxN@sx.png' so it can match
   them to the canonical slots. The Contents.json referenced 10
   of them but they were still under the wrong name.

2. Contents.json only listed 10 of the 25 PNGs as bound. The
   remaining 15 (20x20, 29x29, 38x38, 40x40, 60x60, 64x64, 68x68,
   76x76, 83.5x83.5) were orphaned. actool emitted a warning
   ('The image set AppIcon has 23 unassigned children') and the
   resulting app-icon-NxN.png renditions in Assets.car were all
   330 bytes — empty placeholders.

3. The 1024x1024 reference in Contents.json pointed at a file
   that ALSO existed at the project root
   (ColimaStack/ColimaStackIcon-macOS-Default-1024x1024@1x.png).
   Because the project uses PBXFileSystemSynchronizedRootGroup,
   that loose PNG was bundled into Contents/Resources as a
   side-effect, even though it was the source the asset
   catalog was supposed to consume.

The same commit (88a0169) shipped a parallel
'AppIcon_square.appiconset/' with the correct structure:
10 PNGs named 'app-icon-NxN.png' / 'app-icon-NxN@sx.png' that
match the actool slot names. The Contents.json binds all 10
slots. The 1024x1024 ('app-icon-512@2x.png') is 8-bit sRGB
without alpha, which is what macOS prefers for app icons.

Fix:
  - Replace the broken AppIcon.imageset/ with the working
    AppIcon_square.appiconset/, renamed to AppIcon.appiconset/
    so the existing ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon
    build setting picks it up unchanged.
  - Delete the loose 1024x1024 PNG in the project root so the
    file-system-synchronized build phase stops copying a stray
    2.9MB resource into the bundle.

Verified:
  - xcodebuild clean build (Debug, macOS): BUILD SUCCEEDED
  - Bundle Contents/Resources now contains AppIcon.icns (68KB)
    and Assets.car (1.3MB) only; no stray PNGs.
  - iconutil -c iconset of the compiled AppIcon.icns yields the
    proper ColimaStack icon at every size (16, 32, 128, 256, 512,
    1024).
  - actool warning 'AppIcon has 23 unassigned children' is gone.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant