| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
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> | 2 天前 | |
improve announcements handling | 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> | 2 天前 | |
feat: mobile product telemetry and PostHog experimentation (#15883) * feat: add mobile product telemetry and PostHog experiment infrastructure * test: verify billing projection at the webhook persistence boundary * Harden PostHog scorecard completeness * harden: keep unknown feedback failure causes explicit * chore: regenerate clients for mobile feedback contract * fix: restore backend CI contracts for telemetry changes * fix: keep feedback receipt validation strict | 3 天前 | |
fix: plugin provider conflict | 2 年前 | |
feat(app): detect silent pendant capture and bound BLE self-recovery Failure-Class: none Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: multica-agent <github@multica.ai> | 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> | 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> | 2 天前 | |
Phase 4.1: replace DevApiKey/DevApiKeyCreated hand-written classes with generated typedefs DevApiKey and DevApiKeyCreated were pure 1:1 field-mapping wrappers around GeneratedDevApiKey / GeneratedDevApiKeyCreated. Replace with typedefs. Call-site updates: - dev_api.dart: drop fromGenerated indirection, use ClassName.fromJson directly; remove now-unused wire import. - dev_api_key_provider.dart: the created types are now siblings without inheritance, so the List<DevApiKey>.insert(DevApiKeyCreated) site converts explicitly via DevApiKey.fromJson(created.toJson()). | 2 个月前 | |
chore: retire dormant flags and register experiment decisions (#18968) * chore: retire dormant flags without changing production defaults Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * chore: preserve UX and changelog contracts during flag retirement Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * test(backend): unpin the calendar-event dump removed with auto-link The K3 removal deleted the only model_dump in _emit_derived_effects; the trust-boundary pin must shrink with it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 14 小时前 | |
fix(backend): notify users about BYOK LLM key failures (#7323) * fix(backend): tag BYOK LLM errors by source * fix(backend): notify users about BYOK LLM key failures * fix(backend): notify on sync embedding BYOK failures; release dedupe lock on failed delivery Route the explicit _OpenAIEmbeddingsProxy.embed_query/embed_documents methods through handle_llm_error before falling back/re-raising (they bypass the __getattr__ wrapper, so sync embedding failures were never surfaced). Only keep the 24h dedupe lock after >=1 successful FCM send: release it when send_each raises or no token succeeds, so transient failures stay retryable. Adds regression tests. * fix(backend): batch FCM sends at 500; align async embedding BYOK fallback messaging.send_each rejects >500 messages, so batch device tokens at 500 per call (partial success keeps the dedupe lock; total failure releases it). Make async aembed_query/aembed_documents notify-and-fall-back to Omi's key on BYOK key failures, matching the sync methods. Adds >500-token batching and async-fallback regression tests. * Fix PR CI after merge * Format Dart files touched by PR diff * Fix frontend prettier lint * Make BYOK LLM error imports test-safe * Allow known slow backend unit tests * Allow payment promotion duration guard baseline * Allow canonical maintenance duration guard baseline --------- Co-authored-by: David Zhang <david@scalingforever.com> | 2 个月前 | |
feat(app): detect silent pendant capture and bound BLE self-recovery Failure-Class: none Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: multica-agent <github@multica.ai> | 1 天前 | |
fix(app): recover terminal auth sessions Prevent authenticated requests from degrading to anonymous traffic, bind token refreshes to the originating Firebase UID, and terminate confirmed dead sessions exactly once. Clear user-scoped provider/cache state, cancel stale async work and conversation retries, route to localized reauthentication UI, and add a signed iOS refresh canary. Verification: cd app && flutter test --no-pub (704 passed); flutter gen-l10n (zero untranslated warnings); targeted dart analyze (no compile errors); git diff --check; independent agent review approved. UI limitation: no physical iOS device or simulator was available for the signed canary; the reauthentication path was exercised through its Flutter widget regression test. | 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> | 2 天前 | |
fix(app): keep cached people when the people fetch fails (#14911) | 5 天前 | |
feat: mobile product telemetry and PostHog experimentation (#15883) * feat: add mobile product telemetry and PostHog experiment infrastructure * test: verify billing projection at the webhook persistence boundary * Harden PostHog scorecard completeness * harden: keep unknown feedback failure causes explicit * chore: regenerate clients for mobile feedback contract * fix: restore backend CI contracts for telemetry changes * fix: keep feedback receipt validation strict | 3 天前 | |
fix(app): keep offline recordings with the account that made them (#15049) * fix(app): keep offline recordings with the account that made them * fix(app): fail closed on unreadable recording ownership and keep other accounts' jobs dormant A corrupt or truncated omibatch_owners.json read as an empty map, so the next scan stamped every recording to the signed-in uid. Ownership now fails closed and the sidecar is written through a temp file. Reconciliation only polls jobs whose recording is visible to the signed-in uid, and terminal mutations are abandoned if the uid changes mid-flight. | 5 天前 | |
Add support 21 languages for app's language. Clarify settings > profile > languages (#4212) | 8 个月前 | |
fix(app): recover terminal auth sessions Prevent authenticated requests from degrading to anonymous traffic, bind token refreshes to the originating Firebase UID, and terminate confirmed dead sessions exactly once. Clear user-scoped provider/cache state, cancel stale async work and conversation retries, route to localized reauthentication UI, and add a signed iOS refresh canary. Verification: cd app && flutter test --no-pub (704 passed); flutter gen-l10n (zero untranslated warnings); targeted dart analyze (no compile errors); git diff --check; independent agent review approved. UI limitation: no physical iOS device or simulator was available for the signed canary; the reauthentication path was exercised through its Flutter widget regression test. | 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> | 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> | 2 天前 | |
feat: mobile product telemetry and PostHog experimentation (#15883) * feat: add mobile product telemetry and PostHog experiment infrastructure * test: verify billing projection at the webhook persistence boundary * Harden PostHog scorecard completeness * harden: keep unknown feedback failure causes explicit * chore: regenerate clients for mobile feedback contract * fix: restore backend CI contracts for telemetry changes * fix: keep feedback receipt validation strict | 3 天前 | |
feat: show speaker labels optimistically (#19061) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> | 7 小时前 | |
feat(mobile): request native reviews after meaningful reading moments | 4 天前 | |
feat(people): speaker tag prompts — teach Omi voices from the last 48 hours (#18483) * feat(people): speaker tag prompts that teach Omi voices from the last 48 hours Once a day, a small card on the conversations list plays a few short, clean clips from the last 48 hours and asks "Is this you?", "Is this <name>?" or "Who is this?". Answers reuse the manual speaker-assignment transaction and feed the existing voice-sample quality gates, so each one improves speaker recognition for that user over time. - Owner check is free; naming other people follows the named speaker-ID entitlement (paid plans). - "That's me" now trains the owner voiceprint: a verified clip is pooled with the enrollment voiceprint (last 5 confirmations). - Saving other people's voices is on by default with a new setting to turn it off; the first prompt shows the switch inline. The switch is enforced in extract_speaker_samples, the one path every teaching flow reaches. - Server-owned pacing: 20h cooldown after a shown set, 7-day back-off after three unanswered sets, 2h cache of empty scans. - Privacy-safe instrumentation: Prometheus counters with closed-enum labels (requests, served, answers, quality outcomes, voice samples, setting changes), backend product events without names/text/ids, and typed mobile events with a registered funnel consumer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(people): typed ApiResult boundary for tag-prompt client; JSON clip payload The mobile API boundary forbids new nullable HTTP helpers, so every speaker tag-prompt call now goes through executeApi and the provider handles each failure explicitly. executeApi decodes bodies as text, so the clip endpoint returns base64 WAV in a typed SpeakerTagPromptClip instead of raw bytes. The typed conversation screen harness registers an inert prompts provider. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(people): declare route policy for speaker tag-prompt endpoints Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(people): harden speaker tag prompts after Devin Fusion review Findings from a Devin Fusion review pass, verified and adjusted: - Clean clips must not overlap another diarized voice in time; a clean run wins over a longer overlapping one for the same voice. - Owner clips: one diarized voice only; verification text comes from segments overlapping the clip window (a cropped long turn still counts); only segments the answer actually assigned are sampled. - Owner pooling creates the user document when missing. - The "save other people's voices" opt-out is re-checked inside the person profile write transaction, closing the race with in-flight extraction. - Pacing state writes (shown / dismissed / answered) are transactional so concurrent requests cannot lose counts or answered ids. - Mobile provider ignores responses from a previous account session and stale clip loads (generation + playback tickets). Reviewer-proposed changes not taken: a started_at scan (new composite index) — created_at >= started_at, so the existing indexed created_at range already covers every conversation that started in the window; and a StrictFirestore change that broke existing DELETE_FIELD assertions. Co-Authored-By: Devin <noreply@cognition.ai> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(people): satisfy backend typecheck for speaker tag prompts Own PCM16 trim/WAV helpers in clips.py instead of importing private speaker_identification functions; public speaker_id_of; typed talk-time map; explicit Optional handling in the owner-sample path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(people): regenerate TypeScript OpenAPI types for speaker tag-prompt endpoints Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(people): regenerate Swift OpenAPI types for speaker tag-prompt endpoints Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(people): add mobile changelog fragment for speaker tag prompts Co-Authored-By: GLM (zcode) <noreply@z.ai> * refactor(people): decode speaker tag-prompt responses through the wire prefix The app-client schema inventory gate requires OpenAPI-backed routes to decode through wire.Generated* parsers; qualify the generated imports and inline the object guard on the clip and voice-profile-settings calls. Co-Authored-By: GLM (zcode) <noreply@z.ai> * refactor(people): adopt shared Omi UX primitives in the speaker tag prompt card INV-UI-3 (new from main's UX consistency pass) ratchets hand-rolled UI in changed files: swap color/font literals for OmiColors/OmiType, the raw name AlertDialog for OmiAlertDialog, and extend OmiDialogAction with an optional key so tests can still target the rendered save button. Co-Authored-By: GLM (zcode) <noreply@z.ai> * refactor(app): decode the capture-group separate ack through its generated model The app client schema inventory gate (raw OpenAPI response decode migration) fails on separateConversationFromCaptureGroup, which still read body['status'] from a raw jsonDecode. Pre-existing from #17297 and currently red on origin/main, so this PR cannot go green without it. - Add StatusResponse to the conversation wire group and regenerate conversation_wire.g.dart (GeneratedStatusResponse). - Decode the separate ack via wire.GeneratedStatusResponse.fromJson; behavior unchanged (same enum mapping, same catch-all fallback). Verification: tests/unit/test_app_client_schema_inventory.py 12 passed; dart generator + utf8 contract + inventory suites 40 passed; flutter test capture_groups_test.dart (17) and conversation_hub_actions_test.dart (10) passed; dart analyze clean; generate_dart_models.py --all --check clean. * refactor(people): make the tag-prompt name dialog and suggestions work on iOS cold start The "Someone new" dialog is a CupertinoAlertDialog on iOS, which has no Material ancestor, so its TextField threw. Wrap it in a transparent Material like the other text-field dialogs. Suggested-person chips now watch PeopleProvider so they appear when the people list loads after the card. Both covered by widget tests that fail without the change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(people): align People page controls and tidy the tag-prompt card - People page: person rows, sample rows and the voice switches share one 16pt trailing edge, so the delete icons line up; samples are numbered Sample 1, 2, ... instead of "Voice Profile" then "Sample 1". - Tag-prompt card: answers are uniform 44pt pills, and the "1 of 3" progress sits beside the question so the header subtitle no longer wraps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Devin <noreply@cognition.ai> Co-authored-by: GLM (zcode) <noreply@z.ai> Co-authored-by: David Zhang <9387252+Git-on-my-level@users.noreply.github.com> | 1 天前 | |
| 1 天前 | ||
fix(app): keep task default unchanged when save fails (#15088) * fix(app): commit task default only after server accepts Failure-Class: none * test(app): cover rejected default task integration | 5 天前 | |
fix(app): keep the loaded plan when a subscription refresh fails (#14940) | 4 天前 | |
fix(app): keep the training data status when its fetch fails (#15242) | 4 天前 | |
fix: create voice recording file before starting | 1 个月前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 2 天前 | ||
| 7 个月前 | ||
| 2 天前 | ||
| 3 天前 | ||
| 2 年前 | ||
| 1 天前 | ||
| 2 天前 | ||
| 2 天前 | ||
| 2 个月前 | ||
| 14 小时前 | ||
| 2 个月前 | ||
| 1 天前 | ||
| 2 个月前 | ||
| 2 天前 | ||
| 5 天前 | ||
| 3 天前 | ||
| 5 天前 | ||
| 8 个月前 | ||
| 2 个月前 | ||
| 2 天前 | ||
| 2 天前 | ||
| 3 天前 | ||
| 7 小时前 | ||
| 4 天前 | ||
| 1 天前 | ||
| 1 天前 | ||
| 5 天前 | ||
| 4 天前 | ||
| 4 天前 | ||
| 1 个月前 |