Skip to content

Duck.Ai/ChatHistory: Add context menu - #9760

Merged
karlenDimla merged 7 commits into
feature/karl/duckai-sidebar/re-routefrom
feature/karl/duckai-sidebar/history-context-menu
Sep 16, 2026
Merged

karlenDimla merged 7 commits into
feature/karl/duckai-sidebar/re-routefrom
feature/karl/duckai-sidebar/history-context-menu

Conversation

@karlenDimla

@karlenDimla karlenDimla commented Sep 9, 2026 •

Copy link
Copy Markdown
Contributor

Task/Issue URL: https://app.asana.com/1/137249556945/task/1218238186970938?focus=true
Tech Design URL (if applicable):
API Proposals URL(s) (if applicable):

Description

  • Chat history screen gains an overflow menu (gated by the flag) with Select chats and Chats protection actions; Chats protection opens the Duck.ai chat-protection URL (new RealDuckChat.getChatProtectionUrl()), in a new tab unless already on a chat tab.

Steps to test this PR

Setup

  • Internal build installed
  • At least three saved chat exists in history

nativeDuckAiSidebar enabled, default

  • Open the native chat history screen (Duck.ai sidebar button with nativeDuckAiSidebar on) from a duck.ai chat tab
  • Overflow (⋮) icon IS visible in the chat history toolbar
  • Tapping ⋮ opens a popup with Select chats and *Chats protection
  • Tapping Select chats enters selection mode (toolbar switches to select-mode actions)
  • Verify items are selectable, selecting/deselecting ALL works and Overflow (⋮) icon and search icon are NOT visible
  • Press back and verify you exit the select state
  • Tapping Overflow (⋮) icon and select Chat protection from the menu
  • Verify that history screen is dismissed and on the previously opened tab, the chat protection sheet becomes visible
  • Go to New Tab Page
  • Select menu and press "Chats"
  • Tapping Overflow (⋮) icon and select Chat protection from the menu
  • Verify a new duck.ai chat tab opens and the chat protection sheet becomes visible

nativeDuckAiSidebar disabled (via FF inventory)

  • Go to New Tab Page
  • Select menu and press "Chats"
  • Verify that Overflow (⋮) icon is NOT visible while the history search bar is showing

Note

Low Risk
UI and navigation-only changes behind an existing feature flag, with unit tests for URL/tab routing; no auth or data-layer changes.

Overview
Adds a toolbar overflow (⋮) menu on native Duck.ai chat history, shown only when nativeDuckAiSidebar is enabled. The menu offers Select Chats (enters bulk selection; hidden when history isn’t loaded) and Chat Protection.

Chat Protection navigates via a new getChatProtectionUrl() on RealDuckChat (chatProtection=open on the chat URL). The ViewModel opens that URL in the current tab if the user was already on a Duck.ai chat tab, otherwise in a new tab; the fragment wires OpenChatProtection to BrowserNav accordingly.

Overflow is hidden in select mode and during search. Tests cover tab-reuse vs new-tab behavior; the test fake implements getChatProtectionUrl() and isDuckChatUrl for duck.ai.

Reviewed by Cursor Bugbot for commit b58e76e. Bugbot is set up for automated code reviews on this repo. Configure here.

karlenDimla commented Sep 9, 2026 •

Copy link
Copy Markdown
Contributor Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ 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 3165abd. Configure here.

Comment thread duckchat/duckchat-impl/src/main/java/com/duckduckgo/duckchat/impl/RealDuckChat.kt Outdated

@malmstein malmstein left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm, one suggestion: the new chat-tab vs new-tab branch has no test, and ChatHistoryViewModelTest already has the pattern for it.

controls.update { it.copy(mode = Mode.Selecting(emptySet())) }
}

fun onChatsProtectionClicked() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: the inNewTab decision here isn't covered, and the fixture is already in place since FakeDuckChatInternal stubs getChatProtectionUrl. two cases worth pinning with the existing navigationEvents pattern: selected tab is a duck.ai chat so we reuse it, and anything else so we open a new one.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed.

