Hide Duck.ai toggle in chat, show back arrow with sidebar#8699
Conversation
Duck.ai now always opens in a new tab rather than converting the current one. Keeping the Search/Duck.ai toggle visible inside a chat would mean every toggle interaction spawns a new tab, which caused tab bloat. The toggle row stays in the browser context but disappears once the user is in a fullscreen chat. NativeInputState.toggleVisible already excluded DUCK_AI_CONTEXTUAL; extend that to DUCK_AI by phrasing the rule positively as "inputContext == BROWSER". The contextual sheet remains unchanged. applyOmnibarShape() used `toggleVisible` to short-circuit when the toggle row was shown, implicitly covering DUCK_AI fullscreen. Narrow its guard to `inputContext != BROWSER` so DUCK_AI fullscreen no longer falls through to the minimized-pill card styling. Asana: https://app.asana.com/1/137249556945/task/1215159003631557 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously the back arrow next to the chat-history sidebar in a Duck.ai tab only appeared when the user had the unified input on but the Search/Duck.ai toggle setting off (a narrow Search-Only case). With the toggle setting on (Search & Duck.ai), the back arrow stayed hidden, so users had no consistent way back to the prior tab while viewing a chat. Tie the back arrow to the sidebar's own visibility instead: when the sidebar is shown, the back arrow is shown. Both still hide together on focus or in voice mode, since that gating already sits on showDuckAISidebar. isDuckAiBackAvailable has no other consumers, so drop it from ViewState along with its dedicated tests. Asana: https://app.asana.com/1/137249556945/task/1215159003631561 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 65f05dc. Configure here.
| } | ||
| } | ||
|
|
||
| @Test |
There was a problem hiding this comment.
Unused test variable and mock setup left behind
Low Severity
inputScreenUserSettingFlow and its mock setup for observeInputScreenUserSettingEnabled() are now dead code. The ViewModel's combine block no longer consumes this flow (it was only used to compute the removed isDuckAiBackAvailable), and no remaining test sets inputScreenUserSettingFlow.value. This leftover setup can mislead future developers into thinking the ViewModel still depends on this flow.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 65f05dc. Configure here.
Task/Issue URL: https://app.asana.com/1/137249556945/task/1215159003631554?focus=true ### Description Under fullscreen mode the unified input's Duck.ai entries used to convert the current browser tab into a chat. Three paths now spawn a new tab instead, source-linked to the previous tab so the back arrow (already shipped in #8699) returns the user to where they came from: - Toggle submit with Duck.ai selected - Chat-history suggestion tap - "Ask Duck.ai" autocomplete row A single helper on `BrowserTabViewModel` — `openDuckAiQuery` for query paths and `openDuckAiChatById` for chat-history's pre-built URLs — funnels all three through one decision. The shared private `navigateToDuckAi` adds an NTP carve-out: on an empty tab the chat loads in place so we don't leave the user staring at two empty tabs. Outside fullscreen mode every path falls through to today's legacy Intent behaviour, untouched. `Command.SubmitChat` only had the autocomplete caller and is removed along with its handler in `BrowserTabFragment`. ### Steps to test this PR _Toggle submit (Duck.ai selected)_ - [x] On a browser tab with content, focus the input and switch the toggle to Duck.ai. - [x] Submit a query. A new tab opens with Duck.ai showing the chat; the original tab stays in place behind it. - [x] Tap the back arrow next to the chat-history icon. The original tab is foregrounded. - [x] Repeat the test from an NTP (open a new tab, do not load a URL). Submitting the query reuses the NTP tab — no extra empty tab is spawned. _Chat history suggestion_ - [x] Have a couple of existing Duck.ai chats in history. - [x] On a browser tab with content, focus the input and switch the toggle to Duck.ai. Tap one of the chat-history suggestions. - [x] A new tab opens on the selected chat. Back arrow returns to the originating tab. - [x] Repeat from NTP. The NTP tab is reused for the selected chat. _"Ask Duck.ai" autocomplete row_ - [x] On a browser tab with content, focus the input with the Search tab selected and type a query. - [x] Tap the "Ask Duck.ai" row in the autocomplete suggestions list. - [x] A new tab opens with Duck.ai answering the prompt; the original tab stays. - [x] Repeat from NTP. NTP is reused. _Existing Duck.ai chat unchanged_ - [x] Open a Duck.ai chat, focus the input, send a follow-up message. The message reaches the existing chat — no new tab is spawned. _Legacy fallback (fullscreen mode off)_ - [x] Disable the Duck.ai fullscreen mode feature flag. - [x] Repeat the toggle-submit and autocomplete flows. Chat opens via the legacy Intent activity exactly as before; no behaviour change. ### UI changes | Before | After | | ------ | ----- | !(Upload before screenshot)|(Upload after screenshot)| <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Changes core tab/navigation and Duck.ai entry flows behind a feature flag, with broad unit test coverage; legacy paths are preserved when fullscreen is disabled. > > **Overview** > When **Duck.ai fullscreen mode** is on, unified-input actions that used to replace the current tab now route through **`openDuckAiQuery`** / **`openDuckAiChatById`** and **`navigateToDuckAi`**: toggle submit, chat-history taps, and “Ask Duck.ai” autocomplete open a **source-linked new tab** on an active browser tab, and **reuse the NTP tab** when nothing is loaded yet. **Legacy Intent** behavior is unchanged when fullscreen is off. > > **`Command.SubmitChat`** is removed; **`NativeInputManager`** calls **`onDuckAiQuerySubmitted`** instead of pushing a URL via search submit. **`OpenInNewTab`** now dismisses the focused overlay, autocomplete, and native input so they don’t sit on top of the new tab. > > Tests cover fullscreen vs legacy and NTP vs browser tab for each entry path. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 4a6bd24. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: David Gonzalez <malmstein@Davids-MacBook-Pro.local> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Task/Issue URL: https://app.asana.com/1/137249556945/project/1214157224317277/task/1215159003631560?focus=true ### Description Last piece of the iOS Ship Review nav model for Duck.ai. Two coordinated changes, both scoped to Duck.ai fullscreen context (no change anywhere else): 1. **Omnibar fire → "+" menu.** In `ViewMode.DuckAI` the omnibar's fire icon is replaced by a "+" that opens a popup menu. The swap is derived from the view mode at render time (no separate state flag that can drift), and `tabsMenu` follows whichever of fire/+ is visible via a barrier. 2. **Fire button moves into the chat input.** In a Duck.ai chat (bottom bar) the fire button sits at the leading edge of the input row, outside the card so the card's rounded corners don't clip it. The trailing in-widget fire is hidden there so the user only ever sees one fire affordance, and the leading fire hides while the input is focused. **The "+" popup menu** (`popup_chat_menu.xml`, shown anchored via the shared `PopupMenu.showAnchoredView` so it positions above/below depending on whether the omnibar is at the top or bottom): | Item | Action | |---|---| | New Chat | `openNewDuckChat` → `NativeAction.NEW_CHAT` | | New Voice Chat | `duckChat.openVoiceDuckChat()` | | New Tab | `launchNewTab` (fresh NTP) | | New Fire Tab | `launchNewTab` (same as New Tab for now) | **New Fire Tab** is only shown when `FireModeAvailability.isAvailable()` is true (feature flag + WebView MultiProfile support), re-checked each time the menu opens, so we don't surface it to users who can't use Fire Mode. Outside fullscreen mode (legacy Intent path), nothing changes — the omnibar never enters `ViewMode.DuckAI`. Stacks conceptually on #8699 (toggle removal + back arrow) and #8700 (new-tab navigation), but is independent and lands on its own. ### Steps to test this PR _Omnibar fire → +_ - [x] On a normal browser tab the omnibar shows the fire icon (unchanged). - [x] Open a Duck.ai chat. The omnibar shows **+** where fire was; the fire icon is gone; the tabs button stays correctly positioned next to it. - [x] Switch back to a browser tab — fire returns, + is gone. Confirm + never appears on NTP or in custom tabs. _+ popup menu_ - [x] Tap **+** in a Duck.ai chat. A popup opens anchored to the + (above it when the omnibar is at the bottom, below when at the top). - [x] **New Chat** starts a fresh chat. - [x] **New Voice Chat** opens voice mode. - [x] **New Tab** opens a fresh New Tab Page. - [ ] **New Fire Tab** opens a fresh New Tab Page (same as New Tab for now). _New Fire Tab gating_ - [ ] With Fire Mode available (flag on + supported WebView), **New Fire Tab** is present. - [x] With Fire Mode unavailable (flag off or unsupported WebView), **New Fire Tab** is hidden and the rest of the menu is intact. _Fire button in the chat input_ - [x] In a Duck.ai chat (bottom bar), a fire icon sits at the leading edge of the input row, fully visible (not clipped by the card corners). - [x] The trailing in-widget fire (next to tabs/menu) is not shown in chat. - [x] Tap the leading fire — the fire / clear-data dialog opens, same as the omnibar fire. - [x] Focus the input — the leading fire hides; defocus — it reappears. - [x] On a browser tab, the trailing fire is shown and the leading fire is hidden (unchanged). _Legacy mode (fullscreen mode off)_ - [ ] Disable the Duck.ai fullscreen-mode flag. No UI changes anywhere — Duck.ai opens via the legacy Intent path and the omnibar never enters DuckAI view mode. ### UI changes | Before | After | | ------ | ----- | !(Upload before screenshot)|(Upload after screenshot)| --------- Co-authored-by: David Gonzalez <malmstein@Davids-MacBook-Pro.local> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Task/Issue URL: https://app.asana.com/1/137249556945/project/1214157224317277/task/1215159003631560?focus=true ### Description Last piece of the iOS Ship Review nav model for Duck.ai. Two coordinated changes, both scoped to Duck.ai fullscreen context (no change anywhere else): 1. **Omnibar fire → "+" menu.** In `ViewMode.DuckAI` the omnibar's fire icon is replaced by a "+" that opens a popup menu. The swap is derived from the view mode at render time (no separate state flag that can drift), and `tabsMenu` follows whichever of fire/+ is visible via a barrier. 2. **Fire button moves into the chat input.** In a Duck.ai chat (bottom bar) the fire button sits at the leading edge of the input row, outside the card so the card's rounded corners don't clip it. The trailing in-widget fire is hidden there so the user only ever sees one fire affordance, and the leading fire hides while the input is focused. **The "+" popup menu** (`popup_chat_menu.xml`, shown anchored via the shared `PopupMenu.showAnchoredView` so it positions above/below depending on whether the omnibar is at the top or bottom): | Item | Action | |---|---| | New Chat | `openNewDuckChat` → `NativeAction.NEW_CHAT` | | New Voice Chat | `duckChat.openVoiceDuckChat()` | | New Tab | `launchNewTab` (fresh NTP) | | New Fire Tab | `launchNewTab` (same as New Tab for now) | **New Fire Tab** is only shown when `FireModeAvailability.isAvailable()` is true (feature flag + WebView MultiProfile support), re-checked each time the menu opens, so we don't surface it to users who can't use Fire Mode. Outside fullscreen mode (legacy Intent path), nothing changes — the omnibar never enters `ViewMode.DuckAI`. Stacks conceptually on #8699 (toggle removal + back arrow) and #8700 (new-tab navigation), but is independent and lands on its own. ### Steps to test this PR _Omnibar fire → +_ - [x] On a normal browser tab the omnibar shows the fire icon (unchanged). - [x] Open a Duck.ai chat. The omnibar shows **+** where fire was; the fire icon is gone; the tabs button stays correctly positioned next to it. - [x] Switch back to a browser tab — fire returns, + is gone. Confirm + never appears on NTP or in custom tabs. _+ popup menu_ - [x] Tap **+** in a Duck.ai chat. A popup opens anchored to the + (above it when the omnibar is at the bottom, below when at the top). - [x] **New Chat** starts a fresh chat. - [x] **New Voice Chat** opens voice mode. - [x] **New Tab** opens a fresh New Tab Page. - [ ] **New Fire Tab** opens a fresh New Tab Page (same as New Tab for now). _New Fire Tab gating_ - [ ] With Fire Mode available (flag on + supported WebView), **New Fire Tab** is present. - [x] With Fire Mode unavailable (flag off or unsupported WebView), **New Fire Tab** is hidden and the rest of the menu is intact. _Fire button in the chat input_ - [x] In a Duck.ai chat (bottom bar), a fire icon sits at the leading edge of the input row, fully visible (not clipped by the card corners). - [x] The trailing in-widget fire (next to tabs/menu) is not shown in chat. - [x] Tap the leading fire — the fire / clear-data dialog opens, same as the omnibar fire. - [x] Focus the input — the leading fire hides; defocus — it reappears. - [x] On a browser tab, the trailing fire is shown and the leading fire is hidden (unchanged). _Legacy mode (fullscreen mode off)_ - [ ] Disable the Duck.ai fullscreen-mode flag. No UI changes anywhere — Duck.ai opens via the legacy Intent path and the omnibar never enters DuckAI view mode. ### UI changes | Before | After | | ------ | ----- | !(Upload before screenshot)|(Upload after screenshot)| <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > UI and navigation changes in Duck.ai fullscreen only; Fire Mode menu item is gated; no auth or data-layer changes. > > **Overview** > For **fullscreen Duck.ai** (`ViewMode.DuckAI`), the omnibar **swaps the fire icon for a "+"** that opens a anchored popup (`popup_chat_menu.xml`) with New Chat, New Voice Chat, New Tab, and optionally **New Fire Tab** (gated by `FireModeAvailability` on each open). **New Fire Tab** currently calls the same `launchNewTab()` as New Tab. > > In the **Duck.ai bottom input bar**, a **leading fire** sits beside the input card; the in-widget trailing fire is hidden in `DUCK_AI` context, and the leading fire hides while the field is focused. Fire/clear-data is wired through `NativeInputCallbacks` / `onFireButtonTapped` from both placements. > > Omnibar layout uses a **barrier** so tabs align with whichever leading control is visible; transitions key off `isDuckAiMode` so fire/+ cannot drift from view mode. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 799555e. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: David Gonzalez <malmstein@Davids-MacBook-Pro.local>
Task/Issue URL: https://app.asana.com/1/137249556945/project/1214157224317277/task/1215159003631560?focus=true ### Description Last piece of the iOS Ship Review nav model for Duck.ai. Two coordinated changes, both scoped to Duck.ai fullscreen context (no change anywhere else): 1. **Omnibar fire → "+" menu.** In `ViewMode.DuckAI` the omnibar's fire icon is replaced by a "+" that opens a popup menu. The swap is derived from the view mode at render time (no separate state flag that can drift), and `tabsMenu` follows whichever of fire/+ is visible via a barrier. 2. **Fire button moves into the chat input.** In a Duck.ai chat (bottom bar) the fire button sits at the leading edge of the input row, outside the card so the card's rounded corners don't clip it. The trailing in-widget fire is hidden there so the user only ever sees one fire affordance, and the leading fire hides while the input is focused. **The "+" popup menu** (`popup_chat_menu.xml`, shown anchored via the shared `PopupMenu.showAnchoredView` so it positions above/below depending on whether the omnibar is at the top or bottom): | Item | Action | |---|---| | New Chat | `openNewDuckChat` → `NativeAction.NEW_CHAT` | | New Voice Chat | `duckChat.openVoiceDuckChat()` | | New Tab | `launchNewTab` (fresh NTP) | | New Fire Tab | `launchNewTab` (same as New Tab for now) | **New Fire Tab** is only shown when `FireModeAvailability.isAvailable()` is true (feature flag + WebView MultiProfile support), re-checked each time the menu opens, so we don't surface it to users who can't use Fire Mode. Outside fullscreen mode (legacy Intent path), nothing changes — the omnibar never enters `ViewMode.DuckAI`. Stacks conceptually on #8699 (toggle removal + back arrow) and #8700 (new-tab navigation), but is independent and lands on its own. ### Steps to test this PR _Omnibar fire → +_ - [x] On a normal browser tab the omnibar shows the fire icon (unchanged). - [x] Open a Duck.ai chat. The omnibar shows **+** where fire was; the fire icon is gone; the tabs button stays correctly positioned next to it. - [x] Switch back to a browser tab — fire returns, + is gone. Confirm + never appears on NTP or in custom tabs. _+ popup menu_ - [x] Tap **+** in a Duck.ai chat. A popup opens anchored to the + (above it when the omnibar is at the bottom, below when at the top). - [x] **New Chat** starts a fresh chat. - [x] **New Voice Chat** opens voice mode. - [x] **New Tab** opens a fresh New Tab Page. - [ ] **New Fire Tab** opens a fresh New Tab Page (same as New Tab for now). _New Fire Tab gating_ - [ ] With Fire Mode available (flag on + supported WebView), **New Fire Tab** is present. - [x] With Fire Mode unavailable (flag off or unsupported WebView), **New Fire Tab** is hidden and the rest of the menu is intact. _Fire button in the chat input_ - [x] In a Duck.ai chat (bottom bar), a fire icon sits at the leading edge of the input row, fully visible (not clipped by the card corners). - [x] The trailing in-widget fire (next to tabs/menu) is not shown in chat. - [x] Tap the leading fire — the fire / clear-data dialog opens, same as the omnibar fire. - [x] Focus the input — the leading fire hides; defocus — it reappears. - [x] On a browser tab, the trailing fire is shown and the leading fire is hidden (unchanged). _Legacy mode (fullscreen mode off)_ - [ ] Disable the Duck.ai fullscreen-mode flag. No UI changes anywhere — Duck.ai opens via the legacy Intent path and the omnibar never enters DuckAI view mode. ### UI changes | Before | After | | ------ | ----- | !(Upload before screenshot)|(Upload after screenshot)| --------- Co-authored-by: David Gonzalez <malmstein@Davids-MacBook-Pro.local> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>


