diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx
index 95f8c01843fd..98548ab2333b 100644
--- a/packages/app/src/pages/layout.tsx
+++ b/packages/app/src/pages/layout.tsx
@@ -972,17 +972,6 @@ export default function LegacyLayout(props: ParentProps) {
keybind: "shift+alt+arrowdown",
onSelect: () => navigateSessionByUnseen(1),
},
- {
- id: "session.archive",
- title: language.t("command.session.archive"),
- category: language.t("command.category.session"),
- keybind: "mod+shift+backspace",
- disabled: !params.dir || !params.id,
- onSelect: () => {
- const session = currentSessions().find((s) => s.id === params.id)
- if (session) void archiveSession(session)
- },
- },
{
id: "workspace.new",
title: language.t("workspace.new"),
diff --git a/packages/app/src/pages/session/session-archive.ts b/packages/app/src/pages/session/session-archive.ts
new file mode 100644
index 000000000000..5e1314dbd14d
--- /dev/null
+++ b/packages/app/src/pages/session/session-archive.ts
@@ -0,0 +1,71 @@
+import { useNavigate } from "@solidjs/router"
+import { produce } from "solid-js/store"
+import { notifySessionTabsRemoved } from "@/components/titlebar-session-events"
+import { useLanguage } from "@/context/language"
+import { useSDK } from "@/context/sdk"
+import { useSync } from "@/context/sync"
+import { useTabs } from "@/context/tabs"
+import { errorMessage } from "@/pages/layout/helpers"
+import { useSessionKey } from "@/pages/session/session-layout"
+import { legacySessionHref, requireServerKey, sessionHref } from "@/utils/session-route"
+import { showToast } from "@/utils/toast"
+
+export function useSessionArchive() {
+ const language = useLanguage()
+ const navigate = useNavigate()
+ const sdk = useSDK()
+ const sync = useSync()
+ const tabs = useTabs()
+ const { params } = useSessionKey()
+
+ const navigateAfterRemoval = (sessionID: string, parentID?: string, nextSessionID?: string) => {
+ if (params.id !== sessionID) return
+ const href = (id: string) =>
+ params.serverKey ? sessionHref(requireServerKey(params.serverKey), id) : legacySessionHref(sdk().directory, id)
+ if (parentID) {
+ navigate(href(parentID))
+ return
+ }
+ if (nextSessionID) {
+ navigate(href(nextSessionID))
+ return
+ }
+ if (params.serverKey) {
+ tabs.newDraft({ server: requireServerKey(params.serverKey), directory: sdk().directory })
+ return
+ }
+ navigate(`/${params.dir}/session`)
+ }
+
+ const archive = async (sessionID: string) => {
+ const session = sync().session.get(sessionID)
+ if (!session) return
+ if ((await sdk().protocol) !== "v1") return
+
+ const sessions = sync().data.session ?? []
+ const index = sessions.findIndex((s) => s.id === sessionID)
+ const nextSession = index === -1 ? undefined : (sessions[index + 1] ?? sessions[index - 1])
+
+ await sdk()
+ .client.session.update({ sessionID, directory: sdk().directory, time: { archived: Date.now() } })
+ .then(() => {
+ sync().set(
+ produce((draft) => {
+ const index = draft.session.findIndex((s) => s.id === sessionID)
+ if (index !== -1) draft.session.splice(index, 1)
+ }),
+ )
+ sync().session.evict(sessionID)
+ navigateAfterRemoval(sessionID, session.parentID, nextSession?.id)
+ notifySessionTabsRemoved({ directory: sdk().directory, sessionIDs: [sessionID] })
+ })
+ .catch((err) => {
+ showToast({
+ title: language.t("common.requestFailed"),
+ description: errorMessage(err, language.t("common.requestFailed")),
+ })
+ })
+ }
+
+ return { archive, navigateAfterRemoval }
+}
diff --git a/packages/app/src/pages/session/timeline/message-timeline.tsx b/packages/app/src/pages/session/timeline/message-timeline.tsx
index 25abdc9e7341..18a153d29971 100644
--- a/packages/app/src/pages/session/timeline/message-timeline.tsx
+++ b/packages/app/src/pages/session/timeline/message-timeline.tsx
@@ -63,10 +63,10 @@ import { SessionContextUsage } from "@/components/session-context-usage"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { useLanguage } from "@/context/language"
import { useSessionKey } from "@/pages/session/session-layout"
+import { useSessionArchive } from "@/pages/session/session-archive"
import { useServerSDK } from "@/context/server-sdk"
import { usePlatform } from "@/context/platform"
import { useSettings } from "@/context/settings"
-import { useTabs } from "@/context/tabs"
import { legacySessionHref, requireServerKey, sessionHref } from "@/utils/session-route"
import { useSDK } from "@/context/sdk"
import { useSync } from "@/context/sync"
@@ -263,8 +263,8 @@ export function MessageTimeline(props: {
const sdk = useSDK()
const sync = useSync()
const settings = useSettings()
- const tabs = useTabs()
const dialog = useDialog()
+ const sessionArchive = useSessionArchive()
const language = useLanguage()
const { params, sessionKey } = useSessionKey()
const ownerSessionKey = sessionKey()
@@ -790,25 +790,6 @@ export function MessageTimeline(props: {
titleMutation.mutate({ id, title: next })
}
- const navigateAfterSessionRemoval = (sessionID: string, parentID?: string, nextSessionID?: string) => {
- if (params.id !== sessionID) return
- const href = (id: string) =>
- params.serverKey ? sessionHref(requireServerKey(params.serverKey), id) : legacySessionHref(sdk().directory, id)
- if (parentID) {
- navigate(href(parentID))
- return
- }
- if (nextSessionID) {
- navigate(href(nextSessionID))
- return
- }
- if (params.serverKey) {
- tabs.newDraft({ server: requireServerKey(params.serverKey), directory: sdk().directory })
- return
- }
- navigate(`/${params.dir}/session`)
- }
-
const exportSession = async (sessionID: string) => {
try {
const data = await fetchSessionExport({
@@ -832,36 +813,6 @@ export function MessageTimeline(props: {
}
}
- const archiveSession = async (sessionID: string) => {
- const session = sync().session.get(sessionID)
- if (!session) return
- if ((await sdk().protocol) !== "v1") return
-
- const sessions = sync().data.session ?? []
- const index = sessions.findIndex((s) => s.id === sessionID)
- const nextSession = index === -1 ? undefined : (sessions[index + 1] ?? sessions[index - 1])
-
- await sdk()
- .client.session.update({ sessionID, directory: sdk().directory, time: { archived: Date.now() } })
- .then(() => {
- sync().set(
- produce((draft) => {
- const index = draft.session.findIndex((s) => s.id === sessionID)
- if (index !== -1) draft.session.splice(index, 1)
- }),
- )
- sync().session.evict(sessionID)
- navigateAfterSessionRemoval(sessionID, session.parentID, nextSession?.id)
- notifySessionTabsRemoved({ directory: sdk().directory, sessionIDs: [sessionID] })
- })
- .catch((err) => {
- showToast({
- title: language.t("common.requestFailed"),
- description: errorMessage(err),
- })
- })
- }
-
const deleteSession = async (sessionID: string) => {
const session = sync().session.get(sessionID)
if (!session) return false
@@ -911,7 +862,7 @@ export function MessageTimeline(props: {
}
}
- navigateAfterSessionRemoval(sessionID, session.parentID, nextSession?.id)
+ sessionArchive.navigateAfterRemoval(sessionID, session.parentID, nextSession?.id)
sync().set(
produce((draft) => {
@@ -1593,7 +1544,7 @@ export function MessageTimeline(props: {
exportSession(id)}>
{language.t("common.export")}
- void archiveSession(id)}>
+ void sessionArchive.archive(id)}>
{language.t("common.archive")}
@@ -1667,7 +1618,7 @@ export function MessageTimeline(props: {
exportSession(id)}>
{language.t("common.export")}...
- void archiveSession(id)}>
+ void sessionArchive.archive(id)}>
{language.t("common.archive")}
diff --git a/packages/app/src/pages/session/use-session-commands.tsx b/packages/app/src/pages/session/use-session-commands.tsx
index c4c8c44a99f0..18d5b172ff17 100644
--- a/packages/app/src/pages/session/use-session-commands.tsx
+++ b/packages/app/src/pages/session/use-session-commands.tsx
@@ -18,6 +18,7 @@ import { createSessionTabs } from "@/pages/session/helpers"
import { extractPromptFromParts } from "@/utils/prompt"
import { Message, Part, UserMessage } from "@opencode-ai/sdk/v2"
import { useSessionLayout } from "@/pages/session/session-layout"
+import { useSessionArchive } from "@/pages/session/session-archive"
import { createSessionOwnership } from "./session-ownership"
import { useLocal } from "@/context/local"
@@ -52,6 +53,7 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
const navigate = useNavigate()
const { params, sessionKey, tabs, view } = useSessionLayout()
const sessionOwnership = createSessionOwnership(sessionKey)
+ const sessionArchive = useSessionArchive()
const openDialog = async (load: () => Promise, show: (value: T) => void) => {
const owner = sessionOwnership.capture()
const value = await load()
@@ -497,6 +499,16 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
disabled: !params.id,
onSelect: exportSession,
}),
+ sessionCommand({
+ id: "session.archive",
+ title: language.t("command.session.archive"),
+ keybind: "mod+shift+backspace",
+ disabled: !params.id,
+ onSelect: () => {
+ const id = params.id
+ if (id) void sessionArchive.archive(id)
+ },
+ }),
]
const fileCmds = () => {