v0.1.4 tui uniform transparent background policy, tui example added
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
// fv — file voyager: an lf / midnight-commander / xxd hybrid.
|
||||
// Demo application for the terminal + tui + color packages.
|
||||
//
|
||||
// Layout: [parent dir] [current dir list] [preview: info + text/hex/dir table]
|
||||
// Enter on a file opens a fullscreen hex viewer with byte cursor and search.
|
||||
//
|
||||
// Keys (browse): j/k/arrows move · h/l or ←/→ parent/enter · Enter open
|
||||
//
|
||||
// gg/G top/bottom · PgUp/PgDn, Ctrl+D/U page · ~ home · . hidden · s sort
|
||||
// / live filter (Esc clears) · Space mark · a mark-all · u unmark
|
||||
// y yank paths · r rename · m mkdir · D delete (confirm) · T theme
|
||||
// ? help · Ctrl+L redraw · q quit · Q quit + exec $SHELL in last dir
|
||||
//
|
||||
// Keys (hex): hjkl/arrows · 0/$ row ends · g/G · PgUp/PgDn · Ctrl+D/U
|
||||
//
|
||||
// / search (hex bytes like "de ad" / "0xdead", or literal text) · n/N cycle
|
||||
// Esc/q back
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/lixenwraith/terminal"
|
||||
)
|
||||
|
||||
const tickInterval = 100 * time.Millisecond // 10 fps animation clock
|
||||
|
||||
func main() {
|
||||
term := terminal.New()
|
||||
|
||||
// Raw-mode safety net: restore the terminal before re-panicking.
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
terminal.EmergencyReset(os.Stdout)
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := term.Init(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "terminal init:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
_ = term.SetMouseMode(terminal.MouseModeClick) // wheel + click select
|
||||
|
||||
start, err := os.Getwd()
|
||||
if err != nil {
|
||||
start = "/"
|
||||
}
|
||||
app := newApp(term, start)
|
||||
if err := app.loadDir(start, ""); err != nil {
|
||||
term.Fini()
|
||||
fmt.Fprintln(os.Stderr, "read dir:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Animation clock: the input reader never emits {EventKey, KeyNone}
|
||||
// (unknown sequences are swallowed), so it is a collision-free
|
||||
// synthetic tick marker through the MPSC event channel.
|
||||
tickDone := make(chan struct{})
|
||||
go func() {
|
||||
t := time.NewTicker(tickInterval)
|
||||
defer t.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-tickDone:
|
||||
return
|
||||
case <-t.C:
|
||||
term.PostEvent(terminal.Event{Type: terminal.EventKey, Key: terminal.KeyNone})
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
for !app.quit {
|
||||
app.render()
|
||||
app.handleEvent(term.PollEvent())
|
||||
}
|
||||
close(tickDone)
|
||||
term.Fini()
|
||||
|
||||
// --- cd-on-exit -----------------------------------------------------
|
||||
// A child cannot change the parent shell's cwd. Best scriptless
|
||||
// approximation: chdir + exec a fresh $SHELL (Q). Plain quit (q)
|
||||
// records the dir for optional shell-function integration.
|
||||
last := app.br.cwd
|
||||
_ = os.Chdir(last)
|
||||
if dir, err := os.UserCacheDir(); err == nil {
|
||||
_ = os.WriteFile(filepath.Join(dir, "fv_lastdir"), []byte(last+"\n"), 0o600)
|
||||
}
|
||||
if app.execShell {
|
||||
sh := os.Getenv("SHELL")
|
||||
if sh == "" {
|
||||
sh = "/bin/sh"
|
||||
}
|
||||
if _, err := os.Stat(sh); err == nil {
|
||||
_ = syscall.Exec(sh, []string{sh}, os.Environ()) // no return on success
|
||||
}
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, last)
|
||||
}
|
||||
Reference in New Issue
Block a user