diff --git a/web/src/components/common/Card.tsx b/web/src/components/common/Card.tsx
index 1a6193f..c569c6e 100644
--- a/web/src/components/common/Card.tsx
+++ b/web/src/components/common/Card.tsx
@@ -21,6 +21,7 @@ import {
Send,
X,
Bookmark,
+ BookOpen,
} from "lucide-react";
import ShareMenu from "../modals/ShareMenu";
import AddToCollectionModal from "../modals/AddToCollectionModal";
@@ -338,6 +339,20 @@ export default function Card({
})()
: null;
+ const isbn = pageUrl?.match(/^urn:isbn:(.+)$/i)?.[1].replace(/-/g, "");
+ const bookUrl = isbn ? `https://openlibrary.org/isbn/${isbn}` : null;
+ const bookTitle =
+ item.target?.title || item.title || (isbn ? `ISBN ${isbn}` : null);
+
+ const quoteLinkUrl = (() => {
+ const sel = item.target?.selector;
+ if (!sel?.exact) return null;
+ if (bookUrl) return bookUrl;
+ const prefix = sel.prefix ? encodeURIComponent(sel.prefix) + "-," : "";
+ const suffix = sel.suffix ? ",-" + encodeURIComponent(sel.suffix) : "";
+ return `${pageUrl}#:~:text=${prefix}${encodeURIComponent(sel.exact)}${suffix}`;
+ })();
+
const decodeHTMLEntities = (text: string) => {
if (!text.includes("&")) return text;
try {
@@ -549,14 +564,18 @@ export default function Card({
{pageUrl && !isBookmark && !(contentWarning && !contentRevealed) && (
handleExternalClick(e, pageUrl)}
+ onClick={(e) => handleExternalClick(e, bookUrl || pageUrl)}
className="inline-flex items-center gap-1 text-xs text-primary-600 dark:text-primary-400 hover:underline mt-0.5 max-w-full"
>
-
- {displayUrl}
+ {isbn ? (
+
+ ) : (
+
+ )}
+ {isbn ? bookTitle : displayUrl}
)}
@@ -697,14 +716,11 @@ export default function Card({
}
>
{
- const sel = item.target?.selector;
- if (!sel) return;
- const url = `${pageUrl}#:~:text=${sel.prefix ? encodeURIComponent(sel.prefix) + "-," : ""}${encodeURIComponent(sel.exact)}${sel.suffix ? ",-" + encodeURIComponent(sel.suffix) : ""}`;
- handleExternalClick(e, url);
+ if (quoteLinkUrl) handleExternalClick(e, quoteLinkUrl);
}}
className="block break-words"
>
--
knotmirror 0.0.1
From ffdc76159a8a2a879b7fd0f436a16acaa2c9ad1e Mon Sep 17 00:00:00 2001
From: JP Hastings-Spital
Date: Thu, 25 Jun 2026 16:30:22 +0200
Subject: [PATCH 2/2] guards against malformed records
---
web/src/components/common/Card.tsx | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/web/src/components/common/Card.tsx b/web/src/components/common/Card.tsx
index c569c6e..09fc867 100644
--- a/web/src/components/common/Card.tsx
+++ b/web/src/components/common/Card.tsx
@@ -339,7 +339,8 @@ export default function Card({
})()
: null;
- const isbn = pageUrl?.match(/^urn:isbn:(.+)$/i)?.[1].replace(/-/g, "");
+ const isbnMatch = pageUrl?.match(/^urn:isbn:(.+)$/i);
+ const isbn = isbnMatch ? isbnMatch[1].replace(/-/g, "") : null;
const bookUrl = isbn ? `https://openlibrary.org/isbn/${isbn}` : null;
const bookTitle =
item.target?.title || item.title || (isbn ? `ISBN ${isbn}` : null);
@@ -348,6 +349,7 @@ export default function Card({
const sel = item.target?.selector;
if (!sel?.exact) return null;
if (bookUrl) return bookUrl;
+ if (!pageUrl) return null;
const prefix = sel.prefix ? encodeURIComponent(sel.prefix) + "-," : "";
const suffix = sel.suffix ? ",-" + encodeURIComponent(sel.suffix) : "";
return `${pageUrl}#:~:text=${prefix}${encodeURIComponent(sel.exact)}${suffix}`;