fix(tui): keep session switching pinned-only (#27775)

This commit is contained in:
Shoubhit Dash
2026-05-16 00:14:07 +05:30
committed by GitHub
parent 2b0e72ab79
commit f99339e525
5 changed files with 14 additions and 178 deletions
@@ -1,6 +1,6 @@
import { createStore } from "solid-js/store"
import { createSimpleContext } from "./helper"
import { batch, createEffect, createMemo, on } from "solid-js"
import { batch, createEffect, createMemo } from "solid-js"
import { useSync } from "@tui/context/sync"
import { useTheme } from "@tui/context/theme"
import { useRoute } from "@tui/context/route"
@@ -8,7 +8,6 @@ import { useEvent } from "@tui/context/event"
import { uniqueBy } from "remeda"
import path from "path"
import { Global } from "@opencode-ai/core/global"
import { Flag } from "@opencode-ai/core/flag/flag"
import { iife } from "@/util/iife"
import { useToast } from "../ui/toast"
import { useArgs } from "./args"
@@ -387,13 +386,9 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
const [sessionStore, setSessionStore] = createStore<{
ready: boolean
pinned: string[]
dismissedRecent: string[]
recentOrder: string[]
}>({
ready: false,
pinned: [],
dismissedRecent: [],
recentOrder: [],
})
const filePath = path.join(Global.Path.state, "session.json")
@@ -409,16 +404,12 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
state.pending = false
void Filesystem.writeJson(filePath, {
pinned: sessionStore.pinned,
dismissedRecent: sessionStore.dismissedRecent,
recentOrder: sessionStore.recentOrder,
})
}
Filesystem.readJson(filePath)
.then((x: any) => {
if (Array.isArray(x.pinned)) setSessionStore("pinned", x.pinned)
if (Array.isArray(x.dismissedRecent)) setSessionStore("dismissedRecent", x.dismissedRecent)
if (Array.isArray(x.recentOrder)) setSessionStore("recentOrder", x.recentOrder)
})
.catch(() => {})
.finally(() => {
@@ -428,19 +419,10 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
const route = useRoute()
const event = useEvent()
let cycling = false
const slots = createMemo(() => {
const rootSessions = sync.data.session.filter((x) => x.parentID === undefined)
const existing = new Set(rootSessions.map((x) => x.id))
const dismissed = new Set(sessionStore.dismissedRecent)
const pins = sessionStore.pinned.filter((id) => existing.has(id))
const pinnedSet = new Set(pins)
const recent = rootSessions
.filter((x) => !pinnedSet.has(x.id) && !dismissed.has(x.id))
.toSorted((a, b) => b.time.updated - a.time.updated)
.map((x) => x.id)
return [...pins, ...recent].slice(0, 9)
const existing = new Set(sync.data.session.filter((x) => x.parentID === undefined).map((x) => x.id))
return sessionStore.pinned.filter((id) => existing.has(id)).slice(0, 9)
})
function prune(sessionID: string) {
@@ -451,18 +433,6 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
sessionStore.pinned.filter((x) => x !== sessionID),
)
}
if (sessionStore.dismissedRecent.includes(sessionID)) {
setSessionStore(
"dismissedRecent",
sessionStore.dismissedRecent.filter((x) => x !== sessionID),
)
}
if (sessionStore.recentOrder.includes(sessionID)) {
setSessionStore(
"recentOrder",
sessionStore.recentOrder.filter((x) => x !== sessionID),
)
}
save()
})
}
@@ -471,25 +441,6 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
prune(evt.properties.info.id)
})
if (Flag.OPENCODE_EXPERIMENTAL_SESSION_SWITCHING) {
createEffect(
on(
() => (sessionStore.ready && route.data.type === "session" ? route.data.sessionID : undefined),
(sessionID) => {
if (!sessionID) return
if (cycling) {
cycling = false
return
}
const filtered = sessionStore.recentOrder.filter((x) => x !== sessionID)
const next = [sessionID, ...filtered].slice(0, 20)
setSessionStore("recentOrder", next)
save()
},
),
)
}
return {
get ready() {
return sessionStore.ready
@@ -497,19 +448,10 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
pinned() {
return sessionStore.pinned
},
dismissedRecent() {
return sessionStore.dismissedRecent
},
recentOrder() {
return sessionStore.recentOrder
},
slots,
isPinned(sessionID: string) {
return sessionStore.pinned.includes(sessionID)
},
isDismissed(sessionID: string) {
return sessionStore.dismissedRecent.includes(sessionID)
},
togglePin(sessionID: string) {
batch(() => {
const exists = sessionStore.pinned.includes(sessionID)
@@ -520,52 +462,12 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
save()
})
},
toggleRecent(sessionID: string) {
batch(() => {
const exists = sessionStore.dismissedRecent.includes(sessionID)
const next = exists
? sessionStore.dismissedRecent.filter((x) => x !== sessionID)
: [sessionID, ...sessionStore.dismissedRecent]
setSessionStore("dismissedRecent", next)
save()
})
},
quickSwitch(slot: number) {
const target = slots()[slot - 1]
if (!target) return
if (route.data.type === "session" && route.data.sessionID === target) return
route.navigate({ type: "session", sessionID: target })
},
cycleRecent(direction: 1 | -1) {
if (route.data.type !== "session") {
toast.show({
variant: "info",
message: "Open a session first to cycle between recent sessions",
duration: 3000,
})
return
}
const current = route.data.sessionID
const order = sessionStore.recentOrder.filter((id) =>
sync.data.session.some((s) => s.id === id && s.parentID === undefined),
)
if (order.length < 2) {
toast.show({
variant: "info",
message: "No other recent sessions to cycle to",
duration: 3000,
})
return
}
const index = order.indexOf(current)
if (index === -1) return
const next = index + direction
if (next < 0 || next >= order.length) return
const target = order[next]
if (!target || target === current) return
cycling = true
route.navigate({ type: "session", sessionID: target })
},
}
})