Support Ubuntu image preview backend

This commit is contained in:
vrubelroman 2026-04-24 22:53:12 +03:00
parent ded9b9fdfb
commit e229cbc6cc
5 changed files with 70 additions and 32 deletions

View file

@ -1,5 +1,5 @@
pkgname=vcom
pkgver=0.1.5
pkgver=0.1.7
pkgrel=1
pkgdesc="Terminal file manager inspired by Midnight Commander"
arch=("x86_64" "aarch64")

View file

@ -64,26 +64,26 @@ go build -o vcom ./cmd/vcom
Run directly from the flake:
```bash
nix run github:vrubelroman/vcom?ref=v0.1.5
nix run github:vrubelroman/vcom?ref=v0.1.7
```
Install into user profile:
```bash
nix profile add github:vrubelroman/vcom?ref=v0.1.5
nix profile add github:vrubelroman/vcom?ref=v0.1.7
```
The Nix package wraps `vcom` with `ueberzugpp` in `PATH`, so image preview works in non-`kitty` terminals out of the box.
### Debian / Ubuntu
Download the release `.deb` for `v0.1.5`, then install:
Download the release `.deb` for `v0.1.7`, then install:
```bash
sudo apt install ./vcom_0.1.5_amd64.deb
sudo apt install ./vcom_0.1.7_amd64.deb
```
The Debian package declares `ueberzugpp` as a dependency for image preview outside `kitty`.
The Debian package declares `ueberzug` (or `ueberzugpp` where available) as a dependency for image preview outside `kitty`.
Install a Nerd Font (example):
@ -136,7 +136,7 @@ Built-in themes:
## Releases
Pushing a tag like `v0.1.5` triggers GitHub Actions release workflow (`.github/workflows/release.yml`) which:
Pushing a tag like `v0.1.7` triggers GitHub Actions release workflow (`.github/workflows/release.yml`) which:
- runs tests
- vendors Go modules
@ -146,9 +146,9 @@ Pushing a tag like `v0.1.5` triggers GitHub Actions release workflow (`.github/w
Release artifacts:
- `vcom-v0.1.5-x86_64-unknown-linux-gnu.tar.gz`
- `vcom_0.1.5_amd64.deb`
- `vcom-v0.1.5-checksums.txt`
- `vcom-v0.1.7-x86_64-unknown-linux-gnu.tar.gz`
- `vcom_0.1.7_amd64.deb`
- `vcom-v0.1.7-checksums.txt`
## Notes

View file

@ -13,7 +13,7 @@
lib = pkgs.lib;
packageBase = pkgs.buildGoModule {
pname = "vcom";
version = "0.1.5";
version = "0.1.7";
src = ./.;
vendorHash = null;

View file

@ -127,36 +127,68 @@ func (m *imageOverlayManager) startBackend(backend string) error {
return nil
}
func (m *imageOverlayManager) startLegacyBackend() error {
cmd := exec.Command("ueberzug", "layer", "--parser", "json")
stdin, err := cmd.StdinPipe()
if err != nil {
return err
}
cmd.Stdout = io.Discard
cmd.Stderr = io.Discard
if err := cmd.Start(); err != nil {
_ = stdin.Close()
return err
}
m.cmd = cmd
m.stdin = stdin
m.running = true
m.backend = "ueberzug"
return nil
}
func (m *imageOverlayManager) ensureStarted() error {
if m.running {
return nil
}
if _, err := exec.LookPath("ueberzugpp"); err != nil {
return err
if _, err := exec.LookPath("ueberzugpp"); err == nil {
var lastErr error
for _, backend := range m.backendList() {
if err := m.startBackend(backend); err != nil {
lastErr = err
continue
}
// Probe command channel right away; some backends terminate instantly.
if err := m.send(map[string]any{
"action": "remove",
"identifier": m.identifier,
}); err != nil {
lastErr = err
m.stop()
continue
}
return nil
}
if lastErr != nil {
return lastErr
}
}
var lastErr error
for _, backend := range m.backendList() {
if err := m.startBackend(backend); err != nil {
lastErr = err
continue
if _, err := exec.LookPath("ueberzug"); err == nil {
if err := m.startLegacyBackend(); err != nil {
return err
}
// Probe command channel right away; some backends terminate instantly.
if err := m.send(map[string]any{
"action": "remove",
"identifier": m.identifier,
}); err != nil {
lastErr = err
m.stop()
continue
return err
}
return nil
}
if lastErr != nil {
return lastErr
}
return fmt.Errorf("could not start ueberzugpp overlay")
return fmt.Errorf("could not start image overlay backend")
}
func (m *imageOverlayManager) send(payload map[string]any) error {
@ -202,12 +234,16 @@ func (m *imageOverlayManager) show(path string, rect overlayRect) error {
"path": path,
"x": rect.x,
"y": rect.y,
"max_width": rect.width,
"max_height": rect.height,
"scaler": "fit_contain",
}
if m.backend == "ueberzug" {
payload["width"] = rect.width
payload["height"] = rect.height
} else {
payload["max_width"] = rect.width
payload["max_height"] = rect.height
payload["scaler"] = "fit_contain"
}
if err := m.send(payload); err == nil {
m.backend = "ueberzugpp"
m.visible = true
m.lastPath = path
m.lastRect = rect
@ -215,8 +251,10 @@ func (m *imageOverlayManager) show(path string, rect overlayRect) error {
}
m.stop()
if len(m.backends) > 0 {
if m.backend != "ueberzug" && len(m.backends) > 0 {
m.backends = append(m.backends[1:], m.backends[0])
} else {
break
}
}
return fmt.Errorf("could not render image overlay")
@ -229,7 +267,7 @@ func (m *imageOverlayManager) hide() {
switch m.backend {
case "kitty":
m.clearKitty()
case "ueberzugpp":
case "ueberzugpp", "ueberzug":
if m.running {
_ = m.send(map[string]any{
"action": "remove",

View file

@ -29,7 +29,7 @@ Section: utils
Priority: optional
Architecture: amd64
Maintainer: Roman Vrubel <roman@vrubel.dev>
Depends: ueberzugpp
Depends: ueberzug | ueberzugpp
Description: Terminal file manager inspired by Midnight Commander
A two-pane terminal file manager with inspect mode and text previews.
EOF