Commit Graph

178 Commits

Author SHA1 Message Date
vasyansk 60383cd543 feat(daemon): snapshot ticker + writer wiring + stopped-attach reads disk + cleanup on close
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 16:00:39 +07:00
vasyansk 69f2e73832 feat(daemon): snapshot writer task (Save/Remove over one channel)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 15:49:29 +07:00
vasyansk 0674872c9d feat(daemon): actor Snapshot message + dirty tracking + final snapshot on exit
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 15:47:40 +07:00
vasyansk 1a7d04aab0 feat(daemon): [resume] config map + snapshot_interval_secs with built-in defaults
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 15:38:30 +07:00
vasyansk bd36a83db2 feat(daemon): per-surface JSON snapshot store (atomic write, corrupt-tolerant)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 15:36:21 +07:00
vasyansk bb5edb941c feat(core): Snapshot derives Deserialize + PartialEq for disk persistence
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 15:30:16 +07:00
vasyansk 4419f5660e wip: in-progress changes (grid, config, wizard, settings, pty) before session-persistence
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 15:28:19 +07:00
vasyansk e37faf49d3 docs: sync session-persistence spec to leaner RestartSurface-based design
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 15:20:02 +07:00
vasyansk 1f69973606 docs: session persistence implementation plan + spec sync to leaner design
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 15:18:55 +07:00
vasyansk 3d54d679d3 docs: session persistence (resurrect + resume) design spec
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 15:05:21 +07:00
vasyansk 95ddf30b8c Update index.html
Build / Build & push landing (push) Successful in 14s
Build / Deploy to prod (push) Successful in 7s
Build / Notify Max (push) Successful in 1s
2026-06-15 14:37:58 +07:00
vasyansk 614d7fea06 Add cloud download icon and improve update UI handling
Build / Build & push landing (push) Successful in 14s
Build / Deploy to prod (push) Successful in 6s
Build / Notify Max (push) Successful in 2s
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
2026-06-15 14:36:30 +07:00
vasyansk 74abea5467 fix(deploy): stable container_name for the proxy (avoid pinned-IP collision)
container_name: spacesh-proxy lets NPM forward by name instead of the fragile
pinned 172.18.0.28, which another webproxy container could grab — sending NPM
to the wrong target (or itself) and looping.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 14:25:31 +07:00
vasyansk fcbf4a69a0 Update VERSION
Build / Build & push landing (push) Successful in 14s
Build / Deploy to prod (push) Successful in 6s
Build / Notify Max (push) Successful in 2s
2026-06-15 14:24:35 +07:00
vasyansk 9db52595c7 Add update check functionality
Implement version checking and update notifications in the GUI
2026-06-15 14:23:30 +07:00
vasyansk 4c9eacccb7 fix(deploy): put landing on proxy's network + runtime DNS resolve
The landing service had no networks: key, so it joined the auto 'default'
network while proxy was only on spaceshell-network + webproxy — they shared no
network, so proxy_pass to 'landing' couldn't resolve. With a static
upstream{ server landing:80 } nginx fails to boot on an unresolvable name and
restart-loops, so the proxy flapped (page intermittently up/down). Fixes:
- landing now joins spaceshell-network (shared with proxy).
- proxy.conf resolves 'landing' at request time via Docker DNS (127.0.0.11)
  using a variable proxy_pass, so nginx starts even if landing is briefly down.
