144 lines · 5.2 KB · TypeScript
RawDownload
import { scopedThreadKey } from "@t3tools/client-runtime/environment";import type { ScopedThreadRef, TurnId } from "@t3tools/contracts";import { create } from "zustand";import { createJSONStorage, persist } from "zustand/middleware"; import { resolveStorage } from "./lib/storage"; export type DiffPanelSelection =  | { kind: "branch"; baseRef: string | null }  | { kind: "unstaged" }  | { kind: "turn"; turnId: TurnId; filePath: string | null; revealRequestId: number }; const DEFAULT_SELECTION: DiffPanelSelection = { kind: "branch", baseRef: null };const DEFAULT_WORKING_TREE_SELECTION: DiffPanelSelection = { kind: "unstaged" }; interface DiffPanelStoreState {  byThreadKey: Record<string, DiffPanelSelection>;  branchBaseRefByThreadKey: Record<string, string | null>;  selectGitScope: (ref: ScopedThreadRef, scope: "branch" | "unstaged") => void;  selectBranchBaseRef: (ref: ScopedThreadRef, baseRef: string | null) => void;  selectTurn: (ref: ScopedThreadRef, turnId: TurnId, filePath?: string) => void;  reconcileTurnSelection: (ref: ScopedThreadRef, availableTurnIds: ReadonlyArray<TurnId>) => void;  removeThread: (ref: ScopedThreadRef) => void;} function normalizeBaseRef(baseRef: string | null): string | null {  const normalized = baseRef?.trim();  return normalized ? normalized : null;} export const useDiffPanelStore = create<DiffPanelStoreState>()(  persist(    (set) => ({      byThreadKey: {},      branchBaseRefByThreadKey: {},      selectGitScope: (ref, scope) =>        set((state) => {          const threadKey = scopedThreadKey(ref);          const previous = state.byThreadKey[threadKey];          const previousBaseRef =            previous?.kind === "branch"              ? previous.baseRef              : (state.branchBaseRefByThreadKey[threadKey] ?? null);          return {            byThreadKey: {              ...state.byThreadKey,              [threadKey]:                scope === "branch"                  ? { kind: "branch", baseRef: previousBaseRef }                  : { kind: "unstaged" },            },            branchBaseRefByThreadKey:              previous?.kind === "branch"                ? { ...state.branchBaseRefByThreadKey, [threadKey]: previous.baseRef }                : state.branchBaseRefByThreadKey,          };        }),      selectBranchBaseRef: (ref, baseRef) =>        set((state) => {          const threadKey = scopedThreadKey(ref);          const normalizedBaseRef = normalizeBaseRef(baseRef);          return {            byThreadKey: {              ...state.byThreadKey,              [threadKey]: { kind: "branch", baseRef: normalizedBaseRef },            },            branchBaseRefByThreadKey: {              ...state.branchBaseRefByThreadKey,              [threadKey]: normalizedBaseRef,            },          };        }),      selectTurn: (ref, turnId, filePath) =>        set((state) => {          const threadKey = scopedThreadKey(ref);          const previous = state.byThreadKey[threadKey];          return {            byThreadKey: {              ...state.byThreadKey,              [threadKey]: {                kind: "turn",                turnId,                filePath: filePath?.trim() || null,                revealRequestId: previous?.kind === "turn" ? previous.revealRequestId + 1 : 1,              },            },          };        }),      reconcileTurnSelection: (ref, availableTurnIds) =>        set((state) => {          const threadKey = scopedThreadKey(ref);          const previous = state.byThreadKey[threadKey];          const latestTurnId = availableTurnIds[0];          if (            previous?.kind !== "turn" ||            latestTurnId === undefined ||            availableTurnIds.includes(previous.turnId)          ) {            return state;          }          return {            byThreadKey: {              ...state.byThreadKey,              [threadKey]: { ...previous, turnId: latestTurnId },            },          };        }),      removeThread: (ref) =>        set((state) => {          const threadKey = scopedThreadKey(ref);          if (!(threadKey in state.byThreadKey) && !(threadKey in state.branchBaseRefByThreadKey)) {            return state;          }          const { [threadKey]: _removed, ...byThreadKey } = state.byThreadKey;          const { [threadKey]: _removedBaseRef, ...branchBaseRefByThreadKey } =            state.branchBaseRefByThreadKey;          return { byThreadKey, branchBaseRefByThreadKey };        }),    }),    {      name: "t3code:diff-panel-state:v1",      version: 1,      storage: createJSONStorage(() =>        resolveStorage(typeof window !== "undefined" ? window.localStorage : undefined),      ),      partialize: (state) => ({        byThreadKey: state.byThreadKey,        branchBaseRefByThreadKey: state.branchBaseRefByThreadKey,      }),    },  ),); export function selectThreadDiffPanelSelection(  byThreadKey: Record<string, DiffPanelSelection>,  ref: ScopedThreadRef | null | undefined,  hasWorkingTreeChanges = false,): DiffPanelSelection {  if (!ref) return DEFAULT_SELECTION;  return (    byThreadKey[scopedThreadKey(ref)] ??    (hasWorkingTreeChanges ? DEFAULT_WORKING_TREE_SELECTION : DEFAULT_SELECTION)  );}