Skip to content

cmux NEXT browser: Downloads popup under its toolbar button, draggable rows; Chromium's bubble off - #23050

Merged
austinywang merged 6 commits into
feat-cmux-nextfrom
feat-cmux-next-downloads-popover
Oct 21, 2026
Merged

austinywang merged 6 commits into
feat-cmux-nextfrom
feat-cmux-next-downloads-popover

Conversation

@austinywang

@austinywang austinywang commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Summary

In cmux-next, Chromium's own download bubble opened by itself when a download finished. cmux never shows Chromium's toolbar, so the bubble had nothing to anchor to and appeared over the middle of the page, far from cmux's Downloads button. The Downloads button opened a plain NSMenu, and an NSMenu row cannot start a drag, so a finished download could not be dragged into a terminal.

  • Chromium's bubble is off. The CEF shim sets download_bubble.partial_view_enabled to false for every request context when it loads (shim_process.mm), incognito included. In Chromium 154, DownloadBubbleUIController::GetPartialView() returns no rows while this pref is false, and OnBubbleAnchorAssembled then shows nothing. The full bubble opens only from Chromium's toolbar button, which cmux does not show.
  • The Downloads button opens a native popup (BrowserDownloadsPopup, built on NativePopupHost like the bookmark bubble). It sits under the button, right-aligned, newest download first. Clicking a finished row opens the file; dragging it carries the file URL out, so a terminal pastes its path. A right-click gives the row's actions (Open, Show in Finder, pause/resume, retry, cancel, copy link). A hovered finished row shows a folder button for Show in Finder, and the footer has one-line Open Downloads Folder and Clear Finished Downloads rows.
  • A progress ring around the Downloads button while a download runs (Chrome's): running bytes over their sizes, from 12 o'clock clockwise, in whole-percent steps over a faint track. A download of unknown size shows a short arc. No animation; the arc follows the bytes.
  • A finished download opens the popup by itself under that tab's button, without key focus (typing stays where it was). It closes after 5 s (DemandTimer) unless the pointer goes into it. A tab whose toolbar is not on screen keeps the existing notice over the tab. The popup closes when its toolbar leaves the screen (tab switch or close).

Testing

  • python3 scripts/cmux-next/prepush-gate.py: no new violations at each pushed head.
  • Fleet app builds (cmux-ci build cmux, tag nx-downloads-popover-anchor): job 07a26c025e8d at 5634e0a failed to compile (.failed is failed(String)), fixed in e700d94; jobs e4a85ddc01c1 (e700d94) and 23f29d7fdda8 (39f3fec) built.
  • A subagent correctness review of the diff found the compile error and five smaller issues (first-click on footer buttons, the popup outliving its toolbar, stacked row observers, file checks in body, a pointer already inside). All are fixed in 39f3fec. Its Chromium 154 source check confirmed the pref path above.

Observed / Not run

  • Observed: on 39f3fec in the tagged app, Chromium downloads of a local test file and an image finished (downloads.json). The person dogfooding saw cmux's popup under the Downloads button and no Chromium bubble over the page. The footer wrap they saw there is what 3a1c4c6 fixes.
  • Not run: no capture of the 3a1c4c6 footer yet. No automated drag into a terminal (this host has no screen-capture or computer-use access). No new tests: this is native popup UI, and the proof is the tagged-app dogfood.

Changelog

Fixed: cmux NEXT's download dialog opens under the browser's Downloads button instead of over the middle of the page, finished downloads drag from it into a terminal, and the button shows a progress ring while a download runs

Proof

Visual review on the exact head is still to come (macOS captures of the popup at rest, with a hovered row, and auto-shown after a download).

Checklist

  • Behavior changes have added or updated tests, or Testing says why not
  • UI change: localization audited. One new string, browserHits.downloads.showFolder ("Open Downloads Folder"), copied with all 21 locales from the existing action.browser.downloads.showFolder entry; the other strings are existing ones.

🤖 Generated with Claude Code

austinywang and others added 4 commits October 20, 2026 22:46
…ggable rows; Chromium's own bubble off

Chromium's download bubble opened by itself when a download finished and,
with no Chromium toolbar to anchor to, landed over the middle of the page.
The shim turns its partial view off for every profile
(download_bubble.partial_view_enabled), and cmux's Downloads button opens a
native popup under itself instead of an NSMenu. A finished row opens on a
click and drags out as its file (a terminal takes the path); a right-click
gives the row's actions. A finished download opens the popup without the keys
and closes it after 5 s unless the pointer goes in; a tab not on screen keeps
the notice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Footer buttons act on the first click while the popup has no keys; the
popup closes when its toolbar leaves the screen (tab switch or close); a
reopen stops the previous show's row observation; the row checks its file
only on a click or drag; a pointer already over the popup counts as inside.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… Finder on a hovered row

The footer's two titles sat side by side and wrapped onto two lines each.
Each command is now its own one-line row with an icon and the rows' hover
fill (Chrome's bubble footer), and a hovered finished row shows a folder
button that reveals the file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@austinywang austinywang added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 21, 2026
@austinywang

Copy link
Copy Markdown
Contributor Author

Correctness review (subagent, static, against 5634e0a), and what changed:

  1. Blocking: download.status == .failed doesn't compile (case failed(String)). Fixed in e700d94 (if case .failed).
  2. Footer buttons in the popup that opened by itself needed a first click to make the panel key. Fixed in 39f3fec: the content is a FirstMouseHostingView.
  3. The popup stayed up after its toolbar left the screen (tab switch, tab closed). Fixed in 39f3fec: BrowserToolbarButtonsView.onDetach (viewDidHide, or moved out of its window) closes it, and so does a resize when the anchor is gone.
  4. When Downloads is collapsed, opening it from More loses More's pressed look (present resets isMenuOpen after the menu). Not fixed: cosmetic, only in the collapsed path.
  5. Row-count observers piled up across reopens. Fixed in 39f3fec: a generation counter drops observers from earlier shows.
  6. A pointer already over the popup didn't count as inside; Escape doesn't close the popup that opened by itself. The first is fixed in 39f3fec (.assumeInside). The second is not fixed: that popup has no key focus, and a click outside closes it.
  7. A file system check ran in each row's body. Fixed in 39f3fec: the file is resolved on click or drag only.
  8. The global CEF context doesn't get the pref. Not fixed: the host always passes a profile cache path, so RequestContextFor("") is never reached.

Chromium 154 check: GetPartialView() returns {} while download_bubble.partial_view_enabled is false, and OnBubbleAnchorAssembled returns on empty models, so the bubble doesn't show. The security subpage (OpenSecuritySubpage) still opens on request, unchanged.

3a1c4c6 changes only the UI after dogfooding: footer commands are full-width one-line rows, and a hovered finished row gets a Show in Finder button. A review of that head comes before landing.

@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

cmux status (one comment, edited in place; each line is updated by the job that owns it)

Review 749711e: HQ feed card · gallery before/after runs · checks

App of this head (app.zip, kept 1 day)

gh workflow run reload-build.yml -R manaflow-ai/cmux --ref feat-cmux-next -f tag=pr23050 -f ref=feat-cmux-next-downloads-popover -f platform=macos -f backend_mode=local

Not verified: macOS jobs did not run: compile and app tests were skipped on 749711efce; ci-status is green without them.

CI passes on 749711efce (run 39558070691 attempt 1).
macOS jobs did not run: compile and app tests were skipped: Fast static checks skipped (the macos job needs both to succeed). Re-run CI once they pass, before merging.

Written by scripts/ci/classify_failures.py (ci-failure-attribution.yml); signatures are its SIGNATURES table. A machine verdict is the runner's fault, not this PR's; yours means the failing file is one this PR changes, also red on main that main's latest full suite fails the same way, seen on other PRs that it failed on another pull request's run lately.

@teamleaderleo

Copy link
Copy Markdown
Collaborator

Bead cx-rwy08 created for this PR by the cmux coordinator.

…art unhovered each show

The popup resizes when Clear Finished Downloads comes or goes, not only on a
row count change. Rows are rebuilt per show, so none keeps the last show's
hover. The folder button has a fixed slot (the name no longer shifts on
hover) and shows only while the file is still there. Icons are hidden from
VoiceOver.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@teamleaderleo

Copy link
Copy Markdown
Collaborator

cmux-next guards / defaults-snapshot is red on 24 of 24 fresh feat-cmux-next PR heads in the last 2:00:00, so it is most likely a base red, not this PR's. The base-reds lane has it; a base merge picks up the fix once it lands.

@austinywang

Copy link
Copy Markdown
Contributor Author

Correctness review of 3a1c4c6 (the footer and hover change), subagent, static. Nothing blocked compiling or clicking. What changed:

  1. The popup kept its old height when the Clear row appeared or went without a row-count change (a download finishing, or the only running one being cancelled, while the popup is open). Fixed in 3d0fa89: the resize observation also reads canClear.
  2. A row kept its hover, and its folder button, from the previous show, because the hosting view is reused. Fixed in 3d0fa89: rows rebuild per show (.id(model.shows)).
  3. The name shifted when the folder button appeared. Fixed in 3d0fa89: the button has a fixed 24 pt slot.
  4. VoiceOver: the row and footer icons were their own elements, and Show in Finder exists only on hover. The first is fixed (icons accessibilityHidden). For the second, Show in Finder stays reachable from the row's menu; no accessibility action added.
  5. The folder button showed for a file that had since been deleted. Fixed in 3d0fa89: it shows only while draggableFile finds the file.

…download runs

Chrome's ring: the running downloads' bytes over their sizes, from 12
o'clock clockwise, in whole percent steps, over a faint track; a download of
unknown size shows a short arc. No animation: the arc follows the bytes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@austinywang
austinywang merged commit 7d2ee97 into feat-cmux-next Oct 21, 2026
@austinywang
austinywang deleted the feat-cmux-next-downloads-popover branch October 21, 2026 06:58
@github-actions

Copy link
Copy Markdown
Contributor

The feat-cmux-next push run https://github.com/manaflow-ai/cmux/actions/runs/39558816819 at c7ce585 failed: cmux-next Release compile (Xcode 26).
Those jobs last passed at 233cc81. This pull request is one of 4 merged since: #23065, #23050, #23073, #22992.
If the failure is in your change, fix forward on feat-cmux-next. Pull requests run only the tiers their paths reach (docs/ci/cmux-next-tiers.md); label a batch PR full-ci to run them all before merging.

@github-actions

Copy link
Copy Markdown
Contributor

The feat-cmux-next push run https://github.com/manaflow-ai/cmux/actions/runs/39559424526 at 240de48 failed: cmux-next Release compile (Xcode 26).
Those jobs last passed at 233cc81. This pull request is one of 5 merged since: #23065, #23050, #23073, #22992, #23062.
If the failure is in your change, fix forward on feat-cmux-next. Pull requests run only the tiers their paths reach (docs/ci/cmux-next-tiers.md); label a batch PR full-ci to run them all before merging.

teamleaderleo added a commit that referenced this pull request Oct 21, 2026
… red from #23050) (#23121)

BrowserToolbarButtonsView (an NSStackView) calls super.viewDidHide() since #23050; the
unrecognized-selector rule had no probe for it. Probed on macOS 27.0 (cmux16) and
27.0.1 (cosmic-gray): NSStackView answers viewDidHide.


Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d18-9c97-76c3-88d7-6d0422d2fadc&host=cosmic-gray

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo added a commit that referenced this pull request Oct 21, 2026
…View's deinit (#23233)

#23050 added a generic NSHostingView subclass. Its isolated deinit crashes
swift-frontend in the Release compile (EarlyPerfInliner,
isCallerAndCalleeLayoutConstraintsCompatible, exit 65), the same crash #20408
fixed for other main-actor generic classes.


Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bc4-cecd-72b1-b539-62a19ed43671&host=cosmic-gray

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo added a commit that referenced this pull request Oct 21, 2026
…ough themed tooltips (NativeTooltipGuardTests red on base) (#23132)

* fix(next): downloads popup and calm row tooltips go through themed tooltips

NativeTooltipGuardTests is red on feat-cmux-next: the downloads popup
(#23050) uses raw SwiftUI .help for Show in Finder and the row's file
name, and the calm-mode workspace row (#22623) sets a raw toolTip for a
clipped name. Both now use the themed tooltip. The popup keeps the name
tooltip because the name truncates in the middle. The calm row sets its
tooltip in layout as well as on hover, since a themed tracker added
while the pointer is inside gets no mouseEntered.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d41-f5e8-7631-b4aa-a544e3ef96a8&host=cosmic-gray

* fix(next): Stashed page tooltips go through themedHelp

The Stashed page (#22098) landed with raw .help on a stashed tab's URL
and its delete button, a third NativeTooltipGuardTests offender on
feat-cmux-next.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d41-f5e8-7631-b4aa-a544e3ef96a8&host=cosmic-gray

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Shipped in:

teamleaderleo pushed a commit that referenced this pull request Oct 21, 2026
…o nonisolated deinit

In a uiSwiftSettings target a generic class's implicit deinit is isolated, and
the Release compile's optimizer crashes on it (EarlyPerfInliner, swift-frontend
6.3, macOS 14 target): #23050's FirstMouseHostingView broke every Release
compile until #23233, the same crash #20408 fixed before. The crash-class rule
now requires `nonisolated deinit` there (a reviewed `// concurrency-allow:`
escapes it). Non-UI targets stay out of scope.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-4ce2-7e02-8fa2-5cd34f7648b1&host=cosmic-gray
teamleaderleo added a commit that referenced this pull request Oct 21, 2026
…o nonisolated deinit (the #23050 Release crash) (#23361)

* ci(next): a crash-class test for a generic main-actor class with no nonisolated deinit (red)

#23050's FirstMouseHostingView, a generic NSHostingView subclass in a
uiSwiftSettings target, had an implicit isolated deinit that crashed the
Release compile's optimizer (fixed in #23233). This fixture test expects
check-concurrency.sh to refuse that shape; the rule comes next.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-4ce2-7e02-8fa2-5cd34f7648b1&host=cosmic-gray

* ci(next): check-concurrency refuses a generic main-actor class with no nonisolated deinit

In a uiSwiftSettings target a generic class's implicit deinit is isolated, and
the Release compile's optimizer crashes on it (EarlyPerfInliner, swift-frontend
6.3, macOS 14 target): #23050's FirstMouseHostingView broke every Release
compile until #23233, the same crash #20408 fixed before. The crash-class rule
now requires `nonisolated deinit` there (a reviewed `// concurrency-allow:`
escapes it). Non-UI targets stay out of scope.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-4ce2-7e02-8fa2-5cd34f7648b1&host=cosmic-gray

* Merge feat-cmux-next; one registry entry for test_crash_class_fast_gate; braces in strings don't end a class

Base registered tests/test_crash_class_fast_gate.py (#23352) after the
pre-commit hook had registered it on this branch, so keep base's entry only.
Review: declares_nonisolated_deinit counted a "}" in a string literal; it now
scans main_actor_scope.strip(line), and the header names the rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-4ce2-7e02-8fa2-5cd34f7648b1&host=cosmic-gray

* ci(next): the generic-class rule ignores braces in strings; the header names it

Review: declares_nonisolated_deinit counted a "}" in a string literal and ended
the class before its deinit. Scan main_actor_scope.strip(line), as the scope
scan does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba2-4ce2-7e02-8fa2-5cd34f7648b1&host=cosmic-gray

---------

Co-authored-by: Leo Li <leoli.4u@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants