-
+
+
{{ preview.site_name }}
-
+
{{ preview.title || preview.url }}
-
+
+
{{ preview.description }}
diff --git a/frontend/src/components/NoteCard.vue b/frontend/src/components/NoteCard.vue
index 04783c9..b6ef1bf 100644
--- a/frontend/src/components/NoteCard.vue
+++ b/frontend/src/components/NoteCard.vue
@@ -68,6 +68,26 @@ const otherAttachments = computed(() => props.note.attachments.filter((a) => !a.
// exactly, and skips parsing a body the card was never going to show.
const PREVIEW_LINES = 8;
+// --- Links ------------------------------------------------------------------
+//
+// A note that is NOTHING but a URL is a link, and its preview is the whole card —
+// showing the raw URL underneath a card that already says where it goes is saying the
+// same thing twice, badly. A URL mentioned *inside* a note is a footnote to it, and
+// gets a compact strip at the bottom instead.
+//
+// Whitespace either side still counts as lone: someone pasting a link rarely trims it.
+const LONE_URL_RE = /^\s*(https?:\/\/[^\s<>"'\])]+)\s*$/;
+
+const isLoneUrl = computed(() => LONE_URL_RE.test(props.note.body) && !props.note.items.length);
+
+/** The preview for a lone-URL note — null while it is still being fetched, or if it
+ * could never be fetched at all. */
+const loneUrlPreview = computed(() => {
+ if (!isLoneUrl.value) return null;
+ const url = props.note.body.trim();
+ return props.note.previews.find((p) => p.url === url) ?? null;
+});
+
const bodyPreview = computed(() => {
const lines = props.note.body.split("\n");
if (lines.length <= PREVIEW_LINES) return props.note.body;
@@ -226,9 +246,6 @@ onBeforeUnmount(() => document.removeEventListener("mousedown", onDocMousedown))
- document.removeEventListener("mousedown", onDocMousedown))
Empty note