diff --git a/apps/desktop/src/lib/trpc/routers/auto-update/index.ts b/apps/desktop/src/lib/trpc/routers/auto-update/index.ts index 8e15e0787..3515aa840 100644 --- a/apps/desktop/src/lib/trpc/routers/auto-update/index.ts +++ b/apps/desktop/src/lib/trpc/routers/auto-update/index.ts @@ -1,8 +1,10 @@ import { observable } from "@trpc/server/observable"; +import { app } from "electron"; import { type AutoUpdateStatusEvent, autoUpdateEmitter, checkForUpdates, + checkForUpdatesInteractive, dismissUpdate, getUpdateStatus, installUpdate, @@ -34,10 +36,18 @@ export const createAutoUpdateRouter = () => { return getUpdateStatus(); }), + getVersion: publicProcedure.query(() => { + return app.getVersion(); + }), + check: publicProcedure.mutation(() => { checkForUpdates(); }), + checkForUpdates: publicProcedure.mutation(() => { + checkForUpdatesInteractive(); + }), + install: publicProcedure.mutation(() => { installUpdate(); }), diff --git a/apps/desktop/src/renderer/screens/main/components/SettingsView/AccountSettings/AccountSettings.tsx b/apps/desktop/src/renderer/screens/main/components/SettingsView/AccountSettings/AccountSettings.tsx index 9f16c773f..d3db81331 100644 --- a/apps/desktop/src/renderer/screens/main/components/SettingsView/AccountSettings/AccountSettings.tsx +++ b/apps/desktop/src/renderer/screens/main/components/SettingsView/AccountSettings/AccountSettings.tsx @@ -2,15 +2,42 @@ import { Avatar, AvatarFallback, AvatarImage } from "@superset/ui/avatar"; import { Button } from "@superset/ui/button"; import { Skeleton } from "@superset/ui/skeleton"; import { toast } from "@superset/ui/sonner"; +import { HiOutlineClipboardDocument } from "react-icons/hi2"; import { trpc } from "renderer/lib/trpc"; +import { AUTO_UPDATE_STATUS } from "shared/auto-update"; +import { + isItemVisible, + SETTING_ITEM_ID, + type SettingItemId, +} from "../settings-search"; -export function AccountSettings() { +interface AccountSettingsProps { + visibleItems?: SettingItemId[] | null; +} + +export function AccountSettings({ visibleItems }: AccountSettingsProps) { + const showProfile = isItemVisible( + SETTING_ITEM_ID.ACCOUNT_PROFILE, + visibleItems, + ); + const showVersion = isItemVisible( + SETTING_ITEM_ID.ACCOUNT_VERSION, + visibleItems, + ); + const showSignOut = isItemVisible( + SETTING_ITEM_ID.ACCOUNT_SIGNOUT, + visibleItems, + ); const { data: user, isLoading } = trpc.user.me.useQuery(); + const { data: version } = trpc.autoUpdate.getVersion.useQuery(); + const { data: updateStatus } = trpc.autoUpdate.getStatus.useQuery(); + const checkForUpdatesMutation = trpc.autoUpdate.checkForUpdates.useMutation(); const signOutMutation = trpc.auth.signOut.useMutation({ onSuccess: () => toast.success("Signed out"), }); const signOut = () => signOutMutation.mutate(); + const isChecking = updateStatus?.status === AUTO_UPDATE_STATUS.CHECKING; const initials = user?.name ?.split(" ") @@ -20,7 +47,7 @@ export function AccountSettings() { .slice(0, 2); return ( -
+

Account

@@ -30,46 +57,84 @@ export function AccountSettings() {

{/* Profile Section */} -
-

Profile

-
- {isLoading ? ( - <> - -
- - -
- - ) : user ? ( - <> - - - - {initials || "?"} - - -
-

{user.name}

-

{user.email}

-
- - ) : ( -

Unable to load user info

- )} + {showProfile && ( +
+

Profile

+
+ {isLoading ? ( + <> + +
+ + +
+ + ) : user ? ( + <> + + + + {initials || "?"} + + +
+

{user.name}

+

+ {user.email} +

+
+ + ) : ( +

+ Unable to load user info +

+ )} +
+
+ )} + + {/* Version */} + {showVersion && ( +
+
+
+

Version

+ +
+
+ +

+ {isChecking ? "Checking..." : "Up to date"} +

+
+
-
+ )} - {/* Sign Out Section */} -
-

Sign Out

-

- Sign out of your Superset account on this device. -

- -
+ {/* Sign Out */} + {showSignOut && ( +
+ +
+ )}
); diff --git a/apps/desktop/src/renderer/screens/main/components/SettingsView/AppearanceSettings.tsx b/apps/desktop/src/renderer/screens/main/components/SettingsView/AppearanceSettings.tsx index ce8e19aff..d289582a9 100644 --- a/apps/desktop/src/renderer/screens/main/components/SettingsView/AppearanceSettings.tsx +++ b/apps/desktop/src/renderer/screens/main/components/SettingsView/AppearanceSettings.tsx @@ -14,9 +14,30 @@ import { useThemeStore, } from "renderer/stores"; import { builtInThemes } from "shared/themes"; +import { + isItemVisible, + SETTING_ITEM_ID, + type SettingItemId, +} from "./settings-search"; import { ThemeCard } from "./ThemeCard"; -export function AppearanceSettings() { +interface AppearanceSettingsProps { + visibleItems?: SettingItemId[] | null; +} + +export function AppearanceSettings({ visibleItems }: AppearanceSettingsProps) { + const showTheme = isItemVisible( + SETTING_ITEM_ID.APPEARANCE_THEME, + visibleItems, + ); + const showMarkdown = isItemVisible( + SETTING_ITEM_ID.APPEARANCE_MARKDOWN, + visibleItems, + ); + const showCustomThemes = isItemVisible( + SETTING_ITEM_ID.APPEARANCE_CUSTOM_THEMES, + visibleItems, + ); const activeThemeId = useThemeId(); const setTheme = useSetTheme(); const customThemes = useThemeStore((state) => state.customThemes); @@ -36,50 +57,58 @@ export function AppearanceSettings() {
{/* Theme Section */} -
-

Theme

-
- {allThemes.map((theme) => ( - setTheme(theme.id)} - /> - ))} + {showTheme && ( +
+

Theme

+
+ {allThemes.map((theme) => ( + setTheme(theme.id)} + /> + ))} +
-
+ )} -
-

Markdown Style

-

- Rendering style for markdown files when viewing rendered content -

- -

- Tufte style uses elegant serif typography inspired by Edward Tufte's - books -

-
+ {showMarkdown && ( +
+

Markdown Style

+

+ Rendering style for markdown files when viewing rendered content +

+ +

+ Tufte style uses elegant serif typography inspired by Edward + Tufte's books +

+
+ )} -
-

Custom Themes

-

- Custom theme import coming soon. You'll be able to import JSON theme - files to create your own themes. -

-
+ {showCustomThemes && ( +
+

Custom Themes

+

+ Custom theme import coming soon. You'll be able to import JSON + theme files to create your own themes. +

+
+ )}
); diff --git a/apps/desktop/src/renderer/screens/main/components/SettingsView/BehaviorSettings.tsx b/apps/desktop/src/renderer/screens/main/components/SettingsView/BehaviorSettings.tsx index f21aff0a3..54e711a3d 100644 --- a/apps/desktop/src/renderer/screens/main/components/SettingsView/BehaviorSettings.tsx +++ b/apps/desktop/src/renderer/screens/main/components/SettingsView/BehaviorSettings.tsx @@ -1,8 +1,21 @@ import { Label } from "@superset/ui/label"; import { Switch } from "@superset/ui/switch"; import { trpc } from "renderer/lib/trpc"; +import { + isItemVisible, + SETTING_ITEM_ID, + type SettingItemId, +} from "./settings-search"; -export function BehaviorSettings() { +interface BehaviorSettingsProps { + visibleItems?: SettingItemId[] | null; +} + +export function BehaviorSettings({ visibleItems }: BehaviorSettingsProps) { + const showConfirmQuit = isItemVisible( + SETTING_ITEM_ID.BEHAVIOR_CONFIRM_QUIT, + visibleItems, + ); const utils = trpc.useUtils(); const { data: confirmOnQuit, isLoading } = trpc.settings.getConfirmOnQuit.useQuery(); @@ -42,22 +55,24 @@ export function BehaviorSettings() {
-
-
- -

- Show a confirmation dialog when quitting the app -

+ {showConfirmQuit && ( +
+
+ +

+ Show a confirmation dialog when quitting the app +

+
+
- -
+ )}
); diff --git a/apps/desktop/src/renderer/screens/main/components/SettingsView/RingtonesSettings/RingtonesSettings.tsx b/apps/desktop/src/renderer/screens/main/components/SettingsView/RingtonesSettings/RingtonesSettings.tsx index 85d4d7fee..bd44a8d4c 100644 --- a/apps/desktop/src/renderer/screens/main/components/SettingsView/RingtonesSettings/RingtonesSettings.tsx +++ b/apps/desktop/src/renderer/screens/main/components/SettingsView/RingtonesSettings/RingtonesSettings.tsx @@ -8,6 +8,11 @@ import { useSelectedRingtoneId, useSetRingtone, } from "renderer/stores"; +import { + isItemVisible, + SETTING_ITEM_ID, + type SettingItemId, +} from "../settings-search"; function formatDuration(seconds: number): string { return `${seconds}s`; @@ -149,7 +154,15 @@ function RingtoneCard({ ); } -export function RingtonesSettings() { +interface RingtonesSettingsProps { + visibleItems?: SettingItemId[] | null; +} + +export function RingtonesSettings({ visibleItems }: RingtonesSettingsProps) { + const showNotification = isItemVisible( + SETTING_ITEM_ID.RINGTONES_NOTIFICATION, + visibleItems, + ); const selectedRingtoneId = useSelectedRingtoneId(); const setRingtone = useSetRingtone(); const [playingId, setPlayingId] = useState(null); @@ -238,29 +251,33 @@ export function RingtonesSettings() {
{/* Ringtone Section */} -
-

Notification Sound

-
- {AVAILABLE_RINGTONES.map((ringtone) => ( - handleSelect(ringtone.id)} - onTogglePlay={() => handleTogglePlay(ringtone)} - /> - ))} -
-
+ {showNotification && ( + <> +
+

Notification Sound

+
+ {AVAILABLE_RINGTONES.map((ringtone) => ( + handleSelect(ringtone.id)} + onTogglePlay={() => handleTogglePlay(ringtone)} + /> + ))} +
+
- {/* Tip */} -
-

- Click the play button to preview a sound. Click stop or play another - to stop the current sound. -

-
+ {/* Tip */} +
+

+ Click the play button to preview a sound. Click stop or play + another to stop the current sound. +

+
+ + )}
); diff --git a/apps/desktop/src/renderer/screens/main/components/SettingsView/SettingsContent.tsx b/apps/desktop/src/renderer/screens/main/components/SettingsView/SettingsContent.tsx index 2af699cd2..c41594991 100644 --- a/apps/desktop/src/renderer/screens/main/components/SettingsView/SettingsContent.tsx +++ b/apps/desktop/src/renderer/screens/main/components/SettingsView/SettingsContent.tsx @@ -6,23 +6,41 @@ import { KeyboardShortcutsSettings } from "./KeyboardShortcutsSettings"; import { PresetsSettings } from "./PresetsSettings"; import { ProjectSettings } from "./ProjectSettings"; import { RingtonesSettings } from "./RingtonesSettings"; +import { getMatchingItemsForSection } from "./settings-search"; import { WorkspaceSettings } from "./WorkspaceSettings"; interface SettingsContentProps { activeSection: SettingsSection; + searchQuery?: string; } -export function SettingsContent({ activeSection }: SettingsContentProps) { +export function SettingsContent({ + activeSection, + searchQuery = "", +}: SettingsContentProps) { + const matchingItems = searchQuery + ? getMatchingItemsForSection(searchQuery, activeSection) + : null; + const matchingIds = matchingItems?.map((item) => item.id) ?? null; + return (
- {activeSection === "account" && } + {activeSection === "account" && ( + + )} {activeSection === "project" && } {activeSection === "workspace" && } - {activeSection === "appearance" && } - {activeSection === "ringtones" && } + {activeSection === "appearance" && ( + + )} + {activeSection === "ringtones" && ( + + )} {activeSection === "keyboard" && } {activeSection === "presets" && } - {activeSection === "behavior" && } + {activeSection === "behavior" && ( + + )}
); } diff --git a/apps/desktop/src/renderer/screens/main/components/SettingsView/SettingsSidebar/GeneralSettings.tsx b/apps/desktop/src/renderer/screens/main/components/SettingsView/SettingsSidebar/GeneralSettings.tsx index 7803fa3b1..4371608d7 100644 --- a/apps/desktop/src/renderer/screens/main/components/SettingsView/SettingsSidebar/GeneralSettings.tsx +++ b/apps/desktop/src/renderer/screens/main/components/SettingsView/SettingsSidebar/GeneralSettings.tsx @@ -12,8 +12,13 @@ import type { SettingsSection } from "renderer/stores"; interface GeneralSettingsProps { activeSection: SettingsSection; onSectionChange: (section: SettingsSection) => void; + matchCounts?: Record | null; } +/** + * Sidebar section definitions. + * Search keywords are centralized in settings-search.ts - do not duplicate here. + */ const GENERAL_SECTIONS: { id: SettingsSection; label: string; @@ -54,29 +59,45 @@ const GENERAL_SECTIONS: { export function GeneralSettings({ activeSection, onSectionChange, + matchCounts, }: GeneralSettingsProps) { + // When searching, only show sections that have matches + const filteredSections = matchCounts + ? GENERAL_SECTIONS.filter((section) => (matchCounts[section.id] ?? 0) > 0) + : GENERAL_SECTIONS; + + if (filteredSections.length === 0) { + return null; + } + return (

General

); diff --git a/apps/desktop/src/renderer/screens/main/components/SettingsView/SettingsSidebar/ProjectsSettings.tsx b/apps/desktop/src/renderer/screens/main/components/SettingsView/SettingsSidebar/ProjectsSettings.tsx index e2427284a..e95797fe1 100644 --- a/apps/desktop/src/renderer/screens/main/components/SettingsView/SettingsSidebar/ProjectsSettings.tsx +++ b/apps/desktop/src/renderer/screens/main/components/SettingsView/SettingsSidebar/ProjectsSettings.tsx @@ -8,11 +8,13 @@ import type { SettingsSection } from "renderer/stores"; interface ProjectsSettingsProps { activeSection: SettingsSection; onSectionChange: (section: SettingsSection) => void; + searchQuery?: string; } export function ProjectsSettings({ activeSection, onSectionChange, + searchQuery = "", }: ProjectsSettingsProps) { const { data: groups = [] } = trpc.workspaces.getAllGrouped.useQuery(); const { data: activeWorkspace } = trpc.workspaces.getActive.useQuery(); @@ -28,6 +30,27 @@ export function ProjectsSettings({ } }, [groups]); + // Filter groups based on search query + const filteredGroups = groups + .map((group) => { + const projectMatches = group.project.name + .toLowerCase() + .includes(searchQuery.toLowerCase()); + const matchingWorkspaces = group.workspaces.filter((ws) => + ws.name.toLowerCase().includes(searchQuery.toLowerCase()), + ); + + // Include if project name matches or any workspace matches + if (projectMatches || matchingWorkspaces.length > 0) { + return { + ...group, + workspaces: projectMatches ? group.workspaces : matchingWorkspaces, + }; + } + return null; + }) + .filter(Boolean) as typeof groups; + const toggleProject = (projectId: string) => { setExpandedProjects((prev) => { const next = new Set(prev); @@ -51,7 +74,7 @@ export function ProjectsSettings({ onSectionChange("workspace"); }; - if (groups.length === 0) { + if (filteredGroups.length === 0) { return null; } @@ -61,7 +84,7 @@ export function ProjectsSettings({ Projects