diff --git a/Sources/Keydoze/MainView.swift b/Sources/Keydoze/MainView.swift index c8ff9cc..3d5e993 100644 --- a/Sources/Keydoze/MainView.swift +++ b/Sources/Keydoze/MainView.swift @@ -184,7 +184,7 @@ struct MainView: View { .contentTransition(reduceMotion ? .identity : .numericText(countsDown: true)) .animation(reduceMotion ? nil : KeydozeMotion.digits, value: model.timeLabel) .accessibilityLabel(model.screen == .arming ? "Preparing" : "Time remaining") - .accessibilityValue("\(Int(ceil(model.remaining))) seconds") + .accessibilityValue(model.timeSpoken) .accessibilityIdentifier("countdown") Text(model.screen == .arming ? "until input pauses" : model.screen == .releasing ? "finishing" : "until input returns") .font(.system(size: 12)).foregroundStyle(.secondary) diff --git a/Sources/Keydoze/SessionModel.swift b/Sources/Keydoze/SessionModel.swift index 71b6402..1b51a87 100644 --- a/Sources/Keydoze/SessionModel.swift +++ b/Sources/Keydoze/SessionModel.swift @@ -98,6 +98,10 @@ final class SessionModel { let seconds = Int(ceil(max(0, remaining))) return String(format: "%d:%02d", seconds / 60, seconds % 60) } + var timeSpoken: String { + let seconds = Int(ceil(max(0, remaining))) + return seconds == 1 ? "1 second" : "\(seconds) seconds" + } func refreshPermission() { permitted = isSimulation ? permitted : AXIsProcessTrusted() diff --git a/Tests/KeydozeAdapterTests/SessionModelTests.swift b/Tests/KeydozeAdapterTests/SessionModelTests.swift index 518875c..d020cf3 100644 --- a/Tests/KeydozeAdapterTests/SessionModelTests.swift +++ b/Tests/KeydozeAdapterTests/SessionModelTests.swift @@ -36,4 +36,19 @@ struct SessionModelTests { #expect(model.screen == .finished(.cancelled)) model.reset() } + + @Test("Spoken time uses the singular for one second") + func spokenTime() { + let model = SessionModel(simulation: true) + model.remaining = 1 + #expect(model.timeSpoken == "1 second") + model.remaining = 0.4 + #expect(model.timeSpoken == "1 second") + model.remaining = 0 + #expect(model.timeSpoken == "0 seconds") + model.remaining = 2 + #expect(model.timeSpoken == "2 seconds") + model.remaining = 48 + #expect(model.timeSpoken == "48 seconds") + } }