Skip to content

Commit e9750be

Browse files
apanchooclaude
andcommitted
feat: close window when last terminal exits (term:closeonlasttermclose)
Adds a new global setting `term:closeonlasttermclose` that automatically closes the window when the last shell/terminal block exits. When enabled, typing `exit` in the last remaining terminal closes the window immediately (no delay), while non-last terminals are unaffected. Closes #3026 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 568027d commit e9750be

5 files changed

Lines changed: 50 additions & 2 deletions

File tree

frontend/types/gotypes.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,6 +1342,7 @@ declare global {
13421342
"term:bellindicator"?: boolean;
13431343
"term:osc52"?: string;
13441344
"term:durable"?: boolean;
1345+
"term:closeonlasttermclose"?: boolean;
13451346
"editor:minimapenabled"?: boolean;
13461347
"editor:stickyscrollenabled"?: boolean;
13471348
"editor:wordwrap"?: boolean;

pkg/blockcontroller/shellcontroller.go

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,39 @@ func (union *ConnUnion) getRemoteInfoAndShellType(blockMeta waveobj.MetaMapType)
647647
return nil
648648
}
649649

650+
func isLastShellBlockInWorkspace(ctx context.Context, blockId string) bool {
651+
tabId, err := wstore.DBFindTabForBlockId(ctx, blockId)
652+
if err != nil || tabId == "" {
653+
return false
654+
}
655+
workspaceId, err := wstore.DBFindWorkspaceForTabId(ctx, tabId)
656+
if err != nil || workspaceId == "" {
657+
return false
658+
}
659+
workspace, err := wstore.DBGet[*waveobj.Workspace](ctx, workspaceId)
660+
if err != nil || workspace == nil {
661+
return false
662+
}
663+
shellBlockCount := 0
664+
for _, wsTabId := range workspace.TabIds {
665+
tab, err := wstore.DBGet[*waveobj.Tab](ctx, wsTabId)
666+
if err != nil || tab == nil {
667+
continue
668+
}
669+
for _, wsBlockId := range tab.BlockIds {
670+
block, err := wstore.DBGet[*waveobj.Block](ctx, wsBlockId)
671+
if err != nil || block == nil {
672+
continue
673+
}
674+
controller := block.Meta.GetString(waveobj.MetaKey_Controller, "")
675+
if controller == BlockController_Shell || controller == BlockController_Cmd {
676+
shellBlockCount++
677+
}
678+
}
679+
}
680+
return shellBlockCount == 1
681+
}
682+
650683
func checkCloseOnExit(blockId string, exitCode int) {
651684
ctx, cancelFn := context.WithTimeout(context.Background(), DefaultTimeout)
652685
defer cancelFn()
@@ -657,10 +690,19 @@ func checkCloseOnExit(blockId string, exitCode int) {
657690
}
658691
closeOnExit := blockData.Meta.GetBool(waveobj.MetaKey_CmdCloseOnExit, false)
659692
closeOnExitForce := blockData.Meta.GetBool(waveobj.MetaKey_CmdCloseOnExitForce, false)
693+
defaultDelayMs := 2000.0
660694
if !closeOnExitForce && !(closeOnExit && exitCode == 0) {
661-
return
695+
// Check global setting: close when last terminal exits
696+
settings := wconfig.GetWatcher().GetFullConfig().Settings
697+
if !settings.TermCloseOnLastTermClose {
698+
return
699+
}
700+
if !isLastShellBlockInWorkspace(ctx, blockId) {
701+
return
702+
}
703+
defaultDelayMs = 0
662704
}
663-
delayMs := blockData.Meta.GetFloat(waveobj.MetaKey_CmdCloseOnExitDelay, 2000)
705+
delayMs := blockData.Meta.GetFloat(waveobj.MetaKey_CmdCloseOnExitDelay, defaultDelayMs)
664706
if delayMs < 0 {
665707
delayMs = 0
666708
}

pkg/wconfig/metaconsts.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ const (
5858
ConfigKey_TermBellIndicator = "term:bellindicator"
5959
ConfigKey_TermOsc52 = "term:osc52"
6060
ConfigKey_TermDurable = "term:durable"
61+
ConfigKey_TermCloseOnLastTermClose = "term:closeonlasttermclose"
6162

6263
ConfigKey_EditorMinimapEnabled = "editor:minimapenabled"
6364
ConfigKey_EditorStickyScrollEnabled = "editor:stickyscrollenabled"

pkg/wconfig/settingsconfig.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ type SettingsType struct {
109109
TermBellIndicator *bool `json:"term:bellindicator,omitempty"`
110110
TermOsc52 string `json:"term:osc52,omitempty" jsonschema:"enum=focus,enum=always"`
111111
TermDurable *bool `json:"term:durable,omitempty"`
112+
TermCloseOnLastTermClose bool `json:"term:closeonlasttermclose,omitempty"`
112113

113114
EditorMinimapEnabled bool `json:"editor:minimapenabled,omitempty"`
114115
EditorStickyScrollEnabled bool `json:"editor:stickyscrollenabled,omitempty"`

schema/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,9 @@
161161
"term:durable": {
162162
"type": "boolean"
163163
},
164+
"term:closeonlasttermclose": {
165+
"type": "boolean"
166+
},
164167
"editor:minimapenabled": {
165168
"type": "boolean"
166169
},

0 commit comments

Comments
 (0)