Skip to main content

Config reference

~/Library/Application Support/com.oddinteractive.vindue/config.json

Edit it in the Settings window — all changes apply immediately (the panel live-reloads on save; hotkey changes re-register live too) — or by hand. Top-level keys mirror the Settings UI sections 1:1. Defaults are no hotkey, zero gaps/margins, no shortcuts, API on:

{
"version": 1,
"grid": {
"rows": 6,
"cols": 6,
"windowGap": { "width": 0, "height": 0 },
"screenMargins": { "top": 0, "right": 0, "bottom": 0, "left": 0 }
},
"keybindings": { "openPanel": "" },
"shortcuts": {
"assignment": "pinned",
"keys": {
"Digit1": {
"monitor": null,
"selection": { "startRow": 0, "endRow": 5, "startCol": 0, "endCol": 2 }
},
"Backquote": {
"monitor": { "name": "DELL U2720Q (left)", "index": 1 },
"selection": { "startRow": 0, "endRow": 5, "startCol": 0, "endCol": 5 }
}
}
},
"api": { "port": 47725, "enabled": true }
}

Fields​

FieldMeaning
grid.rows / grid.colsGrid dimensions, 1–12
grid.windowGapGap between adjacent placed windows (each inset by half)
grid.screenMarginsScreen-edge insets (0–300, physical px)
keybindings.openPanelGlobal panel toggle, parsed by tauri-plugin-global-shortcut (e.g. Cmd+Shift+Space, Ctrl+Alt+D); "" = none — the tray icon always opens the panel
shortcuts.assignmentWhat a panel-mode assignment (key + drag) saves: "pinned" (default) binds it to the display it was created on; "relative" saves no monitor, so it follows the panel at apply time
shortcuts.keysKey code → { monitor, selection }. monitor: null = relative to the panel's display; otherwise { name, index } of the display it was assigned on (name = canonical label). Easiest created via assignment mode in the panel
api.portLoopback server port, 1024–65535 (default 47725)
api.enabledServe the HTTP API + MCP at all

Key codes are KeyboardEvent.code values (physical positions): Digit1, Backquote, Minus, Semicolon, … — layout-independent.

Versioning​

version is the schema revision. On a version/shape mismatch the config is reseeded to defaults — while the app is pre-1.0 there are no migrations; the seed function marks the seam where real migrations go for shipped releases.

The Rust seed/validation core and the TypeScript defaults are pinned to this exact shape from both directions by tests — see the fixture-parity rule in CONTRIBUTING.md before changing anything here.