Skip to main content

max / goingson

UX audit sweep: Tier 1-4 + Tier 6 + design-system charter Result of the Phase 0-7 frontend audit (124 findings, 6 surfaces). Audit reports under docs/ux-audit/; charter at docs/design-system.md; lint guards at scripts/lint-frontend.sh enforce 7 rules. Charter rules now in code: - No state-by-color-alone — sync indicator paired with label; swipe peek-labels carry semantic colors + text. - URL-mirrored filter state — new js/query-state.js helper wired into tasks, emails, contacts; reload / deep-link restores filters. - Bulk-undo everywhere — bulkActionWithUndo used by 7 call sites across tasks, emails, contacts. - No native browser dialogs — showPromptDialog / showConfirmDialog replace prompt / confirm / alert; lint rule blocks regressions. Tier 1 (safety nets): - send-with-delay undo toast (queueSend, already landed with compose stage 1) generalised to all destructive paths via showUndoToast. - Recurring-event scope confirm before edit / delete in events.js. - Attachment 25 MB cap (compose stages 1-2). Tier 2 (trust-state surfaces): - About / version line in Settings. - Pagination + total-count chips on emails / tasks / contacts. - Sync indicator now carries a label, not just a dot color. - Cloud Sync section shows "Signed in as {email} ({username})" via new sync_account_info Tauri command (synckit-client side already supports GET /api/v1/sync/account). Tier 3 (mobile correctness): - Hide kanban on touch devices. - Swipe peek-labels with text + semantic colors. - Bottom-nav long-press → pill picker. - iOS meta tags + theme-color sync from active theme. - window.prompt eradicated (showPromptDialog). Tier 4 (URL-mirrored filter state): - js/query-state.js helper. - Tasks, emails, contacts: filter / search / tag state read from URL on load, written on change; selection clears on filter change so bulk actions can't target hidden rows. Tier 5 polish (the larger Tier 5 features — event snooze + reminders, contact sub-collection edit — landed in their own commits): - All-day events: form checkbox + 00:00 → next-day-00:00 normaliser. - Monthly review: tasks_completed_top (up to 6) for the Accomplished card. - Weekly review: optional weekStart param so the reviewer can navigate to any week, not just the current one. parse_week_start snaps any date to its Monday. upcoming_events block removed (was unused). Tier 6 (architecture): - Side-drawer task detail with J/K keyboard nav + row-active marker. - Settings overlay with Esc / backdrop dismiss. - (Compose unification stages 1-2 already landed; stages 3-6 staged per docs/ux-audit/compose-migration.md.) Mechanical: - Inline-style sweep across ~30 JS files: style="..." → CSS classes, matching the lint rules. styles.css gains the new class library. - Regenerated styles.min.css and Tauri ACL manifest.
Co-Authored-By
Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Author: Max J. <87768334+MaxJMath@users.noreply.github.com> · 2026-05-20 23:52 UTC
Commit: 47a0908b232b501a6a0b16b517ccd5b743f134c4
Parent: 916d094
68 files changed, +6116 insertions, -1505 deletions
@@ -19,17 +19,20 @@
19 19 - [ ] Windows: `cargo tauri build` → verify `.msi`, code-sign with Authenticode, test on Windows
20 20 - [ ] Linux: AppImage (x86_64 + aarch64)
21 21
22 - ### Mobile (iOS TestFlight — keep in scope, near completion)
22 + ### Mobile (iOS TestFlight Internal only — external + Android deferred post-launch)
23 23 See `todo_mobile.md` for full breakdown. Launch requires:
24 24 - [x] iOS TestFlight upload working — v0.3.3 delivered 2026-05-17 (icon fix; v0.3.1 had Tauri default logo)
25 - - [ ] Android emulator smoke (`cargo tauri android dev`) + CRUD verified on mobile WebView
26 - - [ ] Physical device testing (iOS + Android) — run mobile P0+P2 checklist in `docs/human_testing.md`
27 - - [ ] Safe area insets across device models, keyboard-doesn't-obscure-inputs, background/foreground transitions
28 - - [ ] iOS internal testing: invite phone Apple ID into ASC Users and Access (Developer role), add to TestFlight Internal group, install + smoke-test on v0.3.3
25 + - [x] iOS internal testing: phone Apple ID invited + added to TestFlight Internal group, build installed on own device (2026-05-19)
26 + - [ ] Physical device testing (iOS) — run mobile P0+P2 checklist in `docs/human_testing.md`
27 + - [ ] Safe area insets across device models, keyboard-doesn't-obscure-inputs, background/foreground transitions — verify on installed build
29 28 - [ ] Resolve Missing Compliance prompt on v0.3.3 build (encryption answer — HTTPS-only exempt). **Plist key added 2026-05-18** in `gen/apple/goingson-desktop_iOS/Info.plist` (`ITSAppUsesNonExemptEncryption = false`); future uploads won't re-prompt. The currently-stuck v0.3.3 build still needs the answer set once via App Store Connect UI (TestFlight → build → Encryption: "Uses only exempt encryption").
30 - - [ ] Add Privacy Policy page to GoingsOn project on MNW (URL: `https://makenot.work/p/goingson#section-privacy-policy`)
31 - - [ ] iOS External Testing: add build, fill Beta App Information, submit for Beta App Review, enable Public Link
32 - - [ ] Android release: Google Play Developer account ($25), release AAB, Play Console listing, submit
29 +
30 + Post-launch (out of launch scope):
31 + - [ ] (post-launch) Android emulator smoke (`cargo tauri android dev`) + CRUD verified on mobile WebView
32 + - [ ] (post-launch) Physical device testing (Android)
33 + - [ ] (post-launch) Add Privacy Policy page to GoingsOn project on MNW (URL: `https://makenot.work/p/goingson#section-privacy-policy`)
34 + - [ ] (post-launch) iOS External Testing: add build, fill Beta App Information, submit for Beta App Review, enable Public Link
35 + - [ ] (post-launch) Android release: Google Play Developer account ($25), release AAB, Play Console listing, submit
33 36
34 37 Explicitly **out of launch scope** (defer until after public launch):
35 38 - All of Sprint: Backup & Export (current backup works for primary data types)
@@ -41,6 +44,47 @@
41 44
42 45 ---
43 46
47 + ## UX audit sweep (phased, post-launch)
48 +
49 + Audit complete 2026-05-19/20. **124 findings** across 6 surfaces, **4 charter rules** promoted, lint guards enforce 7 rules clean. See `docs/ux-audit/phase-7.md` for the roll-up; per-phase reports at `docs/ux-audit/phase-{0..7}.md`; charter at `docs/design-system.md`. Audit phases archived to `todo_done.md`.
50 +
51 + ### Audit backlog execution (Tier 1–6, per `phase-7.md` Part C)
52 +
53 + - [x] **Tier 1 — safety nets** (5/5). Bulk-undo across tasks/emails/contacts; selection clears on filter change; send-with-delay (undo-send); attachment-size warning at 25 MB; recurring-event scope confirm.
54 + - [x] **Tier 2 — trust-state surfaces** (4/4). Sync indicator label + state-by-color-alone fix; About / version in Settings; pagination + total counts; Account section now surfaces "Signed in as {email} ({username})" inside Cloud Sync via `sync_account_info`.
55 + - [x] **Tier 3 — mobile correctness** (5/5). Hide Kanban on touch; swipe peek-labels; bottom-nav long-press → pill picker; iOS meta tags + theme-color sync; `window.prompt` killed.
56 + - [x] **Tier 4 — URL-mirrored filter state** (1/1). `js/query-state.js` helper; wired into tasks, emails, contacts. Closes Phase 1 #9 / Phase 2 #10 / Phase 3 #8 / Phase 5 search-persistence in one helper.
57 + - [x] **Tier 5 — feature gaps** (4/4). All-day events, event snooze, contact sub-collection edit, and event reminders all wired end-to-end.
58 + - [~] **Tier 6 — architecture** (2.17/3). Side-drawer task detail (`#task-detail-drawer`, J/K keyboard nav, row-active marker) and settings overlay (`#settings-overlay`, Esc-to-close, backdrop dismiss) both landed 2026-05-20. Compose unification staged out in `docs/ux-audit/compose-migration.md`; **stage 1 of 6 landed 2026-05-20** — new `js/compose-form.js` owns the SendEmailInput payload shape, attachment caps, and validation. Both modal (`emails.js`) and desktop window (`compose.html`) call into it; the `to` vs `toAddress` double-emit shim is gone. Stages 2-6 are independently shippable; one per release during soft launch.
59 +
60 + **Backend trio + frontend wirings landed 2026-05-20** (migration 049, +9 Tauri commands, 4 new tests, ~3 frontend surfaces):
61 + - [x] Event snooze: migration adds `snoozed_until` + rebuilt sync triggers. `snooze_event` / `unsnooze_event` / `list_snoozed_events` commands. `EventResponse` exposes `isSnoozed` + `snoozedUntil`. Event detail modal has Snooze / Unsnooze toggle + "Snoozed until …" line. `snooze.js` generalised to three item types via `ITEM_LABEL` / `apiFor()` / `reloadFor()` helpers.
62 + - [x] Contact sub-collection edit: `update_contact_{email,phone,social_handle,custom_field}` commands. `SUB_COLLECTIONS` map extended with `updateCommand` + `prefill`; `buildSubCollectionFormHtml(type, cid, editingId?)` routes add vs edit. Edit-contact modal rows now carry inline Edit + Remove buttons. Pre-existing reload race (`load()` not awaited before `open*()`) fixed inline.
63 + - [x] MNW account info: new `GET /api/v1/sync/account` (server) + `get_account_info()` (synckit-client) + `sync_account_info` Tauri command. Surfaced inline in Cloud Sync (chose not to add a separate sidebar item — the data only exists because of sync).
64 +
65 + **Event reminders landed 2026-05-20** (migration 050, scheduler in `notifications.rs`, 12 new tests):
66 + - [x] Schema: `reminder_offsets_seconds TEXT` JSON column on events + rebuilt sync triggers + `apply.rs` column list.
67 + - [x] Scheduler: `check_event_reminders` runs each 60s tick; tracks `(event_id, offset)` pairs; bootstrap-on-first-tick suppresses backfill spam after app restart; skips snoozed events.
68 + - [x] UI: `REMINDER_PRESETS` (At time / 5m / 15m / 30m / 1h / 1d before) checkbox group in new/edit event forms; "Reminders: …" line on event detail modal.
69 + - [x] `sanitize_reminder_offsets` strips negatives, dedupes, sorts, caps at 8.
70 + - Known limitations (post-launch follow-ups, not blockers): recurring events fire reminders only against the template's anchor `start_time`, not virtual instances; reminder fire state is in-memory (close the app around a fire time and you miss it).
71 +
72 + **Tier 5 follow-up worth doing if you ship more broadly:**
73 + - [ ] Snoozed-events list view (the `list_snoozed_events` endpoint exists; nothing renders it yet — parity with the snoozed-tasks list).
74 +
75 + ### Charter rules now enforced in code (not just docs)
76 + 1. No state-by-color-alone (sync indicator paired with label; swipe peek-labels carry semantic colors + text)
77 + 2. URL-mirrored filter state (tasks/emails/contacts)
78 + 3. Bulk-undo everywhere (7 call sites)
79 + 4. No native browser dialogs (`scripts/lint-frontend.sh` rule #7; `showPromptDialog` helper)
80 +
81 + ### Deferred deliberately
82 + - [ ] **Step 4 — `renderRow` primitive — DEFERRED.** Re-evaluate if a future phase identifies parallel-markup as the real friction; until then, per-kind row renderers are fine (they all consume canonical CSS classes). See `docs/ux-audit/remediation-plan.md`.
83 +
84 + Out of scope for the audit: smart-feature behavior (forbidden by Apps/CLAUDE.md), full WCAG audit, performance. Recommendations are post-launch work; nothing here blocks ship.
85 +
86 + ---
87 +
44 88 ## Sprint: Backup & Export
45 89
46 90 Backup system has both coverage and performance problems (Run 24 cross-cutting concern).
@@ -236,3 +236,34 @@
236 236 - [x] Attachment sending — MIME multipart via lettre, file picker in compose window + modal, multiple files
237 237 - [x] Labels / folders — local labels (migration 043), folder/label filter dropdowns, move to folder (IMAP + local), label editing
238 238 - [x] Notifications — per-account opt-in (migration 044), off by default, fires from auto-sync scheduler when new emails saved
239 +
240 + ---
241 +
242 + ## UX Audit sweep — Phase 0 through Phase 7 (2026-05-19/20)
243 +
244 + Multi-phase audit of the frontend. All artifacts under `docs/ux-audit/`; charter at `docs/design-system.md`; lint guards at `scripts/lint-frontend.sh`.
245 +
246 + **Phase 0 + remediation:**
247 + - [x] Phase 0 — design-system conformance audit. `docs/ux-audit/phase-0.md` + `docs/design-system.md`.
248 + - [x] Consolidation pre-plan. `docs/ux-audit/remediation-plan.md` — 10 steps.
249 + - [x] Step 1 — Toast styling moved into CSS.
250 + - [x] Step 2 — Updater/keycaps/var-fallback hex removed.
251 + - [x] Step 3 — `renderFormField` primitive + form-modal/settings/settings-sync/email-accounts migrated.
252 + - [x] Step 5 — Empty-state consolidation.
253 + - [x] Step 6 — Layout utilities + style-attr sweep; lint reports clean.
254 + - [x] Step 7 — `compose.html` embedded styles reconciled.
255 + - [x] Step 8 — Theme coverage sweep. `docs/ux-audit/theme-coverage.md`.
256 + - [x] Step 9 — `window.confirm()` calls removed.
257 + - [x] Step 10 — Lint guards at `scripts/lint-frontend.sh` (7 rules).
258 + - Step 4 (`renderRow`) deliberately deferred — kept active in todo.md.
259 +
260 + **Surface audits (124 total findings across 6 surfaces):**
261 + - [x] Phase 1 — Shell & navigation. 17 findings (3 critical).
262 + - [x] Phase 2 — Tasks surface. 18 findings (3 critical).
263 + - [x] Phase 3 — Compose & email. 21 findings (3 critical).
264 + - [x] Phase 4 — Events & calendar. 23 findings (4 critical).
265 + - [x] Phase 5 — Projects, contacts, settings. 23 findings (4 critical).
266 + - [x] Phase 6 — Mobile parity sweep. 22 findings (3 critical). Verdict: mobile is ~70 % parallel implementation, ~30 % CSS restyle.
267 + - [x] Phase 7 — Cross-cutting + theme conformance + roll-up. 4 meta-patterns promoted to charter rules (state-by-color-alone, URL-mirrored filter state, bulk-undo, native-dialogs-forbidden). Landed in this commit: `GoingsOn.ui.showPromptDialog`, `bulkTag` off `window.prompt`, `no-native-dialogs` lint rule, cross-cutting rules section in `design-system.md`.
268 +
269 + Recommendations are post-launch; nothing in the audit blocks ship. Tier 1–6 ship order in `phase-7.md` Part C.
@@ -1,17 +1,17 @@
1 1 # GoingsOn - Mobile Port
2 2
3 - Done: Phases 1-7 (CSS, touch, navigation, views, build config, tab bar, distribution setup); touch-native UX rework (paint, reschedule, week carousel); polish (tap targets, scroll lock, modal cutoffs, icon, provider hints, segmented events view). Active: None. Next: Internal TestFlight install + smoke test on a real device.
3 + Done: Phases 1-7 (CSS, touch, navigation, views, build config, tab bar, distribution setup); touch-native UX rework (paint, reschedule, week carousel); polish (tap targets, scroll lock, modal cutoffs, icon, provider hints, segmented events view); iOS TestFlight internal install on phone (2026-05-19). Active: None. External TestFlight + Android deferred post-launch.
4 4
5 5 ---
6 6
7 7 ## Remaining
8 8
9 9 ### Build & Test
10 - - [ ] Test on Android emulator (`cargo tauri android dev`)
11 - - [ ] All CRUD operations verified on mobile WebView
10 + - [ ] (post-launch) Test on Android emulator (`cargo tauri android dev`)
11 + - [ ] All CRUD operations verified on mobile WebView (iOS — on installed TestFlight build)
12 12
13 13 ### Polish
14 - - [ ] Physical device testing (iOS + Android)
14 + - [ ] Physical device testing (iOS); (post-launch) Android
15 15 - [x] Safe area insets on various device models (notched, non-notched) — 2026-05-16: timer-widget bottom now uses `calc(52px + env(safe-area-inset-bottom))`; body + fixed UI respect `safe-area-inset-left/right` for landscape. Verify on device.
16 16 - [x] Virtual scroller performance on mobile — 2026-05-16: short-circuit re-renders when visible range unchanged; removed redundant O(N) walks per scroll tick. Verify on device with long lists.
17 17 - [x] VoiceOver / TalkBack accessibility — 2026-05-16: `mobile-more-btn` gets `aria-expanded`/`aria-haspopup`/`aria-controls`; popover is `role="menu"` with `aria-hidden` toggle; action sheet now traps focus + restores on close + closes on Escape. Spot-check with VoiceOver.
@@ -60,31 +60,31 @@
60 60 - [x] Answer encryption export-compliance prompt — set via API to `usesNonExemptEncryption: false` (exempt; standard HTTPS/system crypto)
61 61
62 62 #### Internal testing (immediate, for own device)
63 - - [ ] App Store Connect → Users and Access → invite phone's Apple ID (role: Developer)
64 - - [ ] Accept invitation from phone's Apple ID
65 - - [ ] TestFlight → Internal Testing → "+" group → add phone Apple ID → add build
66 - - [ ] Install TestFlight on phone, accept invite, install build, smoke-test
63 + - [x] App Store Connect → Users and Access → invite phone's Apple ID (role: Developer)
64 + - [x] Accept invitation from phone's Apple ID
65 + - [x] TestFlight → Internal Testing → "+" group → add phone Apple ID → add build
66 + - [x] Install TestFlight on phone, accept invite, install build, smoke-test
67 67
68 - #### External testing (public link, slower first time)
68 + #### External testing (public link, slower first time) — POST-LAUNCH
69 69 - [x] Privacy policy drafted at `docs/privacy-policy.md`
70 - - [ ] Add Privacy Policy page to GoingsOn project on MNW (dashboard → Settings → Pages); URL will be `https://makenot.work/p/goingson#section-privacy-policy`. Pages feature shipped in MNW v0.5.17.
71 - - [ ] TestFlight → External Testing → "+" group → add v0.3.1 build
72 - - [ ] Fill in Beta App Information: description, feedback email, test notes ("no login required for core features")
73 - - [ ] Submit for Beta App Review (first time: ~24-48h; subsequent builds: minutes via automated screening)
74 - - [ ] Once approved, enable Public Link
75 - - [ ] Push at least one build per quarter — TestFlight builds expire after 90 days
70 + - [ ] (post-launch) Add Privacy Policy page to GoingsOn project on MNW (dashboard → Settings → Pages); URL will be `https://makenot.work/p/goingson#section-privacy-policy`. Pages feature shipped in MNW v0.5.17.
71 + - [ ] (post-launch) TestFlight → External Testing → "+" group → add v0.3.1 build
72 + - [ ] (post-launch) Fill in Beta App Information: description, feedback email, test notes ("no login required for core features")
73 + - [ ] (post-launch) Submit for Beta App Review (first time: ~24-48h; subsequent builds: minutes via automated screening)
74 + - [ ] (post-launch) Once approved, enable Public Link
75 + - [ ] (post-launch) Push at least one build per quarter — TestFlight builds expire after 90 days
76 76
77 - ### Android
77 + ### Android — POST-LAUNCH
78 78 ```bash
79 79 ./dist/release-android.sh # release AAB
80 80 ./dist/release-android.sh --apk # release APK
81 81 ./dist/release-android.sh --debug # debug APK
82 82 ```
83 - - [ ] Google Play Developer account ($25 one-time)
84 - - [ ] Release AAB build + test
85 - - [ ] Play Console listing (title, description, screenshots)
86 - - [ ] Test on emulator + physical device
87 - - [ ] Play Store submission
83 + - [ ] (post-launch) Google Play Developer account ($25 one-time)
84 + - [ ] (post-launch) Release AAB build + test
85 + - [ ] (post-launch) Play Console listing (title, description, screenshots)
86 + - [ ] (post-launch) Test on emulator + physical device
87 + - [ ] (post-launch) Play Store submission
88 88
89 89 ---
90 90
@@ -3,9 +3,17 @@
3 3 <head>
4 4 <meta charset="UTF-8">
5 5 <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
6 + <!-- Phase 7 Tier 3 #13 — iOS / PWA chrome integration.
7 + theme-color is updated by js/themes.js when the theme changes so the
8 + iOS status bar and Android browser chrome match the active surface. -->
9 + <meta name="theme-color" content="#E0E4FA" id="meta-theme-color">
10 + <meta name="apple-mobile-web-app-capable" content="yes">
11 + <meta name="apple-mobile-web-app-status-bar-style" content="default">
12 + <meta name="apple-mobile-web-app-title" content="GoingsOn">
6 13 <title>GoingsOn</title>
7 14 <link rel="icon" type="image/svg+xml" href="favicon.svg">
8 15 <link rel="icon" type="image/png" sizes="32x32" href="../icons/32x32.png">
16 + <link rel="apple-touch-icon" href="../icons/128x128.png">
9 17 <link rel="stylesheet" href="css/styles.css">
10 18 </head>
11 19 <body>
@@ -30,6 +38,7 @@
30 38 <div class="header-actions">
31 39 <button class="sync-indicator" id="sync-indicator" onclick="GoingsOn.settings.openCloudSync()" title="Cloud Sync" aria-label="Cloud sync status" style="display:none;">
32 40 <span class="sync-dot" id="sync-dot"></span>
41 + <span class="sync-label" id="sync-label"></span>
33 42 </button>
34 43 <button class="settings-btn" onclick="GoingsOn.search.open()" title="Search (Cmd+K)" aria-label="Search">Search <kbd class="kbd-hint">&#8984;K</kbd></button>
35 44 <button class="settings-btn" onclick="GoingsOn.settings.open()" title="Settings" aria-label="Open settings">Settings</button>
@@ -106,6 +115,7 @@
106 115 Waiting Only
107 116 </label>
108 117 <div class="filter-actions">
118 + <span id="task-count" class="filter-count" aria-live="polite"></span>
109 119 <button class="btn btn-link" onclick="GoingsOn.tasks.clearFilters()">Clear filters</button>
110 120 </div>
111 121 </div>
@@ -171,7 +181,7 @@
171 181 </div>
172 182 <button class="btn btn-secondary" id="edit-project-btn" onclick="GoingsOn.projects.editCurrent()">Edit Project</button>
173 183 </div>
174 - <p id="project-dashboard-description" style="color: var(--text-secondary); margin-bottom: 1rem;"></p>
184 + <p id="project-dashboard-description" class="project-dashboard-desc"></p>
175 185 <div id="project-milestones-section" class="milestones-section"></div>
176 186 <div class="project-dashboard-grid">
177 187 <div class="dashboard-column">
@@ -207,7 +217,7 @@
207 217 <button class="btn btn-sm btn-primary" id="project-attach-btn">+ Attach</button>
208 218 </div>
209 219 <div id="project-attachments-list" class="dashboard-list">
210 - <div class="empty-dashboard-list">No attachments</div>
220 + <div class="empty-state empty-state--dashboard">No attachments</div>
211 221 </div>
212 222 </div>
213 223 </div>
@@ -251,13 +261,9 @@
251 261 <input type="date" id="day-plan-date" class="day-plan-date-picker" onchange="GoingsOn.dayPlan.onDatePickerChange()">
252 262 <span id="day-plan-date-display" class="day-plan-date-display"></span>
253 263 </div>
254 - <div class="view-toggle" id="day-plan-review-toggle" data-plan-pane="day-plan-pane" data-review-pane="day-review-pane">
255 - <button class="view-toggle-btn active" data-mode="plan" onclick="GoingsOn.planReviewToggle.setMode('day', 'plan')" title="Schedule your day">Plan</button>
256 - <button class="view-toggle-btn" data-mode="review" onclick="GoingsOn.planReviewToggle.setMode('day', 'review')" title="Reflect on your day">Review</button>
257 - </div>
264 + <span id="day-review-status-badge" class="review-status hidden"></span>
258 265 </div>
259 - <!-- Plan pane -->
260 - <div id="day-plan-pane" class="day-plan-content">
266 + <div class="day-plan-content">
261 267 <div class="day-plan-main" id="day-plan-container">
262 268 <p class="timeline-hint">Click and drag across time slots to schedule blocks</p>
263 269 <div class="timeline-container" id="timeline-container">
@@ -280,13 +286,13 @@
280 286 <div id="unscheduled-tasks" class="sidebar-task-list" role="list">
281 287 <!-- Virtual scroller renders items here -->
282 288 </div>
289 + <div id="day-accomplished-inline" class="day-accomplished-inline"></div>
283 290 </div>
284 291 </div>
285 - <!-- Review pane -->
286 - <div id="day-review-pane" class="hidden">
287 - <div id="day-review-content" class="day-review-inline">
288 - <p style="color: var(--text-secondary);">Loading...</p>
289 - </div>
292 + <div class="finish-review-bar">
293 + <button class="btn btn-primary finish-review-btn" id="day-finish-review-btn" onclick="GoingsOn.dayPlanSchedule.openFinishReviewModal()">
294 + Finish &amp; Review
295 + </button>
290 296 </div>
291 297 </div>
292 298
@@ -294,10 +300,6 @@
294 300 <div id="weekly-review-view" class="subview hidden" role="tabpanel" aria-labelledby="weekly-review-tab">
295 301 <div class="page-header">
296 302 <h2 class="page-title">Weekly Review</h2>
297 - <div class="view-toggle" id="week-plan-review-toggle" data-plan-pane="week-plan-pane" data-review-pane="week-review-pane">
298 - <button class="view-toggle-btn active" data-mode="plan" onclick="GoingsOn.planReviewToggle.setMode('week', 'plan')" title="Schedule your week">Plan</button>
299 - <button class="view-toggle-btn" data-mode="review" onclick="GoingsOn.planReviewToggle.setMode('week', 'review')" title="Reflect on your week">Review</button>
300 - </div>
301 303 </div>
302 304 <div id="weekly-review-content" class="weekly-review-content">
303 305 <div class="skeleton-shimmer" aria-label="Loading weekly review">
@@ -317,10 +319,7 @@
317 319 <button class="btn btn-secondary" onclick="GoingsOn.monthlyReview.nextMonth()" title="Next month">&rarr;</button>
318 320 <span id="monthly-review-month-display" class="monthly-review-month-display"></span>
319 321 </div>
320 - <div class="view-toggle" id="month-plan-review-toggle" data-plan-pane="month-plan-pane" data-review-pane="month-review-pane">
321 - <button class="view-toggle-btn active" data-mode="plan" onclick="GoingsOn.planReviewToggle.setMode('month', 'plan')" title="Set goals for the month">Plan</button>
322 - <button class="view-toggle-btn" data-mode="review" onclick="GoingsOn.planReviewToggle.setMode('month', 'review')" title="Reflect on your month">Review</button>
323 - </div>
322 + <span id="month-review-status-badge" class="review-status hidden"></span>
324 323 </div>
325 324 <div id="monthly-review-content" class="monthly-review-content">
326 325 <div class="skeleton-shimmer" aria-label="Loading monthly review">
@@ -432,6 +431,7 @@
432 431 <select class="form-select" id="email-label-filter" onchange="GoingsOn.emails.filterByLabel(this.value)" style="width: auto; min-width: 120px;">
433 432 <option value="">All labels</option>
434 433 </select>
434 + <span id="email-count" class="filter-count" aria-live="polite"></span>
435 435 </div>
436 436 <div id="email-bulk-actions" class="bulk-actions-bar hidden" role="toolbar" aria-label="Bulk email actions">
437 437 <span id="email-bulk-count" class="bulk-count">0 selected</span>
@@ -506,10 +506,17 @@
506 506 </div><!-- /messages-view -->
507 507
508 508 <!-- Settings View (standalone, not a tab group) -->
509 - <div id="settings-view" class="view hidden">
509 + </main>
510 + </div><!-- /app-body -->
511 +
512 + <!-- Settings Overlay (Phase 7 Tier 6 — settings as modeless drawer) -->
513 + <div id="settings-overlay" class="settings-overlay hidden" role="dialog" aria-modal="true" aria-labelledby="settings-overlay-title" aria-hidden="true">
514 + <div class="settings-overlay-backdrop" onclick="GoingsOn.settings.goBack()" aria-hidden="true"></div>
515 + <div id="settings-view" class="view settings-overlay-card">
510 516 <div class="settings-page-layout">
511 517 <nav class="settings-sidebar">
512 - <button class="settings-back-btn" onclick="GoingsOn.settings.goBack()" aria-label="Go back">&larr; Back</button>
518 + <button class="settings-back-btn" onclick="GoingsOn.settings.goBack()" aria-label="Close settings">&larr; Close</button>
519 + <h2 class="visually-hidden" id="settings-overlay-title">Settings</h2>
513 520 <div class="settings-nav-items">
514 521 <button class="settings-nav-item active" data-section="appearance" onclick="GoingsOn.settings.showSection('appearance')">Appearance</button>
515 522 <button class="settings-nav-item" data-section="notifications" onclick="GoingsOn.settings.showSection('notifications')">Notifications</button>
@@ -517,16 +524,15 @@
517 524 <button class="settings-nav-item" data-section="plugins" onclick="GoingsOn.settings.showSection('plugins')">Plugins</button>
518 525 <button class="settings-nav-item" data-section="sync" onclick="GoingsOn.settings.showSection('sync')">Cloud Sync</button>
519 526 <button class="settings-nav-item" data-section="data" onclick="GoingsOn.settings.showSection('data')">Import &amp; Export</button>
527 + <button class="settings-nav-item" data-section="about" onclick="GoingsOn.settings.showSection('about')">About</button>
520 528 </div>
521 529 </nav>
522 530 <div class="settings-content" id="settings-content">
523 531 <!-- Populated by JS -->
524 532 </div>
525 533 </div>
526 - </div><!-- /settings-view -->
527 -
528 - </main>
529 - </div><!-- /app-body -->
534 + </div>
535 + </div><!-- /settings-overlay -->
530 536
531 537 <!-- Modal -->
532 538 <div id="modal-overlay" class="modal-overlay hidden" role="dialog" aria-modal="true" aria-labelledby="modal-title">
@@ -545,6 +551,21 @@
545 551 <!-- Content populated dynamically by JS -->
546 552 </div>
547 553
554 + <!-- Task Detail Drawer (Phase 7 Tier 6 — side-drawer task detail) -->
555 + <aside id="task-detail-drawer" class="task-drawer" role="dialog" aria-modal="false" aria-labelledby="task-drawer-title" aria-hidden="true">
556 + <div class="task-drawer-header">
557 + <button class="btn btn-sm btn-secondary task-drawer-close" onclick="GoingsOn.taskOverview.close()" title="Close (Esc)" aria-label="Close task detail">x</button>
558 + <h2 class="task-drawer-title" id="task-drawer-title">Task</h2>
559 + <div class="task-drawer-actions" id="task-drawer-actions"></div>
560 + </div>
561 + <div class="task-drawer-content" id="task-drawer-content">
562 + <div class="skeleton-shimmer" aria-label="Loading task detail">
563 + <div class="skeleton-row"><div class="skeleton-lines"><div class="skeleton-line long"></div><div class="skeleton-line medium"></div></div></div>
564 + <div class="skeleton-row"><div class="skeleton-lines"><div class="skeleton-line medium"></div><div class="skeleton-line short"></div></div></div>
565 + </div>
566 + </div>
567 + </aside>
568 +
548 569 <!-- Mobile Bottom Tab Bar (hidden on desktop) -->
549 570 <nav id="mobile-tab-bar" class="mobile-tab-bar" role="tablist" aria-label="Mobile navigation">
550 571 <button class="mobile-tab active" data-view="work" role="tab" aria-selected="true">
@@ -575,11 +596,6 @@
575 596 </div>
576 597 </div>
577 598
578 - <!-- Third-party libraries -->
579 - <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"
580 - integrity="sha512-BNaRQnYJYiPSqHHDb58B0yaPfCu+Wgds8Gp/gU33kqBtgNS4tSPHuGibyoeqMV/TJlSKda6FXzoEyYGjTReTEA=="
581 - crossorigin="anonymous"
582 - referrerpolicy="no-referrer"></script>
583 599
584 600 <!-- Namespace (must load first) -->
585 601 <script src="js/goingson.js"></script>
@@ -592,6 +608,7 @@
592 608 <script src="js/components.js"></script>
593 609 <script src="js/form-modal.js"></script>
594 610 <script src="js/themes.js"></script>
611 + <script src="js/query-state.js"></script>
595 612
596 613 <!-- Utility Managers -->
597 614 <script src="js/cache.js"></script>
@@ -622,6 +639,7 @@
622 639 <script src="js/attachments.js"></script>
623 640 <script src="js/autocomplete.js"></script>
624 641 <script src="js/address-highlight.js"></script>
642 + <script src="js/compose-form.js"></script>
625 643 <script src="js/tasks.js"></script>
626 644 <script src="js/task-overview.js"></script>
627 645 <script src="js/events.js"></script>
@@ -265,6 +265,7 @@
265 265 commands::sync_setup_encryption_new,
266 266 commands::sync_setup_encryption_existing,
267 267 commands::sync_update_settings,
268 + commands::sync_account_info,
268 269 commands::sync_subscription_status,
269 270 commands::sync_subscribe,
270 271 commands::list_themes,
@@ -585,6 +585,7 @@
585 585 commands::sync_setup_encryption_new,
586 586 commands::sync_setup_encryption_existing,
587 587 commands::sync_update_settings,
588 + commands::sync_account_info,
588 589 commands::sync_subscription_status,
589 590 commands::sync_subscribe,
590 591 // Themes