-
Notifications
You must be signed in to change notification settings - Fork 9
v4.1.9 #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
v4.1.9 #2
Conversation
Add SSH/multi-session support without exposing tcell types. - InitWithConfig() accepts io.ReadWriter for custom TTY - PollEventsWithContext() fixes goroutine leaks - Fully backward compatible - Simplifies SSH example from 28 to 10 lines
Refactored core rendering and event logic to support multiple independent Backend instances, enabling true multi-user and SSH session support. Updated the SSH dashboard example to use per-session backends, removed global state reliance, and improved documentation for SSH usage. Added new dependencies for SSH support and updated .gitignore for SSH-related files.
Updated Init and InitWithConfig to assign Screen and ScreenshotMode from DefaultBackend after initialization. Also added a nil check for Screen in Backend.Render to prevent rendering when the screen is uninitialized. Updated dependencies in go.mod and go.sum.
InitWithConfig now checks if the provided config is nil and falls back to the default Init method. This prevents potential nil pointer dereference errors when no configuration is supplied.
Add SSH/multi-session support without exposing tcell types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR (v4.1.9) introduces a significant architectural refactoring to support multi-tenant TUI applications, particularly for SSH-based deployments. The main change is transitioning from global state (Screen variable) to instance-based Backend objects, enabling multiple isolated terminal sessions to run concurrently.
Key changes:
- Refactored Backend architecture to support instance-based screens instead of global state
- Added context-aware event polling with
PollEventsWithContextfor graceful cancellation - Introduced SSH/custom TTY support through
InitConfigandTTYHandleinterface - Updated dependencies (tcell v2.13.4, go-runewidth v0.0.19, golang.org/x packages)
Reviewed changes
Copilot reviewed 7 out of 9 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| backend.go | Major refactoring: introduces Backend struct, InitConfig for custom TTY, and ttyAdapter for SSH support |
| render.go | Converts Render function to Backend method while maintaining global compatibility |
| events.go | Adds Backend.PollEvents methods and context-aware PollEventsWithContext for proper cleanup |
| go.mod | Updates dependencies and Go version to 1.24.0 |
| go.sum | Reflects updated dependency checksums |
| _examples/ssh-dashboard/main.go | New complete example demonstrating multi-user SSH dashboard |
| _examples/ssh-dashboard/README.md | Documentation for the SSH dashboard example |
| README.md | Adds SSH/remote apps feature documentation and usage examples |
| .gitignore | Adds SSH-related files to ignore list |
Comments suppressed due to low confidence (1)
render.go:61
- Lines 59-61 show dead code where width and height are calculated but immediately overwritten. The calculation on line 59 (1024/7, 768/13) appears to be leftover code that should be removed for clarity.
width, height := 1024/7, 768/13 // approx 146x59
// Or 120x40
width, height = 120, 60
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| func newSessionTTY(sess ssh.Session) (*sessionTTY, error) { | ||
| pty, winCh, ok := sess.Pty() | ||
| if !ok { | ||
| return nil, fmt.Errorf("no PTY requested (try: ssh -tt host -p 2222)") |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error message suggests using "ssh -tt host -p 2222" but 'host' is not a valid hostname. It should suggest a concrete example like "ssh -tt localhost -p 2222" or "ssh -tt 0.0.0.0 -p 2222" to match the server address.
| return nil, fmt.Errorf("no PTY requested (try: ssh -tt host -p 2222)") | |
| return nil, fmt.Errorf("no PTY requested (try: ssh -tt localhost -p 2222)") |
|
|
||
| ```bash | ||
| $ ssh-keygen -t ed25519 -f hostkey -N "" # Generate sample host key | ||
| $ go run _examples/stacked_barchart/main.go |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The command references the wrong example path. It should reference ssh-dashboard/main.go instead of stacked_barchart/main.go.
| $ go run _examples/stacked_barchart/main.go | |
| $ go run _examples/ssh-dashboard/main.go |
| | **Radarchart** | <img src="_examples/radarchart/screenshot.png" height="80" /> | [View Example Code](_examples/radarchart/main.go) | | ||
| | **Scrollbar** | <img src="_examples/scrollbar/screenshot.png" height="80" /> | [View Example Code](_examples/scrollbar/main.go) | | ||
| | **Sparkline** | <img src="_examples/sparkline/screenshot.png" height="80" /> | [View Example Code](_examples/sparkline/main.go) | | ||
| | **SSH Dashboard** | <img src="_examples/dashboard/screenshot.png" height="80" /> | [View Example Code](_examples/ssh-dashboard/main.go) | |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The image reference points to _examples/dashboard/screenshot.png but this is for the SSH Dashboard example. The path should likely be _examples/ssh-dashboard/screenshot.png to match the example's actual directory name.
| | **SSH Dashboard** | <img src="_examples/dashboard/screenshot.png" height="80" /> | [View Example Code](_examples/ssh-dashboard/main.go) | | |
| | **SSH Dashboard** | <img src="_examples/ssh-dashboard/screenshot.png" height="80" /> | [View Example Code](_examples/ssh-dashboard/main.go) | |
| for i, arg := range os.Args { | ||
| if arg == "-screenshot" { | ||
| ScreenshotMode = true | ||
| b.ScreenshotMode = true | ||
| // Remove flag so app logic doesn't see it | ||
| os.Args = append(os.Args[:i], os.Args[i+1:]...) |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modifying os.Args during iteration can lead to incorrect behavior if multiple backends are initialized or if the flag appears more than once. After removing an element at index i, the loop continues with i+1 but the slice has shifted. Consider breaking after finding and removing the flag, or iterating backwards.
| @@ -0,0 +1,20 @@ | |||
| # SSH Dashboard Example | |||
|
|
|||
| This example demonstrates the **SSH Dashboard** widget/feature. | |||
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment says "SSH Dashboard widget/feature" but this is not a widget, it's a complete example application demonstrating how to use gotui over SSH. Consider changing to "This example demonstrates serving a gotui dashboard over SSH" or similar to avoid confusion.
| This example demonstrates the **SSH Dashboard** widget/feature. | |
| This example demonstrates serving a gotui dashboard over SSH. |
| app, _ := ui.NewBackend(&ui.InitConfig{ | ||
| CustomTTY: sess, // ssh.Session implements io.ReadWriter | ||
| }) |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code example shows error handling with _ (ignored errors) which is not a good practice to demonstrate in documentation. Consider showing proper error handling with return or log.Fatal to set a better example for users.
| app, _ := ui.NewBackend(&ui.InitConfig{ | |
| CustomTTY: sess, // ssh.Session implements io.ReadWriter | |
| }) | |
| app, err := ui.NewBackend(&ui.InitConfig{ | |
| CustomTTY: sess, // ssh.Session implements io.ReadWriter | |
| }) | |
| if err != nil { | |
| // handle error appropriately, e.g., log or return | |
| return | |
| } |
| go func() { | ||
| for { | ||
| select { | ||
| case <-t.closed: | ||
| return | ||
| case win, ok := <-t.winCh: | ||
| if !ok { | ||
| return | ||
| } | ||
| t.mu.Lock() | ||
| t.w, t.h = win.Width, win.Height | ||
| cb := t.resizeCb | ||
| t.mu.Unlock() | ||
| if cb != nil { | ||
| cb() | ||
| } | ||
| } | ||
| } | ||
| }() |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The goroutine started in newSessionTTY (lines 44-62) can leak if winCh is never closed and the closed channel is not signaled. While the Close method does close the channel, if Close is not called (e.g., if newSessionTTY returns an error after creating the channel but before returning), the goroutine will leak. Consider starting the goroutine after all initialization is complete and the sessionTTY is ready to be returned, or ensure the closed channel is always properly signaled even in error paths.
Note
Introduce a multi-instance Backend with CustomTTY (SSH-ready), context-aware event polling, updated render/clear APIs, and a new SSH dashboard example with docs; bump deps.
Backendtype withDefaultBackendand config-drivenInitWithConfig(CustomTTY,SimulationMode, size).TTYHandleandttyAdapterto bind custom I/O (e.g., SSH) totcell.Init,Close,Render,Clear, etc.) delegating toDefaultBackend; keep legacyScreen/ScreenshotModevars.Backend.PollEventsandPollEventsWithContext(ctx); implement context-cancellable polling viatcell.EventInterrupt.Renderto instance method (Backend.Render); respect per-backendScreenshotMode._examples/ssh-dashboard/showcasing multi-user SSH TUI usinggliderlabs/sshand per-session backends.tcelltov2.13.4,go-runewidthtov0.0.19; addgolang.org/x/image; new indirectuax29..gitignorefor SSH host keys and artifacts.Written by Cursor Bugbot for commit 9e6c0eb. This will update automatically on new commits. Configure here.