Skip to Content
Design systemPrototype implementation status

Prototype Implementation Status

Maps every screen and sub-view in the ~/Desktop/Oter design prototypes to its implementation in the KMP app, per platform. Companion to Tokens and design-system-conventions.md. Follow-ups: tech-debt/design-system.md, tech-debt/migrations.md.

Two UI stacks (read this first)

StackPlatformsEntry / navhostNotes
SharedAndroid · iOS · Web (WASM)SharedAppNavHost (shared/.../navigation/AppNavHost.kt); hosts androidApp/.../MainActivity.kt, composeApp/iosMain/.../IOSApp.kt, composeApp/wasmJsMain/.../App.ktOne commonMain code path → Android, iOS and Web are identical. The prototype rebuilds live here.
DesktopDesktop (JVM)composeApp/desktopMain/.../ui/navigation/AppNavHost.kt + per-feature *DestinationsReuses the shared Dashboard, Notifications, Work-Jobs-Hub, Statistics and Nutrition prototype rebuilds (the desktop-only older StatisticsScreen/RecipesScreen + destinations were removed). Command / Search / Focus / Onboarding routes are now registered, with first-launch onboarding gating wired into the desktop navhost (reads OnboardingPreferences, routes un-onboarded users into the wizard). The topbar bell now opens a dropdown-panel variant.

Because the shared stack is one code path, the per-view tables below use a single Shared (A·iOS·Web) column plus a Desktop column. “Shared ✅” therefore means done on Android and iOS and Web.

Status legend: ✅ done (prototype-faithful, wired) · 🟡 partial · ◐ present but a different/older screen, or limited wiring · ⬜ not present.

Top-level matrix (all four platforms explicit)

Prototype featureAndroidiOSWebDesktop
Login / Auth
Dashboard
Tasks
Habits
Journal
Books
Calendar
Finance home
Finance sub-screens (stats/category-mapper/product-prices)
Finance sub-screens (receipt/reconcile/importer/prediction)🟡
Workout (+ history)
Study
Settings
Projects
Tags · Timers · Pomodoros · Chat
Statistics
Notifications
Onboarding
Command palette
Search🟡🟡🟡🟡
Focus
Work🟡🟡🟡
Nutrition

Per-platform tally — Android = iOS = Web: 19 ✅ · 2 🟡 (Work, Search) + the transaction-backed finance sub-screens still stubbed (ProductPrices/ReceiptScan/Reconcile/Importer/BalancePrediction). Desktop now wires Command / Search / Focus / Onboarding / WorkTimeLog / WorkStats + the Work role typed-routes (Jobs Hub stays panel-based, which is richer than the mobile drill-downs). Statistics & Nutrition render the shared prototype rebuilds on desktop; FinanceStatistics + CategoryKeywordMapper are now real on the shared stack (were “coming soon” stubs).

Update: TransactionViewModel was migrated to commonMain (shared/.../ui/viewmodels/TransactionViewModel.kt, converted from androidx ViewModel to the plain-class scope pattern, registered single{} in all four platform DI modules; desktop call sites now use koinInject). ProductPrices is now the real screen on the shared stack. ReceiptScan and Reconcile are now functional on iOS — the iOS picker actuals are implemented (UIImagePickerController for receipts, UIDocumentPickerViewController for bank statements; ReceiptImageBitmap.ios.kt now decodes via skiko). Android pickers were already real. Bank-statement picker now real on all four platforms — the desktop/JVM BankStatementFilePicker.jvm.kt actual (which previously returned emptyList(), breaking the Finance Importer on desktop) now uses a multi-select JFileChooser (csv/txt/tsv, read as text to match the Android actual). Android, iOS and Web (wasmJs) pickers were already implemented. FinanceImporter + BalancePrediction are now also wired on the shared navhost (Screen.FinanceImporter / Screen.FinanceBalancePrediction).


Per-view breakdown (done / not done, by platform)

