From 4614f815bc902875df6c4b71fadc7555fe63d425 Mon Sep 17 00:00:00 2001 From: Muthuvel Date: Sat, 3 Jan 2026 13:43:16 +0800 Subject: [PATCH 1/2] feat: add delete session button to session header Adds an X button next to the session selector that allows users to delete the current session. Shows a confirmation dialog before deletion and navigates to a new session page after successful deletion. --- .../src/components/session/session-header.tsx | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/packages/app/src/components/session/session-header.tsx b/packages/app/src/components/session/session-header.tsx index 7e4ade52339..cc9fae3743c 100644 --- a/packages/app/src/components/session/session-header.tsx +++ b/packages/app/src/components/session/session-header.tsx @@ -1,4 +1,4 @@ -import { createEffect, createMemo, createResource, Show } from "solid-js" +import { createEffect, createMemo, createResource, createSignal, Show } from "solid-js" import { A, useNavigate, useParams } from "@solidjs/router" import { useLayout } from "@/context/layout" import { useCommand } from "@/context/command" @@ -6,6 +6,7 @@ import { useServer } from "@/context/server" import { useDialog } from "@opencode-ai/ui/context/dialog" import { useSync } from "@/context/sync" import { useGlobalSDK } from "@/context/global-sdk" +import { useSDK } from "@/context/sdk" import { getFilename } from "@opencode-ai/util/path" import { base64Decode, base64Encode } from "@opencode-ai/util/encode" import { iife } from "@opencode-ai/util/iife" @@ -19,10 +20,12 @@ import { TextField } from "@opencode-ai/ui/text-field" import { DialogSelectServer } from "@/components/dialog-select-server" import { SessionLspIndicator } from "@/components/session-lsp-indicator" import { SessionMcpIndicator } from "@/components/session-mcp-indicator" +import { showToast } from "@opencode-ai/ui/toast" import type { Session } from "@opencode-ai/sdk/v2/client" export function SessionHeader() { const globalSDK = useGlobalSDK() + const sdk = useSDK() const layout = useLayout() const params = useParams() const navigate = useNavigate() @@ -37,6 +40,8 @@ export function SessionHeader() { const currentSession = createMemo(() => sessions().find((s) => s.id === params.id)) const shareEnabled = createMemo(() => sync.data.config.share !== "disabled") + const [deleting, setDeleting] = createSignal(false) + function navigateToProject(directory: string) { navigate(`/${base64Encode(directory)}`) } @@ -46,6 +51,30 @@ export function SessionHeader() { navigate(`/${params.dir}/session/${session.id}`) } + async function deleteCurrentSession() { + const session = currentSession() + if (!session || deleting()) return + + // Simple confirmation + if (!confirm(`Delete session "${session.title}"?`)) return + + setDeleting(true) + try { + await sdk.client.session.delete({ + sessionID: session.id, + directory: projectDirectory(), + }) + showToast({ title: "Session deleted" }) + // Navigate to new session page + navigate(`/${params.dir}/session`) + } catch (e) { + console.error("Failed to delete session", e) + showToast({ title: "Failed to delete session", description: String(e) }) + } finally { + setDeleting(false) + } + } + return (
+ + + + + ) +} diff --git a/packages/app/src/components/session/session-header.tsx b/packages/app/src/components/session/session-header.tsx index cc9fae3743c..5968fbd935f 100644 --- a/packages/app/src/components/session/session-header.tsx +++ b/packages/app/src/components/session/session-header.tsx @@ -18,6 +18,7 @@ import { Select } from "@opencode-ai/ui/select" import { Popover } from "@opencode-ai/ui/popover" import { TextField } from "@opencode-ai/ui/text-field" import { DialogSelectServer } from "@/components/dialog-select-server" +import { DialogConfirmDelete } from "@/components/dialog-confirm-delete" import { SessionLspIndicator } from "@/components/session-lsp-indicator" import { SessionMcpIndicator } from "@/components/session-mcp-indicator" import { showToast } from "@opencode-ai/ui/toast" @@ -51,28 +52,32 @@ export function SessionHeader() { navigate(`/${params.dir}/session/${session.id}`) } - async function deleteCurrentSession() { + function deleteCurrentSession() { const session = currentSession() if (!session || deleting()) return - // Simple confirmation - if (!confirm(`Delete session "${session.title}"?`)) return - - setDeleting(true) - try { - await sdk.client.session.delete({ - sessionID: session.id, - directory: projectDirectory(), - }) - showToast({ title: "Session deleted" }) - // Navigate to new session page - navigate(`/${params.dir}/session`) - } catch (e) { - console.error("Failed to delete session", e) - showToast({ title: "Failed to delete session", description: String(e) }) - } finally { - setDeleting(false) - } + dialog.show(() => ( + { + setDeleting(true) + try { + await sdk.client.session.delete({ + sessionID: session.id, + directory: projectDirectory(), + }) + showToast({ title: "Session deleted" }) + navigate(`/${params.dir}/session`) + } catch (e) { + console.error("Failed to delete session", e) + showToast({ title: "Failed to delete session", description: String(e) }) + } finally { + setDeleting(false) + } + }} + /> + )) } return ( diff --git a/packages/app/src/context/global-sync.tsx b/packages/app/src/context/global-sync.tsx index 913e54d1065..d9e860e7f55 100644 --- a/packages/app/src/context/global-sync.tsx +++ b/packages/app/src/context/global-sync.tsx @@ -246,6 +246,18 @@ function createGlobalSync() { bootstrapInstance(directory) break } + case "session.deleted": { + const result = Binary.search(store.session, event.properties.info.id, (s) => s.id) + if (result.found) { + setStore( + "session", + produce((draft) => { + draft.splice(result.index, 1) + }), + ) + } + break + } case "session.updated": { const result = Binary.search(store.session, event.properties.info.id, (s) => s.id) if (event.properties.info.time.archived) {