"use client"; import { IconBrandGoogleFilled } from "@tabler/icons-react"; import { useAtom } from "jotai"; import { BrainCog, Eye, EyeOff, Rocket, RotateCcw, Zap } from "lucide-react"; import Image from "next/image"; import { useRouter } from "next/navigation"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { toast } from "sonner"; import { loginMutationAtom } from "@/atoms/auth/auth-mutation.atoms"; import { DEFAULT_SHORTCUTS, keyEventToAccelerator } from "@/components/desktop/shortcut-recorder"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Separator } from "@/components/ui/separator"; import { ShortcutKbd } from "@/components/ui/shortcut-kbd"; import { Spinner } from "@/components/ui/spinner"; import { useElectronAPI } from "@/hooks/use-platform"; import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service"; import { setBearerToken } from "@/lib/auth-utils"; import { AUTH_TYPE, BACKEND_URL } from "@/lib/env-config"; const isGoogleAuth = AUTH_TYPE === "GOOGLE"; type ShortcutKey = "generalAssist" | "quickAsk" | "autocomplete"; type ShortcutMap = typeof DEFAULT_SHORTCUTS; const HOTKEY_ROWS: Array<{ key: ShortcutKey; label: string; description: string; icon: React.ElementType; }> = [ { key: "generalAssist", label: "General Assist", description: "Launch SurfSense instantly from any application", icon: Rocket, }, { key: "quickAsk", label: "Quick Assist", description: "Select text anywhere, then ask AI to explain, rewrite, or act on it", icon: Zap, }, { key: "autocomplete", label: "Extreme Assist", description: "AI drafts text using your screen context and knowledge base", icon: BrainCog, }, ]; function acceleratorToKeys(accel: string, isMac: boolean): string[] { if (!accel) return []; return accel.split("+").map((part) => { if (part === "CommandOrControl") { return isMac ? "⌘" : "Ctrl"; } if (part === "Alt") { return isMac ? "⌥" : "Alt"; } if (part === "Shift") { return isMac ? "⇧" : "Shift"; } if (part === "Space") return "Space"; return part.length === 1 ? part.toUpperCase() : part; }); } function HotkeyRow({ label, description, value, defaultValue, icon: Icon, isMac, onChange, onReset, }: { label: string; description: string; value: string; defaultValue: string; icon: React.ElementType; isMac: boolean; onChange: (accelerator: string) => void; onReset: () => void; }) { const [recording, setRecording] = useState(false); const inputRef = useRef(null); const isDefault = value === defaultValue; const displayKeys = useMemo(() => acceleratorToKeys(value, isMac), [value, isMac]); const handleKeyDown = useCallback( (e: React.KeyboardEvent) => { if (!recording) return; e.preventDefault(); e.stopPropagation(); if (e.key === "Escape") { setRecording(false); return; } const accel = keyEventToAccelerator(e); if (accel) { onChange(accel); setRecording(false); } }, [onChange, recording] ); return (

{label}

{description}

{!isDefault && ( )}
); } export default function DesktopLoginPage() { const router = useRouter(); const api = useElectronAPI(); const [{ mutateAsync: login, isPending: isLoggingIn }] = useAtom(loginMutationAtom); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [showPassword, setShowPassword] = useState(false); const [loginError, setLoginError] = useState(null); const [shortcuts, setShortcuts] = useState(DEFAULT_SHORTCUTS); const [shortcutsLoaded, setShortcutsLoaded] = useState(false); const isMac = api?.versions?.platform === "darwin"; useEffect(() => { if (!api?.getShortcuts) { setShortcutsLoaded(true); return; } api .getShortcuts() .then((config: ShortcutMap | null) => { if (config) setShortcuts(config); setShortcutsLoaded(true); }) .catch(() => setShortcutsLoaded(true)); }, [api]); const updateShortcut = useCallback( (key: "generalAssist" | "quickAsk" | "autocomplete", accelerator: string) => { setShortcuts((prev) => { const updated = { ...prev, [key]: accelerator }; api?.setShortcuts?.({ [key]: accelerator }).catch(() => { toast.error("Failed to update shortcut"); }); return updated; }); toast.success("Shortcut updated"); }, [api] ); const resetShortcut = useCallback( (key: "generalAssist" | "quickAsk" | "autocomplete") => { updateShortcut(key, DEFAULT_SHORTCUTS[key]); }, [updateShortcut] ); const handleGoogleLogin = () => { window.location.href = `${BACKEND_URL}/auth/google/authorize-redirect`; }; const autoSetSearchSpace = async () => { try { const stored = await api?.getActiveSearchSpace?.(); if (stored) return; const spaces = await searchSpacesApiService.getSearchSpaces(); if (spaces?.length) { await api?.setActiveSearchSpace?.(String(spaces[0].id)); } } catch { // non-critical — dashboard-sync will catch it later } }; const handleLocalLogin = async (e: React.FormEvent) => { e.preventDefault(); setLoginError(null); try { const data = await login({ username: email, password, grant_type: "password", }); if (typeof window !== "undefined") { sessionStorage.setItem("login_success_tracked", "true"); } setBearerToken(data.access_token); await autoSetSearchSpace(); setTimeout(() => { router.push(`/auth/callback?token=${data.access_token}`); }, 300); } catch (err) { if (err instanceof Error) { setLoginError(err.message); } else { setLoginError("Login failed. Please check your credentials."); } } }; return (
{/* Header */}
SurfSense

Welcome to SurfSense Desktop

Configure shortcuts, then sign in to get started

{/* Scrollable content */}
{/* ---- Shortcuts ---- */} {shortcutsLoaded ? (
{/*

Hotkeys

*/}
{HOTKEY_ROWS.map((row) => ( updateShortcut(row.key, accel)} onReset={() => resetShortcut(row.key)} /> ))}
) : (
)} {/* ---- Auth ---- */}
{/*

Sign In

*/} {isGoogleAuth ? ( ) : (
{loginError && (
{loginError}
)}
setEmail(e.target.value)} disabled={isLoggingIn} autoFocus className="h-9" />
setPassword(e.target.value)} disabled={isLoggingIn} className="h-9 pr-9" />
)}
); }