refactor: re-skin UI with native iOS components

Replace the Opal-style custom presentation with the bare iOS design
language, keeping the backend, flows, and accessibility identifiers:

- Home: NavigationStack + List; rules now presented as list rows with
  kind icon, block summary, and trailing live status; '+' in the toolbar
- New Rule: plain list of rule types and presets; editor still pushed
  via navigationDestination
- Editor: native Form (DatePicker rows, day circles with summary in the
  section header, toggle rows with footers, stepper rows); create
  commits with a prominent 'Add Rule' button replacing Hold to Commit;
  edit uses toolbar Done plus red Disable/Delete rows
- Detail: sheet with inline title + status caption, LabeledContent
  rows; Edit Rule pushes the editor natively
- Default color scheme: drop forced dark mode and custom tint; system
  appearance and accent throughout
- Delete Theme, HoldToCommitButton, RuleCardView and custom chrome
- Use concrete Color.primary/secondary in button row labels (the
  hierarchical styles resolve against the button tint)
- Tests: 93 passing; only delta is holdToCommitButton press →
  commitRuleButton tap in 5 call sites
- Spec: add §6 mapping the original presentation to the native one
This commit is contained in:
2026-06-12 15:14:01 -04:00
parent fd1f5d758f
commit 2d609043f3
13 changed files with 525 additions and 914 deletions

View File

@@ -23,7 +23,7 @@ final class RuleCreationUITests: XCTestCase {
XCTAssertTrue(app.staticTexts["During this time"].exists)
// Hold to commit saves the rule and returns home.
app.buttons["holdToCommitButton"].waitToAppear().press(forDuration: 2.0)
app.buttons["commitRuleButton"].waitToAppear().tap()
app.buttons["ruleCard-In the Zone"].waitToAppear()
XCTAssertFalse(app.element("emptyRulesCard").exists)
}
@@ -35,7 +35,7 @@ final class RuleCreationUITests: XCTestCase {
app.buttons["preset-work-time"].waitToAppear().tap()
XCTAssertEqual(app.staticTexts["ruleEditorTitle"].waitToAppear().label, "Work Time")
app.buttons["holdToCommitButton"].waitToAppear().press(forDuration: 2.0)
app.buttons["commitRuleButton"].waitToAppear().tap()
app.buttons["ruleCard-Work Time"].waitToAppear()
}
@@ -53,7 +53,7 @@ final class RuleCreationUITests: XCTestCase {
app.buttons["OK"].tap()
XCTAssertEqual(app.staticTexts["ruleEditorTitle"].label, "My Focus")
app.buttons["holdToCommitButton"].waitToAppear().press(forDuration: 2.0)
app.buttons["commitRuleButton"].waitToAppear().tap()
app.buttons["ruleCard-My Focus"].waitToAppear()
}
@@ -81,7 +81,7 @@ final class RuleCreationUITests: XCTestCase {
app.steppers["dailyLimitStepper"].buttons["Increment"].tap()
XCTAssertEqual(app.staticTexts["dailyLimitStepperValue"].label, "60m")
app.buttons["holdToCommitButton"].waitToAppear().press(forDuration: 2.0)
app.buttons["commitRuleButton"].waitToAppear().tap()
app.buttons["ruleCard-Time Keeper"].waitToAppear()
}
@@ -91,7 +91,7 @@ final class RuleCreationUITests: XCTestCase {
app.buttons["ruleKind-schedule"].waitToAppear().tap()
app.switches["adultContentToggle"].waitToAppear().tap()
app.buttons["holdToCommitButton"].waitToAppear().press(forDuration: 2.0)
app.buttons["commitRuleButton"].waitToAppear().tap()
app.buttons["ruleCard-In the Zone"].waitToAppear().tap()
let row = app.element("detailRow-Adult websites").waitToAppear()