1641 lines · 58.8 KB · TypeScript
RawDownload
import { ArchiveIcon, ArchiveX, LoaderIcon, PlusIcon, RefreshCwIcon } from "lucide-react";import { Link } from "@tanstack/react-router";import { useCallback, useMemo, useRef, useState } from "react";import { useAtomValue } from "@effect/atom-react";import {  defaultInstanceIdForDriver,  type DesktopUpdateChannel,  PROVIDER_DISPLAY_NAMES,  ProviderDriverKind,  type ProviderInstanceConfig,  type ProviderInstanceId,  type ScopedThreadRef,} from "@t3tools/contracts";import { scopeThreadRef } from "@t3tools/client-runtime/environment";import {  isAtomCommandInterrupted,  settlePromise,  squashAtomCommandFailure,} from "@t3tools/client-runtime/state/runtime";import { DEFAULT_UNIFIED_SETTINGS } from "@t3tools/contracts/settings";import { createModelSelection } from "@t3tools/shared/model";import * as Arr from "effect/Array";import * as Duration from "effect/Duration";import * as Equal from "effect/Equal";import * as Result from "effect/Result";import { APP_VERSION, HOSTED_APP_CHANNEL, HOSTED_APP_CHANNEL_LABEL } from "../../branding";import {  canCheckForUpdate,  getDesktopUpdateButtonTooltip,  getDesktopUpdateInstallConfirmationMessage,  isDesktopUpdateButtonDisabled,  resolveDesktopUpdateButtonAction,} from "../../components/desktopUpdate.logic";import { ProviderModelPicker } from "../chat/ProviderModelPicker";import { TraitsPicker } from "../chat/TraitsPicker";import { isElectron } from "../../env";import { buildHostedChannelSelectionUrl, type HostedAppChannel } from "../../hostedPairing";import { useTheme } from "../../hooks/useTheme";import { usePrimarySettings, useUpdatePrimarySettings } from "../../hooks/useSettings";import { useThreadActions } from "../../hooks/useThreadActions";import { useDesktopUpdateState } from "../../state/desktopUpdate";import {  getCustomModelOptionsByInstance,  resolveAppModelSelectionState,} from "../../modelSelection";import {  applyProviderInstanceSettings,  deriveProviderInstanceEntries,  sortProviderInstanceEntries,} from "../../providerInstances";import { ensureLocalApi, readLocalApi } from "../../localApi";import {  primaryServerObservabilityAtom,  primaryServerProvidersAtom,  serverEnvironment,} from "../../state/server";import { usePrimaryEnvironment } from "../../state/environments";import { useProjects } from "../../state/entities";import { useArchivedThreadSnapshots } from "../../lib/archivedThreadsState";import { formatRelativeTime, formatRelativeTimeLabel } from "../../timestampFormat";import { Button } from "../ui/button";import { DraftInput } from "../ui/draft-input";import { Select, SelectItem, SelectPopup, SelectTrigger, SelectValue } from "../ui/select";import { Switch } from "../ui/switch";import { stackedThreadToast, toastManager } from "../ui/toast";import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip";import { AddProviderInstanceDialog } from "./AddProviderInstanceDialog";import {  canOneClickUpdateProviderCandidate,  collectProviderUpdateCandidates,  hasOneClickUpdateProviderCandidate,  isProviderUpdateActive,  type ProviderUpdateCandidate,} from "../ProviderUpdateLaunchNotification.logic";import { ProviderInstanceCard } from "./ProviderInstanceCard";import { DRIVER_OPTIONS, getDriverOption } from "./providerDriverMeta";import {  buildProviderInstanceUpdatePatch,  formatDiagnosticsDescription,} from "./SettingsPanels.logic";import {  SettingResetButton,  SettingsPageContainer,  SettingsRow,  SettingsSection,  useRelativeTimeTick,} from "./settingsLayout";import { ProjectFavicon } from "../ProjectFavicon";import { useAtomCommand } from "../../state/use-atom-command"; const THEME_OPTIONS = [  {    value: "system",    label: "System",  },  {    value: "light",    label: "Light",  },  {    value: "dark",    label: "Dark",  },] as const; const TIMESTAMP_FORMAT_LABELS = {  locale: "System default",  "12-hour": "12-hour",  "24-hour": "24-hour",} as const; const DEFAULT_DRIVER_KIND = ProviderDriverKind.make("codex"); function withoutProviderInstanceKey<V>(  record: Readonly<Record<ProviderInstanceId, V>> | undefined,  key: ProviderInstanceId,): Record<ProviderInstanceId, V> {  const next = { ...record } as Record<ProviderInstanceId, V>;  delete next[key];  return next;} function withoutProviderInstanceFavorites(  favorites: ReadonlyArray<{ readonly provider: ProviderInstanceId; readonly model: string }>,  instanceId: ProviderInstanceId,) {  return favorites.filter((favorite) => favorite.provider !== instanceId);} const PROVIDER_SETTINGS = DRIVER_OPTIONS.map((definition) => ({  provider: definition.value,})); function ProviderLastChecked({ lastCheckedAt }: { lastCheckedAt: string | null }) {  useRelativeTimeTick();  const lastCheckedRelative = lastCheckedAt ? formatRelativeTime(lastCheckedAt) : null;   if (!lastCheckedRelative) {    return null;  }   return (    <span className="text-[11px] text-muted-foreground/60">      {lastCheckedRelative.suffix ? (        <>          Checked <span className="font-mono tabular-nums">{lastCheckedRelative.value}</span>{" "}          {lastCheckedRelative.suffix}        </>      ) : (        <>Checked {lastCheckedRelative.value}</>      )}    </span>  );} function AboutVersionTitle() {  return (    <span className="inline-flex items-center gap-2">      <span>Version</span>      <code className="text-[11px] font-medium text-muted-foreground">{APP_VERSION}</code>    </span>  );} function AboutVersionSection() {  const updateState = useDesktopUpdateState();  const [isChangingUpdateChannel, setIsChangingUpdateChannel] = useState(false);   const hasDesktopBridge = typeof window !== "undefined" && Boolean(window.desktopBridge);  const selectedUpdateChannel = updateState?.channel ?? "latest";  const selectedHostedAppChannel = hasDesktopBridge ? null : HOSTED_APP_CHANNEL;   const handleUpdateChannelChange = useCallback(    (channel: DesktopUpdateChannel) => {      const bridge = window.desktopBridge;      if (        !bridge ||        typeof bridge.setUpdateChannel !== "function" ||        channel === selectedUpdateChannel      ) {        return;      }       setIsChangingUpdateChannel(true);      void bridge        .setUpdateChannel(channel)        .catch((error: unknown) => {          toastManager.add(            stackedThreadToast({              type: "error",              title: "Could not change update track",              description: error instanceof Error ? error.message : "Update track change failed.",            }),          );        })        .finally(() => {          setIsChangingUpdateChannel(false);        });    },    [selectedUpdateChannel],  );   const handleButtonClick = useCallback(() => {    const bridge = window.desktopBridge;    if (!bridge) return;     const action = updateState ? resolveDesktopUpdateButtonAction(updateState) : "none";     if (action === "download") {      void bridge.downloadUpdate().catch((error: unknown) => {        toastManager.add(          stackedThreadToast({            type: "error",            title: "Could not download update",            description: error instanceof Error ? error.message : "Download failed.",          }),        );      });      return;    }     if (action === "install") {      const confirmed = window.confirm(        getDesktopUpdateInstallConfirmationMessage(          updateState ?? { availableVersion: null, downloadedVersion: null },        ),      );      if (!confirmed) return;      void bridge.installUpdate().catch((error: unknown) => {        toastManager.add(          stackedThreadToast({            type: "error",            title: "Could not install update",            description: error instanceof Error ? error.message : "Install failed.",          }),        );      });      return;    }     if (typeof bridge.checkForUpdate !== "function") return;    void bridge      .checkForUpdate()      .then((result) => {        if (!result.checked) {          toastManager.add(            stackedThreadToast({              type: "error",              title: "Could not check for updates",              description:                result.state.message ?? "Automatic updates are not available in this build.",            }),          );        }      })      .catch((error: unknown) => {        toastManager.add(          stackedThreadToast({            type: "error",            title: "Could not check for updates",            description: error instanceof Error ? error.message : "Update check failed.",          }),        );      });  }, [updateState]);   const action = updateState ? resolveDesktopUpdateButtonAction(updateState) : "none";  const buttonTooltip = updateState ? getDesktopUpdateButtonTooltip(updateState) : null;  const buttonDisabled =    action === "none"      ? !canCheckForUpdate(updateState)      : isDesktopUpdateButtonDisabled(updateState);   const actionLabel: Record<string, string> = { download: "Download", install: "Install" };  const statusLabel: Record<string, string> = {    checking: "Checking…",    downloading: "Downloading…",    "up-to-date": "Up to Date",  };  const buttonLabel =    actionLabel[action] ?? statusLabel[updateState?.status ?? ""] ?? "Check for Updates";  const description =    action === "download" || action === "install"      ? "Update available."      : "Current version of the application.";   return (    <>      <SettingsRow        title={<AboutVersionTitle />}        description={description}        control={          <Tooltip>            <TooltipTrigger              render={                <Button                  size="xs"                  variant={action === "install" ? "default" : "outline"}                  disabled={buttonDisabled}                  onClick={handleButtonClick}                >                  {buttonLabel}                </Button>              }            />            {buttonTooltip ? <TooltipPopup>{buttonTooltip}</TooltipPopup> : null}          </Tooltip>        }      />      {hasDesktopBridge ? (        <SettingsRow          title="Update track"          description="Stable follows full releases. Nightly follows the nightly desktop channel and can switch back to stable immediately."          control={            <Select              value={selectedUpdateChannel}              onValueChange={(value) => {                handleUpdateChannelChange(value as DesktopUpdateChannel);              }}            >              <SelectTrigger                className="w-full sm:w-40"                aria-label="Update track"                disabled={isChangingUpdateChannel}              >                <SelectValue>                  {selectedUpdateChannel === "nightly" ? "Nightly" : "Stable"}                </SelectValue>              </SelectTrigger>              <SelectPopup align="end" alignItemWithTrigger={false}>                <SelectItem hideIndicator value="latest">                  Stable                </SelectItem>                <SelectItem hideIndicator value="nightly">                  Nightly                </SelectItem>              </SelectPopup>            </Select>          }        />      ) : selectedHostedAppChannel ? (        <SettingsRow          title="Update track"          description="Switches the hosted app release channel."          control={            <Select              value={selectedHostedAppChannel}              onValueChange={(value) => {                if (value === selectedHostedAppChannel) return;                window.location.assign(                  buildHostedChannelSelectionUrl({ channel: value as HostedAppChannel }),                );              }}            >              <SelectTrigger className="w-full sm:w-40" aria-label="Update track">                <SelectValue>{HOSTED_APP_CHANNEL_LABEL}</SelectValue>              </SelectTrigger>              <SelectPopup align="end" alignItemWithTrigger={false}>                <SelectItem hideIndicator value="latest">                  Latest                </SelectItem>                <SelectItem hideIndicator value="nightly">                  Nightly                </SelectItem>              </SelectPopup>            </Select>          }        />      ) : null}    </>  );} export function useSettingsRestore(onRestored?: () => void) {  const { theme, setTheme } = useTheme();  const settings = usePrimarySettings();  const updateSettings = useUpdatePrimarySettings();   const isGitWritingModelDirty = !Equal.equals(    settings.textGenerationModelSelection ?? null,    DEFAULT_UNIFIED_SETTINGS.textGenerationModelSelection ?? null,  );   const changedSettingLabels = useMemo(    () => [      ...(theme !== "system" ? ["Theme"] : []),      ...(settings.timestampFormat !== DEFAULT_UNIFIED_SETTINGS.timestampFormat        ? ["Time format"]        : []),      ...(settings.sidebarThreadPreviewCount !== DEFAULT_UNIFIED_SETTINGS.sidebarThreadPreviewCount        ? ["Visible threads"]        : []),      ...(settings.diffWordWrap !== DEFAULT_UNIFIED_SETTINGS.diffWordWrap        ? ["Diff line wrapping"]        : []),      ...(settings.diffIgnoreWhitespace !== DEFAULT_UNIFIED_SETTINGS.diffIgnoreWhitespace        ? ["Diff whitespace changes"]        : []),      ...(settings.autoOpenPlanSidebar !== DEFAULT_UNIFIED_SETTINGS.autoOpenPlanSidebar        ? ["Auto-open task panel"]        : []),      ...(settings.enableAssistantStreaming !== DEFAULT_UNIFIED_SETTINGS.enableAssistantStreaming        ? ["Assistant output"]        : []),      ...(Duration.toMillis(settings.automaticGitFetchInterval) !==      Duration.toMillis(DEFAULT_UNIFIED_SETTINGS.automaticGitFetchInterval)        ? ["Automatic Git fetch interval"]        : []),      ...(settings.defaultThreadEnvMode !== DEFAULT_UNIFIED_SETTINGS.defaultThreadEnvMode        ? ["New thread mode"]        : []),      ...(settings.newWorktreesStartFromOrigin !==      DEFAULT_UNIFIED_SETTINGS.newWorktreesStartFromOrigin        ? ["New worktrees start from origin"]        : []),      ...(settings.addProjectBaseDirectory !== DEFAULT_UNIFIED_SETTINGS.addProjectBaseDirectory        ? ["Add project base directory"]        : []),      ...(settings.confirmThreadArchive !== DEFAULT_UNIFIED_SETTINGS.confirmThreadArchive        ? ["Archive confirmation"]        : []),      ...(settings.confirmThreadDelete !== DEFAULT_UNIFIED_SETTINGS.confirmThreadDelete        ? ["Delete confirmation"]        : []),      ...(isGitWritingModelDirty ? ["Git writing model"] : []),    ],    [      isGitWritingModelDirty,      settings.autoOpenPlanSidebar,      settings.confirmThreadArchive,      settings.confirmThreadDelete,      settings.addProjectBaseDirectory,      settings.defaultThreadEnvMode,      settings.newWorktreesStartFromOrigin,      settings.diffIgnoreWhitespace,      settings.diffWordWrap,      settings.automaticGitFetchInterval,      settings.enableAssistantStreaming,      settings.sidebarThreadPreviewCount,      settings.timestampFormat,      theme,    ],  );   const restoreDefaults = useCallback(async () => {    if (changedSettingLabels.length === 0) return;    const api = readLocalApi();    const confirmed = await (api ?? ensureLocalApi()).dialogs.confirm(      ["Restore default settings?", `This will reset: ${changedSettingLabels.join(", ")}.`].join(        "\n",      ),    );    if (!confirmed) return;     setTheme("system");    updateSettings({      timestampFormat: DEFAULT_UNIFIED_SETTINGS.timestampFormat,      diffWordWrap: DEFAULT_UNIFIED_SETTINGS.diffWordWrap,      diffIgnoreWhitespace: DEFAULT_UNIFIED_SETTINGS.diffIgnoreWhitespace,      sidebarThreadPreviewCount: DEFAULT_UNIFIED_SETTINGS.sidebarThreadPreviewCount,      autoOpenPlanSidebar: DEFAULT_UNIFIED_SETTINGS.autoOpenPlanSidebar,      enableAssistantStreaming: DEFAULT_UNIFIED_SETTINGS.enableAssistantStreaming,      automaticGitFetchInterval: DEFAULT_UNIFIED_SETTINGS.automaticGitFetchInterval,      defaultThreadEnvMode: DEFAULT_UNIFIED_SETTINGS.defaultThreadEnvMode,      newWorktreesStartFromOrigin: DEFAULT_UNIFIED_SETTINGS.newWorktreesStartFromOrigin,      addProjectBaseDirectory: DEFAULT_UNIFIED_SETTINGS.addProjectBaseDirectory,      confirmThreadArchive: DEFAULT_UNIFIED_SETTINGS.confirmThreadArchive,      confirmThreadDelete: DEFAULT_UNIFIED_SETTINGS.confirmThreadDelete,      textGenerationModelSelection: DEFAULT_UNIFIED_SETTINGS.textGenerationModelSelection,    });    onRestored?.();  }, [changedSettingLabels, onRestored, setTheme, updateSettings]);   return {    changedSettingLabels,    restoreDefaults,  };} export function GeneralSettingsPanel() {  const { theme, setTheme } = useTheme();  const settings = usePrimarySettings();  const updateSettings = useUpdatePrimarySettings();  const observability = useAtomValue(primaryServerObservabilityAtom);  const serverProviders = useAtomValue(primaryServerProvidersAtom);  const diagnosticsDescription = formatDiagnosticsDescription({    localTracingEnabled: observability?.localTracingEnabled ?? false,    otlpTracesEnabled: observability?.otlpTracesEnabled ?? false,    otlpTracesUrl: observability?.otlpTracesUrl,    otlpMetricsEnabled: observability?.otlpMetricsEnabled ?? false,    otlpMetricsUrl: observability?.otlpMetricsUrl,  });   const textGenerationModelSelection = resolveAppModelSelectionState(settings, serverProviders);  const textGenInstanceId = textGenerationModelSelection.instanceId;  const textGenModel = textGenerationModelSelection.model;  const textGenModelOptions = textGenerationModelSelection.options;  const gitModelInstanceEntries = sortProviderInstanceEntries(    applyProviderInstanceSettings(deriveProviderInstanceEntries(serverProviders), settings),  );

Lines 1–500 of 1641.