Add Gitea package registry support
Add publish-dmg target for versioned DMG uploads
Update deploy-dmg to include Gitea publishing
Document Gitea token requirements in README
Update UI to use CloudDownload icon instead of RefreshCw
Improve error handling for update checks
Add better visual feedback for update states
Change GitHub link to RealManual repository
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>