164 lines · 6.1 KB · TypeScript
RawDownload
import type { EnvironmentId, ScopedProjectRef } from "@t3tools/contracts";import { buildProjectGroups, type ProjectGroupingSettings } from "./logicalProject";import type { Project } from "./types"; export type EnvironmentPresence = "local-only" | "remote-only" | "mixed"; export interface SidebarProjectGroupMember extends Project {  physicalProjectKey: string;  environmentLabel: string | null;} export interface SidebarProjectSnapshot extends Project {  projectKey: string;  displayName: string;  groupedProjectCount: number;  environmentPresence: EnvironmentPresence;  // True iff every non-primary member of this group lives in a  // desktop-local environment. The sidebar uses this  // to differentiate "lives on this machine but in a sandbox" from  // "lives on a real remote" so the project header can pick a  // local-device treatment instead of the generic remote treatment.  allRemoteMembersAreDesktopLocal: boolean;  allRemoteMembersAreWsl: boolean;  memberProjects: readonly SidebarProjectGroupMember[];  memberProjectRefs: readonly ScopedProjectRef[];  remoteEnvironmentLabels: readonly string[];} export interface SidebarProjectPickerEntry {  group: SidebarProjectSnapshot;  targetProject: SidebarProjectGroupMember;  isPreferred: boolean;} export function buildPhysicalToLogicalProjectKeyMap(input: {  projects: ReadonlyArray<Project>;  settings: ProjectGroupingSettings;  primaryEnvironmentId: EnvironmentId | null;}): Map<string, string> {  const mapping = new Map<string, string>();  const groups = buildProjectGroups({    projects: input.projects,    settings: input.settings,    preferredEnvironmentId: input.primaryEnvironmentId,  });  for (const group of groups) {    for (const member of group.members) {      mapping.set(member.physicalProjectKey, group.key);    }  }  return mapping;} export function buildSidebarProjectSnapshots(input: {  projects: ReadonlyArray<Project>;  settings: ProjectGroupingSettings;  primaryEnvironmentId: EnvironmentId | null;  resolveEnvironmentLabel: (environmentId: EnvironmentId) => string | null;  // Returns true when an env id maps to a desktop-local saved-env  // record. Defaults to "false for every  // env" so callers that don't care about the distinction get the  // legacy behavior.  isDesktopLocalEnvironment?: (environmentId: EnvironmentId) => boolean;  isWslEnvironment?: (environmentId: EnvironmentId) => boolean;}): SidebarProjectSnapshot[] {  return buildProjectGroups({    projects: input.projects,    settings: input.settings,    preferredEnvironmentId: input.primaryEnvironmentId,  }).map((group): SidebarProjectSnapshot => {    const members = group.members.map(      ({ physicalProjectKey, project }): SidebarProjectGroupMember => ({        ...project,        physicalProjectKey,        environmentLabel: input.resolveEnvironmentLabel(project.environmentId),      }),    );    const representative =      members.find(        (member) =>          member.environmentId === group.representative.environmentId &&          member.id === group.representative.id,      ) ?? members[0]!;     const hasLocal =      input.primaryEnvironmentId !== null &&      members.some((member) => member.environmentId === input.primaryEnvironmentId);    const hasRemote =      input.primaryEnvironmentId !== null        ? members.some((member) => member.environmentId !== input.primaryEnvironmentId)        : false;    const remoteMembers = members.filter(      (member) =>        input.primaryEnvironmentId !== null && member.environmentId !== input.primaryEnvironmentId,    );    const remoteEnvironmentLabels = remoteMembers      .flatMap((member) => (member.environmentLabel ? [member.environmentLabel] : []))      .filter((label, index, labels) => labels.indexOf(label) === index);    const isDesktopLocal = input.isDesktopLocalEnvironment ?? (() => false);    const isWsl = input.isWslEnvironment ?? (() => false);    const allRemoteMembersAreDesktopLocal =      remoteMembers.length > 0 &&      remoteMembers.every((member) => isDesktopLocal(member.environmentId));    const allRemoteMembersAreWsl =      remoteMembers.length > 0 && remoteMembers.every((member) => isWsl(member.environmentId));     return {      ...representative,      projectKey: group.key,      displayName: group.label,      groupedProjectCount: members.length,      environmentPresence:        hasLocal && hasRemote ? "mixed" : hasRemote ? "remote-only" : "local-only",      allRemoteMembersAreDesktopLocal,      allRemoteMembersAreWsl,      memberProjects: members,      memberProjectRefs: group.memberProjectRefs,      remoteEnvironmentLabels,    };  });} export function buildSidebarProjectPickerEntries(input: {  groups: ReadonlyArray<SidebarProjectSnapshot>;  preferredProjectRef: ScopedProjectRef | null;}) {  const preferredProjectRef = input.preferredProjectRef;  const entries = input.groups.flatMap((group): SidebarProjectPickerEntry[] => {    const isPreferred = preferredProjectRef      ? group.memberProjectRefs.some(          (projectRef) =>            projectRef.environmentId === preferredProjectRef.environmentId &&            projectRef.projectId === preferredProjectRef.projectId,        )      : false;    const preferredProject = preferredProjectRef      ? (group.memberProjects.find(          (project) =>            project.environmentId === preferredProjectRef.environmentId &&            project.id === preferredProjectRef.projectId,        ) ??        group.memberProjects.find(          (project) => project.environmentId === preferredProjectRef.environmentId,        ))      : null;    const targetProject =      preferredProject ??      group.memberProjects.find(        (project) => project.environmentId === group.environmentId && project.id === group.id,      ) ??      group.memberProjects[0];    if (!targetProject) return [];     return [{ group, targetProject, isPreferred }];  });  const preferredIndex = entries.findIndex((entry) => entry.isPreferred);  if (preferredIndex <= 0) return entries;   return [    entries[preferredIndex]!,    ...entries.slice(0, preferredIndex),    ...entries.slice(preferredIndex + 1),  ];}