forked from bots-garden/ori
| ✨ Workspace panel, selectors, previews, desktop app, sandbox template, resizable file tree, light/dark theme | 1 | # ori-desktop — a Wails desktop shell for ori |
| 2 | ||
| 3 | `ori-desktop` wraps the [ori](../README.md) web client in a native window built with | |
| 4 | [Wails v2](https://wails.io) (Go + the platform's webview). It does **not** reimplement ori: | |
| 5 | the ori server keeps serving the React SPA and the `/ws`, `/api/files`, `/ws/terminal` | |
| 6 | endpoints. The desktop app only | |
| 7 | ||
| 8 | 1. shows a small **connection screen** with the ori server URL (default `http://localhost:8888`), | |
| 9 | remembered between runs in a JSON settings file; | |
| 10 | 2. checks the server with **`GET /healthz`** through a Go-bound method; | |
| 11 | 3. then displays the **ori webapp itself** inside the window. | |
| 12 | ||
| 13 | It is a separate Go module (`rickub.com/bots-garden/ori-desktop`), so the parent | |
| 14 | repository's `go test ./...` and `make build` are unaffected. | |
| 15 | ||
| 16 | ## Prerequisites | |
| 17 | ||
| 18 | - Go 1.25+ (the module follows Wails v2.16's requirement). | |
| 19 | - The Wails v2 CLI: `go install github.com/wailsapp/wails/v2/cmd/wails@v2.16.0` | |
| 20 | (then make sure `$(go env GOPATH)/bin` is on your `PATH`). | |
| 21 | - The platform GUI toolchain Wails needs — run `wails doctor` to see what is missing: | |
| 22 | - **Linux**: `gcc`, `pkg-config`, `libgtk-3-dev`, `libwebkit2gtk-4.0-dev` (or `-4.1-dev` | |
| 23 | with `-tags webkit2_41`); | |
| 24 | - **macOS**: Xcode command line tools; | |
| 25 | - **Windows**: WebView2 runtime (preinstalled on Windows 10/11); NSIS only for the installer. | |
| 26 | - A running ori server. From the repository root: `make run` or `make run-mock`. | |
| 27 | ||
| 28 | No Node/npm is needed: the frontend is plain HTML/CSS/JS embedded as-is (no bundler). | |
| 29 | ||
| 30 | ## Run in development mode | |
| 31 | ||
| 32 | ```bash | |
| 33 | cd ori-desktop | |
| 34 | wails dev | |
| 35 | ``` | |
| 36 | ||
| 37 | `wails dev` builds the Go side, opens the window, and reloads the frontend when a file under | |
| 38 | `frontend/src/` changes (`assetdir` in `wails.json`). It also serves the app at | |
| 39 | http://localhost:34115 so you can open it in a normal browser with devtools and still call the | |
| 40 | Go methods. | |
| 41 | ||
| 42 | ## Build a redistributable binary | |
| 43 | ||
| 44 | ```bash | |
| 45 | cd ori-desktop | |
| 46 | wails build # → build/bin/ori-desktop (or .app / .exe) | |
| 47 | # or, from the repository root: | |
| 48 | make desktop | |
| 49 | ``` | |
| 50 | ||
| 51 | Cross-compiling for Windows from Linux/macOS works without any C toolchain: | |
| 52 | `wails build -platform windows/amd64` → `build/bin/ori-desktop.exe`. | |
| 53 | ||
| 54 | ## Tests | |
| 55 | ||
| 56 | The connection logic is pure Go and tested without any GUI dependency: | |
| 57 | ||
| 58 | ```bash | |
| 59 | cd ori-desktop && go test ./internal/... | |
| 60 | # or, from the repository root: | |
| 61 | make desktop-test | |
| 62 | ``` | |
| 63 | ||
| 64 | `internal/settings` covers load/save round trips, defaults on a first run, invalid files and URL | |
| 65 | normalisation; `internal/health` covers `/healthz` against an `httptest` server (healthy, | |
| 66 | non-ori, unreachable, cancelled). | |
| 67 | ||
| 68 | ## How it connects to ori | |
| 69 | ||
| 70 | ``` | |
| 71 | ┌──────────────── ori-desktop (Wails window) ────────────────┐ | |
| 72 | │ frontend/src (embedded, wails:// origin) │ | |
| 73 | │ connection screen ──► window.go.main.App.Connect(url) │ | |
| 74 | │ │ Go: NormalizeURL → GET /healthz → Save settings | |
| 75 | │ ▼ │ | |
| 76 | │ <iframe src="http://localhost:8888/"> ◄── ori server serves the SPA, | |
| 77 | │ SPA opens ws://localhost:8888/ws, /ws/terminal, /api/files as usual | |
| 78 | └─────────────────────────────────────────────────────────────┘ | |
| 79 | ``` | |
| 80 | ||
| 81 | **Why an iframe?** Wails v2 gives no runtime call to navigate the main webview to an external | |
| 82 | URL; the window always shows the embedded frontend (`runtime.BrowserOpenURL` opens the system | |
| 83 | browser instead, which is offered as the "Open in browser" fallback). Loading ori in an | |
| 84 | `<iframe>` keeps a thin native bar (URL, Reload, Open in browser, Disconnect) around the | |
| 85 | untouched ori SPA. This works because ori sends no `X-Frame-Options`/CSP `frame-ancestors` | |
| 86 | header, and because the SPA derives its WebSocket URLs from `window.location` — inside the | |
| 87 | iframe that is the ori origin itself, so ori's `localhost:*` WebSocket origin check passes. | |
| 88 | ||
| 89 | On macOS, `build/darwin/Info.plist` sets `NSAppTransportSecurity/NSAllowsLocalNetworking` so | |
| 90 | App Transport Security allows the plain-HTTP loopback load. | |
| 91 | ||
| 92 | ### Go methods bound to the frontend (`app.go`) | |
| 93 | ||
| 94 | | Method | Purpose | | |
| 95 | | --- | --- | | |
| 96 | | `GetConfig() → settings.Settings` | Remembered settings (defaults on a first run). | | |
| 97 | | `SaveConfig(settings.Settings)` | Normalise the URL and persist. | | |
| 98 | | `CheckHealth(url) → health.Report` | `GET <url>/healthz`; never rejects — `ok`, `statusCode`, `detail`. | | |
| 99 | | `Connect(url) → string` | Normalise, check health, remember the URL, return the base URL to load. | | |
| 100 | | `OpenInBrowser(url)` | `runtime.BrowserOpenURL` fallback. | | |
| 101 | | `SettingsPath() → string` | Where the settings file lives (displayed on the screen). | | |
| 102 | ||
| 103 | The frontend calls them as `window.go.main.App.<Method>()` (Promises). The typed wrappers that | |
| 104 | `wails dev`/`wails build` generate under `frontend/wailsjs/` are gitignored because nothing | |
| 105 | imports them; regenerate them with `wails generate module` if you want the `.d.ts` files. | |
| 106 | ||
| 107 | ### Settings file | |
| 108 | ||
| 109 | `<user config dir>/ori-desktop/settings.json`, i.e. `~/.config/ori-desktop/settings.json` on | |
| 110 | Linux, `~/Library/Application Support/ori-desktop/settings.json` on macOS, | |
| 111 | `%AppData%\ori-desktop\settings.json` on Windows: | |
| 112 | ||
| 113 | ```json | |
| 114 | { | |
| 115 | "serverUrl": "http://localhost:8888" | |
| 116 | } | |
| 117 | ``` | |
| 118 | ||
| 119 | ## Layout | |
| 120 | ||
| 121 | ``` | |
| 122 | ori-desktop/ | |
| 123 | ├── main.go Wails bootstrap (window options, embedded assets, bindings) | |
| 124 | ├── app.go Go methods bound to the frontend | |
| 125 | ├── wails.json Wails project configuration | |
| 126 | ├── internal/settings/ settings file (load/save/normalise) + tests | |
| 127 | ├── internal/health/ GET /healthz probe + tests | |
| 128 | ├── frontend/src/ index.html, main.css, main.js (embedded ES module, no bundler) | |
| 129 | └── build/ Wails build assets (icon, Info.plist, Windows manifest); build/bin is the output | |
| 130 | ``` | |
| 131 | ||
| 132 | ## Status / known limitations | |
| 133 | ||
| 134 | - Verified so far: unit tests, `go vet`, and a Windows cross-build (`wails build -platform | |
| 135 | windows/amd64`) from a Linux sandbox without GUI libraries. The window itself has not yet been | |
| 136 | launched (no WebKitGTK in that sandbox) — see the repository's `.memory/` notes. | |
| 137 | - ori is loaded over plain HTTP; use it against local or trusted servers only. | |
| 138 | - Ori's `--addr` other than `:8888` simply means typing the matching URL on the connection screen. |