Work — WorkScreen.kt + role screens + services/work/*

Desktop registers only Screen.Work (→ shared Jobs Hub); none of the role routes are in the desktop navhost.

View / sub-screenShared (A·iOS·Web)Desktop
Jobs Hub (cards by type)
New-job dialog
Time Log (day-grouped, filter, billable summary)
Log-session dialog (createSession)
Time Stats (KPIs, billable split, by-activity)
SE — Tickets kanban (6 columns)
SE — Ticket detail + interactive checklist
SE — Overview tab
SE — Team tab
CS — Clients & Projects
CS — Project detail (milestones + billing breakdown)
Store — Overview (launch countdown · P&L summary · profile · areas)
Store — Tasks tab
Store — Inventory tab (on-hand/reorder + low-stock + KPIs)
Store — Suppliers tab
Store — Shifts tab
Store — P&L line-item detail
Inventory stock-events sparkline
Dialogs: new ticket / client / project / milestone / supplier / product / stock-event / shift
Live running-timer (start/stop)

Nutrition — NutritionScreen.kt + services/meals/NutritionViewModel.kt

Desktop now renders the shared NutritionScreen (the desktop navhost calls it directly under Screen.Meals.route), so all Nutrition views are at parity across all four platforms.

View / sub-screenShared (A·iOS·Web)Desktop
Today dashboard (calorie ring + protein/carbs/fat bars)
Meals grouped by tag + consume/undo
Library tab (getAllRecipes + meal-tag filters + day badges)
Week tab (7-day plan, today highlighted)
History tab (getRecipeTracksByDateRange 7/14/30d + vs-goal)
Add-food / edit / delete recipe + skip-with-alternative + barcode-scan dialogs (AddEditRecipeDialog / SkipMealDialog / BarcodeLookupDialog; camera capture still manual-entry)
Per-user macro goals (vs prototype defaults)

Statistics — StatisticsScreen.kt

Desktop now renders the shared StatisticsScreen (the desktop navhost calls it directly under Screen.Statistics.route with the shared StatisticsViewModel), at parity across all four platforms.

View / sub-screenShared (A·iOS·Web)Desktop
KPI strip (+ week-over-week deltas: Tasks/Habit-rate/Workouts)
Weekly-activity stacked bars
Activity-by-area donut
Module completion bars · Top streaks
Range switcher (Day / Week / Month / Year — StatRange + OterSegmentedControlsetRange, /statistics?range=)
Real focus / work time aggregation (Time-spent bars over StudySessions + WorkSessions)

Notifications — NotificationsScreen.kt + services/notifications/*

View / sub-screenShared (A·iOS·Web)Desktop
Inbox page (filter chips, day groups, typed rows)
Mark-read / mark-all-read (POST /notifications/read-all)
Topbar bell + unread badge➖ (mobile nav)
Topbar dropdown-panel variant (prototype)➖ (mobile nav)

Onboarding — OnboardingScreen.kt + preferences/OnboardingPreferences

View / sub-screenShared (A·iOS·Web)Desktop
5-step wizard (Welcome→Areas→Goals→Connect→Done)
First-launch gating (start destination)
Persistence (DataStore / localStorage)
Per-day prompt source

Command / Search / Focus

ScreenShared (A·iOS·Web)Desktop
Command palette (CommandScreen.kt)
Search shell (SearchScreen.kt, quick-access + live /search; results deep-link to task/habit/book/recipe/study/journal detail screens; multi-term AND matching + relevance ranking server-side; a true tsvector/GIN index is still a future optimization)
Focus timer (FocusScreen.kt, ring + presets)

Dashboard — DashboardScreen.kt (wired on all four platforms)

Card / elementShared (A·iOS·Web)Desktop
Greeting + responsive Up-Next + KPI strips
Tasks · Habits · Books cards
Nutrition card (calorie ring + carbs/fat macro bars)
Workout card (+ next-session highlight)
Finance card (+ month-net delta line)
Journal card (+ per-entry mood dots)

Tasks (mobile) — TasksScreen.kt + NewEditTaskMobileSheet.kt + TaskDetailScreen.kt

The shared (mobile) Tasks editor was name+priority only; it’s now at field parity with the desktop dialog. The desktop reuses its own richer NewEditTaskDialog (slot-injected) and is unchanged.

View / capabilityShared (A·iOS·Web)Desktop
Create / edit: name · priority
Note
Due & Scheduled date+time (real CustomDatePicker/CustomTimePicker)
Subtasks (add / toggle / remove)
Reminders (offset presets)
Tags (multi-select from TagsViewModel)
Project (picker from ProjectsViewModel)
Detail screen (status · priority · dates · note · tags · subtasks toggle · reminders + complete/edit/delete)✅ (TaskDetailRoute)✅ (TaskDetailScreen)
AI import · AI tag organize · view modes (incl. Eisenhower matrix) · showInListFrom

Create path carries reminders + tags via the extended TaskRepository/TaskService.addTask (+ TasksViewModel.createTask). ProjectApiService is now registered in the Android/iOS DI modules (was desktop/web-only), which the project picker requires.

Habits (mobile) — HabitsScreen.kt (DefaultAddEditHabitDialog) + HabitDetailScreen.kt

The shared habit dialog was name+frequency only; now full. Desktop reuses its own NewEditHabitDialog (already had reminders + subtasks) and is unchanged — a habit tap still opens edit there (the new onOpenDetail is mobile-only).

View / capabilityShared (A·iOS·Web)Desktop
Create / edit: name · frequency
Note
Time (real CustomTimePicker)
Reminders (offset presets)
Subtasks (add / toggle / remove)
Detail screen (frequency · streak · time · note · subtasks toggle · reminders + check-in/edit/delete)✅ (HabitDetailRoute)✅ (HabitDetailRoute → shared HabitDetailScreen via HabitDetailDestination)
Per-day check-in history (30-day heatmap, GET /habits/{id}/history)
Background reminder service◐ (desktop reminder manager)

Pre-existing features (reused, wired on both stacks)

These were migrated before this program; both stacks register their routes. Sub-views (detail screens, dialogs) are pre-existing and not re-audited here — except Tasks and Habits, whose mobile editors + detail screens were brought to desktop parity (see the two sections above).

FeatureShared routesDesktop routes
Authauthauth (own Login/SignUp)
Tasks (+ detail)tasks + TaskDetailRoute (now on the shared stack too)tasks + TaskDetailRoute
Habits (+ detail)habits + HabitDetailRoute (new on shared)habits
Journaljournaljournal
Booksbooksbooks
Calendarcalendarcalendar
Financefinance + importer/receipt/prices/reconcile/statistics/predictionsame + transaction/budget/scheduled detail routes
Workoutworkout, workout_historysame
Studystudystudy
Settingssettingssettings (own)
Projectsprojectsprojects
Tags · Timers · Pomodoros · Chat · CategoryKeywordMapperall wiredall wired (+ TimerListDetailRoute)

Remaining work (tracked follow-ups)

2026-06-21 — mobile (Android) parity hardening: sweep across foundations + Tasks/Habits/Finance Importer + Timers + Pomodoros + Books to close functional gaps. All four targets (:composeApp:compileAndroidMain, :composeApp:compileKotlinDesktop, :composeApp:compileKotlinWasmJs, :shared:compileKotlinIosArm64) green; :androidApp:assembleDebug produces a working APK.

  • Foundations on Android — feature-gate composables (FeatureGuard.kt, AppUnavailableScreen.kt, ConfigLoadingShimmer.kt) were desktop-only; moved to shared/src/commonMain/.../ui/components/ so the shared navhost can guard routes the same way desktop does. SharedAppNavHost now wraps every gated route in a gated("KEY") helper that calls RequireFeatureScreen with the same feature-key map desktop uses. androidApp/.../activities/MainActivity.kt now collects ThemeViewModel.isDarkTheme + AppLocaleViewModel.languageTag and wraps the content in key(languageTag) { OterTheme(darkTheme = …) } — Android can now honor dark/light + locale changes. AppViewModel + AppIntent + GuidanceViewModel + BalancePredictionViewModel were desktop-only; moved to shared/commonMain (BalancePrediction swapped JVM-only System.currentTimeMillis() / java.util.UUID for kotlin.time.Clock + kotlin.uuid.Uuid under @file:OptIn) and registered in AndroidAppModule.kt.
  • Finance Importer is no longer a stubFinanceImporterScreen in shared/.../ui/screens/SubScreens.kt was a 5-line “coming soon” placeholder. TransactionImportScreen (the real CSV/Lines parse + account picker + preview + type overrides + skip-duplicates flow) moved from composeApp/.../desktopMain/.../ui/screens/ to shared/src/commonMain/.../ui/screens/ (same FQN). Switched the AccountViewModel import inside the screen from the desktop ui.viewmodels package to the shared com.esteban.ruano.oter.viewmodel package; registered the shared AccountViewModel on desktop too so the dual-namespace coexists. The stub SubScreens.kt was deleted.
  • Timer notifications on mobileTimerListDetailMobileDestination hardcoded listNotifications = emptyList() and onGetTimerNotifications = { emptyList() }. The mobile TimersViewModel now owns notifications (sourced from webSocketClient.timerNotifications, mirroring desktop), listNotifications (filtered by the open list id), updateListNotifications(listId), and getTimerNotifications(timerId). The destination collects the flow + invokes the loader in LaunchedEffect(timerListId).
  • Pomodoros VM-backedTimersViewModel constructor gained pomodoroService: PomodoroService (Android, iOS, wasmJs DI all updated from TimersViewModel(get(), get())TimersViewModel(get(), get(), get())) plus pomodoros / pomodorosLoading / pomodorosError flows + loadPomodoros() / removePomodoro(id) / resetPomodorosError(). The Pomodoros route in SharedAppNavHost dropped its inline LaunchedEffect { pomodoroService.getPomodoros() } and uses VM state instead.
  • Tasks + Habits behavior parity on mobileTaskDetailScreen now surfaces a time-tracking history section (uses the shared TimeTrackingHistorySection + TimeTrackingService.getSessionsForTask, refreshes on DomainEvent.TimerSessionStopped) and a three-color due-date indicator (danger / warning / primary by days-until). The mobile TasksScreen overload now wires customRangeStartDate / customRangeEndDate / onCustomRangeChange + a customRangePicker slot (using the shared CustomDatePicker); onReschedule opens the same date picker and preserves the existing HH:mm suffix on dueDateTime; onToggleTaskSubtask flips subtasks inline via tasksViewModel.updateTask. The mobile HabitsScreen overload passes virtualHabitRows after switching HabitsViewModel.getHabits() to getByDateRangeWithMeta (the metadata-bearing endpoint desktop already used), wires onToggleHabitSubtask inline, and accepts an onOpenVirtualFeature map that routes VirtualFeatureKind.WORKOUT/BOOK/STUDY/JOURNAL to the matching screens.
  • BooksService on Android — was missing from AndroidAppModule.kt; BookDetailScreen (reached from Books, Search, or the typed BookDetailRoute) called koinInject<BooksService>() and would have crashed with NoBeanDefFoundException. Now registered next to RecipesService / StudyService / WorkoutService with the same single { services.books.BooksService(get(), PlatformConfig.BASE_URL) } shape desktop uses.

Verdict: the shared (Android/iOS/Web) stack is now at functional parity with desktop for every route both platforms target. Deliberately desktop-only items — the 6 typed Finance CRUD routes (FinanceNewTransactionRoute, etc.) and the Work role-specific routes already on the matrix — remain by design; mobile users reach the same operations via FinanceScreen.kt’s inline showTransactionForm / showScheduledTransactionForm / showSavingsGoalForm / showAccountForm / showDebtForm dialogs and the Work feature drill-downs.

2026-06-11 — parity-closure round (real asymmetries found in a fresh code audit):

  • Principles on Web (fixed)DashboardPrinciplesSection is in commonMain (renders on every platform) but PrincipleService/PrinciplesViewModel were only registered in the Android/iOS/Desktop DI modules; they’re now registered in composeApp/src/wasmJsMain/.../di/WebModules.kt too (was a wasmJs DI crash).
  • Mobile transaction detail (fixed) — tapping a transaction on mobile was a dead no-op (FinanceScreen.kt called the no-op onOpenTransactionDetail and short-circuited the edit fallback). The shared navhost now wires onOpenTransactionDetail → a new shared TransactionDetailRoute → a new shared TransactionDetailDestination that renders the existing commonMain TransactionDetailScreen (via the shared TransactionViewModel) and hosts the edit form (TransactionForm) so Edit works without a dedicated route. Desktop unchanged.
  • Chat tier-gate on mobile (fixed) — mobile now gates Screen.Chat.route on AuthState.Authenticated.tier, matching desktop: FREE users see a “premium feature” upsell instead of the assistant.
  • Mobile timer list-detail (fixed, slot-based)TimerControls + TimerListDetailScreen moved to commonMain; the add/edit-timer dialog is now a slot so desktop keeps its rich TimersDialog (which depends on desktop-only duration utils — never ported) and mobile supplies a compact MobileTimerDialog. The screen is now responsive (stacks on narrow widths). New shared TimerListDetailRoute + TimerListDetailMobileDestination (adapts the shared TimersViewModel’s dashboard state into a TimerPlaybackState; live websocket notifications are desktop-only for now). The shared TimersViewModel gained timerDetailList + a public loadTimerListByID. TimersScreen.onOpenTimerList is now wired on mobile.

Verdict: all four asymmetries from this audit are now closed and compile-verified (Principles-on-Web, mobile transaction detail, mobile timer list-detail, chat tier-gate). The shared & desktop stacks are at functional parity; remaining differences (More menu, feature-flag guards, walkthrough, live timer-notification panel) are intentional or platform-shell.

2026-06-11 — post-parity feature round (four prototype features):

  • Nutrition skip-with-alternativeNutritionViewModel.skipMeal() over the existing trackRecipeSkippedWithAlternative endpoint; a “Skip” action on today’s meal rows opens SkipMealDialog (pick a library recipe, or enter a manual name + macros via AlternativeNutrients).
  • Statistics focus/work timeActivityBucketDTO/ActivityBucket gained focusMinutes/workMinutes; the server StatisticsService now sums durationMinutes per bucket from StudySessions (focus) and WorkSessions (work), rendered as a “Time spent” bar panel in StatisticsScreen.
  • Habit per-day history — new GET /habits/{id}/history?startDate=&endDate= (HabitService.getCheckInHistory over HabitTracksHabitHistoryDTO); client HabitService.getHabitHistory + HabitsViewModel.loadHabitHistory feed a 30-day check-in heatmap on HabitDetailScreen (both stacks).
  • Search detail screens — new shared Book/Recipe/StudyItem/JournalDetailScreens (read-only, fetch by id via the existing services; journal reuses the pre-existing GET /daily-journals/{id} with a new client getJournalById). Routed on both navhosts (*DetailRoute, desktop mirrors with local route copies); onOpenResult now deep-links book/recipe/study/journal results, not just task/habit.
  • Search quality — server SearchService now splits the query into terms and requires all to match (AND) across each type’s columns, then ranks results (exact title → prefix → contains → shorter); a true Postgres tsvector/GIN index remains a future optimization.
  • Nutrition barcode scan — server OpenFoodFactsService proxies the free Open Food Facts API (GET /nutrition/barcode/{code} → clean BarcodeProductDTO, sidestepping client JSON-engine differences); client RecipesService.lookupBarcode + NutritionViewModel.lookupBarcode + a “Scan” action on the Library tab open BarcodeLookupDialog, then prefill AddEditRecipeDialog. (Camera capture is out of scope — the user types/pastes the barcode.)
  • Still open: onboarding per-day prompt (no prototype/code reference — needs a placement + content spec before building), camera-based barcode capture, and the tsvector/GIN search index.

2026-06-11 — parity round closed the last mobile↔desktop asymmetries:

  • Mobile Task power-features — the shared TasksViewModel was enriched (in-memory viewMode/sort/overdue/undated flows + previewTaskImport/importTasksFromText/previewTaskTagAi/applyTaskTagAi wrappers over the commonMain TaskService). The shared TasksScreen mobile overload now wires view modes (incl. the Eisenhower matrix), AI import (TaskImportSheet), and AI tag-organize (AiTagOrganizerContent), reached from a TasksMobileSettingsPanel. (showInListFrom already flowed via NewEditTaskMobileSheet.)
  • Desktop Habit detail — new HabitDetailDestination + composable<HabitDetailRoute> in the desktop navhost render the shared HabitDetailScreen (backed by the shared services.habits.HabitsViewModel, registered as a single in desktop Koin). Tapping a habit now opens the detail screen instead of a no-op self-nav.
  • Search deep-linkingSearchScreen gained onOpenResult; both navhosts route task/habit results to TaskDetailRoute/HabitDetailRoute, other types fall back to the module via the now-public routeForType.
  • Nutrition recipe CRUDNutritionViewModel gained createRecipe/updateRecipe/deleteRecipe over the existing RecipesService endpoints; the Library tab has an Add-recipe action and tappable cards opening AddEditRecipeDialog (name · meal-tag · macros · note · delete). Barcode/image “scan” still pending.
  • Desktop bank-statement pickerBankStatementFilePicker.jvm.kt now uses a real JFileChooser (see above).
  • Statistics range switcher — verified already fully wired (Day/Week/Month/Year); no change needed.

None block the core experience.

Desktop parity is now closed — Command / Search / Focus / Onboarding routes are registered (with first-launch onboarding gating), Statistics & Nutrition render the shared prototype rebuilds, and the Work role-routes (WorkTimeLog, WorkStats, WorkTickets/Clients/Store) are wired. iOS finance pickers and the desktop notifications topbar dropdown are done. The genuinely-remaining work, classified by where the gap actually is:

  • Work depth — largely done on the shared stack:

    • New GET /work/jobs/{jobId}/store-pnl/lines (WorkStoreService.getStorePnLLines over the job’s Transactions) + client WorkStorePnLLine / WorkService.getStorePnLLines(), surfaced as an expandable P&L line-item detail in WorkStoreScreen.
    • Inventory stock-events sparklineWorkService.getStoreStockEvents() + a running-level StockSparkline per product, lazy-loaded by WorkStoreViewModel.loadStockEvents.
    • Create dialogs (shared WorkFormDialogs primitives — WorkFormDialog/WorkTextField/WorkOptionRow): supplier, product, stock-event, shift (store; shift uses a people picker now that WorkStoreViewModel loads getPeople); ticket + person (SE); client + project + milestone (CS). Each backed by new *ViewModel.create* methods over the existing WorkService routes.
    • SE tabs: WorkTicketsScreen now has Overview (KPI strip + by-status breakdown bars), Board (the kanban), and Team (people list + add-person) tabs.
    • CS project detail: project rows in WorkClientsScreen expand to show milestones (billing/delivery breakdown, lazy-loaded) with an add-milestone dialog.
    • Live running-timer (done): WorkSessionTimerRepository is now registered as a single in all four platform DI modules (TimerWebSocketClient + TimerService + TokenStorage were already registered everywhere). WorkTimeLogViewModel gained startTimedSession / stopActiveTimer — it opens a session with actualStart, kicks the server timer via the repo (best-effort), runs a local stopwatch so the UI ticks even if the WebSocket is down, then completeSession on stop. WorkTimeLogScreen shows a Start timer action in the session dialog and a running-timer banner (elapsed hh:mm:ss + Stop).

    Work depth is now complete on the shared stack.

  • Statistics (done): new dedicated GET /statistics?range= endpoint (StatisticsService + statisticsRouting, leaving /dashboard untouched) with real per-bucket activity aggregation over Day/Week/Month/Year and prior-period task/workout deltas; streaks/net-cash/completion come from the dashboard slice. Client StatisticsService + StatisticsViewModel + a range segmented control in StatisticsScreen, registered in all four platform DI modules.

  • Nutrition: per-user macro goals are now implemented — added daily{Calorie,Protein,Carbs,Fat,Fiber,Sugar}Goal columns to UserSettings (exposed via the existing /settings GET/PUT), NutritionViewModel reads them (prototype defaults as fallback) and an in-app Daily macro goals dialog persists edits. Remaining (frontend-feasible): recipe-CRUD / add-food / skip-with-alternative sheets (POST/PATCH/DELETE /nutrition/recipes already exist).

  • Search (done): GET /search aggregates across tasks/habits/journal/books/recipes/study (SearchService + searchRouting); client SearchService + SearchViewModel drive the live results in SearchScreen (debounced), registered in all four platform DI modules. Result deep-linking to the specific record is now implemented for all types (see the 2026-06-11 feature round above); only full-text/FTS ranking remains.

  • Tasks & Habits mobile parity (done): the shared editors went from name+priority / name+frequency to full field parity with desktop — note, due/scheduled date+time (real CustomDatePicker/CustomTimePicker), subtasks, reminders, and (tasks) tags + project pickers — plus dedicated Task/Habit detail screens (TaskDetailRoute / HabitDetailRoute) with complete/check-in, edit and delete. TaskService.addTask was extended to carry reminders+tags on create; ProjectApiService was registered in the Android/iOS DI modules (was desktop/web-only — the omission only surfaced once the mobile editor injected ProjectsViewModel). Remaining task-only desktop extras on mobile: showInListFrom, AI import / AI tag organize, Eisenhower-matrix view modes.

Verification

./gradlew :shared:compileKotlinJvm :composeApp:compileAndroidMain \ :composeApp:compileDevelopmentExecutableKotlinWasmJs \ :composeApp:compileKotlinDesktop :server:compileKotlin bash scripts/check_design_tokens.sh --strict