Help us shape the future of Go! Take the 2026 Go Developer Survey

ui

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 24, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Overview

Package ui renders the interactive host browser and hands the terminal over to an SSH session when a host is chosen.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func HumanBytes

func HumanBytes(n int64) string

HumanBytes renders a byte count the way a person reads it. The steps are the 1024 the sizes of files are actually counted in, labelled the way they are usually written.

func RunHostBrowser

func RunHostBrowser(opts BrowserOptions) error

RunHostBrowser shows the host list and blocks until the user quits. Selecting a host releases the terminal, runs an SSH session, then returns to the list.

func TransferEnds

func TransferEnds(direction sshclient.Direction, local, remote string) (from, to string)

TransferEnds orders the two ends of a transfer the way the bytes travel, so a summary reads forwards whichever direction it went. Both front ends quote the ends through here, which is what keeps them saying the same thing.

Types

type AddHostFunc

type AddHostFunc func(ctx context.Context, host config.NewHost) ([]config.SSHHost, error)

AddHostFunc writes a host the user filled in and returns the hosts as they read afterwards, so the browser can show the new one without reading the config again. Where the host is written and what counts as a clash are the service's business; the fields are the form's.

type BrowserOptions

type BrowserOptions struct {
	Hosts []config.SSHHost
	// Session runs the session behind a selected host.
	Session SessionFunc
	// Remote opens the file view's session on the host under the cursor, which
	// the U and D keys show. A nil Remote leaves those keys saying so.
	Remote RemoteSessionFunc
	// Add writes a host typed into the form, which the A key shows. A nil Add
	// leaves that key saying so.
	Add AddHostFunc
	// Remove deletes a host from the list, which the X key asks about. A nil
	// Remove leaves that key saying so.
	Remove RemoveHostFunc
	// HostsFile is the file Add writes. The form shows it before it writes there,
	// so that a host the user cannot then find is at least a host the browser
	// said where it would put.
	HostsFile string
	// Filter seeds the search box.
	Filter string
}

BrowserOptions configures the host browser.

type RemoteEntry

type RemoteEntry struct {
	Name string
	Dir  bool
	Size int64
}

RemoteEntry is one entry the file view's remote pane lists. It is the browser's own shape rather than the protocol's: a pane needs a name, whether it is a directory and how big it is, and nothing else an SFTP listing carries is drawn.

type RemoteSession

type RemoteSession interface {
	// Home is the directory the remote pane opens in: where the login landed,
	// which is the user's home directory. It is read when the session is built
	// rather than asked for here, so that drawing a frame never waits on a
	// round trip.
	Home() string
	// ReadDir lists one remote directory. Paths are the pane's own, so the
	// listing is of exactly the string the pane is showing.
	ReadDir(path string) ([]RemoteEntry, error)
	// Transfer moves one path, reporting progress as it goes. Which way the
	// bytes go is what req.Direction says, as it is everywhere else.
	Transfer(ctx context.Context, req TransferRequest, progress sshclient.ProgressFunc) error
	// Close releases the session and the connection under it.
	Close() error
}

RemoteSession is a connection the file view walks and copies over.

It is opened when the view goes up and closed when it comes down, which is what makes browsing a directory tree one dial rather than one per directory. A transfer the user starts reuses this connection too, so a host with a saved password moves every file of a browse without being asked again.

Transfer is called on a goroutine of the view's own, and ctx is cancelled when the user backs out of it: an implementation must return once ctx is done and must not wait on the terminal, which the view still owns.

type RemoteSessionFunc

type RemoteSessionFunc func(ctx context.Context, host config.SSHHost) (RemoteSession, error)

RemoteSessionFunc opens a session on a host for the file view.

It is the browser's own type rather than the service's, for the same reason SessionFunc is: service imports ui and not the other way round. Passing a nil one leaves the upload and download keys saying that transfers are unavailable, rather than opening a view that could not fill itself.

type RemoveHostFunc

type RemoveHostFunc func(ctx context.Context, host config.SSHHost) ([]config.SSHHost, error)

RemoveHostFunc deletes a host and returns the hosts as they read afterwards, so the browser can take the row off screen without reading the config again. Which hosts may be deleted at all — the file ohmyssh writes, and only that one — is the service's business; asking the user whether they meant this one is the browser's.

type SessionFunc

type SessionFunc func(ctx context.Context, host config.SSHHost) error

SessionFunc runs a session on a host and returns when it ends. The browser calls it only after the terminal has been released for the session; dialling, the command to run and the exit status are all its business, not the browser's.

type TransferProgress

type TransferProgress struct {
	// contains filtered or unexported fields
}

TransferProgress draws a transfer's progress on a writer.

On a terminal it redraws a bar in place, one line for the file being copied and one for the transfer as a whole. Anywhere else — a pipe, a CI log, a file — a stream of cursor moves is unreadable once captured, so it prints one line per completed file instead.

What it writes is dropped on the floor rather than reported: the bar is a convenience, and a terminal that has gone away — a closed pipe, a lost SSH session carrying the output — is not a reason to fail a transfer that is otherwise copying perfectly well.

func NewTransferProgress

func NewTransferProgress(out io.Writer) *TransferProgress

NewTransferProgress returns a reporter that writes to out.

The totals a bar needs are not passed in: they arrive with the first report, which the transfer sends once it has measured its plan.

func (*TransferProgress) Done

func (p *TransferProgress) Done()

Done closes the live display, leaving the cursor on a fresh line so whatever is printed next is not written over the progress bar.

func (*TransferProgress) Update

func (p *TransferProgress) Update(prog sshclient.Progress)

Update draws what the report says. It is safe to call from the copy loop: the work is one formatted line, and a report that changes nothing visible is not redrawn.

type TransferRequest

type TransferRequest struct {
	Local     string
	Remote    string
	Direction sshclient.Direction
}

TransferRequest is one transfer the browser asked for: the two paths and which way the bytes go.

It is the browser's own type rather than the service's, because service imports ui and not the other way round — the same reason SessionFunc exists.

type TransferSummary

type TransferSummary struct {
	Direction sshclient.Direction
	// From and To are the two ends as the user named them.
	From, To string
	Files    int
	Bytes    int64
	// Err, when set, is why the transfer failed. The line then reports that
	// instead of the counts: a transfer that stopped early has no total worth
	// quoting.
	Err error
}

TransferSummary is what there is to say about a transfer that has finished.

func (TransferSummary) Line

func (s TransferSummary) Line() string

Line renders a transfer the way both the command line and the browser say it, so the same operation reads the same in either.

func (TransferSummary) Report

func (s TransferSummary) Report() string

Report is the finished line a command prints for a transfer that worked: Line with the tick and the colour in front of it, both of which belong to the renderer rather than to the wording.

A transfer that failed is not reported this way. Its error goes back up the call chain instead, so the runner turns it into one prefixed line and an exit status rather than the two lines a report of it would produce.

The styles collapse to plain text when the output is not a terminal, which is what keeps a redirected run free of escape codes.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL