Add session eject feature and terminal UX improvements

- Add eject button for tmux sessions (keeps tmux running, removes from Spiceflow)
- Add refresh button to session settings for all providers
- Improve terminal controls: larger buttons, more zoom levels (50-150%), copy selection, paste clipboard, enter key
- Fix session navigation: properly reload when switching between sessions
- Update tmux screen presets to larger dimensions (fullscreen 260x48, desktop 120x48, landscape 80x24)
- Add testing documentation to CLAUDE.md
- Refactor E2E tests to use API-based cleanup

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-20 23:37:06 -05:00
parent 3d121c2e08
commit 5171059692
10 changed files with 448 additions and 131 deletions
+24 -3
View File
@@ -339,9 +339,9 @@
;; Screen size presets for different device orientations
(def ^:private screen-sizes
{:fullscreen {:width 180 :height 24}
:desktop {:width 100 :height 24}
:landscape {:width 65 :height 24}
{:fullscreen {:width 260 :height 48}
:desktop {:width 120 :height 48}
:landscape {:width 80 :height 24}
:portrait {:width 40 :height 24}})
(defn resize-session
@@ -420,3 +420,24 @@
:name new-name
:working-dir (or (run-tmux "display-message" "-t" new-name "-p" "#{pane_current_path}")
(System/getProperty "user.home"))})))))
(defn eject-session
"Eject a spiceflow-managed tmux session by removing the spiceflow prefix.
The tmux session continues running but is no longer managed by spiceflow.
Returns the new session name on success, nil on failure."
[session-name]
(when (and session-name (str/starts-with? session-name session-prefix))
(let [;; Remove the spiceflow- prefix to get the base name
base-name (subs session-name (count session-prefix))
output-file (output-file-path session-name)]
;; Stop pipe-pane first
(run-tmux "pipe-pane" "-t" session-name)
;; Clean up output file
(when output-file
(let [f (File. ^String output-file)]
(when (.exists f)
(.delete f))))
;; Rename the session to remove spiceflow prefix
(when (rename-session session-name base-name)
(log/info "[Tmux] Ejected session" session-name "-> " base-name)
base-name))))
+20
View File
@@ -310,6 +310,25 @@
(error-response 400 "Failed to import session. It may not exist or is already managed by spiceflow."))
(error-response 400 "Session name is required"))))
(defn eject-tmux-handler
"Eject a spiceflow-managed tmux session. Removes it from spiceflow but keeps the tmux session running."
[store]
(fn [request]
(let [id (get-in request [:path-params :id])]
(if (tmux-session-id? id)
(if (tmux/session-alive? id)
(if-let [new-name (tmux/eject-session id)]
(do
;; Remove the session from the database (tmux sessions aren't in DB, but call anyway for safety)
(db/delete-session store id)
(json-response {:status "ejected"
:old-name id
:new-name new-name
:message (str "Session ejected. You can attach to it with: tmux attach -t " new-name)}))
(error-response 500 "Failed to eject tmux session"))
(error-response 400 "Tmux session not alive"))
(error-response 400 "Not a tmux session")))))
;; Health check
(defn health-handler
[_request]
@@ -367,6 +386,7 @@
["/sessions/:id/terminal" {:get (terminal-capture-handler store)}]
["/sessions/:id/terminal/input" {:post (terminal-input-handler store broadcast-fn)}]
["/sessions/:id/terminal/resize" {:post (terminal-resize-handler store)}]
["/sessions/:id/eject" {:post (eject-tmux-handler store)}]
["/tmux/external" {:get list-external-tmux-handler}]
["/tmux/import" {:post import-tmux-handler}]
["/push/vapid-key" {:get (vapid-key-handler push-store)}]