v2.0.1 Β· A fast, powerful Windows 11-style file manager for macOS β a real Finder replacement, packed into a single dependency-free Zig 0.16 binary.
π¨π³ δΈζζζ‘£θ―·η README.zh.md
π Zave is the 2.0 evolution of window-finder (frozen at v1.8) β same project, new name and home.
Let's be honest: macOS Finder is clunky. You can't see the full path, copying a path is a chore, and it's slower than it has any right to be. Zave fixes all of that β a snappy, Windows 11-style file manager that shows and copies the full path instantly, with tabs, multi-select, live preview, ZIP, drag-and-drop, "open in Terminal" and a lot more. It does more than Finder, gets out of your way faster, and ships as one tiny self-contained binary.
And the whole backend is pure Zig β no third-party dependencies, only the standard library β so it doubles as a hands-on tour of Zig 0.16's new I/O API.
| Feature | Description |
|---|---|
| π Full path bar | The top address bar always shows the absolute path of the current directory (like Windows) |
| π One-click path copy | "Copy Path" copies the current directory; each row also has a hover "Copy Path" button |
| π§ Breadcrumbs | Click any level under the path bar to jump there |
| β¬ Up | Go to the parent directory |
| π Navigate | Double-click a folder to enter; double-click a file to copy its full path |
| βοΈ Cut / Copy / Paste | Move or copy files and folders (recursive directory copy) |
| β New folder | Create a directory in the current location |
| βοΈ Rename | Rename a file or folder (in-place move) |
| ποΈ Delete | Delete a file or an entire directory tree (with a confirmation dialog) |
| ποΈ Zip / Unzip | Compress any item to a ZIP, or extract a ZIP in place (via zip/unzip) |
| βοΈ Multi-select | Ctrl/Cmd-click, Shift-click range, Ctrl/Cmd+A; batch cut/copy/paste, delete and drag |
| ποΈβπ¨οΈ Preview | Inline preview of text/code & images in the details panel; everything else gets an "Open with default app" button (macOS open) |
| ποΈ Hidden files | Toggle to show/hide dot-files |
| π Search | Live filter of the current directory by filename |
| β¨οΈ Keyboard shortcuts | β/β to move selection (Home/End for first/last), Enter to open, Cmd/Ctrl + X / C / V cut/copy/paste, F2 rename, Delete remove |
| π Theme & π language | Dark / light theme and English / δΈζ toggles, both persisted |
| π€ Auto sort | Directories first, then files, sorted by name |
| π File size | Auto-formatted as B / KB / MB / GB |
- Download zave.zip from the latest release and double-click to unzip.
- Double-click
Zave. macOS says "Not Opened" β click Done (the app isn't code-signed). - Open System Settings β Privacy & Security, scroll down to "Zave was blockedβ¦", click Open Anyway, then open the app again and confirm.
β οΈ The old "right-click β Open" trick no longer works on macOS 15+ (Sequoia/Tahoe) β you must use System Settings β Privacy & Security β Open Anyway. Only needed once. Developers can instead runxattr -dr com.apple.quarantine Zave.app. Runs from anywhere; universal (Apple Silicon + Intel).
Requirements: Zig 0.16.0
# Build and run (opens a native window)
zig build run
# Or in two steps
zig build
./zig-out/bin/zaveThen open: http://127.0.0.1:9781
The server listens on 127.0.0.1:9781 (local-only, safe).
Set a custom port with the PORT env var (PORT=9000 zig build run). If the chosen port is busy, the server automatically tries the next ones. The port can also be changed from Settings β β System settings in the UI, which saves it and restarts the app on the new port.
zig build run opens a native window (a WKWebView hosting the UI) β no browser needed. Under the hood the HTTP server runs as a child process and the window points at it.
- Build a distributable universal (arm64 + Intel)
.app,.zipor.dmg:./packaging/package.sh # -> dist/Zave.app ./packaging/package.sh --zip # -> dist/zave.zip ./packaging/package.sh --zip --dmg # both
- Installing a downloaded build: the app is not code-signed (no paid
Apple Developer account). On macOS 15+ (Sequoia/Tahoe): double-click β Done,
then System Settings β Privacy & Security β Open Anyway. (Developers:
xattr -dr com.apple.quarantine Zave.app.) - Prefer the browser instead of a window? Run headless:
HEADLESS=1 zig build run, then open the printed URL. - macOS will ask permission the first time it touches Desktop/Documents/Downloads (normal privacy prompts). Click Allow, or grant Full Disk Access to Zave once in System Settings β Privacy & Security.
- Single-click a row to select it; double-click a folder to enter it.
- Use the toolbar buttons (or keyboard shortcuts) to cut / copy / paste / delete.
- Paste drops the clipboard item into the current directory.
Zave/
βββ build.zig # Build script (defines the exe and the `run` step)
βββ src/
βββ main.zig # HTTP server + filesystem logic
βββ index.html # Frontend (HTML/CSS/JS, embedded into the binary at compile time)
index.html is embedded into the executable at compile time via @embedFile("index.html"), so the final artifact is a single binary β no extra static files to ship.
Browser Zig backend (127.0.0.1:9781)
β β
β GET / β
βββββββββββββββββββββββββββββββββββββΆ returns the embedded index.html
β β
β GET /api/list?path=/Users/... β
βββββββββββββββββββββββββββββββββββββΆ Io.Dir.openDirAbsolute + iterate
β ββββββββββββββββββββββββββββββββββ returns JSON listing
β β
β POST /api/move | /api/copy | /api/delete
βββββββββββββββββββββββββββββββββββββΆ rename / copy / deleteTree
β ββββββββββββββββββββββββββββββββββ { "ok": true } or { "error": "..." }
The frontend is a single-page app: all navigation and operations call the API via fetch and refresh asynchronously, without reloading the page.
Lists the contents of a directory. path is URL-encoded; empty defaults to /.
Success 200 application/json
{
"path": "/Users/yurizhang/Documents",
"entries": [
{ "name": "project", "kind": "directory", "size": 0 },
{ "name": "notes.txt", "kind": "file", "size": 1024 }
]
}kind:"directory"or"file"(symlinks etc. are reported asfile)size: bytes; always0for directories
Moves (cut + paste) a file or directory from from to to. Backed by renameAbsolute.
Copies a file or directory (directories are copied recursively).
Deletes a file, or an entire directory tree.
Creates a new directory at path. (Rename reuses /api/move β it's just an in-place move.)
Returns the raw file bytes (capped at 16 MB) with a Content-Type guessed from the extension β used by the details panel to preview text and images.
Opens the file or folder with the default application via macOS open.
Error response (for any of the above)
{ "error": "FileNotFound" }Safety guards: refuses to copy a directory into its own subtree (infinite recursion), refuses to delete /, rejects identical source/destination and non-absolute paths.
Zig 0.16 reworked the I/O model β almost every I/O operation now takes an explicit Io value. This project happens to cover most of that new surface:
| Stdlib module | Used for |
|---|---|
std.Io.Threaded |
Creating the I/O instance (.init(gpa, .{}) β .io()) |
std.Io.net |
Pure-Zig networking: IpAddress.parse β listen β accept |
std.http.Server |
HTTP handling: receiveHead / respond |
std.Io.Dir |
Filesystem: openDirAbsolute, iterate, renameAbsolute, copyFileAbsolute, createDirAbsolute, deleteTree |
std.Io.Writer.Allocating |
Building JSON strings dynamically |
@embedFile |
Embedding the frontend at compile time |
// 0.16: build an Io instance first, then pass it everywhere.
var threaded: std.Io.Threaded = .init(gpa, .{});
const io = threaded.io();
// Networking
var addr = try std.Io.net.IpAddress.parse("127.0.0.1", 9781);
var server = try addr.listen(io, .{ .reuse_address = true });
const stream = try server.accept(io);
// Directory iteration: iterate() returns an iterator, next(io) takes io
var dir = try std.Io.Dir.openDirAbsolute(io, path, .{ .iterate = true });
var it = dir.iterate();
while (try it.next(io)) |entry| { ... }Each request uses an ArenaAllocator that is freed as a whole when the request finishes, avoiding piecemeal manual frees.
A gotcha worth noting:
http.Server.respond()asserts (under keep-alive) that a POST request declares a body length. Since the mutation endpoints are POSTs that may carry noContent-Length, their responses set.keep_alive = falseto bypass the body-discard path.
- Cut / copy / paste files and folders
- Delete (file / tree)
- Create new folder / rename
- Toggle for hidden files
- Filename search / filter
- English / δΈζ + dark / light toggles
- Copy current path
- File preview (text / code / images) + open with default app
- PDF inline preview (
<iframe>embed) - Right-click context menu
- Compress / extract ZIP
- Multi-select (Ctrl/Shift click) with batch operations
- Multiple tabs (independent directories)
- Drag-and-drop to move
- Finer file-type icons
- Listens on
127.0.0.1only β not exposed to the network β suited for local use. - Files are accessed with the permissions of the server process.
| List view (light) | List view (dark) |
|---|---|
![]() |
![]() |
Grid view
MIT Β© 2026 Yong Zhang <zhangyong0604@126.com>


