Commit d97639c
authored
Add Neutralinojs desktop client app (#1)
* Add Neutralinojs desktop client app
Lightweight (~2MB) desktop wrapper that connects to a local or remote
Vibora server. Uses system webview via Neutralinojs. Includes packaging
scripts for Linux AppImage and macOS DMG.
* Use settings.json instead of separate desktop-settings.json
* Fix: use Neutralino.os.getEnv for home directory
* Remove remaining NL_USER reference from console.log
* Add zoom keyboard shortcuts (Cmd/Ctrl +/-/0)
* Add floating zoom controls (hover bottom-right corner)
* Navigate directly to Vibora app instead of iframe
Enables native webview zoom (Cmd+/-) with sharp text rendering.
Removes custom zoom controls since native zoom works better.
* Restore iframe with floating zoom controls
Native webview zoom doesn't work via keyboard shortcuts, so using
iframe with CSS transform zoom and floating controls instead.
* Use CSS zoom instead of transform scale for sharper rendering
* Persist zoom level to settings
* Revert to transform scale zoom with floating buttons
* Implement native zoom via root font-size
Desktop client passes zoom level as query parameter (?zoom=1.25).
Frontend reads this and sets html font-size (e.g., 20px for 125%).
Since shadcn/ui uses rem units, all UI scales natively and sharply.
* Preserve current page when changing zoom level
* Add zoom debugging logs
* Use Neutralino.debug.log and try contentWindow for current URL
* Clean up zoom code, document cross-origin limitation
* Add postMessage bridge for route preservation on zoom
- Frontend posts route changes to parent window via postMessage
- Desktop client listens and stores current route
- Zoom reload now preserves the current page
* Apply desktop zoom to terminal font size
xterm.js uses pixel-based fonts, so it needs explicit zoom scaling
* Add native desktop notifications via postMessage bridge
- Frontend posts notifications to parent window when in iframe
- Desktop client receives and shows native OS notifications via Neutralino
* Add Neutralinojs desktop client app
Lightweight (~2MB) desktop wrapper that connects to a local or remote
Vibora server. Uses system webview via Neutralinojs. Includes packaging
scripts for Linux AppImage and macOS DMG.
* Use settings.json instead of separate desktop-settings.json
* Fix: use Neutralino.os.getEnv for home directory
* Remove remaining NL_USER reference from console.log
* Add zoom keyboard shortcuts (Cmd/Ctrl +/-/0)
* Add floating zoom controls (hover bottom-right corner)
* Navigate directly to Vibora app instead of iframe
Enables native webview zoom (Cmd+/-) with sharp text rendering.
Removes custom zoom controls since native zoom works better.
* Restore iframe with floating zoom controls
Native webview zoom doesn't work via keyboard shortcuts, so using
iframe with CSS transform zoom and floating controls instead.
* Use CSS zoom instead of transform scale for sharper rendering
* Persist zoom level to settings
* Revert to transform scale zoom with floating buttons
* Implement native zoom via root font-size
Desktop client passes zoom level as query parameter (?zoom=1.25).
Frontend reads this and sets html font-size (e.g., 20px for 125%).
Since shadcn/ui uses rem units, all UI scales natively and sharply.
* Preserve current page when changing zoom level
* Add zoom debugging logs
* Use Neutralino.debug.log and try contentWindow for current URL
* Clean up zoom code, document cross-origin limitation
* Add postMessage bridge for route preservation on zoom
- Frontend posts route changes to parent window via postMessage
- Desktop client listens and stores current route
- Zoom reload now preserves the current page
* Apply desktop zoom to terminal font size
xterm.js uses pixel-based fonts, so it needs explicit zoom scaling
* Add native desktop notifications via postMessage bridge
- Frontend posts notifications to parent window when in iframe
- Desktop client receives and shows native OS notifications via Neutralino
* Add desktop:dev command for development mode
- Add DEV_PORT constant (5173) for Vite dev server
- Detect --dev flag via NL_ARGS in Neutralino
- Use dev port when --dev flag is present
- Add desktop:dev mise task that passes --dev to neu run
* Add pre-generated icon.icns for macOS notifications
- Generate icon.icns from icon.png for macOS app bundle
- Update package-dmg.sh to use pre-generated icns if available
- Packaged .app will show Vibora icon in notifications
* Fix DMG packaging to use resources.neu bundle
* Fix macOS app bundle: resources.neu must be in MacOS folder
* Add bundled server and Claude plugin to desktop app
- Add desktop:bundle task to bundle server, PTY libs, migrations, and plugin
- Create vibora-launcher.sh that:
- Checks for bun/dtach dependencies with helpful error dialogs
- Installs Claude plugin to ~/.claude/plugins/vibora/
- Starts bundled server before launching Neutralino UI
- Cleans up server on exit
- Update package-dmg.sh to depend on bundle and include server
- Desktop app now runs standalone with bundled backend
* Add desktop app releases to GitHub Actions with auto-update support
- Multi-platform builds: macOS (arm64/x64), Linux (x64)
- AppImage now bundles server like DMG does
- Update manifest.json generated for each release
- Desktop app checks for updates on startup (daily)
- Version sync enforcement via check:version task
- Pre-commit hook blocks commits with mismatched versions
- README updated: desktop app as recommended installation
- Remote server setup documentation added
* Add desktop build artifacts to .gitignore
* Add plain English license summary to README
Clarify that users can use Vibora for personal or commercial purposes,
we have no claim over software they build with it, and only prohibit
reselling Vibora itself for profit.
* Remove text remnants from icon images
Crop snake head cleanly from logo without the tops of "VIBORA" letters
showing as white lines at the bottom.
* Rename hostname to remoteHost and simplify desktop connection flow
- Rename `hostname` setting to `remoteHost` for clarity (backward compatible)
- Simplify desktop app: always start local server, prompt if remote configured
- Update VSCode URL builder, hooks, settings UI, and locale strings
- Add migration in settings.ts to read old `hostname` if `remoteHost` not set
- Update README and desktop/README with new connection flow docs
* Compile desktop server as standalone executable
- Use `bun build --compile` to create a self-contained binary
- No longer requires Bun to be installed on end-user machines
- Only runtime dependency is dtach for terminal persistence
- Add common paths to launcher PATH for GUI app compatibility
* bump major
* Remove unused xtermReady state variable
* Fix WebKit terminal detection using initial terminal IDs snapshot
Replace unreliable weCreatedTerminalRef with initialTerminalIdsRef approach
that captures terminal IDs when terminals first load. A terminal not in
this initial set is considered "new" and triggers Claude startup commands.
This is more robust against WebKit timing issues during navigation.
* Ensure clean builds by removing dist/ and forcing tsc rebuild
Prevents stale cached artifacts from contaminating production builds.
* Fix race condition by capturing terminal IDs during render
Move initial terminal ID capture from useEffect to synchronous
render-time code. Effects run after render and could interleave
with async terminal list updates, causing the capture to include
newly created terminals. Running during render ensures IDs are
captured before any effects (including createTerminal) execute.
* Fix task terminal startup race condition in React Strict Mode
- Move synchronous guards before async operations to prevent duplicate
startup command execution when React Strict Mode re-runs effects
- Add debug logging gated behind VITE_VIBORA_DEBUG env var
- Enable debug logging by default in dev mode (mise run dev)
- Add /api/debug endpoint for frontend-to-server logging
* Fix monitoring features for macOS compatibility
- Memory metrics: Use vm_stat to calculate used/cache/free memory properly
- Disk usage: Query /System/Volumes/Data for accurate APFS usage
- Top processes: Add getTotalMemory() helper using sysctl hw.memsize
- Claude instances: Use ps -Axo for dtach socket detection
- Process cwd: Improve lsof parsing for working directory
- Process start time: Add ps -o lstart= fallback
- OAuth token: Add macOS Keychain lookup via security command
All monitoring endpoints now work correctly on macOS instead of
failing due to Linux-specific /proc filesystem assumptions.
* Fix terminal/tab deduplication and task terminal navigation
- Add state-level deduplication for terminals and tabs to prevent
duplicates from WebSocket broadcasts to multiple clients
- Add createdByMeRef tracking in TaskTerminal to properly detect
when THIS component created a terminal (vs another instance)
- Reset terminal tracking refs when cwd changes to fix blank screen
when navigating between tasks
- Add pending creation guards to prevent duplicate terminals/tabs
from double-clicks or React Strict Mode
- Add debug logging for terminal operations (gated behind VITE_VIBORA_DEBUG)
* Add centralized JSONL logging system for AI-friendly debugging
- Create shared logger types (LogEntry, Logger interface) in shared/logger/
- Add backend logger (server/lib/logger.ts) that writes JSON lines to stdout and ~/.vibora/vibora.log
- Add frontend logger (src/lib/logger.ts) with batching to /api/logs endpoint
- Migrate all console.log/error/warn calls across backend and frontend to use structured logger
- Add /api/logs endpoint for frontend log batching, keep legacy /api/debug for compatibility
- Update CLAUDE.md with logging documentation and search examples
- Add debug build tasks in mise.toml for LOG_LEVEL=debug builds
The JSONL format enables efficient AI searching with grep/jq while preserving
structured context for each log entry.
* Fix task terminal blank screen race condition in desktop app
When creating task terminals, start() and attach() were called in rapid
succession (within 16ms). The start() method spawned `dtach -n` which
exits immediately after creating the socket, but was storing the PTY
reference in this.pty. When attach() was called before dtach -n exited,
it would return early due to `if (this.pty) return`, preventing the
actual attachment via `dtach -a`.
The fix: start() now uses a local `creationPty` variable instead of
setting this.pty, so attach() always proceeds to spawn `dtach -a` and
set up the proper data handlers.
Also includes enhanced diagnostic logging for terminal output flow.1 parent 1a8dca5 commit d97639c
60 files changed
Lines changed: 3900 additions & 299 deletions
File tree
- .claude-plugin
- .github/workflows
- desktop
- resources
- icons
- js
- scripts
- hooks
- plugins/vibora/.claude-plugin
- public
- server
- lib
- routes
- services
- terminal
- websocket
- src
- components
- terminal
- ui
- hooks
- i18n/locales
- en
- zh
- lib
- routes
- repositories
- settings
- tasks
- terminals
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
11 | | - | |
| 11 | + | |
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
15 | 19 | | |
16 | 20 | | |
17 | 21 | | |
| |||
53 | 57 | | |
54 | 58 | | |
55 | 59 | | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
56 | 64 | | |
57 | 65 | | |
58 | 66 | | |
| |||
61 | 69 | | |
62 | 70 | | |
63 | 71 | | |
| 72 | + | |
| 73 | + | |
64 | 74 | | |
65 | 75 | | |
66 | 76 | | |
| |||
84 | 94 | | |
85 | 95 | | |
86 | 96 | | |
87 | | - | |
| 97 | + | |
88 | 98 | | |
89 | 99 | | |
90 | 100 | | |
91 | 101 | | |
92 | 102 | | |
| 103 | + | |
93 | 104 | | |
94 | 105 | | |
95 | 106 | | |
96 | 107 | | |
97 | 108 | | |
98 | 109 | | |
99 | 110 | | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
100 | 120 | | |
101 | 121 | | |
102 | 122 | | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
40 | 40 | | |
41 | 41 | | |
42 | 42 | | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
43 | 48 | | |
44 | 49 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
146 | 146 | | |
147 | 147 | | |
148 | 148 | | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
149 | 227 | | |
150 | 228 | | |
151 | 229 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
17 | | - | |
| 17 | + | |
18 | 18 | | |
19 | | - | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
20 | 39 | | |
21 | 40 | | |
22 | 41 | | |
23 | 42 | | |
24 | 43 | | |
25 | 44 | | |
26 | 45 | | |
27 | | - | |
| 46 | + | |
28 | 47 | | |
29 | 48 | | |
30 | 49 | | |
| |||
37 | 56 | | |
38 | 57 | | |
39 | 58 | | |
40 | | - | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
41 | 86 | | |
42 | 87 | | |
43 | 88 | | |
44 | 89 | | |
45 | 90 | | |
46 | 91 | | |
47 | 92 | | |
48 | | - | |
49 | | - | |
50 | 93 | | |
51 | 94 | | |
52 | 95 | | |
| |||
59 | 102 | | |
60 | 103 | | |
61 | 104 | | |
62 | | - | |
| 105 | + | |
63 | 106 | | |
64 | 107 | | |
65 | 108 | | |
| |||
189 | 232 | | |
190 | 233 | | |
191 | 234 | | |
| 235 | + | |
| 236 | + | |
0 commit comments