Commit Graph
100 Commits
Author SHA1 Message Date
vasyanskandClaude Opus 4.8 39bb8e5fee feat(app): close (X) on panel header + Close button on stopped overlay
Wires the existing closeSurfaceCmd into the panel header (red-on-hover X next
to zoom) and adds a Close button to the stopped overlay, so a panel — including
an empty/stopped one — can be dismissed instead of resumed/restarted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 16:47:43 +07:00
vasyanskandClaude Opus 4.8 d62628be8d fix(daemon): reseed id counter on restore + heal duplicate leaves
Root cause of the multi-focus/multi-search/linked-terminal bug: the in-memory
id counter resets to 0 each daemon start, but restore() never advanced it past
restored ids. After a restart new_surface_id() re-minted existing ids → the same
surface_id appeared twice in a layout tree (rendered as two panels sharing focus,
search bar, and output channel — one ends up blank). Session-persistence made
restarts routine, surfacing the latent bug.

- restore() now reseeds the counter to max(restored id)+1
- ops::dedupe_leaves heals an already-corrupted persisted tree on load

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 16:46:04 +07:00
vasyanskandClaude Opus 4.8 3317b24d18 fix(daemon): gate NullSnapshotStore behind cfg(test) — silence release dead_code warning
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 16:34:10 +07:00
vasyansk 0275c64ace Add NerdFont for symbols and version bump script
Add version bump script to synchronize GUI and daemon versions
2026-06-15 16:32:31 +07:00
vasyansk 0a67f401c4 Update version to 0.1.3
Add daemon version check and restart logic

Add pane count to CenterToolbar

Add minSlots filter to PresetPicker
2026-06-15 16:32:25 +07:00
vasyanskandClaude Opus 4.8 ce6a8d56be fix(daemon,app): graceful-shutdown final snapshot pass + StoppedSnapshot detach cleanup
Addresses final-review findings: Cmd::Shutdown now snapshots all live surfaces
synchronously before exit (spec graceful-shutdown requirement); StoppedSnapshot
calls detachSurface on unmount to release the bridge output channel.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 16:24:53 +07:00
vasyanskandClaude Opus 4.8 5c76493a34 feat(cli): spacesh restart --resume flag (plan gap: CLI is a first-class client)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 16:11:06 +07:00
vasyanskandClaude Sonnet 4.6 ff0ad7a648 feat(app): stopped panel paints last screen + Resume/Restart fresh controls
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 16:09:39 +07:00
vasyanskandClaude Opus 4.8 375e4c5c92 feat(app): plumb resume flag through restart_surface bridge + binding
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 16:07:32 +07:00
vasyanskandClaude Opus 4.8 31c08b5387 feat(daemon): RestartSurface honors resume — swap to resume_args when mapped
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 16:05:53 +07:00
vasyansk eecea9c38c feat(proto): RestartSurface gains resume flag (defaults false) 2026-06-15 16:03:03 +07:00
vasyanskandClaude Opus 4.8 d00abcd2f6 chore: lock serde_json dev-dep for spacesh-core (Task 1 followup)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 16:01:58 +07:00
vasyanskandClaude Sonnet 4.6 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
vasyanskandClaude Sonnet 4.6 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
vasyanskandClaude Opus 4.8 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
vasyanskandClaude Sonnet 4.6 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
vasyanskandClaude Sonnet 4.6 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
vasyanskandClaude Sonnet 4.6 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
vasyanskandClaude Opus 4.8 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
vasyanskandClaude Opus 4.8 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
vasyanskandClaude Opus 4.8 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
vasyanskandClaude Opus 4.8 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
vasyanskandClaude Opus 4.8 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
vasyanskandClaude Opus 4.8 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
vasyanskandClaude Opus 4.8 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
vasyanskandClaude Opus 4.8 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
vasyanskandClaude Opus 4.8 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
vasyanskandClaude Opus 4.8 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
vasyanskandClaude Opus 4.8 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
vasyanskandClaude Opus 4.8 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
vasyanskandClaude Opus 4.8 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
vasyanskandClaude Opus 4.8 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
vasyanskandClaude Opus 4.8 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
vasyanskandClaude Opus 4.8 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
vasyanskandClaude Opus 4.8 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
vasyanskandClaude Opus 4.8 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
vasyanskandClaude Opus 4.8 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
vasyanskandClaude Opus 4.8 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
vasyanskandClaude Opus 4.8 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
vasyanskandClaude Opus 4.8 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
vasyanskandClaude Opus 4.8 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
vasyanskandClaude Opus 4.8 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
vasyanskandClaude Opus 4.8 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
vasyanskandClaude Opus 4.8 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
vasyanskandClaude Opus 4.8 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 99f5708cbf Merge settings-bugfixes: live font apply + daemon uptime refresh 2026-06-15 10:08:59 +07:00
vasyanskandClaude Opus 4.8 f8d3876c68 fix(app): settings live-apply — font on open terminals, daemon uptime
- Font change now applies to already-open terminals: the WebGL renderer
  caches glyphs in a texture atlas keyed by the old font/size, so the live
  re-apply effect now calls webglAddon.clearTextureAtlas() (via a new ref)
  after updating fontFamily/fontSize, before refitting.
