2026-04-22 22:10:50 +03:00
|
|
|
package vfs
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"io/fs"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
configExtensions = map[string]struct{}{
|
|
|
|
|
"toml": {}, "yaml": {}, "yml": {}, "json": {}, "jsonc": {}, "ini": {}, "conf": {},
|
|
|
|
|
"config": {}, "env": {}, "properties": {}, "xml": {}, "mod": {}, "sum": {}, "lock": {},
|
|
|
|
|
}
|
|
|
|
|
textExtensions = map[string]struct{}{
|
|
|
|
|
"txt": {}, "md": {}, "rst": {}, "go": {}, "rs": {}, "c": {}, "h": {}, "cpp": {}, "hpp": {},
|
fix: open more text file types in editor (nvim)
Expand textExtensions with many missing common text file extensions
(.lua, .rb, .vue, .svelte, .dart, .tex, .scala, .lisp, and many more).
Add textFilenames list for well-known text files without a meaningful
extension (Makefile, Dockerfile, README, LICENSE, .gitignore, etc.)
so they open in the editor via handleEdit() instead of falling back
to xdg-open via handleOpenExternal().
2026-04-27 14:23:29 +03:00
|
|
|
"py": {}, "js": {}, "ts": {}, "tsx": {}, "jsx": {}, "java": {}, "kt": {}, "kts": {}, "swift": {},
|
|
|
|
|
"html": {}, "css": {}, "scss": {}, "sass": {}, "less": {}, "styl": {},
|
|
|
|
|
"sh": {}, "bash": {}, "zsh": {}, "fish": {}, "sql": {},
|
|
|
|
|
"log": {}, "csv": {}, "tsv": {},
|
|
|
|
|
"lua": {}, "rb": {}, "pl": {}, "pm": {}, "t": {}, "ps1": {}, "bat": {}, "cmd": {},
|
|
|
|
|
"vue": {}, "svelte": {}, "astro": {}, "ejs": {}, "hbs": {}, "pug": {}, "haml": {}, "php": {}, "twig": {},
|
|
|
|
|
"scala": {}, "groovy": {}, "clj": {}, "ex": {}, "exs": {}, "elm": {}, "hs": {}, "lisp": {}, "cl": {}, "rkt": {}, "scm": {}, "dart": {},
|
|
|
|
|
"tex": {}, "bib": {}, "sty": {}, "cls": {},
|
|
|
|
|
"gradle": {}, "cmake": {}, "mk": {}, "mak": {},
|
|
|
|
|
"asm": {}, "s": {}, "inc": {},
|
|
|
|
|
"patch": {}, "diff": {},
|
|
|
|
|
"proto": {}, "graphql": {}, "gql": {},
|
|
|
|
|
"tf": {}, "hcl": {},
|
|
|
|
|
"r": {}, "m": {}, "mm": {},
|
2026-04-27 14:25:19 +03:00
|
|
|
"nim": {}, "zig": {}, "odin": {}, "v": {}, "nix": {},
|
fix: open more text file types in editor (nvim)
Expand textExtensions with many missing common text file extensions
(.lua, .rb, .vue, .svelte, .dart, .tex, .scala, .lisp, and many more).
Add textFilenames list for well-known text files without a meaningful
extension (Makefile, Dockerfile, README, LICENSE, .gitignore, etc.)
so they open in the editor via handleEdit() instead of falling back
to xdg-open via handleOpenExternal().
2026-04-27 14:23:29 +03:00
|
|
|
"cr": {}, "jl": {},
|
|
|
|
|
"erl": {}, "hrl": {},
|
|
|
|
|
}
|
|
|
|
|
// textFilenames lists common text files without a meaningful extension
|
|
|
|
|
// (like Makefile, Dockerfile, etc.) so they open in the editor.
|
|
|
|
|
textFilenames = map[string]struct{}{
|
|
|
|
|
"makefile": {}, "dockerfile": {}, "containerfile": {},
|
|
|
|
|
"readme": {}, "license": {}, "licence": {}, "copying": {}, "changelog": {}, "changes": {},
|
|
|
|
|
"todo": {}, "notes": {}, "authors": {}, "contributors": {}, "maintainers": {},
|
|
|
|
|
"procfile": {}, "gemfile": {}, "rakefile": {}, "snapfile": {}, "fastfile": {},
|
|
|
|
|
"cmakelists": {}, "justfile": {}, "taskfile": {},
|
|
|
|
|
"gitignore": {}, "gitattributes": {}, "gitmodules": {}, "gitkeep": {},
|
|
|
|
|
"gitconfig": {}, "git-blame-ignore-revs": {},
|
|
|
|
|
"editorconfig": {}, "envrc": {}, "hushlogin": {},
|
|
|
|
|
"xsession": {}, "xresources": {}, "xinitrc": {},
|
|
|
|
|
"bashrc": {}, "bash_profile": {}, "bash_logout": {},
|
|
|
|
|
"zshrc": {}, "zprofile": {}, "zlogin": {}, "zlogout": {},
|
|
|
|
|
"profile": {}, "inputrc": {}, "tmux.conf": {},
|
|
|
|
|
"npmrc": {}, "yarnrc": {}, "pnpmrc": {},
|
|
|
|
|
"eslintrc": {}, "prettierrc": {}, "babelrc": {},
|
|
|
|
|
"stylelintrc": {}, "commitlintrc": {},
|
|
|
|
|
"htaccess": {}, "htpasswd": {},
|
2026-04-22 22:10:50 +03:00
|
|
|
}
|
|
|
|
|
imageExtensions = map[string]struct{}{
|
|
|
|
|
"png": {}, "jpg": {}, "jpeg": {}, "gif": {}, "webp": {}, "bmp": {}, "svg": {}, "ico": {},
|
fix: open more text file types in editor (nvim)
Expand textExtensions with many missing common text file extensions
(.lua, .rb, .vue, .svelte, .dart, .tex, .scala, .lisp, and many more).
Add textFilenames list for well-known text files without a meaningful
extension (Makefile, Dockerfile, README, LICENSE, .gitignore, etc.)
so they open in the editor via handleEdit() instead of falling back
to xdg-open via handleOpenExternal().
2026-04-27 14:23:29 +03:00
|
|
|
"avif": {}, "heic": {}, "heif": {}, "tiff": {}, "tif": {},
|
2026-04-22 22:10:50 +03:00
|
|
|
}
|
feat: extended preview for PDF, audio, video via external utilities
Add rich preview support for three new file categories by leveraging
external CLI tools with graceful fallback when tools are missing.
- PDF: text extraction via pdftotext, page count via pdfinfo
- Audio: metadata via ffprobe (duration, bitrate, codec, sample rate, channels)
- Video: metadata via ffprobe (duration, bitrate, video/audio codec, resolution)
- New PreviewKind constants: PDF, Audio, Video
- New Metadata fields for extended preview data
- New extension maps and Category() entries for pdf/audio/video
- Icons: PDF (), audio (), video () in preview header
Closes #5
2026-04-27 19:25:03 +03:00
|
|
|
pdfExtensions = map[string]struct{}{
|
|
|
|
|
"pdf": {},
|
|
|
|
|
}
|
|
|
|
|
audioExtensions = map[string]struct{}{
|
|
|
|
|
"mp3": {}, "flac": {}, "ogg": {}, "opus": {}, "wav": {},
|
|
|
|
|
"aac": {}, "m4a": {}, "wma": {}, "dsf": {}, "ape": {},
|
|
|
|
|
}
|
|
|
|
|
videoExtensions = map[string]struct{}{
|
|
|
|
|
"mp4": {}, "mkv": {}, "mov": {}, "avi": {}, "webm": {},
|
|
|
|
|
"m4v": {}, "wmv": {}, "flv": {}, "ts": {}, "mts": {},
|
|
|
|
|
}
|
2026-04-22 22:10:50 +03:00
|
|
|
archiveExtensions = map[string]struct{}{
|
|
|
|
|
"zip": {}, "tar": {}, "gz": {}, "tgz": {}, "xz": {}, "bz2": {}, "7z": {}, "rar": {},
|
fix: open more text file types in editor (nvim)
Expand textExtensions with many missing common text file extensions
(.lua, .rb, .vue, .svelte, .dart, .tex, .scala, .lisp, and many more).
Add textFilenames list for well-known text files without a meaningful
extension (Makefile, Dockerfile, README, LICENSE, .gitignore, etc.)
so they open in the editor via handleEdit() instead of falling back
to xdg-open via handleOpenExternal().
2026-04-27 14:23:29 +03:00
|
|
|
"zst": {}, "lz": {}, "lz4": {}, "lzma": {},
|
2026-04-22 22:10:50 +03:00
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type Entry struct {
|
2026-04-29 03:11:53 +03:00
|
|
|
Name string
|
|
|
|
|
Path string
|
|
|
|
|
Extension string
|
|
|
|
|
Mode fs.FileMode
|
|
|
|
|
Size int64
|
|
|
|
|
ModifiedAt time.Time
|
|
|
|
|
CreatedAt time.Time
|
|
|
|
|
CreatedKnown bool
|
|
|
|
|
IsDir bool
|
|
|
|
|
IsParent bool
|
|
|
|
|
IsHidden bool
|
|
|
|
|
IsRemote bool
|
|
|
|
|
Connected bool
|
|
|
|
|
DirSizeKnown bool
|
|
|
|
|
RemoteHostName string
|
2026-04-22 22:10:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (e Entry) DisplayName() string {
|
|
|
|
|
if e.IsParent {
|
|
|
|
|
return ".."
|
|
|
|
|
}
|
|
|
|
|
if e.IsDir {
|
|
|
|
|
return e.Name + "/"
|
|
|
|
|
}
|
|
|
|
|
return e.Name
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (e Entry) IsFile() bool {
|
|
|
|
|
return !e.IsDir && !e.IsParent
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (e Entry) MatchKey() string {
|
|
|
|
|
return strings.ToLower(e.Name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (e Entry) IsExecutable() bool {
|
|
|
|
|
return !e.IsDir && !e.IsParent && e.Mode&0o111 != 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (e Entry) Category() string {
|
|
|
|
|
switch {
|
|
|
|
|
case e.IsParent:
|
|
|
|
|
return "parent"
|
2026-04-29 03:11:53 +03:00
|
|
|
case e.IsRemote:
|
|
|
|
|
return "remote"
|
2026-04-22 22:10:50 +03:00
|
|
|
case e.IsDir:
|
|
|
|
|
return "directory"
|
|
|
|
|
case hasExt(configExtensions, e.Extension):
|
|
|
|
|
return "config"
|
|
|
|
|
case hasExt(textExtensions, e.Extension):
|
|
|
|
|
return "text"
|
2026-04-27 23:18:48 +03:00
|
|
|
case hasExt(textFilenames, strings.ToLower(e.Name)):
|
|
|
|
|
return "text"
|
|
|
|
|
case hasExt(imageExtensions, e.Extension):
|
|
|
|
|
return "image"
|
feat: extended preview for PDF, audio, video via external utilities
Add rich preview support for three new file categories by leveraging
external CLI tools with graceful fallback when tools are missing.
- PDF: text extraction via pdftotext, page count via pdfinfo
- Audio: metadata via ffprobe (duration, bitrate, codec, sample rate, channels)
- Video: metadata via ffprobe (duration, bitrate, video/audio codec, resolution)
- New PreviewKind constants: PDF, Audio, Video
- New Metadata fields for extended preview data
- New extension maps and Category() entries for pdf/audio/video
- Icons: PDF (), audio (), video () in preview header
Closes #5
2026-04-27 19:25:03 +03:00
|
|
|
case hasExt(pdfExtensions, e.Extension):
|
|
|
|
|
return "pdf"
|
|
|
|
|
case hasExt(audioExtensions, e.Extension):
|
|
|
|
|
return "audio"
|
|
|
|
|
case hasExt(videoExtensions, e.Extension):
|
|
|
|
|
return "video"
|
2026-04-22 22:10:50 +03:00
|
|
|
case hasExt(archiveExtensions, e.Extension):
|
|
|
|
|
return "archive"
|
2026-04-27 23:18:48 +03:00
|
|
|
case e.IsExecutable():
|
|
|
|
|
return "executable"
|
2026-04-22 22:10:50 +03:00
|
|
|
default:
|
|
|
|
|
return "binary"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ext(name string) string {
|
|
|
|
|
value := strings.TrimPrefix(filepath.Ext(name), ".")
|
|
|
|
|
return strings.ToLower(value)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func hasExt(set map[string]struct{}, ext string) bool {
|
|
|
|
|
_, ok := set[strings.ToLower(ext)]
|
|
|
|
|
return ok
|
|
|
|
|
}
|