Commit Graph

24 Commits

Author SHA1 Message Date
vasyansk 9db52595c7 Add update check functionality
Implement version checking and update notifications in the GUI
2026-06-15 14:23:30 +07:00
vasyansk 0a26e77899 fix(app): drop blocking version-handshake; Shutdown is fire-and-forget
The handshake ran synchronously in Bridge::connect: on a build-id mismatch it
sent Cmd::Shutdown and awaited a reply that never flushes (the daemon exits
first), so request() hit its 5s timeout and the reconnect-retry respawned the
daemon and re-sent Shutdown — a loop that produced repeated 'spaceshd
listening' lines and a multi-second launch delay. The id stamps also differed
between the separately-built daemon and GUI, so it fired on normal launches.

- Remove the handshake auto-restart; `make install`/`reinstall` already kill
  and replace the daemon reliably. health.build stays for display in Settings.
- Shutdown now goes through a fire-and-forget send (no reply wait, no retry),
  fixing the same loop for the Settings Restart button.
- Makefile: `make app-bundle` builds just the .app via `tauri build --bundles
  app` (no .dmg, no hdiutil) and `reinstall` uses it — faster self-update that
  can't hang on a mounted DMG volume.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 13:58:04 +07:00
vasyansk f9a565a712 feat(app): clear all events from the Event Center (red trash icon)
Adds Cmd::ClearEvents + Evt::EventsCleared: the daemon drops the persistent
event log (keeping next_id monotonic), persists, and broadcasts so every
client empties its list. A red trash icon next to 'Mark all read' triggers it;
disabled when the list is empty. Threaded through proto, the daemon handler,
the Tauri bridge, and socketBridge. Includes an EventLog::clear test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 13:38:35 +07:00
vasyansk cf7410b46a feat(app): version handshake — GUI restarts a stale running daemon
The daemon outlives the GUI, so after an update an OLD daemon can keep serving
the socket and the new GUI just connects to it (stale code — e.g. the missing
TERM fix). Both binaries are now stamped with the git build id (build.rs):
the daemon reports it in `health.build`, and on connect the bridge compares it
to the GUI's own SPACESH_BUILD; on mismatch it shuts the daemon down and lets
ensure_daemon respawn the bundled (matching) one. No-op for unstamped dev
builds or daemons too old to report a build. Build id is shown in Settings.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 12:39:46 +07:00
vasyansk c84b96abc0 fix(app): abort the old reader on reconnect (fixes doubled keystroke echo)
reconnect() spawned a new reader/writer but left the previous reader task
running. A reconnect triggered while the old connection was still alive (e.g.
a request timing out during a slow daemon start) left TWO live connections;
the daemon broadcast Output to both, so every byte — including input echo —
arrived twice ("ccucurcurl"). The bridge now stores the reader's JoinHandle
and aborts it before establishing the new connection, guaranteeing a single
live reader.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 11:56:35 +07:00
vasyansk a7272fc92e fix(bundle): ship spaceshd inside the .app (packaged GUI was offline)
tauri build bundled only the GUI binary, so the packaged app had no daemon:
find_daemon() looks for a sibling `spaceshd` next to the GUI
(Contents/MacOS/spaceshd) and ensure_daemon failed to spawn it → "offline".
(Dev worked only because find_daemon falls back to the repo-root target path.)

- tauri.bundle.conf.json: a build-only overlay adding bundle.externalBin
  ["bin/spaceshd"], kept out of tauri.conf.json so `tauri dev` doesn't require
  a sidecar file.
- Makefile: `make dmg` now builds spaceshd for both arches, lipo's a universal
  sidecar into src-tauri/bin/spaceshd-universal-apple-darwin, and passes
  --config so it lands in Contents/MacOS/spaceshd. `make dmg-native` does the
  host-arch equivalent.
- .gitignore: ignore the generated app/src-tauri/bin/.

After install, the unsigned helper runs once quarantine is cleared recursively:
xattr -dr com.apple.quarantine /Applications/spacesh.app

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 11:39:03 +07:00
vasyansk 9ca0164d0b fix(app): bridge auto-reconnect so daemon restart no longer bricks the GUI
The Tauri bridge connected to the daemon once at startup and held a single
stream with no recovery: when the daemon exited (Restart/Stop, crash, or an
update), the reader emitted spacesh:disconnected and died, and every later
request went through the dead writer forever — the GUI was permanently stuck
(settings frozen, offline). Since the bridge is Rust-side state that survives
a webview reload, even Cmd+R didn't recover it.

