diff options
| author | Paul Buetow <paul@buetow.org> | 2026-02-24 17:27:10 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-02-24 17:27:10 +0200 |
| commit | da8ddf1cf415f1754c3fe71f3f342327ad00e91e (patch) | |
| tree | af3ba70015cf7db4bfd3a2adcb9334417740e4f3 /internal | |
| parent | 2ae0b33c9f196634eaa55bd6997d1feae9147385 (diff) | |
tui: add toggle to disable snapshot export file writes
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/flags/flags.go | 11 | ||||
| -rw-r--r-- | internal/tui/common/keys.go | 15 | ||||
| -rw-r--r-- | internal/tui/tui.go | 13 | ||||
| -rw-r--r-- | internal/tui/tui_test.go | 37 |
4 files changed, 70 insertions, 6 deletions
diff --git a/internal/flags/flags.go b/internal/flags/flags.go index 729b1b6..87ece5d 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -14,7 +14,9 @@ import ( ) var ( - singleton Flags + singleton = Flags{ + TUIExportEnable: true, + } once sync.Once pidFilter atomic.Int64 ) @@ -56,6 +58,7 @@ type Flags struct { PlainMode bool FlamegraphEnable bool FlamegraphName string + TUIExportEnable bool // To convert ior data into collapsed format IorDataFile string @@ -77,6 +80,11 @@ func SetPidFilter(pid int) { pidFilter.Store(int64(pid)) } +// SetTUIExportEnable toggles TUI snapshot export file writing. +func SetTUIExportEnable(enabled bool) { + singleton.TUIExportEnable = enabled +} + func Parse() { once.Do(func() { parse() @@ -100,6 +108,7 @@ func parse() { flag.BoolVar(&singleton.PlainMode, "plain", false, "Enable plain CSV output mode (disable TUI)") flag.BoolVar(&singleton.FlamegraphEnable, "flamegraph", false, "Enable flamegraph builder") flag.StringVar(&singleton.FlamegraphName, "name", "default", "Name of the flamegraph, used to generate the SVG file") + flag.BoolVar(&singleton.TUIExportEnable, "tuiExport", true, "Enable writing TUI snapshot export files") flag.StringVar(&singleton.IorDataFile, "ior", "", "IOR data file to convert into collapsed format") fields := flag.String("fields", "", diff --git a/internal/tui/common/keys.go b/internal/tui/common/keys.go index 0f9c54d..1111def 100644 --- a/internal/tui/common/keys.go +++ b/internal/tui/common/keys.go @@ -45,14 +45,25 @@ func DefaultKeyMap() KeyMap { // DashboardShortHelp returns compact bindings for dashboard help bars. func (k KeyMap) DashboardShortHelp() []key.Binding { - return []key.Binding{k.Tab, k.ShiftTab, k.Export, k.Help, k.Quit} + bindings := []key.Binding{k.Tab, k.ShiftTab} + if help := k.Export.Help(); help.Key != "" || help.Desc != "" { + bindings = append(bindings, k.Export) + } + bindings = append(bindings, k.Help, k.Quit) + return bindings } // DashboardFullHelp returns grouped bindings for dashboard overlays. func (k KeyMap) DashboardFullHelp() [][]key.Binding { + controls := []key.Binding{k.Tab, k.ShiftTab} + if help := k.Export.Help(); help.Key != "" || help.Desc != "" { + controls = append(controls, k.Export) + } + controls = append(controls, k.Refresh, k.Help, k.Quit) + return [][]key.Binding{ {k.One, k.Two, k.Three, k.Four, k.Five, k.Six}, - {k.Tab, k.ShiftTab, k.Export, k.Refresh, k.Help, k.Quit}, + controls, { key.NewBinding(key.WithKeys("left/right"), key.WithHelp("left/right", "tab")), key.NewBinding(key.WithKeys("h/l"), key.WithHelp("h/l", "tab")), diff --git a/internal/tui/tui.go b/internal/tui/tui.go index 143cf02..4433172 100644 --- a/internal/tui/tui.go +++ b/internal/tui/tui.go @@ -99,13 +99,17 @@ func NewModel(initialPID int, startTrace TraceStarter) Model { if startTrace == nil { startTrace = defaultTraceStarter } + keys := Keys + if !flags.Get().TUIExportEnable { + keys.Export = key.NewBinding() + } model := Model{ screen: ScreenPIDPicker, pidPicker: pidpicker.New(), - dashboard: dashboardui.NewModel(lateBoundDashboardSource{}), + dashboard: dashboardui.NewModelWithConfig(lateBoundDashboardSource{}, 1000, keys), exporter: tuiexport.NewModel(), - keys: Keys, + keys: keys, spin: spin, startTrace: startTrace, } @@ -151,7 +155,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if !m.exporter.Visible() && m.showHelp { return m, nil } - if m.screen == ScreenDashboard && !m.attaching && m.lastErr == nil && key.Matches(msg, m.keys.Export) && !m.exporter.Visible() { + if flags.Get().TUIExportEnable && m.screen == ScreenDashboard && !m.attaching && m.lastErr == nil && key.Matches(msg, m.keys.Export) && !m.exporter.Visible() { m.exporter = m.exporter.Open() return m, nil } @@ -299,6 +303,9 @@ func (m Model) View() string { func runExportCmd(option tuiexport.Option, snap *statsengine.Snapshot) tea.Cmd { return func() tea.Msg { + if !flags.Get().TUIExportEnable { + return tuiexport.FailedMsg{Err: errors.New("tui export is disabled by -tuiExport=false")} + } switch option { case tuiexport.OptionCSV: path, err := exportSnapshotCSV(snap) diff --git a/internal/tui/tui_test.go b/internal/tui/tui_test.go index fd7adfa..e69ff9b 100644 --- a/internal/tui/tui_test.go +++ b/internal/tui/tui_test.go @@ -192,6 +192,9 @@ func TestDashboardRefreshPicksLateBoundSource(t *testing.T) { } func TestExportKeyOpensModalOnDashboard(t *testing.T) { + flags.SetTUIExportEnable(true) + t.Cleanup(func() { flags.SetTUIExportEnable(true) }) + m := NewModel(-1, func(context.Context) error { return nil }) m.screen = ScreenDashboard m.attaching = false @@ -203,6 +206,21 @@ func TestExportKeyOpensModalOnDashboard(t *testing.T) { } } +func TestExportKeyIgnoredWhenExportDisabled(t *testing.T) { + flags.SetTUIExportEnable(false) + t.Cleanup(func() { flags.SetTUIExportEnable(true) }) + + m := NewModel(-1, func(context.Context) error { return nil }) + m.screen = ScreenDashboard + m.attaching = false + + next, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'e'}}) + updated := next.(Model) + if updated.exporter.Visible() { + t.Fatalf("expected export modal to remain closed when export is disabled") + } +} + func TestRunExportCmdCSVWritesFile(t *testing.T) { dir := t.TempDir() prev, err := os.Getwd() @@ -292,6 +310,9 @@ func TestHelpOverlayUsesPickerBindingsOnPickerScreen(t *testing.T) { } func TestHelpToggleDoesNotBreakExportModalInput(t *testing.T) { + flags.SetTUIExportEnable(true) + t.Cleanup(func() { flags.SetTUIExportEnable(true) }) + m := NewModel(-1, func(context.Context) error { return nil }) m.screen = ScreenDashboard @@ -314,6 +335,22 @@ func TestHelpToggleDoesNotBreakExportModalInput(t *testing.T) { } } +func TestHelpOverlayHidesExportBindingWhenExportDisabled(t *testing.T) { + flags.SetTUIExportEnable(false) + t.Cleanup(func() { flags.SetTUIExportEnable(true) }) + + m := NewModel(-1, func(context.Context) error { return nil }) + m.screen = ScreenDashboard + m.showHelp = true + m.width = 100 + m.height = 30 + + out := m.View() + if strings.Contains(out, "e export") { + t.Fatalf("did not expect export shortcut in help overlay when export is disabled") + } +} + func TestExportModalStillAllowsDashboardStatsUpdates(t *testing.T) { m := NewModel(-1, func(context.Context) error { return nil }) m.screen = ScreenDashboard |
