Repository navigation
fix(sidebar): soften tab reorder animation - #17139
Conversation
…ecting row up with new workspace, hover on the ports action - while a machine connects, ports, terminals and displays have nothing current, so the tab row keeps only resources (fleet telemetry) - the connecting spinner takes new workspace's chevron column - the ports status action (refresh, set up vpn) is a quiet rounded chip with the tree's fills; its title brightens and fill deepens on hover and press
…text does nothing
the live cloud tab lists machines under the cloud machines section, and dropping one there is refused today. this fails until the fix.
…he section headers machine drags under the cloud machines section were refused because the drop only looked at top-level rows; drops and menu moves now find the machines wherever they sit. the real machine row is the drag visual: open machines close for the drag, peers spring aside against frozen geometry, and the drop lands every row from where it is on screen. mode tabs drag the same way and hand off to a native drag when pulled out of the bar, so they still open as panes. switching tabs slides one selection highlight, and an icon-only tab centers its icon. refresh moves from the bottom of the panel to the cloud machines and my devices headers.
…up pr keeps this pr to machine and tab reorder. the refresh icons in the cloud machines and my devices headers, the sliding tab highlight and the centered tab icons come back in the stacked cloud sidebar polish pr.
…light, centered tab icons refresh moves from the bottom of the cloud panel to an icon left of the cloud machines + and the my devices menu, with the same hover and a spinner that stays on screen while it runs. switching tabs slides one selection highlight while the widths re-share, and an icon-only tab centers its icon.
review findings on the lift: - closing open machines above the source moved its row up, and that shift counted as pointer travel, so a small nudge could move the machine several slots. only the pointer's travel picks the slot now; the held row glides from under the hand into its closed slot - a drag whose end is never reported cancels once the button has been up for a moment, instead of following the pointer until the next click - the release lays out before landing, so the rows glide instead of snapping - mode bar: a late gesture change after the release no longer starts a phantom drag, and tab frames are measured without the drag offset - tests: the lifted drop gets a proposal that disagrees with the lift, and a new test opens machines above the held one
…l tab names, smoother switching and resize, upgrade at the limit - ports tab: the status text and its button start on the ports tab's edge - an empty fleet keeps a "No cloud machines yet" line, so cloud machines keeps its chevron; my devices says "No other devices yet" - the open-as-pane button stays in the bar in every tab; a tab that can't open as a pane says so in its tooltip - the right sidebar's minimum (and opening) width follows the mode tabs shown, so every tab name fits with the trailing buttons - switching tabs moves one highlight with no crossfade, and row selection is a layer so it no longer flickers while the sidebar resizes - a failed create keeps its warning icon on the name's line - a create refused at the plan's machine limit offers Upgrade Plan when a bigger plan exists - the cloud machines refresh icon spins only for a refresh someone asked for
…cons; ports layout back as it was the minimum and opening width now leave room for one full tab name (the widest, so it doesn't change as the selection moves) with every other tab at its icon, instead of every name in full. the ports tab's status alignment goes back to main's.
…reate keeps its status readable the cloud machines and my devices refresh icons move from the header's trailing buttons to right after the title and count, small and in the count's grey. the header passes clicks through to the row everywhere but the icon, so clicking the header still opens and closes the section. in a narrow sidebar a failed create's name gives way before its status.
…er other devices" - the section headers' refresh icons take the same hover fill as + and ⋯; their resting color and size are unchanged - switching tabs no longer moves a highlight across the bar. the widths ease on one short curve with no overshoot, the old tab's fill fades as it narrows and the new one's as it opens, labels are uncovered by their slot (with a soft edge) instead of re-truncating every frame, and an icon-only tab's icon glides to its leading spot - my devices and settings say "Discover other devices" (and "Stop discovering other devices"), in all 9 locales
…olish brings the ports status fix (only its button acts) and its hover chip into this build. connectingLeading moves to the machine detail tabs extension with the helpers #17070 moved there.
the row drops the "New Machine" name once the create has failed and shows only the warning and "Couldn't create machine"; the name and the reason stay in its tooltip and on the create's page.
…toggling the section each reload of a section header cleared the icon's clickable spot, and SwiftUI reports the icon's frame only when it moves, so after the first reload the click fell through to the row and opened or closed the section. a header now keeps the spot across reloads; it clears only when the cell is reused for another kind of row.
takes #17070's review fixes and its main merge. conflicts: ports status keeps the chip title and hides an empty message; node actions keep both newWorkspaceOnResolvedMachine and the section refreshes; the lift card fill keeps the hover tint on the struct style; the panel keeps the header refresh with main's reveal; the project file is #17070's with this branch's files wired and the deleted refresh button removed.
both header icons use the panel's one refresh (fleet and devices) instead of two new single-section actions; each icon still spins only for its own section. shorter ports status comment and denser panel arguments.
…d-machine-reorder
|
|
) * cloud sidebar: workspaces reorder with the same lift as machines the real workspace row follows the pointer and its pin-tier siblings slide aside, open workspaces close for the drag and reopen after, and the drop commits the slot the rows show. leaving the tree puts the rows back and gives the drag its image again, so a workspace still drops onto a pane. * cloud sidebar: workspace lift review fixes a lifted drop skips the pane ownership check (it only reorders), the drag image is only blanked once the row lifts and comes back if the tree reloads mid-drag, and the snapshot keeps its size. * cloud sidebar: lift begin returns whether the row lifted * cloud sidebar: workspace lift finds rows by id and snapshots the row itself the lift matched rows by object identity, and the tree can hold different objects for the same workspace, so the drag never lifted. the hand-off picture now comes from the row view instead of the drag frame. debug logs say why a lift was skipped. * Animate Cloud machine detail tab switching * Fix tab strip animation composition * Fix cloud reorder drag origin after collapsing rows * Restore outline drag origin capture * Revert "Fix cloud reorder drag origin after collapsing rows" This reverts commit 59a8d91. * Keep dragged cloud folder open during lift * Test that a lifted Cloud row stays under the hand when rows close above it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Anchor the Cloud reorder lift on the held row Closing open rows for a machine or workspace drag moved the held row's slot up by the height that closed above it, and the lift glided the row into that slot, leaving it that far above the pointer. The bottom row drifted most. The lift now scrolls the list so the slot stays where the row stood, carries any remainder as one shift on every row, and anchors the reopen at the end on the landed row, so the row stays under the hand and a cancel restores the original scroll. The held row's own folder closes again so every peer is one row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Import CmuxSidebar in the right sidebar tab tests RightSidebarModeBarDragLayout moved into CmuxSidebar in #17070, so the cmuxTests target stopped compiling. Same line as PR #17139. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test that a lift keeps every row reachable and returns its scroll range Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Borrow scroll range for the lift instead of shifting every row A fixed shift on every row left rows at one end of a long list out of reach and showed blank bands while autoscrolling, since rows without views never draw. The container now lends the scroll view extra inset range for the drag, the real scroll does all of the anchoring, and the range goes back at the drop, cancel, or discard. Hover stays on the held row while begin lays out, and a list with nothing to scroll rests at its top after a drop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test that a lift restores the scroll view's own insets Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Hold the scroll view's insets still while a lift borrows range The Cloud tree's scroll view adjusts its insets automatically, so AppKit recomputed them on its next layout and dropped the borrowed range mid-drag. The container now lends on top of the insets in effect, turns automatic adjustment off for the loan, and restores both when the range comes back. Resting insets are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep throwing lookups out of #expect in the async lift test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Assert the held row's slot, not a timed glide, in the lift tests The determinism guard rejects sleep-then-assert. The drift the sleep waited out was the row gliding into a slot above the hand, so the tests now check that the slot itself stays where the hand pressed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Scroll the lift test fixture to a finite end Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Pin the hand to a window point in the lifted cancel test Rows closing above the held machine now scroll the outline so its slot stays under the hand, so an unmoved hand is a window point, not the press's document coordinate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Austin Wang <38676809+austinywang@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Bugbot is paused — on-demand spend limit reachedBugbot uses usage-based billing for this team and has hit its on-demand spend limit. A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue. |
Bugbot is paused — on-demand spend limit reachedBugbot uses usage-based billing for this team and has hit its on-demand spend limit. A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue. |
…17222) * cloud sidebar: workspaces reorder with the same lift as machines the real workspace row follows the pointer and its pin-tier siblings slide aside, open workspaces close for the drag and reopen after, and the drop commits the slot the rows show. leaving the tree puts the rows back and gives the drag its image again, so a workspace still drops onto a pane. * cloud sidebar: workspace lift review fixes a lifted drop skips the pane ownership check (it only reorders), the drag image is only blanked once the row lifts and comes back if the tree reloads mid-drag, and the snapshot keeps its size. * cloud sidebar: lift begin returns whether the row lifted * cloud sidebar: workspace lift finds rows by id and snapshots the row itself the lift matched rows by object identity, and the tree can hold different objects for the same workspace, so the drag never lifted. the hand-off picture now comes from the row view instead of the drag frame. debug logs say why a lift was skipped. * Animate Cloud machine detail tab switching * Fix tab strip animation composition * Fix cloud reorder drag origin after collapsing rows * Restore outline drag origin capture * Revert "Fix cloud reorder drag origin after collapsing rows" This reverts commit 59a8d91. * Keep dragged cloud folder open during lift * Test that a lifted Cloud row stays under the hand when rows close above it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Anchor the Cloud reorder lift on the held row Closing open rows for a machine or workspace drag moved the held row's slot up by the height that closed above it, and the lift glided the row into that slot, leaving it that far above the pointer. The bottom row drifted most. The lift now scrolls the list so the slot stays where the row stood, carries any remainder as one shift on every row, and anchors the reopen at the end on the landed row, so the row stays under the hand and a cancel restores the original scroll. The held row's own folder closes again so every peer is one row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Import CmuxSidebar in the right sidebar tab tests RightSidebarModeBarDragLayout moved into CmuxSidebar in #17070, so the cmuxTests target stopped compiling. Same line as PR #17139. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test that a lift keeps every row reachable and returns its scroll range Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Borrow scroll range for the lift instead of shifting every row A fixed shift on every row left rows at one end of a long list out of reach and showed blank bands while autoscrolling, since rows without views never draw. The container now lends the scroll view extra inset range for the drag, the real scroll does all of the anchoring, and the range goes back at the drop, cancel, or discard. Hover stays on the held row while begin lays out, and a list with nothing to scroll rests at its top after a drop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test that a lift restores the scroll view's own insets Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Hold the scroll view's insets still while a lift borrows range The Cloud tree's scroll view adjusts its insets automatically, so AppKit recomputed them on its next layout and dropped the borrowed range mid-drag. The container now lends on top of the insets in effect, turns automatic adjustment off for the loan, and restores both when the range comes back. Resting insets are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep throwing lookups out of #expect in the async lift test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Assert the held row's slot, not a timed glide, in the lift tests The determinism guard rejects sleep-then-assert. The drift the sleep waited out was the row gliding into a slot above the hand, so the tests now check that the slot itself stays where the hand pressed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Scroll the lift test fixture to a finite end Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Pin the hand to a window point in the lifted cancel test Rows closing above the held machine now scroll the outline so its slot stays under the hand, so an unmoved hand is a window point, not the press's document coordinate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * cloud sidebar: test that a lift keeps open machines open and the bottom row under the hand * cloud sidebar: keep open machines and workspaces open while dragging closing them for the drag pulled the held row away from the cursor near the bottom of the list. now each one moves with its rows. the closing path stays behind machineLiftClosesOpenRows for its tests. * Document shared Cloud plan loading strings * Clear Swift warning budget regressions * Return resolved Cloud workspace label * Keep one Cloud plan localization record * cloud sidebar: keep lift rows open * cloud: keep prominent cloud buttons visible in an inactive window, keep a known plan through failed or cancelled checks, recheck settings on account change * cloud: ignore stale plan refresh results * cloud: label lift collapse closures and test workspace lifts --------- Co-authored-by: Austin Wang <38676809+austinywang@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Bugbot is paused — on-demand spend limit reachedBugbot uses usage-based billing for this team and has hit its on-demand spend limit. A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue. |
Bugbot is paused — on-demand spend limit reachedBugbot uses usage-based billing for this team and has hit its on-demand spend limit. A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue. |
|
Merge receipt for
Labeled |
15f2c03 Open New Machine immediately with cached plan data (manaflow-ai#17345) 2995bcc Wait for plan preload before showing new machine error (manaflow-ai#17329) 8510b8d fix(sidebar): soften tab reorder animation (manaflow-ai#17139)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(cloud): display health checks must not black-list websockify viewers Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): stop Xvnc black-listing every viewer of additional displays Xvnc black-lists a peer after five unauthenticated connections, for a timeout that doubles each time. Every viewer of an additional display is websockify on 127.0.0.1, and the helper's 30s health check read the RFB greeting and hung up, so displays soon answered every connection with "Too many security failures". Restored panes and quiet retries then failed for minutes. Start additional displays with -UseBlacklist=0 (SecurityTypes None behind the private network gives it nothing to protect; Xvnc refuses runtime changes), complete the None handshake in the health check so it clears marks instead of adding them, and replace recovery's pgrep patterns, which pgrep's regex dialect rejected, with exact argv matching. Each helper request also runs in a plain shell now: the login shell cost about 0.7s per call and only the long-lived service needs its environment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): New Display hands over a running standby display Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * perf(cloud): keep one standby display running per desktop VM Each New Display started Xvnc, a desktop session and websockify on the guest (about 0.7s) while the user waited. The guest helper now keeps one display running outside the catalog, hands it over on create (about 0.1s) and starts the next in the background. The standby is invisible to list, does not count against capacity, and a restarted helper adopts it instead of leaking it. Opening a machine's Displays tab, or restoring it selected, runs guest discovery once so the standby is warm before the first click. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): failed or launching standby displays are never misassigned Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): serialize standby handover; retire failed standbys; rediscover after wake Concurrent creates could record a launching standby's number as a normal display; take the standby under one lock and reserve running numbers. A standby whose start failed is stopped and replaced instead of handed over or retried forever. Displays-tab discovery runs once per open and retries when a sleeping machine wakes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): align two sidebar tests with merged behavior A connecting machine keeps its Terminals tab (#17326), and every visible machine row requests a cached port scan once (#17074). Both tests still asserted the earlier behavior and failed on main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): standby never takes a recorded number; failed discovery retries Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): address review: standby race and Displays discovery retry ensure_standby's locked check now also rejects a number a concurrent create recorded after the unlocked probe, so a display can never be both a catalog display and the standby. Displays-tab discovery reports completion; a failed discovery clears the request and retries, bounded to three attempts per shown machine, instead of treating task launch as done. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): connecting machines keep only the Resources tab (#17139) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): a stale Displays discovery cannot retry after the tab reopens Drive discovery completions explicitly instead of yielding a fixed number of times. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): bind each Displays discovery completion to its request Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…#17327) * test(cloud): display health checks must not black-list websockify viewers Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): stop Xvnc black-listing every viewer of additional displays Xvnc black-lists a peer after five unauthenticated connections, for a timeout that doubles each time. Every viewer of an additional display is websockify on 127.0.0.1, and the helper's 30s health check read the RFB greeting and hung up, so displays soon answered every connection with "Too many security failures". Restored panes and quiet retries then failed for minutes. Start additional displays with -UseBlacklist=0 (SecurityTypes None behind the private network gives it nothing to protect; Xvnc refuses runtime changes), complete the None handshake in the health check so it clears marks instead of adding them, and replace recovery's pgrep patterns, which pgrep's regex dialect rejected, with exact argv matching. Each helper request also runs in a plain shell now: the login shell cost about 0.7s per call and only the long-lived service needs its environment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): New Display hands over a running standby display Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * perf(cloud): keep one standby display running per desktop VM Each New Display started Xvnc, a desktop session and websockify on the guest (about 0.7s) while the user waited. The guest helper now keeps one display running outside the catalog, hands it over on create (about 0.1s) and starts the next in the background. The standby is invisible to list, does not count against capacity, and a restarted helper adopts it instead of leaking it. Opening a machine's Displays tab, or restoring it selected, runs guest discovery once so the standby is warm before the first click. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): failed or launching standby displays are never misassigned Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): serialize standby handover; retire failed standbys; rediscover after wake Concurrent creates could record a launching standby's number as a normal display; take the standby under one lock and reserve running numbers. A standby whose start failed is stopped and replaced instead of handed over or retried forever. Displays-tab discovery runs once per open and retries when a sleeping machine wakes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): a closed display view is not rebuilt from a stale graph Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): load every workspace display and keep closed displays closed Displays beyond the first in a Cloud workspace layout appeared late: the catalog only learns display:N from guest discovery and drops memberships for displays it does not know, and nothing on open or restore ran discovery. Publishing a graph whose memberships name unknown displays now runs discovery once per lifecycle generation. Closed display panes came back: the membership removal is queued while reconciliation still reads the graph holding the token, so it rebuilt the pane under a new panel id and left an orphaned token that resurrected it after every close. Closing now fences the view until a fetched graph drops its token, retries a removal that has not landed, and a pane rebuilt from an orphaned token removes that token. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): discover member displays on deltas; bound and scope token cleanup Membership rows arrive as projection deltas, so discovery also runs from publishDelta. Orphan cleanup skips other Macs' tokens (a no-op write that still cost a guest snapshot), removal retries stop after three attempts, and the client id is read once per close. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): prune this Mac's orphaned display memberships in open workspaces A membership token whose pane is gone (an old close whose removal never landed, a crash, a pane rebuilt under a new id) showed as a duplicate display row and resurrected the display after it was closed; the reconciler treats one live pane as satisfying every token for that display, so nothing removed it. After reconciling an open Cloud workspace, remove this Mac's tokens with no live display pane, once the machine's restore has settled. Tokens of workspaces not open here, and of other Macs, are kept as their layout. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): keep display discovery results across machine-list polls refreshDisplays dropped its result whenever refreshGeneration changed, and every machine-list poll bumps it, so a ~2s guest discovery that overlapped a poll was discarded: workspace displays loaded only after a later lucky discovery. Check the lifecycle generation only; the coordinator already invalidates on identity or image changes. Member-display discovery also re-checks after each attempt (bounded to three per lifecycle) because a launch-time refresh can cancel one, verified live: the first attempt's exec was cancelled after 1ms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): member display listed once; silent desktop probe is not unreachable Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): list a workspace display once; stop repairing healthy desktops The sidebar listed a workspace's display twice: cloudWorkspaceResources appends a copy of a member display carrying its membership view, and localWorkspaceMembers deduped against the first copy (the pool resource, without the view), so the local pane added the display again. Display 1 sat on "Loading Cloud page" for 20s+: the desktop probe's 2s deadline never cancelled its connection, so a busy carrier held it to the proxy's 10s header timeout, and the timeout read as unreachable and ran the control plane's desktop repair (a guest exec, ~12s) on a healthy desktop. The deadline now cancels the connection, and repair runs only when the proxy or service answered with an error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): a New Display pane and its membership are one sidebar row Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): build workspace rows and local-pane rows from one resource list The sidebar built workspace rows from cloudWorkspaceResources (which holds the membership copy of a member display) but deduped local panes against the pool resources alone, so a New Display pane listed its display twice. Both passes now read the same list. Adds debug.cloudtree.rows (DEBUG only), returning the Cloud sidebar's rows from the same builder, so dogfood can assert listed rows; duplicate rows were invisible to cloud tree --json. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): display names parse from the daemon graph Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(cloud): named displays, display tab titles, remove a display from a workspace Display panes showed noVNC's page title, and renaming a workspace display sent a synthetic view id to the daemon's tab rename, which cannot work. Displays now have one shared name per display, stored as a frontend projection beside the workspace memberships so every client follows a rename live. Rows and pane tabs show it ("Display N" until renamed); renaming the tab or the row renames the display; clearing restores the number. Workspace display rows get a hover X and a "Remove from Workspace" menu item, which close the display's pane on this Mac (the fenced membership removal) or remove this Mac's membership when no pane is open. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(ci): list debug.cloudtree.rows as a debug method; fold display names into the membership type The socket capability guard requires debug methods to be listed as intentionally unadvertised, and the package conventions lint rejects an all-static enum, so the display-name constants move onto CloudVMDisplayMembership. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): a display row's Rename names the display The menu now routes display renames to renameDisplay (one name per display, shared by every row and pane) instead of a view's tab rename. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): align two sidebar tests with merged behavior A connecting machine keeps its Terminals tab (#17326), and every visible machine row requests a cached port scan once (#17074). Both tests still asserted the earlier behavior and failed on main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): the desktop placeholder is titled Display 1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): title display panes from the click, never Desktop first The display 1 placeholder (shown before guest discovery) was titled "Desktop" and renamed "Display 1" seconds later; it now uses the same numbered title as discovery. A new display's pane is titled "Starting display…" from the click instead of "New tab", then takes the display's name when it materializes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): standby never takes a recorded number; failed discovery retries Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): address review: standby race and Displays discovery retry ensure_standby's locked check now also rejects a number a concurrent create recorded after the unlocked probe, so a display can never be both a catalog display and the standby. Displays-tab discovery reports completion; a failed discovery clears the request and retries, bounded to three attempts per shown machine, instead of treating task launch as done. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): connecting machines keep only the Resources tab (#17139) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): a stale Displays discovery cannot retry after the tab reopens Drive discovery completions explicitly instead of yielding a fixed number of times. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): bind each Displays discovery completion to its request Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): closing a display pane removes it for every client Drives the real pane-close path. Another client's token in the same workspace must not rebuild the pane, including when the close lands before this pane's own token reaches the graph. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): closed displays stay closed across clients and instances - Closing a display pane or clicking X removes the display from that Cloud workspace for every client. The fence is keyed by machine, workspace and display, set at close from the pane's own workspace, and lifted when this Mac opens the display there again. - Pruning only touches view IDs this process recorded: stable and DEV builds on one Mac share a client ID. - A sleeping machine or an undiscovered display does not spend a removal attempt. - Tab renames run in order, and afterwards every pane shows the display's real name, so a cleared or failed rename reverts the tab. - A failed reserved pane drops its "Starting display…" title. - A reset or refused connection after the tunnel opens reads as unreachable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): a desktop that resets the opened tunnel is unreachable Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): read a reset after the desktop tunnel opens as unreachable The probe's errors arrive as NWConnection.StreamError, so the previous NWError cast never matched. Only the HEAD stage maps a reset or refusal; before the tunnel opens the same error belongs to the local proxy and stays unknown. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): a queued close cannot undo a reopen; another client's re-add shows again Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): order display removals against reopens; release on another client's re-add - A reopen bumps the display's generation, so a close or removal retry that started before it leaves the reopened display alone. - Removal retries run on the machine's lane, ordered with the reopen's attach; the lane's enqueue is split into a resource-keyed core. - A removal records the tokens it deleted. A later graph showing any other token means another client put the display back, so the fence lifts. - Attempts are counted per closed display and only while it is fenced. - A tab rename re-applies the display's name only when it changed nothing or failed, so a successful rename no longer flashes the old name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): type the lane failure handler explicitly The ternary of closures crashed the type checker (failed to produce diagnostic). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cloud): a no-op display removal still fences older graphs; proxy reset is unknown Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): release a closed display by graph revision, not token sets A removal now reports the cursor of the graph it was computed from. A graph no newer than that predates the removal and stays fenced, including when the removal found nothing to delete. A newer graph that still shows the display means another client put it back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(cloud): fence a closed display up to its removal write's own cursor A graph published between the removal's snapshot read and its write could be newer than the snapshot yet still hold the removed tokens, releasing the fence early. The removal now reports the write receipt's cursor (the snapshot's only for a no-op), and a reply without one keeps the fence until the display is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This follow-up carries the sidebar polish and drag behavior fixes for #17074.
Changes:
Validation:
git diff --check; Swift syntax check for changed Swift sources. The exact-head dev build is still being rebuilt after the drag handoff fix.Summary by CodeRabbit