diff --git a/alembic/versions/0020_note_link_previews.py b/alembic/versions/0020_note_link_previews.py new file mode 100644 index 0000000..d31abb0 --- /dev/null +++ b/alembic/versions/0020_note_link_previews.py @@ -0,0 +1,38 @@ +"""note_link_previews (URL unfurl — M6 1901) + +Revision ID: 0020 +Revises: 0019 +Create Date: 2026-07-23 + +Cached OG/meta previews for URLs in a note, fetched server-side on request +(unfurl.py) and stored so they never re-fetch. One per (note, url). +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects.postgresql import UUID + +revision = "0020" +down_revision = "0019" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + op.create_table( + "note_link_previews", + sa.Column("id", UUID(as_uuid=True), primary_key=True), + sa.Column("note_id", UUID(as_uuid=True), sa.ForeignKey("notes.id", ondelete="CASCADE"), nullable=False), + sa.Column("url", sa.Text(), nullable=False), + sa.Column("title", sa.Text(), nullable=True), + sa.Column("description", sa.Text(), nullable=True), + sa.Column("image_url", sa.Text(), nullable=True), + sa.Column("site_name", sa.Text(), nullable=True), + sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), + sa.UniqueConstraint("note_id", "url", name="uq_note_link_previews_note_url"), + ) + op.create_index("ix_note_link_previews_note", "note_link_previews", ["note_id"]) + + +def downgrade() -> None: + op.drop_index("ix_note_link_previews_note", table_name="note_link_previews") + op.drop_table("note_link_previews") diff --git a/frontend/src/components/Icon.vue b/frontend/src/components/Icon.vue index 1121e8f..0871e08 100644 --- a/frontend/src/components/Icon.vue +++ b/frontend/src/components/Icon.vue @@ -27,6 +27,7 @@ const paths: Record = { upload: '', device: '', paperclip: '', + link: '', copy: '', }; diff --git a/frontend/src/components/LinkPreview.vue b/frontend/src/components/LinkPreview.vue new file mode 100644 index 0000000..5d60f9f --- /dev/null +++ b/frontend/src/components/LinkPreview.vue @@ -0,0 +1,46 @@ + + + diff --git a/frontend/src/components/NoteCard.vue b/frontend/src/components/NoteCard.vue index f8669db..1621bf7 100644 --- a/frontend/src/components/NoteCard.vue +++ b/frontend/src/components/NoteCard.vue @@ -11,6 +11,7 @@ import { } from "../notes/colors"; import type { Note } from "../stores/notes"; import Icon from "./Icon.vue"; +import LinkPreview from "./LinkPreview.vue"; import MarkdownText from "./MarkdownText.vue"; import NoteChecklist from "./NoteChecklist.vue"; import { formatReminder, isOverdue } from "../notes/datetime"; @@ -162,6 +163,10 @@ onBeforeUnmount(() => document.removeEventListener("mousedown", onDocMousedown)) +
+ +
+