Skip to main content
Glama

Karakeep MCP server

by karakeep-app
UserOptions.tsx9.41 kB
"use client"; import { useEffect, useState } from "react"; import { useClientConfig } from "@/lib/clientConfig"; import { useTranslation } from "@/lib/i18n/client"; import { useInterfaceLang } from "@/lib/userLocalSettings/bookmarksLayout"; import { updateInterfaceLang } from "@/lib/userLocalSettings/userLocalSettings"; import { useUserSettings } from "@/lib/userSettings"; import { zodResolver } from "@hookform/resolvers/zod"; import { Archive, Bookmark, Clock, Globe } from "lucide-react"; import { useForm } from "react-hook-form"; import { z } from "zod"; import { useUpdateUserSettings } from "@karakeep/shared-react/hooks/users"; import { langNameMappings } from "@karakeep/shared/langs"; import { ZUserSettings, zUserSettingsSchema, } from "@karakeep/shared/types/users"; import { Card, CardContent, CardHeader, CardTitle } from "../ui/card"; import { Form, FormField } from "../ui/form"; import { Label } from "../ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "../ui/select"; import { toast } from "../ui/use-toast"; const LanguageSelect = () => { const lang = useInterfaceLang(); return ( <Select value={lang} onValueChange={async (val) => { await updateInterfaceLang(val); }} > <SelectTrigger className="h-11"> <SelectValue /> </SelectTrigger> <SelectContent> {Object.entries(langNameMappings).map(([lang, name]) => ( <SelectItem key={lang} value={lang}> {name} </SelectItem> ))} </SelectContent> </Select> ); }; export default function UserOptions() { const { t } = useTranslation(); const clientConfig = useClientConfig(); const data = useUserSettings(); const { mutate } = useUpdateUserSettings({ onSuccess: () => { toast({ description: t("settings.info.user_settings.user_settings_updated"), }); }, onError: () => { toast({ description: t("common.something_went_wrong"), variant: "destructive", }); }, }); const [timezones, setTimezones] = useState< { label: string; value: string }[] | null >(null); const bookmarkClickActionTranslation: Record< ZUserSettings["bookmarkClickAction"], string > = { open_original_link: t( "settings.info.user_settings.bookmark_click_action.open_external_url", ), expand_bookmark_preview: t( "settings.info.user_settings.bookmark_click_action.open_bookmark_details", ), }; const archiveDisplayBehaviourTranslation: Record< ZUserSettings["archiveDisplayBehaviour"], string > = { show: t("settings.info.user_settings.archive_display_behaviour.show"), hide: t("settings.info.user_settings.archive_display_behaviour.hide"), }; // Get all supported timezones and format them nicely useEffect(() => { try { const browserTimezones = Intl.supportedValuesOf("timeZone"); setTimezones( browserTimezones .map((tz) => { // Create a more readable label by replacing underscores with spaces // and showing the current time offset const now = new Date(); const formatter = new Intl.DateTimeFormat("en", { timeZone: tz, timeZoneName: "short", }); const parts = formatter.formatToParts(now); const timeZoneName = parts.find((part) => part.type === "timeZoneName")?.value || ""; // Format the timezone name for display const displayName = tz.replace(/_/g, " ").replace("/", " / "); const label = timeZoneName ? `${displayName} (${timeZoneName})` : displayName; return { value: tz, label }; }) .sort((a, b) => a.label.localeCompare(b.label)), ); } catch { setTimezones(null); } }, []); const form = useForm<z.infer<typeof zUserSettingsSchema>>({ resolver: zodResolver(zUserSettingsSchema), defaultValues: data, }); // When the actual user setting is loaded, reset the form to the current value useEffect(() => { form.reset(data); }, [data]); return ( <Form {...form}> <Card> <CardHeader> <CardTitle className="flex items-center gap-2 text-xl"> <Globe className="h-5 w-5" /> {t("settings.info.options")} </CardTitle> </CardHeader> <CardContent className="space-y-6"> <div className="space-y-2"> <Label className="text-sm font-medium"> {t("settings.info.interface_lang")} </Label> <LanguageSelect /> </div> <FormField control={form.control} name="timezone" render={({ field }) => ( <div className="space-y-2"> <Label className="flex items-center gap-2 text-sm font-medium"> <Clock className="h-4 w-4" /> Timezone </Label> <Select disabled={!!clientConfig.demoMode || timezones === null} value={field.value} onValueChange={(value) => { mutate({ timezone: value, }); }} > <SelectTrigger className="h-11"> <SelectValue> {timezones?.find( (tz: { value: string; label: string }) => tz.value === field.value, )?.label || field.value} </SelectValue> </SelectTrigger> <SelectContent> {timezones?.map((tz: { value: string; label: string }) => ( <SelectItem key={tz.value} value={tz.value}> {tz.label} </SelectItem> ))} </SelectContent> </Select> </div> )} /> <div className="grid grid-cols-2 gap-6"> <FormField control={form.control} name="bookmarkClickAction" render={({ field }) => ( <div className="space-y-2"> <Label className="flex items-center gap-2 text-sm font-medium"> <Bookmark className="h-4 w-4" /> {t( "settings.info.user_settings.bookmark_click_action.title", )} </Label> <Select disabled={!!clientConfig.demoMode} value={field.value} onValueChange={(value) => { mutate({ bookmarkClickAction: value as ZUserSettings["bookmarkClickAction"], }); }} > <SelectTrigger className="h-11"> <SelectValue> {bookmarkClickActionTranslation[field.value]} </SelectValue> </SelectTrigger> <SelectContent> {Object.entries(bookmarkClickActionTranslation).map( ([value, label]) => ( <SelectItem key={value} value={value}> {label} </SelectItem> ), )} </SelectContent> </Select> </div> )} /> <FormField control={form.control} name="archiveDisplayBehaviour" render={({ field }) => ( <div className="space-y-2"> <Label className="flex items-center gap-2 text-sm font-medium"> <Archive className="h-4 w-4" /> {t( "settings.info.user_settings.archive_display_behaviour.title", )} </Label> <Select disabled={!!clientConfig.demoMode} value={field.value} onValueChange={(value) => { mutate({ archiveDisplayBehaviour: value as ZUserSettings["archiveDisplayBehaviour"], }); }} > <SelectTrigger className="h-11"> <SelectValue> {archiveDisplayBehaviourTranslation[field.value]} </SelectValue> </SelectTrigger> <SelectContent> {Object.entries(archiveDisplayBehaviourTranslation).map( ([value, label]) => ( <SelectItem key={value} value={value}> {label} </SelectItem> ), )} </SelectContent> </Select> </div> )} /> </div> </CardContent> </Card> </Form> ); }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/karakeep-app/karakeep'

If you have feedback or need assistance with the MCP directory API, please join our Discord server