fix(remote): preserve route validation and picker effects
- commit
- 87c233886dde4d50e0aba94c3f902bc3b9896fda
- author
- brice · · 2026-08-08 17:48 UTC
- parent
- 310aed6
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
7 files · +33 -16
M internal/app/run.go +8 -8
| @@ -889,6 +889,12 @@ func runAttachWithDeps(ctx context.Context, intent uint8, name, remoteTarget, ac | |||
| 889 | 889 | seenHandoffs[attachHandoffKey{endpoint: remoteTarget, session: name, intent: intent}] = struct{}{} | |
| 890 | 890 | } | |
| 891 | 891 | pickerHandoff := remoteTarget == "" | |
| 892 | + | pickerEnvironmentPolicy := func(target *domain.RemoteSessionTarget, policy ports.EnvironmentPolicy) ports.EnvironmentPolicy { | |
| 893 | + | if pickerHandoff && target == nil { | |
| 894 | + | return ports.EnvironmentPolicyDaemonOwned | |
| 895 | + | } | |
| 896 | + | return policy | |
| 897 | + | } | |
| 892 | 898 | handoff := func(target ports.AttachTarget) (ports.Dialer, client.AttachRequest, error) { | |
| 893 | 899 | if err := validateRemoteAttachHandoff(target); err != nil { | |
| 894 | 900 | return nil, client.AttachRequest{}, fmt.Errorf("vev: invalid remote attach handoff: %w", err) | |
| @@ -915,10 +921,7 @@ func runAttachWithDeps(ctx context.Context, intent uint8, name, remoteTarget, ac | |||
| 915 | 921 | if err != nil { | |
| 916 | 922 | return nil, client.AttachRequest{}, err | |
| 917 | 923 | } | |
| 918 | - | policy := target.EnvironmentPolicy | |
| 919 | - | if pickerHandoff && target.RemoteTarget == nil { | |
| 920 | - | policy = ports.EnvironmentPolicyDaemonOwned | |
| 921 | - | } | |
| 924 | + | policy := pickerEnvironmentPolicy(selection, target.EnvironmentPolicy) | |
| 922 | 925 | return dialer, client.AttachRequest{Intent: target.Intent, SessionName: target.Session, Remote: true, RemoteTarget: selection, EnvironmentPolicy: policy}, nil | |
| 923 | 926 | } | |
| 924 | 927 | ||
| @@ -987,14 +990,11 @@ func runAttachWithDeps(ctx context.Context, intent uint8, name, remoteTarget, ac | |||
| 987 | 990 | name = handoffErr.Target.Session | |
| 988 | 991 | intent = handoffErr.Target.Intent | |
| 989 | 992 | remoteSelection = nil | |
| 990 | - | remoteEnvironmentPolicy = handoffErr.Target.EnvironmentPolicy | |
| 991 | - | if pickerHandoff && remoteEnvironmentPolicy == ports.EnvironmentPolicyClientOwned { | |
| 992 | - | remoteEnvironmentPolicy = ports.EnvironmentPolicyDaemonOwned | |
| 993 | - | } | |
| 994 | 993 | if handoffErr.Target.RemoteTarget != nil { | |
| 995 | 994 | copyTarget := *handoffErr.Target.RemoteTarget | |
| 996 | 995 | remoteSelection = ©Target | |
| 997 | 996 | } | |
| 997 | + | remoteEnvironmentPolicy = pickerEnvironmentPolicy(remoteSelection, handoffErr.Target.EnvironmentPolicy) | |
| 998 | 998 | if modeErr != nil { | |
| 999 | 999 | return modeErr | |
| 1000 | 1000 | } | |
| @@ -889,6 +889,12 @@ func runAttachWithDeps(ctx context.Context, intent uint8, name, remoteTarget, ac | |||||
| 889 | seenHandoffs[attachHandoffKey{endpoint: remoteTarget, session: name, intent: intent}] = struct{}{} | 889 | seenHandoffs[attachHandoffKey{endpoint: remoteTarget, session: name, intent: intent}] = struct{}{} | ||
| 890 | } | 890 | } | ||
| 891 | pickerHandoff := remoteTarget == "" | 891 | pickerHandoff := remoteTarget == "" | ||
| 892 | + | pickerEnvironmentPolicy := func(target *domain.RemoteSessionTarget, policy ports.EnvironmentPolicy) ports.EnvironmentPolicy { | |||
| 893 | + | if pickerHandoff && target == nil { | |||
| 894 | + | return ports.EnvironmentPolicyDaemonOwned | |||
| 895 | + | } | |||
| 896 | + | return policy | |||
| 897 | + | } | |||
| 892 | handoff := func(target ports.AttachTarget) (ports.Dialer, client.AttachRequest, error) { | 898 | handoff := func(target ports.AttachTarget) (ports.Dialer, client.AttachRequest, error) { | ||
| 893 | if err := validateRemoteAttachHandoff(target); err != nil { | 899 | if err := validateRemoteAttachHandoff(target); err != nil { | ||
| 894 | return nil, client.AttachRequest{}, fmt.Errorf("vev: invalid remote attach handoff: %w", err) | 900 | return nil, client.AttachRequest{}, fmt.Errorf("vev: invalid remote attach handoff: %w", err) | ||
| @@ -915,10 +921,7 @@ func runAttachWithDeps(ctx context.Context, intent uint8, name, remoteTarget, ac | |||||
| 915 | if err != nil { | 921 | if err != nil { | ||
| 916 | return nil, client.AttachRequest{}, err | 922 | return nil, client.AttachRequest{}, err | ||
| 917 | } | 923 | } | ||
| 918 | - | policy := target.EnvironmentPolicy | 924 | + | policy := pickerEnvironmentPolicy(selection, target.EnvironmentPolicy) |
| 919 | - | if pickerHandoff && target.RemoteTarget == nil { | |||
| 920 | - | policy = ports.EnvironmentPolicyDaemonOwned | |||
| 921 | - | } | |||
| 922 | return dialer, client.AttachRequest{Intent: target.Intent, SessionName: target.Session, Remote: true, RemoteTarget: selection, EnvironmentPolicy: policy}, nil | 925 | return dialer, client.AttachRequest{Intent: target.Intent, SessionName: target.Session, Remote: true, RemoteTarget: selection, EnvironmentPolicy: policy}, nil | ||
| 923 | } | 926 | } | ||
| 924 | 927 | ||||
| @@ -987,14 +990,11 @@ func runAttachWithDeps(ctx context.Context, intent uint8, name, remoteTarget, ac | |||||
| 987 | name = handoffErr.Target.Session | 990 | name = handoffErr.Target.Session | ||
| 988 | intent = handoffErr.Target.Intent | 991 | intent = handoffErr.Target.Intent | ||
| 989 | remoteSelection = nil | 992 | remoteSelection = nil | ||
| 990 | - | remoteEnvironmentPolicy = handoffErr.Target.EnvironmentPolicy | |||
| 991 | - | if pickerHandoff && remoteEnvironmentPolicy == ports.EnvironmentPolicyClientOwned { | |||
| 992 | - | remoteEnvironmentPolicy = ports.EnvironmentPolicyDaemonOwned | |||
| 993 | - | } | |||
| 994 | if handoffErr.Target.RemoteTarget != nil { | 993 | if handoffErr.Target.RemoteTarget != nil { | ||
| 995 | copyTarget := *handoffErr.Target.RemoteTarget | 994 | copyTarget := *handoffErr.Target.RemoteTarget | ||
| 996 | remoteSelection = ©Target | 995 | remoteSelection = ©Target | ||
| 997 | } | 996 | } | ||
| 997 | + | remoteEnvironmentPolicy = pickerEnvironmentPolicy(remoteSelection, handoffErr.Target.EnvironmentPolicy) | |||
| 998 | if modeErr != nil { | 998 | if modeErr != nil { | ||
| 999 | return modeErr | 999 | return modeErr | ||
| 1000 | } | 1000 | } | ||
M internal/ports/navigation_test.go +1 -0
| @@ -34,6 +34,7 @@ func TestNavigationActionWireTable(t *testing.T) { | |||
| 34 | 34 | decoded, err := UnmarshalNavigationAction(got) | |
| 35 | 35 | require.NoError(t, err) | |
| 36 | 36 | require.Equal(t, tt.action, decoded) | |
| 37 | + | assertAllPrefixesFail(t, got, UnmarshalNavigationAction) | |
| 37 | 38 | _, err = UnmarshalNavigationAction(append(append([]byte(nil), got...), 0)) | |
| 38 | 39 | require.ErrorIs(t, err, ErrInvalidNavigation) | |
| 39 | 40 | }) | |
| @@ -34,6 +34,7 @@ func TestNavigationActionWireTable(t *testing.T) { | |||||
| 34 | decoded, err := UnmarshalNavigationAction(got) | 34 | decoded, err := UnmarshalNavigationAction(got) | ||
| 35 | require.NoError(t, err) | 35 | require.NoError(t, err) | ||
| 36 | require.Equal(t, tt.action, decoded) | 36 | require.Equal(t, tt.action, decoded) | ||
| 37 | + | assertAllPrefixesFail(t, got, UnmarshalNavigationAction) | |||
| 37 | _, err = UnmarshalNavigationAction(append(append([]byte(nil), got...), 0)) | 38 | _, err = UnmarshalNavigationAction(append(append([]byte(nil), got...), 0)) | ||
| 38 | require.ErrorIs(t, err, ErrInvalidNavigation) | 39 | require.ErrorIs(t, err, ErrInvalidNavigation) | ||
| 39 | }) | 40 | }) | ||
M internal/usecase/client/client.go +7 -4
| @@ -387,6 +387,9 @@ func (r *Runner) Run(ctx context.Context, request AttachRequest) (retErr error) | |||
| 387 | 387 | } | |
| 388 | 388 | ||
| 389 | 389 | for { | |
| 390 | + | if err := validateAttachRequest(attemptRequest); err != nil { | |
| 391 | + | return err | |
| 392 | + | } | |
| 390 | 393 | handshakeCtx, timedOut, finishHandshake := newHandshakeContext(ctx, r.clock) | |
| 391 | 394 | transport, err := boundedDial(handshakeCtx, dialer) | |
| 392 | 395 | if err != nil { | |
| @@ -476,13 +479,13 @@ func (r *Runner) Run(ctx context.Context, request AttachRequest) (retErr error) | |||
| 476 | 479 | homeNavigationPending = true | |
| 477 | 480 | dialer = homeRoute.dialer | |
| 478 | 481 | attemptRequest = homeRoute.request | |
| 479 | - | attemptRequest.Remote = false | |
| 482 | + | attemptRequest.Remote = homeRoute.request.Remote || r.remote | |
| 480 | 483 | attemptRequest.Intent = ports.IntentAttach | |
| 481 | 484 | attemptRequest.NavigationCapabilities = ports.NavigationCapabilityBack | |
| 482 | 485 | attemptRequest.StartupOverlay = ports.StartupOverlaySessionPicker | |
| 483 | 486 | attemptRequest.RemoteTarget = nil | |
| 484 | 487 | attemptRequest.EnvironmentPolicy = ports.EnvironmentPolicyClientOwned | |
| 485 | - | remote = syncReconnectRemote(reconnect, false) | |
| 488 | + | remote = syncReconnectRemote(reconnect, homeRoute.request.Remote || r.remote) | |
| 486 | 489 | resumeToken = 0 | |
| 487 | 490 | backoff = defaultReconnectBackoff.initial | |
| 488 | 491 | continue | |
| @@ -599,9 +602,9 @@ func (r *Runner) Run(ctx context.Context, request AttachRequest) (retErr error) | |||
| 599 | 602 | attemptRequest.StartupOverlay = ports.StartupOverlaySessionPicker | |
| 600 | 603 | attemptRequest.RemoteTarget = nil | |
| 601 | 604 | attemptRequest.EnvironmentPolicy = ports.EnvironmentPolicyClientOwned | |
| 602 | - | attemptRequest.Remote = false | |
| 605 | + | attemptRequest.Remote = homeRoute.request.Remote || r.remote | |
| 603 | 606 | resumeToken = 0 | |
| 604 | - | remote = syncReconnectRemote(reconnect, false) | |
| 607 | + | remote = syncReconnectRemote(reconnect, homeRoute.request.Remote || r.remote) | |
| 605 | 608 | backoff = defaultReconnectBackoff.initial | |
| 606 | 609 | continue | |
| 607 | 610 | } | |
| @@ -387,6 +387,9 @@ func (r *Runner) Run(ctx context.Context, request AttachRequest) (retErr error) | |||||
| 387 | } | 387 | } | ||
| 388 | 388 | ||||
| 389 | for { | 389 | for { | ||
| 390 | + | if err := validateAttachRequest(attemptRequest); err != nil { | |||
| 391 | + | return err | |||
| 392 | + | } | |||
| 390 | handshakeCtx, timedOut, finishHandshake := newHandshakeContext(ctx, r.clock) | 393 | handshakeCtx, timedOut, finishHandshake := newHandshakeContext(ctx, r.clock) | ||
| 391 | transport, err := boundedDial(handshakeCtx, dialer) | 394 | transport, err := boundedDial(handshakeCtx, dialer) | ||
| 392 | if err != nil { | 395 | if err != nil { | ||
| @@ -476,13 +479,13 @@ func (r *Runner) Run(ctx context.Context, request AttachRequest) (retErr error) | |||||
| 476 | homeNavigationPending = true | 479 | homeNavigationPending = true | ||
| 477 | dialer = homeRoute.dialer | 480 | dialer = homeRoute.dialer | ||
| 478 | attemptRequest = homeRoute.request | 481 | attemptRequest = homeRoute.request | ||
| 479 | - | attemptRequest.Remote = false | 482 | + | attemptRequest.Remote = homeRoute.request.Remote || r.remote |
| 480 | attemptRequest.Intent = ports.IntentAttach | 483 | attemptRequest.Intent = ports.IntentAttach | ||
| 481 | attemptRequest.NavigationCapabilities = ports.NavigationCapabilityBack | 484 | attemptRequest.NavigationCapabilities = ports.NavigationCapabilityBack | ||
| 482 | attemptRequest.StartupOverlay = ports.StartupOverlaySessionPicker | 485 | attemptRequest.StartupOverlay = ports.StartupOverlaySessionPicker | ||
| 483 | attemptRequest.RemoteTarget = nil | 486 | attemptRequest.RemoteTarget = nil | ||
| 484 | attemptRequest.EnvironmentPolicy = ports.EnvironmentPolicyClientOwned | 487 | attemptRequest.EnvironmentPolicy = ports.EnvironmentPolicyClientOwned | ||
| 485 | - | remote = syncReconnectRemote(reconnect, false) | 488 | + | remote = syncReconnectRemote(reconnect, homeRoute.request.Remote || r.remote) |
| 486 | resumeToken = 0 | 489 | resumeToken = 0 | ||
| 487 | backoff = defaultReconnectBackoff.initial | 490 | backoff = defaultReconnectBackoff.initial | ||
| 488 | continue | 491 | continue | ||
| @@ -599,9 +602,9 @@ func (r *Runner) Run(ctx context.Context, request AttachRequest) (retErr error) | |||||
| 599 | attemptRequest.StartupOverlay = ports.StartupOverlaySessionPicker | 602 | attemptRequest.StartupOverlay = ports.StartupOverlaySessionPicker | ||
| 600 | attemptRequest.RemoteTarget = nil | 603 | attemptRequest.RemoteTarget = nil | ||
| 601 | attemptRequest.EnvironmentPolicy = ports.EnvironmentPolicyClientOwned | 604 | attemptRequest.EnvironmentPolicy = ports.EnvironmentPolicyClientOwned | ||
| 602 | - | attemptRequest.Remote = false | 605 | + | attemptRequest.Remote = homeRoute.request.Remote || r.remote |
| 603 | resumeToken = 0 | 606 | resumeToken = 0 | ||
| 604 | - | remote = syncReconnectRemote(reconnect, false) | 607 | + | remote = syncReconnectRemote(reconnect, homeRoute.request.Remote || r.remote) |
| 605 | backoff = defaultReconnectBackoff.initial | 608 | backoff = defaultReconnectBackoff.initial | ||
| 606 | continue | 609 | continue | ||
| 607 | } | 610 | } | ||
M internal/usecase/client/client_test.go +7 -1
| @@ -67,7 +67,13 @@ func (c *reconnectTestClock) fireReconnect(t *testing.T) { | |||
| 67 | 67 | timer.mu.Lock() | |
| 68 | 68 | if !timer.stopped && !timer.fired { | |
| 69 | 69 | timer.fired = true | |
| 70 | - | timer.ch <- time.Time{} | |
| 70 | + | select { | |
| 71 | + | case timer.ch <- time.Time{}: | |
| 72 | + | default: | |
| 73 | + | timer.fired = false | |
| 74 | + | timer.mu.Unlock() | |
| 75 | + | continue | |
| 76 | + | } | |
| 71 | 77 | timer.mu.Unlock() | |
| 72 | 78 | return true | |
| 73 | 79 | } | |
| @@ -67,7 +67,13 @@ func (c *reconnectTestClock) fireReconnect(t *testing.T) { | |||||
| 67 | timer.mu.Lock() | 67 | timer.mu.Lock() | ||
| 68 | if !timer.stopped && !timer.fired { | 68 | if !timer.stopped && !timer.fired { | ||
| 69 | timer.fired = true | 69 | timer.fired = true | ||
| 70 | - | timer.ch <- time.Time{} | 70 | + | select { |
| 71 | + | case timer.ch <- time.Time{}: | |||
| 72 | + | default: | |||
| 73 | + | timer.fired = false | |||
| 74 | + | timer.mu.Unlock() | |||
| 75 | + | continue | |||
| 76 | + | } | |||
| 71 | timer.mu.Unlock() | 77 | timer.mu.Unlock() | ||
| 72 | return true | 78 | return true | ||
| 73 | } | 79 | } | ||
M internal/usecase/client/palette_attach_generation_test.go +5 -1
| @@ -43,7 +43,11 @@ func (*attachPaletteClock) Now() time.Time { return time.Time{} } | |||
| 43 | 43 | func (c *attachPaletteClock) NewTimer(delay time.Duration) ports.Timer { | |
| 44 | 44 | timer := &attachPaletteTimer{ch: make(chan time.Time, 1), stopped: make(chan struct{}), duration: delay} | |
| 45 | 45 | if delay == ports.HandshakeTimeout { | |
| 46 | - | c.handshakeTimers <- timer | |
| 46 | + | select { | |
| 47 | + | case c.handshakeTimers <- timer: | |
| 48 | + | default: | |
| 49 | + | panic("unexpected extra handshake timer") | |
| 50 | + | } | |
| 47 | 51 | } else { | |
| 48 | 52 | c.timers <- timer | |
| 49 | 53 | } | |
| @@ -43,7 +43,11 @@ func (*attachPaletteClock) Now() time.Time { return time.Time{} } | |||||
| 43 | func (c *attachPaletteClock) NewTimer(delay time.Duration) ports.Timer { | 43 | func (c *attachPaletteClock) NewTimer(delay time.Duration) ports.Timer { | ||
| 44 | timer := &attachPaletteTimer{ch: make(chan time.Time, 1), stopped: make(chan struct{}), duration: delay} | 44 | timer := &attachPaletteTimer{ch: make(chan time.Time, 1), stopped: make(chan struct{}), duration: delay} | ||
| 45 | if delay == ports.HandshakeTimeout { | 45 | if delay == ports.HandshakeTimeout { | ||
| 46 | - | c.handshakeTimers <- timer | 46 | + | select { |
| 47 | + | case c.handshakeTimers <- timer: | |||
| 48 | + | default: | |||
| 49 | + | panic("unexpected extra handshake timer") | |||
| 50 | + | } | |||
| 47 | } else { | 51 | } else { | ||
| 48 | c.timers <- timer | 52 | c.timers <- timer | ||
| 49 | } | 53 | } | ||
M internal/usecase/daemon/daemon.go +2 -1
| @@ -1219,14 +1219,15 @@ func (d *Daemon) handleHelloWithContext(handshakeCtx context.Context, timedOut < | |||
| 1219 | 1219 | failAttachment() | |
| 1220 | 1220 | return | |
| 1221 | 1221 | } | |
| 1222 | - | postWelcomeTicket.End() | |
| 1223 | 1222 | if !d.commitResumeClaim(ac) { | |
| 1223 | + | postWelcomeTicket.End() | |
| 1224 | 1224 | failAttachment() | |
| 1225 | 1225 | return | |
| 1226 | 1226 | } | |
| 1227 | 1227 | if ac.startupOverlay == ports.StartupOverlaySessionPicker { | |
| 1228 | 1228 | d.enterPicker(sess, ac) | |
| 1229 | 1229 | } | |
| 1230 | + | postWelcomeTicket.End() | |
| 1230 | 1231 | stopHandshakeTransport() | |
| 1231 | 1232 | finishHandshake() | |
| 1232 | 1233 | d.runConnLoop(ac) | |
| @@ -1219,14 +1219,15 @@ func (d *Daemon) handleHelloWithContext(handshakeCtx context.Context, timedOut < | |||||
| 1219 | failAttachment() | 1219 | failAttachment() | ||
| 1220 | return | 1220 | return | ||
| 1221 | } | 1221 | } | ||
| 1222 | - | postWelcomeTicket.End() | |||
| 1223 | if !d.commitResumeClaim(ac) { | 1222 | if !d.commitResumeClaim(ac) { | ||
| 1223 | + | postWelcomeTicket.End() | |||
| 1224 | failAttachment() | 1224 | failAttachment() | ||
| 1225 | return | 1225 | return | ||
| 1226 | } | 1226 | } | ||
| 1227 | if ac.startupOverlay == ports.StartupOverlaySessionPicker { | 1227 | if ac.startupOverlay == ports.StartupOverlaySessionPicker { | ||
| 1228 | d.enterPicker(sess, ac) | 1228 | d.enterPicker(sess, ac) | ||
| 1229 | } | 1229 | } | ||
| 1230 | + | postWelcomeTicket.End() | |||
| 1230 | stopHandshakeTransport() | 1231 | stopHandshakeTransport() | ||
| 1231 | finishHandshake() | 1232 | finishHandshake() | ||
| 1232 | d.runConnLoop(ac) | 1233 | d.runConnLoop(ac) | ||
M internal/usecase/daemon/picker.go +3 -1
| @@ -373,6 +373,7 @@ func (d *Daemon) handlePickerInput(ac *attachedClient, data []byte, effects ...* | |||
| 373 | 373 | d.registerPreviewForSelection(ac) | |
| 374 | 374 | } | |
| 375 | 375 | committing := (result.action == '\r' || result.action == '\n') && ok | |
| 376 | + | backNavigationSent := false | |
| 376 | 377 | if result.exit && !committing { | |
| 377 | 378 | back := ac.startupOverlay == ports.StartupOverlaySessionPicker && ac.navigationCapabilities&ports.NavigationCapabilityBack != 0 | |
| 378 | 379 | if back && len(effects) != 0 && effects[0] != nil { | |
| @@ -381,6 +382,7 @@ func (d *Daemon) handlePickerInput(ac *attachedClient, data []byte, effects ...* | |||
| 381 | 382 | d.invalidateRender(sess, ac, true, "picker.go") | |
| 382 | 383 | return | |
| 383 | 384 | } | |
| 385 | + | backNavigationSent = true | |
| 384 | 386 | } | |
| 385 | 387 | d.closePicker(ac) | |
| 386 | 388 | } | |
| @@ -420,7 +422,7 @@ func (d *Daemon) handlePickerInput(ac *attachedClient, data []byte, effects ...* | |||
| 420 | 422 | } | |
| 421 | 423 | return | |
| 422 | 424 | } | |
| 423 | - | if result.exit || result.changed { | |
| 425 | + | if (result.exit || result.changed) && !backNavigationSent { | |
| 424 | 426 | d.invalidateRender(sess, ac, true, "picker.go") | |
| 425 | 427 | } | |
| 426 | 428 | } | |
| @@ -373,6 +373,7 @@ func (d *Daemon) handlePickerInput(ac *attachedClient, data []byte, effects ...* | |||||
| 373 | d.registerPreviewForSelection(ac) | 373 | d.registerPreviewForSelection(ac) | ||
| 374 | } | 374 | } | ||
| 375 | committing := (result.action == '\r' || result.action == '\n') && ok | 375 | committing := (result.action == '\r' || result.action == '\n') && ok | ||
| 376 | + | backNavigationSent := false | |||
| 376 | if result.exit && !committing { | 377 | if result.exit && !committing { | ||
| 377 | back := ac.startupOverlay == ports.StartupOverlaySessionPicker && ac.navigationCapabilities&ports.NavigationCapabilityBack != 0 | 378 | back := ac.startupOverlay == ports.StartupOverlaySessionPicker && ac.navigationCapabilities&ports.NavigationCapabilityBack != 0 | ||
| 378 | if back && len(effects) != 0 && effects[0] != nil { | 379 | if back && len(effects) != 0 && effects[0] != nil { | ||
| @@ -381,6 +382,7 @@ func (d *Daemon) handlePickerInput(ac *attachedClient, data []byte, effects ...* | |||||
| 381 | d.invalidateRender(sess, ac, true, "picker.go") | 382 | d.invalidateRender(sess, ac, true, "picker.go") | ||
| 382 | return | 383 | return | ||
| 383 | } | 384 | } | ||
| 385 | + | backNavigationSent = true | |||
| 384 | } | 386 | } | ||
| 385 | d.closePicker(ac) | 387 | d.closePicker(ac) | ||
| 386 | } | 388 | } | ||
| @@ -420,7 +422,7 @@ func (d *Daemon) handlePickerInput(ac *attachedClient, data []byte, effects ...* | |||||
| 420 | } | 422 | } | ||
| 421 | return | 423 | return | ||
| 422 | } | 424 | } | ||
| 423 | - | if result.exit || result.changed { | 425 | + | if (result.exit || result.changed) && !backNavigationSent { |
| 424 | d.invalidateRender(sess, ac, true, "picker.go") | 426 | d.invalidateRender(sess, ac, true, "picker.go") | ||
| 425 | } | 427 | } | ||
| 426 | } | 428 | } | ||