Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions packages/app/src/pages/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand Down
71 changes: 71 additions & 0 deletions packages/app/src/pages/session/session-archive.ts
Original file line number Diff line number Diff line change
@@ -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 }
}
59 changes: 5 additions & 54 deletions packages/app/src/pages/session/timeline/message-timeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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({
Expand All @@ -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
Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -1593,7 +1544,7 @@ export function MessageTimeline(props: {
<DropdownMenu.Item onSelect={() => exportSession(id)}>
<DropdownMenu.ItemLabel>{language.t("common.export")}</DropdownMenu.ItemLabel>
</DropdownMenu.Item>
<DropdownMenu.Item onSelect={() => void archiveSession(id)}>
<DropdownMenu.Item onSelect={() => void sessionArchive.archive(id)}>
<DropdownMenu.ItemLabel>{language.t("common.archive")}</DropdownMenu.ItemLabel>
</DropdownMenu.Item>
<DropdownMenu.Separator />
Expand Down Expand Up @@ -1667,7 +1618,7 @@ export function MessageTimeline(props: {
<MenuV2.Item onSelect={() => exportSession(id)}>
{language.t("common.export")}...
</MenuV2.Item>
<MenuV2.Item onSelect={() => void archiveSession(id)}>
<MenuV2.Item onSelect={() => void sessionArchive.archive(id)}>
{language.t("common.archive")}
</MenuV2.Item>
<MenuV2.Separator />
Expand Down
12 changes: 12 additions & 0 deletions packages/app/src/pages/session/use-session-commands.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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 <T,>(load: () => Promise<T>, show: (value: T) => void) => {
const owner = sessionOwnership.capture()
const value = await load()
Expand Down Expand Up @@ -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 = () => {
Expand Down
Loading