Documentation
¶
Overview ¶
Package shell holds the pure composition/model logic of the desktop shell: the launchable-app index, MIME "open with" resolution, the categorized application-menu model, the directory listing model and the thumbnail cache-key derivation. None of it touches a rendering surface, so every branch is unit-coverable against temp-dir fixtures; the render package turns these models into go-widgets widgets.
Index ¶
- Constants
- Variables
- func HumanBytes(n int64) string
- func IsImageName(name string) bool
- func MimeByExt(name string) string
- func MoveFile(src, destDir string) (string, error)
- func Thumbnailable(it FileItem) bool
- type App
- type AppIndex
- type AppSource
- type Category
- type Dir
- type FileItem
- type MenuModel
- type OpenWith
- type Place
- type PlaceKind
- type Places
- type Resolver
- type Thumbnailer
Constants ¶
const MimeDirectory = "inode/directory"
MimeDirectory is the MIME type the shell assigns to directory entries.
Variables ¶
var ( // ErrMoveIntoSelf is returned when src already lives directly inside // destDir, so the move would be a no-op. ErrMoveIntoSelf = errors.New("move: source is already in the destination") // ErrMoveExists is returned when an entry of the same name already exists in // destDir: the move is refused rather than silently overwriting it. ErrMoveExists = errors.New("move: an item with that name already exists") // ErrMoveNotDir is returned when destDir exists but is not a directory. ErrMoveNotDir = errors.New("move: destination is not a directory") )
Move refusals: MoveFile returns one of these sentinel errors when it declines a move for a structural reason (as opposed to an OS error it passes through).
Functions ¶
func HumanBytes ¶ added in v0.11.0
HumanBytes formats a byte count as a compact human-readable string using French unit abbreviations (o, Ko, Mo, Go, To) and decimal (1000) steps, the convention macOS/Finder uses.
func IsImageName ¶ added in v0.10.0
IsImageName reports whether name has a known image file extension. It is the single source of truth for extension-based image detection, shared by the thumbnail-eligibility policy here and the render layer's picture-glyph fallback, so the two never drift.
func MimeByExt ¶ added in v0.11.0
MimeByExt maps a file name's extension to a MIME type by a small built-in table, or "" when the extension is not recognised. It covers the common image/video/audio/text/document kinds the file manager needs to pick an icon and a Type label; it never touches the filesystem.
func MoveFile ¶ added in v0.12.0
MoveFile moves the file (or directory) at src into destDir, preserving src's base name, and returns the final destination path.
On the same volume it is a single os.Rename; when the rename reports a cross-device error (EXDEV) it falls back to copying src's bytes into destDir and removing the original. It refuses, without touching the filesystem:
- a no-op — src is already directly inside destDir (ErrMoveIntoSelf);
- a name collision — destDir already holds an entry of src's base name (ErrMoveExists), so an existing file is never silently overwritten;
- a destDir that exists but is not a directory (ErrMoveNotDir).
Any other filesystem error (a missing destDir, a permission denial on the rename/copy/remove, a source that cannot be read) is returned unwrapped so the caller can surface it to the user — MoveFile never panics on one.
func Thumbnailable ¶
Thumbnailable reports whether a file item should be given an image preview: a regular file that is an image, recognised either by its classified MIME type (image/*) or, when MIME classification is unavailable or weak, by a known image file extension — so a macOS .png classified as application/octet-stream is still thumbnailed. Directories and non-image files are never thumbnailed.
Types ¶
type App ¶
type App struct {
ID string
Name string
GenericName string
Comment string
Icon string
Exec string
Categories []string
Keywords []string
Terminal bool
// contains filtered or unexported fields
}
App is one launchable application, distilled from a desktop entry into the fields a dock / launcher needs. The originating entry is retained so the launcher can expand its Exec line at click time.
func (App) Entry ¶
func (a App) Entry() *desktopentry.Entry
Entry returns the desktop entry this App was built from (nil for an App that was not derived from one). The launcher passes it to desktopentry.ExpandExec.
type AppIndex ¶
type AppIndex struct {
// contains filtered or unexported fields
}
AppIndex is a sorted, de-duplicated, searchable set of launchable apps.
func NewAppIndex ¶
func NewAppIndex(entries []*desktopentry.Entry) *AppIndex
NewAppIndex builds an index from scanned desktop entries. Non-launchable entries (wrong Type, no Exec) are dropped; the first entry seen for a given non-empty id wins (desktopentry.Scan already applies XDG directory precedence, so "first" means "highest priority"). Apps are ordered case-insensitively by their display label.
type AppSource ¶ added in v0.4.0
type AppSource interface {
// Apps is the sorted, de-duplicated, searchable launchable-app index that
// feeds the dock and launcher.
Apps() *AppIndex
// Menu is the flattened, ordered application menu.
Menu() *MenuModel
// Dir is the current directory listing, already MIME-classified, that
// feeds the file grid. It may be nil when no directory is available.
Dir() *Dir
// Resolve answers the "open with" query for a bare file name plus optional
// leading content bytes: the classified MIME type, its default application
// and the ordered candidate applications.
Resolve(name string, content []byte) OpenWith
// IconBytes returns the encoded image bytes (PNG/JPEG/GIF) for an icon
// referenced by theme name (e.g. "web-browser") or by absolute path (e.g.
// a thumbnail), and ok=false when it cannot be resolved. The render layer
// decodes the bytes into a go-widgets Image (falling back to a placeholder
// swatch on ok=false), so this is the single icon-pixel seam a source must
// satisfy — no source touches the toolkit.
IconBytes(name string) ([]byte, bool)
// ThumbKey returns the IconBytes key of a file item's thumbnail — an
// absolute cache path for the native source, a virtual asset name for the
// embedded one — or "" when the item is not thumbnailed.
ThumbKey(it FileItem) string
}
AppSource is the data source behind the desktop shell: everything the UI needs to render a populated desktop, abstracted away from where it comes from. It supplies the launchable-app index, the categorized application menu, the current directory listing (already MIME-classified), the "open with" resolution for a file, the encoded image bytes for an icon (by theme name or absolute path), and — for a file item — the icon-loader key of its thumbnail (or "" when it has none).
The two implementations live in github.com/go-widgets/desktop/source:
- the native xdgSource scans a real XDG filesystem (desktopentry.Scan + icontheme + mime/mimeapps + menu.Load + go-thumbnail), exactly the behavior the shell has always had; and
- the embeddedSource serves a curated set from an embed.FS, so the shell renders a real, populated desktop in the browser (js/wasm), where no real filesystem exists.
Both drive the identical shell/render composition logic — the whole point of the seam — so a Scene built from either is indistinguishable to the toolkit.
type Category ¶
Category is one branch of the application menu: a user-visible directory name (plus its icon) and the launchable apps allocated to it.
type Dir ¶
Dir is a listed directory: its path and its items, directories first then files, each group ordered case-insensitively by name.
func ListDir ¶
ListDir lists path into a Dir. Hidden entries (dotfiles: names beginning with ".", e.g. .ssh, .cache, .Trash) are skipped, so the file grid shows a clean user-facing directory rather than a wall of dotfile clutter. It does not classify MIME types (call Classify for that); a read failure (missing / unreadable directory) is returned as an error.
func (*Dir) Classify ¶
Classify fills each item's Mime using r: directories get MimeDirectory, regular files are classified by name+content. A file that cannot be resolved (e.g. it became unreadable) is left with an empty Mime rather than aborting the whole listing.
func (*Dir) ClassifyLite ¶ added in v0.11.0
func (d *Dir) ClassifyLite()
ClassifyLite fills each item's Mime from its name/extension only, without the heavy content-sniffing Resolver — the portable classifier the file manager uses while navigating (a Resolver needs an XDG MIME database that does not exist on macOS/Windows or in the browser). Directories get MimeDirectory; regular files get an extension-derived MIME (or "" when the extension is unknown, which the Type column renders from the extension itself).
type FileItem ¶
type FileItem struct {
Name string
Path string
IsDir bool
Mime string
Size int64 // byte size of a regular file (0 for directories)
ModTime time.Time // last-modification time (zero when unavailable)
}
FileItem is one entry of a listed directory.
func (FileItem) HumanSize ¶ added in v0.11.0
HumanSize renders the item's byte size for the list view: an em dash for a directory (Finder shows "--" there), otherwise a compact human-readable size.
func (FileItem) IsImage ¶ added in v0.11.0
IsImage reports whether the item is a raster image the file manager can thumbnail (by MIME class when classified, else by name extension).
func (FileItem) ModTimeString ¶ added in v0.11.0
ModTimeString formats the item's modification time for the list view's Date column ("--" when the time is unavailable).
type MenuModel ¶
type MenuModel struct {
Categories []Category
}
MenuModel is the desktop application menu flattened into an ordered list of non-empty categories, ready to render as a go-widgets Menu / MenuBar. Nested submenus are walked depth-first, so a category appears once per menu node that carries apps, in menu (layout) order.
func NewMenuModel ¶
NewMenuModel flattens a resolved menu.Tree. A nil tree (or a tree with a nil Root) yields an empty model. Only menu nodes that actually contain apps become categories; a purely structural submenu with no direct apps is skipped but still descended into.
type OpenWith ¶
OpenWith is the resolved "open with" answer for a path or MIME type: the classified MIME type, the default application (if any) and the ordered list of candidate applications.
type Place ¶ added in v0.11.0
Place is one sidebar row: a display label, the directory it navigates to (empty when the kind is not navigable, e.g. the network) and its kind.
type PlaceKind ¶ added in v0.11.0
type PlaceKind int
PlaceKind classifies a sidebar entry so the render layer can pick an icon and so navigation can special-case the non-directory kinds (the network location has no path and shows an honest empty state).
const ( // PlaceFolder is an ordinary navigable directory (a Favoris entry). PlaceFolder PlaceKind = iota // PlaceApplications is the applications folder (a Favoris entry). PlaceApplications // PlacePictures / PlaceMovies / PlaceMusic / PlaceDownloads are the media // Favoris entries, split out so each gets a distinct sidebar glyph. PlacePictures PlaceMovies PlaceMusic PlaceDownloads PlaceDesktop PlaceDocuments // PlaceHome is the user's home directory (an Emplacements entry). PlaceHome // PlaceVolume is the startup volume / root drive (an Emplacements entry). PlaceVolume // PlaceNetwork is the network location: there is no real network browsing // in a pure-Go shell, so it navigates to an honest empty "Aucun partage" // state rather than a fake listing. Its Path is "". PlaceNetwork // PlaceTrash is the trash / recycle bin (an Emplacements entry). PlaceTrash )
type Places ¶ added in v0.11.0
Places is the file manager's sidebar model: the two labelled sections a macOS Finder shows — Favoris (user folders) and Emplacements (home, the startup volume, the network and the trash).
func DefaultPlaces ¶ added in v0.11.0
func DefaultPlaces() *Places
DefaultPlaces resolves the sidebar model for the running OS: real per-user paths on macOS (~/Desktop, ~/Documents, /Applications, ~/Pictures, ~/Movies, ~/Music, ~/Downloads; "Macintosh HD" -> /, ~/.Trash), the equivalent XDG user-dirs on Linux, and the known folders on Windows. When the home directory cannot be resolved (notably the js/wasm browser build, which has no real filesystem) it returns a minimal, non-crashing model so the sidebar still renders and the shell still builds.
type Resolver ¶
type Resolver struct {
// contains filtered or unexported fields
}
Resolver answers MIME classification and application-association queries by combining a shared MIME-info database with a mimeapps.list resolver.
func NewResolver ¶
NewResolver wires a MIME database and an application-association resolver into a Resolver.
func (*Resolver) ResolveName ¶
ResolveName classifies a bare name plus optional content bytes and resolves the associated applications. It performs no I/O, so it is the pure seam the path-based helper builds on.
func (*Resolver) ResolvePath ¶
ResolvePath classifies path by both name and content and resolves its associated applications. Directories (and other non-regular paths) are classified by name only. A stat failure (e.g. the path does not exist) or a content-read failure is returned as an error.
type Thumbnailer ¶
type Thumbnailer struct {
// contains filtered or unexported fields
}
Thumbnailer derives the freedesktop Thumbnail Managing Standard cache keys and paths for a file grid. It delegates the URI canonicalization, MD5 hash, cache-path layout and the actual decode+downscale+cache to github.com/go-thumbnail/thumbnail (the owner of that mechanism) and adds only the shell's policy: which items are thumbnailed and under which stable key.
func NewThumbnailer ¶
func NewThumbnailer(size thumbnail.Size) *Thumbnailer
NewThumbnailer builds a Thumbnailer for the given standard thumbnail size.
func (*Thumbnailer) Ensure ¶ added in v0.10.0
func (t *Thumbnailer) Ensure(it FileItem) string
Ensure generates (or refreshes) the item's thumbnail through the go-thumbnail cache — decode the source image, downscale it into the standard size bucket and write the freedesktop-keyed PNG under $XDG_CACHE_HOME/thumbnails — and returns its on-disk cache path (the IconBytes key the render layer decodes), or "" when the item is not thumbnailable or the thumbnail could not be produced (an unreadable or undecodable source). The outcome is memoized per source path, positive and negative, so a repeated call is a map lookup.
func (*Thumbnailer) Key ¶
func (t *Thumbnailer) Key(it FileItem) string
Key returns the stable cache key (the MD5 hash of the file's canonical file:// URI) for a thumbnailable item, or "" when the item is not thumbnailable. Two items with the same path always produce the same key.
func (*Thumbnailer) Path ¶
func (t *Thumbnailer) Path(it FileItem) string
Path returns the on-disk cache path where the item's thumbnail lives (or would be generated), or "" for a non-thumbnailable item.