-
{account.email || account.id}
+
+ {account.email || account.id}
+
{account.isDefault && (
diff --git a/ui/src/components/layout.tsx b/ui/src/components/layout.tsx
index 2872d377..e7f841d7 100644
--- a/ui/src/components/layout.tsx
+++ b/ui/src/components/layout.tsx
@@ -3,6 +3,7 @@ import { Outlet } from 'react-router-dom';
import { SidebarProvider } from '@/components/ui/sidebar';
import { AppSidebar } from '@/components/app-sidebar';
import { ThemeToggle } from '@/components/theme-toggle';
+import { PrivacyToggle } from '@/components/privacy-toggle';
import { GitHubLink } from '@/components/github-link';
import { DocsLink } from '@/components/docs-link';
import { ConnectionIndicator } from '@/components/connection-indicator';
@@ -29,6 +30,7 @@ export function Layout() {
+
diff --git a/ui/src/components/privacy-toggle.tsx b/ui/src/components/privacy-toggle.tsx
new file mode 100644
index 00000000..eb70362d
--- /dev/null
+++ b/ui/src/components/privacy-toggle.tsx
@@ -0,0 +1,39 @@
+/**
+ * Privacy Toggle Button
+ * Toggles demo mode to blur personal information (emails, account IDs)
+ */
+
+import { Button } from '@/components/ui/button';
+import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
+import { usePrivacy } from '@/contexts/privacy-context';
+import { Eye, EyeOff } from 'lucide-react';
+import { cn } from '@/lib/utils';
+
+export function PrivacyToggle() {
+ const { privacyMode, togglePrivacyMode } = usePrivacy();
+
+ return (
+
+
+
+
+
+
+ {privacyMode
+ ? 'Privacy mode ON - Click to show data'
+ : 'Privacy mode OFF - Click to hide data'}
+
+
+
+ );
+}
diff --git a/ui/src/components/quick-setup-wizard.tsx b/ui/src/components/quick-setup-wizard.tsx
index dfc3d6b4..1fc36d70 100644
--- a/ui/src/components/quick-setup-wizard.tsx
+++ b/ui/src/components/quick-setup-wizard.tsx
@@ -39,6 +39,8 @@ import {
import { useCliproxyAuth, useCreateVariant, useStartAuth } from '@/hooks/use-cliproxy';
import type { AuthStatus, OAuthAccount } from '@/lib/api-client';
import { MODEL_CATALOGS } from '@/lib/model-catalogs';
+import { cn } from '@/lib/utils';
+import { usePrivacy, PRIVACY_BLUR_CLASS } from '@/contexts/privacy-context';
interface QuickSetupWizardProps {
open: boolean;
@@ -68,6 +70,7 @@ export function QuickSetupWizard({ open, onClose }: QuickSetupWizardProps) {
const { data: authData, refetch } = useCliproxyAuth();
const createMutation = useCreateVariant();
const startAuthMutation = useStartAuth();
+ const { privacyMode } = usePrivacy();
// Get auth status for selected provider
const providerAuth = authData?.authStatus.find(
@@ -347,7 +350,9 @@ export function QuickSetupWizard({ open, onClose }: QuickSetupWizardProps) {
-
{acc.email || acc.id}
+
+ {acc.email || acc.id}
+
{acc.isDefault && (
Default account
)}
@@ -403,7 +408,12 @@ export function QuickSetupWizard({ open, onClose }: QuickSetupWizardProps) {
{selectedAccount && (
- Using: {selectedAccount.email || selectedAccount.id}
+
+ Using:{' '}
+
+ {selectedAccount.email || selectedAccount.id}
+
+
)}
diff --git a/ui/src/contexts/privacy-context.tsx b/ui/src/contexts/privacy-context.tsx
new file mode 100644
index 00000000..98cc98fb
--- /dev/null
+++ b/ui/src/contexts/privacy-context.tsx
@@ -0,0 +1,54 @@
+/**
+ * Privacy Context - Global demo mode for censoring personal information
+ * Provides blur effect on emails, account IDs, and other PII
+ * Persists state to localStorage
+ */
+
+/* eslint-disable react-refresh/only-export-components */
+import { createContext, useContext, useState, useEffect, type ReactNode } from 'react';
+
+interface PrivacyContextValue {
+ /** Whether privacy/demo mode is enabled */
+ privacyMode: boolean;
+ /** Toggle privacy mode on/off */
+ togglePrivacyMode: () => void;
+}
+
+const PrivacyContext = createContext
(null);
+
+const STORAGE_KEY = 'ccs-privacy-mode';
+
+export function PrivacyProvider({ children }: { children: ReactNode }) {
+ const [privacyMode, setPrivacyMode] = useState(() => {
+ // Initialize from localStorage
+ if (typeof window !== 'undefined') {
+ return localStorage.getItem(STORAGE_KEY) === 'true';
+ }
+ return false;
+ });
+
+ // Persist to localStorage when changed
+ useEffect(() => {
+ localStorage.setItem(STORAGE_KEY, String(privacyMode));
+ }, [privacyMode]);
+
+ const togglePrivacyMode = () => setPrivacyMode((prev) => !prev);
+
+ return (
+
+ {children}
+
+ );
+}
+
+export function usePrivacy() {
+ const context = useContext(PrivacyContext);
+ if (!context) {
+ throw new Error('usePrivacy must be used within a PrivacyProvider');
+ }
+ return context;
+}
+
+/** CSS class for blurring sensitive content */
+export const PRIVACY_BLUR_CLASS =
+ 'blur-[4px] select-none hover:blur-none transition-all duration-200';