- Daemon uptime now reflects a restart: the Settings daemon section ticks
  every second for live uptime, and Stop/Restart trigger an onReload callback
  that re-fetches health/status in App so a restarted daemon's new
  started_at_ms is shown instead of the stale value.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 10:08:59 +07:00
vasyansk b63ed2ea83 Merge settings-modal: daemon config, Get/Set/ConfigChanged, CSS-var theming, settings modal with Stop/Restart 2026-06-15 09:05:45 +07:00
vasyanskandClaude Sonnet 4.6 5e6cf4d982 fix(app): settings review — startup theme default, slider/shell input UX, dedupe accents, memoize palette
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 19:10:19 +07:00
vasyanskandClaude Sonnet 4.6 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
vasyanskandClaude Sonnet 4.6 a2087a0de5 feat(app): settings modal — terminal, appearance, shell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 18:34:14 +07:00
vasyanskandClaude Sonnet 4.6 61c69adb17 feat(app): terminal font and xterm theme from daemon config
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 09:43:38 +07:00
vasyanskandClaude Sonnet 4.6 0f28be1300 feat(app): apply theme from daemon config on load and live
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 09:41:15 +07:00
vasyanskandClaude Sonnet 4.6 dc95381870 feat(app): CSS-variable theming with dark/light palettes and accents
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 09:39:07 +07:00
vasyansk b9f46a407d feat(app): socketBridge getConfig/setConfig + config_changed 2026-06-14 09:37:45 +07:00
vasyansk 62f1f8e9a8 feat(app): tauri get_config/set_config bridge commands 2026-06-14 09:36:53 +07:00
vasyansk ad29665352 feat(spaceshd): GetConfig/SetConfig handlers with live ConfigChanged broadcast 2026-06-14 09:35:27 +07:00
vasyansk c4746f9864 feat(proto): GetConfig/SetConfig commands and ConfigChanged event 2026-06-14 09:22:39 +07:00
vasyansk e990e694b5 feat(proto): ConfigView wire type 2026-06-14 09:21:22 +07:00
vasyansk 80113da066 feat(spaceshd): config terminal+appearance sections and save 2026-06-14 09:20:24 +07:00
vasyanskandClaude Opus 4.8 052f484142 docs: settings modal implementation plan
11 TDD tasks: daemon config model/save, ConfigView + Get/Set/ConfigChanged
protocol, bridge + socketBridge, CSS-var theming, TerminalView font/theme,
settings modal, daemon Stop/Restart.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 09:05:22 +07:00
vasyanskandClaude Opus 4.8 4aacebcc60 docs: settings modal design spec
Config in config.toml via daemon (Get/Set/ConfigChanged), low-churn CSS-var
theme switching (Dark/Light + accent), terminal font, default shell, and
daemon status with Stop/Restart.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 09:01:53 +07:00
vasyansk 5b08b204b6 Merge workspace-list-features: pinned/favorites, drag-reorder, delete-with-confirm 2026-06-14 08:56:31 +07:00
vasyanskandClaude Opus 4.8 a55555983b feat(app): sidebar favorites, drag-reorder, and delete-with-confirm
- FAVORITES section at the top collects pinned workspaces (removed from their
  group listing); a star toggle on each row pins/unpins via setWorkspaceMeta.
- Drag-to-reorder within a section using raw pointer events (HTML5 DnD is
  unreliable in the macOS WKWebView), with a drop-line indicator; on drop the
  section's `order` is reassigned sequentially and persisted. Cross-section
  drops are ignored (group membership unchanged).
- Trash icon on row hover opens a ConfirmDelete modal that shows the live
  terminal count and warns before terminating them, then calls close_workspace
  and re-points the active workspace.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 08:56:20 +07:00
vasyanskandClaude Opus 4.8 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 2c8ac8ebac Merge fix-spawn-search: lazy PTY spawn, working search, prompt-dup fix 2026-06-14 08:34:57 +07:00
vasyanskandClaude Opus 4.8 04ac7cdec2 fix(app): working scrollback search + stop prompt duplication on focus
Search fixes:
- TerminalView sets allowProposedApi (the search addon's match decorations
  use registerMarker/registerDecoration); without it findNext threw before
  firing results, so the counter was stuck at 0/0.
- The search bar now renders inside the panel it targets (in the header)
  instead of a global top-right overlay, so it's obvious which panel is
  searched.