Task/Issue URL: https://app.asana.com/1/137249556945/project/1214157224317277/task/1215159003631557?focus=true
Description
Two related navigation tweaks from the iOS Ship Review fallout that apply
to Android — both small, both in service of the broader "Duck.ai opens in
a new tab" model that the next PR delivers.
Hide the Search/Duck.ai toggle inside a chat. The toggle row only
makes sense in the browser context now that Duck.ai will open in a new
tab; keeping it visible inside chat would spawn a new tab on every
toggle interaction.
NativeInputState.toggleVisiblealready excludedthe contextual sheet — this extends the rule to the fullscreen chat,
phrased positively as
inputContext == BROWSER.applyOmnibarShape()used
toggleVisibleto short-circuit and implicitly covered DUCK_AI;its guard is narrowed to
inputContext != BROWSERso DUCK_AI fullscreendoesn't fall through to the minimized-pill card styling.
Show the back arrow whenever the chat-history sidebar is visible.
The back arrow next to the sidebar only appeared when the user had the
unified input on but the Search/Duck.ai toggle setting off — a narrow
Search-Only case. With the toggle on (Search & Duck.ai), the arrow
stayed hidden, leaving no consistent way back to the previous tab while
viewing a chat. Tying the arrow to the sidebar's own visibility makes
both appear and disappear together; focus and voice-mode gating still
sit on
showDuckAISidebar.isDuckAiBackAvailablehas no otherconsumers, so it and its dedicated tests are removed.
Companion Asana task for #2:
https://app.asana.com/1/137249556945/task/1215159003631561
Steps to test this PR
Toggle hidden in chat
Back arrow alongside the sidebar
UI changes
Note
Low Risk
UI and visibility rules in omnibar and native input only; no auth, networking, or data-layer changes.
Overview
Aligns Duck.ai omnibar and unified input behavior with Duck.ai in its own tab: the Search/Duck.ai toggle only appears in browser context, and the control beside chat history tracks the sidebar instead of a narrow “search-only + native input” case.
Unified input:
NativeInputState.toggleVisibleis now true only wheninputContext == BROWSER(fullscreen chat and contextual sheet no longer show the toggle).NativeInputModeWidget.applyOmnibarShape()applies minimized-pill card styling only in browser context so fullscreen Duck.ai does not inherit that shape. Tests reflect toggle hidden in Duck.ai chat.Omnibar:
showDuckBackmatchesshowDuckAISidebar(sidebar and back/close control show and hide together).isDuckAiBackAvailableand its dependency on the input-screen user setting are removed, along with related ViewModel tests.duckAiBackuses the close icon instead of the back arrow.Reviewed by Cursor Bugbot for commit 65f05dc. Bugbot is set up for automated code reviews on this repo. Configure here.