diff --git a/README.md b/README.md index 94d60aa..944baa9 100644 --- a/README.md +++ b/README.md @@ -36,13 +36,13 @@ go build -o vcom ./cmd/vcom Run directly from the flake: ```bash -nix run github:vrubelroman/vcom?ref=v0.2.0 +nix run github:vrubelroman/vcom?ref=v0.2.1 ``` Install into user profile: ```bash -nix profile add github:vrubelroman/vcom?ref=v0.2.0 +nix profile add github:vrubelroman/vcom?ref=v0.2.1 ``` The Nix package wraps `vcom` with `ueberzugpp` in `PATH`, so image preview works in non-`kitty` terminals out of the box. @@ -52,7 +52,7 @@ The Nix package wraps `vcom` with `ueberzugpp` in `PATH`, so image preview works Download and install the latest release: ```bash -curl -sL https://github.com/vrubelroman/vcom/releases/download/v0.2.0/vcom_0.2.0_amd64.deb -o /tmp/vcom_0.2.0_amd64.deb +curl -sL https://github.com/vrubelroman/vcom/releases/download/v0.2.1/vcom_0.2.1_amd64.deb -o /tmp/vcom_0.2.1_amd64.deb sudo apt install /tmp/vcom_0.2.0_amd64.deb ``` @@ -183,7 +183,7 @@ Built-in themes (use `T` to cycle or set `ui.theme` in config): ## Releases -Pushing a tag like `v0.2.0` triggers GitHub Actions release workflow (`.github/workflows/release.yml`) which: +Pushing a tag like `v0.2.1` triggers GitHub Actions release workflow (`.github/workflows/release.yml`) which: - runs tests - vendors Go modules @@ -193,9 +193,9 @@ Pushing a tag like `v0.2.0` triggers GitHub Actions release workflow (`.github/w Release artifacts: -- `vcom-v0.2.0-x86_64-unknown-linux-gnu.tar.gz` -- `vcom_0.2.0_amd64.deb` -- `vcom-v0.2.0-checksums.txt` +- `vcom-v0.2.1-x86_64-unknown-linux-gnu.tar.gz` +- `vcom_0.2.1_amd64.deb` +- `vcom-v0.2.1-checksums.txt` ## Notes diff --git a/internal/fs/entry.go b/internal/fs/entry.go index fdb9e03..9265ac9 100644 --- a/internal/fs/entry.go +++ b/internal/fs/entry.go @@ -116,14 +116,14 @@ func (e Entry) Category() string { return "parent" case e.IsDir: return "directory" - case e.IsExecutable(): - return "executable" case hasExt(configExtensions, e.Extension): return "config" - case hasExt(imageExtensions, e.Extension): - return "image" case hasExt(textExtensions, e.Extension): return "text" + case hasExt(textFilenames, strings.ToLower(e.Name)): + return "text" + case hasExt(imageExtensions, e.Extension): + return "image" case hasExt(pdfExtensions, e.Extension): return "pdf" case hasExt(audioExtensions, e.Extension): @@ -132,8 +132,8 @@ func (e Entry) Category() string { return "video" case hasExt(archiveExtensions, e.Extension): return "archive" - case hasExt(textFilenames, strings.ToLower(e.Name)): - return "text" + case e.IsExecutable(): + return "executable" default: return "binary" } diff --git a/internal/ui/model.go b/internal/ui/model.go index ab7316f..86b447c 100644 --- a/internal/ui/model.go +++ b/internal/ui/model.go @@ -23,7 +23,7 @@ import ( "vcom/internal/theme" ) -const version = "v0.2.0" +const version = "v0.2.1" type modalKind int @@ -51,6 +51,7 @@ const ( opEdit opView opArchive + opExecute ) type pendingOperation struct { @@ -353,6 +354,9 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case opView: m.status = "Viewer closed" return m, enableMouseCmd() + case opExecute: + m.status = "Executable closed" + return m, tea.Batch(m.loadPreviewCmd(), enableMouseCmd()) } leftSelection := selectedName(&m.left) @@ -1440,10 +1444,14 @@ func (m *Model) handleOpenSelected() (tea.Model, tea.Cmd) { return m, m.loadPreviewCmd() } - if isEditableEntry(selected) { + switch selected.Category() { + case "text", "config": return m.handleEdit() + case "executable": + return m.handleExecute(selected) + default: + return m.handleOpenExternal() } - return m.handleOpenExternal() } func (m *Model) goParent() error { @@ -1769,6 +1777,16 @@ func (m *Model) handleEdit() (tea.Model, tea.Cmd) { }) } +func (m *Model) handleExecute(entry vfs.Entry) (tea.Model, tea.Cmd) { + m.cleanupImageOverlay() + cmd := exec.Command(entry.Path) + cmd.Dir = filepath.Dir(entry.Path) + m.status = fmt.Sprintf("Executing %s", entry.DisplayName()) + return m, tea.ExecProcess(cmd, func(err error) tea.Msg { + return opMsg{kind: opExecute, sourcePath: entry.Path, err: err} + }) +} + func (m *Model) handleMouse(msg tea.MouseMsg) (tea.Model, tea.Cmd) { if m.viewMode { switch { @@ -4242,7 +4260,7 @@ func paneIndexFromMouse(localY int, height int, pane *BrowserPane) (int, bool) { func isEditableEntry(entry vfs.Entry) bool { switch entry.Category() { - case "text", "config", "executable": + case "text", "config": return true default: return false