Compare commits

..
2 Commits
Author SHA1 Message Date
stefankoelle bd23403e6c release v0.3.1
Compact menu layout and cross-env namespace discovery.

- Reduce menu item height from 3 rows to 1 (no description, no spacing) for denser display
- Merge namespaces from all configured envs so namespace-only-in-prod is no longer missing
2026-09-16 12:21:41 +02:00
stefankoelle 6a0405640d feat: compact menu layout and merge namespaces across all envs
- Reduce menu item height from 3 rows to 1 (no description, no spacing)
- Merge namespaces from all configured envs so namespace-only-in-prod is no longer missing
2026-09-16 12:18:17 +02:00
3 changed files with 27 additions and 17 deletions
+14 -16
View File
@@ -47,7 +47,7 @@ type fullModel struct {
}
func newFullModel() *fullModel {
l := list.New(nil, list.NewDefaultDelegate(), 0, 0)
l := list.New(nil, newCompactDelegate(), 0, 0)
l.Title = "kctl-tui"
l.SetShowStatusBar(false)
l.SetFilteringEnabled(false)
@@ -210,23 +210,21 @@ func (m *fullModel) handleSelect() (tea.Model, tea.Cmd) {
return m, nil
}
// bootstrapContext resolves a kubectl context purely to discover
// namespaces/labels for the team/namespace screens. The first configured
// env is used as a stable default for this discovery step, since
// namespace names are assumed to be identical across envs.
func (m *fullModel) bootstrapContext() string {
if len(m.cfg.Envs) == 0 {
return ""
}
return m.cfg.ResolveContext(m.cfg.Envs[0], m.selectedContext)
}
func (m *fullModel) loadTeams() tea.Msg {
namespaces, err := kubeexec.GetNamespacesWithLabels(m.bootstrapContext())
if err != nil {
return errMsg{err}
merged := map[string]map[string]string{}
for _, env := range m.cfg.Envs {
ctx := m.cfg.ResolveContext(env, m.selectedContext)
namespaces, err := kubeexec.GetNamespacesWithLabels(ctx)
if err != nil {
continue
}
for ns, labels := range namespaces {
if _, exists := merged[ns]; !exists {
merged[ns] = labels
}
}
}
return *toTeamsLoadedMsg(namespaces, m.cfg.TeamLabelKey)
return *toTeamsLoadedMsg(merged, m.cfg.TeamLabelKey)
}
func (m *fullModel) loadTeamsFor(namespaces map[string]map[string]string) tea.Cmd {
+12
View File
@@ -3,6 +3,8 @@
package main
import "github.com/charmbracelet/bubbles/list"
// simpleItem is a minimal implementation of list.Item used for all
// selection screens (contexts, teams, namespaces, menu actions).
type simpleItem struct {
@@ -13,3 +15,13 @@ type simpleItem struct {
func (i simpleItem) Title() string { return i.label }
func (i simpleItem) Description() string { return "" }
func (i simpleItem) FilterValue() string { return i.label }
// newCompactDelegate returns a list delegate that renders each item as a
// single line with no extra spacing, maximizing the number of visible
// entries in the terminal.
func newCompactDelegate() list.DefaultDelegate {
d := list.NewDefaultDelegate()
d.ShowDescription = false
d.SetSpacing(0)
return d
}
+1 -1
View File
@@ -83,7 +83,7 @@ func newPanelModel(context, ns, team string) *panelModel {
cfgPath, _ := config.DefaultPath()
cfg, loadErr := config.Load(cfgPath)
l := list.New(nil, list.NewDefaultDelegate(), 0, 0)
l := list.New(nil, newCompactDelegate(), 0, 0)
l.SetShowStatusBar(false)
l.SetFilteringEnabled(false)