detached 525159d

refactor(daemon): build the palette decision directly in its enter branch

commit
525159d3ddf728818ebee2c1bcbaedd0e3e21a45
author
brice · · 2026-10-08 09:01 UTC
committer
brice · · 2026-10-08 09:31 UTC
parent
db16c31
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
1 file · +22 -59
M internal/usecase/daemon/palette.go +22 -59
@@ -388,21 +388,8 @@ type paletteDecision struct {
388388 // decidePaletteInput takes paletteMu, applies data to the attachment's palette,
389389 // and returns the action to run after unlocking.
390390 func decidePaletteInput(ac *attachedClient, data []byte) paletteDecision {
391- var cmd command.Command
392- var sessionTarget palette.Result
393- var hasSessionTarget bool
394- var importedSourceKey, importedEntryKey string
395- var hasImportedTarget bool
396- var routeTarget protocol.RouteNavigationAction
397- var hasRouteTarget bool
398- var createDestination palette.Result
399- var hasCreateDestination bool
400- var args []string
401- var routeSnapshot protocol.RecentRouteSnapshot
402- var generation uint64
403- var rawQuery string
404- var cancelInventoryInteraction uint64
405- var hasCancelInventory bool
391+ // decision holds the executed selection; execute reports one was made.
392+ var decision paletteDecision
406393 changed, cancel, execute, chooseDestination := false, false, false, false
407394
408395 ac.overlays.paletteMu.Lock()
@@ -456,40 +443,35 @@ func decidePaletteInput(ac *attachedClient, data []byte) paletteDecision {
456443 chooseDestination = true
457444 return
458445 }
459- rawQuery = ac.overlays.palette.Query()
460- if selectedCommand, ok := selected.Command(); ok {
461- cmd = selectedCommand
446+ next := paletteDecision{rawQuery: ac.overlays.palette.Query(), generation: ac.overlays.paletteGeneration}
447+ if cmd, ok := selected.Command(); ok {
448+ next.kind, next.cmd = paletteDecisionCommand, cmd
462449 if cmd.Arguments != command.ArgumentsNone {
463- action, valid := palette.ParseAction([]palette.Result{selected}, rawQuery)
450+ action, valid := palette.ParseAction([]palette.Result{selected}, next.rawQuery)
464451 if valid {
465- args = action.Args
452+ next.args = action.Args
466453 } else if cmd.Arguments == command.ArgumentsRequired {
467454 changed = true
468455 return
469456 }
470457 }
471458 if cmd.ContextHint == command.ContextHintRecentSessions {
472- routeSnapshot = ac.overlays.paletteRouteSnapshot
473- routeSnapshot.Entries = append([]protocol.RecentRouteEntry(nil), routeSnapshot.Entries...)
459+ next.routeSnapshot = ac.overlays.paletteRouteSnapshot
460+ next.routeSnapshot.Entries = append([]protocol.RecentRouteEntry(nil), next.routeSnapshot.Entries...)
474461 }
475462 } else if sourceKey, entryKey, ok := selected.ImportedSessionKey(); ok {
476- importedSourceKey = sourceKey
477- importedEntryKey = entryKey
478- hasImportedTarget = true
463+ next.kind, next.importedSourceKey, next.importedEntryKey = paletteDecisionImportedSession, sourceKey, entryKey
479464 } else if action, ok := selected.RouteNavigationAction(); ok {
480- routeTarget = action
481- hasRouteTarget = true
465+ next.kind, next.routeTarget = paletteDecisionRouteNavigation, action
482466 } else if _, _, _, _, _, ok := selected.CreateSessionDestination(); ok {
483- createDestination = selected
484- hasCreateDestination = true
467+ next.kind, next.createDestination = paletteDecisionCreateDestination, selected
485468 } else if _, ok := selected.SessionTarget(); ok {
486- sessionTarget = selected
487- hasSessionTarget = true
469+ next.kind, next.sessionTarget = paletteDecisionSessionTarget, selected
488470 } else {
489471 changed = true
490472 return
491473 }
492- generation = ac.overlays.paletteGeneration
474+ decision = next
493475 execute = true
494476 },
495477 })
@@ -515,41 +497,22 @@ func decidePaletteInput(ac *attachedClient, data []byte) paletteDecision {
515497 }
516498 }
517499 if cancel {
518- cancelInventoryInteraction, hasCancelInventory = takePaletteInventoryClose(ac.overlays)
500+ cancelled := paletteDecision{kind: paletteDecisionCancel}
501+ cancelled.cancelInventoryInteraction, cancelled.hasCancelInventory = takePaletteInventoryClose(ac.overlays)
519502 ac.clearPaletteLocked()
503+ ac.overlays.paletteMu.Unlock()
504+ return cancelled
520505 }
521506 ac.overlays.paletteMu.Unlock()
522507
523508 switch {
524- case cancel:
525- return paletteDecision{kind: paletteDecisionCancel, cancelInventoryInteraction: cancelInventoryInteraction, hasCancelInventory: hasCancelInventory}
526- case !execute && changed:
509+ case execute:
510+ return decision
511+ case changed:
527512 return paletteDecision{kind: paletteDecisionRedraw}
528- case !execute:
529- return paletteDecision{}
530- }
531- decision := paletteDecision{generation: generation, rawQuery: rawQuery}
532- switch {
533- case hasCreateDestination:
534- decision.kind = paletteDecisionCreateDestination
535- decision.createDestination = createDestination
536- case hasImportedTarget:
537- decision.kind = paletteDecisionImportedSession
538- decision.importedSourceKey = importedSourceKey
539- decision.importedEntryKey = importedEntryKey
540- case hasRouteTarget:
541- decision.kind = paletteDecisionRouteNavigation
542- decision.routeTarget = routeTarget
543- case hasSessionTarget:
544- decision.kind = paletteDecisionSessionTarget
545- decision.sessionTarget = sessionTarget
546513 default:
547- decision.kind = paletteDecisionCommand
548- decision.cmd = cmd
549- decision.args = args
550- decision.routeSnapshot = routeSnapshot
514+ return paletteDecision{}
551515 }
552- return decision
553516 }
554517
555518 func (d *Daemon) handlePaletteInput(ac *attachedClient, data []byte, effects ...*attachmentEffect) {
@@ -388,21 +388,8 @@ type paletteDecision struct {
388 // decidePaletteInput takes paletteMu, applies data to the attachment's palette,388 // decidePaletteInput takes paletteMu, applies data to the attachment's palette,
389 // and returns the action to run after unlocking.389 // and returns the action to run after unlocking.
390 func decidePaletteInput(ac *attachedClient, data []byte) paletteDecision {390 func decidePaletteInput(ac *attachedClient, data []byte) paletteDecision {
391- var cmd command.Command391+ // decision holds the executed selection; execute reports one was made.
392- var sessionTarget palette.Result392+ var decision paletteDecision
393- var hasSessionTarget bool
394- var importedSourceKey, importedEntryKey string
395- var hasImportedTarget bool
396- var routeTarget protocol.RouteNavigationAction
397- var hasRouteTarget bool
398- var createDestination palette.Result
399- var hasCreateDestination bool
400- var args []string
401- var routeSnapshot protocol.RecentRouteSnapshot
402- var generation uint64
403- var rawQuery string
404- var cancelInventoryInteraction uint64
405- var hasCancelInventory bool
406 changed, cancel, execute, chooseDestination := false, false, false, false393 changed, cancel, execute, chooseDestination := false, false, false, false
407 394
408 ac.overlays.paletteMu.Lock()395 ac.overlays.paletteMu.Lock()
@@ -456,40 +443,35 @@ func decidePaletteInput(ac *attachedClient, data []byte) paletteDecision {
456 chooseDestination = true443 chooseDestination = true
457 return444 return
458 }445 }
459- rawQuery = ac.overlays.palette.Query()446+ next := paletteDecision{rawQuery: ac.overlays.palette.Query(), generation: ac.overlays.paletteGeneration}
460- if selectedCommand, ok := selected.Command(); ok {447+ if cmd, ok := selected.Command(); ok {
461- cmd = selectedCommand448+ next.kind, next.cmd = paletteDecisionCommand, cmd
462 if cmd.Arguments != command.ArgumentsNone {449 if cmd.Arguments != command.ArgumentsNone {
463- action, valid := palette.ParseAction([]palette.Result{selected}, rawQuery)450+ action, valid := palette.ParseAction([]palette.Result{selected}, next.rawQuery)
464 if valid {451 if valid {
465- args = action.Args452+ next.args = action.Args
466 } else if cmd.Arguments == command.ArgumentsRequired {453 } else if cmd.Arguments == command.ArgumentsRequired {
467 changed = true454 changed = true
468 return455 return
469 }456 }
470 }457 }
471 if cmd.ContextHint == command.ContextHintRecentSessions {458 if cmd.ContextHint == command.ContextHintRecentSessions {
472- routeSnapshot = ac.overlays.paletteRouteSnapshot459+ next.routeSnapshot = ac.overlays.paletteRouteSnapshot
473- routeSnapshot.Entries = append([]protocol.RecentRouteEntry(nil), routeSnapshot.Entries...)460+ next.routeSnapshot.Entries = append([]protocol.RecentRouteEntry(nil), next.routeSnapshot.Entries...)
474 }461 }
475 } else if sourceKey, entryKey, ok := selected.ImportedSessionKey(); ok {462 } else if sourceKey, entryKey, ok := selected.ImportedSessionKey(); ok {
476- importedSourceKey = sourceKey463+ next.kind, next.importedSourceKey, next.importedEntryKey = paletteDecisionImportedSession, sourceKey, entryKey
477- importedEntryKey = entryKey
478- hasImportedTarget = true
479 } else if action, ok := selected.RouteNavigationAction(); ok {464 } else if action, ok := selected.RouteNavigationAction(); ok {
480- routeTarget = action465+ next.kind, next.routeTarget = paletteDecisionRouteNavigation, action
481- hasRouteTarget = true
482 } else if _, _, _, _, _, ok := selected.CreateSessionDestination(); ok {466 } else if _, _, _, _, _, ok := selected.CreateSessionDestination(); ok {
483- createDestination = selected467+ next.kind, next.createDestination = paletteDecisionCreateDestination, selected
484- hasCreateDestination = true
485 } else if _, ok := selected.SessionTarget(); ok {468 } else if _, ok := selected.SessionTarget(); ok {
486- sessionTarget = selected469+ next.kind, next.sessionTarget = paletteDecisionSessionTarget, selected
487- hasSessionTarget = true
488 } else {470 } else {
489 changed = true471 changed = true
490 return472 return
491 }473 }
492- generation = ac.overlays.paletteGeneration474+ decision = next
493 execute = true475 execute = true
494 },476 },
495 })477 })
@@ -515,41 +497,22 @@ func decidePaletteInput(ac *attachedClient, data []byte) paletteDecision {
515 }497 }
516 }498 }
517 if cancel {499 if cancel {
518- cancelInventoryInteraction, hasCancelInventory = takePaletteInventoryClose(ac.overlays)500+ cancelled := paletteDecision{kind: paletteDecisionCancel}
501+ cancelled.cancelInventoryInteraction, cancelled.hasCancelInventory = takePaletteInventoryClose(ac.overlays)
519 ac.clearPaletteLocked()502 ac.clearPaletteLocked()
503+ ac.overlays.paletteMu.Unlock()
504+ return cancelled
520 }505 }
521 ac.overlays.paletteMu.Unlock()506 ac.overlays.paletteMu.Unlock()
522 507
523 switch {508 switch {
524- case cancel:509+ case execute:
525- return paletteDecision{kind: paletteDecisionCancel, cancelInventoryInteraction: cancelInventoryInteraction, hasCancelInventory: hasCancelInventory}510+ return decision
526- case !execute && changed:511+ case changed:
527 return paletteDecision{kind: paletteDecisionRedraw}512 return paletteDecision{kind: paletteDecisionRedraw}
528- case !execute:
529- return paletteDecision{}
530- }
531- decision := paletteDecision{generation: generation, rawQuery: rawQuery}
532- switch {
533- case hasCreateDestination:
534- decision.kind = paletteDecisionCreateDestination
535- decision.createDestination = createDestination
536- case hasImportedTarget:
537- decision.kind = paletteDecisionImportedSession
538- decision.importedSourceKey = importedSourceKey
539- decision.importedEntryKey = importedEntryKey
540- case hasRouteTarget:
541- decision.kind = paletteDecisionRouteNavigation
542- decision.routeTarget = routeTarget
543- case hasSessionTarget:
544- decision.kind = paletteDecisionSessionTarget
545- decision.sessionTarget = sessionTarget
546 default:513 default:
547- decision.kind = paletteDecisionCommand514+ return paletteDecision{}
548- decision.cmd = cmd
549- decision.args = args
550- decision.routeSnapshot = routeSnapshot
551 }515 }
552- return decision
553 }516 }
554 517
555 func (d *Daemon) handlePaletteInput(ac *attachedClient, data []byte, effects ...*attachmentEffect) {518 func (d *Daemon) handlePaletteInput(ac *attachedClient, data []byte, effects ...*attachmentEffect) {

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