6053 lines · 220.4 KB · TypeScript
RawDownload
import {  type ApprovalRequestId,  DEFAULT_MODEL,  defaultInstanceIdForDriver,  type EnvironmentId,  type MessageId,  type ModelSelection,  type ProjectScript,  type ProjectId,  type ProviderApprovalDecision,  ProviderInstanceId,  type ServerProvider,  type ResolvedKeybindingsConfig,  type ScopedThreadRef,  type ThreadId,  type TurnId,  type KeybindingCommand,  OrchestrationThreadActivity,  ProviderInteractionMode,  ProviderDriverKind,  RuntimeMode,  TerminalOpenInput,} from "@t3tools/contracts";import {  connectionStatusTitle,  type EnvironmentConnectionPresentation,} from "@t3tools/client-runtime/connection";import { effectiveSettled, effectiveSnoozed } from "@t3tools/client-runtime/state/thread-settled";import {  parseScopedThreadKey,  scopedThreadKey,  scopeProjectRef,  scopeThreadRef,} from "@t3tools/client-runtime/environment";import {  applyClaudePromptEffortPrefix,  createModelSelection,  resolvePromptInjectedEffort,} from "@t3tools/shared/model";import { CHAT_LIST_ANCHOR_OFFSET } from "@t3tools/shared/chatList";import { projectScriptCwd, projectScriptRuntimeEnv } from "@t3tools/shared/projectScripts";import { truncate } from "@t3tools/shared/String";import { nextTerminalId, resolveTerminalSessionLabel } from "@t3tools/shared/terminalLabels";import { Debouncer } from "@tanstack/react-pacer";import { useAtomValue } from "@effect/atom-react";import {  lazy,  memo,  Suspense,  useCallback,  useEffect,  useLayoutEffect,  useMemo,  useRef,  useState,} from "react";import { flushSync } from "react-dom";import { useNavigate } from "@tanstack/react-router";import { useShallow } from "zustand/react/shallow";import {  isAtomCommandInterrupted,  mapAtomCommandResult,  settlePromise,  squashAtomCommandFailure,  type AtomCommandResult,} from "@t3tools/client-runtime/state/runtime";import * as Cause from "effect/Cause";import { AsyncResult } from "effect/unstable/reactivity";import { isElectron } from "../env";import { readLocalApi } from "../localApi";import { useDiffPanelStore } from "../diffPanelStore";import {  collapseExpandedComposerCursor,  parseStandaloneComposerSlashCommand,} from "../composer-logic";import {  derivePendingApprovals,  derivePendingUserInputs,  derivePhase,  deriveTimelineEntries,  deriveActiveWorkStartedAt,  deriveActivePlanState,  findSidebarProposedPlan,  findLatestProposedPlan,  deriveWorkLogEntries,  hasActionableProposedPlan,  isLatestTurnSettled,} from "../session-logic";import { type LegendListRef } from "@legendapp/list/react";import { getAnchoredTurnMetrics, type TimelineScrollMode } from "./chat/timelineScrollAnchoring";import {  buildPendingUserInputAnswers,  derivePendingUserInputProgress,  setPendingUserInputCustomAnswer,  togglePendingUserInputOptionSelection,  type PendingUserInputDraftAnswer,} from "../pendingUserInput";import { useUiStateStore } from "../uiStateStore";import {  buildPlanImplementationThreadTitle,  buildPlanImplementationPrompt,  resolvePlanFollowUpSubmission,} from "../proposedPlan";import {  DEFAULT_INTERACTION_MODE,  DEFAULT_RUNTIME_MODE,  DEFAULT_THREAD_TERMINAL_ID,  MAX_TERMINALS_PER_GROUP,  type ChatMessage,  type SessionPhase,  type Thread,  type TurnDiffSummary,} from "../types";import { useTheme } from "../hooks/useTheme";import { useTurnDiffSummaries } from "../hooks/useTurnDiffSummaries";import { isCommandPaletteOpen } from "../commandPaletteBus";import { buildTemporaryWorktreeBranchName } from "@t3tools/shared/git";import { useMediaQuery } from "../hooks/useMediaQuery";import { RIGHT_PANEL_INLINE_LAYOUT_MEDIA_QUERY } from "../rightPanelLayout";import {  selectActiveRightPanel,  selectActiveRightPanelSurface,  selectThreadRightPanelState,  type RightPanelSurface,  useRightPanelStore,} from "../rightPanelStore";import {  isPreviewSupportedInRuntime,  setActivePreviewTab,  useThreadPreviewState,} from "../previewStateStore";import { addBrowserSurface } from "./preview/addBrowserSurface";import { closePreviewSession } from "./preview/closePreviewSession";import { subscribePreviewAction } from "./preview/previewActionBus";import { getConfiguredPreviewUrls } from "./preview/previewEmptyStateLogic";import { RightPanelTabs } from "./RightPanelTabs";import { DiffWorkerPoolProvider } from "./DiffWorkerPoolProvider";import { BranchToolbar } from "./BranchToolbar";import { resolveShortcutCommand, shortcutLabelForCommand } from "../keybindings";import PlanSidebar from "./PlanSidebar";import ThreadTerminalDrawer from "./ThreadTerminalDrawer";import {  AlarmClockIcon,  CheckCircle2Icon,  ChevronDownIcon,  GitBranchIcon,  TriangleAlertIcon,  WifiOffIcon,} from "lucide-react";import { cn, randomHex } from "~/lib/utils";import { COLLAPSED_SIDEBAR_TITLEBAR_INSET_CLASS } from "~/workspaceTitlebar";import { stackedThreadToast, toastManager } from "./ui/toast";import { decodeProjectScriptKeybindingRule } from "~/lib/projectScriptKeybindings";import { type NewProjectScriptInput } from "./ProjectScriptsControl";import {  buildProjectScript,  commandForProjectScript,  nextProjectScriptId,  projectScriptIdFromCommand,} from "~/projectScripts";import { newDraftId, newMessageId, newThreadId } from "~/lib/utils";import { getProviderModelCapabilities, resolveSelectableProvider } from "../providerModels";import { NO_PROVIDER_MODEL_SELECTION } from "../providerInstances";import { useClientSettings, useEnvironmentSettings } from "../hooks/useSettings";import { useNowMinute } from "../hooks/useNowMinute";import { resolveAppModelSelectionForInstance } from "../modelSelection";import { getTerminalFocusOwner } from "../lib/terminalFocus";import { resolveNewDraftStartFromOrigin } from "../lib/chatThreadActions";import {  deriveLogicalProjectKeyFromSettings,  selectProjectGroupingSettings,} from "../logicalProject";import { buildDraftThreadRouteParams } from "../threadRoutes";import {  type ComposerImageAttachment,  type DraftThreadEnvMode,  useComposerDraftStore,  type DraftId,} from "../composerDraftStore";import {  appendTerminalContextsToPrompt,  formatTerminalContextLabel,  type TerminalContextDraft,  type TerminalContextSelection,} from "../lib/terminalContext";import {  appendElementContextsToPrompt,  type ElementContextDraft,  formatElementContextLabel,} from "../lib/elementContext";import { appendPreviewAnnotationPrompt } from "../lib/previewAnnotation";import { appendReviewCommentsToPrompt, type ReviewCommentContext } from "../reviewCommentContext";import { environmentCatalog } from "../connection/catalog";import { selectThreadTerminalUiState, useTerminalUiStateStore } from "../terminalUiStateStore";import { useKnownTerminalSessions, useThreadRunningTerminalIds } from "../state/terminalSessions";import { projectEnvironment } from "../state/projects";import { useEnvironmentQuery } from "../state/query";import {  primaryServerAvailableEditorsAtom,  primaryServerKeybindingsAtom,  primaryServerSettingsAtom,  serverEnvironment,} from "../state/server";import { terminalEnvironment } from "../state/terminal";import { threadEnvironment } from "../state/threads";import { vcsEnvironment } from "../state/vcs";import { useEnvironments, usePrimaryEnvironment } from "../state/environments";import {  useProject,  useProjects,  useThread,  useThreadProposedPlans,  useThreadRefs,  useThreadShell,} from "../state/entities";import { environmentShell } from "../state/shell";import { ChatComposer, type ChatComposerHandle } from "./chat/ChatComposer";import { DraftHeroHeadline } from "./chat/DraftHeroHeadline";import { ExpandedImageDialog } from "./chat/ExpandedImageDialog";import { PullRequestThreadDialog } from "./PullRequestThreadDialog";import { MessagesTimeline } from "./chat/MessagesTimeline";import { ChatHeader } from "./chat/ChatHeader";import { PanelLayoutControls, RightPanelMaximizeControl } from "./chat/PanelLayoutControls";import { type ExpandedImagePreview } from "./chat/ExpandedImagePreview";import { NoActiveThreadState } from "./NoActiveThreadState";import { resolveEffectiveEnvMode, resolveLocalCheckoutBranchMismatch } from "./BranchToolbar.logic";import {  getProviderStatusBannerKey,  ProviderStatusBanner,  shouldShowProviderStatusBanner,} from "./chat/ProviderStatusBanner";import { ThreadErrorBanner } from "./chat/ThreadErrorBanner";import { resolveThreadPr } from "./ThreadStatusIndicators";import { ComposerBannerStack, type ComposerBannerStackItem } from "./chat/ComposerBannerStack";import {  DRAFT_HERO_TRANSITION_ANIMATION_ID,  DRAFT_HERO_TRANSITION_DURATION_MS,  DRAFT_HERO_TRANSITION_EASING,  MOBILE_COMPOSER_VIEW_TRANSITION_NAME,  MOBILE_DRAFT_HEADLINE_VIEW_TRANSITION_NAME,  runMobileComposerTransition,} from "./chat/draftHeroTransition";import {  MAX_HIDDEN_MOUNTED_TERMINAL_THREADS,  branchMismatchKey,  buildExpiredTerminalContextToastCopy,  buildLocalDraftThread,  buildThreadTurnInterruptInput,  collectUserMessageBlobPreviewUrls,  createLocalDispatchSnapshot,  deriveComposerSendState,  dismissBranchMismatchForSession,  hasServerAcknowledgedLocalDispatch,  isBranchMismatchDismissedForSession,  shouldShowBranchMismatchBanner,  getStartedThreadModelChangeBlockReason,  LAST_INVOKED_SCRIPT_BY_PROJECT_KEY,  LastInvokedScriptByProjectSchema,  type LocalDispatchSnapshot,  PullRequestDialogState,  cloneComposerImageForRetry,  deriveLockedProvider,  readFileAsDataUrl,  reconcileMountedTerminalThreadIds,  resolveThreadMetadataUpdateForNextTurn,  resolveSendEnvMode,  revokeBlobPreviewUrl,  revokeUserMessagePreviewUrls,  waitForStartedServerThread,} from "./ChatView.logic";import { useLocalStorage } from "~/hooks/useLocalStorage";import { useComposerHandleContext } from "../composerHandleContext";import { sanitizeThreadErrorMessage } from "~/rpc/transportError";import { RightPanelSheet } from "./RightPanelSheet";import { previewEnvironment } from "../state/preview";import { useAtomCommand } from "../state/use-atom-command";import { Button } from "./ui/button";import {  AlertDialog,  AlertDialogClose,  AlertDialogDescription,  AlertDialogFooter,  AlertDialogHeader,  AlertDialogPopup,  AlertDialogTitle,} from "./ui/alert-dialog";import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip";import { ServerUpdateAction } from "./ServerUpdateAction";import {  buildVersionMismatchDismissalKey,  dismissVersionMismatch,  isVersionMismatchDismissed,  resolveServerConfigVersionMismatch,  resolveServerSelfUpdateCapability,  serverUpdateGuidance,} from "../versionSkew";import { useAssetUrls } from "../assets/assetUrls"; const IMAGE_ONLY_BOOTSTRAP_PROMPT =  "[User attached one or more images without additional text. Respond using the conversation context and the attached image(s).]";const EMPTY_ACTIVITIES: OrchestrationThreadActivity[] = [];const EMPTY_PROVIDERS: ServerProvider[] = [];const EMPTY_PROVIDER_SKILLS: ServerProvider["skills"] = [];const EMPTY_PENDING_USER_INPUT_ANSWERS: Record<string, PendingUserInputDraftAnswer> = {};function useDraftHeroLayoutTransition(isDraftHeroState: boolean) {  const transitionGroupRef = useRef<HTMLDivElement | null>(null);  const composerAnchorRef = useRef<HTMLDivElement | null>(null);  const previousStateRef = useRef(isDraftHeroState);  const previousComposerRectRef = useRef<DOMRect | null>(null);  const animationRef = useRef<Animation | null>(null);  const attachTransitionGroupRef = (element: HTMLDivElement | null) => {    transitionGroupRef.current = element;  };  const attachComposerAnchorRef = (element: HTMLDivElement | null) => {    composerAnchorRef.current = element;  };  const captureComposerRect = () => {    previousComposerRectRef.current = composerAnchorRef.current?.getBoundingClientRect() ?? null;  };   useLayoutEffect(() => {    const transitionGroup = transitionGroupRef.current;    const nextComposerRect = composerAnchorRef.current?.getBoundingClientRect() ?? null;    const stateChanged = previousStateRef.current !== isDraftHeroState;    const prefersReducedMotion =      typeof window !== "undefined" &&      window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;    const mobileComposerTransitionActive =      typeof document !== "undefined" &&      document.documentElement.dataset.mobileComposerRouteTransition === "true";     animationRef.current?.cancel();    animationRef.current = null;     const previousComposerRect = previousComposerRectRef.current;    if (      stateChanged &&      !prefersReducedMotion &&      !mobileComposerTransitionActive &&      transitionGroup &&      previousComposerRect &&      nextComposerRect &&      typeof transitionGroup.animate === "function"    ) {      const translateX = previousComposerRect.left - nextComposerRect.left;      const translateY = previousComposerRect.top - nextComposerRect.top;      if (Math.abs(translateX) >= 0.5 || Math.abs(translateY) >= 0.5) {        const animation = transitionGroup.animate(          [            { transform: `translate3d(${translateX}px, ${translateY}px, 0)` },            { transform: "translate3d(0, 0, 0)" },          ],          {            duration: DRAFT_HERO_TRANSITION_DURATION_MS,            easing: DRAFT_HERO_TRANSITION_EASING,          },        );        animation.id = DRAFT_HERO_TRANSITION_ANIMATION_ID;        animationRef.current = animation;        void animation.finished          .catch(() => undefined)          .then(() => {            if (animationRef.current !== animation) {              return;            }            animationRef.current = null;          });      }    }     previousStateRef.current = isDraftHeroState;    previousComposerRectRef.current = nextComposerRect;  }, [isDraftHeroState]);   return [attachTransitionGroupRef, attachComposerAnchorRef, captureComposerRect] as const;}const PreviewPanel = lazy(() =>  import("./preview/PreviewPanel").then((module) => ({ default: module.PreviewPanel })),);const DiffPanel = lazy(() => import("./DiffPanel"));const FilePreviewPanel = lazy(() => import("./files/FilePreviewPanel"));const EMPTY_PENDING_FILE_SURFACE_IDS: ReadonlySet<string> = new Set();const TYPE_TO_FOCUS_EDITABLE_SELECTOR = [  "input",  "textarea",  "select",  '[contenteditable="true"]',  '[contenteditable="plaintext-only"]',  '[role="textbox"]',].join(",");const TYPE_TO_FOCUS_INTERACTIVE_SELECTOR = [  "button",  "a[href]",  "summary",  '[role="button"]',  '[role="checkbox"]',  '[role="menuitem"]',  '[role="option"]',  '[role="radio"]',  '[role="switch"]',  '[role="tab"]',].join(",");const TYPE_TO_FOCUS_FLOATING_LAYER_SELECTOR = [  '[data-slot="dialog"]',  '[data-slot="menu-popup"]',  '[data-slot="select-popup"]',  '[data-slot="popover-popup"]',  '[data-slot="combobox-popup"]',  '[data-slot="autocomplete-popup"]',].join(","); type EnvironmentUnavailableState = {  readonly environmentId: EnvironmentId;  readonly label: string;  readonly connection: EnvironmentConnectionPresentation;}; type ThreadPlanCatalogEntry = Pick<Thread, "id" | "proposedPlans">; function eventPathContainsSelector(event: Event, selector: string): boolean {  const path = event.composedPath();  if (path.length === 0 && event.target) {    path.push(event.target);  }  return path.some((target) => target instanceof Element && target.closest(selector));} function shouldTypeToFocusComposer(event: KeyboardEvent): boolean {  if (event.defaultPrevented || event.isComposing) return false;  if (event.metaKey || event.ctrlKey || event.altKey) return false;  if (event.key.length !== 1) return false;   if (eventPathContainsSelector(event, TYPE_TO_FOCUS_EDITABLE_SELECTOR)) return false;  if (eventPathContainsSelector(event, TYPE_TO_FOCUS_INTERACTIVE_SELECTOR)) return false;  if (document.querySelector(TYPE_TO_FOCUS_FLOATING_LAYER_SELECTOR)) return false;   return true;} function formatOutgoingPrompt(params: {  provider: ProviderDriverKind;  model: string | null;  models: ReadonlyArray<ServerProvider["models"][number]>;  effort: string | null;  text: string;}): string {  const caps = getProviderModelCapabilities(params.models, params.model, params.provider);  const promptEffort = resolvePromptInjectedEffort(caps, params.effort);  return applyClaudePromptEffortPrefix(params.text, promptEffort);}const SCRIPT_TERMINAL_COLS = 120;const SCRIPT_TERMINAL_ROWS = 30; type ChatViewProps =  | {      environmentId: EnvironmentId;      threadId: ThreadId;      onDiffPanelOpen?: () => void;      reserveTitleBarControlInset?: boolean;      forceExpandedMobileComposer?: boolean;      routeKind: "server";      draftId?: never;    }  | {      environmentId: EnvironmentId;      threadId: ThreadId;      onDiffPanelOpen?: () => void;      reserveTitleBarControlInset?: boolean;      forceExpandedMobileComposer?: boolean;      routeKind: "draft";      draftId: DraftId;    }; interface TerminalLaunchContext {  threadId: ThreadId;  cwd: string;  worktreePath: string | null;} type PersistentTerminalLaunchContext = Pick<TerminalLaunchContext, "cwd" | "worktreePath">; function useLocalDispatchState(input: {  activeThread: Thread | undefined;  activeLatestTurn: Thread["latestTurn"] | null;  phase: SessionPhase;  activePendingApproval: ApprovalRequestId | null;  activePendingUserInput: ApprovalRequestId | null;  threadError: string | null | undefined;}) {  const [localDispatch, setLocalDispatch] = useState<LocalDispatchSnapshot | null>(null);  const latestUserMessageId =    input.activeThread?.messages.findLast((message) => message.role === "user")?.id ?? null;   const resetLocalDispatch = useCallback(() => {    setLocalDispatch(null);  }, []);   const serverAcknowledgedLocalDispatch = useMemo(    () =>      hasServerAcknowledgedLocalDispatch({

Lines 1–500 of 6053.