a tool for shared writing and social publishing
0

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'main' of https://github.com/hyperlink-academy/minilink into refactor/pub-settings

celine (Apr 7, 2026, 4:55 PM EDT) 72df5731 45ed95d0

+39 -17
-1
actions/createNewLeaflet.ts
··· 12 12 email_auth_tokens, 13 13 } from "drizzle/schema"; 14 14 import { redirect } from "next/navigation"; 15 - import postgres from "postgres"; 16 15 import { v7 } from "uuid"; 17 16 import { sql, eq, and } from "drizzle-orm"; 18 17 import { cookies } from "next/headers";
+1 -1
actions/emailAuth.ts
··· 2 2 3 3 import { randomBytes } from "crypto"; 4 4 import { drizzle } from "drizzle-orm/node-postgres"; 5 - import postgres from "postgres"; 5 + 6 6 import { email_auth_tokens, identities } from "drizzle/schema"; 7 7 import { and, eq } from "drizzle-orm"; 8 8 import { cookies } from "next/headers";
-1
actions/login.ts
··· 1 1 "use server"; 2 2 import { drizzle } from "drizzle-orm/node-postgres"; 3 - import postgres from "postgres"; 4 3 import { 5 4 email_auth_tokens, 6 5 identities,
+1 -1
actions/removeLeafletFromHome.ts
··· 2 2 3 3 import { drizzle } from "drizzle-orm/node-postgres"; 4 4 import { permission_token_on_homepage } from "drizzle/schema"; 5 - import postgres from "postgres"; 5 + 6 6 import { v7 } from "uuid"; 7 7 import { sql, eq, inArray, and } from "drizzle-orm"; 8 8 import { cookies } from "next/headers";
+1 -1
actions/subscriptions/confirmEmailSubscription.ts
··· 8 8 facts, 9 9 permission_tokens, 10 10 } from "drizzle/schema"; 11 - import postgres from "postgres"; 11 + 12 12 import type { Fact } from "src/replicache"; 13 13 import { Database } from "supabase/database.types"; 14 14 import { pool } from "supabase/pool";
+1 -1
actions/subscriptions/deleteSubscription.ts
··· 2 2 3 3 import { drizzle } from "drizzle-orm/node-postgres"; 4 4 import { email_subscriptions_to_entity, facts } from "drizzle/schema"; 5 - import postgres from "postgres"; 5 + 6 6 import { eq, and, sql } from "drizzle-orm"; 7 7 import type { Fact } from "src/replicache"; 8 8 import { v7 } from "uuid";
+1 -1
actions/subscriptions/sendPostToSubscribers.ts
··· 5 5 import { and, eq } from "drizzle-orm"; 6 6 import { drizzle } from "drizzle-orm/node-postgres"; 7 7 import { email_subscriptions_to_entity, entities } from "drizzle/schema"; 8 - import postgres from "postgres"; 8 + 9 9 import type { PermissionToken } from "src/replicache"; 10 10 import { Database } from "supabase/database.types"; 11 11 import { pool } from "supabase/pool";
+1 -1
actions/subscriptions/subscribeToMailboxWithEmail.ts
··· 5 5 import { and, eq } from "drizzle-orm"; 6 6 import { drizzle } from "drizzle-orm/node-postgres"; 7 7 import { email_subscriptions_to_entity } from "drizzle/schema"; 8 - import postgres from "postgres"; 8 + 9 9 import { getBlocksWithTypeLocal } from "src/replicache/getBlocks"; 10 10 import type { Fact, PermissionToken } from "src/replicache"; 11 11 import type { Attribute } from "src/replicache/attributes";
+1 -1
app/emails/unsubscribe/route.ts
··· 1 1 import { NextRequest } from "next/server"; 2 2 import { drizzle } from "drizzle-orm/node-postgres"; 3 3 import { email_subscriptions_to_entity } from "drizzle/schema"; 4 - import postgres from "postgres"; 4 + 5 5 import { eq } from "drizzle-orm"; 6 6 import { pool } from "supabase/pool"; 7 7
+10 -4
app/globals.css
··· 362 362 } 363 363 364 364 .selection-highlight { 365 - background-color: Highlight; 365 + background-color: transparent; 366 366 @apply py-[1.5px]; 367 367 } 368 368 369 - .ProseMirror:focus-within .selection-highlight { 369 + .block-focused .selection-highlight { 370 + background-color: Highlight; 371 + } 372 + 373 + .block-focused:focus-within .selection-highlight { 370 374 background-color: transparent; 371 375 } 372 376 ··· 392 396 white-space: normal; 393 397 } 394 398 395 - .multiselected:focus-within .selection-highlight { 399 + .multiselected .block-focused:focus-within .selection-highlight { 396 400 background-color: transparent; 397 401 } 398 402 ··· 611 615 max-height: calc(4.5em + 8px); 612 616 opacity: calc(var(--bg-page-alpha) * 0.7); 613 617 } 614 - .footnote-side-item:not(:hover):not(:focus-within):not(.footnote-side-focused) .footnote-item > div:last-of-type { 618 + .footnote-side-item:not(:hover):not(:focus-within):not(.footnote-side-focused) 619 + .footnote-item 620 + > div:last-of-type { 615 621 display: -webkit-box; 616 622 -webkit-line-clamp: 3; 617 623 -webkit-box-orient: vertical;
+3
components/Blocks/BlueskyPostBlock/BlueskyRichText.tsx
··· 28 28 href={segment.link.uri} 29 29 className="text-accent-contrast hover:underline" 30 30 target="_blank" 31 + rel="noopener noreferrer" 31 32 > 32 33 {segment.text} 33 34 </a>, ··· 42 43 href={`https://bsky.app/profile/${segment.mention.did}`} 43 44 className="text-accent-contrast hover:underline" 44 45 target="_blank" 46 + rel="noopener noreferrer" 45 47 > 46 48 {segment.text} 47 49 </a>, ··· 56 58 href={`https://bsky.app/tag/${segment.tag.tag}`} 57 59 className="text-accent-contrast hover:underline" 58 60 target="_blank" 61 + rel="noopener noreferrer" 59 62 > 60 63 {segment.text} 61 64 </a>,
+1
components/Blocks/ButtonBlock.tsx
··· 41 41 <a 42 42 href={url?.data.value} 43 43 target="_blank" 44 + referrerPolicy="no-referrer" 44 45 className={` ${alignment === "justify" ? "w-full" : "w-fit"}`} 45 46 > 46 47 <ButtonPrimary
+1
components/Blocks/EmbedBlock.tsx
··· 94 94 src={url?.data.value} 95 95 allow="fullscreen" 96 96 loading="lazy" 97 + referrerPolicy="no-referrer" 97 98 ></iframe> 98 99 </BlockLayout> 99 100 {/* <div className="w-full overflow-x-hidden truncate text-xs italic text-accent-contrast">
+1
components/Blocks/ExternalLinkBlock.tsx
··· 73 73 <a 74 74 href={url?.data.value} 75 75 target="_blank" 76 + referrerPolicy="no-referrer" 76 77 className="flex w-full h-full text-primary hover:no-underline no-underline" 77 78 > 78 79 <div className="pt-2 pb-2 px-3 grow min-w-0">
+16 -4
components/Blocks/TextBlock/index.tsx
··· 170 170 <div 171 171 style={{ 172 172 wordBreak: "break-word", 173 - ...(props.type === "heading" ? { fontSize: headingFontSize[headingLevel?.data.value || 1] } : {}), 173 + ...(props.type === "heading" 174 + ? { fontSize: headingFontSize[headingLevel?.data.value || 1] } 175 + : {}), 174 176 }} 175 177 onClick={(e) => { 176 178 let target = e.target as HTMLElement; ··· 290 292 // forces break if a single text string (e.g. a url) spans more than a full line 291 293 style={{ 292 294 wordBreak: "break-word", 293 - fontFamily: props.type === "heading" ? "var(--theme-heading-font)" : "var(--theme-font)", 294 - ...(props.type === "heading" ? { fontSize: headingFontSize[headingLevel?.data.value || 1] } : {}), 295 + fontFamily: 296 + props.type === "heading" 297 + ? "var(--theme-heading-font)" 298 + : "var(--theme-font)", 299 + ...(props.type === "heading" 300 + ? { fontSize: headingFontSize[headingLevel?.data.value || 1] } 301 + : {}), 295 302 }} 296 303 className={` 297 304 ${alignmentClass} 298 305 grow resize-none align-top whitespace-pre-wrap bg-transparent 299 306 outline-hidden 307 + ${focused ? "block-focused" : ""} 300 308 301 309 ${props.type === "heading" ? HeadingStyle[headingLevel?.data.value || 1] : textStyle} 302 310 ${props.className}`} ··· 316 324 props.nextBlock === null ? ( 317 325 // if this is the only block on the page and is empty or is a canvas, show placeholder 318 326 <div 319 - style={props.type === "heading" ? { fontSize: headingFontSize[headingLevel?.data.value || 1] } : undefined} 327 + style={ 328 + props.type === "heading" 329 + ? { fontSize: headingFontSize[headingLevel?.data.value || 1] } 330 + : undefined 331 + } 320 332 className={`${props.className} ${alignmentClass} w-full pointer-events-none absolute top-0 left-0 italic text-tertiary flex flex-col 321 333 ${props.type === "heading" ? HeadingStyle[headingLevel?.data.value || 1] : textStyle} 322 334 `}