summaryrefslogtreecommitdiff
path: root/Shared
Commit message (Collapse)AuthorAgeFilesLines
* fix(a11y): rebalance the light palette so hues surviveChristian Cleberg2026-07-208-19/+409
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Reported as "colors seem muted and hard to see on light mode", and correct. The light palette optimised contrast and produced mud: #7A5600 reads olive rather than amber, #146C2E bottle-dark rather than green. Contrast passed while the UI got harder to read, because hue identity is what distinguishes warning from critical at a glance. Two causes, both fixed. Every foreground was required to clear 4.5:1 against its own 16% badge tint — the harshest surface it ever sits on — which pushed each colour about 20% darker than the common case needed. Most of what is actually on screen is plain text on a card, with far more headroom. The fill is now decoupled from the foreground: AppStatusTone carries a foreground and a surface authored independently, with matching …Surface colorsets, so a foreground no longer has to survive a wash of itself. Every status foreground is now fully saturated. And warning was yellow. Yellow cannot stay yellow at a lightness low enough to pass 4.5:1 on white — it becomes olive. That is colorimetric, not a tuning problem. Warning is now orange: #AD5100 light, #FF9F0A dark. New light values: positive #008035, warning #AD5100, critical #CC0700. Worst-case ratios 4.54–6.76 across page, card, and surface in both schemes. Audit findings are unchanged — light 21, dark 18 — so the vividness costs nothing. Also picks up a literal .blue missed in phase 1: DomainDiffItem's low-severity change colour, which the phase 1 sweep did not cover because its pattern listed only cyan/yellow/green/red/orange/pink.
* feat(a11y): unlock light mode and add appearance preference (#21 phase 2)Christian Cleberg2026-07-201-0/+78
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Removes the 16 scattered .preferredColorScheme(.dark) calls and the one .toolbarColorScheme, and applies appearance in exactly one place — the WindowGroup in DomainDigApp. Re-applying per view is what let the lock spread across eight files unnoticed until light mode was unreachable. Adds AppAppearance (System / Light / Dark) in @AppStorage, exposed under Settings > Display next to Density. Honouring the system setting and offering an override is one key, and it keeps the deliberate dark aesthetic reachable for anyone who wants it. Also replaces .secondary with AppTextSecondary across 191 sites. iOS's own secondaryLabel is 3.29:1 on a light card — below AA — which never showed while the app was locked to dark, where the same colour reads 6.32:1. Unlocking light mode is precisely what exposed it, so it belongs here rather than in a later phase: without it, light mode would ship with body text under 4.5:1 app-wide. Dark mode reports 18 findings, unchanged from phase 1 — no regression from unlocking. Light mode reports 21. The three extra are iOS-rendered Section headers (TIER, PREFERENCES, SERVICES) using the system's grey; overriding system header styling across every section to gain ~0.3:1 on decorative labels is a poor trade and is left alone. Two long-standing Settings contrast findings are now explained. They are the last rows of a section sitting under the translucent tab bar, so the audit measures text against a blended background — confirmed by screenshot, present in dark mode since phase 0, and standard iOS scroll-under behaviour rather than a defect.
* feat(a11y): semantic colour system (#21 phase 1)Christian Cleberg2026-07-2012-0/+784
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Replaces every hard-coded colour with semantic asset colours that adapt to light, dark, and Increase Contrast. Dark mode stays locked, so this is a pure refactor: the audit reports the same findings before and after. The accent is now blue rather than cyan, per the tech/DNS theme. Why custom values rather than the system palette: every system colour fails WCAG AA in light mode. Measured on white — systemYellow 1.51:1, systemOrange 2.20:1, systemGreen 2.22:1, systemCyan 2.54:1, systemRed 3.55:1. All of them pass in dark mode, which is why the dark-locked app looked fine, and why unlocking light mode was never a matter of deleting .preferredColorScheme(.dark). Every new value clears 4.5:1 as text on its page, its card, and its own 16% badge tint — the way AppStatusBadgeView actually draws it. The accent needed splitting in two. As text on a dark background it must be light; as a fill behind a white label it must be dark. #4DA3FF reads 8.00:1 as text on black but 2.63:1 behind white text, so StatusInfo / AccentColor cover the foreground role and AccentFill covers .borderedProminent. AppOnAccent is the label colour for a solid fill and flips by scheme. Colours live in Shared/Colors.xcassets rather than the app catalog: the Shared folder is already a synchronized group in all three targets, so the widget and share extension pick the palette up with no project-file surgery. AccentColor stays in the app catalog as the global tint — and is now actually defined, having been an empty colorset that silently left system controls rendering in stock blue while custom chrome used cyan. Two deliberate visual changes: orange folds into StatusWarning and pink into StatusCritical. They encoded the same severity as the colours they now share, and both sites also carry a text label. Audit findings drop 15 to 14, and one of the originals turned out to be a phantom: the Inspect contrast failure was the Run button in its disabled state, which WCAG 1.4.3 exempts. testInspectScreen now types a domain first so the audit measures an enabled control. Findings also carry the offending element now, so the remaining clipped-text items name themselves ("No Portfolio Yet", "Search domains") instead of being anonymous.
* fix: StoreKit config path and Swift 6 concurrency warningsChristian Cleberg2026-07-201-2/+5
| | | | | | | | | | | | | | | | | | The StoreKitConfigurationFileReference added in #11 used one '../' too many, resolving outside the repository. Xcode resolves it relative to the .xcodeproj's xcshareddata directory, so two levels reaches the repo root. SweepActivityAttributes is now explicitly nonisolated. The app target sets SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor while the widget target does not, so a type shared by both inferred a main-actor-isolated ActivityAttributes conformance that ActivityKit cannot use from its concurrent contexts. LocalAPIService's logger closures captured self strongly while their inner Tasks declared [weak self]. The weak capture is now on the outer closure and bound before the Task, so the concurrently-executing closure references an immutable strong local rather than the weak capture.
* Implement v4.6.0: sweep Live Activity, share extension, iPad split view, ↵Christian Cleberg2026-07-172-0/+53
| | | | | | | | | | | | | | | | actionable notifications - Sweep Live Activity: SweepActivityAttributes (Shared/), lock-screen + Dynamic Island UI in the widget extension, driven by SweepActivityController wired into the batch pipeline (begin/update/end); NSSupportsLiveActivities in Info.plist. - Share extension (DomainDigShareExtension): accepts a web URL from the share sheet, extracts the host, and hands it to the app via the App Group inbox (DomainDigShareInbox); the app consumes it on activation and inspects it. - iPad layout: RootTabView uses NavigationSplitView in the regular size class and the tab bar in compact. - Actionable notifications: per-domain threadIdentifier grouping, a Re-inspect action, and tap routing into the domain detail via the intent router. - Bump version to 4.6.0 (build 38) across app, widget, and share targets.
* Complete v4.5.0: Run Sweep intent, detail deep link, and portfolio widgetChristian Cleberg2026-07-172-0/+153
Finishes the v4.5.0 "Home Screen & Shortcuts reach" scope that the tag shipped partially: - Add RunSweepIntent (opens the app and runs refreshAllTrackedDomains via the in-process router) and expose it in DomainDigShortcuts. - Extend the domaindig:// scheme with `sweep` and `domain` (detail) actions; route .detail to present TrackedDomainDetailView and .sweep to refresh the watchlist. Move DomainDigDeepLink into Shared/ so the widget can build links. - Add a WidgetKit extension (DomainDigWidgetExtension) with small/medium/large Portfolio widgets showing health counts, per-domain status, and certificate countdowns; tapping a domain deep-links into its detail. - Share portfolio state via an App Group (group.net.cleberg.DomainDig): the app writes a DomainDigWidgetData snapshot on launch/foreground and on watchlist changes and reloads timelines; the widget reads the same store.