super-productivity/docs/plans
Miklos 67d1e32ffc
feat(focus-mode): focus screen UX overhaul (#7586)
* feat(focus-mode): focus screen UX overhaul

Major rework of the focus mode timer screens (#7349):

- Shared <focus-clock-face> drives Pomodoro / Flowtime / Countdown /
  Break with a single visual chrome; size tokens scale fluidly via
  clamp() + vmin/vh, no discrete breakpoints.
- Single source of truth: --clock-time-size and --control-offset
  derive from --clock-face-size.
- Countdown: click-to-edit duration, draggable handle on the ring,
  hybrid 5-min/15-min snap with 6h-per-rotation above 1h
  ([useFlexibleIncrement] on input-duration-slider, opt-in for other
  consumers).
- Pomodoro prep inherits the click-to-type input; drag handle hidden
  so dragging the ring can't silently shift the value.
- Pause keeps the selected task on screen (displayedTask falls back
  to the paused task while currentTask is null).
- Notes panel acts as a modal: clock stays put, backdrop dims and
  closes on outside-click.
- Session controls row below the circle (pause / complete / reset
  cycles); buttons fade via shared --revealed-opacity gated on host
  hover + document.hasFocus().
- Break screen mirrors focus-mode-main layout; cycle counter inside
  the circle on both focus and break; back-to-planning unified.
- Flowtime settings dialog: all fields render with proper
  disable/enable, stable dialog width when switching modes.
- arrow_backward -> arrow_back: fixes glitched glyph in repeat-type
  context menus (boards, simple counters, take-a-break, flowtime).

* fix(focus-mode): silence naming-convention lint on formly 'props.disabled'

Formly's expressionProperties path-string keys ('props.disabled') aren't
camelCase and the rule has no requiresQuotes exemption; matches the
existing pattern in src/app/features/issue/common-issue-form-stuff.const.ts.

* test(focus-mode): mock pomodoroConfig signal on FocusModeService

The component's initialization effect now reads
focusModeService.pomodoroConfig() in Pomodoro+Preparation mode, but the
three mocked FocusModeService instances in the spec didn't provide it,
causing TypeError in 47 tests on CI (somehow not surfaced locally).

* test(focus-mode): align specs with unified back-to-planning flow

- focus-mode-break.spec: exitBreakToPlanning -> cancelFocusSession
- focus-mode-session-done.spec: drop obsolete hideFocusOverlay assertion
  (cancelFocusSession now handles both clearing tracking and hiding the
  overlay, matching the production component)
- focus-mode-main.spec: storeSpy gains selectSignal returning a signal,
  needed by the displayedTask paused-task fallback

* fix(focus-mode): restore E2E selector hooks on refactored controls

The shared <focus-clock-face> refactor moved pause/complete buttons out
of the clock face into a new .circle-controls row but didn't carry the
class names forward; 23 E2E specs key off them. Also re-add
.task-title-placeholder on the no-task FAB so prep-state checks find it.

- .pause-resume-btn on pause/resume in focus-mode-main + focus-mode-break
- .complete-session-btn on the done_all button in focus-mode-main
- .task-title-placeholder added to .select-task-cta FAB

* fix(focus-mode): aria-label icon buttons; align break E2Es with new flow

- focus-mode-break: pause/resume/skip/reset icon buttons now carry
  [attr.aria-label] in addition to matTooltip — icon ligature alone
  isn't an accessible name and breaks getByRole locators.
- pomodoro-break-timing-bug-6044.spec: skipButton uses getByRole with
  accessible name rather than hasText (mat-icon ligature is "skip_next",
  not "skip break").
- focus-mode-break.spec: "exit break to planning and change timer mode"
  and "Back to Planning should NOT auto-start next session" now match
  the unified back-to-planning flow — overlay closes on click, user
  re-opens focus mode to change settings or verify prep state.

* fix(focus-mode): address PR #7586 review feedback

Maintainer review (johannesjo):
- Debounce the Pomodoro work-duration write so editing it emits one
  synced config op instead of one per keystroke; flush on session start
  so a value typed inside the debounce window is not lost. (A1)
- Replace the local ::ng-deep restyling of the shared input-duration-slider
  with opt-in [bareRing] and [hideHandle] inputs that own the chrome
  overrides in the slider's own styles; the four other consumers keep the
  default look. (A2)
- Add unit tests for the flexible drag math (_setValueFromRotationFlex):
  the A<->B boundary anchoring at 55/60 min and both +/-180 degree wrap
  branches. (A3)
- Delete the orphaned exitBreakToPlanning action, its
  stopTrackingOnExitBreakToPlanning$ effect, reducer case and specs;
  cancelFocusSession already unsets the current task. (B1)
- Drop the unreferenced CONTINUE_TO_NEXT_SESSION and BREAK_RELAX_MSG
  i18n keys. (B2)
- Collapse the duplicated clock-size clamp() into a single
  --clock-face-size-default token. (B4)

Smaller focus-screen fixes (beerkumquatpome):
- Hide the break task title when "pause tracking during breaks" is on. (C7)
- Commit and close the duration editor on Enter. (C9)
- Rename "Back to Planning" to "Exit focus session". (C11)
- Show Flowtime breaks as a neutral "Break". (C13)

Break-circle vertical alignment (C1) is only partially addressed here
(matched the top reservation); exact alignment is a follow-up.

* refactor(focus-mode): share a layout shell across timer screens and auto-start Flowtime breaks

Extract a presentational focus-mode-layout component (4-row content-projection
skeleton: [fmTop]/[fmTask]/[fmClock]/[fmBottom]) shared by the focus-session and
break screens, so both keep a stable clock baseline across the focus<->break and
prep<->in-progress transitions. focus-mode-main and focus-mode-break now consume
the shell instead of each maintaining their own absolute layout.

Replace the Flowtime "break offer" step with an auto-started break, mirroring
Pomodoro:
- Remove the BreakOffer UI state and the offerFlowtimeBreak action/reducer.
- endFlowtimeSession now dispatches completeFocusSession(isManual:false) +
  startBreak (unsetting the task first when tracking-pause-on-break is on), so
  the break starts automatically and the session is logged exactly once via
  logFocusSession$.

Also reorder the bottom controls (Back to Planning leftmost), restore the
BACK_TO_PLANNING label to "Back to Planning", and drop the now-orphaned
FLOWTIME_BREAK_TITLE / START_BREAK i18n keys.

* test(layout): restore document.activeElement after focus-restoration specs

The LayoutService "Focus restoration" tests override document.activeElement
with Object.defineProperty, which shadows the native (inherited) getter with an
own property on document. The afterEach only removed the mock DOM node, so the
override leaked into later specs: once it ran, document.activeElement was frozen
at the mock element and subsequent .focus() calls could no longer move it.

Depending on Karma's spec order this broke the task.service focusTaskById tests
(#7120), which then saw the stale activeElement instead of the element they
focused. Delete the shadowing own property in afterEach to restore native
behavior.

Repro: ng test --include layout.service.spec.ts --include task.service.spec.ts

* refactor(focus-mode): add interactive tracking widget and polish timer-screen layout

- Replace the read-only task-tracking-info with focus-mode-task-tracking
  (vertical time stack + play/pause), wired through the shared layout shell
- Center the task title and floor the task-row height so the clock baseline
  stays aligned across focus <-> break
- Spacing polish: task-title-row and layout gaps to --s2,
  segmented-button-group padding to 0
- Drop redundant safe-area-bottom padding on the action row (the overlay
  already reserves it for the fixed shell)
- Add "Take a moment to relax" break message and a clock-digit edit affordance

* refactor(focus-mode): share timer/break layout, drop dead tracking toggle

- Extract a shared <focus-mode-layout> skeleton and <focus-mode-task-row> used by both the focus session and the break.

- Remove the in-view tracking play/pause toggle (start/stop stays on the global header button); strip focus-mode-task-tracking to read-only and drop RESUME_TRACKING.

- Pin the mode selector out of flow and center the task·clock·bottom group; equal reserved task/bottom rows keep the clock vertically centered, with the selector kept on top.

- Tighten sizing: horizontal selector segments, settings cog matched to the in-session controls, and a fluid clock clamp.
2026-06-12 11:59:56 +02:00
..
2026-03-22-advanced-onboarding-design.md feat(onboarding): add lightweight onboarding hints 2026-03-22 19:50:24 +01:00
2026-05-21-clean-slate-data-loss-prevention.md feat(focus-mode): focus screen UX overhaul (#7586) 2026-06-12 11:59:56 +02:00
2026-05-28-android-keyboard-resize-jank.md feat(focus-mode): focus screen UX overhaul (#7586) 2026-06-12 11:59:56 +02:00
2026-06-05-project-completion.md feat(project): project completion experience (#8036) 2026-06-08 13:43:38 +02:00