Compare commits

..
8 Commits
Author SHA1 Message Date
stefankoelle d05d35bdc1 release v0.3.7
Windows Terminal maximized mode and quit keybinding fixes.

- Change wt -F (fullscreen) to wt -M (maximized) — keeps titlebar and taskbar visible
- Use DisableQuitKeybindings() in wt mode to fully disable q and ctrl+c in panel
- Hide help text in wt mode to remove visible 'q quit' hint
2026-09-17 06:52:25 +02:00
stefankoelle f8679adf95 fix: use maximized instead of fullscreen and disable quit keybindings
- Change wt -F (fullscreen) to wt -M (maximized) — keeps titlebar and taskbar
- Use DisableQuitKeybindings() in wt mode to fully disable q and ctrl+c
- Hide help text in wt mode to remove visible q quit hint
2026-09-17 06:51:09 +02:00
stefankoelle 73cc510347 release v0.3.6
Windows Terminal fullscreen and keybinding improvements.

- Add -F flag to wt.exe to start in fullscreen mode
- Disable 'q' keybinding in panel when using wt mode — prevents accidental quit (ctrl+c remains available)
2026-09-16 23:19:38 +02:00
stefankoelle d17eb6971c feat: add fullscreen and disable q key in Windows Terminal mode
- Add -F flag to wt.exe to start in fullscreen mode
- Disable 'q' keybinding in panel when using wt mode (ctrl+c remains)
2026-09-16 23:18:45 +02:00
stefankoelle fb4c1be12b release v0.3.5
Disable Quit option in Windows Terminal mode.

- Hide 'Quit (closes this tmux session)' in env menu when using wt mode — prevents confusion as wt windows are independent
- Esc on env menu stays on env menu in wt mode (no tmux kill attempted)
2026-09-16 22:46:26 +02:00
stefankoelle 9e3fefb71a feat: disable Quit option in Windows Terminal mode
- Hide 'Quit (closes this tmux session)' in env menu when using wt mode
- Esc on env menu stays on env menu in wt mode (no tmux kill attempted)
2026-09-16 22:41:34 +02:00
stefankoelle 2d382441a1 release v0.3.4
Improved Windows Terminal layout and update error handling.

