Skip to content
Open
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
44 changes: 44 additions & 0 deletions packages/app/src/pages/home/home-sessions-controller.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { describe, expect, test } from "bun:test"
import type { SessionInfo } from "@opencode-ai/client/promise"
import type { LocalProject } from "@/context/layout"
import { buildHomeSessionRecords } from "./home-sessions-model"

const session = (id: string, directory: string, projectID = "project") =>
({
id,
projectID,
title: id,
time: { created: 1, updated: 1 },
location: { directory },
}) as SessionInfo

const project = (worktree: string, input: Partial<LocalProject> = {}) =>
({ worktree, expanded: false, ...input }) as LocalProject

describe("Home session records", () => {
test("includes sessions from projects that were not added", () => {
const result = buildHomeSessionRecords({
sessions: () => [session("unadded", "/repo/worktree")],
projectDirectories: () => undefined,
projects: () => [],
projectByID: () => new Map(),
projectMetadataByID: () => new Map([["project", project("/repo", { name: "Project name" })]]),
})

expect(result).toHaveLength(1)
expect(result[0]?.project.worktree).toBe("/repo/worktree")
expect(result[0]?.projectName).toBe("Project name")
})

test("keeps selected project scoping", () => {
const result = buildHomeSessionRecords({
sessions: () => [session("selected", "/selected"), session("other", "/other", "other")],
projectDirectories: () => ["/selected"],
projects: () => [project("/selected")],
projectByID: () => new Map(),
projectMetadataByID: () => new Map(),
})

expect(result.map((item) => item.session.id)).toEqual(["selected"])
})
})
38 changes: 12 additions & 26 deletions packages/app/src/pages/home/home-sessions-controller.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ import type { LocalProject } from "@/context/layout"
import { useLanguage } from "@/context/language"
import { ServerConnection } from "@/context/servers"
import { sessionHasOpenTab, useTabs } from "@/context/tabs"
import { compareSessionTime, displayName, errorMessage, projectForSession } from "@/pages/layout/helpers"
import { errorMessage, projectForSession } from "@/pages/layout/helpers"
import { useSessionTabAvatarState } from "@/pages/layout/project-avatar-state"
import { pathKey } from "@/utils/path-key"
import { showToast } from "@/utils/toast"
import { archiveHomeSession } from "../home-session-archive"
import type { HomeController } from "./home-controller"
import { buildHomeSessionRecords } from "./home-sessions-model"

const HOME_SESSION_LIMIT = 64
export type HomeSessionRecord = {
Expand All @@ -43,12 +44,20 @@ export function createHomeSessionsController(home: HomeController) {
const language = useLanguage()
const projectDirectories = createMemo(() => {
const project = home.project.selected()
if (!project) return home.project.list().flatMap(directories)
if (!project) return
return directories(project)
})
const projectByID = createMemo(
() => new Map(home.project.list().flatMap((project) => (project.id ? [[project.id, project] as const] : []))),
)
const projectMetadataByID = createMemo(
() =>
new Map(
(home.server.focusedSync()?.data.project ?? []).map(
(project) => [project.id, { ...project, expanded: false }] as const,
),
),
)
const sessionLoad = useQuery(() => {
const ctx = home.server.focusedContext()
const conn = home.server.focused()
Expand Down Expand Up @@ -84,6 +93,7 @@ export function createHomeSessionsController(home: HomeController) {
projectDirectories,
projects: home.project.list,
projectByID,
projectMetadataByID,
}),
)
const records = createMemo(() => allRecords().slice(0, HOME_SESSION_LIMIT))
Expand Down Expand Up @@ -208,30 +218,6 @@ function directories(project: LocalProject) {
return [project.worktree, ...(project.sandboxes ?? [])]
}

function buildHomeSessionRecords(input: {
sessions: () => SessionInfo[]
projectDirectories: () => string[]
projects: () => LocalProject[]
projectByID: () => Map<string, LocalProject>
}) {
const directories = new Set(input.projectDirectories().map(pathKey))
const sessions = input.sessions().filter((session) => directories.has(pathKey(session.location.directory)))
return [...new Map(sessions.map((session) => [session.id, session] as const)).values()]
.sort(compareSessionTime)
.flatMap((session) => {
const directory = pathKey(session.location.directory)
const project =
input
.projects()
.find(
(item) =>
pathKey(item.worktree) === directory || item.sandboxes?.some((sandbox) => pathKey(sandbox) === directory),
) ?? projectForSession(session, input.projects(), input.projectByID())
if (!project) return []
return { session, project, projectName: displayName(project) }
})
}

export function homeSessionSearchKey(record: HomeSessionRecord) {
return `${pathKey(record.session.location.directory)}:${record.session.id}`
}
Expand Down
34 changes: 34 additions & 0 deletions packages/app/src/pages/home/home-sessions-model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { SessionInfo } from "@opencode-ai/client/promise"
import type { LocalProject } from "@/context/layout"
import { compareSessionTime, displayName } from "@/pages/layout/helpers"
import { pathKey } from "@/utils/path-key"

export function buildHomeSessionRecords(input: {
sessions: () => SessionInfo[]
projectDirectories: () => string[] | undefined
projects: () => LocalProject[]
projectByID: () => Map<string, LocalProject>
projectMetadataByID: () => Map<string, LocalProject>
}) {
const selected = input.projectDirectories()
const directories = selected ? new Set(selected.map(pathKey)) : undefined
const sessions = directories
? input.sessions().filter((session) => directories.has(pathKey(session.location.directory)))
: input.sessions()
return [...new Map(sessions.map((session) => [session.id, session] as const)).values()]
.sort(compareSessionTime)
.map((session) => {
const directory = pathKey(session.location.directory)
const project = input
.projects()
.find(
(item) =>
pathKey(item.worktree) === directory || item.sandboxes?.some((sandbox) => pathKey(sandbox) === directory),
) ?? {
...(input.projectByID().get(session.projectID) ?? input.projectMetadataByID().get(session.projectID)),
worktree: session.location.directory,
expanded: false,
}
return { session, project, projectName: displayName(project) }
})
}
Loading