Documentation
¶
Overview ¶
Package fsbrowser renders any io/fs.FS as a file browser: one directory at a time as a sortable list, or the tree below the current directory as an outline, with a breadcrumb above, a quick filter, a selection, a keyboard cursor, and host-supplied columns beside name, size and modification time (ADR-0200 §SD2).
What the host owns ¶
The host owns the State — current directory, filter, sort, selection, cursor, the directory cache and the outline's expansion — and the io/fs.FS the browser reads. The widget reads directories on demand through io/fs.ReadDir and caches each listing under the host's Input.CacheKey; a host whose tree cannot change under it (a lading snapshot) keeps the key fixed and never pays a read twice, a host over a live tree calls State.Invalidate when it wants a re-read. A change of CacheKey drops the cache and the selection and keeps the directory, so two snapshots of one mount are browsed at the same path — synchronized browsing is the default the location model gives for free.
What the widget decides, and what it reports ¶
A click selects (ctrl toggles, shift extends from the cursor), a double click or Enter *activates*: a directory is entered (list mode) or toggled (outline mode), a file is reported in Result.Activated for the host to preview or open. Backspace goes up a directory; the arrows, Home, End, Page Up and Page Down move the cursor and the selection with it. Nothing here writes: the browser has no notion of copy, move, rename or delete, which is what makes it safe over a read-only store and over a capability grant.
Modes ¶
ModeList shows the current directory's children in an etable — directories first, then files — and is the mode a file-transfer client opens in. ModeOutline shows the tree under the current directory through widgets/tree (ADR-0176): children load when a node is opened and never before, an unread directory shows a disclosure control because it might have children, and an empty one turns into a leaf once read. Both modes share the State, the columns and the cursor.
Index ¶
- Constants
- Variables
- func MinColumnWidth(density styletokens.DensityE) float32
- type Column
- type Entry
- type Input
- type ModeE
- type Result
- type SortByE
- type State
- func (st *State) ClearSelection()
- func (st *State) Cursor() string
- func (st *State) Dir() string
- func (st *State) Filter() string
- func (st *State) Invalidate()
- func (st *State) IsSelected(p string) bool
- func (st *State) Select(p string, on bool)
- func (st *State) SelectOnly(p string)
- func (st *State) Selection() (paths []string)
- func (st *State) SetCursor(p string)
- func (st *State) SetDir(dir string)
- func (st *State) SetFilter(s string)
- func (st *State) SetSort(by SortByE, desc bool)
- func (st *State) Sort() (by SortByE, desc bool)
- func (st *State) Up() bool
Constants ¶
const ( // MaxColumnWidth bounds a drag and a stored width. MaxColumnWidth float32 = 1200 )
Column-width persistence (ADR-0151). The widget keys its columns by what they show — name, size, modified, then the host's — with the view appended to the type, so a width fitted to the list does not reach the outline, whose name column carries the indent and the disclosure control.
Variables ¶
var PackageProps = packageprops.Props{ WASMWASI: packageprops.WASMBlocked, WASMJS: packageprops.WASMBlocked, WASMFreestanding: packageprops.WASMBlocked, }
PackageProps records this package's curated properties (ADR-0080). Seeded by `boxer code analysis golang wasmsurvey props generate`; curate by hand. The same group's `props verify` reconciles it.
Functions ¶
func MinColumnWidth ¶
func MinColumnWidth(density styletokens.DensityE) float32
MinColumnWidth is the drag floor for the density: content plus both cell insets. A host building a colwidth.Resolver for this widget passes the same number as Opts.MinPoints, so a column cannot be dragged below what will come back on the next load.
Types ¶
type Column ¶
type Column struct {
Header string
Width float32
Resizable bool
// Cell draws the cell for e. Emit labels Selectable(false): the row's
// click sense sits behind its cells, and a selectable label would take
// the click.
Cell func(e Entry)
// WidthType discriminates this column for width persistence (ADR-0151
// keys the column tier on name and render type); empty means "host".
WidthType string
}
Column is one host-supplied column beside the built-in three.
type Entry ¶
type Entry struct {
// Name is the base name; Path the io/fs path from the root ("a/b/c").
Name string
Path string
// IsDir and IsSymlink come off the entry's type bits — a symlink is
// reported as recorded, not resolved, which is the Lstat reading an
// [fs.ReadDirFS] gives.
IsDir bool
IsSymlink bool
Size int64
ModTime time.Time
Mode fs.FileMode
// InfoErr is the error [fs.DirEntry.Info] returned, when it did; the entry
// still lists, with zero size and time, because a name that cannot be
// stat'ed is still a name.
InfoErr error
// Ord is the entry's ordinal in its directory's listing, stable for as
// long as the listing is cached. Widget ids key on it, so sorting and
// filtering move rows without moving identities.
Ord int
}
Entry is one directory child as the browser shows it, read from the fs.DirEntry and its fs.FileInfo once per listing.
type Input ¶
type Input struct {
Ids *c.WidgetIdStack
ScopeKey string
// FS is the tree to browse. nil renders a placeholder.
FS fs.FS
// RootLabel names the root in the breadcrumb ("/" when empty).
RootLabel string
// CacheKey scopes the directory cache; a change drops it and the
// selection and keeps the directory.
CacheKey string
State *State
Mode ModeE
Columns []Column
// ShowHidden includes dot-names.
ShowHidden bool
// RowHeight, MaxHeight and Striped are passed to the table; zero means
// the defaults (one text line; fill; plain).
RowHeight float32
MaxHeight float32
Striped bool
// HideBreadcrumb and HideFilter drop those rows when the host draws its
// own location and filter chrome.
HideBreadcrumb bool
HideFilter bool
// Widths persists the columns' widths (ADR-0151): the widget resolves
// them through the resolver before the table is emitted and reports the
// table's settled widths back after, under WidthTag (ScopeKey when
// empty). The host flushes the resolver once per frame. nil keeps the
// defaults and persists nothing.
Widths *colwidth.Resolver
WidthTag string
}
Input is one frame's rendering request.
type Result ¶
type Result struct {
// Rows is what was shown: the filtered, sorted listing in list mode, one
// entry per outline node in outline mode. Indices below refer to it. The
// slice is the State's scratch and is valid until the next Render; a host
// that keeps entries copies them.
Rows []Entry
// Clicked is the row clicked this frame, -1 for none.
Clicked int
// Activated is the file row double-clicked or Enter-ed, -1 for none. A
// directory activation is consumed by the widget (entered or toggled)
// and reported as Navigated instead.
Activated int
Navigated bool
// SelectionChanged is true when the selection or cursor moved.
SelectionChanged bool
Err error
}
Result is what one Render reports.
type SortByE ¶
type SortByE uint8
SortByE is the column a listing is ordered by. Directories always sort before files whatever the column; the column orders within each group.
type State ¶
type State struct {
// contains filtered or unexported fields
}
State is the host-owned view state. Its zero value is a browser at the root with no filter, sorted by name, nothing selected.
func (*State) ClearSelection ¶
func (st *State) ClearSelection()
func (*State) Invalidate ¶
func (st *State) Invalidate()
Invalidate drops every cached listing, so the next frame re-reads what it shows. The selection and the directory stay.
func (*State) IsSelected ¶
IsSelected reports whether p is selected.
func (*State) Select ¶
Select sets p's selection; SelectOnly makes p the whole selection and the cursor; ClearSelection empties it.
func (*State) SelectOnly ¶
func (*State) SetDir ¶
SetDir changes the current directory and clears the selection and the cursor — selection is per directory, as in every file manager. The path is cleaned; "" and "/" mean the root. It is not checked against the tree: a directory that does not exist lists as an error row.