- bridge.rs: requests now reconnect-and-retry on failure with a single-flight
  guard (generation counter) so concurrent failures collapse into one
  reconnect and never open duplicate connections; a 5s reply timeout catches
  silently-dropped connections. ensure_daemon respawns the daemon if it
  exited. On success the bridge emits spacesh:reconnected.
- App.tsx: on spacesh:reconnected, bump a connection epoch that keys
  LayoutEngine, remounting terminals so they re-attach (snapshot + live stream)
  to the restarted daemon; also reload health/config/status.
- Settings: drop the Stop button — with lazy daemon spawn any GUI request
  resurrects the daemon, so an in-GUI "stop" is contradictory. Restart now
  works end to end (shutdown → reconnect respawns → panels re-attach).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 10:22:24 +07:00
vasyansk 9ca1ff3bc5 feat(app): daemon status with Stop/Restart in settings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 18:57:53 +07:00
vasyansk 62f1f8e9a8 feat(app): tauri get_config/set_config bridge commands 2026-06-14 09:36:53 +07:00
vasyansk 7b47052a6f feat(spaceshd): pinned workspace field
Add a `pinned` bool to the workspace model, threaded through proto
(Workspace + WorkspaceView + SetWorkspaceMeta), the registry, the
set_workspace_meta handler, persistence, the CLI mapping, and the Tauri
bridge. serde(default) keeps existing state.json compatible (pinned=false).
Backs the sidebar Favorites section.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 08:56:09 +07:00
vasyansk 0014d9358d feat(app): app icon and macOS bundle config
The dock showed a black square because tauri.conf.json had no bundle.icon
block and icons/ held only tiny placeholders with no .icns. Add an SVG
source logo (terminal prompt + split workspace panes), generate the full
icon set via `tauri icon` (icon.icns/.ico + all PNG/iOS/Android sizes), and
wire bundle.icon so the daemon embeds and macOS renders it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 08:00:54 +07:00
vasyansk daf87d3c09 feat(app): panel zoom — full-grid render + header toggle
Wire Cmd::SetZoom through Tauri bridge (set_zoom command), add zoomed
field to WorkspaceView, short-circuit LayoutEngine to render only the
zoomed panel full-grid, and toggle Maximize2/Minimize2 in panel header.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 12:33:46 +07:00
vasyansk defceb1169 feat(app): real daemon health footer (live, uptime, version)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 12:09:35 +07:00
vasyansk c9ed522230 chore(app): regenerate Tauri schemas for window-state permission
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 09:25:43 +07:00
vasyansk 7d8e39ca3d feat(app): remember window size/position across restarts (window-state plugin)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 09:25:29 +07:00
vasyansk d08a446b6f feat(app): event_log and mark_read bridge commands 2026-06-10 08:19:22 +07:00
vasyansk 92706c0780 fix(app): robust spaceshd discovery for tauri dev + non-fatal connect
The app is its own cargo workspace, so in 'tauri dev' the app binary lives
in app/src-tauri/target/ and spaceshd is NOT a sibling — lazy-start failed
and the .expect() crashed the window. Now: find_daemon tries SPACESHD_BIN,
sibling, repo-root target/{debug,release}, then PATH; bridge honors
SPACESH_SOCK like the daemon/CLI; setup logs instead of panicking.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 23:31:52 +07:00
vasyansk c86627153d chore(app): lock notification plugin deps + regenerated tauri schemas
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 23:14:28 +07:00
vasyansk 1ecefdeb80 feat(app): Event Center, native notifications, auto-unread, state wiring in App
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 23:13:00 +07:00
vasyansk d36548ff39 feat(app): status rings on panels + sidebar aggregate badge from state events
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 23:11:46 +07:00
vasyansk c9c3ba1fcd feat(app): wire set_group/delete_group bridge commands
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 21:41:34 +07:00
vasyansk 0320a2f313 feat(app): tauri bridge commands for M2 (split/ratios/move/preset/restart/groups/meta)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 21:30:26 +07:00
vasyansk 56893c51d0 feat(app): UDS bridge (channel/invoke/emit) + xterm.js terminal, M0 e2e works
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 20:24:57 +07:00
vasyansk 1579686fdd chore(app): scaffold tauri 2 + react + vite, window launches
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 20:22:46 +07:00