diff --git a/apps/desktop/src/components/chat/body/empty.tsx b/apps/desktop/src/components/chat/body/empty.tsx index a5080dd17c..e593fda095 100644 --- a/apps/desktop/src/components/chat/body/empty.tsx +++ b/apps/desktop/src/components/chat/body/empty.tsx @@ -13,7 +13,7 @@ export function ChatBodyEmpty({ const openNew = useTabs((state) => state.openNew); const handleGoToSettings = useCallback(() => { - openNew({ type: "settings", state: { tab: "intelligence" } }); + openNew({ type: "ai", state: { tab: "intelligence" } }); }, [openNew]); const handleOpenChatShortcuts = useCallback(() => { diff --git a/apps/desktop/src/components/main-app-layout.tsx b/apps/desktop/src/components/main-app-layout.tsx index d60d3de864..cc1a8ee13a 100644 --- a/apps/desktop/src/components/main-app-layout.tsx +++ b/apps/desktop/src/components/main-app-layout.tsx @@ -41,19 +41,20 @@ const useNavigationEvents = () => { .navigate(webview) .listen(({ payload }) => { if (payload.path === "/app/settings") { - const tab = (payload.search?.tab as string) ?? "general"; - openNew({ - type: "settings", - state: { - tab: tab as - | "general" - | "calendar" - | "notifications" - | "transcription" - | "intelligence" - | "account", - }, - }); + let tab = (payload.search?.tab as string) ?? "general"; + if (tab === "notifications" || tab === "account") { + tab = "general"; + } + if (tab === "calendar") { + openNew({ type: "calendar" }); + } else if (tab === "transcription" || tab === "intelligence") { + openNew({ + type: "ai", + state: { tab: tab as "transcription" | "intelligence" }, + }); + } else { + openNew({ type: "settings" }); + } } else { navigate({ to: payload.path, search: payload.search ?? undefined }); } diff --git a/apps/desktop/src/components/main/body/ai.tsx b/apps/desktop/src/components/main/body/ai.tsx new file mode 100644 index 0000000000..f46ad61d22 --- /dev/null +++ b/apps/desktop/src/components/main/body/ai.tsx @@ -0,0 +1,94 @@ +import { AudioLinesIcon, SparklesIcon } from "lucide-react"; +import { useCallback } from "react"; + +import { Button } from "@hypr/ui/components/ui/button"; +import { cn } from "@hypr/utils"; + +import { type Tab, useTabs } from "../../../store/zustand/tabs"; +import { LLM } from "../../settings/ai/llm"; +import { STT } from "../../settings/ai/stt"; +import { StandardTabWrapper } from "./index"; +import { type TabItem, TabItemBase } from "./shared"; + +type AITabKey = "transcription" | "intelligence"; + +export const TabItemAI: TabItem> = ({ + tab, + tabIndex, + handleCloseThis, + handleSelectThis, + handleCloseOthers, + handleCloseAll, +}) => { + return ( + } + title={"AI"} + selected={tab.active} + tabIndex={tabIndex} + handleCloseThis={() => handleCloseThis(tab)} + handleSelectThis={() => handleSelectThis(tab)} + handleCloseOthers={handleCloseOthers} + handleCloseAll={handleCloseAll} + /> + ); +}; + +export function TabContentAI({ tab }: { tab: Extract }) { + return ( + + + + ); +} + +function AIView({ tab }: { tab: Extract }) { + const updateAiTabState = useTabs((state) => state.updateAiTabState); + const activeTab = tab.state.tab; + + const setActiveTab = useCallback( + (newTab: AITabKey) => { + updateAiTabState(tab, { tab: newTab }); + }, + [updateAiTabState, tab], + ); + + const headerAction = ( +
+ + +
+ ); + + return ( +
+ {activeTab === "transcription" ? ( + + ) : ( + + )} +
+ ); +} diff --git a/apps/desktop/src/components/main/body/calendar/index.tsx b/apps/desktop/src/components/main/body/calendar/index.tsx new file mode 100644 index 0000000000..67efa70b89 --- /dev/null +++ b/apps/desktop/src/components/main/body/calendar/index.tsx @@ -0,0 +1,38 @@ +import { CalendarDays } from "lucide-react"; + +import { type Tab } from "../../../../store/zustand/tabs"; +import { SettingsCalendar } from "../../../settings/calendar"; +import { StandardTabWrapper } from "../index"; +import { type TabItem, TabItemBase } from "../shared"; + +export const TabItemCalendar: TabItem> = ({ + tab, + tabIndex, + handleCloseThis, + handleSelectThis, + handleCloseOthers, + handleCloseAll, +}) => { + return ( + } + title={"Calendar"} + selected={tab.active} + tabIndex={tabIndex} + handleCloseThis={() => handleCloseThis(tab)} + handleSelectThis={() => handleSelectThis(tab)} + handleCloseOthers={handleCloseOthers} + handleCloseAll={handleCloseAll} + /> + ); +}; + +export function TabContentCalendar() { + return ( + +
+ +
+
+ ); +} diff --git a/apps/desktop/src/components/main/body/index.tsx b/apps/desktop/src/components/main/body/index.tsx index 96e4495a12..2fd930c330 100644 --- a/apps/desktop/src/components/main/body/index.tsx +++ b/apps/desktop/src/components/main/body/index.tsx @@ -22,6 +22,8 @@ import { import { ChatFloatingButton } from "../../chat"; import { TrafficLights } from "../../window/traffic-lights"; import { useNewNote } from "../shared"; +import { TabContentAI, TabItemAI } from "./ai"; +import { TabContentCalendar, TabItemCalendar } from "./calendar"; import { TabContentChatShortcut, TabItemChatShortcut } from "./chat-shortcuts"; import { TabContentContact, TabItemContact } from "./contacts"; import { TabContentEmpty, TabItemEmpty } from "./empty"; @@ -326,6 +328,18 @@ function TabItem({ /> ); } + if (tab.type === "calendar") { + return ( + + ); + } if (tab.type === "extension") { return ( ); } + if (tab.type === "ai") { + return ( + + ); + } return null; } @@ -394,6 +420,9 @@ function ContentWrapper({ tab }: { tab: Tab }) { if (tab.type === "empty") { return ; } + if (tab.type === "calendar") { + return ; + } if (tab.type === "extension") { return ; } @@ -403,6 +432,9 @@ function ContentWrapper({ tab }: { tab: Tab }) { if (tab.type === "settings") { return ; } + if (tab.type === "ai") { + return ; + } return null; } diff --git a/apps/desktop/src/components/main/body/sessions/floating/listen.tsx b/apps/desktop/src/components/main/body/sessions/floating/listen.tsx index 900368bbb5..873bd6e33e 100644 --- a/apps/desktop/src/components/main/body/sessions/floating/listen.tsx +++ b/apps/desktop/src/components/main/body/sessions/floating/listen.tsx @@ -125,7 +125,7 @@ function ListenSplitButton({ const handleAction = useCallback(() => { onPrimaryClick(); - openNew({ type: "settings", state: { tab: "transcription" } }); + openNew({ type: "ai", state: { tab: "transcription" } }); }, [onPrimaryClick, openNew]); return ( diff --git a/apps/desktop/src/components/main/body/sessions/note-input/enhanced/config-error.tsx b/apps/desktop/src/components/main/body/sessions/note-input/enhanced/config-error.tsx index 339fdc5ef3..9d29b043a2 100644 --- a/apps/desktop/src/components/main/body/sessions/note-input/enhanced/config-error.tsx +++ b/apps/desktop/src/components/main/body/sessions/note-input/enhanced/config-error.tsx @@ -9,7 +9,7 @@ export function ConfigError({ status }: { status: LLMConnectionStatus }) { const openNew = useTabs((state) => state.openNew); const handleConfigureClick = () => { - openNew({ type: "settings", state: { tab: "intelligence" } }); + openNew({ type: "ai", state: { tab: "intelligence" } }); }; const message = getMessageForStatus(status); diff --git a/apps/desktop/src/components/main/body/sessions/outer-header/listen.tsx b/apps/desktop/src/components/main/body/sessions/outer-header/listen.tsx index 70e6563f95..7a1a77a5c1 100644 --- a/apps/desktop/src/components/main/body/sessions/outer-header/listen.tsx +++ b/apps/desktop/src/components/main/body/sessions/outer-header/listen.tsx @@ -174,7 +174,7 @@ function StartButton({ sessionId }: { sessionId: string }) { const openNew = useTabs((state) => state.openNew); const handleConfigureAction = useCallback(() => { - openNew({ type: "settings", state: { tab: "transcription" } }); + openNew({ type: "ai", state: { tab: "transcription" } }); }, [openNew]); const button = ( diff --git a/apps/desktop/src/components/main/body/settings.tsx b/apps/desktop/src/components/main/body/settings.tsx new file mode 100644 index 0000000000..4001b7bb2b --- /dev/null +++ b/apps/desktop/src/components/main/body/settings.tsx @@ -0,0 +1,42 @@ +import { SettingsIcon } from "lucide-react"; + +import { type Tab } from "../../../store/zustand/tabs"; +import { SettingsGeneral } from "../../settings/general"; +import { StandardTabWrapper } from "./index"; +import { type TabItem, TabItemBase } from "./shared"; + +export const TabItemSettings: TabItem> = ({ + tab, + tabIndex, + handleCloseThis, + handleSelectThis, + handleCloseOthers, + handleCloseAll, +}) => { + return ( + } + title={"Settings"} + selected={tab.active} + tabIndex={tabIndex} + handleCloseThis={() => handleCloseThis(tab)} + handleSelectThis={() => handleSelectThis(tab)} + handleCloseOthers={handleCloseOthers} + handleCloseAll={handleCloseAll} + /> + ); +}; + +export function TabContentSettings({ + tab: _tab, +}: { + tab: Extract; +}) { + return ( + +
+ +
+
+ ); +} diff --git a/apps/desktop/src/components/main/body/settings/index.tsx b/apps/desktop/src/components/main/body/settings/index.tsx deleted file mode 100644 index 908692a571..0000000000 --- a/apps/desktop/src/components/main/body/settings/index.tsx +++ /dev/null @@ -1,259 +0,0 @@ -import { openUrl } from "@tauri-apps/plugin-opener"; -import { - AudioLines, - Bell, - CalendarDays, - type LucideIcon, - MessageCircleQuestion, - Settings2, - SettingsIcon, - Sparkles, - UserIcon, -} from "lucide-react"; -import { useCallback } from "react"; - -import { Button } from "@hypr/ui/components/ui/button"; -import { cn } from "@hypr/utils"; - -import { type Tab, useTabs } from "../../../../store/zustand/tabs"; -import { SettingsAccount } from "../../../settings/account"; -import { LLM } from "../../../settings/ai/llm"; -import { STT } from "../../../settings/ai/stt"; -import { SettingsCalendar } from "../../../settings/calendar"; -import { SettingsGeneral } from "../../../settings/general"; -import { SettingsNotifications } from "../../../settings/notification"; -import { StandardTabWrapper } from "../index"; -import { type TabItem, TabItemBase } from "../shared"; - -type SettingsTabKey = - | "general" - | "calendar" - | "notifications" - | "transcription" - | "intelligence" - | "account"; - -const TAB_CONFIG: Record< - SettingsTabKey, - { - label: string; - icon: LucideIcon; - group: 1 | 2; - } -> = { - general: { - label: "General", - icon: Settings2, - group: 1, - }, - calendar: { - label: "Calendar", - icon: CalendarDays, - group: 1, - }, - notifications: { - label: "Notifications", - icon: Bell, - group: 1, - }, - transcription: { - label: "Transcription", - icon: AudioLines, - group: 1, - }, - intelligence: { - label: "Intelligence", - icon: Sparkles, - group: 1, - }, - account: { - label: "Account", - icon: UserIcon, - group: 2, - }, -}; - -const TAB_KEYS: SettingsTabKey[] = [ - "general", - "calendar", - "notifications", - "transcription", - "intelligence", - "account", -]; - -export const TabItemSettings: TabItem> = ({ - tab, - tabIndex, - handleCloseThis, - handleSelectThis, - handleCloseOthers, - handleCloseAll, -}) => { - return ( - } - title={"Settings"} - selected={tab.active} - tabIndex={tabIndex} - handleCloseThis={() => handleCloseThis(tab)} - handleSelectThis={() => handleSelectThis(tab)} - handleCloseOthers={handleCloseOthers} - handleCloseAll={handleCloseAll} - /> - ); -}; - -export function TabContentSettings({ - tab, -}: { - tab: Extract; -}) { - return ( - - - - ); -} - -function SettingsView({ tab }: { tab: Extract }) { - const updateSettingsTabState = useTabs( - (state) => state.updateSettingsTabState, - ); - - const activeTab = tab.state.tab; - - const setActiveTab = useCallback( - (newTab: SettingsTabKey) => { - updateSettingsTabState(tab, { tab: newTab }); - }, - [updateSettingsTabState, tab], - ); - - const group1Tabs = TAB_KEYS.filter((t) => TAB_CONFIG[t].group === 1); - const group2Tabs = TAB_KEYS.filter((t) => TAB_CONFIG[t].group === 2); - - return ( -
- - -
-
-
- -
-
-
- ); -} - -function Group({ - tabs, - activeTab, - setActiveTab, - expandHeight = false, -}: { - tabs: (SettingsTabKey | "feedback" | "developers")[]; - activeTab: SettingsTabKey; - setActiveTab?: (tab: SettingsTabKey) => void; - expandHeight?: boolean; -}) { - const handleTabClick = async ( - tab: SettingsTabKey | "feedback" | "developers", - ) => { - if (tab === "feedback") { - await openUrl("https://github.com/fastrepl/hyprnote/discussions"); - } else if (tab === "developers") { - await openUrl("https://cal.com/team/hyprnote/welcome"); - } else if (setActiveTab) { - setActiveTab(tab); - } - }; - - const getTabInfo = (tab: SettingsTabKey | "feedback" | "developers") => { - if (tab === "feedback") { - return { label: "Feedback", icon: MessageCircleQuestion }; - } - if (tab === "developers") { - return { label: "Talk to developers", icon: Settings2 }; - } - return TAB_CONFIG[tab]; - }; - - return ( -
- {tabs.map((tab) => { - const tabInfo = getTabInfo(tab); - const Icon = tabInfo.icon; - const isActive = - tab !== "feedback" && tab !== "developers" && activeTab === tab; - - return ( -
- -
- ); - })} -
- ); -} - -function Header({ activeTab }: { activeTab: SettingsTabKey }) { - return ( -
-

- {TAB_CONFIG[activeTab].label} -

-
- ); -} - -function SettingsContent({ activeTab }: { activeTab: SettingsTabKey }) { - switch (activeTab) { - case "general": - return ; - case "calendar": - return ; - case "transcription": - return ; - case "intelligence": - return ; - case "notifications": - return ; - case "account": - return ; - default: - return null; - } -} diff --git a/apps/desktop/src/components/main/sidebar/banner/index.tsx b/apps/desktop/src/components/main/sidebar/banner/index.tsx index 109ea4bf05..9379712d02 100644 --- a/apps/desktop/src/components/main/sidebar/banner/index.tsx +++ b/apps/desktop/src/components/main/sidebar/banner/index.tsx @@ -35,20 +35,20 @@ export function BannerArea({ await auth?.signIn(); }, [auth]); - const openSettingsTab = useCallback( + const openAiTab = useCallback( (tab: "intelligence" | "transcription") => { - openNew({ type: "settings", state: { tab } }); + openNew({ type: "ai", state: { tab } }); }, [openNew], ); const handleOpenLLMSettings = useCallback(() => { - openSettingsTab("intelligence"); - }, [openSettingsTab]); + openAiTab("intelligence"); + }, [openAiTab]); const handleOpenSTTSettings = useCallback(() => { - openSettingsTab("transcription"); - }, [openSettingsTab]); + openAiTab("transcription"); + }, [openAiTab]); const registry = useMemo( () => diff --git a/apps/desktop/src/components/main/sidebar/profile/auth.tsx b/apps/desktop/src/components/main/sidebar/profile/auth.tsx index 7bb9dfd7a7..7e4bae30dd 100644 --- a/apps/desktop/src/components/main/sidebar/profile/auth.tsx +++ b/apps/desktop/src/components/main/sidebar/profile/auth.tsx @@ -8,8 +8,8 @@ import { useTabs } from "../../../../store/zustand/tabs"; export function AuthSection({ isAuthenticated }: { isAuthenticated: boolean }) { const openNew = useTabs((state) => state.openNew); - const handleOpenAccount = useCallback(() => { - openNew({ type: "settings", state: { tab: "account" } }); + const handleOpenSettings = useCallback(() => { + openNew({ type: "settings" }); }, [openNew]); if (isAuthenticated) { @@ -18,7 +18,7 @@ export function AuthSection({ isAuthenticated }: { isAuthenticated: boolean }) { return (
- diff --git a/apps/desktop/src/components/main/sidebar/profile/index.tsx b/apps/desktop/src/components/main/sidebar/profile/index.tsx index e97aa06c3b..343b3e5b16 100644 --- a/apps/desktop/src/components/main/sidebar/profile/index.tsx +++ b/apps/desktop/src/components/main/sidebar/profile/index.tsx @@ -113,7 +113,7 @@ export function ProfileSection({ onExpandChange }: ProfileSectionProps = {}) { }, [openNew, closeMenu]); const handleClickCalendar = useCallback(() => { - openNew({ type: "extension", extensionId: "calendar" }); + openNew({ type: "calendar" }); closeMenu(); }, [openNew, closeMenu]); diff --git a/apps/desktop/src/components/settings/ai/llm/index.tsx b/apps/desktop/src/components/settings/ai/llm/index.tsx index 7b166c3d82..0b57a226ed 100644 --- a/apps/desktop/src/components/settings/ai/llm/index.tsx +++ b/apps/desktop/src/components/settings/ai/llm/index.tsx @@ -2,11 +2,11 @@ import { ConfigureProviders } from "./configure"; import { HealthCheckForAvailability } from "./health"; import { SelectProviderAndModel } from "./select"; -export function LLM() { +export function LLM({ headerAction }: { headerAction?: React.ReactNode } = {}) { return (
- +
); diff --git a/apps/desktop/src/components/settings/ai/llm/select.tsx b/apps/desktop/src/components/settings/ai/llm/select.tsx index 8da966212f..930d2e60b1 100644 --- a/apps/desktop/src/components/settings/ai/llm/select.tsx +++ b/apps/desktop/src/components/settings/ai/llm/select.tsx @@ -29,7 +29,9 @@ import { ModelCombobox } from "../shared/model-combobox"; import { HealthCheckForConnection } from "./health"; import { PROVIDERS } from "./shared"; -export function SelectProviderAndModel() { +export function SelectProviderAndModel({ + headerAction, +}: { headerAction?: React.ReactNode } = {}) { const configuredProviders = useConfiguredMapping(); const { current_llm_model, current_llm_provider } = useConfigValues([ @@ -76,7 +78,10 @@ export function SelectProviderAndModel() { return (
-

Model being used

+
+

Model being used

+ {headerAction} +
- +
); diff --git a/apps/desktop/src/components/settings/ai/stt/select.tsx b/apps/desktop/src/components/settings/ai/stt/select.tsx index c8cdbc1153..8b64f320af 100644 --- a/apps/desktop/src/components/settings/ai/stt/select.tsx +++ b/apps/desktop/src/components/settings/ai/stt/select.tsx @@ -25,7 +25,9 @@ import { sttModelQueries, } from "./shared"; -export function SelectProviderAndModel() { +export function SelectProviderAndModel({ + headerAction, +}: { headerAction?: React.ReactNode } = {}) { const { current_stt_provider, current_stt_model } = useConfigValues([ "current_stt_provider", "current_stt_model", @@ -72,7 +74,10 @@ export function SelectProviderAndModel() { return (
-

Model being used

+
+

Model being used

+ {headerAction} +
+
+

Notifications

+ +
+ + +
+

Account & Billing

+ +
); } diff --git a/apps/desktop/src/components/settings/notification.tsx b/apps/desktop/src/components/settings/general/notification.tsx similarity index 97% rename from apps/desktop/src/components/settings/notification.tsx rename to apps/desktop/src/components/settings/general/notification.tsx index 7f6170ee89..a3f0dbec75 100644 --- a/apps/desktop/src/components/settings/notification.tsx +++ b/apps/desktop/src/components/settings/general/notification.tsx @@ -14,10 +14,10 @@ import { Button } from "@hypr/ui/components/ui/button"; import { Switch } from "@hypr/ui/components/ui/switch"; import { cn } from "@hypr/utils"; -import { useConfigValues } from "../../config/use-config"; -import * as main from "../../store/tinybase/main"; +import { useConfigValues } from "../../../config/use-config"; +import * as main from "../../../store/tinybase/main"; -export function SettingsNotifications() { +export function NotificationSettingsView() { const [inputValue, setInputValue] = useState(""); const [showDropdown, setShowDropdown] = useState(false); const [selectedIndex, setSelectedIndex] = useState(0); @@ -143,7 +143,6 @@ export function SettingsNotifications() { const installedApps = allInstalledApps?.map((app) => app.name) ?? []; - // Filter apps based on input, excluding already added and default ignored const filteredApps = installedApps.filter((app) => { const matchesSearch = app.toLowerCase().includes(inputValue.toLowerCase()); const notAlreadyAdded = !ignoredPlatforms.includes(app); @@ -151,7 +150,6 @@ export function SettingsNotifications() { return matchesSearch && notAlreadyAdded && notDefaultIgnored; }); - // Show custom option if input doesn't match any apps const showCustomOption = inputValue.trim() && !filteredApps.some((app) => app.toLowerCase() === inputValue.toLowerCase()); @@ -205,7 +203,6 @@ export function SettingsNotifications() { !inputValue && ignoredPlatforms.length > 0 ) { - // Remove last chip when backspace is pressed on empty input const lastApp = ignoredPlatforms[ignoredPlatforms.length - 1]; if (!isDefaultIgnored(lastApp)) { handleRemoveIgnoredApp(lastApp); @@ -219,7 +216,6 @@ export function SettingsNotifications() { setSelectedIndex(0); }; - // Close dropdown when clicking outside useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if ( diff --git a/apps/desktop/src/store/zustand/tabs/schema.ts b/apps/desktop/src/store/zustand/tabs/schema.ts index 00d2f3898b..8e082c1e8b 100644 --- a/apps/desktop/src/store/zustand/tabs/schema.ts +++ b/apps/desktop/src/store/zustand/tabs/schema.ts @@ -118,22 +118,19 @@ export const tabSchema = z.discriminatedUnion("type", [ extensionId: z.string(), state: z.record(z.string(), z.unknown()).default({}), }), + baseTabSchema.extend({ + type: z.literal("calendar"), + }), baseTabSchema.extend({ type: z.literal("settings"), + }), + baseTabSchema.extend({ + type: z.literal("ai"), state: z .object({ - tab: z - .enum([ - "general", - "calendar", - "notifications", - "transcription", - "intelligence", - "account", - ]) - .default("general"), + tab: z.enum(["transcription", "intelligence"]).default("transcription"), }) - .default({ tab: "general" }), + .default({ tab: "transcription" }), }), ]); @@ -186,16 +183,12 @@ export type TabInput = | { type: "folders"; id: string | null } | { type: "empty" } | { type: "extension"; extensionId: string; state?: Record } + | { type: "calendar" } + | { type: "settings" } | { - type: "settings"; + type: "ai"; state?: { - tab?: - | "general" - | "calendar" - | "notifications" - | "transcription" - | "intelligence" - | "account"; + tab?: "transcription" | "intelligence"; }; }; @@ -216,7 +209,9 @@ export const rowIdfromTab = (tab: Tab): string => { case "extensions": case "empty": case "extension": + case "calendar": case "settings": + case "ai": throw new Error("invalid_resource"); case "folders": if (!tab.id) { @@ -252,8 +247,12 @@ export const uniqueIdfromTab = (tab: Tab): string => { return `empty-${tab.slotId}`; case "extension": return `extension-${tab.extensionId}`; + case "calendar": + return `calendar`; case "settings": return `settings`; + case "ai": + return `ai`; } }; diff --git a/apps/desktop/src/store/zustand/tabs/state.ts b/apps/desktop/src/store/zustand/tabs/state.ts index fa97726452..e2ab5370cf 100644 --- a/apps/desktop/src/store/zustand/tabs/state.ts +++ b/apps/desktop/src/store/zustand/tabs/state.ts @@ -30,9 +30,9 @@ export type StateBasicActions = { tab: Tab, state: Extract["state"], ) => void; - updateSettingsTabState: ( + updateAiTabState: ( tab: Tab, - state: Extract["state"], + state: Extract["state"], ) => void; }; @@ -52,8 +52,7 @@ export const createStateUpdaterSlice = ( updateTabState(tab, "chat_shortcuts", state, get, set), updateExtensionsTabState: (tab, state) => updateTabState(tab, "extensions", state, get, set), - updateSettingsTabState: (tab, state) => - updateTabState(tab, "settings", state, get, set), + updateAiTabState: (tab, state) => updateTabState(tab, "ai", state, get, set), }); const updateTabState = (