mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-08 11:33:20 +00:00
99 lines
4 KiB
TypeScript
99 lines
4 KiB
TypeScript
import { pathKey } from "@/utils/path-key"
|
|
import type { WorkspaceDefaultDestination, WorkspaceLastUsed } from "@/context/settings"
|
|
import type { Session } from "@opencode-ai/sdk/v2/client"
|
|
|
|
type WorkspaceProject = { worktree: string; sandboxes?: readonly string[] }
|
|
|
|
export function workspaceDirectories(project: WorkspaceProject) {
|
|
return (project.sandboxes ?? []).filter(
|
|
(directory) => !containsDirectory(project.worktree, directory) || !containsDirectory(directory, project.worktree),
|
|
)
|
|
}
|
|
|
|
export function workspaceInventory<T extends WorkspaceProject & { id: string }>(projects: readonly T[]) {
|
|
return projects.flatMap((project) => workspaceDirectories(project).map((directory) => ({ directory, project })))
|
|
}
|
|
|
|
export function filterWorkspaceInventory<T extends { project: { id: string } }>(
|
|
workspaces: readonly T[],
|
|
project: string,
|
|
) {
|
|
if (project === "all") return [...workspaces]
|
|
return workspaces.filter((workspace) => workspace.project.id === project)
|
|
}
|
|
|
|
export function sessionsForWorkspace(sessions: readonly Session[], workspace: string) {
|
|
return sessions
|
|
.filter((session) => session.time.archived === undefined)
|
|
.filter((session) => containsDirectory(workspace, session.directory))
|
|
.toSorted((a, b) => b.time.updated - a.time.updated)
|
|
}
|
|
|
|
export function mergeWorkspaceSessionInventory(server: readonly Session[], cached: readonly Session[]) {
|
|
const sessions = new Map(server.map((session) => [session.id, session]))
|
|
cached.forEach((session) => {
|
|
const current = sessions.get(session.id)
|
|
if (!current || session.time.updated > current.time.updated) sessions.set(session.id, session)
|
|
})
|
|
return [...sessions.values()]
|
|
}
|
|
|
|
export function removeWorkspacesSequentially<T>(workspaces: readonly T[], remove: (workspace: T) => Promise<void>) {
|
|
return workspaces.reduce((previous, workspace) => previous.then(() => remove(workspace)), Promise.resolve())
|
|
}
|
|
|
|
export type WorkspaceDeleteInspection = "safe" | "active" | "linked" | "dirty"
|
|
|
|
export function inspectWorkspaceDeletion(input: {
|
|
workspace: string
|
|
activeDirectory?: string
|
|
sessions: readonly Session[]
|
|
status: "clean" | "dirty"
|
|
}): WorkspaceDeleteInspection {
|
|
if (input.activeDirectory && containsDirectory(input.workspace, input.activeDirectory)) return "active"
|
|
if (
|
|
input.sessions.some(
|
|
(session) => session.time?.archived === undefined && containsDirectory(input.workspace, session.directory),
|
|
)
|
|
)
|
|
return "linked"
|
|
if (input.status === "dirty") return "dirty"
|
|
return "safe"
|
|
}
|
|
|
|
export function isWorkspaceDirectory(project: WorkspaceProject | undefined, directory: string) {
|
|
if (!project || (containsDirectory(project.worktree, directory) && containsDirectory(directory, project.worktree)))
|
|
return false
|
|
return workspaceDirectories(project).some((workspace) => containsDirectory(workspace, directory))
|
|
}
|
|
|
|
export function isProjectDirectory(project: WorkspaceProject | undefined, directory: string) {
|
|
if (!project) return false
|
|
return [project.worktree, ...(project.sandboxes ?? [])].some((root) => containsDirectory(root, directory))
|
|
}
|
|
|
|
export function containsDirectory(parent: string, child: string) {
|
|
const normalize = (value: string) => {
|
|
const key = pathKey(value)
|
|
return /^[a-z]:\//i.test(key) || key.startsWith("//") ? key.toLowerCase() : key
|
|
}
|
|
const root = normalize(parent)
|
|
const target = normalize(child)
|
|
return target === root || target.startsWith(root.endsWith("/") ? root : `${root}/`)
|
|
}
|
|
|
|
export function isWorkspaceSelection(project: WorkspaceProject | undefined, selection: string) {
|
|
if (selection === "main" || selection === "create") return true
|
|
if (!project) return false
|
|
if (containsDirectory(project.worktree, selection) && containsDirectory(selection, project.worktree)) return true
|
|
return isWorkspaceDirectory(project, selection)
|
|
}
|
|
|
|
export function workspaceDefaultSelection(
|
|
setting: WorkspaceDefaultDestination,
|
|
lastUsed: WorkspaceLastUsed | undefined,
|
|
) {
|
|
if (setting === "local") return "main"
|
|
if (setting === "new") return "create"
|
|
return lastUsed === "workspace" ? "create" : "main"
|
|
}
|