134 lines · 3.9 KB · TypeScript
RawDownload
import type { EnvironmentId, OrchestrationThread, ThreadId } from "@t3tools/contracts";import * as Option from "effect/Option";import { useEffect, useMemo, useState } from "react"; import { orchestrationEnvironment } from "./orchestration";import { projectEnvironment } from "./projects";import { useEnvironmentQuery } from "./query";import { useEnvironmentThread } from "./threads";import { vcsEnvironment } from "./vcs";import {  buildCheckpointDiffTargets,  normalizeComposerPathSearchQuery,  type CheckpointDiffTarget,} from "./queryTargets"; const COMPOSER_PATH_SEARCH_DEBOUNCE_MS = 200;const COMPOSER_PATH_SEARCH_LIMIT = 20;const VCS_REF_LIST_LIMIT = 100; export interface ThreadDetailView {  readonly data: OrchestrationThread | null;  readonly error: string | null;  readonly isPending: boolean;  readonly isDeleted: boolean;} export interface ComposerPathSearchTarget {  readonly environmentId: EnvironmentId | null;  readonly cwd: string | null;  readonly query: string | null;} function useDebouncedValue<A>(value: A, delayMs: number): A {  const [debounced, setDebounced] = useState(value);   useEffect(() => {    const timer = setTimeout(() => {      setDebounced(value);    }, delayMs);    return () => {      clearTimeout(timer);    };  }, [delayMs, value]);   return debounced;} export function useThreadDetail(  environmentId: EnvironmentId | null,  threadId: ThreadId | null,): ThreadDetailView {  const state = useEnvironmentThread(environmentId, threadId);  return {    data: Option.getOrNull(state.data),    error: Option.getOrNull(state.error),    isPending: state.status === "synchronizing",    isDeleted: state.status === "deleted",  };} export function useBranches(input: {  readonly environmentId: EnvironmentId | null;  readonly cwd: string | null;  readonly query?: string | null;}) {  const query = input.query?.trim() ?? "";  return useEnvironmentQuery(    input.environmentId !== null && input.cwd !== null      ? vcsEnvironment.listRefs({          environmentId: input.environmentId,          input: {            cwd: input.cwd,            ...(query.length > 0 ? { query } : {}),            limit: VCS_REF_LIST_LIMIT,          },        })      : null,  );} export function useComposerPathSearch(target: ComposerPathSearchTarget) {  const normalizedTarget = useMemo(    () => ({      environmentId: target.environmentId,      cwd: target.cwd,      query: normalizeComposerPathSearchQuery(target.query),    }),    [target.cwd, target.environmentId, target.query],  );  const debouncedTarget = useDebouncedValue(normalizedTarget, COMPOSER_PATH_SEARCH_DEBOUNCE_MS);  const result = useEnvironmentQuery(    debouncedTarget.environmentId !== null &&      debouncedTarget.cwd !== null &&      debouncedTarget.query.length > 0      ? projectEnvironment.searchEntries({          environmentId: debouncedTarget.environmentId,          input: {            cwd: debouncedTarget.cwd,            query: debouncedTarget.query,            limit: COMPOSER_PATH_SEARCH_LIMIT,          },        })      : null,  );   return {    entries: result.data?.entries ?? [],    error: result.error,    isPending: normalizedTarget.query !== debouncedTarget.query || result.isPending,    refresh: result.refresh,  };} export function useCheckpointDiff(target: CheckpointDiffTarget) {  const targets = useMemo(    () => buildCheckpointDiffTargets(target),    [      target.environmentId,      target.fromTurnCount,      target.ignoreWhitespace,      target.threadId,      target.toTurnCount,    ],  );  const fullThread = useEnvironmentQuery(    targets.fullThread === null      ? null      : orchestrationEnvironment.fullThreadDiff(targets.fullThread),  );  const turn = useEnvironmentQuery(    targets.turn === null ? null : orchestrationEnvironment.turnDiff(targets.turn),  );  return targets.fullThread === null ? turn : fullThread;}