- Split ratios: panel 25%, k9sA 37.5%, k9sB 37.5% (was 50/25/25) — k9s panes now get more space
- Update failures now exit with error code 1 instead of continuing silently with the old version
2026-09-16 22:21:56 +02:00
stefankoelle ad7d8534ab feat: improve Windows Terminal split ratios and update error handling
- Split ratios: panel 25%, k9sA 37.5%, k9sB 37.5% (was 50/25/25)
- Update failures now exit with error code 1 instead of continuing with old version
2026-09-16 22:20:42 +02:00
4 changed files with 40 additions and 17 deletions
+4 -2
View File
@@ -337,6 +337,8 @@ func (m *fullModel) startTmuxSessionTmux() tea.Cmd {
// split-pane feature. This avoids the psmux focus-freeze issue on Windows.
// Note: In Windows Terminal, -H (horizontal) stacks panes top/bottom,
// while -V (vertical) places them side by side — opposite of tmux.
// The -s flag controls the split ratio: first split gives k9sA 75%
// (panel keeps 25%), second split divides k9sA equally (37.5% each).
func (m *fullModel) startWtSession() tea.Cmd {
selfPath, err := os.Executable()
if err != nil {
@@ -353,14 +355,14 @@ func (m *fullModel) startWtSession() tea.Cmd {
kubeexec.VerboseLog("[debug] panelCmd=%s\n", panelCmd)
kubeexec.VerboseLog("[debug] k9sCmdA=%s\n", k9sCmdA)
wtCmd := fmt.Sprintf("wt new-tab %s ; split-pane -H %s", panelCmd, k9sCmdA)
wtCmd := fmt.Sprintf("wt -M new-tab %s ; split-pane -H -s 0.75 %s", panelCmd, k9sCmdA)
if len(m.cfg.Envs) > 1 {
envB := m.cfg.Envs[1]
ctxB := m.cfg.ResolveContext(envB, m.selectedContext)
k9sCmdB := fmt.Sprintf("k9s --context %s --namespace %s --command pods", ctxB, m.selectedNamespace)
kubeexec.VerboseLog("[debug] k9sCmdB=%s\n", k9sCmdB)
wtCmd += fmt.Sprintf(" ; split-pane -H %s", k9sCmdB)
wtCmd += fmt.Sprintf(" ; split-pane -H -s 0.5 %s", k9sCmdB)
}
c := exec.Command("cmd", "/c", wtCmd)
+6 -1
View File
@@ -94,7 +94,12 @@ func main() {
fmt.Fprintf(os.Stderr, "WARNING: failed to load config: %v\n", cfgErr)
}
if cfgErr == nil && cfg.IsAutoUpdateCheckEnabled() {
if checkForUpdateInteractive(verbose) {
updated, err := checkForUpdateInteractive(verbose)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
if updated {
os.Exit(0)
}
}
+18 -2
View File
@@ -8,6 +8,7 @@ import (
"flag"
"fmt"
"os/exec"
"runtime"
"strconv"
"strings"
"time"
@@ -61,6 +62,11 @@ type panelModel struct {
err error
}
// isWtMode returns true when running on Windows with multiplexer: "wt".
func (m *panelModel) isWtMode() bool {
return runtime.GOOS == "windows" && m.cfg.MultiplexerBackend() == "wt"
}
func runPanel(args []string) error {
fs := flag.NewFlagSet("panel", flag.ContinueOnError)
context := fs.String("context", "", "context (e.g. internal/external)")
@@ -88,6 +94,10 @@ func newPanelModel(context, ns, team string) *panelModel {
l.SetFilteringEnabled(false)
m := &panelModel{context: context, ns: ns, team: team, cfg: cfg, step: stepEnvMenu, list: l, input: ti}
if m.isWtMode() {
l.DisableQuitKeybindings()
l.SetShowHelp(false)
}
if loadErr != nil {
m.err = fmt.Errorf("config load failed: %w", loadErr)
m.step = stepError
@@ -99,7 +109,9 @@ func newPanelModel(context, ns, team string) *panelModel {
func (m *panelModel) showEnvMenu() {
items := make([]list.Item, 0, len(m.cfg.Envs)+1)
items = append(items, simpleItem{label: "Quit (closes this tmux session)", value: "quit"})
if !m.isWtMode() {
items = append(items, simpleItem{label: "Quit (closes this tmux session)", value: "quit"})
}
for _, env := range m.cfg.Envs {
items = append(items, simpleItem{label: env, value: env})
}
@@ -179,10 +191,14 @@ func (m *panelModel) usesTextInput() bool {
// handleEsc navigates one level up: action menu -> env menu, most
// sub-steps -> action menu. From the top-level env menu it closes the
// whole tmux session (all panes, including the two k9s status panes)
// before quitting this program, per SPEC.md 3.6.
// before quitting this program, per SPEC.md 3.6. In wt mode, Esc on
// the env menu does nothing — the user closes the wt window manually.
func (m *panelModel) handleEsc() (tea.Model, tea.Cmd) {
switch m.step {
case stepEnvMenu:
if m.isWtMode() {
return m, nil
}
exec.Command("tmux", "kill-session", "-t", "kctl").Run()
return m, tea.Quit
case stepActionMenu:
+12 -12
View File
@@ -83,14 +83,15 @@ func runUpdate(verbose bool) error {
}
// checkForUpdateInteractive checks for a new version and prompts the user to update.
// Returns true if an update was applied.
func checkForUpdateInteractive(verbose bool) bool {
// Returns (true, nil) if an update was applied successfully, (false, nil) if no
// update was needed or the user declined, and (false, err) if the update failed.
func checkForUpdateInteractive(verbose bool) (bool, error) {
if version == "dev" {
return false
return false, nil
}
if !term.IsTerminal(int(os.Stdin.Fd())) {
return false
return false, nil
}
updater, err := initUpdater(verbose)
@@ -98,7 +99,7 @@ func checkForUpdateInteractive(verbose bool) bool {
if verbose {
fmt.Fprintf(os.Stderr, "Update check failed: %v\n", err)
}
return false
return false, nil
}
ctx, cancel := context.WithTimeout(context.Background(), updateTimeout)
@@ -110,10 +111,10 @@ func checkForUpdateInteractive(verbose bool) bool {
if verbose {
fmt.Fprintf(os.Stderr, "Update check failed: %v\n", err)
}
return false
return false, nil
}
if !found {
return false
return false, nil
}
current, _ := semver.NewVersion(version)
@@ -121,7 +122,7 @@ func checkForUpdateInteractive(verbose bool) bool {
newVer, _ := semver.NewVersion(newVersion)
if current != nil && !current.LessThan(newVer) {
return false
return false, nil
}
fmt.Printf("New version %s available (current: %s). Update now? [y/N] ", newVersion, version)
@@ -131,17 +132,16 @@ func checkForUpdateInteractive(verbose bool) bool {
answer = strings.TrimSpace(strings.ToLower(answer))
if answer != "y" && answer != "yes" {
return false
return false, nil
}
fmt.Println("Updating...")
if err := updater.UpdateTo(ctx, rel, ""); err != nil {
fmt.Fprintf(os.Stderr, "Update failed: %v\n", err)
return false
return false, fmt.Errorf("update failed: %w", err)
}
fmt.Printf("Updated to %s. Please restart kctl-tui.\n", newVersion)
return true
return true, nil
}
type verboseLogger struct{}