aboutsummaryrefslogtreecommitdiffstats
path: root/apps/web/components/dashboard/preview
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/components/dashboard/preview')
-rw-r--r--apps/web/components/dashboard/preview/AssetContentSection.tsx2
-rw-r--r--apps/web/components/dashboard/preview/AttachmentBox.tsx2
-rw-r--r--apps/web/components/dashboard/preview/BookmarkHtmlHighlighter.tsx23
-rw-r--r--apps/web/components/dashboard/preview/BookmarkPreview.tsx120
-rw-r--r--apps/web/components/dashboard/preview/TextContentSection.tsx2
5 files changed, 102 insertions, 47 deletions
diff --git a/apps/web/components/dashboard/preview/AssetContentSection.tsx b/apps/web/components/dashboard/preview/AssetContentSection.tsx
index fd299320..5cab86bd 100644
--- a/apps/web/components/dashboard/preview/AssetContentSection.tsx
+++ b/apps/web/components/dashboard/preview/AssetContentSection.tsx
@@ -11,8 +11,8 @@ import {
} from "@/components/ui/select";
import { useTranslation } from "@/lib/i18n/client";
-import { getAssetUrl } from "@karakeep/shared-react/utils/assetUtils";
import { BookmarkTypes, ZBookmark } from "@karakeep/shared/types/bookmarks";
+import { getAssetUrl } from "@karakeep/shared/utils/assetUtils";
// 20 MB
const BIG_FILE_SIZE = 20 * 1024 * 1024;
diff --git a/apps/web/components/dashboard/preview/AttachmentBox.tsx b/apps/web/components/dashboard/preview/AttachmentBox.tsx
index 15acd799..674f151c 100644
--- a/apps/web/components/dashboard/preview/AttachmentBox.tsx
+++ b/apps/web/components/dashboard/preview/AttachmentBox.tsx
@@ -19,8 +19,8 @@ import {
useDetachBookmarkAsset,
useReplaceBookmarkAsset,
} from "@karakeep/shared-react/hooks/assets";
-import { getAssetUrl } from "@karakeep/shared-react/utils/assetUtils";
import { BookmarkTypes, ZBookmark } from "@karakeep/shared/types/bookmarks";
+import { getAssetUrl } from "@karakeep/shared/utils/assetUtils";
import {
humanFriendlyNameForAssertType,
isAllowedToAttachAsset,
diff --git a/apps/web/components/dashboard/preview/BookmarkHtmlHighlighter.tsx b/apps/web/components/dashboard/preview/BookmarkHtmlHighlighter.tsx
index a3b34f9a..dc446112 100644
--- a/apps/web/components/dashboard/preview/BookmarkHtmlHighlighter.tsx
+++ b/apps/web/components/dashboard/preview/BookmarkHtmlHighlighter.tsx
@@ -19,6 +19,7 @@ interface ColorPickerMenuProps {
onDelete?: () => void;
selectedHighlight: Highlight | null;
onClose: () => void;
+ isMobile: boolean;
}
const ColorPickerMenu: React.FC<ColorPickerMenuProps> = ({
@@ -27,6 +28,7 @@ const ColorPickerMenu: React.FC<ColorPickerMenuProps> = ({
onDelete,
selectedHighlight,
onClose,
+ isMobile,
}) => {
return (
<Popover
@@ -44,7 +46,10 @@ const ColorPickerMenu: React.FC<ColorPickerMenuProps> = ({
top: position?.y,
}}
/>
- <PopoverContent side="top" className="flex w-fit items-center gap-1 p-2">
+ <PopoverContent
+ side={isMobile ? "bottom" : "top"}
+ className="flex w-fit items-center gap-1 p-2"
+ >
{SUPPORTED_HIGHLIGHT_COLORS.map((color) => (
<Button
size="none"
@@ -113,6 +118,11 @@ function BookmarkHTMLHighlighter({
const [selectedHighlight, setSelectedHighlight] = useState<Highlight | null>(
null,
);
+ const isMobile = useState(
+ () =>
+ typeof window !== "undefined" &&
+ window.matchMedia("(pointer: coarse)").matches,
+ )[0];
// Apply existing highlights when component mounts or highlights change
useEffect(() => {
@@ -160,7 +170,7 @@ function BookmarkHTMLHighlighter({
window.getSelection()?.addRange(newRange);
}, [pendingHighlight, contentRef]);
- const handleMouseUp = (e: React.MouseEvent) => {
+ const handlePointerUp = (e: React.PointerEvent) => {
const selection = window.getSelection();
// Check if we clicked on an existing highlight
@@ -192,11 +202,11 @@ function BookmarkHTMLHighlighter({
return;
}
- // Position the menu above the selection
+ // Position the menu based on device type
const rect = range.getBoundingClientRect();
setMenuPosition({
- x: rect.left + rect.width / 2, // Center the menu
- y: rect.top,
+ x: rect.left + rect.width / 2, // Center the menu horizontally
+ y: isMobile ? rect.bottom : rect.top, // Position below on mobile, above otherwise
});
// Store the highlight for later use
@@ -333,7 +343,7 @@ function BookmarkHTMLHighlighter({
role="presentation"
ref={contentRef}
dangerouslySetInnerHTML={{ __html: htmlContent }}
- onMouseUp={handleMouseUp}
+ onPointerUp={handlePointerUp}
className={className}
/>
<ColorPickerMenu
@@ -342,6 +352,7 @@ function BookmarkHTMLHighlighter({
onDelete={handleDelete}
selectedHighlight={selectedHighlight}
onClose={closeColorPicker}
+ isMobile={isMobile}
/>
</div>
);
diff --git a/apps/web/components/dashboard/preview/BookmarkPreview.tsx b/apps/web/components/dashboard/preview/BookmarkPreview.tsx
index df09f687..e213b9cb 100644
--- a/apps/web/components/dashboard/preview/BookmarkPreview.tsx
+++ b/apps/web/components/dashboard/preview/BookmarkPreview.tsx
@@ -1,11 +1,12 @@
"use client";
-import React from "react";
+import { useState } from "react";
import Link from "next/link";
import { BookmarkTagsEditor } from "@/components/dashboard/bookmarks/BookmarkTagsEditor";
import { FullPageSpinner } from "@/components/ui/full-page-spinner";
import { Separator } from "@/components/ui/separator";
import { Skeleton } from "@/components/ui/skeleton";
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import {
Tooltip,
TooltipContent,
@@ -17,13 +18,13 @@ import { useTranslation } from "@/lib/i18n/client";
import { api } from "@/lib/trpc";
import { CalendarDays, ExternalLink } from "lucide-react";
+import { BookmarkTypes, ZBookmark } from "@karakeep/shared/types/bookmarks";
import {
getBookmarkTitle,
getSourceUrl,
isBookmarkStillCrawling,
isBookmarkStillLoading,
-} from "@karakeep/shared-react/utils/bookmarkUtils";
-import { BookmarkTypes, ZBookmark } from "@karakeep/shared/types/bookmarks";
+} from "@karakeep/shared/utils/bookmarkUtils";
import SummarizeBookmarkArea from "../bookmarks/SummarizeBookmarkArea";
import ActionBar from "./ActionBar";
@@ -68,6 +69,8 @@ export default function BookmarkPreview({
initialData?: ZBookmark;
}) {
const { t } = useTranslation();
+ const [activeTab, setActiveTab] = useState<string>("content");
+
const { data: bookmark } = api.bookmarks.getBookmark.useQuery(
{
bookmarkId,
@@ -111,45 +114,86 @@ export default function BookmarkPreview({
const sourceUrl = getSourceUrl(bookmark);
const title = getBookmarkTitle(bookmark);
- return (
- <div className="grid h-full grid-rows-3 gap-2 overflow-hidden bg-background lg:grid-cols-3 lg:grid-rows-none">
- <div className="row-span-2 h-full w-full overflow-auto p-2 md:col-span-2 lg:row-auto">
- {isBookmarkStillCrawling(bookmark) ? <ContentLoading /> : content}
- </div>
- <div className="row-span-1 flex flex-col gap-4 overflow-auto bg-accent p-4 md:col-span-2 lg:col-span-1 lg:row-auto">
- <div className="flex w-full flex-col items-center justify-center gap-y-2">
- <div className="flex w-full items-center justify-center gap-2">
- <p className="line-clamp-2 text-ellipsis break-words text-lg">
- {title === undefined || title === "" ? "Untitled" : title}
- </p>
- </div>
- {sourceUrl && (
- <Link
- href={sourceUrl}
- target="_blank"
- className="flex items-center gap-2 text-gray-400"
- >
- <span>{t("preview.view_original")}</span>
- <ExternalLink />
- </Link>
- )}
- <Separator />
+ // Common content for both layouts
+ const contentSection = isBookmarkStillCrawling(bookmark) ? (
+ <ContentLoading />
+ ) : (
+ content
+ );
+
+ const detailsSection = (
+ <div className="flex flex-col gap-4">
+ <div className="flex w-full flex-col items-center justify-center gap-y-2">
+ <div className="flex w-full items-center justify-center gap-2">
+ <p className="line-clamp-2 text-ellipsis break-words text-lg">
+ {title === undefined || title === "" ? "Untitled" : title}
+ </p>
</div>
+ {sourceUrl && (
+ <Link
+ href={sourceUrl}
+ target="_blank"
+ className="flex items-center gap-2 text-gray-400"
+ >
+ <span>{t("preview.view_original")}</span>
+ <ExternalLink />
+ </Link>
+ )}
+ <Separator />
+ </div>
+ <CreationTime createdAt={bookmark.createdAt} />
+ <SummarizeBookmarkArea bookmark={bookmark} />
+ <div className="flex items-center gap-4">
+ <p className="text-sm text-gray-400">{t("common.tags")}</p>
+ <BookmarkTagsEditor bookmark={bookmark} />
+ </div>
+ <div className="flex gap-4">
+ <p className="pt-2 text-sm text-gray-400">{t("common.note")}</p>
+ <NoteEditor bookmark={bookmark} />
+ </div>
+ <AttachmentBox bookmark={bookmark} />
+ <HighlightsBox bookmarkId={bookmark.id} />
+ <ActionBar bookmark={bookmark} />
+ </div>
+ );
- <CreationTime createdAt={bookmark.createdAt} />
- <SummarizeBookmarkArea bookmark={bookmark} />
- <div className="flex items-center gap-4">
- <p className="text-sm text-gray-400">{t("common.tags")}</p>
- <BookmarkTagsEditor bookmark={bookmark} />
+ return (
+ <>
+ {/* Render original layout for wide screens */}
+ <div className="hidden h-full grid-cols-3 overflow-hidden bg-background lg:grid">
+ <div className="col-span-2 h-full w-full overflow-auto p-2">
+ {contentSection}
</div>
- <div className="flex gap-4">
- <p className="pt-2 text-sm text-gray-400">{t("common.note")}</p>
- <NoteEditor bookmark={bookmark} />
+ <div className="flex flex-col gap-4 overflow-auto bg-accent p-4">
+ {detailsSection}
</div>
- <AttachmentBox bookmark={bookmark} />
- <HighlightsBox bookmarkId={bookmark.id} />
- <ActionBar bookmark={bookmark} />
</div>
- </div>
+
+ {/* Render tabbed layout for narrow/vertical screens */}
+ <Tabs
+ value={activeTab}
+ onValueChange={setActiveTab}
+ className="flex h-full w-full flex-col overflow-hidden lg:hidden"
+ >
+ <TabsList
+ className={`sticky top-0 z-10 grid h-auto w-full grid-cols-2`}
+ >
+ <TabsTrigger value="content">{t("preview.tabs.content")}</TabsTrigger>
+ <TabsTrigger value="details">{t("preview.tabs.details")}</TabsTrigger>
+ </TabsList>
+ <TabsContent
+ value="content"
+ className="h-full flex-1 overflow-hidden overflow-y-auto bg-background p-2 data-[state=inactive]:hidden"
+ >
+ {contentSection}
+ </TabsContent>
+ <TabsContent
+ value="details"
+ className="h-full overflow-y-auto bg-accent p-4 data-[state=inactive]:hidden"
+ >
+ {detailsSection}
+ </TabsContent>
+ </Tabs>
+ </>
);
}
diff --git a/apps/web/components/dashboard/preview/TextContentSection.tsx b/apps/web/components/dashboard/preview/TextContentSection.tsx
index 0c1aae67..4e33bb92 100644
--- a/apps/web/components/dashboard/preview/TextContentSection.tsx
+++ b/apps/web/components/dashboard/preview/TextContentSection.tsx
@@ -3,8 +3,8 @@ import { BookmarkMarkdownComponent } from "@/components/dashboard/bookmarks/Book
import { ScrollArea } from "@radix-ui/react-scroll-area";
import type { ZBookmarkTypeText } from "@karakeep/shared/types/bookmarks";
-import { getAssetUrl } from "@karakeep/shared-react/utils/assetUtils";
import { BookmarkTypes, ZBookmark } from "@karakeep/shared/types/bookmarks";
+import { getAssetUrl } from "@karakeep/shared/utils/assetUtils";
export function TextContentSection({ bookmark }: { bookmark: ZBookmark }) {
if (bookmark.content.type != BookmarkTypes.TEXT) {