Skip to main content
Glama

Karakeep MCP server

by karakeep-app
ManageListsModal.tsx6.63 kB
import { useState } from "react"; import { ActionButton } from "@/components/ui/action-button"; import { Button } from "@/components/ui/button"; import { Dialog, DialogClose, DialogContent, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Form, FormControl, FormField, FormItem, FormMessage, } from "@/components/ui/form"; import LoadingSpinner from "@/components/ui/spinner"; import { toast } from "@/components/ui/use-toast"; import { useTranslation } from "@/lib/i18n/client"; import { api } from "@/lib/trpc"; import { zodResolver } from "@hookform/resolvers/zod"; import { Archive, X } from "lucide-react"; import { useForm } from "react-hook-form"; import { z } from "zod"; import { useAddBookmarkToList, useBookmarkLists, useRemoveBookmarkFromList, } from "@karakeep/shared-react/hooks/lists"; import { BookmarkListSelector } from "../lists/BookmarkListSelector"; import ArchiveBookmarkButton from "./action-buttons/ArchiveBookmarkButton"; export default function ManageListsModal({ bookmarkId, open, setOpen, }: { bookmarkId: string; open: boolean; setOpen: (open: boolean) => void; }) { const { t } = useTranslation(); const formSchema = z.object({ listId: z.string({ required_error: "Please select a list", }), }); const form = useForm<z.infer<typeof formSchema>>({ resolver: zodResolver(formSchema), defaultValues: { listId: undefined, }, }); const { data: allLists, isPending: isAllListsPending } = useBookmarkLists( undefined, { enabled: open }, ); const { data: alreadyInList, isPending: isAlreadyInListPending } = api.lists.getListsOfBookmark.useQuery( { bookmarkId, }, { enabled: open }, ); const isLoading = isAllListsPending || isAlreadyInListPending; const { mutate: addToList, isPending: isAddingToListPending } = useAddBookmarkToList({ onSuccess: () => { toast({ description: t("toasts.lists.updated"), }); form.resetField("listId"); }, onError: (e) => { if (e.data?.code == "BAD_REQUEST") { toast({ variant: "destructive", description: e.message, }); } else { toast({ variant: "destructive", title: t("common.something_went_wrong"), }); } }, }); const { mutate: deleteFromList, isPending: isDeleteFromListPending } = useRemoveBookmarkFromList({ onSuccess: () => { toast({ description: t("toasts.lists.updated"), }); form.resetField("listId"); }, onError: (e) => { if (e.data?.code == "BAD_REQUEST") { toast({ variant: "destructive", description: e.message, }); } else { toast({ variant: "destructive", title: t("common.something_went_wrong"), }); } }, }); return ( <Dialog open={open} onOpenChange={setOpen}> <DialogContent> <Form {...form}> <form onSubmit={form.handleSubmit((value) => { addToList({ bookmarkId: bookmarkId, listId: value.listId, }); })} > <DialogHeader> <DialogTitle>Manage Lists</DialogTitle> </DialogHeader> {isLoading ? ( <LoadingSpinner className="my-4" /> ) : ( allLists && ( <ul className="flex flex-col gap-2 pb-2 pt-4"> {alreadyInList?.lists.map((list) => ( <li key={list.id} className="flex items-center justify-between rounded-lg border border-border bg-background px-2 py-1 text-foreground" > <p> {allLists .getPathById(list.id)! .map((l) => `${l.icon} ${l.name}`) .join(" / ")} </p> <ActionButton type="button" variant="ghost" size="sm" loading={isDeleteFromListPending} onClick={() => deleteFromList({ bookmarkId, listId: list.id }) } > <X className="size-4" /> </ActionButton> </li> ))} </ul> ) )} <div className="pb-4"> <FormField control={form.control} name="listId" render={({ field }) => { return ( <FormItem> <FormControl> <BookmarkListSelector value={field.value} hideBookmarkIds={alreadyInList?.lists.map( (l) => l.id, )} onChange={field.onChange} listTypes={["manual"]} /> </FormControl> <FormMessage /> </FormItem> ); }} /> </div> <DialogFooter className="sm:justify-end"> <DialogClose asChild> <Button type="button" variant="secondary"> {t("actions.close")} </Button> </DialogClose> <ArchiveBookmarkButton type="button" bookmarkId={bookmarkId} onDone={() => setOpen(false)} > <Archive className="mr-2 size-4" /> {t("actions.archive")} </ArchiveBookmarkButton> <ActionButton type="submit" loading={isAddingToListPending} disabled={isAddingToListPending} > {t("actions.add")} </ActionButton> </DialogFooter> </form> </Form> </DialogContent> </Dialog> ); } export function useManageListsModal(bookmarkId: string) { const [open, setOpen] = useState(false); return { open, setOpen, content: open && ( <ManageListsModal bookmarkId={bookmarkId} open={open} setOpen={setOpen} /> ), }; }

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