export interface Workspace { id: string; name: string; repositories: string[]; openPaths: string[]; activePath: string; } export interface WorkspaceState { selectedId: string; workspaces: Workspace[]; defaultSession: { openPaths: string[]; activePath: string }; } export const WORKSPACES_KEY = "gitlite.workspaces.v1"; const paths = (value: unknown): string[] => Array.isArray(value) ? [...new Set(value.filter((path): path is string => typeof path === "string" && !!path.trim()))] : []; export function readWorkspaces(storage: Pick, openPaths: string[]): WorkspaceState { const fallback: WorkspaceState = { selectedId: "", workspaces: [], defaultSession: { openPaths, activePath: "" } }; try { const raw = storage.getItem(WORKSPACES_KEY); if (raw) { const saved = JSON.parse(raw); const workspaces: Workspace[] = Array.isArray(saved.workspaces) ? saved.workspaces.flatMap((item: any) => { if (!item || typeof item.id !== "string" || !item.id.startsWith("workspace-") || typeof item.name !== "string" || !item.name.trim()) return []; const repositories = paths(item.repositories); const openPaths = paths(item.openPaths).filter(path => repositories.includes(path)); return [{ id: item.id, name: item.name, repositories, openPaths, activePath: openPaths.includes(item.activePath) ? item.activePath : openPaths[0] ?? "" }]; }) : []; const defaultPaths = paths(saved.defaultSession?.openPaths); return { workspaces, selectedId: workspaces.some(item => item.id === saved.selectedId) ? saved.selectedId : "", defaultSession: { openPaths: defaultPaths, activePath: defaultPaths.includes(saved.defaultSession?.activePath) ? saved.defaultSession.activePath : defaultPaths[0] ?? "" } }; } const legacy = JSON.parse(storage.getItem("gitty.dashboard.v1") ?? "{}"); if (Array.isArray(legacy.workspaces)) fallback.workspaces = legacy.workspaces.flatMap((item: any) => { if (!item || typeof item.id !== "string" || !item.id.startsWith("workspace-") || typeof item.name !== "string" || !item.name.trim()) return []; const repositories = Object.entries(legacy.assignments ?? {}).filter(([, id]) => id === item.id).map(([path]) => path); const tabs = openPaths.filter(path => repositories.includes(path)); return [{ ...item, repositories, openPaths: tabs, activePath: tabs[0] ?? "" }]; }); } catch { /* Invalid optional preferences must not block startup. */ } return fallback; }