Skip to main content
Glama

Karakeep MCP server

by karakeep-app
AttachmentBox.tsx6.75 kB
import Link from "next/link"; import { ActionButton } from "@/components/ui/action-button"; import ActionConfirmingDialog from "@/components/ui/action-confirming-dialog"; import { Button } from "@/components/ui/button"; import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from "@/components/ui/collapsible"; import FilePickerButton from "@/components/ui/file-picker-button"; import { toast } from "@/components/ui/use-toast"; import { ASSET_TYPE_TO_ICON } from "@/lib/attachments"; import useUpload from "@/lib/hooks/upload-file"; import { useTranslation } from "@/lib/i18n/client"; import { ChevronsDownUp, Download, Pencil, Plus, Trash2 } from "lucide-react"; import { useAttachBookmarkAsset, useDetachBookmarkAsset, useReplaceBookmarkAsset, } from "@karakeep/shared-react/hooks/assets"; import { BookmarkTypes, ZBookmark } from "@karakeep/shared/types/bookmarks"; import { getAssetUrl } from "@karakeep/shared/utils/assetUtils"; import { humanFriendlyNameForAssertType, isAllowedToAttachAsset, isAllowedToDetachAsset, } from "@karakeep/trpc/lib/attachments"; export default function AttachmentBox({ bookmark }: { bookmark: ZBookmark }) { const { t } = useTranslation(); const { mutate: attachAsset, isPending: isAttaching } = useAttachBookmarkAsset({ onSuccess: () => { toast({ description: "Attachment has been attached!", }); }, onError: (e) => { toast({ description: e.message, variant: "destructive", }); }, }); const { mutate: replaceAsset, isPending: isReplacing } = useReplaceBookmarkAsset({ onSuccess: () => { toast({ description: "Attachment has been replaced!", }); }, onError: (e) => { toast({ description: e.message, variant: "destructive", }); }, }); const { mutate: detachAsset, isPending: isDetaching } = useDetachBookmarkAsset({ onSuccess: () => { toast({ description: "Attachment has been detached!", }); }, onError: (e) => { toast({ description: e.message, variant: "destructive", }); }, }); const { mutate: uploadAsset } = useUpload({ onError: (e) => { toast({ description: e.error, variant: "destructive", }); }, }); bookmark.assets.sort((a, b) => a.assetType.localeCompare(b.assetType)); return ( <Collapsible> <CollapsibleTrigger className="flex w-full items-center justify-between gap-2 text-sm text-gray-400"> {t("common.attachments")} <ChevronsDownUp className="size-4" /> </CollapsibleTrigger> <CollapsibleContent className="flex flex-col gap-1 py-2 text-sm"> {bookmark.assets.map((asset) => ( <div key={asset.id} className="flex items-center justify-between"> <Link target="_blank" href={getAssetUrl(asset.id)} className="flex items-center gap-1" prefetch={false} > {ASSET_TYPE_TO_ICON[asset.assetType]} <p>{humanFriendlyNameForAssertType(asset.assetType)}</p> </Link> <div className="flex gap-2"> <Link title="Download" target="_blank" href={getAssetUrl(asset.id)} className="flex items-center gap-1" download={humanFriendlyNameForAssertType(asset.assetType)} prefetch={false} > <Download className="size-4" /> </Link> {isAllowedToAttachAsset(asset.assetType) && ( <FilePickerButton title="Replace" loading={isReplacing} accept=".jgp,.JPG,.jpeg,.png,.webp" multiple={false} variant="none" size="none" className="flex items-center gap-2" onFileSelect={(file) => uploadAsset(file, { onSuccess: (resp) => { replaceAsset({ bookmarkId: bookmark.id, oldAssetId: asset.id, newAssetId: resp.assetId, }); }, }) } > <Pencil className="size-4" /> </FilePickerButton> )} {isAllowedToDetachAsset(asset.assetType) && ( <ActionConfirmingDialog title="Delete Attachment?" description={`Are you sure you want to delete the attachment of the bookmark?`} actionButton={(setDialogOpen) => ( <ActionButton loading={isDetaching} variant="destructive" onClick={() => detachAsset( { bookmarkId: bookmark.id, assetId: asset.id }, { onSettled: () => setDialogOpen(false) }, ) } > <Trash2 className="mr-2 size-4" /> Delete </ActionButton> )} > <Button variant="none" size="none" title="Delete"> <Trash2 className="size-4" /> </Button> </ActionConfirmingDialog> )} </div> </div> ))} {!bookmark.assets.some((asset) => asset.assetType == "bannerImage") && bookmark.content.type != BookmarkTypes.ASSET && ( <FilePickerButton title="Attach a Banner" loading={isAttaching} accept=".jgp,.JPG,.jpeg,.png,.webp" multiple={false} variant="ghost" size="none" className="flex w-full items-center justify-center gap-2" onFileSelect={(file) => uploadAsset(file, { onSuccess: (resp) => { attachAsset({ bookmarkId: bookmark.id, asset: { id: resp.assetId, assetType: "bannerImage", }, }); }, }) } > <Plus className="size-4" /> Attach a Banner </FilePickerButton> )} </CollapsibleContent> </Collapsible> ); }

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