Repository navigation
Roadmap
Hyprland first: new features land on Linux, while macOS and Windows build in CI and catch up later. Releases are small and frequent. Every feature has to make the core loop (open, type, Enter) faster; zunder isn't trying to be a Raycast clone.
What makes zunder worth switching to, and every release should sharpen at least one of these:
- One tool for launching and ALT+TAB, with live window previews.
- Plugins are any script that prints lines. No SDK, no Node.
- A terminal look with CSS themes.
- Deep Hyprland integration: real binds, workspaces,
hide_windows. - One small native binary that opens instantly.
The biggest hurdle today is setup: hand-editing Hyprland binds, autostart and window rules.
-
One-line installer:
Optionally a short URL later, e.g.
curl -fsSL https://raw.githubusercontent.com/elyesghazel/zunder/main/get.sh | shzunder.elyesghazel.ch/getas a redirect, with no update server behind it. The newget.shat the repo root:- Is POSIX
sh, so it also works when your login shell is fish (curl … | sh). - Finds the newest version through the GitHub releases API.
-
Arch: downloads the
.pkg.tar.zstand runssudo pacman -U(the only sudo prompt). -
Other distros:
- checks for gtk4 4.14+ and gtk4-layer-shell with
pkg-config, and prints the exactapt/dnf/zyppercommand if something is missing; - unpacks the tarball's binary to
~/.local/bin.
- checks for gtk4 4.14+ and gtk4-layer-shell with
- Checks the sha256 against a
SHA256SUMSfile. The release workflow gets a step that generates and uploads it. - Then runs
zunder setupwith the terminal attached (< /dev/tty), so its questions work even though the script itself arrived through a pipe. - Re-running it updates.
get.sh --uninstallremoves zunder.
- Is POSIX
-
zunder setup, interactive, new insrc/setup.rsand wired intosrc/main.rs. Plain terminal prompts with a default on Enter.--yestakes all defaults for scripts.-
Import: detects vicinae, rofi, wofi and fuzzel.
- Offers
zunder import vicinae(favorites and launch counts,src/import.rs). - Offers to replace their autostart lines and binds in the Hyprland config, showing a diff
first and keeping a
.bak.
- Offers
-
Keys: asks which keys you want: ALT+TAB switcher, ALT+SPACE or SUPER+SPACE launcher,
SUPER+V clipboard. It checks
hyprctl binds -jand says when a key is already taken. -
Clipboard: if cliphist is installed but not running, offers to add its
wl-paste --watchautostart lines. - Theme: a pick from the built-in themes, printed as colored swatches in the terminal.
-
Example plugins: offers to link
killandip. - Then does the steps below.
- Detects a Lua or classic Hyprland config. Reuse the detection in
platform::linux::install_binds(src/platform/linux.rs). - Shows a diff of what it will add, then writes
zunder.lua/zunder.confwith:- the autostart line;
- ALT+TAB, ALT+SHIFT+TAB, ALT+SPACE, SUPER+V;
- the settings window rule;
- a
require/sourceline in the main config, added only if missing.
- Idempotent. Has
--dry-run. Refuses to touch binds that are already taken; check withhyprctl binds -j. - Prints the next step.
-
Import: detects vicinae, rofi, wofi and fuzzel.
-
Update notice, new in
src/update.rs:- At most once a day, a background thread calls
https://api.github.com/repos/elyesghazel/zunder/releases/latestwith curl. Same pattern asfetch_ratesinsrc/providers/convert.rs. - Caches the answer in
~/.cache/zunder/latest.json. - If there's a newer version, the start screen gets an item "zunder 0.3 is out" that opens the release page.
- Can be turned off in Settings → General (new
update_checkkey insrc/config.rsSETTINGS).
- At most once a day, a background thread calls
-
ALT+TAB reliability (
src/ui.rs):-
Quick tap: a fast Alt+Tab should flip to the previous window every time. The
alt_held/is_activepath needs a test hook and a fix if it misfires. - Multiple monitors: the overlay opens on the focused monitor; window previews show for every monitor.
- Fullscreen windows: the overlay should show above them.
-
Quick tap: a fast Alt+Tab should flip to the previous window every time. The
-
Speed budget, measured:
-
zunder showto first frame in under 30 ms. - Each keystroke searched in under 5 ms.
-
Engine::searchgets a criterion bench inbenches/. - Apps are cached instead of rescanned on every open: check the
applications/dirs' mtimes inproviders/apps.rsreload.
-
-
First-run screen. When
config.tomldoesn't exist yet, the start screen shows three items: "Run setup", "Pick a theme" and "Read the keys". -
Release:
packaging/bump.sh 0.2.0, a new demo GIF, then repost.
Done when:
- A fresh Arch user runs the one curl line, answers a few questions, and has working ALT+TAB in under 2 minutes.
- Re-running
get.shupdates zunder. -
get.shis tested in a cleanarchlinuxand a cleandebian:trixiecontainer. -
zunder setupis tested against a real vicinae-era Hyprland config. - The README's Install section starts with the curl line.
- CI is green and the bench numbers are in the README.
Goal: a ~10 MB zunder-setup.exe that installs and starts like any other app.
-
Centered window (
src/platform/windows.rsinit_overlayandsrc/ui.rsshow):- Get the HWND through
gdk4-win32(Win32Surface::handle). - After
present(), center it on the monitor under the cursor withSetWindowPos/MonitorFromPoint(windows-sys). - Set the window topmost.
- Get the HWND through
-
Glyphs and icons:
- Glyphs: bundle
JetBrainsMono Nerd Font(~2 MB). The installer installs it per user, or the app loads it privately withAddFontResourceExW(FR_PRIVATE)at startup. - App icons: extract them from the
.lnktargets withSHGetFileInfoW/IShellItemImageFactoryinto%LOCALAPPDATA%\zunder\icons\*.pngonce, then pointApp.iconat that path.ui::icon()already takes absolute paths.
- Glyphs: bundle
-
Smaller bundle (
.github/workflows/release.yml, Windows job):- Ship only the Adwaita icons we actually use (folder, generic file and image types, a few symbolic ones) instead of the whole theme.
- Drop unused DLLs, checked with
ntldd -R. - Expect roughly 22 MB → ~10 MB compressed.
-
Inno Setup installer, new
packaging/windows/zunder.iss:- Built with
isccon the GitHub Windows runner (it's preinstalled there). - Per-user install to
%LOCALAPPDATA%\Programs\zunder, so no admin prompt. - Pages with checkboxes: start with Windows (Startup shortcut with
server), desktop icon, Start menu entry, default hotkey (Ctrl+Alt+Space set as the shortcut key). - Uninstaller included. Optionally opens zunder at the end.
- Becomes a release asset,
zunder-<v>-setup.exe. The zip stays for portable use.
- Built with
-
Later: a winget manifest pointing at the GitHub release, so
winget install zunderworks. No update server of our own; the 0.2 update notice covers Windows too.
Done when: download setup.exe → Next, Next → zunder opens centered, with glyphs and app
icons, and comes back after a reboot. Tested on real Windows.
- "Install fd" / "Install cliphist" results: Enter runs the right package manager in a terminal
-
Setup as a page inside zunder.
setupopens a page likesettings: toggles for the keys, switching over from other launchers, autostart, theme and example plugins, and an Apply row. It uses the same logic aszunder setup(src/setup.rs), so the terminal version stays for scripts. On Windows and macOS it shows what applies there (hotkey, autostart). -
Tray icon. A small zunder icon while the server runs, with a menu: open, settings,
reload, quit. StatusNotifierItem over D-Bus on Linux (
ksni), the notification area on Windows, the menu bar on macOS.
All in src/providers/windows.rs, src/ui.rs and src/platform/linux.rs.
-
Window actions in ALT+TAB and search:
-
Ctrl+Wclose -
Ctrl+1…9move to that workspace -
Ctrl+Ffullscreen -
Ctrl+Msend to the minimized special workspace -
Ctrl+Shift+Enterfocus and pull to the current workspace
Implementation:
- New
platformfunctions:close_window,move_window(ws),fullscreen_window. On Linux they use hyprctl Lua dispatch; on the other systems they're no-ops for now. - The footer shows the keys when a window is selected.
-
-
An action menu (
Ctrl+K) for any item:- Windows: the actions above.
- Files: open, open folder, copy path, open in terminal.
- Apps: launch, launch new window, show the desktop file.
- Plugins can declare extra actions with a new
"actions": [...]field in their JSON lines (providers/plugins.rsLine).
-
Switcher options in Settings → Switcher:
- Group by workspace.
- Current workspace only.
- Windows of the same app next to each other.
Done when: you can close or move any window without leaving the keyboard, and there's a GIF of it for the 0.3 post.
-
A registry repo,
elyesghazel/zunder-plugins:- One folder per plugin: script, README, screenshot.
- An
index.jsonbuilt by CI in that repo.
-
CLI:
zunder plugin search|install|update|remove <name>- Downloads into
~/.config/zunder/plugins/<name>, pinned to a commit. - Settings → Plugins gets install and remove buttons.
-
Plugin settings:
- Header
@zunder.setting key "help", stored inconfig.tomlunder[plugins.<name>]. - Passed to the script as environment variables (
ZUNDER_<KEY>).
- Header
-
Starter set, generic:
- github, quicklinks, kill, ip
- wifi (nmcli), bluetooth (bluetoothctl), audio output (wpctl)
- pass / gopass
- hyprpicker color, screenshot (grim + slurp), systemd units
- an
agendathat uses the ICS URL from settings
-
Streaming results: a slow plugin can print lines as it goes, and they appear live.
Extends the
deferredjob channel insrc/ui.rs.
Done when: zunder plugin install wifi works, and the README links the registry with at least
10 plugins.
Needs: a GitHub OAuth app with Device Flow turned on.
-
Login:
zunder login(also a button in Settings → Account). Uses the GitHub device flow: zunder shows a code, you confirm on github.com. -
Where the token lives: the system keyring through
oo7/secret-service; the file~/.local/share/zunder/tokenwith 0600 permissions as fallback. -
What syncs:
- A private gist "zunder-sync" with
config.toml,favorites.json,themes/*.cssand the plugin list (names and commits, not the scripts). -
binds.jsonper system, because keybinds don't carry over between OSes.
- A private gist "zunder-sync" with
-
When it syncs:
- Pulls when the daemon starts and every hour.
- Pushes whenever any of these files changes.
- Last write wins, with the previous version kept as
*.bak.
Done when: you log in on two machines (one headless, with --no-ui) and favorites,
themes and plugins match on both.
Everything goes behind the existing src/platform/{macos,windows}.rs functions.
macOS:
- Window list and focus:
CGWindowListCopyWindowInfoplus the AX API. - Previews:
ScreenCaptureKit. - Hotkeys: the
global-hotkeycrate. - Clipboard history: our own watcher with
arboardpolling. - A centered
NSPanel, through theobjc2crates. - Login item.
- Signed and notarized dmg, once there is an Apple Developer account. Without it the first launch stays right-click → Open.
Windows:
- Window list:
EnumWindows. - Previews:
DwmRegisterThumbnailorPrintWindow. - Hotkeys:
RegisterHotKey. ALT+TAB itself needs a low-level keyboard hook, opt-in. - Clipboard history:
AddClipboardFormatListener.
-
Raycast extension bridge. An optional
zunder-raycastpackage: a small Node runtime that implements part of@raycast/api(List,Detail,ActionPanel,preferences,LocalStorage,Clipboard,open). It talks to zunder over the plugin JSON protocol.- Start with List-type extensions only.
- vicinae does something similar, so it's proven possible. It's also big: weeks of work and a moving API.
- Only if 0.2–0.5 show real demand.
- A lighter UI on Windows and macOS. If the bundled GTK keeps the installers above ~10 MB or feels slow, a native UI for those two (Slint, or AppKit / Win32) reusing the same engine. Decide after 0.6, based on numbers.
-
1.0 means:
- The
config.tomland plugin protocol are frozen and documented. - No known crashers.
- Packages for Arch (AUR), Windows (winget) and macOS (Homebrew cask).
- People besides you use it daily.
- The
Not doing: an AI chat, a notes app, snippets/text expansion, a calendar UI, an update server of our own. Plugins and the GitHub release page cover these.
- Who it's for: Hyprland users who rice their setup and live in the terminal. That's a small group, but it talks a lot, and it's where launchers like rofi, wofi and vicinae spread.
-
Each release: a 20–30 s GIF of the one new thing.
- Post on r/unixporn (tag [Hyprland], with a rice details comment) and r/hyprland.
- Share in the Hyprland Discord #showcase.
-
Once:
- A pull request adding zunder to the app launcher list on the Hyprland wiki
(
hyprwm/hyprland-wiki). - A pull request to
hyprland-community/awesome-hyprland. - Post the AUR package once registration reopens.
- Show HN at 0.3, when the switcher actions make a strong demo.
- A pull request adding zunder to the app launcher list on the Hyprland wiki
(
- Listen: issue templates for bugs, ideas and plugin requests, and a pinned "what should zunder do next?" discussion. Build what several people ask for.
-
Watch: GitHub stars, issues from people other than you, AUR votes and release download
counts (
gh release view).
- CI green on Linux, macOS and Windows (
.github/workflows/ci.yml), with clippy on strict on Linux. - A release dry run (
gh workflow run release.yml) succeeds, and the downloaded files contain the binary or bundle. The same check I did for 0.1. - A manual checklist on Hyprland, with a demo daemon on a private socket:
- ALT+TAB with previews
- search and files
- clipboard text and image previews
- time, currency, theme switch
- the settings window
- Windows releases: install
setup.exeon the Windows laptop and check that it's centered, has glyphs and icons, and autostarts. macOS releases: someone opens the dmg on a real Mac. - Then
packaging/bump.sh <version>, check the release page, and post the GIF.