nginx -t passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 14:20:33 +07:00
vasyansk 1423150b10 docs: repoint README image/doc links to DOCS/ after the move
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 14:13:26 +07:00
vasyansk 1b8dd9bd93 feat(app): make 'Mark all read' an icon (CheckCheck) next to the trash
Matches the clear-all trash icon; dimmed/disabled when there are no unread events.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 14:12:45 +07:00
vasyansk 524b3def6b docs: move the user guide to the root README.md
Promotes DOCS/GUIDE.md to the repo root README (image/links repointed to DOCS/).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 14:12:45 +07:00
vasyansk a9af60f5cd docs: Russian user guide with screenshots
DOCS/GUIDE.md — feature walkthrough with the two app screenshots from
landing/pics copied into DOCS/images.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 14:09:42 +07:00
vasyansk 179744d8b3 fixes port
Build / Build & push landing (push) Successful in 16s
Build / Deploy to prod (push) Successful in 11s
Build / Notify Max (push) Successful in 1s
2026-06-15 14:05:18 +07:00
vasyansk e15146af60 Merge fix-launch-delay: no blocking handshake, fire-and-forget shutdown, .app-only reinstall 2026-06-15 13:58:05 +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 75134b6fac add deploy
Build / Build & push landing (push) Successful in 15s
Build / Deploy to prod (push) Successful in 5s
Build / Notify Max (push) Successful in 1s
2026-06-15 13:47:50 +07:00
vasyansk 09e7a2b526 Merge sidebar-rail: collapsed icon rail 2026-06-15 13:41:05 +07:00
vasyansk 5d7a80e2a2 feat(app): collapsed sidebar becomes an icon rail (keeps activity visible)
Toggling the sidebar off used to hide it entirely, losing the per-workspace
status rings. It now collapses to a 48px rail showing each workspace's
aggregate status ring (and unread dot), still clickable to switch, plus the
new-workspace button and the daemon live/offline dot. Full sidebar returns
when toggled back on.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 13:41:05 +07:00
vasyansk 569aa39444 Merge events-clear-and-settings-x: clear events + settings close button 2026-06-15 13:38:35 +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 bcc88b6be7 fix(app): add a close (X) button to the settings modal
Esc and click-outside already closed it, but there was no visible affordance.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 13:38:35 +07:00
vasyansk 3dc3da072c fix(app): make the bell badge click-through (number opens the log too)
pointer-events:none lets clicks/hover pass through the unread badge to the
bell button beneath, which the badge was previously swallowing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 13:31:47 +07:00
vasyansk 897a3be659 Merge version-handshake: auto-restart stale daemon on GUI launch 2026-06-15 12:39:46 +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 8f431eaa40 fix(build): clean sidecar dir each build; native-focused install/reinstall
The stale bin/spaceshd-universal-apple-darwin sidecar (left over from an
earlier approach) poisoned the universal bundle — tauri shipped that old
daemon instead of the freshly built one, so the packaged daemon lacked the
TERM fix. dmg/dmg-native now wipe the sidecar dir first. install copies the
native bundle (was preferring the universal one, which could be stale) and
kills the running daemon; reinstall = native rebuild + install for fast
self-updates. Universal stays for distribution via dmg / install-universal.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 12:34:57 +07:00
vasyansk 79b47d42e7 build: make kill-daemon/install/reinstall — daemon survives reinstall
Rebuilding the .app never replaced the RUNNING daemon (it outlives the GUI),
so a stale spaceshd kept serving old code (e.g. the pre-TERM-fix daemon).
`make install` now stops the daemon, copies the fresh bundle to /Applications,
and clears quarantine; `make reinstall` does dmg+install in one shot.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 12:26:49 +07:00
vasyansk df0389b38f Merge fix-double-echo: single reader across reconnects 2026-06-15 11:56:35 +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 ee969371c9 Merge rename-and-term: workspace rename + TERM env fix 2026-06-15 11:47:21 +07:00
vasyansk 07cf7f9ed4 fix(pty): always set TERM/COLORTERM for spawned shells
A GUI/launchd-spawned daemon has no TERM in its environment, so child shells
inherited none and tput/zsh/ncurses failed ('tput: No value for $TERM').
The PTY now defaults TERM=xterm-256color and COLORTERM=truecolor (matching
xterm.js) unless the caller already provides them. Adds a regression test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 11:47:21 +07:00
vasyansk a929c166a3 feat(app): rename a workspace by double-clicking its name
Double-click a sidebar workspace name to edit it inline; Enter/blur commits
via setWorkspaceMeta({name}) (empty/unchanged is a no-op), Esc cancels. The
input stops pointer/key propagation so it doesn't trigger select or drag.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 11:47:21 +07:00
vasyansk 99a916fed6 fix(bundle): provide per-arch spaceshd sidecars for universal build
Tauri's universal target builds each arch separately and resolves externalBin
with that arch's triple (spaceshd-aarch64-apple-darwin / -x86_64-apple-darwin),
lipo'ing them itself. The previous single -universal-apple-darwin sidecar made
the per-arch sub-build fail with 'resource path ... doesn't exist'.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 11:41:57 +07:00
vasyansk 2fc58105a5 Merge bundle-daemon: package spaceshd in the macOS app 2026-06-15 11:39:03 +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 df6eabcd32 Update VERSION
Build / Build & push landing (push) Successful in 18s
Build / Notify Max (push) Successful in 2s
2026-06-15 10:58:31 +07:00
vasyansk d9ea6206c8 Merge ci-landing-only: Gitea builds landing only, DMG is local 2026-06-15 10:57:22 +07:00
vasyansk 8015f329ed ci: drop macOS DMG job — build the .dmg locally via make
No self-hosted macOS runner exists and Tauri can't cross-compile a macOS
bundle on Linux, so the DMG is produced locally with `make dmg`. The Gitea
workflow is now landing-only (build & push the nginx image + Max notify);
removed the dmg job, the changes/paths-filter job, and the app/crates triggers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 10:57:22 +07:00
vasyansk 4dad6075a5 Merge makefile: local build helpers 2026-06-15 10:55:48 +07:00
vasyansk 78b2e2a162 build: Makefile for local builds (DMG, dev, daemon, tests, landing)
No macOS CI runner available, so the universal .dmg is built locally:
`make dmg` (adds the rust targets + tauri build --target universal-apple-darwin).
Also: deps, dmg-native, dev, daemon, test, landing-image/run/push, clean.
`make` / `make help` lists targets.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 10:55:48 +07:00
vasyansk ad09ea6c01 Merge landing-ci: landing site, nginx image, Gitea CI for landing + DMG 2026-06-15 10:54:11 +07:00
vasyansk 2f2159a468 feat(landing): static site + nginx image + Gitea CI (landing + macOS DMG)
- landing/: the spaceshell.ru terminal-dark landing (index.html + screenshots),
  containerized as an nginx:alpine image (Dockerfile + nginx.conf with gzip and
  asset caching, VERSION, .dockerignore).
- .gitea/workflows/build.yaml: adapted from the coddykinder pipeline to this repo.
  Path-gated jobs — `landing` builds & pushes the nginx image to the Gitea
  registry on landing changes; `dmg` builds a universal (Intel + Apple Silicon)
  .dmg via `tauri build` on app/crates changes and uploads it as an artifact;
  Max notification summarizes both. Tags build everything (release).
- DOCS/landing/spaceshell-landing.md: build brief + copy + SEO meta.

Notes: the DMG job needs a self-hosted macOS runner labelled `macos` (Tauri
can't cross-compile macOS from Linux); the DMG is unsigned until Developer ID
secrets are wired. Landing image verified locally (HTTP 200, assets served).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 10:54:11 +07:00
vasyansk a9836f28b7 Merge bridge-reconnect: GUI self-heals after daemon restart 2026-06-15 10:22:24 +07:00