detached a353d55

fix(palette): close remote handoff overlay

commit
a353d5543508d8cdf4f2d06bbbaf88624c1c51c7
author
brice · · 2026-08-25 10:58 UTC
parent
fb6668c
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
3 files · +12 -2
M internal/usecase/daemon/palette.go +6 -1
@@ -438,7 +438,12 @@ func (d *Daemon) handlePaletteInput(ac *attachedClient, data []byte, effects ...
438438 RemoteKey: &remoteKey, RemoteTarget: &remoteTarget, RemoteHost: remoteKey.Host,
439439 UnavailableReason: remoteUnavailableReason, TabIndex: -1,
440440 }
441- if err := d.switchToTargetForAttachment(effect.connectionToken(), target, sessionHandoffGuard{}, "palette-remote-session"); err != nil && !errors.Is(err, errAttachmentTransition) {
441+ err := d.switchToTargetForAttachment(effect.connectionToken(), target, sessionHandoffGuard{}, "palette-remote-session")
442+ if err == nil {
443+ d.closeExecutedPalette(ac, generation, rawQuery)
444+ return
445+ }
446+ if !errors.Is(err, errAttachmentTransition) {
442447 ac.paletteFailure(generation, rawQuery, "requested remote session is unavailable")
443448 d.invalidateRender(entry, ac, true, "palette.go")
444449 }
@@ -438,7 +438,12 @@ func (d *Daemon) handlePaletteInput(ac *attachedClient, data []byte, effects ...
438 RemoteKey: &remoteKey, RemoteTarget: &remoteTarget, RemoteHost: remoteKey.Host,438 RemoteKey: &remoteKey, RemoteTarget: &remoteTarget, RemoteHost: remoteKey.Host,
439 UnavailableReason: remoteUnavailableReason, TabIndex: -1,439 UnavailableReason: remoteUnavailableReason, TabIndex: -1,
440 }440 }
441- if err := d.switchToTargetForAttachment(effect.connectionToken(), target, sessionHandoffGuard{}, "palette-remote-session"); err != nil && !errors.Is(err, errAttachmentTransition) {441+ err := d.switchToTargetForAttachment(effect.connectionToken(), target, sessionHandoffGuard{}, "palette-remote-session")
442+ if err == nil {
443+ d.closeExecutedPalette(ac, generation, rawQuery)
444+ return
445+ }
446+ if !errors.Is(err, errAttachmentTransition) {
442 ac.paletteFailure(generation, rawQuery, "requested remote session is unavailable")447 ac.paletteFailure(generation, rawQuery, "requested remote session is unavailable")
443 d.invalidateRender(entry, ac, true, "palette.go")448 d.invalidateRender(entry, ac, true, "palette.go")
444 }449 }
M internal/usecase/daemon/palette_test.go +5 -0
@@ -500,6 +500,7 @@ func TestPaletteRemoteCatalogSelectionSendsExactAttachTarget(t *testing.T) {
500500 p, release := newBlockingPTY(t)
501501 defer release()
502502 d, current, ac, sends := newManualSessionWithPTYs(t, p)
503+ ac.resumeCapable = true
503504 now := time.Unix(1_000, 0)
504505 d.clock = fixedRemoteRefreshClock{now: now}
505506 remoteLifecycle := domain.SessionLifecycleID{31}
@@ -530,6 +531,10 @@ func TestPaletteRemoteCatalogSelectionSendsExactAttachTarget(t *testing.T) {
530531 Endpoint: "user@arch", Session: "work", Intent: ports.IntentAttach,
531532 RemoteTarget: &remoteTarget, EnvironmentPolicy: ports.EnvironmentPolicyDaemonOwned,
532533 }, target)
534+ require.False(t, ac.overlays.paletteActive())
535+ d.remoteCatalog.mu.Lock()
536+ require.Zero(t, d.remoteCatalog.consumers[ac]&remoteDiscoveryPalette)
537+ d.remoteCatalog.mu.Unlock()
533538 }
534539
535540 func TestPaletteCachedRemoteSelectionFailsClosed(t *testing.T) {
@@ -500,6 +500,7 @@ func TestPaletteRemoteCatalogSelectionSendsExactAttachTarget(t *testing.T) {
500 p, release := newBlockingPTY(t)500 p, release := newBlockingPTY(t)
501 defer release()501 defer release()
502 d, current, ac, sends := newManualSessionWithPTYs(t, p)502 d, current, ac, sends := newManualSessionWithPTYs(t, p)
503+ ac.resumeCapable = true
503 now := time.Unix(1_000, 0)504 now := time.Unix(1_000, 0)
504 d.clock = fixedRemoteRefreshClock{now: now}505 d.clock = fixedRemoteRefreshClock{now: now}
505 remoteLifecycle := domain.SessionLifecycleID{31}506 remoteLifecycle := domain.SessionLifecycleID{31}
@@ -530,6 +531,10 @@ func TestPaletteRemoteCatalogSelectionSendsExactAttachTarget(t *testing.T) {
530 Endpoint: "user@arch", Session: "work", Intent: ports.IntentAttach,531 Endpoint: "user@arch", Session: "work", Intent: ports.IntentAttach,
531 RemoteTarget: &remoteTarget, EnvironmentPolicy: ports.EnvironmentPolicyDaemonOwned,532 RemoteTarget: &remoteTarget, EnvironmentPolicy: ports.EnvironmentPolicyDaemonOwned,
532 }, target)533 }, target)
534+ require.False(t, ac.overlays.paletteActive())
535+ d.remoteCatalog.mu.Lock()
536+ require.Zero(t, d.remoteCatalog.consumers[ac]&remoteDiscoveryPalette)
537+ d.remoteCatalog.mu.Unlock()
533 }538 }
534 539
535 func TestPaletteCachedRemoteSelectionFailsClosed(t *testing.T) {540 func TestPaletteCachedRemoteSelectionFailsClosed(t *testing.T) {
M internal/usecase/palette/result.go +1 -1
@@ -20,7 +20,7 @@ const (
2020 )
2121
2222 // Result is an immutable palette target. Its kind is the sole discriminator
23-// for its private command or session payload.
23+// for its private command, local-session, remote-session, or route payload.
2424 type Result struct {
2525 kind ResultKind
2626 command command.Command
@@ -20,7 +20,7 @@ const (
20 )20 )
21 21
22 // Result is an immutable palette target. Its kind is the sole discriminator22 // Result is an immutable palette target. Its kind is the sole discriminator
23-// for its private command or session payload.23+// for its private command, local-session, remote-session, or route payload.
24 type Result struct {24 type Result struct {
25 kind ResultKind25 kind ResultKind
26 command command.Command26 command command.Command

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 12

Tags 11

↑↓ moveenter openesc close