import { useEffect, useState } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import type { PatchInstanceGeneralSettings } from "@paperclipai/shared"; import { LogOut, SlidersHorizontal } from "lucide-react"; import { authApi } from "@/api/auth"; import { instanceSettingsApi } from "@/api/instanceSettings"; import { Button } from "../components/ui/button"; import { useBreadcrumbs } from "../context/BreadcrumbContext"; import { queryKeys } from "../lib/queryKeys"; import { ToggleSwitch } from "@/components/ui/toggle-switch"; import { cn } from "../lib/utils"; const FEEDBACK_TERMS_URL = import.meta.env.VITE_FEEDBACK_TERMS_URL?.trim() || "https://paperclip.ing/tos"; export function InstanceGeneralSettings() { const { setBreadcrumbs } = useBreadcrumbs(); const queryClient = useQueryClient(); const [actionError, setActionError] = useState(null); const signOutMutation = useMutation({ mutationFn: () => authApi.signOut(), onSuccess: () => { queryClient.invalidateQueries({ queryKey: queryKeys.auth.session }); }, onError: (error) => { setActionError(error instanceof Error ? error.message : "Failed to sign out."); }, }); useEffect(() => { setBreadcrumbs([ { label: "Instance Settings" }, { label: "General" }, ]); }, [setBreadcrumbs]); const generalQuery = useQuery({ queryKey: queryKeys.instance.generalSettings, queryFn: () => instanceSettingsApi.getGeneral(), }); const updateGeneralMutation = useMutation({ mutationFn: instanceSettingsApi.updateGeneral, onSuccess: async () => { setActionError(null); await queryClient.invalidateQueries({ queryKey: queryKeys.instance.generalSettings }); }, onError: (error) => { setActionError(error instanceof Error ? error.message : "Failed to update general settings."); }, }); if (generalQuery.isLoading) { return
Loading general settings...
; } if (generalQuery.error) { return (
{generalQuery.error instanceof Error ? generalQuery.error.message : "Failed to load general settings."}
); } const censorUsernameInLogs = generalQuery.data?.censorUsernameInLogs === true; const keyboardShortcuts = generalQuery.data?.keyboardShortcuts === true; const feedbackDataSharingPreference = generalQuery.data?.feedbackDataSharingPreference ?? "prompt"; return (

General

Configure instance-wide defaults that affect how operator-visible logs are displayed.

{actionError && (
{actionError}
)}

Censor username in logs

Hide the username segment in home-directory paths and similar operator-visible log output. Standalone username mentions outside of paths are not yet masked in the live transcript view. This is off by default.

updateGeneralMutation.mutate({ censorUsernameInLogs: !censorUsernameInLogs })} disabled={updateGeneralMutation.isPending} aria-label="Toggle username log censoring" />

Keyboard shortcuts

Enable app keyboard shortcuts, including inbox navigation and global shortcuts like creating issues or toggling panels. This is off by default.

updateGeneralMutation.mutate({ keyboardShortcuts: !keyboardShortcuts })} disabled={updateGeneralMutation.isPending} aria-label="Toggle keyboard shortcuts" />

AI feedback sharing

Control whether thumbs up and thumbs down votes can send the voted AI output to Paperclip Labs. Votes are always saved locally.

{FEEDBACK_TERMS_URL ? ( Read our terms of service ) : null}
{feedbackDataSharingPreference === "prompt" ? (
No default is saved yet. The next thumbs up or thumbs down choice will ask once and then save the answer here.
) : null}
{[ { value: "allowed", label: "Always allow", description: "Share voted AI outputs automatically.", }, { value: "not_allowed", label: "Don't allow", description: "Keep voted AI outputs local only.", }, ].map((option) => { const active = feedbackDataSharingPreference === option.value; return ( ); })}

To retest the first-use prompt in local dev, remove the{" "} feedbackDataSharingPreference key from the{" "} instance_settings.general JSON row for this instance, or set it back to{" "} "prompt". Unset and "prompt" both mean no default has been chosen yet.

Sign out

Sign out of this Paperclip instance. You will be redirected to the login page.

); }