Skip to content

Repository files navigation

VibeLaunch

One launcher for every AI on your machine. Detects what you have installed, tells you whether each one runs in a terminal or as a desktop app, and opens it into a window position you chose.

Tauri 2 + Rust backend, vanilla TypeScript frontend, no runtime dependencies.

Running it

npm install
npm run tauri dev

How detection works

src-tauri/src/detect.rs runs four probes against the catalog in catalog.rs:

  1. PATH — resolved through PATHEXT, plus .ps1 so npm/pip shims count.
  2. Install directories — known folders per tool, with a fallback that picks the first plausible .exe when an app renames its binary between versions.
  3. Uninstall registryDisplayName matched on word boundaries so Jan does not match Japanese Language Pack.
  4. Microsoft Store (MSIX) packages — how Claude, ChatGPT and Copilot ship. These leave no uninstall entry and live in C:\Program Files\WindowsApps, which a normal user cannot even list, so the only route without elevation is the per-user mirror of the package repository in HKCU. They also cannot be started by running their executable — the launcher activates them through the shell with explorer.exe shell:AppsFolder\<AUMID>.

Each tool comes back as cli, desktop, both, or missing. Missing tools stay in the list with a link to install them (toggleable in Settings).

Adding a tool is one struct in catalog.rs.

Window placement

Placements are stored as fractions of the monitor's work area, not pixels, so a layout survives a resolution change or moving to a different display.

Finding the window to move is the tricky part: wt.exe and Electron launchers hand off to an existing process, so the pid you spawned often does not own the window that appears. placement.rs snapshots every top-level window before the launch, then prefers a pid match but accepts any window that appeared afterwards. The wait happens on a background thread and reports back via a placement-result event, so the UI never blocks.

Design

The chrome is deliberately colourless. The only colour in the interface comes from the AI brands themselves — a tinted UI would compete with the thing the app exists to list.

  • Type is Geist, bundled via @fontsource-variable rather than a CDN, since the app runs offline under a 'self' CSP. Numerals are tabular so coordinates do not reflow as they change.
  • Surfaces are nested enclosures: an outer tray with a hairline, an inner core with its own top highlight. Radii are derived, never guessed — inner = outer - padding.
  • No pill badges, no tinted icon tiles, no per-row buttons. Metadata is typographic, logos sit bare, and the action only materialises on the row you are actually on. Brand colour enters as a 2px rule on the active row.
  • One easing curve (cubic-bezier(0.32, 0.72, 0, 1)) everywhere. Rows animate in on arrival only, not on every keystroke.

brandColor() in ui.ts handles a wrinkle from the catalog: brands that register a near-black mark carry a lightened stand-in so they read on the dark themes, which makes them invisible on the light one. It darkens those back down, gated on chroma — a grey stand-in can go dark with nothing lost, but a saturated colour cannot, since yellows read as "bright" by luminance and darkening amber to the same target just turns it to mud.

Settings

Theme (5 palettes), accent source, compact rows, show/hide missing tools, sort order, terminal choice (Windows Terminal / pwsh / PowerShell / cmd / custom template with {cmd}), working directory, CLI-vs-app preference, hide-on-launch, placement toggle, focus toggle, and placement timeout.

Config lives at %APPDATA%\VibeLaunch\config.json and is written atomically.

Icons

src/logos.ts resolves each tool's mark from the best source available:

  1. Your own file, dropped in %APPDATA%\VibeLaunch\icons\ and named after the tool id — claude-code.svg, devin.png, and so on. SVG, PNG, WebP, JPEG or GIF. This wins over everything, so any mark that is wrong or missing can be fixed without touching the code. Settings → Custom icons opens the folder; hit Rescan afterwards.
  2. Official brand marks from simple-icons (CC0-1.0), vendored into src/brand-icons.ts by npm run icons. Generating at build time keeps the 3400-icon package out of the bundle. These are bare glyphs, so they take the tool's brand colour and sit on the tile with no box of their own.
  3. The app's own .exe icon. src-tauri/src/icons.rs calls SHDefExtractIconW and converts the HICON to RGBA. Most app icons are a glyph on a solid tile, so it flood-fills inward from the edges to erase that tile and crops to the remaining artwork — the launcher shows the logo, not a box inside a box. The fill bails out unless the border is convincingly uniform, which leaves edge-to-edge artwork alone.
  4. A hand-drawn mark, for the few tools with none of the above — OpenAI (who asked to be removed from simple-icons), Grok, and some smaller CLIs.

Raster icons from either source (2 or 3) run through the same clean-up in icons.rs: strip the background, crop to the artwork, downscale to 128px with premultiplied alpha so the old backdrop cannot halo the edges.

The backend then classifies each one and tells the UI how to draw it:

Treatment When How it renders
normal bright enough already as-is
tint near-black and sparse silhouette filled with the brand colour
invert near-black and solid lightness flipped in CSS

That split matters. A logo like Devin's is a bare glyph, so its alpha channel is the artwork and filling it with the brand colour loses nothing. A logo like Codebuff's is a filled tile with the glyph knocked out of it — masking that would collapse it into a featureless square, so it gets inverted instead, which keeps the internal detail. Inversion is disabled under the light theme, where the original artwork is already the readable one.

A note on source files: JPEG cannot store transparency, so a .jpg grabbed off a web page arrives with its background baked in. The stripper removes a flat one, but a gradient or photographic backdrop will survive. SVG or a transparent PNG gives the best result.

Brand colours follow the official hex wherever it stays legible on a dark background. Cursor, Ollama, Copilot, LM Studio, OpenCode, and Windsurf register near-black, which is useless as an outline colour, so those use a lightened stand-in. The icons themselves are always the official artwork.

Tests

cd src-tauri && cargo test

Covers env expansion, registry name matching, installer-leftover filtering, shell quoting, terminal selection, placement math, and config migration.

To see what the detector finds on your machine:

cargo test real_scan -- --ignored --nocapture

Building

npm run tauri build

Produces an NSIS setup .exe, an .msi, and a standalone vibelaunch.exe in src-tauri/target/release/. They are small because Tauri uses the system WebView2 rather than bundling a browser.

The binaries are not code-signed, so Windows SmartScreen will warn on first run until you click through More info → Run anyway.

Licence

MIT — see LICENSE.

The brand marks in src/brand-icons.ts are generated from simple-icons, which is CC0-1.0. No trademarked artwork is redistributed in this repository: icons for apps installed on your machine are read from those apps at runtime, and any icon you supply yourself lives in %APPDATA%\VibeLaunch\icons\, outside the repo.

Product names and logos belong to their respective owners. VibeLaunch is not affiliated with, endorsed by, or sponsored by any of the tools it launches.

About

One launcher for every AI on your machine — finds what's installed, tells you whether it runs in a terminal or as a desktop app, and opens it into a window position you chose.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages