Skip to content

Commit 61c2792

Browse files
authored
Better terminal resizing and improve error handling (#146)
1 parent 8fc42ba commit 61c2792

File tree

3 files changed

+125
-44
lines changed

3 files changed

+125
-44
lines changed

tunnel/cmd/portr/start.go

-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package main
22

33
import (
4-
"fmt"
54
"log"
65

76
"os"
@@ -50,8 +49,6 @@ func startTunnels(c *cli.Context, tunnelFromCli *config.Tunnel) error {
5049
}
5150
}()
5251

53-
fmt.Println("🚨 Portr inspector running on http://localhost:7777")
54-
5552
signalCh := make(chan os.Signal, 1)
5653
signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM)
5754
<-signalCh

tunnel/internal/client/ssh/ssh.go

+18-5
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ func (s *SshClient) logHttpRequest(
356356
// Get tunnel name
357357
tunnelName := s.config.Tunnel.Name
358358
if tunnelName == "" {
359-
tunnelName = fmt.Sprintf(":%d", s.config.Tunnel.Port)
359+
tunnelName = fmt.Sprintf("%d", s.config.Tunnel.Port)
360360
}
361361

362362
// Send log directly to TUI
@@ -442,12 +442,25 @@ func (s *SshClient) Start(ctx context.Context) {
442442
// Wait for either an error or successful connection
443443
select {
444444
case err := <-errChan:
445-
// Update TUI with error and exit
445+
// Update TUI with error and wait for it to quit
446446
s.tui.Send(tui.ErrorMsg{Error: err})
447-
time.Sleep(time.Second) // Give TUI time to show error
448-
os.Exit(1)
449-
case <-time.After(5 * time.Second):
450447

448+
// Wait for TUI to quit
449+
done := make(chan struct{})
450+
go func() {
451+
s.tui.Wait()
452+
close(done)
453+
}()
454+
455+
// Wait for either context cancellation or TUI to quit
456+
select {
457+
case <-ctx.Done():
458+
os.Exit(1)
459+
case <-done:
460+
os.Exit(1)
461+
}
462+
463+
case <-time.After(5 * time.Second):
451464
// Start the health check routine for http connections
452465
if s.config.Tunnel.Type == constants.Http {
453466
s.StartHealthCheck(ctx)

tunnel/internal/client/tui/tui.go

+107-36
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ type ErrorMsg struct {
1616
Error error
1717
}
1818

19+
type QuitMsg struct{}
20+
1921
type AddTunnelMsg struct {
2022
Config *config.Tunnel
2123
ClientConfig *config.ClientConfig
@@ -91,33 +93,42 @@ type model struct {
9193
}
9294

9395
func New(debug bool) *tea.Program {
94-
// Regular table setup
96+
// Initial default widths
97+
const (
98+
timeWidth = 12
99+
tunnelWidth = 15
100+
methodWidth = 8
101+
statusWidth = 8
102+
urlWidth = 50
103+
)
104+
105+
// Regular table setup with minimum widths
95106
columns := []table.Column{
96-
{Title: "Time", Width: 12},
97-
{Title: "Tunnel", Width: 15},
98-
{Title: "Method", Width: 8},
99-
{Title: "Status", Width: 8},
100-
{Title: "URL", Width: 50},
107+
{Title: "Time", Width: timeWidth},
108+
{Title: "Tunnel", Width: tunnelWidth},
109+
{Title: "Method", Width: methodWidth},
110+
{Title: "Status", Width: statusWidth},
111+
{Title: "URL", Width: urlWidth},
101112
}
102113

103114
t := table.New(
104115
table.WithColumns(columns),
105116
table.WithFocused(true),
106-
table.WithHeight(15),
117+
table.WithHeight(10), // Reduced default height
107118
)
108119

109-
// Debug table setup
120+
// Debug table setup with minimum widths
110121
debugColumns := []table.Column{
111-
{Title: "Time", Width: 12},
112-
{Title: "Level", Width: 8},
113-
{Title: "Message", Width: 50},
114-
{Title: "Error", Width: 30},
122+
{Title: "Time", Width: timeWidth},
123+
{Title: "Level", Width: methodWidth},
124+
{Title: "Message", Width: 30},
125+
{Title: "Error", Width: 20},
115126
}
116127

117128
dt := table.New(
118129
table.WithColumns(debugColumns),
119130
table.WithFocused(false),
120-
table.WithHeight(10),
131+
table.WithHeight(6), // Reduced default height
121132
)
122133

123134
// Set styles for both tables
@@ -127,10 +138,6 @@ func New(debug bool) *tea.Program {
127138
BorderForeground(lipgloss.Color("240")).
128139
BorderBottom(true).
129140
Bold(false)
130-
s.Selected = s.Selected.
131-
Foreground(lipgloss.Color("229")).
132-
Background(lipgloss.Color("57")).
133-
Bold(false)
134141

135142
t.SetStyles(s)
136143
dt.SetStyles(s)
@@ -164,29 +171,24 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
164171

165172
switch msg := msg.(type) {
166173
case tea.KeyMsg:
174+
if m.err != nil {
175+
// Any key press when there's an error will quit
176+
m.quitting = true
177+
return m, tea.Quit
178+
}
179+
167180
switch msg.String() {
168181
case "q", "ctrl+c":
169182
m.quitting = true
170183
return m, tea.Quit
171-
172-
case "tab":
173-
// Cycle through tunnels
174-
var ports []string
175-
for port := range m.tunnels {
176-
ports = append(ports, port)
177-
}
178-
if len(ports) > 0 {
179-
for i, port := range ports {
180-
if port == m.selected {
181-
m.selected = ports[(i+1)%len(ports)]
182-
break
183-
}
184-
}
185-
}
186184
}
187185

188186
case ErrorMsg:
189187
m.err = msg.Error
188+
// Don't quit immediately, let the user see the error
189+
return m, nil
190+
191+
case QuitMsg:
190192
m.quitting = true
191193
return m, tea.Quit
192194

@@ -207,8 +209,54 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
207209
}
208210

209211
case tea.WindowSizeMsg:
210-
m.table.SetWidth(msg.Width - 4)
211-
m.debugTable.SetWidth(msg.Width - 4)
212+
// Calculate dynamic widths based on terminal size
213+
totalWidth := msg.Width - 4 // Account for margins
214+
215+
// Adjust table heights based on terminal height
216+
tableHeight := (msg.Height - 15) / 2 // Account for headers and other UI elements
217+
tableHeight = max(tableHeight, 5)
218+
219+
m.table.SetHeight(tableHeight)
220+
221+
if m.debug {
222+
m.debugTable.SetHeight(tableHeight / 2)
223+
}
224+
225+
// Adjust URL column width to fill remaining space
226+
timeWidth := 12
227+
tunnelWidth := 15
228+
methodWidth := 8
229+
statusWidth := 8
230+
urlWidth := totalWidth - (timeWidth + tunnelWidth + methodWidth + statusWidth + 5)
231+
232+
urlWidth = max(urlWidth, 20)
233+
234+
// Update main table columns
235+
cols := []table.Column{
236+
{Title: "Time", Width: timeWidth},
237+
{Title: "Tunnel", Width: tunnelWidth},
238+
{Title: "Method", Width: methodWidth},
239+
{Title: "Status", Width: statusWidth},
240+
{Title: "URL", Width: urlWidth},
241+
}
242+
m.table.SetColumns(cols)
243+
244+
// Update debug table columns if debug is enabled
245+
if m.debug {
246+
debugWidth := totalWidth / 2
247+
debugWidth = max(debugWidth, 40)
248+
249+
debugCols := []table.Column{
250+
{Title: "Time", Width: timeWidth},
251+
{Title: "Level", Width: methodWidth},
252+
{Title: "Message", Width: debugWidth / 2},
253+
{Title: "Error", Width: debugWidth / 2},
254+
}
255+
m.debugTable.SetColumns(debugCols)
256+
}
257+
258+
m.table.SetWidth(totalWidth)
259+
m.debugTable.SetWidth(totalWidth)
212260
return m, nil
213261

214262
case tickMsg:
@@ -231,11 +279,16 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
231279
func (m model) View() string {
232280
if m.quitting {
233281
if m.err != nil {
234-
return errorStyle.Render(fmt.Sprintf("Error: %v\n", m.err))
282+
return "\n" + errorStyle.Render(fmt.Sprintf("Error: %v", m.err)) + "\n"
235283
}
236284
return "Goodbye!\n"
237285
}
238286

287+
if m.err != nil {
288+
return "\n" + errorStyle.Render(fmt.Sprintf("Error: %v", m.err)) + "\n\n" +
289+
subtitleStyle.Render("Press any key to exit...") + "\n"
290+
}
291+
239292
var s string
240293

241294
s += titleStyle.Render("🌍 Portr Tunnel Dashboard") + "\n"
@@ -300,12 +353,20 @@ func (m model) View() string {
300353
}
301354
s += "\n"
302355

303-
// Just render the table - no need to query DB
356+
// Add waiting message if no logs
357+
if len(m.table.Rows()) == 0 {
358+
// Create empty table with just headers
359+
m.table.SetRows([]table.Row{{"", "", "", "", "Waiting for logs..."}})
360+
}
304361
s += tableStyle.Render(m.table.View()) + "\n"
305362

306363
// Show debug table if debug mode is enabled
307364
if m.debug {
308365
s += "\n" + titleStyle.Render("🔍 Debug Logs") + "\n"
366+
if len(m.debugTable.Rows()) == 0 {
367+
// Create empty debug table with just headers
368+
m.debugTable.SetRows([]table.Row{{"", "", "Waiting for logs...", ""}})
369+
}
309370
s += tableStyle.Render(m.debugTable.View()) + "\n"
310371
}
311372

@@ -317,6 +378,11 @@ func (m model) View() string {
317378
}
318379

319380
func (m *model) AddLog(msg AddLogMsg) {
381+
// Clear waiting message if it exists
382+
if len(m.table.Rows()) == 1 && m.table.Rows()[0][4] == "Waiting for logs..." {
383+
m.table.SetRows([]table.Row{})
384+
}
385+
320386
rows := []table.Row{{
321387
msg.Time,
322388
msg.Name,
@@ -342,6 +408,11 @@ func (m *model) AddDebugLog(msg AddDebugLogMsg) {
342408
return
343409
}
344410

411+
// Clear waiting message if it exists
412+
if len(m.debugTable.Rows()) == 1 && m.debugTable.Rows()[0][2] == "Waiting for logs..." {
413+
m.debugTable.SetRows([]table.Row{})
414+
}
415+
345416
rows := []table.Row{{
346417
msg.Time,
347418
msg.Level,

0 commit comments

Comments
 (0)