- Search is anchored to the panel it was opened on (searchSurfaceId) and no
  longer follows focus — opening it in one panel and switching away no longer
  shows it open elsewhere.

Prompt duplication:
- The focus border was 1px when unfocused, 2px when focused; with border-box
  that resized the content on every focus switch, firing ResizeObserver -> fit
  -> PTY SIGWINCH and making zsh/powerlevel10k reprint its prompt. The border
  is now a constant 2px, color-only on focus.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 08:34:43 +07:00
vasyanskandClaude Opus 4.8 a30ec1cc7f fix(spaceshd): lazy PTY spawn so prompts render at the correct size
A new surface spawned its shell immediately at the default 80x24, so the
shell (e.g. zsh + powerlevel10k) printed its first prompt before the GUI
fit-resized the panel. The post-print resize reflowed the grid, leaving the
prompt invisible until Enter and shifted by a line.

The surface actor now defers the child spawn: it waits for the first Resize
(spawning at that geometry) or, for headless/CLI surfaces that never attach a
GUI, falls back to the spec geometry after 250ms. Attaches received before
the spawn get an empty-grid snapshot plus a live subscription; pre-spawn
input is buffered and replayed. Adds two deterministic tests via `stty size`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 08:34:28 +07:00
vasyansk 907df5b01c Merge backlog-polish: search fix, app icon, shell config, lock-test fix, GUI backlog 2026-06-14 08:01:47 +07:00
vasyanskandClaude Opus 4.8 58c75c71ae feat(app): GUI backlog — splitter, drag-reorder, fit, persist, modal focus
- LayoutEngine: fix splitter resize (track pointer 1:1 via delta-from-start)
  and add panel drag-to-reorder using raw pointer events with drop indicators
- TerminalView: auto-fit xterm to container via FitAddon + ResizeObserver
- App/TopBar: toggleable sidebar; persist sidebar/events collapse in
  localStorage; bell icon opens the activity log
- Wizard: new-workspace modal now grabs focus and handles keyboard
- deps: add @xterm/addon-fit

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 08:01:15 +07:00
vasyanskandClaude Opus 4.8 6a3875670a feat(spaceshd): configurable default shell
Add a config.rs module that loads optional ~/.spacesh/config.toml and a
default_shell() resolver: SPACESH_SHELL env -> config.toml default_shell ->
login shell from the passwd DB -> $SHELL -> /bin/sh. The passwd lookup
matters under launchd where $SHELL is absent. All plain-panel spawn sites in
server.rs now use the resolver. Adds toml + libc deps.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 08:01:04 +07:00
vasyanskandClaude Opus 4.8 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
vasyanskandClaude Opus 4.8 89373676e8 fix(app): incremental scrollback search
Search only ran on Enter; typing merely reset the counter to 0/0, so a
visible term showed no matches until the user pressed Enter. run() now takes
an optional query override and onChange fires it on every keystroke for
search-as-you-type, while Enter/Shift+Enter still navigate matches.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 08:00:45 +07:00
vasyanskandClaude Opus 4.8 21180ae9e0 fix(spaceshd): isolate instance-lock test via SPACESH_LOCK
lock_is_exclusive_within_process acquired the global ~/.spacesh/daemon.lock,
so it flaked whenever a real daemon was running. Add a SPACESH_LOCK env
override to lock_path() and point the test at a private temp file under a
serial() guard, making the suite deterministic regardless of a live daemon.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 08:00:35 +07:00
vasyanskandClaude Opus 4.8 cd44f0b263 Merge SP1+SP3+SP4: daemon health, scrollback search, panel zoom
- SP1: Cmd::Health (version/pid/uptime) → real sidebar live/uptime footer
- SP3: ⌘F scrollback search on the focused panel via xterm addon-search
- SP4: persisted per-workspace panel zoom (full-grid render, auto-clear on removal)
- 119 workspace tests green; frontend build clean

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 12:56:09 +07:00
vasyansk 8ea680d0df docs: SP1/SP3/SP4 manual scenarios and updated limitations 2026-06-10 12:55:07 +07:00
vasyanskandClaude Sonnet 4.6 ac3f0886d5 feat(app): scrollback search bar (⌘F) on the focused panel
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 12:48:03 +07:00
vasyansk 52a502c38b feat(app): load xterm search addon + surface→addon registry 2026-06-10 12:35:41 +07:00
vasyanskandClaude Opus 4.8 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
vasyanskandClaude Sonnet 4.6 2b1ccaf31d feat: persisted per-workspace panel zoom (proto + daemon, auto-clear on removal)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 12:24:23 +07:00
vasyanskandClaude Opus 4.8 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 f7763a84fc feat: Health command — version, pid, started_at (proto + daemon) 2026-06-10 12:06:38 +07:00