karlenDimla and others added 6 commits September 15, 2026 13:30
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fixes test module compilation broken when getChatProtectionUrl was added
to the DuckChatInternal interface without updating the test fake.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
reduce() stays on Empty when there are no chats, so entering select mode
from the empty state was a no-op. Only offer Select Chats when chats are
present; Chat Protection remains available regardless.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cover both branches: reuse the current tab when it is a duck.ai chat,
open a new tab otherwise. Make FakeDuckChatInternal.isDuckChatUrl match
duck.ai hosts so the reuse branch is reachable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@karlenDimla
karlenDimla force-pushed the feature/karl/duckai-sidebar/re-route branch from 6c82012 to 577a8ee Compare September 15, 2026 17:50
@karlenDimla
karlenDimla force-pushed the feature/karl/duckai-sidebar/history-context-menu branch from 1f24033 to b58e76e Compare September 15, 2026 17:50
…9761)

Task/Issue URL:
https://app.asana.com/1/137249556945/task/1218238186970934?focus=true
Tech Design URL (if applicable): 
API Proposals URL(s) (if applicable):

### Description
- New "+" plus menu on the omnibar with New Chat, New Voice Chat, New
Image (sidebar-only), New Search / New Tab, and New Fire Tab entries.
- New Search (the sidebar relabel of "New Tab") opens a regular new tab
and auto-surfaces the native input focused on the Search tab; with the
flag off the entry stays "New Tab" and behaves as before.
- Generalized the onboarding input-screen one-shot into
InputScreenLaunchTarget (carries an InputMode instead of a DUCK_AI
boolean) so both onboarding (Duck.ai tab) and New Search (Search tab)
can drive the initial input mode.

### Steps to test this PR
Setup
  - [x] Internal build installed
- [x] Native input / input screen feature is active (native omnibar
input widget with the Search / Duck.ai toggle is in use)
  - [x] Do at least one duck.ai chat 
- [x] Open the browser on a New Tab Page and focus the omnibar so the
"+" plus button is available

  1. `nativeDuckAiSidebar` enabled (default) — open the "+" menu
  - [x] Tap the "+" plus button in the omnibar
