feat(design-system): introduce DesignSystem module and port primitive components#11
Open
RealDyllon wants to merge 18 commits into
Open
feat(design-system): introduce DesignSystem module and port primitive components#11RealDyllon wants to merge 18 commits into
RealDyllon wants to merge 18 commits into
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.
What's in this PR
Tokens (new module
ColimaStack/DesignSystem/Tokens/)display,title1,title2,title3,body,caption,code,monobacked byFont.text/{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.
control6,card12,pill999.reduce-motion observer that collapses non-essential animation.
Primitives (new module
ColimaStack/DesignSystem/Primitives/)SectionCard,MetricTile,StatusBanner,KeyValueGridEmptyStateView(renamed fromSurfaceStateView; newEmptyStateKind: noResults, noData, loading, error, unavailable,disabled)
StateDot,IconBadgeToolbarActionButton,PrimaryButton,DestructiveButtonWorkspaceTone(moved out ofWorkspaceComponents.swiftso itis 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.*.iconControl(16pt),.iconRow(20pt),.iconHero(48pt)
Kubernetes each get a distinct symbol; one symbol per concept.
Other
Assets.xcassets(placeholder;Icon.brandfalls back to theshippingbox.fillSF Symbol untilthe asset is finalized).
scripts/lint-no-raw-tokens.shfails the buildif a new
Image(systemName:),Color(...), or.font(.system(size:))literal is introduced outside
ColimaStack/DesignSystem/. A baselineof 22 pre-existing violations is captured for incremental cleanup.
ColimaStackTests/DesignSystemContrastTests.swiftasserts 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.
reduce-motion observer via
.designSystemColorResolution()and.observeReducedMotion().EmptyStateView(kind: ...)andDesignSystem.Spacingtokens as the reference implementation.Backwards compatibility
SurfaceStateViewandStatusDotare typealiased to the newtypes; the legacy
(title:message:symbol:tone:)initializer onEmptyStateViewinfers the kind from the tone so existing callsites continue to compile while subsequent PRs migrate them.
Build status
xcodebuild build(Debug, macOS) — greenxcodebuild build-for-testing— greenscripts/lint-no-raw-tokens.sh— clean (22 baseline violations,no new ones)
Out of scope (subsequent PRs)
The remaining 113 tasks of the change are grouped into 11 follow-up
workstreams:
Table-based data tablesText("No …"))Each group will be a separate PR with a narrow scope so the diff is
reviewable.
Refs