From 46056ee514cdd29e21dfac0b69eeaf63ce266b9a Mon Sep 17 00:00:00 2001
From: Anish Sarkar <104695310+AnishSarkar22@users.noreply.github.com>
Date: Thu, 23 Apr 2026 23:52:49 +0530
Subject: [PATCH] fix(settings): update user settings dialog labels and enhance
DesktopShortcutsContent component for better hotkey management
---
.../components/DesktopContent.tsx | 2 +-
.../components/DesktopShortcutsContent.tsx | 194 ++++++++++++++----
.../settings/user-settings-dialog.tsx | 6 +-
3 files changed, 161 insertions(+), 41 deletions(-)
diff --git a/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/DesktopContent.tsx b/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/DesktopContent.tsx
index 3ec14076d..9861f5536 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/DesktopContent.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/DesktopContent.tsx
@@ -72,7 +72,7 @@ export function DesktopContent() {
return (
- Desktop settings are only available in the SurfSense desktop app.
+ App preferences are only available in the SurfSense desktop app.
);
diff --git a/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/DesktopShortcutsContent.tsx b/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/DesktopShortcutsContent.tsx
index 773665e63..f4981b8f0 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/DesktopShortcutsContent.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/DesktopShortcutsContent.tsx
@@ -1,17 +1,152 @@
"use client";
-import { BrainCog, Info, Rocket, Zap } from "lucide-react";
-import { useEffect, useState } from "react";
+import { ArrowBigUp, BrainCog, Command, Option, Rocket, RotateCcw, Zap } from "lucide-react";
+import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { toast } from "sonner";
-import { DEFAULT_SHORTCUTS, ShortcutRecorder } from "@/components/desktop/shortcut-recorder";
-import { Alert, AlertDescription } from "@/components/ui/alert";
+import { DEFAULT_SHORTCUTS, keyEventToAccelerator } from "@/components/desktop/shortcut-recorder";
+import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/spinner";
import { useElectronAPI } from "@/hooks/use-platform";
+type ShortcutKey = "generalAssist" | "quickAsk" | "autocomplete";
+type ShortcutMap = typeof DEFAULT_SHORTCUTS;
+
+const HOTKEY_ROWS: Array<{ key: ShortcutKey; label: string; icon: React.ElementType }> = [
+ { key: "generalAssist", label: "General Assist", icon: Rocket },
+ { key: "quickAsk", label: "Quick Assist", icon: Zap },
+ { key: "autocomplete", label: "Extreme Assist", icon: BrainCog },
+];
+
+type ShortcutToken =
+ | { kind: "text"; value: string }
+ | { kind: "icon"; value: "command" | "option" | "shift" };
+
+function acceleratorToTokens(accel: string, isMac: boolean): ShortcutToken[] {
+ if (!accel) return [];
+ return accel.split("+").map((part) => {
+ if (part === "CommandOrControl") {
+ return isMac ? { kind: "icon", value: "command" as const } : { kind: "text", value: "Ctrl" };
+ }
+ if (part === "Alt") {
+ return isMac ? { kind: "icon", value: "option" as const } : { kind: "text", value: "Alt" };
+ }
+ if (part === "Shift") {
+ return isMac ? { kind: "icon", value: "shift" as const } : { kind: "text", value: "Shift" };
+ }
+ if (part === "Space") return { kind: "text", value: "Space" };
+ return { kind: "text", value: part.length === 1 ? part.toUpperCase() : part };
+ });
+}
+
+function HotkeyRow({
+ label,
+ value,
+ defaultValue,
+ icon: Icon,
+ isMac,
+ onChange,
+ onReset,
+}: {
+ label: 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 displayTokens = useMemo(() => acceleratorToTokens(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 (
+
+
+
+ {!isDefault && (
+
+ )}
+
+
+
+ );
+}
+
export function DesktopShortcutsContent() {
const api = useElectronAPI();
const [shortcuts, setShortcuts] = useState(DEFAULT_SHORTCUTS);
const [shortcutsLoaded, setShortcutsLoaded] = useState(false);
+ const isMac = api?.versions?.platform === "darwin";
useEffect(() => {
if (!api) {
@@ -21,7 +156,7 @@ export function DesktopShortcutsContent() {
let mounted = true;
(api.getShortcuts?.() ?? Promise.resolve(null))
- .then((config) => {
+ .then((config: ShortcutMap | null) => {
if (!mounted) return;
if (config) setShortcuts(config);
setShortcutsLoaded(true);
@@ -58,46 +193,27 @@ export function DesktopShortcutsContent() {
toast.success("Shortcut updated");
};
- const resetShortcut = (key: "generalAssist" | "quickAsk" | "autocomplete") => {
+ const resetShortcut = (key: ShortcutKey) => {
updateShortcut(key, DEFAULT_SHORTCUTS[key]);
};
return (
shortcutsLoaded ? (
-
-
-
- Click a shortcut and press a new key combination to change it.
-
-
-
updateShortcut("generalAssist", accel)}
- onReset={() => resetShortcut("generalAssist")}
- defaultValue={DEFAULT_SHORTCUTS.generalAssist}
- label="General Assist"
- description="Launch SurfSense instantly from any application"
- icon={Rocket}
- />
- updateShortcut("quickAsk", accel)}
- onReset={() => resetShortcut("quickAsk")}
- defaultValue={DEFAULT_SHORTCUTS.quickAsk}
- label="Quick Assist"
- description="Select text anywhere, then ask AI to explain, rewrite, or act on it"
- icon={Zap}
- />
- updateShortcut("autocomplete", accel)}
- onReset={() => resetShortcut("autocomplete")}
- defaultValue={DEFAULT_SHORTCUTS.autocomplete}
- label="Extreme Assist"
- description="AI drafts text using your screen context and knowledge base"
- icon={BrainCog}
- />
+
+ {HOTKEY_ROWS.map((row) => (
+ updateShortcut(row.key, accel)}
+ onReset={() => resetShortcut(row.key)}
+ />
+ ))}
+
) : (
diff --git a/surfsense_web/components/settings/user-settings-dialog.tsx b/surfsense_web/components/settings/user-settings-dialog.tsx
index a406f6352..cc36392ae 100644
--- a/surfsense_web/components/settings/user-settings-dialog.tsx
+++ b/surfsense_web/components/settings/user-settings-dialog.tsx
@@ -101,7 +101,11 @@ export function UserSettingsDialog() {
},
...(isDesktop
? [
- { value: "desktop", label: "Desktop", icon: },
+ {
+ value: "desktop",
+ label: "App Preferences",
+ icon: ,
+ },
{
value: "desktop-shortcuts",
label: "Hotkeys",