- [x] The menu entry (with the add-tab icon) reads New Search (not "New
Tab")
  - [x] The New Image entry IS visible

  2. nativeDuckAiSidebar enabled — tap New Search
  - [x] A new tab opens (regular, non-Fire)
- [x] The native input widget IS auto-surfaced (keyboard shown), without
a manual omnibar tap
  - [x] The widget opens with the Search tab selected (not Duck.ai)
  - [x] The input field is empty and ready to type
- [x] Typing a query and submitting performs a search (not a Duck.ai
chat)

3. `nativeDuckAiSidebar` disabled (via FF Inventory) — open the "+" menu
  - [x] Tap the "+" plus button in the omnibar
  - [x] The menu entry reads New Tab (not "New Search")
  - [x] The New Image entry is NOT visible

  4. nativeDuckAiSidebar disabled — tap New Tab
  - [x] A new tab opens (regular, non-Fire)
- [x] The tab lands with the address bar unfocused (native input is NOT
force-opened)
- [ ] No Search-tab pre-selection behavior occurs (unchanged
pre-existing behavior)

  Regression check
- [ ] Duck.ai onboarding: completing the Search-path onboarding still
auto-opens the input screen on the Duck.ai (chat) tab
  - [ ] Custom-AI onboarding still finishes on the Duck.ai (chat) tab
- [ ] Focusing the omnibar on an existing tab (no menu action) still
opens the input with the default toggle position — SEARCH is not
spuriously forced


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches new-tab creation, activity recreate/deferred actions, and
native input initial mode; behavior is gated on `nativeDuckAiSidebar`
but onboarding Duck.ai landing must stay correct.
> 
> **Overview**
> When **native Duck.ai sidebar** is on, the omnibar **+** menu relabels
**New Tab** to **New Search**, shows a **New Image** row (action still
TODO), and opening that entry starts a regular tab with the native input
screen on the **Search** tab—not Duck.ai.
> 
> **Per-tab initial input mode** is threaded through `launchNewTab` →
`PendingAction.OpenNewTab` (including bundle round-trip on browser-mode
recreate) → `pendingInputModeTargets` /
`BrowserTabFragment.inputModeTarget` →
`BrowserTabViewModel.consumeInitialInputMode()`, which prefers that
one-shot target and still falls back to post-onboarding **Duck.ai** via
`OnboardingInputScreenLaunchTarget` in the ViewModel (removed from the
fragment).
> 
> Tests cover `InputMode` persistence across pending mode switch and
one-shot consumption behavior.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
aabfdda. 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: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ API Proposal reminder

This PR changes the public surface of one or more -api modules, but the API Proposals field of the PR description is empty. This is a non-blocking reminder — please double-check whether a proposal is needed.

Changed -api source files:

  • browser-api/src/main/java/com/duckduckgo/app/tabs/BrowserNav.kt
  • duckchat/duckchat-api/src/main/java/com/duckduckgo/duckchat/api/DuckChat.kt

To clear this reminder, edit the PR description and either:

  • add the approved API Proposal link(s) in the API Proposals field (one per line), or
  • write None there if this PR does not change the public API (e.g. KDoc, comments, tests).

See the API Proposals section in .claude/docs/contributions.md for details.

@karlenDimla
karlenDimla merged commit 43ffe64 into feature/karl/duckai-sidebar/re-route Sep 16, 2026
13 checks passed
@karlenDimla
karlenDimla deleted the feature/karl/duckai-sidebar/history-context-menu branch September 16, 2026 17:44
daotuananh1999tgdd-sudo pushed a commit to daotuananh1999tgdd-sudo/Android that referenced this pull request Sep 27, 2026
…ckduckgo#9752)

Task/Issue URL:
https://app.asana.com/1/137249556945/task/1218228567702514?focus=true
Tech Design URL (if applicable): 
API Proposals URL(s) (if applicable):
https://app.asana.com/1/137249556945/project/1201462763415876/task/1218312857360677?focus=true

### Description
This is the main PR to Implement replace Chat Sidebar with chats history
and include all change outlined in the stack:
- duckduckgo#9760
- duckduckgo#9761
- duckduckgo#9773
### Steps to test this PR
_nativeDuckAiSidebar enabled, default_
- [x] Open a duck.ai chat
- [x] Select the sidebar
- [x] Verify that chats history opens


_nativeDuckAiSidebar disabled_
- [x] Open a duck.ai chat
- [x] Select the sidebar
- [ ] Verify that the FE sidebar opens



<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes omnibar Duck.ai navigation behind a remote flag; incorrect
rollout could send users to the wrong sidebar/history surface, though
legacy behavior is preserved when the flag is off.
> 
> **Overview**
> **Routes the omnibar Duck.ai sidebar tap through a feature-flagged
path** so users with `nativeDuckAiSidebar` enabled open native chat
history (`openDuckChatHistory` / `LaunchDuckChatHistory`) instead of the
legacy web sidebar JS bridge.
> 
> Adds **`nativeDuckAiSidebar`** to `DuckAiFeatureState`, wired from
remote config via new `DuckChatFeature.nativeDuckAiSidebar()` (default
internal) in `RealDuckChat`. `BrowserTabFragment` now calls
`onDuckChatSidebarButtonPressed()` instead of `openDuckChatSidebar()`
directly.
> 
> Also **fixes swapped native actions**: `onCustomizeResponsesClicked`
now emits `NativeAction.CUSTOMIZE_RESPONSES` and `openDuckChatSidebar`
emits `NativeAction.SIDEBAR` (they were reversed). Unit tests cover the
flag branches, history vs sidebar fallback, and the corrected actions.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
577a8ee. 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: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants