apps/mobile. It builds on the design-system
stack that started with #6857:
- #6857: one sheet design and one loader;
- #6860: profile edit and timeline;
- #6861: Assistant settings IA;
- #6862: Mira Live redesign;
- #6863: drawers, toggles and dialogs.
Decisions
Information architecture
The mockup proposes five tabs (Home, Apps, Mira, Inbox, You) plus a search action. Today the dock has Home, Apps and the centre Assistant (Mira) entry. Profile and Notifications are reachable from the shell.- Search. The dock’s trailing search action opens the existing shell search field. It does not add a second search field in the body.
- Back. Back stays on the dock (decision 1). The task detail mockup’s top-left chevron is dropped. Its trailing check, share and overflow actions become shell chrome actions.
-
Workspace switcher. Chips are All, then pinned workspaces, then
More. More opens a popover menu with:
- the remaining workspaces (initial tile and name);
- Find Workspace (opens the existing picker search);
- Manage Workspaces;
- New Workspace.
-
Tablet and desktop. These widths keep the leading
ShellNavigationRailwith the same five destinations. The dock and rail must not remount a visited root tab.
core/router/routes.dart, the shell
chrome config titles, and the dock regression tests.
Component inventory
Each component either exists incore/ui or is new and owned by the listed
phase. Rows marked “exists” must be reused.
Shared rules for all phases:
- Colours come from
AppTokens. - No emojis and no hard-coded hues. The mockup’s green, orange and red swipe
fills map to
AppTone.success,warninganddestructive. - Every row action has a semantics action.
- Every interactive target is at least 44pt.
Appearance style system
Appearance already persists the mode, theme, accent, background, intensity, softness and card opacity on the device. This plan adds an Interface group. Its values are applied by the theme layer, not by per-widget platform forks.
Design constraints:
- One extension, read through
AppTokens. Components read the active style fromAppTokens.of(context). A widget never branches onPlatform.isIOS. The only remaining platform checks are system-owned behaviours such as Android predictive Back and iOS edge-swipe interception. - Persisted per device. The settings use the same storage as the existing
appearance payload: a versioned JSON field in
AppearanceSettings, decoded field by field so unknown values fall back. They are not synced to the account. - Contrast.
appearance_contrast_test.dartgains cases for every Style and Radius combination, in light and dark. - Preview. The Appearance page shows a live sample row: a task row with a swipe hint, a toggle and a sheet header.
- Reset to default restores every option.
Pomodoro and Focus Time
Focus Time is time tracking, not a separate store. A Pomodoro focus interval is a normaltime_tracking_sessions row, and Focus Time statistics are
existing tracker statistics filtered to those rows.
Proposed feature set
- Configurable focus, short-break and long-break lengths, plus the number of
focus intervals before a long break. These are the existing
PomodoroSettingsfields and the control foundation’s config contract: minutes 1–180, cycle 1–24. - Auto-start breaks and auto-start focus (existing booleans).
- Link to a task. Starting focus from a task row (the context menu’s
“Start focus”, phase 6) or from task detail sets
task_idon the session. - Notifications. At each phase end, the device schedules a local
notification (
flutter_local_notifications, already a dependency) and cancels it when the phase ends early. Cross-device delivery when every client is closed needs the server deadline described below. - Pause, resume and skip. Pause uses the existing break model
(
time_tracking_breaks). Skip ends the current phase. - Focus Time stats. Today, last week and the current month totals, a seven-day bar chart, the completed-interval count and a streak, on the You tab and in the Timer statistics.
- Web parity. The web time tracker shows the same running phase and remaining time. This waits for shared control activation.
Data model (reuse first)
Phase deadlines that must fire while all clients are closed belong to a
Cloudflare scheduler (Durable Object alarm per actor and workspace), not
Vercel Cron, pg_cron or Edge Functions. That work must have:
- bounded retries;
- idempotent phase transitions keyed by the control revision;
- a tested durable stop fence when the control returns to
off.
Data and storage
- Workspace pins. Pins are stored per user in
user_configsunder a mobile pins key: an ordered JSON array of workspace IDs. That is no schema change, but check the key against anyuser_configsid allowlist. Reads drop IDs the user can no longer access. Pins are not shared with other users. - Appearance. The new options are device-local (see above).
- Swipe and menu actions reuse the board cubit mutations and add no storage.
Phased PR breakdown
Every phase is a native stack layer above #6863 (stack #6883). If a phase does not depend on the stack, it goes on its own branch frommain. Each
phase needs:
- English and Vietnamese ARB strings and
flutter gen-l10n; - widget tests at 320x568 with 2x text in light and dark;
- Thanh
flutter analyzeand focused tests; - exact-commit CI.
Open questions
- Should Inbox replace the shell notifications bell entirely once it is a tab, or keep the bell on Home?
- Should Focus Time count only Pomodoro-tagged sessions, or all tracked time linked to tasks?
- Should Style be offered per device only (planned), or also follow the account to a new device?
- Should the Kanban card long-press also move to the context menu, matching List view?