| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
feat(app): add System / Light / Dark appearance setting on mobile (#19378) * feat(app): add system-aware light and dark appearance * fix(app): restore light appearance contrast on v1 surfaces * fix(app): provide appearance in visual audit harness Failure-Class: none * test(app): support light mode in visual audit harness * fix(app): correct light-mode color contrast Failure-Class: none * fix(app): improve remaining light-mode contrast Failure-Class: none * fix(app): use palette tokens for light transcript bubbles Replace the two hardcoded light-mode bubble colours with surface2 and a pale alpha-blend of the speaker colour, so the mobile UX contract's colour-literal ratchet stays flat. Failure-Class: none Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 2 天前 | |
fix: format 40 files on main + strengthen pre-commit hook docs (#8656) * fix: format 40 files that drifted from pre-commit standards 38 Dart files + 2 Python files had formatting violations on main. Root causes: dart format version drift since the March 2026 mass-format, and commits merged without the pre-commit hook installed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(AGENTS.md): strengthen pre-commit hook guidance - Expand Formatting section: list both hooks (pre-commit + pre-push), all 6 language formatters, and verification command - Add explicit warning in Git section that skipping hooks lets violations land on main - Reference `make setup` consistently as the single install command Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(AGENTS.md): use raw ln commands instead of make setup Replace make setup references with explicit ln -s commands for pre-commit and pre-push hooks. The Makefile hasn't been reviewed yet. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(AGENTS.md): drop pre-push hook, keep pre-commit only Pre-push script (scripts/pre-push) is unreviewed. Only reference the pre-commit hook which has been in the repo since the beginning. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(AGENTS.md): restore make setup line, keep pre-commit alongside it The make setup rule was already on main — don't remove unrelated rules in a formatting PR. Add the pre-commit ln -s command as a second entry. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> | 2 个月前 | |
fix(mobile): ask more users for a store review after value moments (#19707) * fix(mobile): ask more users for a store review after value moments Failure-Class: none * docs(mobile): note store review prompt timing * fix(mobile): contain review suppression storage errors | 10 小时前 | |
feat(app): mobile UX consistency pass: shared primitives, contract and audit fixes (#18436) * feat(app/ui): add OmiColors tokens and stop using colorScheme.primary as a page background Add lib/ui/omi_tokens.dart: OmiColors (surfaces, text, neutral accent, status), OmiType ramp, OmiRadius, OmiSpacing, OmiMotion (honours Reduce Motion) and OmiHaptics. 53 sites read Theme.of(context).colorScheme.primary as a page or app bar background because the theme's primary was black. They now name OmiColors.surface0 (same black), so the theme can get a real accent. In the four files over the line-count threshold the explicit colour is dropped instead; the theme default is the same black. AppStyles and ResponsiveHelper get doc comments pointing at the tokens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): one theme and shared controls for navigation, buttons, sheets and page states Theme (lib/ui/omi_theme.dart, used by main.dart and the visual_audit harness so they cannot drift): a real dark ColorScheme with the white accent as primary, black scaffold, app bar theme (black, flat, centred 17pt semibold title), white progress indicators (the unstyled spinners were black on black), floating surface2 snack bars, surface1 dialogs and sheets, neutral switches. Material 2 stays on. MaterialApp.builder now syncs Intl.defaultLocale to the resolved locale. Components under lib/ui/components (barrel lib/ui/ui.dart): OmiBackButton / OmiCloseButton (platform glyph, localized label, 44pt target, circled variants), OmiIconButton (required label, tooltip), OmiButton (primary/secondary/destructive/tertiary, regular/compact, async loading that always clears), OmiSpinner, OmiLoadingState / OmiEmptyState / OmiErrorState, showOmiSheet and OmiSheetScaffold, OmiSwitch, OmiSettingsGroup / OmiSettingsRow / OmiSectionHeader, OmiSearchField, and omiPageRoute for code that needs a Route object. HeaderCircleButton is now a wrapper over OmiIconButton.filled and AnimatedLoadingButton over OmiButton (fixes the spinner that never cleared after a throw, and the 40pt target). find_device's two unstyled ElevatedButtons keep their black fill explicitly now that primary is white. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/settings): move data privacy, Asana and ClickUp settings onto the shared controls Proving set for the new primitives. Data & Privacy uses OmiBackButton, a section header and an OmiSettingsGroup of app rows, OmiEmptyState, and drops its accent tint for neutral tokens. Asana and ClickUp show an app bar with a back button and OmiLoadingState during their first load (the page used to be an invisible spinner with no way out), OmiSpinner for section loads, and tokens for their colours. The shared IntegrationSettingsPage header uses OmiBackButton and a labelled refresh OmiIconButton, and its disconnect control is an OmiButton.destructive. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): one adaptive confirmation system (showOmiConfirm/showOmiAlert) getDialog, ConfirmationDialog, OmiConfirmDialog and AppDialog become thin adapters over OmiAlertDialog: Cancel always shown and always closes, real CupertinoDialogActions on iOS (destructive red, safe choice bold), localized defaults, a destructive flag, and AppDialog.show no longer drops singleButton and onCancel. Non-page raw AlertDialogs in providers/widgets migrated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): OmiFeedback toasts and OmiClipboard One floating neutral toast at a time with contract timings (confirm 1.5 s, info 4 s, error 8 s with close, undo 5 s with no close, resolving whether the reader undid). Snackbars with actions no longer persist forever. The home shell registers its tab-bar clearance. AppSnackbar's ~180 call sites route through it. OmiClipboard refuses empty text and skips its toast on Android 13+ where the system already confirms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): OmiDateFormat, OmiDuration and shared speaker names Dates follow the app locale and the 24-hour setting; durations have one offset/compact/long vocabulary (the playback bar no longer drops hours). SpeakerNames is the single source for speaker labels in app and export: You / person / Omi / dense Speaker N, localized in copied transcripts. Legacy helpers delegate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): PromptQueue for startup and background prompts Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(checks): INV-UI-3 mobile UX contract ratchet Per-rule, per-file no-increase ratchet over hand-rolled back glyphs, PageRouteBuilder, raw sheets/dialogs/snackbars/clipboard, date patterns, colour/font-size/radius literals, raw spinners, '...' and hardcoded Text copy under app/lib, with an allow-comment escape hatch and a test proving each rule fires. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(app): mobile UX contract The numbered rules and the one primitive for each: leaving a surface, sheets, controls, destructive actions (D5), dialogs, feedback timing, copying, dates and durations, speakers, tokens, words (D3), settings (D4), page states, prompts, permissions and accessibility, plus the D1/D2/D6 navigation calls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/ui): dense speaker numbering on live and saved transcripts Both transcript renderers pass the conversation's segments to speakerLabel so Speaker N is numbered per conversation. Drops the dead duplicate formatChatTimestamp in widgets/transcript.dart, makes Material OmiAlertDialog scrollable, and points primitive docs at the contract's sections. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/l10n): contract words sweep Task(s) replaces Action Item(s)/To-Do in every locale (D3); Omi capitalised; '…' instead of '...'; search placeholders drop the ellipsis; Try Again, Not Now, OK, Title Case buttons. New keys: filterBySpeaker, notNow, discard, keepEditing, discardChangesTitle, discardChangesMessage. Translates the nine live-capture keys that only existed in English and removes five orphaned locale keys, so scripts/l10n.py check passes. The upgrade alert offers Not Now (postpone, not ignore-forever) and Update, honours barrierDismissible, and loses the emoji. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app/ui): export feedback, format and prompt primitives from the ui barrel; token colours Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app/ui): follow the Title Case clear-search label Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/l10n): strings for the conversation detail pass Link Event sheet, calendar event actions, search result navigation, playback labels, the share confirmation, the Tasks tab counts and empty state, and the speaker identify label, with translations for every locale. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): conversation detail on the shared primitives - D2: no body-wide swipe to another conversation and no PageRouteBuilder; Transcript/Summary(/Tasks) swipe as tabs, the Tasks tab exists only when there are tasks. - Header: OmiBackButton.circled plus same-size OmiIconButton circles (star, share, more) and a compact Ask Omi button that pushes Chat (D1). - Share asks before making a private conversation public and reverts it when the share sheet reports a dismissal. - Copy Conversation ID and Test Prompt only in developer mode; copies go through OmiClipboard, toasts through OmiFeedback. - Link Event and calendar event sheets on showOmiSheet with OmiDateFormat, l10n and full attendee names; visibility, summary template, name speaker and edit segment sheets on the shared shell. - Audio download sheet pops with its own context, offers Cancel and blocks back with PopScope. - Title: single line, Done, Untitled placeholder, saves on blur with an announced outcome; detail date/duration use OmiDateFormat/OmiDuration.compact (same rule as the list). - Tasks tab: tapping a task opens the shared task editor; labelled count pills; Tasks wording (D3); OmiEmptyState. - Edit segment sheet: speaker from SpeakerNames (You for the owner), Save and Cancel, discard guard. - Extract calendar sheets, search bar, transcript tab and tasks tab; delete dead ActionItemsListWidget, GetSheetTitle, GetDevToolsOptions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): keep INV-UI-3 allow comments on the flagged line Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): transcript bubbles and playback bar on the shared primitives Transcript: one SpeakerNames resolver per build (dense numbering, live people), no transcription-provider name, the start time as an OmiDuration offset, a labelled 44pt play button, labelled speaker targets, neutral user bubble and search highlight (INV-UI-1), localized 'translated by Omi', and getLastTranscript numbers speakers across the whole conversation. Playback bar: neutral surfaces instead of purple, a labelled 32pt play/pause in a 44pt target, a labelled stop button, OmiSpinner, OmiFeedback errors, the summary pill ellipsizes by layout instead of a hand-made '...', and the summary template sheet opens through showSummarizedAppsSheet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/onboarding): shared permission row, real progress, back and resume - OmiPermissionRow (lib/ui/components): reason, Allow -> system prompt, Allowed, Open Settings when the system will not ask again. One OnboardingPermissionsPanel drives both the first-run step and the returning-user interstitial; Continue never fires prompts and 'Always' location is no longer asked during first run. - Onboarding wrapper: placeholder pages removed, progress dots count the six real steps with a spoken 'Step N of M', PopScope makes system back step back, the last reached step is saved per account and resumed. - Consent step offers 'Use a Different Account' (sign out, back to start). - Acquisition survey is skippable; language step saves with a busy button and a Try Again error; picker selects on tap. - Steps share OnboardingCard (content scrolls, footer stays; min-height, no fixed text heights) and OmiButton; completion screen localized, purple gone. - Voice introduction: mic refusal shows the permission row; 'Skip Question' vs 'Skip'; device tutorial exits by a trailing close X. - Permission reasons rewritten, platform-neutral Location Services copy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/conversations): one delete path with Undo, row menu, bulk actions, recaps page - Conversation delete (list swipe, row menu, detail page) goes through one path: confirm unless opted out ("Don't ask again" is allowed because Undo backs it), then an Undo toast backed by the provider's pending-delete window (now 6 s, longer than the 5 s toast; the toast commits early when it closes) (D5). - Long-press on a conversation row opens one context menu: Open, Star/Unstar, Move to Folder, Share, Select, Delete. The selection bar gains Move and Delete (always confirmed) beside Merge. - Daily Recaps is its own pushed page with a back button; Home's View All pushes it and the Conversations tab never switches into a recap mode. Pull-to-refresh reloads recaps and a failed load offers Try Again. Recap counts are labelled. - One date filter for list and search, shown as a removable chip; the search field is seeded from the provider and uses OmiSearchField. - Dates and lengths via OmiDateFormat / OmiDuration (Today gets a header, the New badge sits beside the time, WAL/recording offsets keep their hours). - Discarded rows read "Discarded · 12s" with a plain-text snippet; blank titles read "Untitled Conversation". - Shared capture state table (capture_state_labels.dart) for the in-progress card; the processing card shows its real start time. - Local recording swipe always confirms. Sheets, dialogs, toasts, spinners and back buttons in conversations/, sync and recap pages move onto lib/ui. - Offline Sync is the one name for the device-storage sync screen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app/conversations): cover Undo-backed delete, row menu, recaps page and capture labels - Provider: the pending-delete window outlasts the Undo toast; undo restores, commit sends once, a regroup during the window keeps the row hidden. - Widgets: Undo restores / timeout commits / bulk toast counts; long-press menu entries and Select; untitled and discarded row titles; Today header; the date filter chip; Daily Recaps page back button, labelled counts, Try Again and pull-to-refresh. - Unit: the shared capture state table (an audio interruption is Paused). - The Home preview's row menu leaves out Select (no selection bar there); the in-progress card's photo count is labelled; last raw spinner and "..." in the capture card go. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/pairing): pairing recovery, one Bluetooth prompt, shared sheets - Find devices: a missing Bluetooth/location permission goes through the one BluetoothReadiness guidance prompt (Open Settings); the local 'Enable Bluetooth' dialogs are gone. - Nothing found: 'Scan Again', 'How to Pair' (connection guide), then 'Contact Support' instead of a lone mailto link. - Offline saved device: says how to wake it and 'Try Again' rescans (the old Retry did nothing). - Firmware compatibility warning is an acknowledge-only alert; the non-critical one keeps its opt-out row. - Device rows, guide cards and capture cards are real buttons with labels; battery shows a glyph and percentage with a spoken label, no emoji. - Connection guide, pairing steps, Ray-Ban picker/setup and Apple Watch setup use showOmiSheet; Apple Watch is only offered on iOS; 'I've Done This' becomes 'Done'. - Connect page: OmiBackButton, labelled settings button, and connecting returns to the existing Home (HomeNavigation.returnHome) instead of stacking a second one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): edit-sheet dirty guard and row long-press menu primitives showOmiEditSheet/OmiEditSheet own the drag so swipe-down, scrim tap, the close X and system back all ask "Discard changes?" while an editor is dirty (the framework's drag-to-dismiss pops without asking). showOmiRowMenu is the one long-press menu shape for list rows (Open, edit actions, Select, Delete). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): follow the warning token on the paused dot and the Conversation Map title Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/l10n): lane 2 strings for memories, tasks, goals and people Sheet titles without emoji, read-only memory hint, Undo toasts, task plural titles and counts, shared-task strings, memory review card and People page labels, with every locale translated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/memories): Undo-only deletes, openable read-only memories, guarded edit sheet - Every memory delete (swipe, long-press menu, edit sheet) goes through deleteMemoryWithUndo: no "cannot be undone" dialog, the shared OmiFeedback undo toast with no X, and a commit scoped to the memory the toast offered. The provider's pending window becomes an 8 s backstop that outlasts the toast. - Every memory opens: locked/superseded/ledger rows open the same sheet read-only with full text; swipe-delete only where editing is allowed. - Long-press menu Open / Edit / Delete. - Edit and new-memory sheets: explicit Cancel and Save, dirty guard, OmiButton. - Page: OmiBackButton, OmiSearchField, labelled 44pt header buttons, neutral FAB, OmiEmptyState / error state, management sheet on showOmiSheet with a confirm that names the action and toasts that reach the page after the sheet closes. - Memory graph: OmiBackButton, localized title, OmiLoading/Error/EmptyState, no purple. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/tasks): fixed swipe meaning, Undo deletes, guarded task and goal sheets - One swipe meaning on every task row at every indent: right completes (or reopens), left deletes. Indent/outdent move to the long-press menu (and drag keeps its horizontal indent); long-press shows Open / Mark Complete / Indent / Outdent / Select / Delete. - Single task and goal deletes are immediate with an Undo toast, backed by new staged-delete APIs on ActionItemsProvider and GoalsProvider (nothing hits the server until the toast closes). Bulk and clear-completed deletes confirm with a plural, verb-named dialog (the old one said "This will only remove deadlines"). - ActionItemFormSheet keeps its constructor; adds showActionItemFormSheet, explicit Cancel/Save, a close X in edit mode, a dirty guard and instant completion. The date picker and time picker are neutral (no purple) and locale/24-hour aware. - Home and Tasks share one GoalFormSheet (Cancel/Save, dirty guard, localized). - Tasks page: OmiSearchField "Search tasks", labelled overflow button, OmiEmptyState, neutral FAB/selection/drop indicators, localized "Exported to", counts with labels. - Selection bar, shared-tasks sheet and Home Today card on the primitives (44pt targets, rows open the task sheet). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/people): tapping a sample plays it; delete is a labelled control behind a confirm People page gets OmiBackButton, labelled Add/Help controls, an adaptive person dialog, OmiEmptyState/OmiLoadingState and confirms that name the action. The memory review card is localized with 44pt controls and tokens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): one header for live, processing and saved conversations; one media viewer - Live and processing pages share ConversationStateAppBar: the circled back button of the saved page and the state as the title (Recording / Paused / Reconnecting / Processing, one local mapping until lane 1a's shared capture_state_labels lands). No emoji status, no back button inside the title row. - Live page: remove the unreachable Summary tab and its TabController; Process Now is an OmiButton, mute a labelled OmiIconButton; the stop confirmation is showOmiConfirmWithOptOut with a verb; speaker names from SpeakerNames; photo-group time from OmiDateFormat and a localized photo count; tokens for the timeline and the upload indicator. - Processing page: one view (the transcript so far) under the shared header instead of Content/Summary tabs whose Summary only said 'Processing'. - Media viewer: always a full-screen modal with a trailing OmiCloseButton and swipe-down to close (MediaViewerPage.open), labelled share button, OmiSpinner; photo grids and the live timeline open it that way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/chat): contract composer, drawer and failed-reply retry Chat is a pushed page with a leading back (D1). Composer controls are labelled 44pt targets; Send that cannot send (offline, uploading) is drawn disabled with an offline hint. The apps drawer keeps Clear Chat at the bottom behind a destructive confirm, and each app has one labelled Disable (with Undo). A failed reply shows a localized error with Try Again that resends the user's message instead of raw server text. The feedback sheet is localized with a neutral Submit, thumbs read Helpful / Not Helpful, and copy/share analytics send the message length, not its text. Adds the lane-3 l10n keys for chat, apps and announcements. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/apps): one app action button, one consent dialog, Undo on disable AppListActionButton reads Open / Enable / View with the same meaning in every store list (no decorative Install). Enabling an app that works outside Omi asks one localized question naming what leaves Omi; Disable is immediate with a 5 s Undo on the detail page and in the chat drawer. The detail page moves onto the header, button, sheet, dialog and feedback primitives, splits its cards into widgets, and its cancel-subscription dialog reads Keep Subscription / Cancel Subscription. Referral and app home pages share one web page with a loading state, an error state with Try Again, and back within web history. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/announcements): answer-only dismissal through the prompt queue Announcements, feature screens and the changelog present through PromptQueue. The dialog's scrim no longer dismisses; the close X or the call to action marks an announcement seen, Not Now postpones it. The feature screen is a full-screen modal route with a trailing close; the changelog uses the shared sheet shell and error state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/ai-app-generator): one leading back, system back steps to the prompt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app): sweep conversation sheets and small widgets onto the primitives - Share via SMS and Create Template sheets use showOmiSheet, OmiSearchField, OmiButton, OmiSwitch, OmiEmptyState/OmiLoadingState and tokens; no purple (INV-UI-1). Create Template blocks back while the app is being created and opens the template picker from the navigator, not its own popped context. - Markdown and expandable text: neutral search highlight and link colour. - OmiSpinner and type/radius tokens in the speaker summary action, test prompts, photo image, person chip and waveform section. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(app/ui): edit sheets and row menus in the barrel and the contract Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): conversation detail UX contract cases Attendee names, detail duration rule, speaker numbering across the getLastTranscript window, transcript bubble chrome (no provider, offset time, 44pt labelled play), the shared capture state header, search bar position, the audio download sheet's own-context close/Cancel/back, the edit segment discard guard, and the media viewer's trailing close. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): show conversation developer tools in debug builds too No settings row writes devModeEnabled yet, so gating on it alone would hide Copy Conversation ID and Test Prompt from everyone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * wip(app): lane 3 checkpoint after spend-limit stop (uncommitted work preserved, not reviewed) * wip(app): lane 4 checkpoint after spend-limit stop (uncommitted work preserved, not reviewed) * wip(app): lane 5 checkpoint after spend-limit stop (uncommitted work preserved, not reviewed) * test(app/settings): device page stub getters and sign-out keep-list The one device page reads battery, charging and firmware getters the double-tap test stub lacked, and its connected picture pulses; stub the getters and turn on Reduce Motion so pumpAndSettle settles. Cover the Sign Out keep-list: paired device and display preferences survive, account values are cleared, and no credential is ever kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): name speaker sheet saves through the shared OmiButton Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/ai-app-generator): sweep the generator page onto the primitives App bar with a leading OmiBackButton on both steps; neutral accent instead of indigo/purple (INV-UI-1); OmiButton for Create App (busy state built in), OmiIconButton for send and regenerate-icon (labelled, 44pt), OmiSwitch for Make Public / Paid App, OmiSpinner, tokens for every colour, size and radius. Description and Features headings come from l10n; category and capability names use the shared localized titles. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/lane-3): plans sheet in the sheet shell, review fixes to the WIP checkpoint - chat: the quota plans sheet opens through showOmiSheet (lane 4 handoff). - chat evidence card: localized screen-reader hint. - capability apps page: localized category titles, token refresh colours. - const constructors and block-bodied ifs the WIP left for the analyzer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/chat): conversation links push through routeToPage; evidence test hosts l10n Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app): every push takes the platform route; shared-task link copy localized - routeToPage builds its route with omiPageRoute (defaultTargetPlatform) instead of dart:io Platform, the Foundation A handoff. - Deep links in AppShell, the People link in the name-speaker sheet and the firmware prompt in DeviceProvider push through routeToPage/omiPageRoute, never a bare MaterialPageRoute (iOS back swipe intact). - The shared-tasks deep link's not-found toast and unknown-sender fallback were hardcoded English; now sharedTasksLinkExpired and sharedTasksUnknownSender in all 49 locales. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): drop imports the ui barrel already provides Lane 2's edit-sheet and row-menu primitives joined the ui.dart barrel, which left seven direct imports redundant and pushed unnecessary_import over the analyzer baseline (CI's Dart Analyze & Tests failed on it). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/payments): labelled overflow menu, titled country picker sheet Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): people samples follow the one Voice Profile name Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/home): links open inside the one Home; startup prompts queue - Notification taps and app links open inside the Home already on screen (HomeNavigation.openRoute: pop to Home, select the tab, push the page) instead of pushReplacement-ing a second Home over whatever was showing. Home registers itself while mounted, so "Done" at the end of the firmware and connect flows also returns to it (nav #3). - Deep links move to home_deep_links.dart: parent before child (the Settings sheet opens before Data & Privacy is pushed over it), every push through routeToPage, and a removed app or deleted conversation now says so instead of only logging (nav #18). - D1: the home chat bar, its mic, chat deep links and the Ask Omi / Voice quick actions push chat as a normal page (no fullscreenDialog). The chat bar is one labelled button. - D6: Android back on the Tasks, Conversations or Apps tab returns to Home before it leaves the app (root PopScope). - Settings: the language / speech profile / transcription model comparison runs once the reader is back on Home, not the moment the sheet opens (onboarding-home #25). - Startup prompts go through PromptQueue (onboarding-home #23): upgrade alert (a required update first), changelog + announcements, the CV1 tutorial (waits for the pendant), firmware announcements, the Plus-meter plans sheet (only when the meter crosses its threshold, once per session, showOmiSheet) and Bluetooth guidance. HomePromptGate holds the queue while phone recording, on a call or during a firmware update and pumps it when that clears; passive pendant capture does not hold it. - Handoffs applied: dead toggleDailySummaries call removed (lane 1a); sync, task-export and quick-action pushes use routeToPage (lanes 2, 4). Header controls use neutral tokens (INV-UI-1) and OmiHaptics. - New strings (7 keys, every locale): appNotFoundOrRemoved plus the startup failure and discard-recording strings used by the next commits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/startup): startup failure offers Try Again and Contact Support - The failure screen words the message by error class: a rejected build configuration (Env / startup-routing checks, now thrown as StartupConfigurationError) says the build is at fault and offers Contact Support; any other failure says to check the connection and offers Try Again, which re-runs start-up, plus Contact Support (onboarding-home #27). - main() moves start-up into _start() so Try Again can run it again; ServiceManager.init, which refuses a second call, runs once. - Localized without a localization scope (lookupAppLocalizations for the device locale, English fallback); tokens replace literal colours/sizes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/speech-profile): confirm before discarding a recording in progress - Back or swipe while the voice sample is recording asks "Discard Recording?" (Discard / Keep Recording) instead of dropping it (onboarding-home #26). - "Reconnecting…" is an info toast, not an error. - The help control is a labelled OmiIconButton ("How to take a good sample?") opening showOmiAlert; back is OmiBackButton. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/home): header pill and record button expose their actions - The device pill and the record button wrap their gestures in Semantics(excludeSemantics: true), which also dropped the tap and long-press actions; the Semantics node now carries onTap (and the record options long-press), so VoiceOver/TalkBack can activate them (onboarding-home #15, #24). - Phone-mic offline fallback notice is an OmiFeedback info toast. - Sweep: auth-gate spinner is OmiSpinner; welcome Continue uses OmiHaptics; OmiGlass OTA page formatted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): delete webhooks.dart, reachable only from the removed dev-tools widget Its one importer was GetDevToolsOptions in conversation_detail/widgets.dart, which nothing constructed and lane 1b deleted. The dead-code ratchet flags the now-unreachable file; the webhook settings themselves live in DeveloperModeProvider and the users API and are unaffected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/home): a link counts as opened once its page is pushed openHomeDeepLink awaited each push, so HomeNavigation.openRoute (and the notification-open telemetry behind it) only resolved when the reader closed the page. It now resolves once the destination is on screen. A missing app or conversation is an info toast, as the contract says; the contract names HomeNavigation.openRoute / returnHome. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 1 (ZCode GLM-5.3) - A memory's Undo restores only that memory: restoreLastDeletedMemory takes the toast's id, so a stale toast cannot restore a newer delete (test added). - A failed task delete no longer inserts a Home-only task into the Tasks list; the staged index goes straight to _restoreDeletedItem, which skips -1. - The conversation pending-delete window is 10 s, so an Undo toast that starts late behind other snack bars still gets its full time; the toast still commits early when it closes. - The integration page's button reads "Disconnect" instead of stripping the question mark from a translated sentence (broke ¿…? and ? locales). - mounted checks after the long awaits in the Create Template sheet and Wrapped 2025 generation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): keep memories_provider under its line-count ceiling Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/apps): app creation forms on the shared form pieces and primitives Submit App and Manage App draw their screenshot strip and GitHub URL card from app_form_fields (screenshots open as a modal viewer with a trailing close), use AppFormCard/section titles/input decoration, OmiSwitch rows for Public/Paid (no indigo/green thumbs), OmiButton for submit/update, a leading OmiBackButton, and l10n for every string. Update asks with showOmiConfirm and a verb button (Update App); Submit's confirm reads Submit App; Start Earning is an Omi sheet with Not Now. App metadata pickers are Omi sheets, App ID copy goes through OmiClipboard. API keys: adaptive dialogs, destructive Revoke confirm, OmiFeedback toasts, localized dates. Add MCP Server: OmiBackButton, OmiButton, OmiFeedback. Provider snackbars and picker titles localized. New l10n keys: generateDescription, selectImageFileTitle, selectThumbnailImageTitle, appIdNotFoundError, manifestRefreshedSuccess, manifestRefreshFailed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/apps): store home, filters and reviews on the primitives The store's filter sheet opens in the Omi sheet shell; header icon controls are labelled 44pt OmiIconButtons; empty search is OmiEmptyState. Reviews: OmiBackButton, adaptive reply dialog, OmiFeedback toasts, OmiButton busy state, localized Add/Edit Your Review and rating counts, readable (not grey-700) secondary text. App summary and capabilities card localized. Screenshot taps on the detail page open through openAppScreenshots (modal, trailing close). Review avatar palette drops its purple entry (INV-UI-1). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app/apps): Disable hides at once, commits after the Undo toast, Undo restores Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/lane-3): localized pricing value on Manage App and file-picker title in chat Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 2 (lane 5) - A throw while preparing the Omi firmware update (for example a corrupt download that fails zip extraction) now ends in the failed state instead of an installing spinner with back blocked and every prompt held. - Home drops the prompts it enqueued when it is disposed, so after sign-out and sign-in the new Home's announcements, device tutorial and firmware notices are not deduplicated against dead closures. - The speech step re-reads the microphone permission when the app resumes, so granting it in Settings clears the Open Settings row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): retire AnimatedLoadingButton Lane 3 moved its last callers (payments, app detail) onto OmiButton, which owns the busy state and recovers when the action throws; the dead-code ratchet flags the unreachable widget. Its throw-recovery test now runs against OmiButton directly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 3 (lane 3) - Try Again on a failed reply releases the composer when the failure was already cleared, and the first app message releases it even if chat closes mid-request (both could leave typed sends blocked). - Enabling an app again inside its 5 s disable Undo window cancels the pending disable instead of being overwritten when the toast ends; tests for that and for a refused disable. - Stripe setup names the selected country from the full list, not the picker's filtered one, and always stops polling on dispose. - The AI app generator shows a failed submit on the generated view. - OmiWebPage's Try Again reloads the page that failed, not the first URL; a malformed app home URL shows the error state instead of throwing. The reported Stripe double-submit is already prevented: OmiButton ignores taps while its onPressed future runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 4 (#17297 merge and rulings) - Share from a conversation row now behaves like the page: a private conversation asks first ("Anyone with the link can view") and goes back to private when the share sheet is dismissed (was: made public at once). - Developer Settings gains a Conversation Developer Tools switch (writes devModeEnabled), so release users can still reach Copy Conversation ID and Test Prompt; 2 keys in 49 locales. - The row's Separate… always disposes its controller; tests cover a confirmed separation from a row (the other recording, never the row) and the row share confirmation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): phone number verified date holds in every timezone The fixture was UTC midnight, which is Jan 1 in Pago Pago; CI runs the suite at UTC+14 and UTC-11. Local noon reads Jan 2 everywhere. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): conversation title and emoji share one line in the header The title field kept Material's 48pt input height, which pushed the title below the emoji; it is dense now and the one-line row centres both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): every voice-profile entry opens the guided introduction David's ruling (2026-09-24): the voice profile is the guided fill-in-the- blank flow from #14514 (sentence starters, review, then voice, memories and a goal). Two entry points still reached the old question-based page: the Settings/Profile Voice Profile row for anyone who already had a profile, and the capture card's "Teach Omi your voice". Both now open the guided flow. The old page is unreachable, so it goes, with its two widgets, the getDialog helper and the fade-in text widget only it used (dead-code ratchet). Open PR #12531 edits the old page and will need to drop those hunks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): Star and Share back in the conversation top bar; record conversation actions David, 2026-09-24 (overrides the earlier one-overflow ruling): - the conversation page's top bar is Ask Omi (the primary, filled), Star and Share as matching 44pt icon buttons, then one "…" holding Rename, Move to Folder, Recordings and the rest, Delete last and destructive; - one registered event, Conversation Action, records every conversation action with two closed enums: action (ask_omi, star, unstar, share, rename, move_folder, recordings_open, separate, delete, copy_transcript, …) and surface (top_bar, overflow, detail_body, row_long_press, row_swipe). No ids, titles or content. It goes through the C7 typed registry (contracts/analytics/events.json, generated Dart, a presence consumer) and AnalyticsManager, so consent and queueing apply. Tests cover the event shape for every action and the top bar, overflow, row long-press and row swipe call sites. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): the people chip names only named speakers David, 2026-09-24: never label a participant "Speaker 1". The conversation header's people chip now shows the first named participant ("You" for the account owner) and counts everyone else: "David + 3 others" (participantsSummary, an ICU plural in 49 locales). With nobody named it hides; a linked calendar event's attendees still stand in when the transcript names no one. The transcript's own per-line speaker labels are unchanged. Tests cover named, mixed and none. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: drop pointers to the deleted speech-profile page Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 5 (voice profile and people chip) - SpeechProfileProvider drove only the deleted question page; the guided introduction records through GuidedVoiceIO. Remove the provider, its registration, the onboarding back-step close() that no longer stopped anything, and its test. - Drop pointers to deleted files: the addressability baseline entry and the logout flow's covers (now sign_out.dart); a stale doc paragraph. - participantsSummary carries few/many forms so Polish, Czech and Slovak agree for 2-4 and 5+; other Slavic and Baltic locales use a count-agnostic "one more" form. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 5 天前 | |
feat(app): mobile UX consistency pass: shared primitives, contract and audit fixes (#18436) * feat(app/ui): add OmiColors tokens and stop using colorScheme.primary as a page background Add lib/ui/omi_tokens.dart: OmiColors (surfaces, text, neutral accent, status), OmiType ramp, OmiRadius, OmiSpacing, OmiMotion (honours Reduce Motion) and OmiHaptics. 53 sites read Theme.of(context).colorScheme.primary as a page or app bar background because the theme's primary was black. They now name OmiColors.surface0 (same black), so the theme can get a real accent. In the four files over the line-count threshold the explicit colour is dropped instead; the theme default is the same black. AppStyles and ResponsiveHelper get doc comments pointing at the tokens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): one theme and shared controls for navigation, buttons, sheets and page states Theme (lib/ui/omi_theme.dart, used by main.dart and the visual_audit harness so they cannot drift): a real dark ColorScheme with the white accent as primary, black scaffold, app bar theme (black, flat, centred 17pt semibold title), white progress indicators (the unstyled spinners were black on black), floating surface2 snack bars, surface1 dialogs and sheets, neutral switches. Material 2 stays on. MaterialApp.builder now syncs Intl.defaultLocale to the resolved locale. Components under lib/ui/components (barrel lib/ui/ui.dart): OmiBackButton / OmiCloseButton (platform glyph, localized label, 44pt target, circled variants), OmiIconButton (required label, tooltip), OmiButton (primary/secondary/destructive/tertiary, regular/compact, async loading that always clears), OmiSpinner, OmiLoadingState / OmiEmptyState / OmiErrorState, showOmiSheet and OmiSheetScaffold, OmiSwitch, OmiSettingsGroup / OmiSettingsRow / OmiSectionHeader, OmiSearchField, and omiPageRoute for code that needs a Route object. HeaderCircleButton is now a wrapper over OmiIconButton.filled and AnimatedLoadingButton over OmiButton (fixes the spinner that never cleared after a throw, and the 40pt target). find_device's two unstyled ElevatedButtons keep their black fill explicitly now that primary is white. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/settings): move data privacy, Asana and ClickUp settings onto the shared controls Proving set for the new primitives. Data & Privacy uses OmiBackButton, a section header and an OmiSettingsGroup of app rows, OmiEmptyState, and drops its accent tint for neutral tokens. Asana and ClickUp show an app bar with a back button and OmiLoadingState during their first load (the page used to be an invisible spinner with no way out), OmiSpinner for section loads, and tokens for their colours. The shared IntegrationSettingsPage header uses OmiBackButton and a labelled refresh OmiIconButton, and its disconnect control is an OmiButton.destructive. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): one adaptive confirmation system (showOmiConfirm/showOmiAlert) getDialog, ConfirmationDialog, OmiConfirmDialog and AppDialog become thin adapters over OmiAlertDialog: Cancel always shown and always closes, real CupertinoDialogActions on iOS (destructive red, safe choice bold), localized defaults, a destructive flag, and AppDialog.show no longer drops singleButton and onCancel. Non-page raw AlertDialogs in providers/widgets migrated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): OmiFeedback toasts and OmiClipboard One floating neutral toast at a time with contract timings (confirm 1.5 s, info 4 s, error 8 s with close, undo 5 s with no close, resolving whether the reader undid). Snackbars with actions no longer persist forever. The home shell registers its tab-bar clearance. AppSnackbar's ~180 call sites route through it. OmiClipboard refuses empty text and skips its toast on Android 13+ where the system already confirms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): OmiDateFormat, OmiDuration and shared speaker names Dates follow the app locale and the 24-hour setting; durations have one offset/compact/long vocabulary (the playback bar no longer drops hours). SpeakerNames is the single source for speaker labels in app and export: You / person / Omi / dense Speaker N, localized in copied transcripts. Legacy helpers delegate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): PromptQueue for startup and background prompts Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(checks): INV-UI-3 mobile UX contract ratchet Per-rule, per-file no-increase ratchet over hand-rolled back glyphs, PageRouteBuilder, raw sheets/dialogs/snackbars/clipboard, date patterns, colour/font-size/radius literals, raw spinners, '...' and hardcoded Text copy under app/lib, with an allow-comment escape hatch and a test proving each rule fires. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(app): mobile UX contract The numbered rules and the one primitive for each: leaving a surface, sheets, controls, destructive actions (D5), dialogs, feedback timing, copying, dates and durations, speakers, tokens, words (D3), settings (D4), page states, prompts, permissions and accessibility, plus the D1/D2/D6 navigation calls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/ui): dense speaker numbering on live and saved transcripts Both transcript renderers pass the conversation's segments to speakerLabel so Speaker N is numbered per conversation. Drops the dead duplicate formatChatTimestamp in widgets/transcript.dart, makes Material OmiAlertDialog scrollable, and points primitive docs at the contract's sections. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/l10n): contract words sweep Task(s) replaces Action Item(s)/To-Do in every locale (D3); Omi capitalised; '…' instead of '...'; search placeholders drop the ellipsis; Try Again, Not Now, OK, Title Case buttons. New keys: filterBySpeaker, notNow, discard, keepEditing, discardChangesTitle, discardChangesMessage. Translates the nine live-capture keys that only existed in English and removes five orphaned locale keys, so scripts/l10n.py check passes. The upgrade alert offers Not Now (postpone, not ignore-forever) and Update, honours barrierDismissible, and loses the emoji. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app/ui): export feedback, format and prompt primitives from the ui barrel; token colours Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app/ui): follow the Title Case clear-search label Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/l10n): strings for the conversation detail pass Link Event sheet, calendar event actions, search result navigation, playback labels, the share confirmation, the Tasks tab counts and empty state, and the speaker identify label, with translations for every locale. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): conversation detail on the shared primitives - D2: no body-wide swipe to another conversation and no PageRouteBuilder; Transcript/Summary(/Tasks) swipe as tabs, the Tasks tab exists only when there are tasks. - Header: OmiBackButton.circled plus same-size OmiIconButton circles (star, share, more) and a compact Ask Omi button that pushes Chat (D1). - Share asks before making a private conversation public and reverts it when the share sheet reports a dismissal. - Copy Conversation ID and Test Prompt only in developer mode; copies go through OmiClipboard, toasts through OmiFeedback. - Link Event and calendar event sheets on showOmiSheet with OmiDateFormat, l10n and full attendee names; visibility, summary template, name speaker and edit segment sheets on the shared shell. - Audio download sheet pops with its own context, offers Cancel and blocks back with PopScope. - Title: single line, Done, Untitled placeholder, saves on blur with an announced outcome; detail date/duration use OmiDateFormat/OmiDuration.compact (same rule as the list). - Tasks tab: tapping a task opens the shared task editor; labelled count pills; Tasks wording (D3); OmiEmptyState. - Edit segment sheet: speaker from SpeakerNames (You for the owner), Save and Cancel, discard guard. - Extract calendar sheets, search bar, transcript tab and tasks tab; delete dead ActionItemsListWidget, GetSheetTitle, GetDevToolsOptions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): keep INV-UI-3 allow comments on the flagged line Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): transcript bubbles and playback bar on the shared primitives Transcript: one SpeakerNames resolver per build (dense numbering, live people), no transcription-provider name, the start time as an OmiDuration offset, a labelled 44pt play button, labelled speaker targets, neutral user bubble and search highlight (INV-UI-1), localized 'translated by Omi', and getLastTranscript numbers speakers across the whole conversation. Playback bar: neutral surfaces instead of purple, a labelled 32pt play/pause in a 44pt target, a labelled stop button, OmiSpinner, OmiFeedback errors, the summary pill ellipsizes by layout instead of a hand-made '...', and the summary template sheet opens through showSummarizedAppsSheet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/onboarding): shared permission row, real progress, back and resume - OmiPermissionRow (lib/ui/components): reason, Allow -> system prompt, Allowed, Open Settings when the system will not ask again. One OnboardingPermissionsPanel drives both the first-run step and the returning-user interstitial; Continue never fires prompts and 'Always' location is no longer asked during first run. - Onboarding wrapper: placeholder pages removed, progress dots count the six real steps with a spoken 'Step N of M', PopScope makes system back step back, the last reached step is saved per account and resumed. - Consent step offers 'Use a Different Account' (sign out, back to start). - Acquisition survey is skippable; language step saves with a busy button and a Try Again error; picker selects on tap. - Steps share OnboardingCard (content scrolls, footer stays; min-height, no fixed text heights) and OmiButton; completion screen localized, purple gone. - Voice introduction: mic refusal shows the permission row; 'Skip Question' vs 'Skip'; device tutorial exits by a trailing close X. - Permission reasons rewritten, platform-neutral Location Services copy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/conversations): one delete path with Undo, row menu, bulk actions, recaps page - Conversation delete (list swipe, row menu, detail page) goes through one path: confirm unless opted out ("Don't ask again" is allowed because Undo backs it), then an Undo toast backed by the provider's pending-delete window (now 6 s, longer than the 5 s toast; the toast commits early when it closes) (D5). - Long-press on a conversation row opens one context menu: Open, Star/Unstar, Move to Folder, Share, Select, Delete. The selection bar gains Move and Delete (always confirmed) beside Merge. - Daily Recaps is its own pushed page with a back button; Home's View All pushes it and the Conversations tab never switches into a recap mode. Pull-to-refresh reloads recaps and a failed load offers Try Again. Recap counts are labelled. - One date filter for list and search, shown as a removable chip; the search field is seeded from the provider and uses OmiSearchField. - Dates and lengths via OmiDateFormat / OmiDuration (Today gets a header, the New badge sits beside the time, WAL/recording offsets keep their hours). - Discarded rows read "Discarded · 12s" with a plain-text snippet; blank titles read "Untitled Conversation". - Shared capture state table (capture_state_labels.dart) for the in-progress card; the processing card shows its real start time. - Local recording swipe always confirms. Sheets, dialogs, toasts, spinners and back buttons in conversations/, sync and recap pages move onto lib/ui. - Offline Sync is the one name for the device-storage sync screen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app/conversations): cover Undo-backed delete, row menu, recaps page and capture labels - Provider: the pending-delete window outlasts the Undo toast; undo restores, commit sends once, a regroup during the window keeps the row hidden. - Widgets: Undo restores / timeout commits / bulk toast counts; long-press menu entries and Select; untitled and discarded row titles; Today header; the date filter chip; Daily Recaps page back button, labelled counts, Try Again and pull-to-refresh. - Unit: the shared capture state table (an audio interruption is Paused). - The Home preview's row menu leaves out Select (no selection bar there); the in-progress card's photo count is labelled; last raw spinner and "..." in the capture card go. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/pairing): pairing recovery, one Bluetooth prompt, shared sheets - Find devices: a missing Bluetooth/location permission goes through the one BluetoothReadiness guidance prompt (Open Settings); the local 'Enable Bluetooth' dialogs are gone. - Nothing found: 'Scan Again', 'How to Pair' (connection guide), then 'Contact Support' instead of a lone mailto link. - Offline saved device: says how to wake it and 'Try Again' rescans (the old Retry did nothing). - Firmware compatibility warning is an acknowledge-only alert; the non-critical one keeps its opt-out row. - Device rows, guide cards and capture cards are real buttons with labels; battery shows a glyph and percentage with a spoken label, no emoji. - Connection guide, pairing steps, Ray-Ban picker/setup and Apple Watch setup use showOmiSheet; Apple Watch is only offered on iOS; 'I've Done This' becomes 'Done'. - Connect page: OmiBackButton, labelled settings button, and connecting returns to the existing Home (HomeNavigation.returnHome) instead of stacking a second one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): edit-sheet dirty guard and row long-press menu primitives showOmiEditSheet/OmiEditSheet own the drag so swipe-down, scrim tap, the close X and system back all ask "Discard changes?" while an editor is dirty (the framework's drag-to-dismiss pops without asking). showOmiRowMenu is the one long-press menu shape for list rows (Open, edit actions, Select, Delete). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): follow the warning token on the paused dot and the Conversation Map title Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/l10n): lane 2 strings for memories, tasks, goals and people Sheet titles without emoji, read-only memory hint, Undo toasts, task plural titles and counts, shared-task strings, memory review card and People page labels, with every locale translated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/memories): Undo-only deletes, openable read-only memories, guarded edit sheet - Every memory delete (swipe, long-press menu, edit sheet) goes through deleteMemoryWithUndo: no "cannot be undone" dialog, the shared OmiFeedback undo toast with no X, and a commit scoped to the memory the toast offered. The provider's pending window becomes an 8 s backstop that outlasts the toast. - Every memory opens: locked/superseded/ledger rows open the same sheet read-only with full text; swipe-delete only where editing is allowed. - Long-press menu Open / Edit / Delete. - Edit and new-memory sheets: explicit Cancel and Save, dirty guard, OmiButton. - Page: OmiBackButton, OmiSearchField, labelled 44pt header buttons, neutral FAB, OmiEmptyState / error state, management sheet on showOmiSheet with a confirm that names the action and toasts that reach the page after the sheet closes. - Memory graph: OmiBackButton, localized title, OmiLoading/Error/EmptyState, no purple. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/tasks): fixed swipe meaning, Undo deletes, guarded task and goal sheets - One swipe meaning on every task row at every indent: right completes (or reopens), left deletes. Indent/outdent move to the long-press menu (and drag keeps its horizontal indent); long-press shows Open / Mark Complete / Indent / Outdent / Select / Delete. - Single task and goal deletes are immediate with an Undo toast, backed by new staged-delete APIs on ActionItemsProvider and GoalsProvider (nothing hits the server until the toast closes). Bulk and clear-completed deletes confirm with a plural, verb-named dialog (the old one said "This will only remove deadlines"). - ActionItemFormSheet keeps its constructor; adds showActionItemFormSheet, explicit Cancel/Save, a close X in edit mode, a dirty guard and instant completion. The date picker and time picker are neutral (no purple) and locale/24-hour aware. - Home and Tasks share one GoalFormSheet (Cancel/Save, dirty guard, localized). - Tasks page: OmiSearchField "Search tasks", labelled overflow button, OmiEmptyState, neutral FAB/selection/drop indicators, localized "Exported to", counts with labels. - Selection bar, shared-tasks sheet and Home Today card on the primitives (44pt targets, rows open the task sheet). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/people): tapping a sample plays it; delete is a labelled control behind a confirm People page gets OmiBackButton, labelled Add/Help controls, an adaptive person dialog, OmiEmptyState/OmiLoadingState and confirms that name the action. The memory review card is localized with 44pt controls and tokens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): one header for live, processing and saved conversations; one media viewer - Live and processing pages share ConversationStateAppBar: the circled back button of the saved page and the state as the title (Recording / Paused / Reconnecting / Processing, one local mapping until lane 1a's shared capture_state_labels lands). No emoji status, no back button inside the title row. - Live page: remove the unreachable Summary tab and its TabController; Process Now is an OmiButton, mute a labelled OmiIconButton; the stop confirmation is showOmiConfirmWithOptOut with a verb; speaker names from SpeakerNames; photo-group time from OmiDateFormat and a localized photo count; tokens for the timeline and the upload indicator. - Processing page: one view (the transcript so far) under the shared header instead of Content/Summary tabs whose Summary only said 'Processing'. - Media viewer: always a full-screen modal with a trailing OmiCloseButton and swipe-down to close (MediaViewerPage.open), labelled share button, OmiSpinner; photo grids and the live timeline open it that way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/chat): contract composer, drawer and failed-reply retry Chat is a pushed page with a leading back (D1). Composer controls are labelled 44pt targets; Send that cannot send (offline, uploading) is drawn disabled with an offline hint. The apps drawer keeps Clear Chat at the bottom behind a destructive confirm, and each app has one labelled Disable (with Undo). A failed reply shows a localized error with Try Again that resends the user's message instead of raw server text. The feedback sheet is localized with a neutral Submit, thumbs read Helpful / Not Helpful, and copy/share analytics send the message length, not its text. Adds the lane-3 l10n keys for chat, apps and announcements. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/apps): one app action button, one consent dialog, Undo on disable AppListActionButton reads Open / Enable / View with the same meaning in every store list (no decorative Install). Enabling an app that works outside Omi asks one localized question naming what leaves Omi; Disable is immediate with a 5 s Undo on the detail page and in the chat drawer. The detail page moves onto the header, button, sheet, dialog and feedback primitives, splits its cards into widgets, and its cancel-subscription dialog reads Keep Subscription / Cancel Subscription. Referral and app home pages share one web page with a loading state, an error state with Try Again, and back within web history. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/announcements): answer-only dismissal through the prompt queue Announcements, feature screens and the changelog present through PromptQueue. The dialog's scrim no longer dismisses; the close X or the call to action marks an announcement seen, Not Now postpones it. The feature screen is a full-screen modal route with a trailing close; the changelog uses the shared sheet shell and error state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/ai-app-generator): one leading back, system back steps to the prompt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app): sweep conversation sheets and small widgets onto the primitives - Share via SMS and Create Template sheets use showOmiSheet, OmiSearchField, OmiButton, OmiSwitch, OmiEmptyState/OmiLoadingState and tokens; no purple (INV-UI-1). Create Template blocks back while the app is being created and opens the template picker from the navigator, not its own popped context. - Markdown and expandable text: neutral search highlight and link colour. - OmiSpinner and type/radius tokens in the speaker summary action, test prompts, photo image, person chip and waveform section. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(app/ui): edit sheets and row menus in the barrel and the contract Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): conversation detail UX contract cases Attendee names, detail duration rule, speaker numbering across the getLastTranscript window, transcript bubble chrome (no provider, offset time, 44pt labelled play), the shared capture state header, search bar position, the audio download sheet's own-context close/Cancel/back, the edit segment discard guard, and the media viewer's trailing close. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): show conversation developer tools in debug builds too No settings row writes devModeEnabled yet, so gating on it alone would hide Copy Conversation ID and Test Prompt from everyone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * wip(app): lane 3 checkpoint after spend-limit stop (uncommitted work preserved, not reviewed) * wip(app): lane 4 checkpoint after spend-limit stop (uncommitted work preserved, not reviewed) * wip(app): lane 5 checkpoint after spend-limit stop (uncommitted work preserved, not reviewed) * test(app/settings): device page stub getters and sign-out keep-list The one device page reads battery, charging and firmware getters the double-tap test stub lacked, and its connected picture pulses; stub the getters and turn on Reduce Motion so pumpAndSettle settles. Cover the Sign Out keep-list: paired device and display preferences survive, account values are cleared, and no credential is ever kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): name speaker sheet saves through the shared OmiButton Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/ai-app-generator): sweep the generator page onto the primitives App bar with a leading OmiBackButton on both steps; neutral accent instead of indigo/purple (INV-UI-1); OmiButton for Create App (busy state built in), OmiIconButton for send and regenerate-icon (labelled, 44pt), OmiSwitch for Make Public / Paid App, OmiSpinner, tokens for every colour, size and radius. Description and Features headings come from l10n; category and capability names use the shared localized titles. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/lane-3): plans sheet in the sheet shell, review fixes to the WIP checkpoint - chat: the quota plans sheet opens through showOmiSheet (lane 4 handoff). - chat evidence card: localized screen-reader hint. - capability apps page: localized category titles, token refresh colours. - const constructors and block-bodied ifs the WIP left for the analyzer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/chat): conversation links push through routeToPage; evidence test hosts l10n Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app): every push takes the platform route; shared-task link copy localized - routeToPage builds its route with omiPageRoute (defaultTargetPlatform) instead of dart:io Platform, the Foundation A handoff. - Deep links in AppShell, the People link in the name-speaker sheet and the firmware prompt in DeviceProvider push through routeToPage/omiPageRoute, never a bare MaterialPageRoute (iOS back swipe intact). - The shared-tasks deep link's not-found toast and unknown-sender fallback were hardcoded English; now sharedTasksLinkExpired and sharedTasksUnknownSender in all 49 locales. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): drop imports the ui barrel already provides Lane 2's edit-sheet and row-menu primitives joined the ui.dart barrel, which left seven direct imports redundant and pushed unnecessary_import over the analyzer baseline (CI's Dart Analyze & Tests failed on it). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/payments): labelled overflow menu, titled country picker sheet Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): people samples follow the one Voice Profile name Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/home): links open inside the one Home; startup prompts queue - Notification taps and app links open inside the Home already on screen (HomeNavigation.openRoute: pop to Home, select the tab, push the page) instead of pushReplacement-ing a second Home over whatever was showing. Home registers itself while mounted, so "Done" at the end of the firmware and connect flows also returns to it (nav #3). - Deep links move to home_deep_links.dart: parent before child (the Settings sheet opens before Data & Privacy is pushed over it), every push through routeToPage, and a removed app or deleted conversation now says so instead of only logging (nav #18). - D1: the home chat bar, its mic, chat deep links and the Ask Omi / Voice quick actions push chat as a normal page (no fullscreenDialog). The chat bar is one labelled button. - D6: Android back on the Tasks, Conversations or Apps tab returns to Home before it leaves the app (root PopScope). - Settings: the language / speech profile / transcription model comparison runs once the reader is back on Home, not the moment the sheet opens (onboarding-home #25). - Startup prompts go through PromptQueue (onboarding-home #23): upgrade alert (a required update first), changelog + announcements, the CV1 tutorial (waits for the pendant), firmware announcements, the Plus-meter plans sheet (only when the meter crosses its threshold, once per session, showOmiSheet) and Bluetooth guidance. HomePromptGate holds the queue while phone recording, on a call or during a firmware update and pumps it when that clears; passive pendant capture does not hold it. - Handoffs applied: dead toggleDailySummaries call removed (lane 1a); sync, task-export and quick-action pushes use routeToPage (lanes 2, 4). Header controls use neutral tokens (INV-UI-1) and OmiHaptics. - New strings (7 keys, every locale): appNotFoundOrRemoved plus the startup failure and discard-recording strings used by the next commits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/startup): startup failure offers Try Again and Contact Support - The failure screen words the message by error class: a rejected build configuration (Env / startup-routing checks, now thrown as StartupConfigurationError) says the build is at fault and offers Contact Support; any other failure says to check the connection and offers Try Again, which re-runs start-up, plus Contact Support (onboarding-home #27). - main() moves start-up into _start() so Try Again can run it again; ServiceManager.init, which refuses a second call, runs once. - Localized without a localization scope (lookupAppLocalizations for the device locale, English fallback); tokens replace literal colours/sizes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/speech-profile): confirm before discarding a recording in progress - Back or swipe while the voice sample is recording asks "Discard Recording?" (Discard / Keep Recording) instead of dropping it (onboarding-home #26). - "Reconnecting…" is an info toast, not an error. - The help control is a labelled OmiIconButton ("How to take a good sample?") opening showOmiAlert; back is OmiBackButton. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/home): header pill and record button expose their actions - The device pill and the record button wrap their gestures in Semantics(excludeSemantics: true), which also dropped the tap and long-press actions; the Semantics node now carries onTap (and the record options long-press), so VoiceOver/TalkBack can activate them (onboarding-home #15, #24). - Phone-mic offline fallback notice is an OmiFeedback info toast. - Sweep: auth-gate spinner is OmiSpinner; welcome Continue uses OmiHaptics; OmiGlass OTA page formatted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): delete webhooks.dart, reachable only from the removed dev-tools widget Its one importer was GetDevToolsOptions in conversation_detail/widgets.dart, which nothing constructed and lane 1b deleted. The dead-code ratchet flags the now-unreachable file; the webhook settings themselves live in DeveloperModeProvider and the users API and are unaffected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/home): a link counts as opened once its page is pushed openHomeDeepLink awaited each push, so HomeNavigation.openRoute (and the notification-open telemetry behind it) only resolved when the reader closed the page. It now resolves once the destination is on screen. A missing app or conversation is an info toast, as the contract says; the contract names HomeNavigation.openRoute / returnHome. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 1 (ZCode GLM-5.3) - A memory's Undo restores only that memory: restoreLastDeletedMemory takes the toast's id, so a stale toast cannot restore a newer delete (test added). - A failed task delete no longer inserts a Home-only task into the Tasks list; the staged index goes straight to _restoreDeletedItem, which skips -1. - The conversation pending-delete window is 10 s, so an Undo toast that starts late behind other snack bars still gets its full time; the toast still commits early when it closes. - The integration page's button reads "Disconnect" instead of stripping the question mark from a translated sentence (broke ¿…? and ? locales). - mounted checks after the long awaits in the Create Template sheet and Wrapped 2025 generation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): keep memories_provider under its line-count ceiling Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/apps): app creation forms on the shared form pieces and primitives Submit App and Manage App draw their screenshot strip and GitHub URL card from app_form_fields (screenshots open as a modal viewer with a trailing close), use AppFormCard/section titles/input decoration, OmiSwitch rows for Public/Paid (no indigo/green thumbs), OmiButton for submit/update, a leading OmiBackButton, and l10n for every string. Update asks with showOmiConfirm and a verb button (Update App); Submit's confirm reads Submit App; Start Earning is an Omi sheet with Not Now. App metadata pickers are Omi sheets, App ID copy goes through OmiClipboard. API keys: adaptive dialogs, destructive Revoke confirm, OmiFeedback toasts, localized dates. Add MCP Server: OmiBackButton, OmiButton, OmiFeedback. Provider snackbars and picker titles localized. New l10n keys: generateDescription, selectImageFileTitle, selectThumbnailImageTitle, appIdNotFoundError, manifestRefreshedSuccess, manifestRefreshFailed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/apps): store home, filters and reviews on the primitives The store's filter sheet opens in the Omi sheet shell; header icon controls are labelled 44pt OmiIconButtons; empty search is OmiEmptyState. Reviews: OmiBackButton, adaptive reply dialog, OmiFeedback toasts, OmiButton busy state, localized Add/Edit Your Review and rating counts, readable (not grey-700) secondary text. App summary and capabilities card localized. Screenshot taps on the detail page open through openAppScreenshots (modal, trailing close). Review avatar palette drops its purple entry (INV-UI-1). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app/apps): Disable hides at once, commits after the Undo toast, Undo restores Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/lane-3): localized pricing value on Manage App and file-picker title in chat Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 2 (lane 5) - A throw while preparing the Omi firmware update (for example a corrupt download that fails zip extraction) now ends in the failed state instead of an installing spinner with back blocked and every prompt held. - Home drops the prompts it enqueued when it is disposed, so after sign-out and sign-in the new Home's announcements, device tutorial and firmware notices are not deduplicated against dead closures. - The speech step re-reads the microphone permission when the app resumes, so granting it in Settings clears the Open Settings row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): retire AnimatedLoadingButton Lane 3 moved its last callers (payments, app detail) onto OmiButton, which owns the busy state and recovers when the action throws; the dead-code ratchet flags the unreachable widget. Its throw-recovery test now runs against OmiButton directly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 3 (lane 3) - Try Again on a failed reply releases the composer when the failure was already cleared, and the first app message releases it even if chat closes mid-request (both could leave typed sends blocked). - Enabling an app again inside its 5 s disable Undo window cancels the pending disable instead of being overwritten when the toast ends; tests for that and for a refused disable. - Stripe setup names the selected country from the full list, not the picker's filtered one, and always stops polling on dispose. - The AI app generator shows a failed submit on the generated view. - OmiWebPage's Try Again reloads the page that failed, not the first URL; a malformed app home URL shows the error state instead of throwing. The reported Stripe double-submit is already prevented: OmiButton ignores taps while its onPressed future runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 4 (#17297 merge and rulings) - Share from a conversation row now behaves like the page: a private conversation asks first ("Anyone with the link can view") and goes back to private when the share sheet is dismissed (was: made public at once). - Developer Settings gains a Conversation Developer Tools switch (writes devModeEnabled), so release users can still reach Copy Conversation ID and Test Prompt; 2 keys in 49 locales. - The row's Separate… always disposes its controller; tests cover a confirmed separation from a row (the other recording, never the row) and the row share confirmation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): phone number verified date holds in every timezone The fixture was UTC midnight, which is Jan 1 in Pago Pago; CI runs the suite at UTC+14 and UTC-11. Local noon reads Jan 2 everywhere. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): conversation title and emoji share one line in the header The title field kept Material's 48pt input height, which pushed the title below the emoji; it is dense now and the one-line row centres both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): every voice-profile entry opens the guided introduction David's ruling (2026-09-24): the voice profile is the guided fill-in-the- blank flow from #14514 (sentence starters, review, then voice, memories and a goal). Two entry points still reached the old question-based page: the Settings/Profile Voice Profile row for anyone who already had a profile, and the capture card's "Teach Omi your voice". Both now open the guided flow. The old page is unreachable, so it goes, with its two widgets, the getDialog helper and the fade-in text widget only it used (dead-code ratchet). Open PR #12531 edits the old page and will need to drop those hunks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): Star and Share back in the conversation top bar; record conversation actions David, 2026-09-24 (overrides the earlier one-overflow ruling): - the conversation page's top bar is Ask Omi (the primary, filled), Star and Share as matching 44pt icon buttons, then one "…" holding Rename, Move to Folder, Recordings and the rest, Delete last and destructive; - one registered event, Conversation Action, records every conversation action with two closed enums: action (ask_omi, star, unstar, share, rename, move_folder, recordings_open, separate, delete, copy_transcript, …) and surface (top_bar, overflow, detail_body, row_long_press, row_swipe). No ids, titles or content. It goes through the C7 typed registry (contracts/analytics/events.json, generated Dart, a presence consumer) and AnalyticsManager, so consent and queueing apply. Tests cover the event shape for every action and the top bar, overflow, row long-press and row swipe call sites. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): the people chip names only named speakers David, 2026-09-24: never label a participant "Speaker 1". The conversation header's people chip now shows the first named participant ("You" for the account owner) and counts everyone else: "David + 3 others" (participantsSummary, an ICU plural in 49 locales). With nobody named it hides; a linked calendar event's attendees still stand in when the transcript names no one. The transcript's own per-line speaker labels are unchanged. Tests cover named, mixed and none. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: drop pointers to the deleted speech-profile page Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 5 (voice profile and people chip) - SpeechProfileProvider drove only the deleted question page; the guided introduction records through GuidedVoiceIO. Remove the provider, its registration, the onboarding back-step close() that no longer stopped anything, and its test. - Drop pointers to deleted files: the addressability baseline entry and the logout flow's covers (now sign_out.dart); a stale doc paragraph. - participantsSummary carries few/many forms so Polish, Czech and Slovak agree for 2-4 and 5+; other Slavic and Baltic locales use a count-agnostic "one more" form. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 5 天前 | |
feat(app): add System / Light / Dark appearance setting on mobile (#19378) * feat(app): add system-aware light and dark appearance * fix(app): restore light appearance contrast on v1 surfaces * fix(app): provide appearance in visual audit harness Failure-Class: none * test(app): support light mode in visual audit harness * fix(app): correct light-mode color contrast Failure-Class: none * fix(app): improve remaining light-mode contrast Failure-Class: none * fix(app): use palette tokens for light transcript bubbles Replace the two hardcoded light-mode bubble colours with surface2 and a pale alpha-blend of the speaker colour, so the mobile UX contract's colour-literal ratchet stays flat. Failure-Class: none Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 2 天前 | |
feat(app): add System / Light / Dark appearance setting on mobile (#19378) * feat(app): add system-aware light and dark appearance * fix(app): restore light appearance contrast on v1 surfaces * fix(app): provide appearance in visual audit harness Failure-Class: none * test(app): support light mode in visual audit harness * fix(app): correct light-mode color contrast Failure-Class: none * fix(app): improve remaining light-mode contrast Failure-Class: none * fix(app): use palette tokens for light transcript bubbles Replace the two hardcoded light-mode bubble colours with surface2 and a pale alpha-blend of the speaker colour, so the mobile UX contract's colour-literal ratchet stays flat. Failure-Class: none Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 2 天前 | |
feat(app): mobile UX consistency pass: shared primitives, contract and audit fixes (#18436) * feat(app/ui): add OmiColors tokens and stop using colorScheme.primary as a page background Add lib/ui/omi_tokens.dart: OmiColors (surfaces, text, neutral accent, status), OmiType ramp, OmiRadius, OmiSpacing, OmiMotion (honours Reduce Motion) and OmiHaptics. 53 sites read Theme.of(context).colorScheme.primary as a page or app bar background because the theme's primary was black. They now name OmiColors.surface0 (same black), so the theme can get a real accent. In the four files over the line-count threshold the explicit colour is dropped instead; the theme default is the same black. AppStyles and ResponsiveHelper get doc comments pointing at the tokens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): one theme and shared controls for navigation, buttons, sheets and page states Theme (lib/ui/omi_theme.dart, used by main.dart and the visual_audit harness so they cannot drift): a real dark ColorScheme with the white accent as primary, black scaffold, app bar theme (black, flat, centred 17pt semibold title), white progress indicators (the unstyled spinners were black on black), floating surface2 snack bars, surface1 dialogs and sheets, neutral switches. Material 2 stays on. MaterialApp.builder now syncs Intl.defaultLocale to the resolved locale. Components under lib/ui/components (barrel lib/ui/ui.dart): OmiBackButton / OmiCloseButton (platform glyph, localized label, 44pt target, circled variants), OmiIconButton (required label, tooltip), OmiButton (primary/secondary/destructive/tertiary, regular/compact, async loading that always clears), OmiSpinner, OmiLoadingState / OmiEmptyState / OmiErrorState, showOmiSheet and OmiSheetScaffold, OmiSwitch, OmiSettingsGroup / OmiSettingsRow / OmiSectionHeader, OmiSearchField, and omiPageRoute for code that needs a Route object. HeaderCircleButton is now a wrapper over OmiIconButton.filled and AnimatedLoadingButton over OmiButton (fixes the spinner that never cleared after a throw, and the 40pt target). find_device's two unstyled ElevatedButtons keep their black fill explicitly now that primary is white. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/settings): move data privacy, Asana and ClickUp settings onto the shared controls Proving set for the new primitives. Data & Privacy uses OmiBackButton, a section header and an OmiSettingsGroup of app rows, OmiEmptyState, and drops its accent tint for neutral tokens. Asana and ClickUp show an app bar with a back button and OmiLoadingState during their first load (the page used to be an invisible spinner with no way out), OmiSpinner for section loads, and tokens for their colours. The shared IntegrationSettingsPage header uses OmiBackButton and a labelled refresh OmiIconButton, and its disconnect control is an OmiButton.destructive. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): one adaptive confirmation system (showOmiConfirm/showOmiAlert) getDialog, ConfirmationDialog, OmiConfirmDialog and AppDialog become thin adapters over OmiAlertDialog: Cancel always shown and always closes, real CupertinoDialogActions on iOS (destructive red, safe choice bold), localized defaults, a destructive flag, and AppDialog.show no longer drops singleButton and onCancel. Non-page raw AlertDialogs in providers/widgets migrated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): OmiFeedback toasts and OmiClipboard One floating neutral toast at a time with contract timings (confirm 1.5 s, info 4 s, error 8 s with close, undo 5 s with no close, resolving whether the reader undid). Snackbars with actions no longer persist forever. The home shell registers its tab-bar clearance. AppSnackbar's ~180 call sites route through it. OmiClipboard refuses empty text and skips its toast on Android 13+ where the system already confirms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): OmiDateFormat, OmiDuration and shared speaker names Dates follow the app locale and the 24-hour setting; durations have one offset/compact/long vocabulary (the playback bar no longer drops hours). SpeakerNames is the single source for speaker labels in app and export: You / person / Omi / dense Speaker N, localized in copied transcripts. Legacy helpers delegate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): PromptQueue for startup and background prompts Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(checks): INV-UI-3 mobile UX contract ratchet Per-rule, per-file no-increase ratchet over hand-rolled back glyphs, PageRouteBuilder, raw sheets/dialogs/snackbars/clipboard, date patterns, colour/font-size/radius literals, raw spinners, '...' and hardcoded Text copy under app/lib, with an allow-comment escape hatch and a test proving each rule fires. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(app): mobile UX contract The numbered rules and the one primitive for each: leaving a surface, sheets, controls, destructive actions (D5), dialogs, feedback timing, copying, dates and durations, speakers, tokens, words (D3), settings (D4), page states, prompts, permissions and accessibility, plus the D1/D2/D6 navigation calls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/ui): dense speaker numbering on live and saved transcripts Both transcript renderers pass the conversation's segments to speakerLabel so Speaker N is numbered per conversation. Drops the dead duplicate formatChatTimestamp in widgets/transcript.dart, makes Material OmiAlertDialog scrollable, and points primitive docs at the contract's sections. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/l10n): contract words sweep Task(s) replaces Action Item(s)/To-Do in every locale (D3); Omi capitalised; '…' instead of '...'; search placeholders drop the ellipsis; Try Again, Not Now, OK, Title Case buttons. New keys: filterBySpeaker, notNow, discard, keepEditing, discardChangesTitle, discardChangesMessage. Translates the nine live-capture keys that only existed in English and removes five orphaned locale keys, so scripts/l10n.py check passes. The upgrade alert offers Not Now (postpone, not ignore-forever) and Update, honours barrierDismissible, and loses the emoji. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app/ui): export feedback, format and prompt primitives from the ui barrel; token colours Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app/ui): follow the Title Case clear-search label Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/l10n): strings for the conversation detail pass Link Event sheet, calendar event actions, search result navigation, playback labels, the share confirmation, the Tasks tab counts and empty state, and the speaker identify label, with translations for every locale. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): conversation detail on the shared primitives - D2: no body-wide swipe to another conversation and no PageRouteBuilder; Transcript/Summary(/Tasks) swipe as tabs, the Tasks tab exists only when there are tasks. - Header: OmiBackButton.circled plus same-size OmiIconButton circles (star, share, more) and a compact Ask Omi button that pushes Chat (D1). - Share asks before making a private conversation public and reverts it when the share sheet reports a dismissal. - Copy Conversation ID and Test Prompt only in developer mode; copies go through OmiClipboard, toasts through OmiFeedback. - Link Event and calendar event sheets on showOmiSheet with OmiDateFormat, l10n and full attendee names; visibility, summary template, name speaker and edit segment sheets on the shared shell. - Audio download sheet pops with its own context, offers Cancel and blocks back with PopScope. - Title: single line, Done, Untitled placeholder, saves on blur with an announced outcome; detail date/duration use OmiDateFormat/OmiDuration.compact (same rule as the list). - Tasks tab: tapping a task opens the shared task editor; labelled count pills; Tasks wording (D3); OmiEmptyState. - Edit segment sheet: speaker from SpeakerNames (You for the owner), Save and Cancel, discard guard. - Extract calendar sheets, search bar, transcript tab and tasks tab; delete dead ActionItemsListWidget, GetSheetTitle, GetDevToolsOptions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): keep INV-UI-3 allow comments on the flagged line Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): transcript bubbles and playback bar on the shared primitives Transcript: one SpeakerNames resolver per build (dense numbering, live people), no transcription-provider name, the start time as an OmiDuration offset, a labelled 44pt play button, labelled speaker targets, neutral user bubble and search highlight (INV-UI-1), localized 'translated by Omi', and getLastTranscript numbers speakers across the whole conversation. Playback bar: neutral surfaces instead of purple, a labelled 32pt play/pause in a 44pt target, a labelled stop button, OmiSpinner, OmiFeedback errors, the summary pill ellipsizes by layout instead of a hand-made '...', and the summary template sheet opens through showSummarizedAppsSheet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/onboarding): shared permission row, real progress, back and resume - OmiPermissionRow (lib/ui/components): reason, Allow -> system prompt, Allowed, Open Settings when the system will not ask again. One OnboardingPermissionsPanel drives both the first-run step and the returning-user interstitial; Continue never fires prompts and 'Always' location is no longer asked during first run. - Onboarding wrapper: placeholder pages removed, progress dots count the six real steps with a spoken 'Step N of M', PopScope makes system back step back, the last reached step is saved per account and resumed. - Consent step offers 'Use a Different Account' (sign out, back to start). - Acquisition survey is skippable; language step saves with a busy button and a Try Again error; picker selects on tap. - Steps share OnboardingCard (content scrolls, footer stays; min-height, no fixed text heights) and OmiButton; completion screen localized, purple gone. - Voice introduction: mic refusal shows the permission row; 'Skip Question' vs 'Skip'; device tutorial exits by a trailing close X. - Permission reasons rewritten, platform-neutral Location Services copy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/conversations): one delete path with Undo, row menu, bulk actions, recaps page - Conversation delete (list swipe, row menu, detail page) goes through one path: confirm unless opted out ("Don't ask again" is allowed because Undo backs it), then an Undo toast backed by the provider's pending-delete window (now 6 s, longer than the 5 s toast; the toast commits early when it closes) (D5). - Long-press on a conversation row opens one context menu: Open, Star/Unstar, Move to Folder, Share, Select, Delete. The selection bar gains Move and Delete (always confirmed) beside Merge. - Daily Recaps is its own pushed page with a back button; Home's View All pushes it and the Conversations tab never switches into a recap mode. Pull-to-refresh reloads recaps and a failed load offers Try Again. Recap counts are labelled. - One date filter for list and search, shown as a removable chip; the search field is seeded from the provider and uses OmiSearchField. - Dates and lengths via OmiDateFormat / OmiDuration (Today gets a header, the New badge sits beside the time, WAL/recording offsets keep their hours). - Discarded rows read "Discarded · 12s" with a plain-text snippet; blank titles read "Untitled Conversation". - Shared capture state table (capture_state_labels.dart) for the in-progress card; the processing card shows its real start time. - Local recording swipe always confirms. Sheets, dialogs, toasts, spinners and back buttons in conversations/, sync and recap pages move onto lib/ui. - Offline Sync is the one name for the device-storage sync screen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app/conversations): cover Undo-backed delete, row menu, recaps page and capture labels - Provider: the pending-delete window outlasts the Undo toast; undo restores, commit sends once, a regroup during the window keeps the row hidden. - Widgets: Undo restores / timeout commits / bulk toast counts; long-press menu entries and Select; untitled and discarded row titles; Today header; the date filter chip; Daily Recaps page back button, labelled counts, Try Again and pull-to-refresh. - Unit: the shared capture state table (an audio interruption is Paused). - The Home preview's row menu leaves out Select (no selection bar there); the in-progress card's photo count is labelled; last raw spinner and "..." in the capture card go. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/pairing): pairing recovery, one Bluetooth prompt, shared sheets - Find devices: a missing Bluetooth/location permission goes through the one BluetoothReadiness guidance prompt (Open Settings); the local 'Enable Bluetooth' dialogs are gone. - Nothing found: 'Scan Again', 'How to Pair' (connection guide), then 'Contact Support' instead of a lone mailto link. - Offline saved device: says how to wake it and 'Try Again' rescans (the old Retry did nothing). - Firmware compatibility warning is an acknowledge-only alert; the non-critical one keeps its opt-out row. - Device rows, guide cards and capture cards are real buttons with labels; battery shows a glyph and percentage with a spoken label, no emoji. - Connection guide, pairing steps, Ray-Ban picker/setup and Apple Watch setup use showOmiSheet; Apple Watch is only offered on iOS; 'I've Done This' becomes 'Done'. - Connect page: OmiBackButton, labelled settings button, and connecting returns to the existing Home (HomeNavigation.returnHome) instead of stacking a second one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): edit-sheet dirty guard and row long-press menu primitives showOmiEditSheet/OmiEditSheet own the drag so swipe-down, scrim tap, the close X and system back all ask "Discard changes?" while an editor is dirty (the framework's drag-to-dismiss pops without asking). showOmiRowMenu is the one long-press menu shape for list rows (Open, edit actions, Select, Delete). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): follow the warning token on the paused dot and the Conversation Map title Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/l10n): lane 2 strings for memories, tasks, goals and people Sheet titles without emoji, read-only memory hint, Undo toasts, task plural titles and counts, shared-task strings, memory review card and People page labels, with every locale translated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/memories): Undo-only deletes, openable read-only memories, guarded edit sheet - Every memory delete (swipe, long-press menu, edit sheet) goes through deleteMemoryWithUndo: no "cannot be undone" dialog, the shared OmiFeedback undo toast with no X, and a commit scoped to the memory the toast offered. The provider's pending window becomes an 8 s backstop that outlasts the toast. - Every memory opens: locked/superseded/ledger rows open the same sheet read-only with full text; swipe-delete only where editing is allowed. - Long-press menu Open / Edit / Delete. - Edit and new-memory sheets: explicit Cancel and Save, dirty guard, OmiButton. - Page: OmiBackButton, OmiSearchField, labelled 44pt header buttons, neutral FAB, OmiEmptyState / error state, management sheet on showOmiSheet with a confirm that names the action and toasts that reach the page after the sheet closes. - Memory graph: OmiBackButton, localized title, OmiLoading/Error/EmptyState, no purple. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/tasks): fixed swipe meaning, Undo deletes, guarded task and goal sheets - One swipe meaning on every task row at every indent: right completes (or reopens), left deletes. Indent/outdent move to the long-press menu (and drag keeps its horizontal indent); long-press shows Open / Mark Complete / Indent / Outdent / Select / Delete. - Single task and goal deletes are immediate with an Undo toast, backed by new staged-delete APIs on ActionItemsProvider and GoalsProvider (nothing hits the server until the toast closes). Bulk and clear-completed deletes confirm with a plural, verb-named dialog (the old one said "This will only remove deadlines"). - ActionItemFormSheet keeps its constructor; adds showActionItemFormSheet, explicit Cancel/Save, a close X in edit mode, a dirty guard and instant completion. The date picker and time picker are neutral (no purple) and locale/24-hour aware. - Home and Tasks share one GoalFormSheet (Cancel/Save, dirty guard, localized). - Tasks page: OmiSearchField "Search tasks", labelled overflow button, OmiEmptyState, neutral FAB/selection/drop indicators, localized "Exported to", counts with labels. - Selection bar, shared-tasks sheet and Home Today card on the primitives (44pt targets, rows open the task sheet). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/people): tapping a sample plays it; delete is a labelled control behind a confirm People page gets OmiBackButton, labelled Add/Help controls, an adaptive person dialog, OmiEmptyState/OmiLoadingState and confirms that name the action. The memory review card is localized with 44pt controls and tokens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): one header for live, processing and saved conversations; one media viewer - Live and processing pages share ConversationStateAppBar: the circled back button of the saved page and the state as the title (Recording / Paused / Reconnecting / Processing, one local mapping until lane 1a's shared capture_state_labels lands). No emoji status, no back button inside the title row. - Live page: remove the unreachable Summary tab and its TabController; Process Now is an OmiButton, mute a labelled OmiIconButton; the stop confirmation is showOmiConfirmWithOptOut with a verb; speaker names from SpeakerNames; photo-group time from OmiDateFormat and a localized photo count; tokens for the timeline and the upload indicator. - Processing page: one view (the transcript so far) under the shared header instead of Content/Summary tabs whose Summary only said 'Processing'. - Media viewer: always a full-screen modal with a trailing OmiCloseButton and swipe-down to close (MediaViewerPage.open), labelled share button, OmiSpinner; photo grids and the live timeline open it that way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/chat): contract composer, drawer and failed-reply retry Chat is a pushed page with a leading back (D1). Composer controls are labelled 44pt targets; Send that cannot send (offline, uploading) is drawn disabled with an offline hint. The apps drawer keeps Clear Chat at the bottom behind a destructive confirm, and each app has one labelled Disable (with Undo). A failed reply shows a localized error with Try Again that resends the user's message instead of raw server text. The feedback sheet is localized with a neutral Submit, thumbs read Helpful / Not Helpful, and copy/share analytics send the message length, not its text. Adds the lane-3 l10n keys for chat, apps and announcements. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/apps): one app action button, one consent dialog, Undo on disable AppListActionButton reads Open / Enable / View with the same meaning in every store list (no decorative Install). Enabling an app that works outside Omi asks one localized question naming what leaves Omi; Disable is immediate with a 5 s Undo on the detail page and in the chat drawer. The detail page moves onto the header, button, sheet, dialog and feedback primitives, splits its cards into widgets, and its cancel-subscription dialog reads Keep Subscription / Cancel Subscription. Referral and app home pages share one web page with a loading state, an error state with Try Again, and back within web history. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/announcements): answer-only dismissal through the prompt queue Announcements, feature screens and the changelog present through PromptQueue. The dialog's scrim no longer dismisses; the close X or the call to action marks an announcement seen, Not Now postpones it. The feature screen is a full-screen modal route with a trailing close; the changelog uses the shared sheet shell and error state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/ai-app-generator): one leading back, system back steps to the prompt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app): sweep conversation sheets and small widgets onto the primitives - Share via SMS and Create Template sheets use showOmiSheet, OmiSearchField, OmiButton, OmiSwitch, OmiEmptyState/OmiLoadingState and tokens; no purple (INV-UI-1). Create Template blocks back while the app is being created and opens the template picker from the navigator, not its own popped context. - Markdown and expandable text: neutral search highlight and link colour. - OmiSpinner and type/radius tokens in the speaker summary action, test prompts, photo image, person chip and waveform section. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(app/ui): edit sheets and row menus in the barrel and the contract Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): conversation detail UX contract cases Attendee names, detail duration rule, speaker numbering across the getLastTranscript window, transcript bubble chrome (no provider, offset time, 44pt labelled play), the shared capture state header, search bar position, the audio download sheet's own-context close/Cancel/back, the edit segment discard guard, and the media viewer's trailing close. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): show conversation developer tools in debug builds too No settings row writes devModeEnabled yet, so gating on it alone would hide Copy Conversation ID and Test Prompt from everyone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * wip(app): lane 3 checkpoint after spend-limit stop (uncommitted work preserved, not reviewed) * wip(app): lane 4 checkpoint after spend-limit stop (uncommitted work preserved, not reviewed) * wip(app): lane 5 checkpoint after spend-limit stop (uncommitted work preserved, not reviewed) * test(app/settings): device page stub getters and sign-out keep-list The one device page reads battery, charging and firmware getters the double-tap test stub lacked, and its connected picture pulses; stub the getters and turn on Reduce Motion so pumpAndSettle settles. Cover the Sign Out keep-list: paired device and display preferences survive, account values are cleared, and no credential is ever kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): name speaker sheet saves through the shared OmiButton Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/ai-app-generator): sweep the generator page onto the primitives App bar with a leading OmiBackButton on both steps; neutral accent instead of indigo/purple (INV-UI-1); OmiButton for Create App (busy state built in), OmiIconButton for send and regenerate-icon (labelled, 44pt), OmiSwitch for Make Public / Paid App, OmiSpinner, tokens for every colour, size and radius. Description and Features headings come from l10n; category and capability names use the shared localized titles. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/lane-3): plans sheet in the sheet shell, review fixes to the WIP checkpoint - chat: the quota plans sheet opens through showOmiSheet (lane 4 handoff). - chat evidence card: localized screen-reader hint. - capability apps page: localized category titles, token refresh colours. - const constructors and block-bodied ifs the WIP left for the analyzer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/chat): conversation links push through routeToPage; evidence test hosts l10n Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app): every push takes the platform route; shared-task link copy localized - routeToPage builds its route with omiPageRoute (defaultTargetPlatform) instead of dart:io Platform, the Foundation A handoff. - Deep links in AppShell, the People link in the name-speaker sheet and the firmware prompt in DeviceProvider push through routeToPage/omiPageRoute, never a bare MaterialPageRoute (iOS back swipe intact). - The shared-tasks deep link's not-found toast and unknown-sender fallback were hardcoded English; now sharedTasksLinkExpired and sharedTasksUnknownSender in all 49 locales. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): drop imports the ui barrel already provides Lane 2's edit-sheet and row-menu primitives joined the ui.dart barrel, which left seven direct imports redundant and pushed unnecessary_import over the analyzer baseline (CI's Dart Analyze & Tests failed on it). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/payments): labelled overflow menu, titled country picker sheet Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): people samples follow the one Voice Profile name Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/home): links open inside the one Home; startup prompts queue - Notification taps and app links open inside the Home already on screen (HomeNavigation.openRoute: pop to Home, select the tab, push the page) instead of pushReplacement-ing a second Home over whatever was showing. Home registers itself while mounted, so "Done" at the end of the firmware and connect flows also returns to it (nav #3). - Deep links move to home_deep_links.dart: parent before child (the Settings sheet opens before Data & Privacy is pushed over it), every push through routeToPage, and a removed app or deleted conversation now says so instead of only logging (nav #18). - D1: the home chat bar, its mic, chat deep links and the Ask Omi / Voice quick actions push chat as a normal page (no fullscreenDialog). The chat bar is one labelled button. - D6: Android back on the Tasks, Conversations or Apps tab returns to Home before it leaves the app (root PopScope). - Settings: the language / speech profile / transcription model comparison runs once the reader is back on Home, not the moment the sheet opens (onboarding-home #25). - Startup prompts go through PromptQueue (onboarding-home #23): upgrade alert (a required update first), changelog + announcements, the CV1 tutorial (waits for the pendant), firmware announcements, the Plus-meter plans sheet (only when the meter crosses its threshold, once per session, showOmiSheet) and Bluetooth guidance. HomePromptGate holds the queue while phone recording, on a call or during a firmware update and pumps it when that clears; passive pendant capture does not hold it. - Handoffs applied: dead toggleDailySummaries call removed (lane 1a); sync, task-export and quick-action pushes use routeToPage (lanes 2, 4). Header controls use neutral tokens (INV-UI-1) and OmiHaptics. - New strings (7 keys, every locale): appNotFoundOrRemoved plus the startup failure and discard-recording strings used by the next commits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/startup): startup failure offers Try Again and Contact Support - The failure screen words the message by error class: a rejected build configuration (Env / startup-routing checks, now thrown as StartupConfigurationError) says the build is at fault and offers Contact Support; any other failure says to check the connection and offers Try Again, which re-runs start-up, plus Contact Support (onboarding-home #27). - main() moves start-up into _start() so Try Again can run it again; ServiceManager.init, which refuses a second call, runs once. - Localized without a localization scope (lookupAppLocalizations for the device locale, English fallback); tokens replace literal colours/sizes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/speech-profile): confirm before discarding a recording in progress - Back or swipe while the voice sample is recording asks "Discard Recording?" (Discard / Keep Recording) instead of dropping it (onboarding-home #26). - "Reconnecting…" is an info toast, not an error. - The help control is a labelled OmiIconButton ("How to take a good sample?") opening showOmiAlert; back is OmiBackButton. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/home): header pill and record button expose their actions - The device pill and the record button wrap their gestures in Semantics(excludeSemantics: true), which also dropped the tap and long-press actions; the Semantics node now carries onTap (and the record options long-press), so VoiceOver/TalkBack can activate them (onboarding-home #15, #24). - Phone-mic offline fallback notice is an OmiFeedback info toast. - Sweep: auth-gate spinner is OmiSpinner; welcome Continue uses OmiHaptics; OmiGlass OTA page formatted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): delete webhooks.dart, reachable only from the removed dev-tools widget Its one importer was GetDevToolsOptions in conversation_detail/widgets.dart, which nothing constructed and lane 1b deleted. The dead-code ratchet flags the now-unreachable file; the webhook settings themselves live in DeveloperModeProvider and the users API and are unaffected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/home): a link counts as opened once its page is pushed openHomeDeepLink awaited each push, so HomeNavigation.openRoute (and the notification-open telemetry behind it) only resolved when the reader closed the page. It now resolves once the destination is on screen. A missing app or conversation is an info toast, as the contract says; the contract names HomeNavigation.openRoute / returnHome. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 1 (ZCode GLM-5.3) - A memory's Undo restores only that memory: restoreLastDeletedMemory takes the toast's id, so a stale toast cannot restore a newer delete (test added). - A failed task delete no longer inserts a Home-only task into the Tasks list; the staged index goes straight to _restoreDeletedItem, which skips -1. - The conversation pending-delete window is 10 s, so an Undo toast that starts late behind other snack bars still gets its full time; the toast still commits early when it closes. - The integration page's button reads "Disconnect" instead of stripping the question mark from a translated sentence (broke ¿…? and ? locales). - mounted checks after the long awaits in the Create Template sheet and Wrapped 2025 generation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): keep memories_provider under its line-count ceiling Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/apps): app creation forms on the shared form pieces and primitives Submit App and Manage App draw their screenshot strip and GitHub URL card from app_form_fields (screenshots open as a modal viewer with a trailing close), use AppFormCard/section titles/input decoration, OmiSwitch rows for Public/Paid (no indigo/green thumbs), OmiButton for submit/update, a leading OmiBackButton, and l10n for every string. Update asks with showOmiConfirm and a verb button (Update App); Submit's confirm reads Submit App; Start Earning is an Omi sheet with Not Now. App metadata pickers are Omi sheets, App ID copy goes through OmiClipboard. API keys: adaptive dialogs, destructive Revoke confirm, OmiFeedback toasts, localized dates. Add MCP Server: OmiBackButton, OmiButton, OmiFeedback. Provider snackbars and picker titles localized. New l10n keys: generateDescription, selectImageFileTitle, selectThumbnailImageTitle, appIdNotFoundError, manifestRefreshedSuccess, manifestRefreshFailed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/apps): store home, filters and reviews on the primitives The store's filter sheet opens in the Omi sheet shell; header icon controls are labelled 44pt OmiIconButtons; empty search is OmiEmptyState. Reviews: OmiBackButton, adaptive reply dialog, OmiFeedback toasts, OmiButton busy state, localized Add/Edit Your Review and rating counts, readable (not grey-700) secondary text. App summary and capabilities card localized. Screenshot taps on the detail page open through openAppScreenshots (modal, trailing close). Review avatar palette drops its purple entry (INV-UI-1). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app/apps): Disable hides at once, commits after the Undo toast, Undo restores Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/lane-3): localized pricing value on Manage App and file-picker title in chat Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 2 (lane 5) - A throw while preparing the Omi firmware update (for example a corrupt download that fails zip extraction) now ends in the failed state instead of an installing spinner with back blocked and every prompt held. - Home drops the prompts it enqueued when it is disposed, so after sign-out and sign-in the new Home's announcements, device tutorial and firmware notices are not deduplicated against dead closures. - The speech step re-reads the microphone permission when the app resumes, so granting it in Settings clears the Open Settings row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): retire AnimatedLoadingButton Lane 3 moved its last callers (payments, app detail) onto OmiButton, which owns the busy state and recovers when the action throws; the dead-code ratchet flags the unreachable widget. Its throw-recovery test now runs against OmiButton directly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 3 (lane 3) - Try Again on a failed reply releases the composer when the failure was already cleared, and the first app message releases it even if chat closes mid-request (both could leave typed sends blocked). - Enabling an app again inside its 5 s disable Undo window cancels the pending disable instead of being overwritten when the toast ends; tests for that and for a refused disable. - Stripe setup names the selected country from the full list, not the picker's filtered one, and always stops polling on dispose. - The AI app generator shows a failed submit on the generated view. - OmiWebPage's Try Again reloads the page that failed, not the first URL; a malformed app home URL shows the error state instead of throwing. The reported Stripe double-submit is already prevented: OmiButton ignores taps while its onPressed future runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 4 (#17297 merge and rulings) - Share from a conversation row now behaves like the page: a private conversation asks first ("Anyone with the link can view") and goes back to private when the share sheet is dismissed (was: made public at once). - Developer Settings gains a Conversation Developer Tools switch (writes devModeEnabled), so release users can still reach Copy Conversation ID and Test Prompt; 2 keys in 49 locales. - The row's Separate… always disposes its controller; tests cover a confirmed separation from a row (the other recording, never the row) and the row share confirmation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): phone number verified date holds in every timezone The fixture was UTC midnight, which is Jan 1 in Pago Pago; CI runs the suite at UTC+14 and UTC-11. Local noon reads Jan 2 everywhere. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): conversation title and emoji share one line in the header The title field kept Material's 48pt input height, which pushed the title below the emoji; it is dense now and the one-line row centres both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): every voice-profile entry opens the guided introduction David's ruling (2026-09-24): the voice profile is the guided fill-in-the- blank flow from #14514 (sentence starters, review, then voice, memories and a goal). Two entry points still reached the old question-based page: the Settings/Profile Voice Profile row for anyone who already had a profile, and the capture card's "Teach Omi your voice". Both now open the guided flow. The old page is unreachable, so it goes, with its two widgets, the getDialog helper and the fade-in text widget only it used (dead-code ratchet). Open PR #12531 edits the old page and will need to drop those hunks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): Star and Share back in the conversation top bar; record conversation actions David, 2026-09-24 (overrides the earlier one-overflow ruling): - the conversation page's top bar is Ask Omi (the primary, filled), Star and Share as matching 44pt icon buttons, then one "…" holding Rename, Move to Folder, Recordings and the rest, Delete last and destructive; - one registered event, Conversation Action, records every conversation action with two closed enums: action (ask_omi, star, unstar, share, rename, move_folder, recordings_open, separate, delete, copy_transcript, …) and surface (top_bar, overflow, detail_body, row_long_press, row_swipe). No ids, titles or content. It goes through the C7 typed registry (contracts/analytics/events.json, generated Dart, a presence consumer) and AnalyticsManager, so consent and queueing apply. Tests cover the event shape for every action and the top bar, overflow, row long-press and row swipe call sites. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): the people chip names only named speakers David, 2026-09-24: never label a participant "Speaker 1". The conversation header's people chip now shows the first named participant ("You" for the account owner) and counts everyone else: "David + 3 others" (participantsSummary, an ICU plural in 49 locales). With nobody named it hides; a linked calendar event's attendees still stand in when the transcript names no one. The transcript's own per-line speaker labels are unchanged. Tests cover named, mixed and none. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: drop pointers to the deleted speech-profile page Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 5 (voice profile and people chip) - SpeechProfileProvider drove only the deleted question page; the guided introduction records through GuidedVoiceIO. Remove the provider, its registration, the onboarding back-step close() that no longer stopped anything, and its test. - Drop pointers to deleted files: the addressability baseline entry and the logout flow's covers (now sign_out.dart); a stale doc paragraph. - participantsSummary carries few/many forms so Polish, Czech and Slovak agree for 2-4 and 5+; other Slavic and Baltic locales use a count-agnostic "one more" form. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 5 天前 | |
feat(app): add System / Light / Dark appearance setting on mobile (#19378) * feat(app): add system-aware light and dark appearance * fix(app): restore light appearance contrast on v1 surfaces * fix(app): provide appearance in visual audit harness Failure-Class: none * test(app): support light mode in visual audit harness * fix(app): correct light-mode color contrast Failure-Class: none * fix(app): improve remaining light-mode contrast Failure-Class: none * fix(app): use palette tokens for light transcript bubbles Replace the two hardcoded light-mode bubble colours with surface2 and a pale alpha-blend of the speaker colour, so the mobile UX contract's colour-literal ratchet stays flat. Failure-Class: none Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 2 天前 | |
feat(app): add System / Light / Dark appearance setting on mobile (#19378) * feat(app): add system-aware light and dark appearance * fix(app): restore light appearance contrast on v1 surfaces * fix(app): provide appearance in visual audit harness Failure-Class: none * test(app): support light mode in visual audit harness * fix(app): correct light-mode color contrast Failure-Class: none * fix(app): improve remaining light-mode contrast Failure-Class: none * fix(app): use palette tokens for light transcript bubbles Replace the two hardcoded light-mode bubble colours with surface2 and a pale alpha-blend of the speaker colour, so the mobile UX contract's colour-literal ratchet stays flat. Failure-Class: none Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 2 天前 | |
feat(app): mobile UX consistency pass: shared primitives, contract and audit fixes (#18436) * feat(app/ui): add OmiColors tokens and stop using colorScheme.primary as a page background Add lib/ui/omi_tokens.dart: OmiColors (surfaces, text, neutral accent, status), OmiType ramp, OmiRadius, OmiSpacing, OmiMotion (honours Reduce Motion) and OmiHaptics. 53 sites read Theme.of(context).colorScheme.primary as a page or app bar background because the theme's primary was black. They now name OmiColors.surface0 (same black), so the theme can get a real accent. In the four files over the line-count threshold the explicit colour is dropped instead; the theme default is the same black. AppStyles and ResponsiveHelper get doc comments pointing at the tokens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): one theme and shared controls for navigation, buttons, sheets and page states Theme (lib/ui/omi_theme.dart, used by main.dart and the visual_audit harness so they cannot drift): a real dark ColorScheme with the white accent as primary, black scaffold, app bar theme (black, flat, centred 17pt semibold title), white progress indicators (the unstyled spinners were black on black), floating surface2 snack bars, surface1 dialogs and sheets, neutral switches. Material 2 stays on. MaterialApp.builder now syncs Intl.defaultLocale to the resolved locale. Components under lib/ui/components (barrel lib/ui/ui.dart): OmiBackButton / OmiCloseButton (platform glyph, localized label, 44pt target, circled variants), OmiIconButton (required label, tooltip), OmiButton (primary/secondary/destructive/tertiary, regular/compact, async loading that always clears), OmiSpinner, OmiLoadingState / OmiEmptyState / OmiErrorState, showOmiSheet and OmiSheetScaffold, OmiSwitch, OmiSettingsGroup / OmiSettingsRow / OmiSectionHeader, OmiSearchField, and omiPageRoute for code that needs a Route object. HeaderCircleButton is now a wrapper over OmiIconButton.filled and AnimatedLoadingButton over OmiButton (fixes the spinner that never cleared after a throw, and the 40pt target). find_device's two unstyled ElevatedButtons keep their black fill explicitly now that primary is white. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/settings): move data privacy, Asana and ClickUp settings onto the shared controls Proving set for the new primitives. Data & Privacy uses OmiBackButton, a section header and an OmiSettingsGroup of app rows, OmiEmptyState, and drops its accent tint for neutral tokens. Asana and ClickUp show an app bar with a back button and OmiLoadingState during their first load (the page used to be an invisible spinner with no way out), OmiSpinner for section loads, and tokens for their colours. The shared IntegrationSettingsPage header uses OmiBackButton and a labelled refresh OmiIconButton, and its disconnect control is an OmiButton.destructive. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): one adaptive confirmation system (showOmiConfirm/showOmiAlert) getDialog, ConfirmationDialog, OmiConfirmDialog and AppDialog become thin adapters over OmiAlertDialog: Cancel always shown and always closes, real CupertinoDialogActions on iOS (destructive red, safe choice bold), localized defaults, a destructive flag, and AppDialog.show no longer drops singleButton and onCancel. Non-page raw AlertDialogs in providers/widgets migrated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): OmiFeedback toasts and OmiClipboard One floating neutral toast at a time with contract timings (confirm 1.5 s, info 4 s, error 8 s with close, undo 5 s with no close, resolving whether the reader undid). Snackbars with actions no longer persist forever. The home shell registers its tab-bar clearance. AppSnackbar's ~180 call sites route through it. OmiClipboard refuses empty text and skips its toast on Android 13+ where the system already confirms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): OmiDateFormat, OmiDuration and shared speaker names Dates follow the app locale and the 24-hour setting; durations have one offset/compact/long vocabulary (the playback bar no longer drops hours). SpeakerNames is the single source for speaker labels in app and export: You / person / Omi / dense Speaker N, localized in copied transcripts. Legacy helpers delegate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): PromptQueue for startup and background prompts Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(checks): INV-UI-3 mobile UX contract ratchet Per-rule, per-file no-increase ratchet over hand-rolled back glyphs, PageRouteBuilder, raw sheets/dialogs/snackbars/clipboard, date patterns, colour/font-size/radius literals, raw spinners, '...' and hardcoded Text copy under app/lib, with an allow-comment escape hatch and a test proving each rule fires. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(app): mobile UX contract The numbered rules and the one primitive for each: leaving a surface, sheets, controls, destructive actions (D5), dialogs, feedback timing, copying, dates and durations, speakers, tokens, words (D3), settings (D4), page states, prompts, permissions and accessibility, plus the D1/D2/D6 navigation calls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/ui): dense speaker numbering on live and saved transcripts Both transcript renderers pass the conversation's segments to speakerLabel so Speaker N is numbered per conversation. Drops the dead duplicate formatChatTimestamp in widgets/transcript.dart, makes Material OmiAlertDialog scrollable, and points primitive docs at the contract's sections. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/l10n): contract words sweep Task(s) replaces Action Item(s)/To-Do in every locale (D3); Omi capitalised; '…' instead of '...'; search placeholders drop the ellipsis; Try Again, Not Now, OK, Title Case buttons. New keys: filterBySpeaker, notNow, discard, keepEditing, discardChangesTitle, discardChangesMessage. Translates the nine live-capture keys that only existed in English and removes five orphaned locale keys, so scripts/l10n.py check passes. The upgrade alert offers Not Now (postpone, not ignore-forever) and Update, honours barrierDismissible, and loses the emoji. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app/ui): export feedback, format and prompt primitives from the ui barrel; token colours Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app/ui): follow the Title Case clear-search label Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/l10n): strings for the conversation detail pass Link Event sheet, calendar event actions, search result navigation, playback labels, the share confirmation, the Tasks tab counts and empty state, and the speaker identify label, with translations for every locale. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): conversation detail on the shared primitives - D2: no body-wide swipe to another conversation and no PageRouteBuilder; Transcript/Summary(/Tasks) swipe as tabs, the Tasks tab exists only when there are tasks. - Header: OmiBackButton.circled plus same-size OmiIconButton circles (star, share, more) and a compact Ask Omi button that pushes Chat (D1). - Share asks before making a private conversation public and reverts it when the share sheet reports a dismissal. - Copy Conversation ID and Test Prompt only in developer mode; copies go through OmiClipboard, toasts through OmiFeedback. - Link Event and calendar event sheets on showOmiSheet with OmiDateFormat, l10n and full attendee names; visibility, summary template, name speaker and edit segment sheets on the shared shell. - Audio download sheet pops with its own context, offers Cancel and blocks back with PopScope. - Title: single line, Done, Untitled placeholder, saves on blur with an announced outcome; detail date/duration use OmiDateFormat/OmiDuration.compact (same rule as the list). - Tasks tab: tapping a task opens the shared task editor; labelled count pills; Tasks wording (D3); OmiEmptyState. - Edit segment sheet: speaker from SpeakerNames (You for the owner), Save and Cancel, discard guard. - Extract calendar sheets, search bar, transcript tab and tasks tab; delete dead ActionItemsListWidget, GetSheetTitle, GetDevToolsOptions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): keep INV-UI-3 allow comments on the flagged line Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): transcript bubbles and playback bar on the shared primitives Transcript: one SpeakerNames resolver per build (dense numbering, live people), no transcription-provider name, the start time as an OmiDuration offset, a labelled 44pt play button, labelled speaker targets, neutral user bubble and search highlight (INV-UI-1), localized 'translated by Omi', and getLastTranscript numbers speakers across the whole conversation. Playback bar: neutral surfaces instead of purple, a labelled 32pt play/pause in a 44pt target, a labelled stop button, OmiSpinner, OmiFeedback errors, the summary pill ellipsizes by layout instead of a hand-made '...', and the summary template sheet opens through showSummarizedAppsSheet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/onboarding): shared permission row, real progress, back and resume - OmiPermissionRow (lib/ui/components): reason, Allow -> system prompt, Allowed, Open Settings when the system will not ask again. One OnboardingPermissionsPanel drives both the first-run step and the returning-user interstitial; Continue never fires prompts and 'Always' location is no longer asked during first run. - Onboarding wrapper: placeholder pages removed, progress dots count the six real steps with a spoken 'Step N of M', PopScope makes system back step back, the last reached step is saved per account and resumed. - Consent step offers 'Use a Different Account' (sign out, back to start). - Acquisition survey is skippable; language step saves with a busy button and a Try Again error; picker selects on tap. - Steps share OnboardingCard (content scrolls, footer stays; min-height, no fixed text heights) and OmiButton; completion screen localized, purple gone. - Voice introduction: mic refusal shows the permission row; 'Skip Question' vs 'Skip'; device tutorial exits by a trailing close X. - Permission reasons rewritten, platform-neutral Location Services copy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/conversations): one delete path with Undo, row menu, bulk actions, recaps page - Conversation delete (list swipe, row menu, detail page) goes through one path: confirm unless opted out ("Don't ask again" is allowed because Undo backs it), then an Undo toast backed by the provider's pending-delete window (now 6 s, longer than the 5 s toast; the toast commits early when it closes) (D5). - Long-press on a conversation row opens one context menu: Open, Star/Unstar, Move to Folder, Share, Select, Delete. The selection bar gains Move and Delete (always confirmed) beside Merge. - Daily Recaps is its own pushed page with a back button; Home's View All pushes it and the Conversations tab never switches into a recap mode. Pull-to-refresh reloads recaps and a failed load offers Try Again. Recap counts are labelled. - One date filter for list and search, shown as a removable chip; the search field is seeded from the provider and uses OmiSearchField. - Dates and lengths via OmiDateFormat / OmiDuration (Today gets a header, the New badge sits beside the time, WAL/recording offsets keep their hours). - Discarded rows read "Discarded · 12s" with a plain-text snippet; blank titles read "Untitled Conversation". - Shared capture state table (capture_state_labels.dart) for the in-progress card; the processing card shows its real start time. - Local recording swipe always confirms. Sheets, dialogs, toasts, spinners and back buttons in conversations/, sync and recap pages move onto lib/ui. - Offline Sync is the one name for the device-storage sync screen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app/conversations): cover Undo-backed delete, row menu, recaps page and capture labels - Provider: the pending-delete window outlasts the Undo toast; undo restores, commit sends once, a regroup during the window keeps the row hidden. - Widgets: Undo restores / timeout commits / bulk toast counts; long-press menu entries and Select; untitled and discarded row titles; Today header; the date filter chip; Daily Recaps page back button, labelled counts, Try Again and pull-to-refresh. - Unit: the shared capture state table (an audio interruption is Paused). - The Home preview's row menu leaves out Select (no selection bar there); the in-progress card's photo count is labelled; last raw spinner and "..." in the capture card go. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/pairing): pairing recovery, one Bluetooth prompt, shared sheets - Find devices: a missing Bluetooth/location permission goes through the one BluetoothReadiness guidance prompt (Open Settings); the local 'Enable Bluetooth' dialogs are gone. - Nothing found: 'Scan Again', 'How to Pair' (connection guide), then 'Contact Support' instead of a lone mailto link. - Offline saved device: says how to wake it and 'Try Again' rescans (the old Retry did nothing). - Firmware compatibility warning is an acknowledge-only alert; the non-critical one keeps its opt-out row. - Device rows, guide cards and capture cards are real buttons with labels; battery shows a glyph and percentage with a spoken label, no emoji. - Connection guide, pairing steps, Ray-Ban picker/setup and Apple Watch setup use showOmiSheet; Apple Watch is only offered on iOS; 'I've Done This' becomes 'Done'. - Connect page: OmiBackButton, labelled settings button, and connecting returns to the existing Home (HomeNavigation.returnHome) instead of stacking a second one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): edit-sheet dirty guard and row long-press menu primitives showOmiEditSheet/OmiEditSheet own the drag so swipe-down, scrim tap, the close X and system back all ask "Discard changes?" while an editor is dirty (the framework's drag-to-dismiss pops without asking). showOmiRowMenu is the one long-press menu shape for list rows (Open, edit actions, Select, Delete). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): follow the warning token on the paused dot and the Conversation Map title Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/l10n): lane 2 strings for memories, tasks, goals and people Sheet titles without emoji, read-only memory hint, Undo toasts, task plural titles and counts, shared-task strings, memory review card and People page labels, with every locale translated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/memories): Undo-only deletes, openable read-only memories, guarded edit sheet - Every memory delete (swipe, long-press menu, edit sheet) goes through deleteMemoryWithUndo: no "cannot be undone" dialog, the shared OmiFeedback undo toast with no X, and a commit scoped to the memory the toast offered. The provider's pending window becomes an 8 s backstop that outlasts the toast. - Every memory opens: locked/superseded/ledger rows open the same sheet read-only with full text; swipe-delete only where editing is allowed. - Long-press menu Open / Edit / Delete. - Edit and new-memory sheets: explicit Cancel and Save, dirty guard, OmiButton. - Page: OmiBackButton, OmiSearchField, labelled 44pt header buttons, neutral FAB, OmiEmptyState / error state, management sheet on showOmiSheet with a confirm that names the action and toasts that reach the page after the sheet closes. - Memory graph: OmiBackButton, localized title, OmiLoading/Error/EmptyState, no purple. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/tasks): fixed swipe meaning, Undo deletes, guarded task and goal sheets - One swipe meaning on every task row at every indent: right completes (or reopens), left deletes. Indent/outdent move to the long-press menu (and drag keeps its horizontal indent); long-press shows Open / Mark Complete / Indent / Outdent / Select / Delete. - Single task and goal deletes are immediate with an Undo toast, backed by new staged-delete APIs on ActionItemsProvider and GoalsProvider (nothing hits the server until the toast closes). Bulk and clear-completed deletes confirm with a plural, verb-named dialog (the old one said "This will only remove deadlines"). - ActionItemFormSheet keeps its constructor; adds showActionItemFormSheet, explicit Cancel/Save, a close X in edit mode, a dirty guard and instant completion. The date picker and time picker are neutral (no purple) and locale/24-hour aware. - Home and Tasks share one GoalFormSheet (Cancel/Save, dirty guard, localized). - Tasks page: OmiSearchField "Search tasks", labelled overflow button, OmiEmptyState, neutral FAB/selection/drop indicators, localized "Exported to", counts with labels. - Selection bar, shared-tasks sheet and Home Today card on the primitives (44pt targets, rows open the task sheet). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/people): tapping a sample plays it; delete is a labelled control behind a confirm People page gets OmiBackButton, labelled Add/Help controls, an adaptive person dialog, OmiEmptyState/OmiLoadingState and confirms that name the action. The memory review card is localized with 44pt controls and tokens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): one header for live, processing and saved conversations; one media viewer - Live and processing pages share ConversationStateAppBar: the circled back button of the saved page and the state as the title (Recording / Paused / Reconnecting / Processing, one local mapping until lane 1a's shared capture_state_labels lands). No emoji status, no back button inside the title row. - Live page: remove the unreachable Summary tab and its TabController; Process Now is an OmiButton, mute a labelled OmiIconButton; the stop confirmation is showOmiConfirmWithOptOut with a verb; speaker names from SpeakerNames; photo-group time from OmiDateFormat and a localized photo count; tokens for the timeline and the upload indicator. - Processing page: one view (the transcript so far) under the shared header instead of Content/Summary tabs whose Summary only said 'Processing'. - Media viewer: always a full-screen modal with a trailing OmiCloseButton and swipe-down to close (MediaViewerPage.open), labelled share button, OmiSpinner; photo grids and the live timeline open it that way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/chat): contract composer, drawer and failed-reply retry Chat is a pushed page with a leading back (D1). Composer controls are labelled 44pt targets; Send that cannot send (offline, uploading) is drawn disabled with an offline hint. The apps drawer keeps Clear Chat at the bottom behind a destructive confirm, and each app has one labelled Disable (with Undo). A failed reply shows a localized error with Try Again that resends the user's message instead of raw server text. The feedback sheet is localized with a neutral Submit, thumbs read Helpful / Not Helpful, and copy/share analytics send the message length, not its text. Adds the lane-3 l10n keys for chat, apps and announcements. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/apps): one app action button, one consent dialog, Undo on disable AppListActionButton reads Open / Enable / View with the same meaning in every store list (no decorative Install). Enabling an app that works outside Omi asks one localized question naming what leaves Omi; Disable is immediate with a 5 s Undo on the detail page and in the chat drawer. The detail page moves onto the header, button, sheet, dialog and feedback primitives, splits its cards into widgets, and its cancel-subscription dialog reads Keep Subscription / Cancel Subscription. Referral and app home pages share one web page with a loading state, an error state with Try Again, and back within web history. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/announcements): answer-only dismissal through the prompt queue Announcements, feature screens and the changelog present through PromptQueue. The dialog's scrim no longer dismisses; the close X or the call to action marks an announcement seen, Not Now postpones it. The feature screen is a full-screen modal route with a trailing close; the changelog uses the shared sheet shell and error state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/ai-app-generator): one leading back, system back steps to the prompt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app): sweep conversation sheets and small widgets onto the primitives - Share via SMS and Create Template sheets use showOmiSheet, OmiSearchField, OmiButton, OmiSwitch, OmiEmptyState/OmiLoadingState and tokens; no purple (INV-UI-1). Create Template blocks back while the app is being created and opens the template picker from the navigator, not its own popped context. - Markdown and expandable text: neutral search highlight and link colour. - OmiSpinner and type/radius tokens in the speaker summary action, test prompts, photo image, person chip and waveform section. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(app/ui): edit sheets and row menus in the barrel and the contract Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): conversation detail UX contract cases Attendee names, detail duration rule, speaker numbering across the getLastTranscript window, transcript bubble chrome (no provider, offset time, 44pt labelled play), the shared capture state header, search bar position, the audio download sheet's own-context close/Cancel/back, the edit segment discard guard, and the media viewer's trailing close. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): show conversation developer tools in debug builds too No settings row writes devModeEnabled yet, so gating on it alone would hide Copy Conversation ID and Test Prompt from everyone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * wip(app): lane 3 checkpoint after spend-limit stop (uncommitted work preserved, not reviewed) * wip(app): lane 4 checkpoint after spend-limit stop (uncommitted work preserved, not reviewed) * wip(app): lane 5 checkpoint after spend-limit stop (uncommitted work preserved, not reviewed) * test(app/settings): device page stub getters and sign-out keep-list The one device page reads battery, charging and firmware getters the double-tap test stub lacked, and its connected picture pulses; stub the getters and turn on Reduce Motion so pumpAndSettle settles. Cover the Sign Out keep-list: paired device and display preferences survive, account values are cleared, and no credential is ever kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): name speaker sheet saves through the shared OmiButton Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/ai-app-generator): sweep the generator page onto the primitives App bar with a leading OmiBackButton on both steps; neutral accent instead of indigo/purple (INV-UI-1); OmiButton for Create App (busy state built in), OmiIconButton for send and regenerate-icon (labelled, 44pt), OmiSwitch for Make Public / Paid App, OmiSpinner, tokens for every colour, size and radius. Description and Features headings come from l10n; category and capability names use the shared localized titles. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/lane-3): plans sheet in the sheet shell, review fixes to the WIP checkpoint - chat: the quota plans sheet opens through showOmiSheet (lane 4 handoff). - chat evidence card: localized screen-reader hint. - capability apps page: localized category titles, token refresh colours. - const constructors and block-bodied ifs the WIP left for the analyzer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/chat): conversation links push through routeToPage; evidence test hosts l10n Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app): every push takes the platform route; shared-task link copy localized - routeToPage builds its route with omiPageRoute (defaultTargetPlatform) instead of dart:io Platform, the Foundation A handoff. - Deep links in AppShell, the People link in the name-speaker sheet and the firmware prompt in DeviceProvider push through routeToPage/omiPageRoute, never a bare MaterialPageRoute (iOS back swipe intact). - The shared-tasks deep link's not-found toast and unknown-sender fallback were hardcoded English; now sharedTasksLinkExpired and sharedTasksUnknownSender in all 49 locales. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): drop imports the ui barrel already provides Lane 2's edit-sheet and row-menu primitives joined the ui.dart barrel, which left seven direct imports redundant and pushed unnecessary_import over the analyzer baseline (CI's Dart Analyze & Tests failed on it). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/payments): labelled overflow menu, titled country picker sheet Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): people samples follow the one Voice Profile name Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/home): links open inside the one Home; startup prompts queue - Notification taps and app links open inside the Home already on screen (HomeNavigation.openRoute: pop to Home, select the tab, push the page) instead of pushReplacement-ing a second Home over whatever was showing. Home registers itself while mounted, so "Done" at the end of the firmware and connect flows also returns to it (nav #3). - Deep links move to home_deep_links.dart: parent before child (the Settings sheet opens before Data & Privacy is pushed over it), every push through routeToPage, and a removed app or deleted conversation now says so instead of only logging (nav #18). - D1: the home chat bar, its mic, chat deep links and the Ask Omi / Voice quick actions push chat as a normal page (no fullscreenDialog). The chat bar is one labelled button. - D6: Android back on the Tasks, Conversations or Apps tab returns to Home before it leaves the app (root PopScope). - Settings: the language / speech profile / transcription model comparison runs once the reader is back on Home, not the moment the sheet opens (onboarding-home #25). - Startup prompts go through PromptQueue (onboarding-home #23): upgrade alert (a required update first), changelog + announcements, the CV1 tutorial (waits for the pendant), firmware announcements, the Plus-meter plans sheet (only when the meter crosses its threshold, once per session, showOmiSheet) and Bluetooth guidance. HomePromptGate holds the queue while phone recording, on a call or during a firmware update and pumps it when that clears; passive pendant capture does not hold it. - Handoffs applied: dead toggleDailySummaries call removed (lane 1a); sync, task-export and quick-action pushes use routeToPage (lanes 2, 4). Header controls use neutral tokens (INV-UI-1) and OmiHaptics. - New strings (7 keys, every locale): appNotFoundOrRemoved plus the startup failure and discard-recording strings used by the next commits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/startup): startup failure offers Try Again and Contact Support - The failure screen words the message by error class: a rejected build configuration (Env / startup-routing checks, now thrown as StartupConfigurationError) says the build is at fault and offers Contact Support; any other failure says to check the connection and offers Try Again, which re-runs start-up, plus Contact Support (onboarding-home #27). - main() moves start-up into _start() so Try Again can run it again; ServiceManager.init, which refuses a second call, runs once. - Localized without a localization scope (lookupAppLocalizations for the device locale, English fallback); tokens replace literal colours/sizes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/speech-profile): confirm before discarding a recording in progress - Back or swipe while the voice sample is recording asks "Discard Recording?" (Discard / Keep Recording) instead of dropping it (onboarding-home #26). - "Reconnecting…" is an info toast, not an error. - The help control is a labelled OmiIconButton ("How to take a good sample?") opening showOmiAlert; back is OmiBackButton. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/home): header pill and record button expose their actions - The device pill and the record button wrap their gestures in Semantics(excludeSemantics: true), which also dropped the tap and long-press actions; the Semantics node now carries onTap (and the record options long-press), so VoiceOver/TalkBack can activate them (onboarding-home #15, #24). - Phone-mic offline fallback notice is an OmiFeedback info toast. - Sweep: auth-gate spinner is OmiSpinner; welcome Continue uses OmiHaptics; OmiGlass OTA page formatted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): delete webhooks.dart, reachable only from the removed dev-tools widget Its one importer was GetDevToolsOptions in conversation_detail/widgets.dart, which nothing constructed and lane 1b deleted. The dead-code ratchet flags the now-unreachable file; the webhook settings themselves live in DeveloperModeProvider and the users API and are unaffected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/home): a link counts as opened once its page is pushed openHomeDeepLink awaited each push, so HomeNavigation.openRoute (and the notification-open telemetry behind it) only resolved when the reader closed the page. It now resolves once the destination is on screen. A missing app or conversation is an info toast, as the contract says; the contract names HomeNavigation.openRoute / returnHome. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 1 (ZCode GLM-5.3) - A memory's Undo restores only that memory: restoreLastDeletedMemory takes the toast's id, so a stale toast cannot restore a newer delete (test added). - A failed task delete no longer inserts a Home-only task into the Tasks list; the staged index goes straight to _restoreDeletedItem, which skips -1. - The conversation pending-delete window is 10 s, so an Undo toast that starts late behind other snack bars still gets its full time; the toast still commits early when it closes. - The integration page's button reads "Disconnect" instead of stripping the question mark from a translated sentence (broke ¿…? and ? locales). - mounted checks after the long awaits in the Create Template sheet and Wrapped 2025 generation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): keep memories_provider under its line-count ceiling Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/apps): app creation forms on the shared form pieces and primitives Submit App and Manage App draw their screenshot strip and GitHub URL card from app_form_fields (screenshots open as a modal viewer with a trailing close), use AppFormCard/section titles/input decoration, OmiSwitch rows for Public/Paid (no indigo/green thumbs), OmiButton for submit/update, a leading OmiBackButton, and l10n for every string. Update asks with showOmiConfirm and a verb button (Update App); Submit's confirm reads Submit App; Start Earning is an Omi sheet with Not Now. App metadata pickers are Omi sheets, App ID copy goes through OmiClipboard. API keys: adaptive dialogs, destructive Revoke confirm, OmiFeedback toasts, localized dates. Add MCP Server: OmiBackButton, OmiButton, OmiFeedback. Provider snackbars and picker titles localized. New l10n keys: generateDescription, selectImageFileTitle, selectThumbnailImageTitle, appIdNotFoundError, manifestRefreshedSuccess, manifestRefreshFailed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/apps): store home, filters and reviews on the primitives The store's filter sheet opens in the Omi sheet shell; header icon controls are labelled 44pt OmiIconButtons; empty search is OmiEmptyState. Reviews: OmiBackButton, adaptive reply dialog, OmiFeedback toasts, OmiButton busy state, localized Add/Edit Your Review and rating counts, readable (not grey-700) secondary text. App summary and capabilities card localized. Screenshot taps on the detail page open through openAppScreenshots (modal, trailing close). Review avatar palette drops its purple entry (INV-UI-1). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app/apps): Disable hides at once, commits after the Undo toast, Undo restores Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/lane-3): localized pricing value on Manage App and file-picker title in chat Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 2 (lane 5) - A throw while preparing the Omi firmware update (for example a corrupt download that fails zip extraction) now ends in the failed state instead of an installing spinner with back blocked and every prompt held. - Home drops the prompts it enqueued when it is disposed, so after sign-out and sign-in the new Home's announcements, device tutorial and firmware notices are not deduplicated against dead closures. - The speech step re-reads the microphone permission when the app resumes, so granting it in Settings clears the Open Settings row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): retire AnimatedLoadingButton Lane 3 moved its last callers (payments, app detail) onto OmiButton, which owns the busy state and recovers when the action throws; the dead-code ratchet flags the unreachable widget. Its throw-recovery test now runs against OmiButton directly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 3 (lane 3) - Try Again on a failed reply releases the composer when the failure was already cleared, and the first app message releases it even if chat closes mid-request (both could leave typed sends blocked). - Enabling an app again inside its 5 s disable Undo window cancels the pending disable instead of being overwritten when the toast ends; tests for that and for a refused disable. - Stripe setup names the selected country from the full list, not the picker's filtered one, and always stops polling on dispose. - The AI app generator shows a failed submit on the generated view. - OmiWebPage's Try Again reloads the page that failed, not the first URL; a malformed app home URL shows the error state instead of throwing. The reported Stripe double-submit is already prevented: OmiButton ignores taps while its onPressed future runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 4 (#17297 merge and rulings) - Share from a conversation row now behaves like the page: a private conversation asks first ("Anyone with the link can view") and goes back to private when the share sheet is dismissed (was: made public at once). - Developer Settings gains a Conversation Developer Tools switch (writes devModeEnabled), so release users can still reach Copy Conversation ID and Test Prompt; 2 keys in 49 locales. - The row's Separate… always disposes its controller; tests cover a confirmed separation from a row (the other recording, never the row) and the row share confirmation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): phone number verified date holds in every timezone The fixture was UTC midnight, which is Jan 1 in Pago Pago; CI runs the suite at UTC+14 and UTC-11. Local noon reads Jan 2 everywhere. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): conversation title and emoji share one line in the header The title field kept Material's 48pt input height, which pushed the title below the emoji; it is dense now and the one-line row centres both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): every voice-profile entry opens the guided introduction David's ruling (2026-09-24): the voice profile is the guided fill-in-the- blank flow from #14514 (sentence starters, review, then voice, memories and a goal). Two entry points still reached the old question-based page: the Settings/Profile Voice Profile row for anyone who already had a profile, and the capture card's "Teach Omi your voice". Both now open the guided flow. The old page is unreachable, so it goes, with its two widgets, the getDialog helper and the fade-in text widget only it used (dead-code ratchet). Open PR #12531 edits the old page and will need to drop those hunks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): Star and Share back in the conversation top bar; record conversation actions David, 2026-09-24 (overrides the earlier one-overflow ruling): - the conversation page's top bar is Ask Omi (the primary, filled), Star and Share as matching 44pt icon buttons, then one "…" holding Rename, Move to Folder, Recordings and the rest, Delete last and destructive; - one registered event, Conversation Action, records every conversation action with two closed enums: action (ask_omi, star, unstar, share, rename, move_folder, recordings_open, separate, delete, copy_transcript, …) and surface (top_bar, overflow, detail_body, row_long_press, row_swipe). No ids, titles or content. It goes through the C7 typed registry (contracts/analytics/events.json, generated Dart, a presence consumer) and AnalyticsManager, so consent and queueing apply. Tests cover the event shape for every action and the top bar, overflow, row long-press and row swipe call sites. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): the people chip names only named speakers David, 2026-09-24: never label a participant "Speaker 1". The conversation header's people chip now shows the first named participant ("You" for the account owner) and counts everyone else: "David + 3 others" (participantsSummary, an ICU plural in 49 locales). With nobody named it hides; a linked calendar event's attendees still stand in when the transcript names no one. The transcript's own per-line speaker labels are unchanged. Tests cover named, mixed and none. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: drop pointers to the deleted speech-profile page Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 5 (voice profile and people chip) - SpeechProfileProvider drove only the deleted question page; the guided introduction records through GuidedVoiceIO. Remove the provider, its registration, the onboarding back-step close() that no longer stopped anything, and its test. - Drop pointers to deleted files: the addressability baseline entry and the logout flow's covers (now sign_out.dart); a stale doc paragraph. - participantsSummary carries few/many forms so Polish, Czech and Slovak agree for 2-4 and 5+; other Slavic and Baltic locales use a count-agnostic "one more" form. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 5 天前 | |
feat(app): add System / Light / Dark appearance setting on mobile (#19378) * feat(app): add system-aware light and dark appearance * fix(app): restore light appearance contrast on v1 surfaces * fix(app): provide appearance in visual audit harness Failure-Class: none * test(app): support light mode in visual audit harness * fix(app): correct light-mode color contrast Failure-Class: none * fix(app): improve remaining light-mode contrast Failure-Class: none * fix(app): use palette tokens for light transcript bubbles Replace the two hardcoded light-mode bubble colours with surface2 and a pale alpha-blend of the speaker colour, so the mobile UX contract's colour-literal ratchet stays flat. Failure-Class: none Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 2 天前 | |
chore(app): migrate deprecated Flutter APIs in device_widget.dart | 2 个月前 | |
feat(app): add System / Light / Dark appearance setting on mobile (#19378) * feat(app): add system-aware light and dark appearance * fix(app): restore light appearance contrast on v1 surfaces * fix(app): provide appearance in visual audit harness Failure-Class: none * test(app): support light mode in visual audit harness * fix(app): correct light-mode color contrast Failure-Class: none * fix(app): improve remaining light-mode contrast Failure-Class: none * fix(app): use palette tokens for light transcript bubbles Replace the two hardcoded light-mode bubble colours with surface2 and a pale alpha-blend of the speaker colour, so the mobile UX contract's colour-literal ratchet stays flat. Failure-Class: none Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 2 天前 | |
feat(app): mobile UX consistency pass: shared primitives, contract and audit fixes (#18436) * feat(app/ui): add OmiColors tokens and stop using colorScheme.primary as a page background Add lib/ui/omi_tokens.dart: OmiColors (surfaces, text, neutral accent, status), OmiType ramp, OmiRadius, OmiSpacing, OmiMotion (honours Reduce Motion) and OmiHaptics. 53 sites read Theme.of(context).colorScheme.primary as a page or app bar background because the theme's primary was black. They now name OmiColors.surface0 (same black), so the theme can get a real accent. In the four files over the line-count threshold the explicit colour is dropped instead; the theme default is the same black. AppStyles and ResponsiveHelper get doc comments pointing at the tokens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): one theme and shared controls for navigation, buttons, sheets and page states Theme (lib/ui/omi_theme.dart, used by main.dart and the visual_audit harness so they cannot drift): a real dark ColorScheme with the white accent as primary, black scaffold, app bar theme (black, flat, centred 17pt semibold title), white progress indicators (the unstyled spinners were black on black), floating surface2 snack bars, surface1 dialogs and sheets, neutral switches. Material 2 stays on. MaterialApp.builder now syncs Intl.defaultLocale to the resolved locale. Components under lib/ui/components (barrel lib/ui/ui.dart): OmiBackButton / OmiCloseButton (platform glyph, localized label, 44pt target, circled variants), OmiIconButton (required label, tooltip), OmiButton (primary/secondary/destructive/tertiary, regular/compact, async loading that always clears), OmiSpinner, OmiLoadingState / OmiEmptyState / OmiErrorState, showOmiSheet and OmiSheetScaffold, OmiSwitch, OmiSettingsGroup / OmiSettingsRow / OmiSectionHeader, OmiSearchField, and omiPageRoute for code that needs a Route object. HeaderCircleButton is now a wrapper over OmiIconButton.filled and AnimatedLoadingButton over OmiButton (fixes the spinner that never cleared after a throw, and the 40pt target). find_device's two unstyled ElevatedButtons keep their black fill explicitly now that primary is white. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/settings): move data privacy, Asana and ClickUp settings onto the shared controls Proving set for the new primitives. Data & Privacy uses OmiBackButton, a section header and an OmiSettingsGroup of app rows, OmiEmptyState, and drops its accent tint for neutral tokens. Asana and ClickUp show an app bar with a back button and OmiLoadingState during their first load (the page used to be an invisible spinner with no way out), OmiSpinner for section loads, and tokens for their colours. The shared IntegrationSettingsPage header uses OmiBackButton and a labelled refresh OmiIconButton, and its disconnect control is an OmiButton.destructive. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): one adaptive confirmation system (showOmiConfirm/showOmiAlert) getDialog, ConfirmationDialog, OmiConfirmDialog and AppDialog become thin adapters over OmiAlertDialog: Cancel always shown and always closes, real CupertinoDialogActions on iOS (destructive red, safe choice bold), localized defaults, a destructive flag, and AppDialog.show no longer drops singleButton and onCancel. Non-page raw AlertDialogs in providers/widgets migrated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): OmiFeedback toasts and OmiClipboard One floating neutral toast at a time with contract timings (confirm 1.5 s, info 4 s, error 8 s with close, undo 5 s with no close, resolving whether the reader undid). Snackbars with actions no longer persist forever. The home shell registers its tab-bar clearance. AppSnackbar's ~180 call sites route through it. OmiClipboard refuses empty text and skips its toast on Android 13+ where the system already confirms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): OmiDateFormat, OmiDuration and shared speaker names Dates follow the app locale and the 24-hour setting; durations have one offset/compact/long vocabulary (the playback bar no longer drops hours). SpeakerNames is the single source for speaker labels in app and export: You / person / Omi / dense Speaker N, localized in copied transcripts. Legacy helpers delegate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): PromptQueue for startup and background prompts Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(checks): INV-UI-3 mobile UX contract ratchet Per-rule, per-file no-increase ratchet over hand-rolled back glyphs, PageRouteBuilder, raw sheets/dialogs/snackbars/clipboard, date patterns, colour/font-size/radius literals, raw spinners, '...' and hardcoded Text copy under app/lib, with an allow-comment escape hatch and a test proving each rule fires. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(app): mobile UX contract The numbered rules and the one primitive for each: leaving a surface, sheets, controls, destructive actions (D5), dialogs, feedback timing, copying, dates and durations, speakers, tokens, words (D3), settings (D4), page states, prompts, permissions and accessibility, plus the D1/D2/D6 navigation calls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/ui): dense speaker numbering on live and saved transcripts Both transcript renderers pass the conversation's segments to speakerLabel so Speaker N is numbered per conversation. Drops the dead duplicate formatChatTimestamp in widgets/transcript.dart, makes Material OmiAlertDialog scrollable, and points primitive docs at the contract's sections. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/l10n): contract words sweep Task(s) replaces Action Item(s)/To-Do in every locale (D3); Omi capitalised; '…' instead of '...'; search placeholders drop the ellipsis; Try Again, Not Now, OK, Title Case buttons. New keys: filterBySpeaker, notNow, discard, keepEditing, discardChangesTitle, discardChangesMessage. Translates the nine live-capture keys that only existed in English and removes five orphaned locale keys, so scripts/l10n.py check passes. The upgrade alert offers Not Now (postpone, not ignore-forever) and Update, honours barrierDismissible, and loses the emoji. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app/ui): export feedback, format and prompt primitives from the ui barrel; token colours Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app/ui): follow the Title Case clear-search label Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/l10n): strings for the conversation detail pass Link Event sheet, calendar event actions, search result navigation, playback labels, the share confirmation, the Tasks tab counts and empty state, and the speaker identify label, with translations for every locale. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): conversation detail on the shared primitives - D2: no body-wide swipe to another conversation and no PageRouteBuilder; Transcript/Summary(/Tasks) swipe as tabs, the Tasks tab exists only when there are tasks. - Header: OmiBackButton.circled plus same-size OmiIconButton circles (star, share, more) and a compact Ask Omi button that pushes Chat (D1). - Share asks before making a private conversation public and reverts it when the share sheet reports a dismissal. - Copy Conversation ID and Test Prompt only in developer mode; copies go through OmiClipboard, toasts through OmiFeedback. - Link Event and calendar event sheets on showOmiSheet with OmiDateFormat, l10n and full attendee names; visibility, summary template, name speaker and edit segment sheets on the shared shell. - Audio download sheet pops with its own context, offers Cancel and blocks back with PopScope. - Title: single line, Done, Untitled placeholder, saves on blur with an announced outcome; detail date/duration use OmiDateFormat/OmiDuration.compact (same rule as the list). - Tasks tab: tapping a task opens the shared task editor; labelled count pills; Tasks wording (D3); OmiEmptyState. - Edit segment sheet: speaker from SpeakerNames (You for the owner), Save and Cancel, discard guard. - Extract calendar sheets, search bar, transcript tab and tasks tab; delete dead ActionItemsListWidget, GetSheetTitle, GetDevToolsOptions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): keep INV-UI-3 allow comments on the flagged line Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): transcript bubbles and playback bar on the shared primitives Transcript: one SpeakerNames resolver per build (dense numbering, live people), no transcription-provider name, the start time as an OmiDuration offset, a labelled 44pt play button, labelled speaker targets, neutral user bubble and search highlight (INV-UI-1), localized 'translated by Omi', and getLastTranscript numbers speakers across the whole conversation. Playback bar: neutral surfaces instead of purple, a labelled 32pt play/pause in a 44pt target, a labelled stop button, OmiSpinner, OmiFeedback errors, the summary pill ellipsizes by layout instead of a hand-made '...', and the summary template sheet opens through showSummarizedAppsSheet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/onboarding): shared permission row, real progress, back and resume - OmiPermissionRow (lib/ui/components): reason, Allow -> system prompt, Allowed, Open Settings when the system will not ask again. One OnboardingPermissionsPanel drives both the first-run step and the returning-user interstitial; Continue never fires prompts and 'Always' location is no longer asked during first run. - Onboarding wrapper: placeholder pages removed, progress dots count the six real steps with a spoken 'Step N of M', PopScope makes system back step back, the last reached step is saved per account and resumed. - Consent step offers 'Use a Different Account' (sign out, back to start). - Acquisition survey is skippable; language step saves with a busy button and a Try Again error; picker selects on tap. - Steps share OnboardingCard (content scrolls, footer stays; min-height, no fixed text heights) and OmiButton; completion screen localized, purple gone. - Voice introduction: mic refusal shows the permission row; 'Skip Question' vs 'Skip'; device tutorial exits by a trailing close X. - Permission reasons rewritten, platform-neutral Location Services copy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/conversations): one delete path with Undo, row menu, bulk actions, recaps page - Conversation delete (list swipe, row menu, detail page) goes through one path: confirm unless opted out ("Don't ask again" is allowed because Undo backs it), then an Undo toast backed by the provider's pending-delete window (now 6 s, longer than the 5 s toast; the toast commits early when it closes) (D5). - Long-press on a conversation row opens one context menu: Open, Star/Unstar, Move to Folder, Share, Select, Delete. The selection bar gains Move and Delete (always confirmed) beside Merge. - Daily Recaps is its own pushed page with a back button; Home's View All pushes it and the Conversations tab never switches into a recap mode. Pull-to-refresh reloads recaps and a failed load offers Try Again. Recap counts are labelled. - One date filter for list and search, shown as a removable chip; the search field is seeded from the provider and uses OmiSearchField. - Dates and lengths via OmiDateFormat / OmiDuration (Today gets a header, the New badge sits beside the time, WAL/recording offsets keep their hours). - Discarded rows read "Discarded · 12s" with a plain-text snippet; blank titles read "Untitled Conversation". - Shared capture state table (capture_state_labels.dart) for the in-progress card; the processing card shows its real start time. - Local recording swipe always confirms. Sheets, dialogs, toasts, spinners and back buttons in conversations/, sync and recap pages move onto lib/ui. - Offline Sync is the one name for the device-storage sync screen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app/conversations): cover Undo-backed delete, row menu, recaps page and capture labels - Provider: the pending-delete window outlasts the Undo toast; undo restores, commit sends once, a regroup during the window keeps the row hidden. - Widgets: Undo restores / timeout commits / bulk toast counts; long-press menu entries and Select; untitled and discarded row titles; Today header; the date filter chip; Daily Recaps page back button, labelled counts, Try Again and pull-to-refresh. - Unit: the shared capture state table (an audio interruption is Paused). - The Home preview's row menu leaves out Select (no selection bar there); the in-progress card's photo count is labelled; last raw spinner and "..." in the capture card go. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/pairing): pairing recovery, one Bluetooth prompt, shared sheets - Find devices: a missing Bluetooth/location permission goes through the one BluetoothReadiness guidance prompt (Open Settings); the local 'Enable Bluetooth' dialogs are gone. - Nothing found: 'Scan Again', 'How to Pair' (connection guide), then 'Contact Support' instead of a lone mailto link. - Offline saved device: says how to wake it and 'Try Again' rescans (the old Retry did nothing). - Firmware compatibility warning is an acknowledge-only alert; the non-critical one keeps its opt-out row. - Device rows, guide cards and capture cards are real buttons with labels; battery shows a glyph and percentage with a spoken label, no emoji. - Connection guide, pairing steps, Ray-Ban picker/setup and Apple Watch setup use showOmiSheet; Apple Watch is only offered on iOS; 'I've Done This' becomes 'Done'. - Connect page: OmiBackButton, labelled settings button, and connecting returns to the existing Home (HomeNavigation.returnHome) instead of stacking a second one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): edit-sheet dirty guard and row long-press menu primitives showOmiEditSheet/OmiEditSheet own the drag so swipe-down, scrim tap, the close X and system back all ask "Discard changes?" while an editor is dirty (the framework's drag-to-dismiss pops without asking). showOmiRowMenu is the one long-press menu shape for list rows (Open, edit actions, Select, Delete). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): follow the warning token on the paused dot and the Conversation Map title Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/l10n): lane 2 strings for memories, tasks, goals and people Sheet titles without emoji, read-only memory hint, Undo toasts, task plural titles and counts, shared-task strings, memory review card and People page labels, with every locale translated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/memories): Undo-only deletes, openable read-only memories, guarded edit sheet - Every memory delete (swipe, long-press menu, edit sheet) goes through deleteMemoryWithUndo: no "cannot be undone" dialog, the shared OmiFeedback undo toast with no X, and a commit scoped to the memory the toast offered. The provider's pending window becomes an 8 s backstop that outlasts the toast. - Every memory opens: locked/superseded/ledger rows open the same sheet read-only with full text; swipe-delete only where editing is allowed. - Long-press menu Open / Edit / Delete. - Edit and new-memory sheets: explicit Cancel and Save, dirty guard, OmiButton. - Page: OmiBackButton, OmiSearchField, labelled 44pt header buttons, neutral FAB, OmiEmptyState / error state, management sheet on showOmiSheet with a confirm that names the action and toasts that reach the page after the sheet closes. - Memory graph: OmiBackButton, localized title, OmiLoading/Error/EmptyState, no purple. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/tasks): fixed swipe meaning, Undo deletes, guarded task and goal sheets - One swipe meaning on every task row at every indent: right completes (or reopens), left deletes. Indent/outdent move to the long-press menu (and drag keeps its horizontal indent); long-press shows Open / Mark Complete / Indent / Outdent / Select / Delete. - Single task and goal deletes are immediate with an Undo toast, backed by new staged-delete APIs on ActionItemsProvider and GoalsProvider (nothing hits the server until the toast closes). Bulk and clear-completed deletes confirm with a plural, verb-named dialog (the old one said "This will only remove deadlines"). - ActionItemFormSheet keeps its constructor; adds showActionItemFormSheet, explicit Cancel/Save, a close X in edit mode, a dirty guard and instant completion. The date picker and time picker are neutral (no purple) and locale/24-hour aware. - Home and Tasks share one GoalFormSheet (Cancel/Save, dirty guard, localized). - Tasks page: OmiSearchField "Search tasks", labelled overflow button, OmiEmptyState, neutral FAB/selection/drop indicators, localized "Exported to", counts with labels. - Selection bar, shared-tasks sheet and Home Today card on the primitives (44pt targets, rows open the task sheet). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/people): tapping a sample plays it; delete is a labelled control behind a confirm People page gets OmiBackButton, labelled Add/Help controls, an adaptive person dialog, OmiEmptyState/OmiLoadingState and confirms that name the action. The memory review card is localized with 44pt controls and tokens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): one header for live, processing and saved conversations; one media viewer - Live and processing pages share ConversationStateAppBar: the circled back button of the saved page and the state as the title (Recording / Paused / Reconnecting / Processing, one local mapping until lane 1a's shared capture_state_labels lands). No emoji status, no back button inside the title row. - Live page: remove the unreachable Summary tab and its TabController; Process Now is an OmiButton, mute a labelled OmiIconButton; the stop confirmation is showOmiConfirmWithOptOut with a verb; speaker names from SpeakerNames; photo-group time from OmiDateFormat and a localized photo count; tokens for the timeline and the upload indicator. - Processing page: one view (the transcript so far) under the shared header instead of Content/Summary tabs whose Summary only said 'Processing'. - Media viewer: always a full-screen modal with a trailing OmiCloseButton and swipe-down to close (MediaViewerPage.open), labelled share button, OmiSpinner; photo grids and the live timeline open it that way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/chat): contract composer, drawer and failed-reply retry Chat is a pushed page with a leading back (D1). Composer controls are labelled 44pt targets; Send that cannot send (offline, uploading) is drawn disabled with an offline hint. The apps drawer keeps Clear Chat at the bottom behind a destructive confirm, and each app has one labelled Disable (with Undo). A failed reply shows a localized error with Try Again that resends the user's message instead of raw server text. The feedback sheet is localized with a neutral Submit, thumbs read Helpful / Not Helpful, and copy/share analytics send the message length, not its text. Adds the lane-3 l10n keys for chat, apps and announcements. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/apps): one app action button, one consent dialog, Undo on disable AppListActionButton reads Open / Enable / View with the same meaning in every store list (no decorative Install). Enabling an app that works outside Omi asks one localized question naming what leaves Omi; Disable is immediate with a 5 s Undo on the detail page and in the chat drawer. The detail page moves onto the header, button, sheet, dialog and feedback primitives, splits its cards into widgets, and its cancel-subscription dialog reads Keep Subscription / Cancel Subscription. Referral and app home pages share one web page with a loading state, an error state with Try Again, and back within web history. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/announcements): answer-only dismissal through the prompt queue Announcements, feature screens and the changelog present through PromptQueue. The dialog's scrim no longer dismisses; the close X or the call to action marks an announcement seen, Not Now postpones it. The feature screen is a full-screen modal route with a trailing close; the changelog uses the shared sheet shell and error state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/ai-app-generator): one leading back, system back steps to the prompt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app): sweep conversation sheets and small widgets onto the primitives - Share via SMS and Create Template sheets use showOmiSheet, OmiSearchField, OmiButton, OmiSwitch, OmiEmptyState/OmiLoadingState and tokens; no purple (INV-UI-1). Create Template blocks back while the app is being created and opens the template picker from the navigator, not its own popped context. - Markdown and expandable text: neutral search highlight and link colour. - OmiSpinner and type/radius tokens in the speaker summary action, test prompts, photo image, person chip and waveform section. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(app/ui): edit sheets and row menus in the barrel and the contract Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): conversation detail UX contract cases Attendee names, detail duration rule, speaker numbering across the getLastTranscript window, transcript bubble chrome (no provider, offset time, 44pt labelled play), the shared capture state header, search bar position, the audio download sheet's own-context close/Cancel/back, the edit segment discard guard, and the media viewer's trailing close. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): show conversation developer tools in debug builds too No settings row writes devModeEnabled yet, so gating on it alone would hide Copy Conversation ID and Test Prompt from everyone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * wip(app): lane 3 checkpoint after spend-limit stop (uncommitted work preserved, not reviewed) * wip(app): lane 4 checkpoint after spend-limit stop (uncommitted work preserved, not reviewed) * wip(app): lane 5 checkpoint after spend-limit stop (uncommitted work preserved, not reviewed) * test(app/settings): device page stub getters and sign-out keep-list The one device page reads battery, charging and firmware getters the double-tap test stub lacked, and its connected picture pulses; stub the getters and turn on Reduce Motion so pumpAndSettle settles. Cover the Sign Out keep-list: paired device and display preferences survive, account values are cleared, and no credential is ever kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): name speaker sheet saves through the shared OmiButton Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/ai-app-generator): sweep the generator page onto the primitives App bar with a leading OmiBackButton on both steps; neutral accent instead of indigo/purple (INV-UI-1); OmiButton for Create App (busy state built in), OmiIconButton for send and regenerate-icon (labelled, 44pt), OmiSwitch for Make Public / Paid App, OmiSpinner, tokens for every colour, size and radius. Description and Features headings come from l10n; category and capability names use the shared localized titles. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/lane-3): plans sheet in the sheet shell, review fixes to the WIP checkpoint - chat: the quota plans sheet opens through showOmiSheet (lane 4 handoff). - chat evidence card: localized screen-reader hint. - capability apps page: localized category titles, token refresh colours. - const constructors and block-bodied ifs the WIP left for the analyzer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/chat): conversation links push through routeToPage; evidence test hosts l10n Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app): every push takes the platform route; shared-task link copy localized - routeToPage builds its route with omiPageRoute (defaultTargetPlatform) instead of dart:io Platform, the Foundation A handoff. - Deep links in AppShell, the People link in the name-speaker sheet and the firmware prompt in DeviceProvider push through routeToPage/omiPageRoute, never a bare MaterialPageRoute (iOS back swipe intact). - The shared-tasks deep link's not-found toast and unknown-sender fallback were hardcoded English; now sharedTasksLinkExpired and sharedTasksUnknownSender in all 49 locales. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): drop imports the ui barrel already provides Lane 2's edit-sheet and row-menu primitives joined the ui.dart barrel, which left seven direct imports redundant and pushed unnecessary_import over the analyzer baseline (CI's Dart Analyze & Tests failed on it). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/payments): labelled overflow menu, titled country picker sheet Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): people samples follow the one Voice Profile name Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/home): links open inside the one Home; startup prompts queue - Notification taps and app links open inside the Home already on screen (HomeNavigation.openRoute: pop to Home, select the tab, push the page) instead of pushReplacement-ing a second Home over whatever was showing. Home registers itself while mounted, so "Done" at the end of the firmware and connect flows also returns to it (nav #3). - Deep links move to home_deep_links.dart: parent before child (the Settings sheet opens before Data & Privacy is pushed over it), every push through routeToPage, and a removed app or deleted conversation now says so instead of only logging (nav #18). - D1: the home chat bar, its mic, chat deep links and the Ask Omi / Voice quick actions push chat as a normal page (no fullscreenDialog). The chat bar is one labelled button. - D6: Android back on the Tasks, Conversations or Apps tab returns to Home before it leaves the app (root PopScope). - Settings: the language / speech profile / transcription model comparison runs once the reader is back on Home, not the moment the sheet opens (onboarding-home #25). - Startup prompts go through PromptQueue (onboarding-home #23): upgrade alert (a required update first), changelog + announcements, the CV1 tutorial (waits for the pendant), firmware announcements, the Plus-meter plans sheet (only when the meter crosses its threshold, once per session, showOmiSheet) and Bluetooth guidance. HomePromptGate holds the queue while phone recording, on a call or during a firmware update and pumps it when that clears; passive pendant capture does not hold it. - Handoffs applied: dead toggleDailySummaries call removed (lane 1a); sync, task-export and quick-action pushes use routeToPage (lanes 2, 4). Header controls use neutral tokens (INV-UI-1) and OmiHaptics. - New strings (7 keys, every locale): appNotFoundOrRemoved plus the startup failure and discard-recording strings used by the next commits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/startup): startup failure offers Try Again and Contact Support - The failure screen words the message by error class: a rejected build configuration (Env / startup-routing checks, now thrown as StartupConfigurationError) says the build is at fault and offers Contact Support; any other failure says to check the connection and offers Try Again, which re-runs start-up, plus Contact Support (onboarding-home #27). - main() moves start-up into _start() so Try Again can run it again; ServiceManager.init, which refuses a second call, runs once. - Localized without a localization scope (lookupAppLocalizations for the device locale, English fallback); tokens replace literal colours/sizes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/speech-profile): confirm before discarding a recording in progress - Back or swipe while the voice sample is recording asks "Discard Recording?" (Discard / Keep Recording) instead of dropping it (onboarding-home #26). - "Reconnecting…" is an info toast, not an error. - The help control is a labelled OmiIconButton ("How to take a good sample?") opening showOmiAlert; back is OmiBackButton. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/home): header pill and record button expose their actions - The device pill and the record button wrap their gestures in Semantics(excludeSemantics: true), which also dropped the tap and long-press actions; the Semantics node now carries onTap (and the record options long-press), so VoiceOver/TalkBack can activate them (onboarding-home #15, #24). - Phone-mic offline fallback notice is an OmiFeedback info toast. - Sweep: auth-gate spinner is OmiSpinner; welcome Continue uses OmiHaptics; OmiGlass OTA page formatted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): delete webhooks.dart, reachable only from the removed dev-tools widget Its one importer was GetDevToolsOptions in conversation_detail/widgets.dart, which nothing constructed and lane 1b deleted. The dead-code ratchet flags the now-unreachable file; the webhook settings themselves live in DeveloperModeProvider and the users API and are unaffected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/home): a link counts as opened once its page is pushed openHomeDeepLink awaited each push, so HomeNavigation.openRoute (and the notification-open telemetry behind it) only resolved when the reader closed the page. It now resolves once the destination is on screen. A missing app or conversation is an info toast, as the contract says; the contract names HomeNavigation.openRoute / returnHome. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 1 (ZCode GLM-5.3) - A memory's Undo restores only that memory: restoreLastDeletedMemory takes the toast's id, so a stale toast cannot restore a newer delete (test added). - A failed task delete no longer inserts a Home-only task into the Tasks list; the staged index goes straight to _restoreDeletedItem, which skips -1. - The conversation pending-delete window is 10 s, so an Undo toast that starts late behind other snack bars still gets its full time; the toast still commits early when it closes. - The integration page's button reads "Disconnect" instead of stripping the question mark from a translated sentence (broke ¿…? and ? locales). - mounted checks after the long awaits in the Create Template sheet and Wrapped 2025 generation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): keep memories_provider under its line-count ceiling Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/apps): app creation forms on the shared form pieces and primitives Submit App and Manage App draw their screenshot strip and GitHub URL card from app_form_fields (screenshots open as a modal viewer with a trailing close), use AppFormCard/section titles/input decoration, OmiSwitch rows for Public/Paid (no indigo/green thumbs), OmiButton for submit/update, a leading OmiBackButton, and l10n for every string. Update asks with showOmiConfirm and a verb button (Update App); Submit's confirm reads Submit App; Start Earning is an Omi sheet with Not Now. App metadata pickers are Omi sheets, App ID copy goes through OmiClipboard. API keys: adaptive dialogs, destructive Revoke confirm, OmiFeedback toasts, localized dates. Add MCP Server: OmiBackButton, OmiButton, OmiFeedback. Provider snackbars and picker titles localized. New l10n keys: generateDescription, selectImageFileTitle, selectThumbnailImageTitle, appIdNotFoundError, manifestRefreshedSuccess, manifestRefreshFailed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/apps): store home, filters and reviews on the primitives The store's filter sheet opens in the Omi sheet shell; header icon controls are labelled 44pt OmiIconButtons; empty search is OmiEmptyState. Reviews: OmiBackButton, adaptive reply dialog, OmiFeedback toasts, OmiButton busy state, localized Add/Edit Your Review and rating counts, readable (not grey-700) secondary text. App summary and capabilities card localized. Screenshot taps on the detail page open through openAppScreenshots (modal, trailing close). Review avatar palette drops its purple entry (INV-UI-1). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app/apps): Disable hides at once, commits after the Undo toast, Undo restores Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/lane-3): localized pricing value on Manage App and file-picker title in chat Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 2 (lane 5) - A throw while preparing the Omi firmware update (for example a corrupt download that fails zip extraction) now ends in the failed state instead of an installing spinner with back blocked and every prompt held. - Home drops the prompts it enqueued when it is disposed, so after sign-out and sign-in the new Home's announcements, device tutorial and firmware notices are not deduplicated against dead closures. - The speech step re-reads the microphone permission when the app resumes, so granting it in Settings clears the Open Settings row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): retire AnimatedLoadingButton Lane 3 moved its last callers (payments, app detail) onto OmiButton, which owns the busy state and recovers when the action throws; the dead-code ratchet flags the unreachable widget. Its throw-recovery test now runs against OmiButton directly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 3 (lane 3) - Try Again on a failed reply releases the composer when the failure was already cleared, and the first app message releases it even if chat closes mid-request (both could leave typed sends blocked). - Enabling an app again inside its 5 s disable Undo window cancels the pending disable instead of being overwritten when the toast ends; tests for that and for a refused disable. - Stripe setup names the selected country from the full list, not the picker's filtered one, and always stops polling on dispose. - The AI app generator shows a failed submit on the generated view. - OmiWebPage's Try Again reloads the page that failed, not the first URL; a malformed app home URL shows the error state instead of throwing. The reported Stripe double-submit is already prevented: OmiButton ignores taps while its onPressed future runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 4 (#17297 merge and rulings) - Share from a conversation row now behaves like the page: a private conversation asks first ("Anyone with the link can view") and goes back to private when the share sheet is dismissed (was: made public at once). - Developer Settings gains a Conversation Developer Tools switch (writes devModeEnabled), so release users can still reach Copy Conversation ID and Test Prompt; 2 keys in 49 locales. - The row's Separate… always disposes its controller; tests cover a confirmed separation from a row (the other recording, never the row) and the row share confirmation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): phone number verified date holds in every timezone The fixture was UTC midnight, which is Jan 1 in Pago Pago; CI runs the suite at UTC+14 and UTC-11. Local noon reads Jan 2 everywhere. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): conversation title and emoji share one line in the header The title field kept Material's 48pt input height, which pushed the title below the emoji; it is dense now and the one-line row centres both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): every voice-profile entry opens the guided introduction David's ruling (2026-09-24): the voice profile is the guided fill-in-the- blank flow from #14514 (sentence starters, review, then voice, memories and a goal). Two entry points still reached the old question-based page: the Settings/Profile Voice Profile row for anyone who already had a profile, and the capture card's "Teach Omi your voice". Both now open the guided flow. The old page is unreachable, so it goes, with its two widgets, the getDialog helper and the fade-in text widget only it used (dead-code ratchet). Open PR #12531 edits the old page and will need to drop those hunks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): Star and Share back in the conversation top bar; record conversation actions David, 2026-09-24 (overrides the earlier one-overflow ruling): - the conversation page's top bar is Ask Omi (the primary, filled), Star and Share as matching 44pt icon buttons, then one "…" holding Rename, Move to Folder, Recordings and the rest, Delete last and destructive; - one registered event, Conversation Action, records every conversation action with two closed enums: action (ask_omi, star, unstar, share, rename, move_folder, recordings_open, separate, delete, copy_transcript, …) and surface (top_bar, overflow, detail_body, row_long_press, row_swipe). No ids, titles or content. It goes through the C7 typed registry (contracts/analytics/events.json, generated Dart, a presence consumer) and AnalyticsManager, so consent and queueing apply. Tests cover the event shape for every action and the top bar, overflow, row long-press and row swipe call sites. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): the people chip names only named speakers David, 2026-09-24: never label a participant "Speaker 1". The conversation header's people chip now shows the first named participant ("You" for the account owner) and counts everyone else: "David + 3 others" (participantsSummary, an ICU plural in 49 locales). With nobody named it hides; a linked calendar event's attendees still stand in when the transcript names no one. The transcript's own per-line speaker labels are unchanged. Tests cover named, mixed and none. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: drop pointers to the deleted speech-profile page Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 5 (voice profile and people chip) - SpeechProfileProvider drove only the deleted question page; the guided introduction records through GuidedVoiceIO. Remove the provider, its registration, the onboarding back-step close() that no longer stopped anything, and its test. - Drop pointers to deleted files: the addressability baseline entry and the logout flow's covers (now sign_out.dart); a stale doc paragraph. - participantsSummary carries few/many forms so Polish, Czech and Slovak agree for 2-4 and 5+; other Slavic and Baltic locales use a count-agnostic "one more" form. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 5 天前 | |
Mobile: Cloud badge for pending transcriptions, Goals only in Settings, compact Home and Tasks (#19766) * Mobile: pending transcriptions as a Cloud badge; Goals only in Settings The "Transcriptions pending" row on Home opened the same sync page as the Cloud button, so the count moves onto Cloud as a badge (neutral, 9+ cap, orange only for files still on the device). Cloud now also shows with no device paired while phone recordings wait, keeping phone-only users' way in. Goals leave the Tasks page; Settings > Goals already covers them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Format the sync button test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Mobile: compact Home and Tasks; one conversation-row layout Less whitespace without changing structure: tighter conversation cards (title, then time and length), a 24pt Home | Tasks switcher, a tighter live card, recap header and cards, date headers, and task rows. ConversationListItem's desktop-only branch never ran on a phone but was what flutter-tester rendered; it is removed so audit renders match the device. New home-feed-live audit scenario. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Recap card: fit the headline above the map to whole lines The shorter card left ~1pt of spare room for a two-line headline above the map, so large text clipped the second line mid-glyph. Fit whole lines (at most two) the way the overview already does, and give the text 8pt back (map band 76). Test covers 1.0/1.3/1.6x. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 3 小时前 | |
Mobile: simplify the app shell — one Home feed, Ask Omi sheet, global search (#19730) * Mobile: simplify the app shell (Home+Tasks switcher, Granola chat, global search) - Home and Conversations merge: capture row, Daily Recaps, then every conversation. The bottom tab bar is gone; Home | Tasks switch at the top. - Chat opens as a sheet over a blurred Home; greeting + starter chips, composer card with attach / Omi app chip / mic; tool steps as quiet lines that fold into one, with an Activity sheet timeline. - Search overlay drops from the top: browse tiles with counts (starred, folders, recaps, memories, people, places, new folder), grouped results for conversations, recaps, tasks and memories. - Apps tab folds into Settings > Integrations; Memories and Goals get rows on the Settings sheet; the mind map leads Memories. - Light is the default appearance for users who never picked one. - Home Screen settings page and its four toggles are removed. - Backend: GET /v1/users/daily-summaries/search and GET /v1/search/overview, with regenerated OpenAPI and clients. WIP handoff commit: pre-commit and pre-push checks not run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Mobile simple UI: verification fixes and polish - Addressability: Home/Tasks switcher carries the catalogued tab keys; catalog sources and tab indices follow the two-tab shell - Spine fixture: inert device provider answers the capture row's reads - UX contract: folder options sheet and search emoji use tokens - Memories: mind-map preview is a quiet card, no dead retry button - Integrations: drop the misleading Connected header - Home recaps: date eyebrow, bolder headline, overview or stats body - Analyzer: const in search audit scenarios Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Mobile simple UI: typed search results, test fallout, accessibility - Search API returns ApiResult (executeApi + generated DTOs); the search page says when a kind of result failed instead of showing it as none - Home/Tasks switcher exposes its tap to screen readers - ConversationsPage cancels its deferred timers on dispose (it mounts with Home now) - Chat starters wrap under large text instead of scrolling off screen - Tests follow the two-page shell and the Memories/Goals settings rows - Regenerated app-client OpenAPI and TS/Swift clients Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Search overview: a failed count is null, never a false 0 The app already shows a tile without a number for null; returning 0 on a failed aggregation would show 'Memories 0' to someone with hundreds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Mobile simple UI: recap card fits its map band; light-default boot test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Search: absent overview counts decode as no number; notice aligns with Cancel Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Recap card: overview ends on a clean ellipsis Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Light default: tint the white Omi logo; Tasks-in-shell audit; recap lines fit - The header Connect pill and the onboarding permissions screen used a white logo that vanished on the light surfaces - Recap overview shows as many whole lines as fit, ending on an ellipsis - Audit: tasks-in-shell scenario shows Tasks under the Home | Tasks switcher Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Mobile changelog: simpler Home, Ask Omi sheet, global search Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remove the unreachable speaker filter sheet; allowlist AppsPage for the spine Speaker filtering moved to search's People tile. AppsPage stays while the protected B1 spine oracle imports it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 5 小时前 | |
feat(app): add System / Light / Dark appearance setting on mobile (#19378) * feat(app): add system-aware light and dark appearance * fix(app): restore light appearance contrast on v1 surfaces * fix(app): provide appearance in visual audit harness Failure-Class: none * test(app): support light mode in visual audit harness * fix(app): correct light-mode color contrast Failure-Class: none * fix(app): improve remaining light-mode contrast Failure-Class: none * fix(app): use palette tokens for light transcript bubbles Replace the two hardcoded light-mode bubble colours with surface2 and a pale alpha-blend of the speaker colour, so the mobile UX contract's colour-literal ratchet stays flat. Failure-Class: none Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 2 天前 | |
feat(app): mobile UX consistency pass: shared primitives, contract and audit fixes (#18436) * feat(app/ui): add OmiColors tokens and stop using colorScheme.primary as a page background Add lib/ui/omi_tokens.dart: OmiColors (surfaces, text, neutral accent, status), OmiType ramp, OmiRadius, OmiSpacing, OmiMotion (honours Reduce Motion) and OmiHaptics. 53 sites read Theme.of(context).colorScheme.primary as a page or app bar background because the theme's primary was black. They now name OmiColors.surface0 (same black), so the theme can get a real accent. In the four files over the line-count threshold the explicit colour is dropped instead; the theme default is the same black. AppStyles and ResponsiveHelper get doc comments pointing at the tokens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): one theme and shared controls for navigation, buttons, sheets and page states Theme (lib/ui/omi_theme.dart, used by main.dart and the visual_audit harness so they cannot drift): a real dark ColorScheme with the white accent as primary, black scaffold, app bar theme (black, flat, centred 17pt semibold title), white progress indicators (the unstyled spinners were black on black), floating surface2 snack bars, surface1 dialogs and sheets, neutral switches. Material 2 stays on. MaterialApp.builder now syncs Intl.defaultLocale to the resolved locale. Components under lib/ui/components (barrel lib/ui/ui.dart): OmiBackButton / OmiCloseButton (platform glyph, localized label, 44pt target, circled variants), OmiIconButton (required label, tooltip), OmiButton (primary/secondary/destructive/tertiary, regular/compact, async loading that always clears), OmiSpinner, OmiLoadingState / OmiEmptyState / OmiErrorState, showOmiSheet and OmiSheetScaffold, OmiSwitch, OmiSettingsGroup / OmiSettingsRow / OmiSectionHeader, OmiSearchField, and omiPageRoute for code that needs a Route object. HeaderCircleButton is now a wrapper over OmiIconButton.filled and AnimatedLoadingButton over OmiButton (fixes the spinner that never cleared after a throw, and the 40pt target). find_device's two unstyled ElevatedButtons keep their black fill explicitly now that primary is white. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/settings): move data privacy, Asana and ClickUp settings onto the shared controls Proving set for the new primitives. Data & Privacy uses OmiBackButton, a section header and an OmiSettingsGroup of app rows, OmiEmptyState, and drops its accent tint for neutral tokens. Asana and ClickUp show an app bar with a back button and OmiLoadingState during their first load (the page used to be an invisible spinner with no way out), OmiSpinner for section loads, and tokens for their colours. The shared IntegrationSettingsPage header uses OmiBackButton and a labelled refresh OmiIconButton, and its disconnect control is an OmiButton.destructive. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): one adaptive confirmation system (showOmiConfirm/showOmiAlert) getDialog, ConfirmationDialog, OmiConfirmDialog and AppDialog become thin adapters over OmiAlertDialog: Cancel always shown and always closes, real CupertinoDialogActions on iOS (destructive red, safe choice bold), localized defaults, a destructive flag, and AppDialog.show no longer drops singleButton and onCancel. Non-page raw AlertDialogs in providers/widgets migrated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): OmiFeedback toasts and OmiClipboard One floating neutral toast at a time with contract timings (confirm 1.5 s, info 4 s, error 8 s with close, undo 5 s with no close, resolving whether the reader undid). Snackbars with actions no longer persist forever. The home shell registers its tab-bar clearance. AppSnackbar's ~180 call sites route through it. OmiClipboard refuses empty text and skips its toast on Android 13+ where the system already confirms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): OmiDateFormat, OmiDuration and shared speaker names Dates follow the app locale and the 24-hour setting; durations have one offset/compact/long vocabulary (the playback bar no longer drops hours). SpeakerNames is the single source for speaker labels in app and export: You / person / Omi / dense Speaker N, localized in copied transcripts. Legacy helpers delegate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): PromptQueue for startup and background prompts Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(checks): INV-UI-3 mobile UX contract ratchet Per-rule, per-file no-increase ratchet over hand-rolled back glyphs, PageRouteBuilder, raw sheets/dialogs/snackbars/clipboard, date patterns, colour/font-size/radius literals, raw spinners, '...' and hardcoded Text copy under app/lib, with an allow-comment escape hatch and a test proving each rule fires. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(app): mobile UX contract The numbered rules and the one primitive for each: leaving a surface, sheets, controls, destructive actions (D5), dialogs, feedback timing, copying, dates and durations, speakers, tokens, words (D3), settings (D4), page states, prompts, permissions and accessibility, plus the D1/D2/D6 navigation calls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/ui): dense speaker numbering on live and saved transcripts Both transcript renderers pass the conversation's segments to speakerLabel so Speaker N is numbered per conversation. Drops the dead duplicate formatChatTimestamp in widgets/transcript.dart, makes Material OmiAlertDialog scrollable, and points primitive docs at the contract's sections. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/l10n): contract words sweep Task(s) replaces Action Item(s)/To-Do in every locale (D3); Omi capitalised; '…' instead of '...'; search placeholders drop the ellipsis; Try Again, Not Now, OK, Title Case buttons. New keys: filterBySpeaker, notNow, discard, keepEditing, discardChangesTitle, discardChangesMessage. Translates the nine live-capture keys that only existed in English and removes five orphaned locale keys, so scripts/l10n.py check passes. The upgrade alert offers Not Now (postpone, not ignore-forever) and Update, honours barrierDismissible, and loses the emoji. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app/ui): export feedback, format and prompt primitives from the ui barrel; token colours Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app/ui): follow the Title Case clear-search label Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/l10n): strings for the conversation detail pass Link Event sheet, calendar event actions, search result navigation, playback labels, the share confirmation, the Tasks tab counts and empty state, and the speaker identify label, with translations for every locale. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): conversation detail on the shared primitives - D2: no body-wide swipe to another conversation and no PageRouteBuilder; Transcript/Summary(/Tasks) swipe as tabs, the Tasks tab exists only when there are tasks. - Header: OmiBackButton.circled plus same-size OmiIconButton circles (star, share, more) and a compact Ask Omi button that pushes Chat (D1). - Share asks before making a private conversation public and reverts it when the share sheet reports a dismissal. - Copy Conversation ID and Test Prompt only in developer mode; copies go through OmiClipboard, toasts through OmiFeedback. - Link Event and calendar event sheets on showOmiSheet with OmiDateFormat, l10n and full attendee names; visibility, summary template, name speaker and edit segment sheets on the shared shell. - Audio download sheet pops with its own context, offers Cancel and blocks back with PopScope. - Title: single line, Done, Untitled placeholder, saves on blur with an announced outcome; detail date/duration use OmiDateFormat/OmiDuration.compact (same rule as the list). - Tasks tab: tapping a task opens the shared task editor; labelled count pills; Tasks wording (D3); OmiEmptyState. - Edit segment sheet: speaker from SpeakerNames (You for the owner), Save and Cancel, discard guard. - Extract calendar sheets, search bar, transcript tab and tasks tab; delete dead ActionItemsListWidget, GetSheetTitle, GetDevToolsOptions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): keep INV-UI-3 allow comments on the flagged line Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): transcript bubbles and playback bar on the shared primitives Transcript: one SpeakerNames resolver per build (dense numbering, live people), no transcription-provider name, the start time as an OmiDuration offset, a labelled 44pt play button, labelled speaker targets, neutral user bubble and search highlight (INV-UI-1), localized 'translated by Omi', and getLastTranscript numbers speakers across the whole conversation. Playback bar: neutral surfaces instead of purple, a labelled 32pt play/pause in a 44pt target, a labelled stop button, OmiSpinner, OmiFeedback errors, the summary pill ellipsizes by layout instead of a hand-made '...', and the summary template sheet opens through showSummarizedAppsSheet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/onboarding): shared permission row, real progress, back and resume - OmiPermissionRow (lib/ui/components): reason, Allow -> system prompt, Allowed, Open Settings when the system will not ask again. One OnboardingPermissionsPanel drives both the first-run step and the returning-user interstitial; Continue never fires prompts and 'Always' location is no longer asked during first run. - Onboarding wrapper: placeholder pages removed, progress dots count the six real steps with a spoken 'Step N of M', PopScope makes system back step back, the last reached step is saved per account and resumed. - Consent step offers 'Use a Different Account' (sign out, back to start). - Acquisition survey is skippable; language step saves with a busy button and a Try Again error; picker selects on tap. - Steps share OnboardingCard (content scrolls, footer stays; min-height, no fixed text heights) and OmiButton; completion screen localized, purple gone. - Voice introduction: mic refusal shows the permission row; 'Skip Question' vs 'Skip'; device tutorial exits by a trailing close X. - Permission reasons rewritten, platform-neutral Location Services copy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/conversations): one delete path with Undo, row menu, bulk actions, recaps page - Conversation delete (list swipe, row menu, detail page) goes through one path: confirm unless opted out ("Don't ask again" is allowed because Undo backs it), then an Undo toast backed by the provider's pending-delete window (now 6 s, longer than the 5 s toast; the toast commits early when it closes) (D5). - Long-press on a conversation row opens one context menu: Open, Star/Unstar, Move to Folder, Share, Select, Delete. The selection bar gains Move and Delete (always confirmed) beside Merge. - Daily Recaps is its own pushed page with a back button; Home's View All pushes it and the Conversations tab never switches into a recap mode. Pull-to-refresh reloads recaps and a failed load offers Try Again. Recap counts are labelled. - One date filter for list and search, shown as a removable chip; the search field is seeded from the provider and uses OmiSearchField. - Dates and lengths via OmiDateFormat / OmiDuration (Today gets a header, the New badge sits beside the time, WAL/recording offsets keep their hours). - Discarded rows read "Discarded · 12s" with a plain-text snippet; blank titles read "Untitled Conversation". - Shared capture state table (capture_state_labels.dart) for the in-progress card; the processing card shows its real start time. - Local recording swipe always confirms. Sheets, dialogs, toasts, spinners and back buttons in conversations/, sync and recap pages move onto lib/ui. - Offline Sync is the one name for the device-storage sync screen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app/conversations): cover Undo-backed delete, row menu, recaps page and capture labels - Provider: the pending-delete window outlasts the Undo toast; undo restores, commit sends once, a regroup during the window keeps the row hidden. - Widgets: Undo restores / timeout commits / bulk toast counts; long-press menu entries and Select; untitled and discarded row titles; Today header; the date filter chip; Daily Recaps page back button, labelled counts, Try Again and pull-to-refresh. - Unit: the shared capture state table (an audio interruption is Paused). - The Home preview's row menu leaves out Select (no selection bar there); the in-progress card's photo count is labelled; last raw spinner and "..." in the capture card go. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/pairing): pairing recovery, one Bluetooth prompt, shared sheets - Find devices: a missing Bluetooth/location permission goes through the one BluetoothReadiness guidance prompt (Open Settings); the local 'Enable Bluetooth' dialogs are gone. - Nothing found: 'Scan Again', 'How to Pair' (connection guide), then 'Contact Support' instead of a lone mailto link. - Offline saved device: says how to wake it and 'Try Again' rescans (the old Retry did nothing). - Firmware compatibility warning is an acknowledge-only alert; the non-critical one keeps its opt-out row. - Device rows, guide cards and capture cards are real buttons with labels; battery shows a glyph and percentage with a spoken label, no emoji. - Connection guide, pairing steps, Ray-Ban picker/setup and Apple Watch setup use showOmiSheet; Apple Watch is only offered on iOS; 'I've Done This' becomes 'Done'. - Connect page: OmiBackButton, labelled settings button, and connecting returns to the existing Home (HomeNavigation.returnHome) instead of stacking a second one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): edit-sheet dirty guard and row long-press menu primitives showOmiEditSheet/OmiEditSheet own the drag so swipe-down, scrim tap, the close X and system back all ask "Discard changes?" while an editor is dirty (the framework's drag-to-dismiss pops without asking). showOmiRowMenu is the one long-press menu shape for list rows (Open, edit actions, Select, Delete). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): follow the warning token on the paused dot and the Conversation Map title Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/l10n): lane 2 strings for memories, tasks, goals and people Sheet titles without emoji, read-only memory hint, Undo toasts, task plural titles and counts, shared-task strings, memory review card and People page labels, with every locale translated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/memories): Undo-only deletes, openable read-only memories, guarded edit sheet - Every memory delete (swipe, long-press menu, edit sheet) goes through deleteMemoryWithUndo: no "cannot be undone" dialog, the shared OmiFeedback undo toast with no X, and a commit scoped to the memory the toast offered. The provider's pending window becomes an 8 s backstop that outlasts the toast. - Every memory opens: locked/superseded/ledger rows open the same sheet read-only with full text; swipe-delete only where editing is allowed. - Long-press menu Open / Edit / Delete. - Edit and new-memory sheets: explicit Cancel and Save, dirty guard, OmiButton. - Page: OmiBackButton, OmiSearchField, labelled 44pt header buttons, neutral FAB, OmiEmptyState / error state, management sheet on showOmiSheet with a confirm that names the action and toasts that reach the page after the sheet closes. - Memory graph: OmiBackButton, localized title, OmiLoading/Error/EmptyState, no purple. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/tasks): fixed swipe meaning, Undo deletes, guarded task and goal sheets - One swipe meaning on every task row at every indent: right completes (or reopens), left deletes. Indent/outdent move to the long-press menu (and drag keeps its horizontal indent); long-press shows Open / Mark Complete / Indent / Outdent / Select / Delete. - Single task and goal deletes are immediate with an Undo toast, backed by new staged-delete APIs on ActionItemsProvider and GoalsProvider (nothing hits the server until the toast closes). Bulk and clear-completed deletes confirm with a plural, verb-named dialog (the old one said "This will only remove deadlines"). - ActionItemFormSheet keeps its constructor; adds showActionItemFormSheet, explicit Cancel/Save, a close X in edit mode, a dirty guard and instant completion. The date picker and time picker are neutral (no purple) and locale/24-hour aware. - Home and Tasks share one GoalFormSheet (Cancel/Save, dirty guard, localized). - Tasks page: OmiSearchField "Search tasks", labelled overflow button, OmiEmptyState, neutral FAB/selection/drop indicators, localized "Exported to", counts with labels. - Selection bar, shared-tasks sheet and Home Today card on the primitives (44pt targets, rows open the task sheet). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/people): tapping a sample plays it; delete is a labelled control behind a confirm People page gets OmiBackButton, labelled Add/Help controls, an adaptive person dialog, OmiEmptyState/OmiLoadingState and confirms that name the action. The memory review card is localized with 44pt controls and tokens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): one header for live, processing and saved conversations; one media viewer - Live and processing pages share ConversationStateAppBar: the circled back button of the saved page and the state as the title (Recording / Paused / Reconnecting / Processing, one local mapping until lane 1a's shared capture_state_labels lands). No emoji status, no back button inside the title row. - Live page: remove the unreachable Summary tab and its TabController; Process Now is an OmiButton, mute a labelled OmiIconButton; the stop confirmation is showOmiConfirmWithOptOut with a verb; speaker names from SpeakerNames; photo-group time from OmiDateFormat and a localized photo count; tokens for the timeline and the upload indicator. - Processing page: one view (the transcript so far) under the shared header instead of Content/Summary tabs whose Summary only said 'Processing'. - Media viewer: always a full-screen modal with a trailing OmiCloseButton and swipe-down to close (MediaViewerPage.open), labelled share button, OmiSpinner; photo grids and the live timeline open it that way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/chat): contract composer, drawer and failed-reply retry Chat is a pushed page with a leading back (D1). Composer controls are labelled 44pt targets; Send that cannot send (offline, uploading) is drawn disabled with an offline hint. The apps drawer keeps Clear Chat at the bottom behind a destructive confirm, and each app has one labelled Disable (with Undo). A failed reply shows a localized error with Try Again that resends the user's message instead of raw server text. The feedback sheet is localized with a neutral Submit, thumbs read Helpful / Not Helpful, and copy/share analytics send the message length, not its text. Adds the lane-3 l10n keys for chat, apps and announcements. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/apps): one app action button, one consent dialog, Undo on disable AppListActionButton reads Open / Enable / View with the same meaning in every store list (no decorative Install). Enabling an app that works outside Omi asks one localized question naming what leaves Omi; Disable is immediate with a 5 s Undo on the detail page and in the chat drawer. The detail page moves onto the header, button, sheet, dialog and feedback primitives, splits its cards into widgets, and its cancel-subscription dialog reads Keep Subscription / Cancel Subscription. Referral and app home pages share one web page with a loading state, an error state with Try Again, and back within web history. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/announcements): answer-only dismissal through the prompt queue Announcements, feature screens and the changelog present through PromptQueue. The dialog's scrim no longer dismisses; the close X or the call to action marks an announcement seen, Not Now postpones it. The feature screen is a full-screen modal route with a trailing close; the changelog uses the shared sheet shell and error state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/ai-app-generator): one leading back, system back steps to the prompt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app): sweep conversation sheets and small widgets onto the primitives - Share via SMS and Create Template sheets use showOmiSheet, OmiSearchField, OmiButton, OmiSwitch, OmiEmptyState/OmiLoadingState and tokens; no purple (INV-UI-1). Create Template blocks back while the app is being created and opens the template picker from the navigator, not its own popped context. - Markdown and expandable text: neutral search highlight and link colour. - OmiSpinner and type/radius tokens in the speaker summary action, test prompts, photo image, person chip and waveform section. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(app/ui): edit sheets and row menus in the barrel and the contract Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): conversation detail UX contract cases Attendee names, detail duration rule, speaker numbering across the getLastTranscript window, transcript bubble chrome (no provider, offset time, 44pt labelled play), the shared capture state header, search bar position, the audio download sheet's own-context close/Cancel/back, the edit segment discard guard, and the media viewer's trailing close. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): show conversation developer tools in debug builds too No settings row writes devModeEnabled yet, so gating on it alone would hide Copy Conversation ID and Test Prompt from everyone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * wip(app): lane 3 checkpoint after spend-limit stop (uncommitted work preserved, not reviewed) * wip(app): lane 4 checkpoint after spend-limit stop (uncommitted work preserved, not reviewed) * wip(app): lane 5 checkpoint after spend-limit stop (uncommitted work preserved, not reviewed) * test(app/settings): device page stub getters and sign-out keep-list The one device page reads battery, charging and firmware getters the double-tap test stub lacked, and its connected picture pulses; stub the getters and turn on Reduce Motion so pumpAndSettle settles. Cover the Sign Out keep-list: paired device and display preferences survive, account values are cleared, and no credential is ever kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): name speaker sheet saves through the shared OmiButton Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/ai-app-generator): sweep the generator page onto the primitives App bar with a leading OmiBackButton on both steps; neutral accent instead of indigo/purple (INV-UI-1); OmiButton for Create App (busy state built in), OmiIconButton for send and regenerate-icon (labelled, 44pt), OmiSwitch for Make Public / Paid App, OmiSpinner, tokens for every colour, size and radius. Description and Features headings come from l10n; category and capability names use the shared localized titles. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/lane-3): plans sheet in the sheet shell, review fixes to the WIP checkpoint - chat: the quota plans sheet opens through showOmiSheet (lane 4 handoff). - chat evidence card: localized screen-reader hint. - capability apps page: localized category titles, token refresh colours. - const constructors and block-bodied ifs the WIP left for the analyzer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/chat): conversation links push through routeToPage; evidence test hosts l10n Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app): every push takes the platform route; shared-task link copy localized - routeToPage builds its route with omiPageRoute (defaultTargetPlatform) instead of dart:io Platform, the Foundation A handoff. - Deep links in AppShell, the People link in the name-speaker sheet and the firmware prompt in DeviceProvider push through routeToPage/omiPageRoute, never a bare MaterialPageRoute (iOS back swipe intact). - The shared-tasks deep link's not-found toast and unknown-sender fallback were hardcoded English; now sharedTasksLinkExpired and sharedTasksUnknownSender in all 49 locales. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): drop imports the ui barrel already provides Lane 2's edit-sheet and row-menu primitives joined the ui.dart barrel, which left seven direct imports redundant and pushed unnecessary_import over the analyzer baseline (CI's Dart Analyze & Tests failed on it). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/payments): labelled overflow menu, titled country picker sheet Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): people samples follow the one Voice Profile name Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/home): links open inside the one Home; startup prompts queue - Notification taps and app links open inside the Home already on screen (HomeNavigation.openRoute: pop to Home, select the tab, push the page) instead of pushReplacement-ing a second Home over whatever was showing. Home registers itself while mounted, so "Done" at the end of the firmware and connect flows also returns to it (nav #3). - Deep links move to home_deep_links.dart: parent before child (the Settings sheet opens before Data & Privacy is pushed over it), every push through routeToPage, and a removed app or deleted conversation now says so instead of only logging (nav #18). - D1: the home chat bar, its mic, chat deep links and the Ask Omi / Voice quick actions push chat as a normal page (no fullscreenDialog). The chat bar is one labelled button. - D6: Android back on the Tasks, Conversations or Apps tab returns to Home before it leaves the app (root PopScope). - Settings: the language / speech profile / transcription model comparison runs once the reader is back on Home, not the moment the sheet opens (onboarding-home #25). - Startup prompts go through PromptQueue (onboarding-home #23): upgrade alert (a required update first), changelog + announcements, the CV1 tutorial (waits for the pendant), firmware announcements, the Plus-meter plans sheet (only when the meter crosses its threshold, once per session, showOmiSheet) and Bluetooth guidance. HomePromptGate holds the queue while phone recording, on a call or during a firmware update and pumps it when that clears; passive pendant capture does not hold it. - Handoffs applied: dead toggleDailySummaries call removed (lane 1a); sync, task-export and quick-action pushes use routeToPage (lanes 2, 4). Header controls use neutral tokens (INV-UI-1) and OmiHaptics. - New strings (7 keys, every locale): appNotFoundOrRemoved plus the startup failure and discard-recording strings used by the next commits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/startup): startup failure offers Try Again and Contact Support - The failure screen words the message by error class: a rejected build configuration (Env / startup-routing checks, now thrown as StartupConfigurationError) says the build is at fault and offers Contact Support; any other failure says to check the connection and offers Try Again, which re-runs start-up, plus Contact Support (onboarding-home #27). - main() moves start-up into _start() so Try Again can run it again; ServiceManager.init, which refuses a second call, runs once. - Localized without a localization scope (lookupAppLocalizations for the device locale, English fallback); tokens replace literal colours/sizes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/speech-profile): confirm before discarding a recording in progress - Back or swipe while the voice sample is recording asks "Discard Recording?" (Discard / Keep Recording) instead of dropping it (onboarding-home #26). - "Reconnecting…" is an info toast, not an error. - The help control is a labelled OmiIconButton ("How to take a good sample?") opening showOmiAlert; back is OmiBackButton. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/home): header pill and record button expose their actions - The device pill and the record button wrap their gestures in Semantics(excludeSemantics: true), which also dropped the tap and long-press actions; the Semantics node now carries onTap (and the record options long-press), so VoiceOver/TalkBack can activate them (onboarding-home #15, #24). - Phone-mic offline fallback notice is an OmiFeedback info toast. - Sweep: auth-gate spinner is OmiSpinner; welcome Continue uses OmiHaptics; OmiGlass OTA page formatted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): delete webhooks.dart, reachable only from the removed dev-tools widget Its one importer was GetDevToolsOptions in conversation_detail/widgets.dart, which nothing constructed and lane 1b deleted. The dead-code ratchet flags the now-unreachable file; the webhook settings themselves live in DeveloperModeProvider and the users API and are unaffected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/home): a link counts as opened once its page is pushed openHomeDeepLink awaited each push, so HomeNavigation.openRoute (and the notification-open telemetry behind it) only resolved when the reader closed the page. It now resolves once the destination is on screen. A missing app or conversation is an info toast, as the contract says; the contract names HomeNavigation.openRoute / returnHome. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 1 (ZCode GLM-5.3) - A memory's Undo restores only that memory: restoreLastDeletedMemory takes the toast's id, so a stale toast cannot restore a newer delete (test added). - A failed task delete no longer inserts a Home-only task into the Tasks list; the staged index goes straight to _restoreDeletedItem, which skips -1. - The conversation pending-delete window is 10 s, so an Undo toast that starts late behind other snack bars still gets its full time; the toast still commits early when it closes. - The integration page's button reads "Disconnect" instead of stripping the question mark from a translated sentence (broke ¿…? and ? locales). - mounted checks after the long awaits in the Create Template sheet and Wrapped 2025 generation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): keep memories_provider under its line-count ceiling Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/apps): app creation forms on the shared form pieces and primitives Submit App and Manage App draw their screenshot strip and GitHub URL card from app_form_fields (screenshots open as a modal viewer with a trailing close), use AppFormCard/section titles/input decoration, OmiSwitch rows for Public/Paid (no indigo/green thumbs), OmiButton for submit/update, a leading OmiBackButton, and l10n for every string. Update asks with showOmiConfirm and a verb button (Update App); Submit's confirm reads Submit App; Start Earning is an Omi sheet with Not Now. App metadata pickers are Omi sheets, App ID copy goes through OmiClipboard. API keys: adaptive dialogs, destructive Revoke confirm, OmiFeedback toasts, localized dates. Add MCP Server: OmiBackButton, OmiButton, OmiFeedback. Provider snackbars and picker titles localized. New l10n keys: generateDescription, selectImageFileTitle, selectThumbnailImageTitle, appIdNotFoundError, manifestRefreshedSuccess, manifestRefreshFailed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/apps): store home, filters and reviews on the primitives The store's filter sheet opens in the Omi sheet shell; header icon controls are labelled 44pt OmiIconButtons; empty search is OmiEmptyState. Reviews: OmiBackButton, adaptive reply dialog, OmiFeedback toasts, OmiButton busy state, localized Add/Edit Your Review and rating counts, readable (not grey-700) secondary text. App summary and capabilities card localized. Screenshot taps on the detail page open through openAppScreenshots (modal, trailing close). Review avatar palette drops its purple entry (INV-UI-1). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app/apps): Disable hides at once, commits after the Undo toast, Undo restores Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/lane-3): localized pricing value on Manage App and file-picker title in chat Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 2 (lane 5) - A throw while preparing the Omi firmware update (for example a corrupt download that fails zip extraction) now ends in the failed state instead of an installing spinner with back blocked and every prompt held. - Home drops the prompts it enqueued when it is disposed, so after sign-out and sign-in the new Home's announcements, device tutorial and firmware notices are not deduplicated against dead closures. - The speech step re-reads the microphone permission when the app resumes, so granting it in Settings clears the Open Settings row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): retire AnimatedLoadingButton Lane 3 moved its last callers (payments, app detail) onto OmiButton, which owns the busy state and recovers when the action throws; the dead-code ratchet flags the unreachable widget. Its throw-recovery test now runs against OmiButton directly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 3 (lane 3) - Try Again on a failed reply releases the composer when the failure was already cleared, and the first app message releases it even if chat closes mid-request (both could leave typed sends blocked). - Enabling an app again inside its 5 s disable Undo window cancels the pending disable instead of being overwritten when the toast ends; tests for that and for a refused disable. - Stripe setup names the selected country from the full list, not the picker's filtered one, and always stops polling on dispose. - The AI app generator shows a failed submit on the generated view. - OmiWebPage's Try Again reloads the page that failed, not the first URL; a malformed app home URL shows the error state instead of throwing. The reported Stripe double-submit is already prevented: OmiButton ignores taps while its onPressed future runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 4 (#17297 merge and rulings) - Share from a conversation row now behaves like the page: a private conversation asks first ("Anyone with the link can view") and goes back to private when the share sheet is dismissed (was: made public at once). - Developer Settings gains a Conversation Developer Tools switch (writes devModeEnabled), so release users can still reach Copy Conversation ID and Test Prompt; 2 keys in 49 locales. - The row's Separate… always disposes its controller; tests cover a confirmed separation from a row (the other recording, never the row) and the row share confirmation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): phone number verified date holds in every timezone The fixture was UTC midnight, which is Jan 1 in Pago Pago; CI runs the suite at UTC+14 and UTC-11. Local noon reads Jan 2 everywhere. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): conversation title and emoji share one line in the header The title field kept Material's 48pt input height, which pushed the title below the emoji; it is dense now and the one-line row centres both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): every voice-profile entry opens the guided introduction David's ruling (2026-09-24): the voice profile is the guided fill-in-the- blank flow from #14514 (sentence starters, review, then voice, memories and a goal). Two entry points still reached the old question-based page: the Settings/Profile Voice Profile row for anyone who already had a profile, and the capture card's "Teach Omi your voice". Both now open the guided flow. The old page is unreachable, so it goes, with its two widgets, the getDialog helper and the fade-in text widget only it used (dead-code ratchet). Open PR #12531 edits the old page and will need to drop those hunks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): Star and Share back in the conversation top bar; record conversation actions David, 2026-09-24 (overrides the earlier one-overflow ruling): - the conversation page's top bar is Ask Omi (the primary, filled), Star and Share as matching 44pt icon buttons, then one "…" holding Rename, Move to Folder, Recordings and the rest, Delete last and destructive; - one registered event, Conversation Action, records every conversation action with two closed enums: action (ask_omi, star, unstar, share, rename, move_folder, recordings_open, separate, delete, copy_transcript, …) and surface (top_bar, overflow, detail_body, row_long_press, row_swipe). No ids, titles or content. It goes through the C7 typed registry (contracts/analytics/events.json, generated Dart, a presence consumer) and AnalyticsManager, so consent and queueing apply. Tests cover the event shape for every action and the top bar, overflow, row long-press and row swipe call sites. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): the people chip names only named speakers David, 2026-09-24: never label a participant "Speaker 1". The conversation header's people chip now shows the first named participant ("You" for the account owner) and counts everyone else: "David + 3 others" (participantsSummary, an ICU plural in 49 locales). With nobody named it hides; a linked calendar event's attendees still stand in when the transcript names no one. The transcript's own per-line speaker labels are unchanged. Tests cover named, mixed and none. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: drop pointers to the deleted speech-profile page Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 5 (voice profile and people chip) - SpeechProfileProvider drove only the deleted question page; the guided introduction records through GuidedVoiceIO. Remove the provider, its registration, the onboarding back-step close() that no longer stopped anything, and its test. - Drop pointers to deleted files: the addressability baseline entry and the logout flow's covers (now sign_out.dart); a stale doc paragraph. - participantsSummary carries few/many forms so Polish, Czech and Slovak agree for 2-4 and 5+; other Slavic and Baltic locales use a count-agnostic "one more" form. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 5 天前 | |
Maps: keyless static previews + native-map handoff; fix Unknown location labels (#12841) * feat(backend): authed static-map proxy route with Redis-cached renders GET /v1/static-map renders dark-styled Google Static Maps images server-side (the only Maps key stays server-restricted) and caches the bytes in Redis keyed by the quantized pin set + size, so repeat renders of the same place across users/sessions cost one upstream call per distinct pin set per week. utils/static_map.py is the single provider seam for app map previews; swapping providers touches that module only. - pins parsed/bounded/de-duplicated/sorted, capped at 50; ~11m quantization makes users at the same place share one cached image - one pin centers at street zoom; several pins use provider auto-fit - failures return 502 (never cached) - the app renders its offline pin-dot canvas; auth prevents an open proxy on the project key - rate policy static_map:get (240/h per uid) stops hot loops; cached hits are one Redis read Tests: tests/routers/test_static_map.py (auth 401, 400 malformed pins, 502 upstream failure, 200 + private cache headers, cache hit/miss/order- insensitivity/no-cache-on-failure/missing key tolerances). Verified: .venv/bin/python -m pytest tests/routers/test_static_map.py -q -> 14 passed; scan_async_blockers clean. * fix(backend): enrich sync-path geolocation at the pipeline coordinator Failure-Class: FC-sync-geolocation-missing-address | new | none Offline-synced conversations were created with raw coordinates and no address: REST create, developer API, integration ingest, and live finalization all run resolve_geolocation, but the sync path shipped raw coords straight through. Recaps built from synced conversations showed 'Unknown' timeline rows. _run_full_pipeline_background_async - the one coordinator both the inline and Cloud Tasks dispatch branches call - now enriches the job's geolocation once, before any segment is processed and before the concurrency gate (no slot held during the geocode call). The resolver keeps the caller's exact coordinates and returns its input unchanged on any geocode miss/error, so a failure never drops the location. Tests: tests/unit/test_sync_geolocation_enrichment.py drives the real coordinator with a fake geocoder - one geocode per job (not per segment), enriched value reaches every segment, raw geolocation survives a geocode failure, None passthrough. Fixture stub lists in test_sync_v2 / test_sync_transcription_prefs gained the utils.conversations.location stub with an identity passthrough (the real resolver's miss behavior). Verified: pytest tests/unit/test_sync_geolocation_enrichment.py test_sync_v2.py test_sync_transcription_prefs.py -> all passed. * fix(backend): fill empty daily-summary pin addresses at read time Failure-Class: FC-sync-geolocation-missing-address | new | none generate_comprehensive_daily_summary copied c.geolocation.address verbatim, so pins from conversations created before write-time enrichment (the sync path) rendered as 'Unknown' recap timeline rows. The pins loop now fills an empty address through the shared ~100m- rounded geocode cache - the same entries write-time enrichment writes, so an already-enriched day costs no extra upstream call. All three callers are sync contexts, so the sync geocoder is used. A geocode miss or error leaves the address empty and keeps the pin (the app labels it 'Unknown'); regenerating a summary retroactively fixes history with no migration. Tests: tests/unit/test_daily_summary_location_address_fill.py (filled from geocoder with exact coords preserved, present address skips the geocoder, miss and exception both keep the pin address-less). Verified: pytest tests/unit/test_daily_summary_location_address_fill.py test_daily_summary_zero_coordinate_locations.py -> all passed. * feat(app): OmiMapPreview - one static-map widget with an offline pin-dot canvas Every map preview now funnels through a single widget backed by the authed backend static-map proxy (GET /v1/static-map): URL built by buildOmiStaticMapUrl (pins quantized to 4 decimals to match the server's cache quantization, deduped, capped at 50), image fetched with the session's Authorization header, and a deterministic dark canvas with one white dot per pin while loading, offline, or on any failure - never an error state. Product direction: previews in-app, tapping opens the native map app via MapsUtil.launchMap. The conversation detail geolocation card migrates from the client-keyed getMapImageUrl URL to this widget; getMapImageUrl is removed (maps_util keeps launchMap + place URL), taking the last direct client-side Google Static Maps call with it. Verified: flutter test test/widgets/omi_map_preview_test.dart test/widgets/daily_summary_card_test.dart (widget+URL builder tests); flutter analyze clean on changed files; analyze_ratchet.sh passed. * feat(app): replace CARTO tile maps with static previews; drop flutter_map CARTO began enforcing API keys on basemaps.cartocdn.com (keyless requests now return watermarked tiles), and Omi has no CARTO key. All three flutter_map surfaces now render OmiMapPreview instead - zero remaining tile traffic, no keyed tile provider in the app: - conversation map page: static preview of every ~100m cluster anchor (tap opens the native map app) above a grouped 'conversations at this place' list; single-conversation places open the conversation directly, multi-conversation places keep the cluster bottom sheet; grouping logic and cluster-row keys unchanged - daily summary card: 96px preview strip of the day's pins in the recap carousel (the highest-frequency map surface) - now one cached server-side render per distinct pin set instead of dozens of tiles per card - daily summary detail 'Your Day's Journey': 200px preview; image tap still opens the day's first stop (Apple Maps cannot take waypoints via map_launcher), per-stop timeline rows unchanged flutter_map is removed from pubspec (latlong2 stays - journey grouping uses it). The client-embedded Env.googleMapsApiKey goes with it: all static maps come from the server-restricted key behind the proxy now. envied outputs regenerated; test EnvFields stubs updated to match. Verified: bash test.sh / flutter test -> 1758 passed, 5 skipped, 0 failed; scripts/analyze_ratchet.sh passed (9 lint counts improved). * test(app): update map-surface tests for static previews - daily summary card: assert the preview strip, proxy URL pins/quantize/ dedupe, offline canvas fallback, and no-map-for-invalid-coordinates (was: tile provider request counting) - daily summary detail page: drop the TileProvider seam (widget no longer takes one) - conversation map groups: group-card key + cluster sheet rows replace marker keys; journey/env stubs gain the EnvFields change Verified: flutter test on all four files -> passed. * test(backend): stub utils.conversations.location in the cloud-tasks loader pipeline.py now imports async_resolve_geolocation at module scope; the cloud-tasks stub loader replaces its parent packages with MagicMocks, so the submodule import fails in file isolation (the earlier green run leaned on a module cached by another test file). Adds the explicit stub with an identity passthrough - the real resolver's miss behavior. Verified: BACKEND_UNIT_TEST_FILE_LIST=<this file> bash test.sh -> 91 passed. * chore(app): drop GOOGLE_MAPS_API_KEY from the env template The client no longer embeds a Maps key - all static-map renders go through the server-restricted key behind GET /v1/static-map. * test(backend): provide redis r on the usage-tracking stub set external_integrations now imports utils.conversations.location (daily- summary address fill), which imports the redis client symbol; the usage-tracking stub environment replaces database.redis_db with an empty module, so the import fails in file isolation. Give the stub an r. Verified: BACKEND_UNIT_TEST_FILE_LIST=<this file> bash test.sh -> 21 passed. * docs(backend): note the static-map provider URL budget with current limits * chore(backend): declare the static-map route policy in the manifest * fix(backend): drop the redundant isinstance on the typed pins parameter * fix(app): hold the preview canvas until the auth header resolves The first build fired the authed proxy request before getAuthHeader() resolved; CachedNetworkImage keys its cache by URL, so the later header-arriving setState never re-fetched - a permanent 401 fallback canvas. The widget now renders the pin-dot canvas (which already doubles as the error/offline path) until the header resolves, and only then mounts the network image with the Authorization header attached. The header resolver is injectable (authHeaderProvider) so tests control when it completes; an explicit imageUrl (test seam) skips the gate. Tests: new regression case asserts no CachedNetworkImage while auth is unresolved and the authed image after the completer fires; the card tests repoint URL assertions at the preview widget's pins (URL building has its own unit tests). Verified: flutter analyze clean; flutter test (full) -> 1759 passed. * fix(app): restore the conversation_map_marker automation key on group rows The per-place tappable key predates the static preview; keep it stable for automation (the PR brief promises preserved keys). Verified: flutter test test/unit/conversation_map_groups_test.dart passed. * fix(backend): normalize static-map dimensions, dedup render stampedes, count canvas fallbacks Review findings on the static-map proxy: - Dimension handling: one proportional scale factor (min(1, 640/w, 640/h)) computed once in fetch_static_map replaces the independent per-axis clamps - aspect is preserved and every request that differs only by scale normalizes onto the SAME cache entry (cache key and provider URL both use the effective size). - Stampede dedup: after a miss, a per-key render lock (r.set nx, 30s TTL) elects one renderer; concurrent misses poll the cache (0.25s interval, 15s budget) for the holder's result. Lock-held-timeout and lock-unavailable (Redis broken) both fail OPEN to an unlocked render - a lost lock never becomes a 502, and a broken Redis never pays the 15s wait budget. - record_fallback (component=static_map, provider_static_map -> client_pin_canvas, outcome=degraded) fires before the 502 so the degrade is counted in the shared telemetry. Tests: proportional normalization + shared cache entry, oversized fetch normalizes (size=640x150), concurrent misses render once, waiter polls a foreign lock holder to its hit, wait-timeout renders unlocked, 422s for out-of-bounds width/height via TestClient (Query contract), fallback telemetry kwargs. Verified: pytest tests/routers/test_static_map.py -> 21 passed; pyright (scripts/typecheck.sh) 0 errors, no findings in changed files. * fix(backend): cap daily-summary geocode attempts; boost-exempt static_map:get - The read-time address fill now bounds geocode ATTEMPTS at 10 per summary generation: cache hits are cheap but attempts are the deterministic wall-clock bound (10 x the geocoder's 10s worst case stays inside the job budget). Pins past the cap keep an empty address and the app's 'Unknown' fallback. Test: 11 empty-address pins -> 10 filled, 11th untouched, 10 geocoder calls. - static_map:get joins the boost-exempt rate policies: under prod's RATE_LIMIT_BOOST the 240/h hot-loop cap would otherwise resolve to 24k/h and stop protecting the billable provider calls. Verified: pytest test_daily_summary_location_address_fill.py -> 5 passed; utils.rate_limit_config import shows static_map:get in BOOST_EXEMPT_POLICIES. * test(backend): self-contained enrichment fakes; model the resolver short-circuit - test_sync_geolocation_enrichment no longer imports the transcription-prefs fixture module: sharing it loaded real heavyweight modules (GCS/Firestore protos, anthropic/jiter) inside the stub window, and stub_modules' teardown evicted them so later files broke on re-import (duplicate proto registration, jiter NameError) when run in one process. The fakes are now minimal and self-contained - every heavy leaf is stubbed, light enum modules (stt.outcomes, sync.lanes, sync.telemetry) stay real. - The None-geolocation test now models the real resolver's short-circuit (falsy input returns immediately, attempts recorded only for truthy input) and asserts: resolver called exactly once with None AND zero geocode attempts. Docstring states what is actually proven. - One-line caveat at the cloud-tasks loader injection site: new submodule imports must be added to heavy_deps explicitly (MagicMock parents do not resolve submodules). Migrating that hand-rolled loader to stub_modules would cascade across its 37 call sites - deliberately left as-is. Verified: pytest on the combined focused group (static_map, summary fill, sync enrichment, cloud tasks, sync v2, transcription prefs, geocode resolve) -> 372 passed in one process. | 22 天前 | |
fix(app,desktop): wrap long titles instead of clipping (#19364) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 1 天前 | |
feat(app): mobile UX consistency pass: shared primitives, contract and audit fixes (#18436) * feat(app/ui): add OmiColors tokens and stop using colorScheme.primary as a page background Add lib/ui/omi_tokens.dart: OmiColors (surfaces, text, neutral accent, status), OmiType ramp, OmiRadius, OmiSpacing, OmiMotion (honours Reduce Motion) and OmiHaptics. 53 sites read Theme.of(context).colorScheme.primary as a page or app bar background because the theme's primary was black. They now name OmiColors.surface0 (same black), so the theme can get a real accent. In the four files over the line-count threshold the explicit colour is dropped instead; the theme default is the same black. AppStyles and ResponsiveHelper get doc comments pointing at the tokens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): one theme and shared controls for navigation, buttons, sheets and page states Theme (lib/ui/omi_theme.dart, used by main.dart and the visual_audit harness so they cannot drift): a real dark ColorScheme with the white accent as primary, black scaffold, app bar theme (black, flat, centred 17pt semibold title), white progress indicators (the unstyled spinners were black on black), floating surface2 snack bars, surface1 dialogs and sheets, neutral switches. Material 2 stays on. MaterialApp.builder now syncs Intl.defaultLocale to the resolved locale. Components under lib/ui/components (barrel lib/ui/ui.dart): OmiBackButton / OmiCloseButton (platform glyph, localized label, 44pt target, circled variants), OmiIconButton (required label, tooltip), OmiButton (primary/secondary/destructive/tertiary, regular/compact, async loading that always clears), OmiSpinner, OmiLoadingState / OmiEmptyState / OmiErrorState, showOmiSheet and OmiSheetScaffold, OmiSwitch, OmiSettingsGroup / OmiSettingsRow / OmiSectionHeader, OmiSearchField, and omiPageRoute for code that needs a Route object. HeaderCircleButton is now a wrapper over OmiIconButton.filled and AnimatedLoadingButton over OmiButton (fixes the spinner that never cleared after a throw, and the 40pt target). find_device's two unstyled ElevatedButtons keep their black fill explicitly now that primary is white. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/settings): move data privacy, Asana and ClickUp settings onto the shared controls Proving set for the new primitives. Data & Privacy uses OmiBackButton, a section header and an OmiSettingsGroup of app rows, OmiEmptyState, and drops its accent tint for neutral tokens. Asana and ClickUp show an app bar with a back button and OmiLoadingState during their first load (the page used to be an invisible spinner with no way out), OmiSpinner for section loads, and tokens for their colours. The shared IntegrationSettingsPage header uses OmiBackButton and a labelled refresh OmiIconButton, and its disconnect control is an OmiButton.destructive. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): one adaptive confirmation system (showOmiConfirm/showOmiAlert) getDialog, ConfirmationDialog, OmiConfirmDialog and AppDialog become thin adapters over OmiAlertDialog: Cancel always shown and always closes, real CupertinoDialogActions on iOS (destructive red, safe choice bold), localized defaults, a destructive flag, and AppDialog.show no longer drops singleButton and onCancel. Non-page raw AlertDialogs in providers/widgets migrated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): OmiFeedback toasts and OmiClipboard One floating neutral toast at a time with contract timings (confirm 1.5 s, info 4 s, error 8 s with close, undo 5 s with no close, resolving whether the reader undid). Snackbars with actions no longer persist forever. The home shell registers its tab-bar clearance. AppSnackbar's ~180 call sites route through it. OmiClipboard refuses empty text and skips its toast on Android 13+ where the system already confirms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): OmiDateFormat, OmiDuration and shared speaker names Dates follow the app locale and the 24-hour setting; durations have one offset/compact/long vocabulary (the playback bar no longer drops hours). SpeakerNames is the single source for speaker labels in app and export: You / person / Omi / dense Speaker N, localized in copied transcripts. Legacy helpers delegate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): PromptQueue for startup and background prompts Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(checks): INV-UI-3 mobile UX contract ratchet Per-rule, per-file no-increase ratchet over hand-rolled back glyphs, PageRouteBuilder, raw sheets/dialogs/snackbars/clipboard, date patterns, colour/font-size/radius literals, raw spinners, '...' and hardcoded Text copy under app/lib, with an allow-comment escape hatch and a test proving each rule fires. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(app): mobile UX contract The numbered rules and the one primitive for each: leaving a surface, sheets, controls, destructive actions (D5), dialogs, feedback timing, copying, dates and durations, speakers, tokens, words (D3), settings (D4), page states, prompts, permissions and accessibility, plus the D1/D2/D6 navigation calls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/ui): dense speaker numbering on live and saved transcripts Both transcript renderers pass the conversation's segments to speakerLabel so Speaker N is numbered per conversation. Drops the dead duplicate formatChatTimestamp in widgets/transcript.dart, makes Material OmiAlertDialog scrollable, and points primitive docs at the contract's sections. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/l10n): contract words sweep Task(s) replaces Action Item(s)/To-Do in every locale (D3); Omi capitalised; '…' instead of '...'; search placeholders drop the ellipsis; Try Again, Not Now, OK, Title Case buttons. New keys: filterBySpeaker, notNow, discard, keepEditing, discardChangesTitle, discardChangesMessage. Translates the nine live-capture keys that only existed in English and removes five orphaned locale keys, so scripts/l10n.py check passes. The upgrade alert offers Not Now (postpone, not ignore-forever) and Update, honours barrierDismissible, and loses the emoji. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app/ui): export feedback, format and prompt primitives from the ui barrel; token colours Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app/ui): follow the Title Case clear-search label Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/l10n): strings for the conversation detail pass Link Event sheet, calendar event actions, search result navigation, playback labels, the share confirmation, the Tasks tab counts and empty state, and the speaker identify label, with translations for every locale. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): conversation detail on the shared primitives - D2: no body-wide swipe to another conversation and no PageRouteBuilder; Transcript/Summary(/Tasks) swipe as tabs, the Tasks tab exists only when there are tasks. - Header: OmiBackButton.circled plus same-size OmiIconButton circles (star, share, more) and a compact Ask Omi button that pushes Chat (D1). - Share asks before making a private conversation public and reverts it when the share sheet reports a dismissal. - Copy Conversation ID and Test Prompt only in developer mode; copies go through OmiClipboard, toasts through OmiFeedback. - Link Event and calendar event sheets on showOmiSheet with OmiDateFormat, l10n and full attendee names; visibility, summary template, name speaker and edit segment sheets on the shared shell. - Audio download sheet pops with its own context, offers Cancel and blocks back with PopScope. - Title: single line, Done, Untitled placeholder, saves on blur with an announced outcome; detail date/duration use OmiDateFormat/OmiDuration.compact (same rule as the list). - Tasks tab: tapping a task opens the shared task editor; labelled count pills; Tasks wording (D3); OmiEmptyState. - Edit segment sheet: speaker from SpeakerNames (You for the owner), Save and Cancel, discard guard. - Extract calendar sheets, search bar, transcript tab and tasks tab; delete dead ActionItemsListWidget, GetSheetTitle, GetDevToolsOptions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): keep INV-UI-3 allow comments on the flagged line Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): transcript bubbles and playback bar on the shared primitives Transcript: one SpeakerNames resolver per build (dense numbering, live people), no transcription-provider name, the start time as an OmiDuration offset, a labelled 44pt play button, labelled speaker targets, neutral user bubble and search highlight (INV-UI-1), localized 'translated by Omi', and getLastTranscript numbers speakers across the whole conversation. Playback bar: neutral surfaces instead of purple, a labelled 32pt play/pause in a 44pt target, a labelled stop button, OmiSpinner, OmiFeedback errors, the summary pill ellipsizes by layout instead of a hand-made '...', and the summary template sheet opens through showSummarizedAppsSheet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/onboarding): shared permission row, real progress, back and resume - OmiPermissionRow (lib/ui/components): reason, Allow -> system prompt, Allowed, Open Settings when the system will not ask again. One OnboardingPermissionsPanel drives both the first-run step and the returning-user interstitial; Continue never fires prompts and 'Always' location is no longer asked during first run. - Onboarding wrapper: placeholder pages removed, progress dots count the six real steps with a spoken 'Step N of M', PopScope makes system back step back, the last reached step is saved per account and resumed. - Consent step offers 'Use a Different Account' (sign out, back to start). - Acquisition survey is skippable; language step saves with a busy button and a Try Again error; picker selects on tap. - Steps share OnboardingCard (content scrolls, footer stays; min-height, no fixed text heights) and OmiButton; completion screen localized, purple gone. - Voice introduction: mic refusal shows the permission row; 'Skip Question' vs 'Skip'; device tutorial exits by a trailing close X. - Permission reasons rewritten, platform-neutral Location Services copy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/conversations): one delete path with Undo, row menu, bulk actions, recaps page - Conversation delete (list swipe, row menu, detail page) goes through one path: confirm unless opted out ("Don't ask again" is allowed because Undo backs it), then an Undo toast backed by the provider's pending-delete window (now 6 s, longer than the 5 s toast; the toast commits early when it closes) (D5). - Long-press on a conversation row opens one context menu: Open, Star/Unstar, Move to Folder, Share, Select, Delete. The selection bar gains Move and Delete (always confirmed) beside Merge. - Daily Recaps is its own pushed page with a back button; Home's View All pushes it and the Conversations tab never switches into a recap mode. Pull-to-refresh reloads recaps and a failed load offers Try Again. Recap counts are labelled. - One date filter for list and search, shown as a removable chip; the search field is seeded from the provider and uses OmiSearchField. - Dates and lengths via OmiDateFormat / OmiDuration (Today gets a header, the New badge sits beside the time, WAL/recording offsets keep their hours). - Discarded rows read "Discarded · 12s" with a plain-text snippet; blank titles read "Untitled Conversation". - Shared capture state table (capture_state_labels.dart) for the in-progress card; the processing card shows its real start time. - Local recording swipe always confirms. Sheets, dialogs, toasts, spinners and back buttons in conversations/, sync and recap pages move onto lib/ui. - Offline Sync is the one name for the device-storage sync screen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app/conversations): cover Undo-backed delete, row menu, recaps page and capture labels - Provider: the pending-delete window outlasts the Undo toast; undo restores, commit sends once, a regroup during the window keeps the row hidden. - Widgets: Undo restores / timeout commits / bulk toast counts; long-press menu entries and Select; untitled and discarded row titles; Today header; the date filter chip; Daily Recaps page back button, labelled counts, Try Again and pull-to-refresh. - Unit: the shared capture state table (an audio interruption is Paused). - The Home preview's row menu leaves out Select (no selection bar there); the in-progress card's photo count is labelled; last raw spinner and "..." in the capture card go. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/pairing): pairing recovery, one Bluetooth prompt, shared sheets - Find devices: a missing Bluetooth/location permission goes through the one BluetoothReadiness guidance prompt (Open Settings); the local 'Enable Bluetooth' dialogs are gone. - Nothing found: 'Scan Again', 'How to Pair' (connection guide), then 'Contact Support' instead of a lone mailto link. - Offline saved device: says how to wake it and 'Try Again' rescans (the old Retry did nothing). - Firmware compatibility warning is an acknowledge-only alert; the non-critical one keeps its opt-out row. - Device rows, guide cards and capture cards are real buttons with labels; battery shows a glyph and percentage with a spoken label, no emoji. - Connection guide, pairing steps, Ray-Ban picker/setup and Apple Watch setup use showOmiSheet; Apple Watch is only offered on iOS; 'I've Done This' becomes 'Done'. - Connect page: OmiBackButton, labelled settings button, and connecting returns to the existing Home (HomeNavigation.returnHome) instead of stacking a second one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): edit-sheet dirty guard and row long-press menu primitives showOmiEditSheet/OmiEditSheet own the drag so swipe-down, scrim tap, the close X and system back all ask "Discard changes?" while an editor is dirty (the framework's drag-to-dismiss pops without asking). showOmiRowMenu is the one long-press menu shape for list rows (Open, edit actions, Select, Delete). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): follow the warning token on the paused dot and the Conversation Map title Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/l10n): lane 2 strings for memories, tasks, goals and people Sheet titles without emoji, read-only memory hint, Undo toasts, task plural titles and counts, shared-task strings, memory review card and People page labels, with every locale translated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/memories): Undo-only deletes, openable read-only memories, guarded edit sheet - Every memory delete (swipe, long-press menu, edit sheet) goes through deleteMemoryWithUndo: no "cannot be undone" dialog, the shared OmiFeedback undo toast with no X, and a commit scoped to the memory the toast offered. The provider's pending window becomes an 8 s backstop that outlasts the toast. - Every memory opens: locked/superseded/ledger rows open the same sheet read-only with full text; swipe-delete only where editing is allowed. - Long-press menu Open / Edit / Delete. - Edit and new-memory sheets: explicit Cancel and Save, dirty guard, OmiButton. - Page: OmiBackButton, OmiSearchField, labelled 44pt header buttons, neutral FAB, OmiEmptyState / error state, management sheet on showOmiSheet with a confirm that names the action and toasts that reach the page after the sheet closes. - Memory graph: OmiBackButton, localized title, OmiLoading/Error/EmptyState, no purple. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/tasks): fixed swipe meaning, Undo deletes, guarded task and goal sheets - One swipe meaning on every task row at every indent: right completes (or reopens), left deletes. Indent/outdent move to the long-press menu (and drag keeps its horizontal indent); long-press shows Open / Mark Complete / Indent / Outdent / Select / Delete. - Single task and goal deletes are immediate with an Undo toast, backed by new staged-delete APIs on ActionItemsProvider and GoalsProvider (nothing hits the server until the toast closes). Bulk and clear-completed deletes confirm with a plural, verb-named dialog (the old one said "This will only remove deadlines"). - ActionItemFormSheet keeps its constructor; adds showActionItemFormSheet, explicit Cancel/Save, a close X in edit mode, a dirty guard and instant completion. The date picker and time picker are neutral (no purple) and locale/24-hour aware. - Home and Tasks share one GoalFormSheet (Cancel/Save, dirty guard, localized). - Tasks page: OmiSearchField "Search tasks", labelled overflow button, OmiEmptyState, neutral FAB/selection/drop indicators, localized "Exported to", counts with labels. - Selection bar, shared-tasks sheet and Home Today card on the primitives (44pt targets, rows open the task sheet). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/people): tapping a sample plays it; delete is a labelled control behind a confirm People page gets OmiBackButton, labelled Add/Help controls, an adaptive person dialog, OmiEmptyState/OmiLoadingState and confirms that name the action. The memory review card is localized with 44pt controls and tokens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): one header for live, processing and saved conversations; one media viewer - Live and processing pages share ConversationStateAppBar: the circled back button of the saved page and the state as the title (Recording / Paused / Reconnecting / Processing, one local mapping until lane 1a's shared capture_state_labels lands). No emoji status, no back button inside the title row. - Live page: remove the unreachable Summary tab and its TabController; Process Now is an OmiButton, mute a labelled OmiIconButton; the stop confirmation is showOmiConfirmWithOptOut with a verb; speaker names from SpeakerNames; photo-group time from OmiDateFormat and a localized photo count; tokens for the timeline and the upload indicator. - Processing page: one view (the transcript so far) under the shared header instead of Content/Summary tabs whose Summary only said 'Processing'. - Media viewer: always a full-screen modal with a trailing OmiCloseButton and swipe-down to close (MediaViewerPage.open), labelled share button, OmiSpinner; photo grids and the live timeline open it that way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/chat): contract composer, drawer and failed-reply retry Chat is a pushed page with a leading back (D1). Composer controls are labelled 44pt targets; Send that cannot send (offline, uploading) is drawn disabled with an offline hint. The apps drawer keeps Clear Chat at the bottom behind a destructive confirm, and each app has one labelled Disable (with Undo). A failed reply shows a localized error with Try Again that resends the user's message instead of raw server text. The feedback sheet is localized with a neutral Submit, thumbs read Helpful / Not Helpful, and copy/share analytics send the message length, not its text. Adds the lane-3 l10n keys for chat, apps and announcements. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/apps): one app action button, one consent dialog, Undo on disable AppListActionButton reads Open / Enable / View with the same meaning in every store list (no decorative Install). Enabling an app that works outside Omi asks one localized question naming what leaves Omi; Disable is immediate with a 5 s Undo on the detail page and in the chat drawer. The detail page moves onto the header, button, sheet, dialog and feedback primitives, splits its cards into widgets, and its cancel-subscription dialog reads Keep Subscription / Cancel Subscription. Referral and app home pages share one web page with a loading state, an error state with Try Again, and back within web history. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/announcements): answer-only dismissal through the prompt queue Announcements, feature screens and the changelog present through PromptQueue. The dialog's scrim no longer dismisses; the close X or the call to action marks an announcement seen, Not Now postpones it. The feature screen is a full-screen modal route with a trailing close; the changelog uses the shared sheet shell and error state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/ai-app-generator): one leading back, system back steps to the prompt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app): sweep conversation sheets and small widgets onto the primitives - Share via SMS and Create Template sheets use showOmiSheet, OmiSearchField, OmiButton, OmiSwitch, OmiEmptyState/OmiLoadingState and tokens; no purple (INV-UI-1). Create Template blocks back while the app is being created and opens the template picker from the navigator, not its own popped context. - Markdown and expandable text: neutral search highlight and link colour. - OmiSpinner and type/radius tokens in the speaker summary action, test prompts, photo image, person chip and waveform section. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(app/ui): edit sheets and row menus in the barrel and the contract Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): conversation detail UX contract cases Attendee names, detail duration rule, speaker numbering across the getLastTranscript window, transcript bubble chrome (no provider, offset time, 44pt labelled play), the shared capture state header, search bar position, the audio download sheet's own-context close/Cancel/back, the edit segment discard guard, and the media viewer's trailing close. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): show conversation developer tools in debug builds too No settings row writes devModeEnabled yet, so gating on it alone would hide Copy Conversation ID and Test Prompt from everyone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * wip(app): lane 3 checkpoint after spend-limit stop (uncommitted work preserved, not reviewed) * wip(app): lane 4 checkpoint after spend-limit stop (uncommitted work preserved, not reviewed) * wip(app): lane 5 checkpoint after spend-limit stop (uncommitted work preserved, not reviewed) * test(app/settings): device page stub getters and sign-out keep-list The one device page reads battery, charging and firmware getters the double-tap test stub lacked, and its connected picture pulses; stub the getters and turn on Reduce Motion so pumpAndSettle settles. Cover the Sign Out keep-list: paired device and display preferences survive, account values are cleared, and no credential is ever kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): name speaker sheet saves through the shared OmiButton Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/ai-app-generator): sweep the generator page onto the primitives App bar with a leading OmiBackButton on both steps; neutral accent instead of indigo/purple (INV-UI-1); OmiButton for Create App (busy state built in), OmiIconButton for send and regenerate-icon (labelled, 44pt), OmiSwitch for Make Public / Paid App, OmiSpinner, tokens for every colour, size and radius. Description and Features headings come from l10n; category and capability names use the shared localized titles. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/lane-3): plans sheet in the sheet shell, review fixes to the WIP checkpoint - chat: the quota plans sheet opens through showOmiSheet (lane 4 handoff). - chat evidence card: localized screen-reader hint. - capability apps page: localized category titles, token refresh colours. - const constructors and block-bodied ifs the WIP left for the analyzer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/chat): conversation links push through routeToPage; evidence test hosts l10n Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app): every push takes the platform route; shared-task link copy localized - routeToPage builds its route with omiPageRoute (defaultTargetPlatform) instead of dart:io Platform, the Foundation A handoff. - Deep links in AppShell, the People link in the name-speaker sheet and the firmware prompt in DeviceProvider push through routeToPage/omiPageRoute, never a bare MaterialPageRoute (iOS back swipe intact). - The shared-tasks deep link's not-found toast and unknown-sender fallback were hardcoded English; now sharedTasksLinkExpired and sharedTasksUnknownSender in all 49 locales. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): drop imports the ui barrel already provides Lane 2's edit-sheet and row-menu primitives joined the ui.dart barrel, which left seven direct imports redundant and pushed unnecessary_import over the analyzer baseline (CI's Dart Analyze & Tests failed on it). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/payments): labelled overflow menu, titled country picker sheet Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): people samples follow the one Voice Profile name Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/home): links open inside the one Home; startup prompts queue - Notification taps and app links open inside the Home already on screen (HomeNavigation.openRoute: pop to Home, select the tab, push the page) instead of pushReplacement-ing a second Home over whatever was showing. Home registers itself while mounted, so "Done" at the end of the firmware and connect flows also returns to it (nav #3). - Deep links move to home_deep_links.dart: parent before child (the Settings sheet opens before Data & Privacy is pushed over it), every push through routeToPage, and a removed app or deleted conversation now says so instead of only logging (nav #18). - D1: the home chat bar, its mic, chat deep links and the Ask Omi / Voice quick actions push chat as a normal page (no fullscreenDialog). The chat bar is one labelled button. - D6: Android back on the Tasks, Conversations or Apps tab returns to Home before it leaves the app (root PopScope). - Settings: the language / speech profile / transcription model comparison runs once the reader is back on Home, not the moment the sheet opens (onboarding-home #25). - Startup prompts go through PromptQueue (onboarding-home #23): upgrade alert (a required update first), changelog + announcements, the CV1 tutorial (waits for the pendant), firmware announcements, the Plus-meter plans sheet (only when the meter crosses its threshold, once per session, showOmiSheet) and Bluetooth guidance. HomePromptGate holds the queue while phone recording, on a call or during a firmware update and pumps it when that clears; passive pendant capture does not hold it. - Handoffs applied: dead toggleDailySummaries call removed (lane 1a); sync, task-export and quick-action pushes use routeToPage (lanes 2, 4). Header controls use neutral tokens (INV-UI-1) and OmiHaptics. - New strings (7 keys, every locale): appNotFoundOrRemoved plus the startup failure and discard-recording strings used by the next commits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/startup): startup failure offers Try Again and Contact Support - The failure screen words the message by error class: a rejected build configuration (Env / startup-routing checks, now thrown as StartupConfigurationError) says the build is at fault and offers Contact Support; any other failure says to check the connection and offers Try Again, which re-runs start-up, plus Contact Support (onboarding-home #27). - main() moves start-up into _start() so Try Again can run it again; ServiceManager.init, which refuses a second call, runs once. - Localized without a localization scope (lookupAppLocalizations for the device locale, English fallback); tokens replace literal colours/sizes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/speech-profile): confirm before discarding a recording in progress - Back or swipe while the voice sample is recording asks "Discard Recording?" (Discard / Keep Recording) instead of dropping it (onboarding-home #26). - "Reconnecting…" is an info toast, not an error. - The help control is a labelled OmiIconButton ("How to take a good sample?") opening showOmiAlert; back is OmiBackButton. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/home): header pill and record button expose their actions - The device pill and the record button wrap their gestures in Semantics(excludeSemantics: true), which also dropped the tap and long-press actions; the Semantics node now carries onTap (and the record options long-press), so VoiceOver/TalkBack can activate them (onboarding-home #15, #24). - Phone-mic offline fallback notice is an OmiFeedback info toast. - Sweep: auth-gate spinner is OmiSpinner; welcome Continue uses OmiHaptics; OmiGlass OTA page formatted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): delete webhooks.dart, reachable only from the removed dev-tools widget Its one importer was GetDevToolsOptions in conversation_detail/widgets.dart, which nothing constructed and lane 1b deleted. The dead-code ratchet flags the now-unreachable file; the webhook settings themselves live in DeveloperModeProvider and the users API and are unaffected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/home): a link counts as opened once its page is pushed openHomeDeepLink awaited each push, so HomeNavigation.openRoute (and the notification-open telemetry behind it) only resolved when the reader closed the page. It now resolves once the destination is on screen. A missing app or conversation is an info toast, as the contract says; the contract names HomeNavigation.openRoute / returnHome. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 1 (ZCode GLM-5.3) - A memory's Undo restores only that memory: restoreLastDeletedMemory takes the toast's id, so a stale toast cannot restore a newer delete (test added). - A failed task delete no longer inserts a Home-only task into the Tasks list; the staged index goes straight to _restoreDeletedItem, which skips -1. - The conversation pending-delete window is 10 s, so an Undo toast that starts late behind other snack bars still gets its full time; the toast still commits early when it closes. - The integration page's button reads "Disconnect" instead of stripping the question mark from a translated sentence (broke ¿…? and ? locales). - mounted checks after the long awaits in the Create Template sheet and Wrapped 2025 generation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): keep memories_provider under its line-count ceiling Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/apps): app creation forms on the shared form pieces and primitives Submit App and Manage App draw their screenshot strip and GitHub URL card from app_form_fields (screenshots open as a modal viewer with a trailing close), use AppFormCard/section titles/input decoration, OmiSwitch rows for Public/Paid (no indigo/green thumbs), OmiButton for submit/update, a leading OmiBackButton, and l10n for every string. Update asks with showOmiConfirm and a verb button (Update App); Submit's confirm reads Submit App; Start Earning is an Omi sheet with Not Now. App metadata pickers are Omi sheets, App ID copy goes through OmiClipboard. API keys: adaptive dialogs, destructive Revoke confirm, OmiFeedback toasts, localized dates. Add MCP Server: OmiBackButton, OmiButton, OmiFeedback. Provider snackbars and picker titles localized. New l10n keys: generateDescription, selectImageFileTitle, selectThumbnailImageTitle, appIdNotFoundError, manifestRefreshedSuccess, manifestRefreshFailed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/apps): store home, filters and reviews on the primitives The store's filter sheet opens in the Omi sheet shell; header icon controls are labelled 44pt OmiIconButtons; empty search is OmiEmptyState. Reviews: OmiBackButton, adaptive reply dialog, OmiFeedback toasts, OmiButton busy state, localized Add/Edit Your Review and rating counts, readable (not grey-700) secondary text. App summary and capabilities card localized. Screenshot taps on the detail page open through openAppScreenshots (modal, trailing close). Review avatar palette drops its purple entry (INV-UI-1). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app/apps): Disable hides at once, commits after the Undo toast, Undo restores Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/lane-3): localized pricing value on Manage App and file-picker title in chat Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 2 (lane 5) - A throw while preparing the Omi firmware update (for example a corrupt download that fails zip extraction) now ends in the failed state instead of an installing spinner with back blocked and every prompt held. - Home drops the prompts it enqueued when it is disposed, so after sign-out and sign-in the new Home's announcements, device tutorial and firmware notices are not deduplicated against dead closures. - The speech step re-reads the microphone permission when the app resumes, so granting it in Settings clears the Open Settings row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): retire AnimatedLoadingButton Lane 3 moved its last callers (payments, app detail) onto OmiButton, which owns the busy state and recovers when the action throws; the dead-code ratchet flags the unreachable widget. Its throw-recovery test now runs against OmiButton directly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 3 (lane 3) - Try Again on a failed reply releases the composer when the failure was already cleared, and the first app message releases it even if chat closes mid-request (both could leave typed sends blocked). - Enabling an app again inside its 5 s disable Undo window cancels the pending disable instead of being overwritten when the toast ends; tests for that and for a refused disable. - Stripe setup names the selected country from the full list, not the picker's filtered one, and always stops polling on dispose. - The AI app generator shows a failed submit on the generated view. - OmiWebPage's Try Again reloads the page that failed, not the first URL; a malformed app home URL shows the error state instead of throwing. The reported Stripe double-submit is already prevented: OmiButton ignores taps while its onPressed future runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 4 (#17297 merge and rulings) - Share from a conversation row now behaves like the page: a private conversation asks first ("Anyone with the link can view") and goes back to private when the share sheet is dismissed (was: made public at once). - Developer Settings gains a Conversation Developer Tools switch (writes devModeEnabled), so release users can still reach Copy Conversation ID and Test Prompt; 2 keys in 49 locales. - The row's Separate… always disposes its controller; tests cover a confirmed separation from a row (the other recording, never the row) and the row share confirmation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): phone number verified date holds in every timezone The fixture was UTC midnight, which is Jan 1 in Pago Pago; CI runs the suite at UTC+14 and UTC-11. Local noon reads Jan 2 everywhere. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): conversation title and emoji share one line in the header The title field kept Material's 48pt input height, which pushed the title below the emoji; it is dense now and the one-line row centres both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): every voice-profile entry opens the guided introduction David's ruling (2026-09-24): the voice profile is the guided fill-in-the- blank flow from #14514 (sentence starters, review, then voice, memories and a goal). Two entry points still reached the old question-based page: the Settings/Profile Voice Profile row for anyone who already had a profile, and the capture card's "Teach Omi your voice". Both now open the guided flow. The old page is unreachable, so it goes, with its two widgets, the getDialog helper and the fade-in text widget only it used (dead-code ratchet). Open PR #12531 edits the old page and will need to drop those hunks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): Star and Share back in the conversation top bar; record conversation actions David, 2026-09-24 (overrides the earlier one-overflow ruling): - the conversation page's top bar is Ask Omi (the primary, filled), Star and Share as matching 44pt icon buttons, then one "…" holding Rename, Move to Folder, Recordings and the rest, Delete last and destructive; - one registered event, Conversation Action, records every conversation action with two closed enums: action (ask_omi, star, unstar, share, rename, move_folder, recordings_open, separate, delete, copy_transcript, …) and surface (top_bar, overflow, detail_body, row_long_press, row_swipe). No ids, titles or content. It goes through the C7 typed registry (contracts/analytics/events.json, generated Dart, a presence consumer) and AnalyticsManager, so consent and queueing apply. Tests cover the event shape for every action and the top bar, overflow, row long-press and row swipe call sites. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): the people chip names only named speakers David, 2026-09-24: never label a participant "Speaker 1". The conversation header's people chip now shows the first named participant ("You" for the account owner) and counts everyone else: "David + 3 others" (participantsSummary, an ICU plural in 49 locales). With nobody named it hides; a linked calendar event's attendees still stand in when the transcript names no one. The transcript's own per-line speaker labels are unchanged. Tests cover named, mixed and none. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: drop pointers to the deleted speech-profile page Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 5 (voice profile and people chip) - SpeechProfileProvider drove only the deleted question page; the guided introduction records through GuidedVoiceIO. Remove the provider, its registration, the onboarding back-step close() that no longer stopped anything, and its test. - Drop pointers to deleted files: the addressability baseline entry and the logout flow's covers (now sign_out.dart); a stale doc paragraph. - participantsSummary carries few/many forms so Polish, Czech and Slovak agree for 2-4 and 5+; other Slavic and Baltic locales use a count-agnostic "one more" form. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 5 天前 | |
feat(app): add System / Light / Dark appearance setting on mobile (#19378) * feat(app): add system-aware light and dark appearance * fix(app): restore light appearance contrast on v1 surfaces * fix(app): provide appearance in visual audit harness Failure-Class: none * test(app): support light mode in visual audit harness * fix(app): correct light-mode color contrast Failure-Class: none * fix(app): improve remaining light-mode contrast Failure-Class: none * fix(app): use palette tokens for light transcript bubbles Replace the two hardcoded light-mode bubble colours with surface2 and a pale alpha-blend of the speaker colour, so the mobile UX contract's colour-literal ratchet stays flat. Failure-Class: none Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 2 天前 | |
feat(app): add System / Light / Dark appearance setting on mobile (#19378) * feat(app): add system-aware light and dark appearance * fix(app): restore light appearance contrast on v1 surfaces * fix(app): provide appearance in visual audit harness Failure-Class: none * test(app): support light mode in visual audit harness * fix(app): correct light-mode color contrast Failure-Class: none * fix(app): improve remaining light-mode contrast Failure-Class: none * fix(app): use palette tokens for light transcript bubbles Replace the two hardcoded light-mode bubble colours with surface2 and a pale alpha-blend of the speaker colour, so the mobile UX contract's colour-literal ratchet stays flat. Failure-Class: none Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 2 天前 | |
fix(mobile): ask more users for a store review after value moments (#19707) * fix(mobile): ask more users for a store review after value moments Failure-Class: none * docs(mobile): note store review prompt timing * fix(mobile): contain review suppression storage errors | 10 小时前 | |
chore(app): dart format --line-length 120 on all Dart files Run dart format across all 456 Dart files to fix formatting drift. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> | 6 个月前 | |
fix: add shimmer timeout to prevent battery drain Created ShimmerWithTimeout widget that automatically falls back to a static skeleton after 5 seconds. Replaced all Shimmer.fromColors usages across the app with ShimmerWithTimeout to prevent indefinite shimmer animations that drain battery. Files updated: - New: widgets/shimmer_with_timeout.dart - 24 files updated to use ShimmerWithTimeout | 7 个月前 | |
feat(app): mobile UX consistency pass: shared primitives, contract and audit fixes (#18436) * feat(app/ui): add OmiColors tokens and stop using colorScheme.primary as a page background Add lib/ui/omi_tokens.dart: OmiColors (surfaces, text, neutral accent, status), OmiType ramp, OmiRadius, OmiSpacing, OmiMotion (honours Reduce Motion) and OmiHaptics. 53 sites read Theme.of(context).colorScheme.primary as a page or app bar background because the theme's primary was black. They now name OmiColors.surface0 (same black), so the theme can get a real accent. In the four files over the line-count threshold the explicit colour is dropped instead; the theme default is the same black. AppStyles and ResponsiveHelper get doc comments pointing at the tokens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): one theme and shared controls for navigation, buttons, sheets and page states Theme (lib/ui/omi_theme.dart, used by main.dart and the visual_audit harness so they cannot drift): a real dark ColorScheme with the white accent as primary, black scaffold, app bar theme (black, flat, centred 17pt semibold title), white progress indicators (the unstyled spinners were black on black), floating surface2 snack bars, surface1 dialogs and sheets, neutral switches. Material 2 stays on. MaterialApp.builder now syncs Intl.defaultLocale to the resolved locale. Components under lib/ui/components (barrel lib/ui/ui.dart): OmiBackButton / OmiCloseButton (platform glyph, localized label, 44pt target, circled variants), OmiIconButton (required label, tooltip), OmiButton (primary/secondary/destructive/tertiary, regular/compact, async loading that always clears), OmiSpinner, OmiLoadingState / OmiEmptyState / OmiErrorState, showOmiSheet and OmiSheetScaffold, OmiSwitch, OmiSettingsGroup / OmiSettingsRow / OmiSectionHeader, OmiSearchField, and omiPageRoute for code that needs a Route object. HeaderCircleButton is now a wrapper over OmiIconButton.filled and AnimatedLoadingButton over OmiButton (fixes the spinner that never cleared after a throw, and the 40pt target). find_device's two unstyled ElevatedButtons keep their black fill explicitly now that primary is white. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/settings): move data privacy, Asana and ClickUp settings onto the shared controls Proving set for the new primitives. Data & Privacy uses OmiBackButton, a section header and an OmiSettingsGroup of app rows, OmiEmptyState, and drops its accent tint for neutral tokens. Asana and ClickUp show an app bar with a back button and OmiLoadingState during their first load (the page used to be an invisible spinner with no way out), OmiSpinner for section loads, and tokens for their colours. The shared IntegrationSettingsPage header uses OmiBackButton and a labelled refresh OmiIconButton, and its disconnect control is an OmiButton.destructive. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): one adaptive confirmation system (showOmiConfirm/showOmiAlert) getDialog, ConfirmationDialog, OmiConfirmDialog and AppDialog become thin adapters over OmiAlertDialog: Cancel always shown and always closes, real CupertinoDialogActions on iOS (destructive red, safe choice bold), localized defaults, a destructive flag, and AppDialog.show no longer drops singleButton and onCancel. Non-page raw AlertDialogs in providers/widgets migrated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): OmiFeedback toasts and OmiClipboard One floating neutral toast at a time with contract timings (confirm 1.5 s, info 4 s, error 8 s with close, undo 5 s with no close, resolving whether the reader undid). Snackbars with actions no longer persist forever. The home shell registers its tab-bar clearance. AppSnackbar's ~180 call sites route through it. OmiClipboard refuses empty text and skips its toast on Android 13+ where the system already confirms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): OmiDateFormat, OmiDuration and shared speaker names Dates follow the app locale and the 24-hour setting; durations have one offset/compact/long vocabulary (the playback bar no longer drops hours). SpeakerNames is the single source for speaker labels in app and export: You / person / Omi / dense Speaker N, localized in copied transcripts. Legacy helpers delegate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): PromptQueue for startup and background prompts Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(checks): INV-UI-3 mobile UX contract ratchet Per-rule, per-file no-increase ratchet over hand-rolled back glyphs, PageRouteBuilder, raw sheets/dialogs/snackbars/clipboard, date patterns, colour/font-size/radius literals, raw spinners, '...' and hardcoded Text copy under app/lib, with an allow-comment escape hatch and a test proving each rule fires. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(app): mobile UX contract The numbered rules and the one primitive for each: leaving a surface, sheets, controls, destructive actions (D5), dialogs, feedback timing, copying, dates and durations, speakers, tokens, words (D3), settings (D4), page states, prompts, permissions and accessibility, plus the D1/D2/D6 navigation calls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/ui): dense speaker numbering on live and saved transcripts Both transcript renderers pass the conversation's segments to speakerLabel so Speaker N is numbered per conversation. Drops the dead duplicate formatChatTimestamp in widgets/transcript.dart, makes Material OmiAlertDialog scrollable, and points primitive docs at the contract's sections. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/l10n): contract words sweep Task(s) replaces Action Item(s)/To-Do in every locale (D3); Omi capitalised; '…' instead of '...'; search placeholders drop the ellipsis; Try Again, Not Now, OK, Title Case buttons. New keys: filterBySpeaker, notNow, discard, keepEditing, discardChangesTitle, discardChangesMessage. Translates the nine live-capture keys that only existed in English and removes five orphaned locale keys, so scripts/l10n.py check passes. The upgrade alert offers Not Now (postpone, not ignore-forever) and Update, honours barrierDismissible, and loses the emoji. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app/ui): export feedback, format and prompt primitives from the ui barrel; token colours Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app/ui): follow the Title Case clear-search label Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/l10n): strings for the conversation detail pass Link Event sheet, calendar event actions, search result navigation, playback labels, the share confirmation, the Tasks tab counts and empty state, and the speaker identify label, with translations for every locale. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): conversation detail on the shared primitives - D2: no body-wide swipe to another conversation and no PageRouteBuilder; Transcript/Summary(/Tasks) swipe as tabs, the Tasks tab exists only when there are tasks. - Header: OmiBackButton.circled plus same-size OmiIconButton circles (star, share, more) and a compact Ask Omi button that pushes Chat (D1). - Share asks before making a private conversation public and reverts it when the share sheet reports a dismissal. - Copy Conversation ID and Test Prompt only in developer mode; copies go through OmiClipboard, toasts through OmiFeedback. - Link Event and calendar event sheets on showOmiSheet with OmiDateFormat, l10n and full attendee names; visibility, summary template, name speaker and edit segment sheets on the shared shell. - Audio download sheet pops with its own context, offers Cancel and blocks back with PopScope. - Title: single line, Done, Untitled placeholder, saves on blur with an announced outcome; detail date/duration use OmiDateFormat/OmiDuration.compact (same rule as the list). - Tasks tab: tapping a task opens the shared task editor; labelled count pills; Tasks wording (D3); OmiEmptyState. - Edit segment sheet: speaker from SpeakerNames (You for the owner), Save and Cancel, discard guard. - Extract calendar sheets, search bar, transcript tab and tasks tab; delete dead ActionItemsListWidget, GetSheetTitle, GetDevToolsOptions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): keep INV-UI-3 allow comments on the flagged line Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): transcript bubbles and playback bar on the shared primitives Transcript: one SpeakerNames resolver per build (dense numbering, live people), no transcription-provider name, the start time as an OmiDuration offset, a labelled 44pt play button, labelled speaker targets, neutral user bubble and search highlight (INV-UI-1), localized 'translated by Omi', and getLastTranscript numbers speakers across the whole conversation. Playback bar: neutral surfaces instead of purple, a labelled 32pt play/pause in a 44pt target, a labelled stop button, OmiSpinner, OmiFeedback errors, the summary pill ellipsizes by layout instead of a hand-made '...', and the summary template sheet opens through showSummarizedAppsSheet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/onboarding): shared permission row, real progress, back and resume - OmiPermissionRow (lib/ui/components): reason, Allow -> system prompt, Allowed, Open Settings when the system will not ask again. One OnboardingPermissionsPanel drives both the first-run step and the returning-user interstitial; Continue never fires prompts and 'Always' location is no longer asked during first run. - Onboarding wrapper: placeholder pages removed, progress dots count the six real steps with a spoken 'Step N of M', PopScope makes system back step back, the last reached step is saved per account and resumed. - Consent step offers 'Use a Different Account' (sign out, back to start). - Acquisition survey is skippable; language step saves with a busy button and a Try Again error; picker selects on tap. - Steps share OnboardingCard (content scrolls, footer stays; min-height, no fixed text heights) and OmiButton; completion screen localized, purple gone. - Voice introduction: mic refusal shows the permission row; 'Skip Question' vs 'Skip'; device tutorial exits by a trailing close X. - Permission reasons rewritten, platform-neutral Location Services copy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/conversations): one delete path with Undo, row menu, bulk actions, recaps page - Conversation delete (list swipe, row menu, detail page) goes through one path: confirm unless opted out ("Don't ask again" is allowed because Undo backs it), then an Undo toast backed by the provider's pending-delete window (now 6 s, longer than the 5 s toast; the toast commits early when it closes) (D5). - Long-press on a conversation row opens one context menu: Open, Star/Unstar, Move to Folder, Share, Select, Delete. The selection bar gains Move and Delete (always confirmed) beside Merge. - Daily Recaps is its own pushed page with a back button; Home's View All pushes it and the Conversations tab never switches into a recap mode. Pull-to-refresh reloads recaps and a failed load offers Try Again. Recap counts are labelled. - One date filter for list and search, shown as a removable chip; the search field is seeded from the provider and uses OmiSearchField. - Dates and lengths via OmiDateFormat / OmiDuration (Today gets a header, the New badge sits beside the time, WAL/recording offsets keep their hours). - Discarded rows read "Discarded · 12s" with a plain-text snippet; blank titles read "Untitled Conversation". - Shared capture state table (capture_state_labels.dart) for the in-progress card; the processing card shows its real start time. - Local recording swipe always confirms. Sheets, dialogs, toasts, spinners and back buttons in conversations/, sync and recap pages move onto lib/ui. - Offline Sync is the one name for the device-storage sync screen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app/conversations): cover Undo-backed delete, row menu, recaps page and capture labels - Provider: the pending-delete window outlasts the Undo toast; undo restores, commit sends once, a regroup during the window keeps the row hidden. - Widgets: Undo restores / timeout commits / bulk toast counts; long-press menu entries and Select; untitled and discarded row titles; Today header; the date filter chip; Daily Recaps page back button, labelled counts, Try Again and pull-to-refresh. - Unit: the shared capture state table (an audio interruption is Paused). - The Home preview's row menu leaves out Select (no selection bar there); the in-progress card's photo count is labelled; last raw spinner and "..." in the capture card go. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/pairing): pairing recovery, one Bluetooth prompt, shared sheets - Find devices: a missing Bluetooth/location permission goes through the one BluetoothReadiness guidance prompt (Open Settings); the local 'Enable Bluetooth' dialogs are gone. - Nothing found: 'Scan Again', 'How to Pair' (connection guide), then 'Contact Support' instead of a lone mailto link. - Offline saved device: says how to wake it and 'Try Again' rescans (the old Retry did nothing). - Firmware compatibility warning is an acknowledge-only alert; the non-critical one keeps its opt-out row. - Device rows, guide cards and capture cards are real buttons with labels; battery shows a glyph and percentage with a spoken label, no emoji. - Connection guide, pairing steps, Ray-Ban picker/setup and Apple Watch setup use showOmiSheet; Apple Watch is only offered on iOS; 'I've Done This' becomes 'Done'. - Connect page: OmiBackButton, labelled settings button, and connecting returns to the existing Home (HomeNavigation.returnHome) instead of stacking a second one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): edit-sheet dirty guard and row long-press menu primitives showOmiEditSheet/OmiEditSheet own the drag so swipe-down, scrim tap, the close X and system back all ask "Discard changes?" while an editor is dirty (the framework's drag-to-dismiss pops without asking). showOmiRowMenu is the one long-press menu shape for list rows (Open, edit actions, Select, Delete). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): follow the warning token on the paused dot and the Conversation Map title Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/l10n): lane 2 strings for memories, tasks, goals and people Sheet titles without emoji, read-only memory hint, Undo toasts, task plural titles and counts, shared-task strings, memory review card and People page labels, with every locale translated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/memories): Undo-only deletes, openable read-only memories, guarded edit sheet - Every memory delete (swipe, long-press menu, edit sheet) goes through deleteMemoryWithUndo: no "cannot be undone" dialog, the shared OmiFeedback undo toast with no X, and a commit scoped to the memory the toast offered. The provider's pending window becomes an 8 s backstop that outlasts the toast. - Every memory opens: locked/superseded/ledger rows open the same sheet read-only with full text; swipe-delete only where editing is allowed. - Long-press menu Open / Edit / Delete. - Edit and new-memory sheets: explicit Cancel and Save, dirty guard, OmiButton. - Page: OmiBackButton, OmiSearchField, labelled 44pt header buttons, neutral FAB, OmiEmptyState / error state, management sheet on showOmiSheet with a confirm that names the action and toasts that reach the page after the sheet closes. - Memory graph: OmiBackButton, localized title, OmiLoading/Error/EmptyState, no purple. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/tasks): fixed swipe meaning, Undo deletes, guarded task and goal sheets - One swipe meaning on every task row at every indent: right completes (or reopens), left deletes. Indent/outdent move to the long-press menu (and drag keeps its horizontal indent); long-press shows Open / Mark Complete / Indent / Outdent / Select / Delete. - Single task and goal deletes are immediate with an Undo toast, backed by new staged-delete APIs on ActionItemsProvider and GoalsProvider (nothing hits the server until the toast closes). Bulk and clear-completed deletes confirm with a plural, verb-named dialog (the old one said "This will only remove deadlines"). - ActionItemFormSheet keeps its constructor; adds showActionItemFormSheet, explicit Cancel/Save, a close X in edit mode, a dirty guard and instant completion. The date picker and time picker are neutral (no purple) and locale/24-hour aware. - Home and Tasks share one GoalFormSheet (Cancel/Save, dirty guard, localized). - Tasks page: OmiSearchField "Search tasks", labelled overflow button, OmiEmptyState, neutral FAB/selection/drop indicators, localized "Exported to", counts with labels. - Selection bar, shared-tasks sheet and Home Today card on the primitives (44pt targets, rows open the task sheet). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/people): tapping a sample plays it; delete is a labelled control behind a confirm People page gets OmiBackButton, labelled Add/Help controls, an adaptive person dialog, OmiEmptyState/OmiLoadingState and confirms that name the action. The memory review card is localized with 44pt controls and tokens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): one header for live, processing and saved conversations; one media viewer - Live and processing pages share ConversationStateAppBar: the circled back button of the saved page and the state as the title (Recording / Paused / Reconnecting / Processing, one local mapping until lane 1a's shared capture_state_labels lands). No emoji status, no back button inside the title row. - Live page: remove the unreachable Summary tab and its TabController; Process Now is an OmiButton, mute a labelled OmiIconButton; the stop confirmation is showOmiConfirmWithOptOut with a verb; speaker names from SpeakerNames; photo-group time from OmiDateFormat and a localized photo count; tokens for the timeline and the upload indicator. - Processing page: one view (the transcript so far) under the shared header instead of Content/Summary tabs whose Summary only said 'Processing'. - Media viewer: always a full-screen modal with a trailing OmiCloseButton and swipe-down to close (MediaViewerPage.open), labelled share button, OmiSpinner; photo grids and the live timeline open it that way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/chat): contract composer, drawer and failed-reply retry Chat is a pushed page with a leading back (D1). Composer controls are labelled 44pt targets; Send that cannot send (offline, uploading) is drawn disabled with an offline hint. The apps drawer keeps Clear Chat at the bottom behind a destructive confirm, and each app has one labelled Disable (with Undo). A failed reply shows a localized error with Try Again that resends the user's message instead of raw server text. The feedback sheet is localized with a neutral Submit, thumbs read Helpful / Not Helpful, and copy/share analytics send the message length, not its text. Adds the lane-3 l10n keys for chat, apps and announcements. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/apps): one app action button, one consent dialog, Undo on disable AppListActionButton reads Open / Enable / View with the same meaning in every store list (no decorative Install). Enabling an app that works outside Omi asks one localized question naming what leaves Omi; Disable is immediate with a 5 s Undo on the detail page and in the chat drawer. The detail page moves onto the header, button, sheet, dialog and feedback primitives, splits its cards into widgets, and its cancel-subscription dialog reads Keep Subscription / Cancel Subscription. Referral and app home pages share one web page with a loading state, an error state with Try Again, and back within web history. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/announcements): answer-only dismissal through the prompt queue Announcements, feature screens and the changelog present through PromptQueue. The dialog's scrim no longer dismisses; the close X or the call to action marks an announcement seen, Not Now postpones it. The feature screen is a full-screen modal route with a trailing close; the changelog uses the shared sheet shell and error state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/ai-app-generator): one leading back, system back steps to the prompt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app): sweep conversation sheets and small widgets onto the primitives - Share via SMS and Create Template sheets use showOmiSheet, OmiSearchField, OmiButton, OmiSwitch, OmiEmptyState/OmiLoadingState and tokens; no purple (INV-UI-1). Create Template blocks back while the app is being created and opens the template picker from the navigator, not its own popped context. - Markdown and expandable text: neutral search highlight and link colour. - OmiSpinner and type/radius tokens in the speaker summary action, test prompts, photo image, person chip and waveform section. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(app/ui): edit sheets and row menus in the barrel and the contract Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): conversation detail UX contract cases Attendee names, detail duration rule, speaker numbering across the getLastTranscript window, transcript bubble chrome (no provider, offset time, 44pt labelled play), the shared capture state header, search bar position, the audio download sheet's own-context close/Cancel/back, the edit segment discard guard, and the media viewer's trailing close. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): show conversation developer tools in debug builds too No settings row writes devModeEnabled yet, so gating on it alone would hide Copy Conversation ID and Test Prompt from everyone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * wip(app): lane 3 checkpoint after spend-limit stop (uncommitted work preserved, not reviewed) * wip(app): lane 4 checkpoint after spend-limit stop (uncommitted work preserved, not reviewed) * wip(app): lane 5 checkpoint after spend-limit stop (uncommitted work preserved, not reviewed) * test(app/settings): device page stub getters and sign-out keep-list The one device page reads battery, charging and firmware getters the double-tap test stub lacked, and its connected picture pulses; stub the getters and turn on Reduce Motion so pumpAndSettle settles. Cover the Sign Out keep-list: paired device and display preferences survive, account values are cleared, and no credential is ever kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): name speaker sheet saves through the shared OmiButton Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/ai-app-generator): sweep the generator page onto the primitives App bar with a leading OmiBackButton on both steps; neutral accent instead of indigo/purple (INV-UI-1); OmiButton for Create App (busy state built in), OmiIconButton for send and regenerate-icon (labelled, 44pt), OmiSwitch for Make Public / Paid App, OmiSpinner, tokens for every colour, size and radius. Description and Features headings come from l10n; category and capability names use the shared localized titles. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/lane-3): plans sheet in the sheet shell, review fixes to the WIP checkpoint - chat: the quota plans sheet opens through showOmiSheet (lane 4 handoff). - chat evidence card: localized screen-reader hint. - capability apps page: localized category titles, token refresh colours. - const constructors and block-bodied ifs the WIP left for the analyzer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/chat): conversation links push through routeToPage; evidence test hosts l10n Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app): every push takes the platform route; shared-task link copy localized - routeToPage builds its route with omiPageRoute (defaultTargetPlatform) instead of dart:io Platform, the Foundation A handoff. - Deep links in AppShell, the People link in the name-speaker sheet and the firmware prompt in DeviceProvider push through routeToPage/omiPageRoute, never a bare MaterialPageRoute (iOS back swipe intact). - The shared-tasks deep link's not-found toast and unknown-sender fallback were hardcoded English; now sharedTasksLinkExpired and sharedTasksUnknownSender in all 49 locales. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): drop imports the ui barrel already provides Lane 2's edit-sheet and row-menu primitives joined the ui.dart barrel, which left seven direct imports redundant and pushed unnecessary_import over the analyzer baseline (CI's Dart Analyze & Tests failed on it). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/payments): labelled overflow menu, titled country picker sheet Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): people samples follow the one Voice Profile name Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/home): links open inside the one Home; startup prompts queue - Notification taps and app links open inside the Home already on screen (HomeNavigation.openRoute: pop to Home, select the tab, push the page) instead of pushReplacement-ing a second Home over whatever was showing. Home registers itself while mounted, so "Done" at the end of the firmware and connect flows also returns to it (nav #3). - Deep links move to home_deep_links.dart: parent before child (the Settings sheet opens before Data & Privacy is pushed over it), every push through routeToPage, and a removed app or deleted conversation now says so instead of only logging (nav #18). - D1: the home chat bar, its mic, chat deep links and the Ask Omi / Voice quick actions push chat as a normal page (no fullscreenDialog). The chat bar is one labelled button. - D6: Android back on the Tasks, Conversations or Apps tab returns to Home before it leaves the app (root PopScope). - Settings: the language / speech profile / transcription model comparison runs once the reader is back on Home, not the moment the sheet opens (onboarding-home #25). - Startup prompts go through PromptQueue (onboarding-home #23): upgrade alert (a required update first), changelog + announcements, the CV1 tutorial (waits for the pendant), firmware announcements, the Plus-meter plans sheet (only when the meter crosses its threshold, once per session, showOmiSheet) and Bluetooth guidance. HomePromptGate holds the queue while phone recording, on a call or during a firmware update and pumps it when that clears; passive pendant capture does not hold it. - Handoffs applied: dead toggleDailySummaries call removed (lane 1a); sync, task-export and quick-action pushes use routeToPage (lanes 2, 4). Header controls use neutral tokens (INV-UI-1) and OmiHaptics. - New strings (7 keys, every locale): appNotFoundOrRemoved plus the startup failure and discard-recording strings used by the next commits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/startup): startup failure offers Try Again and Contact Support - The failure screen words the message by error class: a rejected build configuration (Env / startup-routing checks, now thrown as StartupConfigurationError) says the build is at fault and offers Contact Support; any other failure says to check the connection and offers Try Again, which re-runs start-up, plus Contact Support (onboarding-home #27). - main() moves start-up into _start() so Try Again can run it again; ServiceManager.init, which refuses a second call, runs once. - Localized without a localization scope (lookupAppLocalizations for the device locale, English fallback); tokens replace literal colours/sizes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/speech-profile): confirm before discarding a recording in progress - Back or swipe while the voice sample is recording asks "Discard Recording?" (Discard / Keep Recording) instead of dropping it (onboarding-home #26). - "Reconnecting…" is an info toast, not an error. - The help control is a labelled OmiIconButton ("How to take a good sample?") opening showOmiAlert; back is OmiBackButton. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/home): header pill and record button expose their actions - The device pill and the record button wrap their gestures in Semantics(excludeSemantics: true), which also dropped the tap and long-press actions; the Semantics node now carries onTap (and the record options long-press), so VoiceOver/TalkBack can activate them (onboarding-home #15, #24). - Phone-mic offline fallback notice is an OmiFeedback info toast. - Sweep: auth-gate spinner is OmiSpinner; welcome Continue uses OmiHaptics; OmiGlass OTA page formatted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): delete webhooks.dart, reachable only from the removed dev-tools widget Its one importer was GetDevToolsOptions in conversation_detail/widgets.dart, which nothing constructed and lane 1b deleted. The dead-code ratchet flags the now-unreachable file; the webhook settings themselves live in DeveloperModeProvider and the users API and are unaffected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/home): a link counts as opened once its page is pushed openHomeDeepLink awaited each push, so HomeNavigation.openRoute (and the notification-open telemetry behind it) only resolved when the reader closed the page. It now resolves once the destination is on screen. A missing app or conversation is an info toast, as the contract says; the contract names HomeNavigation.openRoute / returnHome. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 1 (ZCode GLM-5.3) - A memory's Undo restores only that memory: restoreLastDeletedMemory takes the toast's id, so a stale toast cannot restore a newer delete (test added). - A failed task delete no longer inserts a Home-only task into the Tasks list; the staged index goes straight to _restoreDeletedItem, which skips -1. - The conversation pending-delete window is 10 s, so an Undo toast that starts late behind other snack bars still gets its full time; the toast still commits early when it closes. - The integration page's button reads "Disconnect" instead of stripping the question mark from a translated sentence (broke ¿…? and ? locales). - mounted checks after the long awaits in the Create Template sheet and Wrapped 2025 generation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): keep memories_provider under its line-count ceiling Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/apps): app creation forms on the shared form pieces and primitives Submit App and Manage App draw their screenshot strip and GitHub URL card from app_form_fields (screenshots open as a modal viewer with a trailing close), use AppFormCard/section titles/input decoration, OmiSwitch rows for Public/Paid (no indigo/green thumbs), OmiButton for submit/update, a leading OmiBackButton, and l10n for every string. Update asks with showOmiConfirm and a verb button (Update App); Submit's confirm reads Submit App; Start Earning is an Omi sheet with Not Now. App metadata pickers are Omi sheets, App ID copy goes through OmiClipboard. API keys: adaptive dialogs, destructive Revoke confirm, OmiFeedback toasts, localized dates. Add MCP Server: OmiBackButton, OmiButton, OmiFeedback. Provider snackbars and picker titles localized. New l10n keys: generateDescription, selectImageFileTitle, selectThumbnailImageTitle, appIdNotFoundError, manifestRefreshedSuccess, manifestRefreshFailed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/apps): store home, filters and reviews on the primitives The store's filter sheet opens in the Omi sheet shell; header icon controls are labelled 44pt OmiIconButtons; empty search is OmiEmptyState. Reviews: OmiBackButton, adaptive reply dialog, OmiFeedback toasts, OmiButton busy state, localized Add/Edit Your Review and rating counts, readable (not grey-700) secondary text. App summary and capabilities card localized. Screenshot taps on the detail page open through openAppScreenshots (modal, trailing close). Review avatar palette drops its purple entry (INV-UI-1). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app/apps): Disable hides at once, commits after the Undo toast, Undo restores Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/lane-3): localized pricing value on Manage App and file-picker title in chat Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 2 (lane 5) - A throw while preparing the Omi firmware update (for example a corrupt download that fails zip extraction) now ends in the failed state instead of an installing spinner with back blocked and every prompt held. - Home drops the prompts it enqueued when it is disposed, so after sign-out and sign-in the new Home's announcements, device tutorial and firmware notices are not deduplicated against dead closures. - The speech step re-reads the microphone permission when the app resumes, so granting it in Settings clears the Open Settings row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): retire AnimatedLoadingButton Lane 3 moved its last callers (payments, app detail) onto OmiButton, which owns the busy state and recovers when the action throws; the dead-code ratchet flags the unreachable widget. Its throw-recovery test now runs against OmiButton directly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 3 (lane 3) - Try Again on a failed reply releases the composer when the failure was already cleared, and the first app message releases it even if chat closes mid-request (both could leave typed sends blocked). - Enabling an app again inside its 5 s disable Undo window cancels the pending disable instead of being overwritten when the toast ends; tests for that and for a refused disable. - Stripe setup names the selected country from the full list, not the picker's filtered one, and always stops polling on dispose. - The AI app generator shows a failed submit on the generated view. - OmiWebPage's Try Again reloads the page that failed, not the first URL; a malformed app home URL shows the error state instead of throwing. The reported Stripe double-submit is already prevented: OmiButton ignores taps while its onPressed future runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 4 (#17297 merge and rulings) - Share from a conversation row now behaves like the page: a private conversation asks first ("Anyone with the link can view") and goes back to private when the share sheet is dismissed (was: made public at once). - Developer Settings gains a Conversation Developer Tools switch (writes devModeEnabled), so release users can still reach Copy Conversation ID and Test Prompt; 2 keys in 49 locales. - The row's Separate… always disposes its controller; tests cover a confirmed separation from a row (the other recording, never the row) and the row share confirmation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): phone number verified date holds in every timezone The fixture was UTC midnight, which is Jan 1 in Pago Pago; CI runs the suite at UTC+14 and UTC-11. Local noon reads Jan 2 everywhere. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): conversation title and emoji share one line in the header The title field kept Material's 48pt input height, which pushed the title below the emoji; it is dense now and the one-line row centres both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): every voice-profile entry opens the guided introduction David's ruling (2026-09-24): the voice profile is the guided fill-in-the- blank flow from #14514 (sentence starters, review, then voice, memories and a goal). Two entry points still reached the old question-based page: the Settings/Profile Voice Profile row for anyone who already had a profile, and the capture card's "Teach Omi your voice". Both now open the guided flow. The old page is unreachable, so it goes, with its two widgets, the getDialog helper and the fade-in text widget only it used (dead-code ratchet). Open PR #12531 edits the old page and will need to drop those hunks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): Star and Share back in the conversation top bar; record conversation actions David, 2026-09-24 (overrides the earlier one-overflow ruling): - the conversation page's top bar is Ask Omi (the primary, filled), Star and Share as matching 44pt icon buttons, then one "…" holding Rename, Move to Folder, Recordings and the rest, Delete last and destructive; - one registered event, Conversation Action, records every conversation action with two closed enums: action (ask_omi, star, unstar, share, rename, move_folder, recordings_open, separate, delete, copy_transcript, …) and surface (top_bar, overflow, detail_body, row_long_press, row_swipe). No ids, titles or content. It goes through the C7 typed registry (contracts/analytics/events.json, generated Dart, a presence consumer) and AnalyticsManager, so consent and queueing apply. Tests cover the event shape for every action and the top bar, overflow, row long-press and row swipe call sites. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): the people chip names only named speakers David, 2026-09-24: never label a participant "Speaker 1". The conversation header's people chip now shows the first named participant ("You" for the account owner) and counts everyone else: "David + 3 others" (participantsSummary, an ICU plural in 49 locales). With nobody named it hides; a linked calendar event's attendees still stand in when the transcript names no one. The transcript's own per-line speaker labels are unchanged. Tests cover named, mixed and none. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: drop pointers to the deleted speech-profile page Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 5 (voice profile and people chip) - SpeechProfileProvider drove only the deleted question page; the guided introduction records through GuidedVoiceIO. Remove the provider, its registration, the onboarding back-step close() that no longer stopped anything, and its test. - Drop pointers to deleted files: the addressability baseline entry and the logout flow's covers (now sign_out.dart); a stale doc paragraph. - participantsSummary carries few/many forms so Polish, Czech and Slovak agree for 2-4 and 5+; other Slavic and Baltic locales use a count-agnostic "one more" form. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 5 天前 | |
feat(app): add System / Light / Dark appearance setting on mobile (#19378) * feat(app): add system-aware light and dark appearance * fix(app): restore light appearance contrast on v1 surfaces * fix(app): provide appearance in visual audit harness Failure-Class: none * test(app): support light mode in visual audit harness * fix(app): correct light-mode color contrast Failure-Class: none * fix(app): improve remaining light-mode contrast Failure-Class: none * fix(app): use palette tokens for light transcript bubbles Replace the two hardcoded light-mode bubble colours with surface2 and a pale alpha-blend of the speaker colour, so the mobile UX contract's colour-literal ratchet stays flat. Failure-Class: none Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 2 天前 | |
fix(sync): converge speakers across jobs and follow bridged conversations (#19432) * fix(sync): preserve speaker actions across bridges and mark unresolved labels * docs(app): note synced speaker label recovery * docs(api): refresh app-client speaker assignment contract * chore(api): regenerate TypeScript clients for optional speaker body * chore(api): refresh generated Swift speaker assignment client * fix(app): adopt sync bridge survivor in open detail * fix(conversations): tolerate legacy assignment route fixtures without id | 1 天前 | |
feat(app): mobile UX consistency pass: shared primitives, contract and audit fixes (#18436) * feat(app/ui): add OmiColors tokens and stop using colorScheme.primary as a page background Add lib/ui/omi_tokens.dart: OmiColors (surfaces, text, neutral accent, status), OmiType ramp, OmiRadius, OmiSpacing, OmiMotion (honours Reduce Motion) and OmiHaptics. 53 sites read Theme.of(context).colorScheme.primary as a page or app bar background because the theme's primary was black. They now name OmiColors.surface0 (same black), so the theme can get a real accent. In the four files over the line-count threshold the explicit colour is dropped instead; the theme default is the same black. AppStyles and ResponsiveHelper get doc comments pointing at the tokens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): one theme and shared controls for navigation, buttons, sheets and page states Theme (lib/ui/omi_theme.dart, used by main.dart and the visual_audit harness so they cannot drift): a real dark ColorScheme with the white accent as primary, black scaffold, app bar theme (black, flat, centred 17pt semibold title), white progress indicators (the unstyled spinners were black on black), floating surface2 snack bars, surface1 dialogs and sheets, neutral switches. Material 2 stays on. MaterialApp.builder now syncs Intl.defaultLocale to the resolved locale. Components under lib/ui/components (barrel lib/ui/ui.dart): OmiBackButton / OmiCloseButton (platform glyph, localized label, 44pt target, circled variants), OmiIconButton (required label, tooltip), OmiButton (primary/secondary/destructive/tertiary, regular/compact, async loading that always clears), OmiSpinner, OmiLoadingState / OmiEmptyState / OmiErrorState, showOmiSheet and OmiSheetScaffold, OmiSwitch, OmiSettingsGroup / OmiSettingsRow / OmiSectionHeader, OmiSearchField, and omiPageRoute for code that needs a Route object. HeaderCircleButton is now a wrapper over OmiIconButton.filled and AnimatedLoadingButton over OmiButton (fixes the spinner that never cleared after a throw, and the 40pt target). find_device's two unstyled ElevatedButtons keep their black fill explicitly now that primary is white. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/settings): move data privacy, Asana and ClickUp settings onto the shared controls Proving set for the new primitives. Data & Privacy uses OmiBackButton, a section header and an OmiSettingsGroup of app rows, OmiEmptyState, and drops its accent tint for neutral tokens. Asana and ClickUp show an app bar with a back button and OmiLoadingState during their first load (the page used to be an invisible spinner with no way out), OmiSpinner for section loads, and tokens for their colours. The shared IntegrationSettingsPage header uses OmiBackButton and a labelled refresh OmiIconButton, and its disconnect control is an OmiButton.destructive. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): one adaptive confirmation system (showOmiConfirm/showOmiAlert) getDialog, ConfirmationDialog, OmiConfirmDialog and AppDialog become thin adapters over OmiAlertDialog: Cancel always shown and always closes, real CupertinoDialogActions on iOS (destructive red, safe choice bold), localized defaults, a destructive flag, and AppDialog.show no longer drops singleButton and onCancel. Non-page raw AlertDialogs in providers/widgets migrated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): OmiFeedback toasts and OmiClipboard One floating neutral toast at a time with contract timings (confirm 1.5 s, info 4 s, error 8 s with close, undo 5 s with no close, resolving whether the reader undid). Snackbars with actions no longer persist forever. The home shell registers its tab-bar clearance. AppSnackbar's ~180 call sites route through it. OmiClipboard refuses empty text and skips its toast on Android 13+ where the system already confirms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): OmiDateFormat, OmiDuration and shared speaker names Dates follow the app locale and the 24-hour setting; durations have one offset/compact/long vocabulary (the playback bar no longer drops hours). SpeakerNames is the single source for speaker labels in app and export: You / person / Omi / dense Speaker N, localized in copied transcripts. Legacy helpers delegate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): PromptQueue for startup and background prompts Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(checks): INV-UI-3 mobile UX contract ratchet Per-rule, per-file no-increase ratchet over hand-rolled back glyphs, PageRouteBuilder, raw sheets/dialogs/snackbars/clipboard, date patterns, colour/font-size/radius literals, raw spinners, '...' and hardcoded Text copy under app/lib, with an allow-comment escape hatch and a test proving each rule fires. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(app): mobile UX contract The numbered rules and the one primitive for each: leaving a surface, sheets, controls, destructive actions (D5), dialogs, feedback timing, copying, dates and durations, speakers, tokens, words (D3), settings (D4), page states, prompts, permissions and accessibility, plus the D1/D2/D6 navigation calls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/ui): dense speaker numbering on live and saved transcripts Both transcript renderers pass the conversation's segments to speakerLabel so Speaker N is numbered per conversation. Drops the dead duplicate formatChatTimestamp in widgets/transcript.dart, makes Material OmiAlertDialog scrollable, and points primitive docs at the contract's sections. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/l10n): contract words sweep Task(s) replaces Action Item(s)/To-Do in every locale (D3); Omi capitalised; '…' instead of '...'; search placeholders drop the ellipsis; Try Again, Not Now, OK, Title Case buttons. New keys: filterBySpeaker, notNow, discard, keepEditing, discardChangesTitle, discardChangesMessage. Translates the nine live-capture keys that only existed in English and removes five orphaned locale keys, so scripts/l10n.py check passes. The upgrade alert offers Not Now (postpone, not ignore-forever) and Update, honours barrierDismissible, and loses the emoji. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app/ui): export feedback, format and prompt primitives from the ui barrel; token colours Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app/ui): follow the Title Case clear-search label Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/l10n): strings for the conversation detail pass Link Event sheet, calendar event actions, search result navigation, playback labels, the share confirmation, the Tasks tab counts and empty state, and the speaker identify label, with translations for every locale. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): conversation detail on the shared primitives - D2: no body-wide swipe to another conversation and no PageRouteBuilder; Transcript/Summary(/Tasks) swipe as tabs, the Tasks tab exists only when there are tasks. - Header: OmiBackButton.circled plus same-size OmiIconButton circles (star, share, more) and a compact Ask Omi button that pushes Chat (D1). - Share asks before making a private conversation public and reverts it when the share sheet reports a dismissal. - Copy Conversation ID and Test Prompt only in developer mode; copies go through OmiClipboard, toasts through OmiFeedback. - Link Event and calendar event sheets on showOmiSheet with OmiDateFormat, l10n and full attendee names; visibility, summary template, name speaker and edit segment sheets on the shared shell. - Audio download sheet pops with its own context, offers Cancel and blocks back with PopScope. - Title: single line, Done, Untitled placeholder, saves on blur with an announced outcome; detail date/duration use OmiDateFormat/OmiDuration.compact (same rule as the list). - Tasks tab: tapping a task opens the shared task editor; labelled count pills; Tasks wording (D3); OmiEmptyState. - Edit segment sheet: speaker from SpeakerNames (You for the owner), Save and Cancel, discard guard. - Extract calendar sheets, search bar, transcript tab and tasks tab; delete dead ActionItemsListWidget, GetSheetTitle, GetDevToolsOptions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): keep INV-UI-3 allow comments on the flagged line Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): transcript bubbles and playback bar on the shared primitives Transcript: one SpeakerNames resolver per build (dense numbering, live people), no transcription-provider name, the start time as an OmiDuration offset, a labelled 44pt play button, labelled speaker targets, neutral user bubble and search highlight (INV-UI-1), localized 'translated by Omi', and getLastTranscript numbers speakers across the whole conversation. Playback bar: neutral surfaces instead of purple, a labelled 32pt play/pause in a 44pt target, a labelled stop button, OmiSpinner, OmiFeedback errors, the summary pill ellipsizes by layout instead of a hand-made '...', and the summary template sheet opens through showSummarizedAppsSheet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/onboarding): shared permission row, real progress, back and resume - OmiPermissionRow (lib/ui/components): reason, Allow -> system prompt, Allowed, Open Settings when the system will not ask again. One OnboardingPermissionsPanel drives both the first-run step and the returning-user interstitial; Continue never fires prompts and 'Always' location is no longer asked during first run. - Onboarding wrapper: placeholder pages removed, progress dots count the six real steps with a spoken 'Step N of M', PopScope makes system back step back, the last reached step is saved per account and resumed. - Consent step offers 'Use a Different Account' (sign out, back to start). - Acquisition survey is skippable; language step saves with a busy button and a Try Again error; picker selects on tap. - Steps share OnboardingCard (content scrolls, footer stays; min-height, no fixed text heights) and OmiButton; completion screen localized, purple gone. - Voice introduction: mic refusal shows the permission row; 'Skip Question' vs 'Skip'; device tutorial exits by a trailing close X. - Permission reasons rewritten, platform-neutral Location Services copy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/conversations): one delete path with Undo, row menu, bulk actions, recaps page - Conversation delete (list swipe, row menu, detail page) goes through one path: confirm unless opted out ("Don't ask again" is allowed because Undo backs it), then an Undo toast backed by the provider's pending-delete window (now 6 s, longer than the 5 s toast; the toast commits early when it closes) (D5). - Long-press on a conversation row opens one context menu: Open, Star/Unstar, Move to Folder, Share, Select, Delete. The selection bar gains Move and Delete (always confirmed) beside Merge. - Daily Recaps is its own pushed page with a back button; Home's View All pushes it and the Conversations tab never switches into a recap mode. Pull-to-refresh reloads recaps and a failed load offers Try Again. Recap counts are labelled. - One date filter for list and search, shown as a removable chip; the search field is seeded from the provider and uses OmiSearchField. - Dates and lengths via OmiDateFormat / OmiDuration (Today gets a header, the New badge sits beside the time, WAL/recording offsets keep their hours). - Discarded rows read "Discarded · 12s" with a plain-text snippet; blank titles read "Untitled Conversation". - Shared capture state table (capture_state_labels.dart) for the in-progress card; the processing card shows its real start time. - Local recording swipe always confirms. Sheets, dialogs, toasts, spinners and back buttons in conversations/, sync and recap pages move onto lib/ui. - Offline Sync is the one name for the device-storage sync screen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app/conversations): cover Undo-backed delete, row menu, recaps page and capture labels - Provider: the pending-delete window outlasts the Undo toast; undo restores, commit sends once, a regroup during the window keeps the row hidden. - Widgets: Undo restores / timeout commits / bulk toast counts; long-press menu entries and Select; untitled and discarded row titles; Today header; the date filter chip; Daily Recaps page back button, labelled counts, Try Again and pull-to-refresh. - Unit: the shared capture state table (an audio interruption is Paused). - The Home preview's row menu leaves out Select (no selection bar there); the in-progress card's photo count is labelled; last raw spinner and "..." in the capture card go. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/pairing): pairing recovery, one Bluetooth prompt, shared sheets - Find devices: a missing Bluetooth/location permission goes through the one BluetoothReadiness guidance prompt (Open Settings); the local 'Enable Bluetooth' dialogs are gone. - Nothing found: 'Scan Again', 'How to Pair' (connection guide), then 'Contact Support' instead of a lone mailto link. - Offline saved device: says how to wake it and 'Try Again' rescans (the old Retry did nothing). - Firmware compatibility warning is an acknowledge-only alert; the non-critical one keeps its opt-out row. - Device rows, guide cards and capture cards are real buttons with labels; battery shows a glyph and percentage with a spoken label, no emoji. - Connection guide, pairing steps, Ray-Ban picker/setup and Apple Watch setup use showOmiSheet; Apple Watch is only offered on iOS; 'I've Done This' becomes 'Done'. - Connect page: OmiBackButton, labelled settings button, and connecting returns to the existing Home (HomeNavigation.returnHome) instead of stacking a second one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/ui): edit-sheet dirty guard and row long-press menu primitives showOmiEditSheet/OmiEditSheet own the drag so swipe-down, scrim tap, the close X and system back all ask "Discard changes?" while an editor is dirty (the framework's drag-to-dismiss pops without asking). showOmiRowMenu is the one long-press menu shape for list rows (Open, edit actions, Select, Delete). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): follow the warning token on the paused dot and the Conversation Map title Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/l10n): lane 2 strings for memories, tasks, goals and people Sheet titles without emoji, read-only memory hint, Undo toasts, task plural titles and counts, shared-task strings, memory review card and People page labels, with every locale translated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/memories): Undo-only deletes, openable read-only memories, guarded edit sheet - Every memory delete (swipe, long-press menu, edit sheet) goes through deleteMemoryWithUndo: no "cannot be undone" dialog, the shared OmiFeedback undo toast with no X, and a commit scoped to the memory the toast offered. The provider's pending window becomes an 8 s backstop that outlasts the toast. - Every memory opens: locked/superseded/ledger rows open the same sheet read-only with full text; swipe-delete only where editing is allowed. - Long-press menu Open / Edit / Delete. - Edit and new-memory sheets: explicit Cancel and Save, dirty guard, OmiButton. - Page: OmiBackButton, OmiSearchField, labelled 44pt header buttons, neutral FAB, OmiEmptyState / error state, management sheet on showOmiSheet with a confirm that names the action and toasts that reach the page after the sheet closes. - Memory graph: OmiBackButton, localized title, OmiLoading/Error/EmptyState, no purple. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/tasks): fixed swipe meaning, Undo deletes, guarded task and goal sheets - One swipe meaning on every task row at every indent: right completes (or reopens), left deletes. Indent/outdent move to the long-press menu (and drag keeps its horizontal indent); long-press shows Open / Mark Complete / Indent / Outdent / Select / Delete. - Single task and goal deletes are immediate with an Undo toast, backed by new staged-delete APIs on ActionItemsProvider and GoalsProvider (nothing hits the server until the toast closes). Bulk and clear-completed deletes confirm with a plural, verb-named dialog (the old one said "This will only remove deadlines"). - ActionItemFormSheet keeps its constructor; adds showActionItemFormSheet, explicit Cancel/Save, a close X in edit mode, a dirty guard and instant completion. The date picker and time picker are neutral (no purple) and locale/24-hour aware. - Home and Tasks share one GoalFormSheet (Cancel/Save, dirty guard, localized). - Tasks page: OmiSearchField "Search tasks", labelled overflow button, OmiEmptyState, neutral FAB/selection/drop indicators, localized "Exported to", counts with labels. - Selection bar, shared-tasks sheet and Home Today card on the primitives (44pt targets, rows open the task sheet). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/people): tapping a sample plays it; delete is a labelled control behind a confirm People page gets OmiBackButton, labelled Add/Help controls, an adaptive person dialog, OmiEmptyState/OmiLoadingState and confirms that name the action. The memory review card is localized with 44pt controls and tokens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): one header for live, processing and saved conversations; one media viewer - Live and processing pages share ConversationStateAppBar: the circled back button of the saved page and the state as the title (Recording / Paused / Reconnecting / Processing, one local mapping until lane 1a's shared capture_state_labels lands). No emoji status, no back button inside the title row. - Live page: remove the unreachable Summary tab and its TabController; Process Now is an OmiButton, mute a labelled OmiIconButton; the stop confirmation is showOmiConfirmWithOptOut with a verb; speaker names from SpeakerNames; photo-group time from OmiDateFormat and a localized photo count; tokens for the timeline and the upload indicator. - Processing page: one view (the transcript so far) under the shared header instead of Content/Summary tabs whose Summary only said 'Processing'. - Media viewer: always a full-screen modal with a trailing OmiCloseButton and swipe-down to close (MediaViewerPage.open), labelled share button, OmiSpinner; photo grids and the live timeline open it that way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/chat): contract composer, drawer and failed-reply retry Chat is a pushed page with a leading back (D1). Composer controls are labelled 44pt targets; Send that cannot send (offline, uploading) is drawn disabled with an offline hint. The apps drawer keeps Clear Chat at the bottom behind a destructive confirm, and each app has one labelled Disable (with Undo). A failed reply shows a localized error with Try Again that resends the user's message instead of raw server text. The feedback sheet is localized with a neutral Submit, thumbs read Helpful / Not Helpful, and copy/share analytics send the message length, not its text. Adds the lane-3 l10n keys for chat, apps and announcements. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/apps): one app action button, one consent dialog, Undo on disable AppListActionButton reads Open / Enable / View with the same meaning in every store list (no decorative Install). Enabling an app that works outside Omi asks one localized question naming what leaves Omi; Disable is immediate with a 5 s Undo on the detail page and in the chat drawer. The detail page moves onto the header, button, sheet, dialog and feedback primitives, splits its cards into widgets, and its cancel-subscription dialog reads Keep Subscription / Cancel Subscription. Referral and app home pages share one web page with a loading state, an error state with Try Again, and back within web history. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/announcements): answer-only dismissal through the prompt queue Announcements, feature screens and the changelog present through PromptQueue. The dialog's scrim no longer dismisses; the close X or the call to action marks an announcement seen, Not Now postpones it. The feature screen is a full-screen modal route with a trailing close; the changelog uses the shared sheet shell and error state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/ai-app-generator): one leading back, system back steps to the prompt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app): sweep conversation sheets and small widgets onto the primitives - Share via SMS and Create Template sheets use showOmiSheet, OmiSearchField, OmiButton, OmiSwitch, OmiEmptyState/OmiLoadingState and tokens; no purple (INV-UI-1). Create Template blocks back while the app is being created and opens the template picker from the navigator, not its own popped context. - Markdown and expandable text: neutral search highlight and link colour. - OmiSpinner and type/radius tokens in the speaker summary action, test prompts, photo image, person chip and waveform section. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(app/ui): edit sheets and row menus in the barrel and the contract Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): conversation detail UX contract cases Attendee names, detail duration rule, speaker numbering across the getLastTranscript window, transcript bubble chrome (no provider, offset time, 44pt labelled play), the shared capture state header, search bar position, the audio download sheet's own-context close/Cancel/back, the edit segment discard guard, and the media viewer's trailing close. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): show conversation developer tools in debug builds too No settings row writes devModeEnabled yet, so gating on it alone would hide Copy Conversation ID and Test Prompt from everyone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * wip(app): lane 3 checkpoint after spend-limit stop (uncommitted work preserved, not reviewed) * wip(app): lane 4 checkpoint after spend-limit stop (uncommitted work preserved, not reviewed) * wip(app): lane 5 checkpoint after spend-limit stop (uncommitted work preserved, not reviewed) * test(app/settings): device page stub getters and sign-out keep-list The one device page reads battery, charging and firmware getters the double-tap test stub lacked, and its connected picture pulses; stub the getters and turn on Reduce Motion so pumpAndSettle settles. Cover the Sign Out keep-list: paired device and display preferences survive, account values are cleared, and no credential is ever kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): name speaker sheet saves through the shared OmiButton Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/ai-app-generator): sweep the generator page onto the primitives App bar with a leading OmiBackButton on both steps; neutral accent instead of indigo/purple (INV-UI-1); OmiButton for Create App (busy state built in), OmiIconButton for send and regenerate-icon (labelled, 44pt), OmiSwitch for Make Public / Paid App, OmiSpinner, tokens for every colour, size and radius. Description and Features headings come from l10n; category and capability names use the shared localized titles. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/lane-3): plans sheet in the sheet shell, review fixes to the WIP checkpoint - chat: the quota plans sheet opens through showOmiSheet (lane 4 handoff). - chat evidence card: localized screen-reader hint. - capability apps page: localized category titles, token refresh colours. - const constructors and block-bodied ifs the WIP left for the analyzer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/chat): conversation links push through routeToPage; evidence test hosts l10n Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app): every push takes the platform route; shared-task link copy localized - routeToPage builds its route with omiPageRoute (defaultTargetPlatform) instead of dart:io Platform, the Foundation A handoff. - Deep links in AppShell, the People link in the name-speaker sheet and the firmware prompt in DeviceProvider push through routeToPage/omiPageRoute, never a bare MaterialPageRoute (iOS back swipe intact). - The shared-tasks deep link's not-found toast and unknown-sender fallback were hardcoded English; now sharedTasksLinkExpired and sharedTasksUnknownSender in all 49 locales. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): drop imports the ui barrel already provides Lane 2's edit-sheet and row-menu primitives joined the ui.dart barrel, which left seven direct imports redundant and pushed unnecessary_import over the analyzer baseline (CI's Dart Analyze & Tests failed on it). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/payments): labelled overflow menu, titled country picker sheet Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): people samples follow the one Voice Profile name Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/home): links open inside the one Home; startup prompts queue - Notification taps and app links open inside the Home already on screen (HomeNavigation.openRoute: pop to Home, select the tab, push the page) instead of pushReplacement-ing a second Home over whatever was showing. Home registers itself while mounted, so "Done" at the end of the firmware and connect flows also returns to it (nav #3). - Deep links move to home_deep_links.dart: parent before child (the Settings sheet opens before Data & Privacy is pushed over it), every push through routeToPage, and a removed app or deleted conversation now says so instead of only logging (nav #18). - D1: the home chat bar, its mic, chat deep links and the Ask Omi / Voice quick actions push chat as a normal page (no fullscreenDialog). The chat bar is one labelled button. - D6: Android back on the Tasks, Conversations or Apps tab returns to Home before it leaves the app (root PopScope). - Settings: the language / speech profile / transcription model comparison runs once the reader is back on Home, not the moment the sheet opens (onboarding-home #25). - Startup prompts go through PromptQueue (onboarding-home #23): upgrade alert (a required update first), changelog + announcements, the CV1 tutorial (waits for the pendant), firmware announcements, the Plus-meter plans sheet (only when the meter crosses its threshold, once per session, showOmiSheet) and Bluetooth guidance. HomePromptGate holds the queue while phone recording, on a call or during a firmware update and pumps it when that clears; passive pendant capture does not hold it. - Handoffs applied: dead toggleDailySummaries call removed (lane 1a); sync, task-export and quick-action pushes use routeToPage (lanes 2, 4). Header controls use neutral tokens (INV-UI-1) and OmiHaptics. - New strings (7 keys, every locale): appNotFoundOrRemoved plus the startup failure and discard-recording strings used by the next commits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/startup): startup failure offers Try Again and Contact Support - The failure screen words the message by error class: a rejected build configuration (Env / startup-routing checks, now thrown as StartupConfigurationError) says the build is at fault and offers Contact Support; any other failure says to check the connection and offers Try Again, which re-runs start-up, plus Contact Support (onboarding-home #27). - main() moves start-up into _start() so Try Again can run it again; ServiceManager.init, which refuses a second call, runs once. - Localized without a localization scope (lookupAppLocalizations for the device locale, English fallback); tokens replace literal colours/sizes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app/speech-profile): confirm before discarding a recording in progress - Back or swipe while the voice sample is recording asks "Discard Recording?" (Discard / Keep Recording) instead of dropping it (onboarding-home #26). - "Reconnecting…" is an info toast, not an error. - The help control is a labelled OmiIconButton ("How to take a good sample?") opening showOmiAlert; back is OmiBackButton. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/home): header pill and record button expose their actions - The device pill and the record button wrap their gestures in Semantics(excludeSemantics: true), which also dropped the tap and long-press actions; the Semantics node now carries onTap (and the record options long-press), so VoiceOver/TalkBack can activate them (onboarding-home #15, #24). - Phone-mic offline fallback notice is an OmiFeedback info toast. - Sweep: auth-gate spinner is OmiSpinner; welcome Continue uses OmiHaptics; OmiGlass OTA page formatted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): delete webhooks.dart, reachable only from the removed dev-tools widget Its one importer was GetDevToolsOptions in conversation_detail/widgets.dart, which nothing constructed and lane 1b deleted. The dead-code ratchet flags the now-unreachable file; the webhook settings themselves live in DeveloperModeProvider and the users API and are unaffected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/home): a link counts as opened once its page is pushed openHomeDeepLink awaited each push, so HomeNavigation.openRoute (and the notification-open telemetry behind it) only resolved when the reader closed the page. It now resolves once the destination is on screen. A missing app or conversation is an info toast, as the contract says; the contract names HomeNavigation.openRoute / returnHome. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 1 (ZCode GLM-5.3) - A memory's Undo restores only that memory: restoreLastDeletedMemory takes the toast's id, so a stale toast cannot restore a newer delete (test added). - A failed task delete no longer inserts a Home-only task into the Tasks list; the staged index goes straight to _restoreDeletedItem, which skips -1. - The conversation pending-delete window is 10 s, so an Undo toast that starts late behind other snack bars still gets its full time; the toast still commits early when it closes. - The integration page's button reads "Disconnect" instead of stripping the question mark from a translated sentence (broke ¿…? and ? locales). - mounted checks after the long awaits in the Create Template sheet and Wrapped 2025 generation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): keep memories_provider under its line-count ceiling Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/apps): app creation forms on the shared form pieces and primitives Submit App and Manage App draw their screenshot strip and GitHub URL card from app_form_fields (screenshots open as a modal viewer with a trailing close), use AppFormCard/section titles/input decoration, OmiSwitch rows for Public/Paid (no indigo/green thumbs), OmiButton for submit/update, a leading OmiBackButton, and l10n for every string. Update asks with showOmiConfirm and a verb button (Update App); Submit's confirm reads Submit App; Start Earning is an Omi sheet with Not Now. App metadata pickers are Omi sheets, App ID copy goes through OmiClipboard. API keys: adaptive dialogs, destructive Revoke confirm, OmiFeedback toasts, localized dates. Add MCP Server: OmiBackButton, OmiButton, OmiFeedback. Provider snackbars and picker titles localized. New l10n keys: generateDescription, selectImageFileTitle, selectThumbnailImageTitle, appIdNotFoundError, manifestRefreshedSuccess, manifestRefreshFailed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(app/apps): store home, filters and reviews on the primitives The store's filter sheet opens in the Omi sheet shell; header icon controls are labelled 44pt OmiIconButtons; empty search is OmiEmptyState. Reviews: OmiBackButton, adaptive reply dialog, OmiFeedback toasts, OmiButton busy state, localized Add/Edit Your Review and rating counts, readable (not grey-700) secondary text. App summary and capabilities card localized. Screenshot taps on the detail page open through openAppScreenshots (modal, trailing close). Review avatar palette drops its purple entry (INV-UI-1). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app/apps): Disable hides at once, commits after the Undo toast, Undo restores Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app/lane-3): localized pricing value on Manage App and file-picker title in chat Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 2 (lane 5) - A throw while preparing the Omi firmware update (for example a corrupt download that fails zip extraction) now ends in the failed state instead of an installing spinner with back blocked and every prompt held. - Home drops the prompts it enqueued when it is disposed, so after sign-out and sign-in the new Home's announcements, device tutorial and firmware notices are not deduplicated against dead closures. - The speech step re-reads the microphone permission when the app resumes, so granting it in Settings clears the Open Settings row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(app): retire AnimatedLoadingButton Lane 3 moved its last callers (payments, app detail) onto OmiButton, which owns the busy state and recovers when the action throws; the dead-code ratchet flags the unreachable widget. Its throw-recovery test now runs against OmiButton directly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 3 (lane 3) - Try Again on a failed reply releases the composer when the failure was already cleared, and the first app message releases it even if chat closes mid-request (both could leave typed sends blocked). - Enabling an app again inside its 5 s disable Undo window cancels the pending disable instead of being overwritten when the toast ends; tests for that and for a refused disable. - Stripe setup names the selected country from the full list, not the picker's filtered one, and always stops polling on dispose. - The AI app generator shows a failed submit on the generated view. - OmiWebPage's Try Again reloads the page that failed, not the first URL; a malformed app home URL shows the error state instead of throwing. The reported Stripe double-submit is already prevented: OmiButton ignores taps while its onPressed future runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 4 (#17297 merge and rulings) - Share from a conversation row now behaves like the page: a private conversation asks first ("Anyone with the link can view") and goes back to private when the share sheet is dismissed (was: made public at once). - Developer Settings gains a Conversation Developer Tools switch (writes devModeEnabled), so release users can still reach Copy Conversation ID and Test Prompt; 2 keys in 49 locales. - The row's Separate… always disposes its controller; tests cover a confirmed separation from a row (the other recording, never the row) and the row share confirmation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(app): phone number verified date holds in every timezone The fixture was UTC midnight, which is Jan 1 in Pago Pago; CI runs the suite at UTC+14 and UTC-11. Local noon reads Jan 2 everywhere. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): conversation title and emoji share one line in the header The title field kept Material's 48pt input height, which pushed the title below the emoji; it is dense now and the one-line row centres both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): every voice-profile entry opens the guided introduction David's ruling (2026-09-24): the voice profile is the guided fill-in-the- blank flow from #14514 (sentence starters, review, then voice, memories and a goal). Two entry points still reached the old question-based page: the Settings/Profile Voice Profile row for anyone who already had a profile, and the capture card's "Teach Omi your voice". Both now open the guided flow. The old page is unreachable, so it goes, with its two widgets, the getDialog helper and the fade-in text widget only it used (dead-code ratchet). Open PR #12531 edits the old page and will need to drop those hunks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): Star and Share back in the conversation top bar; record conversation actions David, 2026-09-24 (overrides the earlier one-overflow ruling): - the conversation page's top bar is Ask Omi (the primary, filled), Star and Share as matching 44pt icon buttons, then one "…" holding Rename, Move to Folder, Recordings and the rest, Delete last and destructive; - one registered event, Conversation Action, records every conversation action with two closed enums: action (ask_omi, star, unstar, share, rename, move_folder, recordings_open, separate, delete, copy_transcript, …) and surface (top_bar, overflow, detail_body, row_long_press, row_swipe). No ids, titles or content. It goes through the C7 typed registry (contracts/analytics/events.json, generated Dart, a presence consumer) and AnalyticsManager, so consent and queueing apply. Tests cover the event shape for every action and the top bar, overflow, row long-press and row swipe call sites. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(app): the people chip names only named speakers David, 2026-09-24: never label a participant "Speaker 1". The conversation header's people chip now shows the first named participant ("You" for the account owner) and counts everyone else: "David + 3 others" (participantsSummary, an ICU plural in 49 locales). With nobody named it hides; a linked calendar event's attendees still stand in when the transcript names no one. The transcript's own per-line speaker labels are unchanged. Tests cover named, mixed and none. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: drop pointers to the deleted speech-profile page Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(app): address review round 5 (voice profile and people chip) - SpeechProfileProvider drove only the deleted question page; the guided introduction records through GuidedVoiceIO. Remove the provider, its registration, the onboarding back-step close() that no longer stopped anything, and its test. - Drop pointers to deleted files: the addressability baseline entry and the logout flow's covers (now sign_out.dart); a stale doc paragraph. - participantsSummary carries few/many forms so Polish, Czech and Slovak agree for 2-4 and 5+; other Slavic and Baltic locales use a count-agnostic "one more" form. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 5 天前 | |
app: use const constructors in widgets/waveform_painter.dart | 2 个月前 | |
feat(app): add System / Light / Dark appearance setting on mobile (#19378) * feat(app): add system-aware light and dark appearance * fix(app): restore light appearance contrast on v1 surfaces * fix(app): provide appearance in visual audit harness Failure-Class: none * test(app): support light mode in visual audit harness * fix(app): correct light-mode color contrast Failure-Class: none * fix(app): improve remaining light-mode contrast Failure-Class: none * fix(app): use palette tokens for light transcript bubbles Replace the two hardcoded light-mode bubble colours with surface2 and a pale alpha-blend of the speaker colour, so the mobile UX contract's colour-literal ratchet stays flat. Failure-Class: none Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 2 天前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 2 天前 | ||
| 2 个月前 | ||
| 10 小时前 | ||
| 5 天前 | ||
| 5 天前 | ||
| 2 天前 | ||
| 2 天前 | ||
| 5 天前 | ||
| 2 天前 | ||
| 2 天前 | ||
| 5 天前 | ||
| 2 天前 | ||
| 2 个月前 | ||
| 2 天前 | ||
| 5 天前 | ||
| 3 小时前 | ||
| 5 小时前 | ||
| 2 天前 | ||
| 5 天前 | ||
| 22 天前 | ||
| 1 天前 | ||
| 5 天前 | ||
| 2 天前 | ||
| 2 天前 | ||
| 10 小时前 | ||
| 6 个月前 | ||
| 7 个月前 | ||
| 5 天前 | ||
| 2 天前 | ||
| 1 天前 | ||
| 5 天前 | ||
| 2 个月前 | ||
| 2 天前 |