feat(workspace): link previews above bullets with a web address (M5)
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m52s

- GET /link-preview?url= fetches the page on the server (https only,
  SSRF-guarded like webhooks), reads Open Graph/Twitter tags, title,
  description and icon from the head, loads picture and icon (no SVG) and
  keeps them in link_previews (migration 6) for 7 days; readers' browsers
  never contact the page. Off in the demo; 300 fetches per address in
  15 minutes.
- Compact card above the bullet (icon, site, title, one line, small
  picture); the duplicate link badge is left out.
- ✕ turns the address into [title](url), so hiding syncs with the note;
  "Link-Vorschauen anzeigen" under appearance switches all off per device.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
sandrodachandClaude Opus 5.5 committed 2026-10-30 00:06:05 +01:00
1 parent 83408d120d
commit 4c26eb2960
16 files changed
+688 -6

No files matched your search

@@ -30,6 +30,8 @@ import { LegalLinks } from '../../components/LegalLinks';
import { AboutModal } from '../../components/AboutModal';
import { ImportCard } from './ImportCard';
import { SessionsPanel } from './SessionsPanel';
import { ToggleSwitch } from '../../components/ToggleSwitch';
import { setLinkPreviewsEnabled, useLinkPreviewsEnabled } from '../../lib/linkPreview';
import { FormSheet } from '../../components/FormSheet';
interface UserSettingsViewProps {
@@ -99,6 +101,7 @@ export const UserSettingsView: React.FC<UserSettingsViewProps> = ({
const [showAbout, setShowAbout] = useState(false);
// The public demo sends nothing and keeps the shared accounts intact (FLAW-33)
const [demoMode, setDemoMode] = useState(false);
const linkPreviewsOn = useLinkPreviewsEnabled();
useEffect(() => {
api
.getAuthStatus()
@@ -774,6 +777,18 @@ export const UserSettingsView: React.FC<UserSettingsViewProps> = ({
</p>
</div>
<div className="p-3.5 rounded-card border border-border bg-canvas flex items-center justify-between gap-4">
<div className="text-xs">
<p className="font-semibold text-main">Link-Vorschauen anzeigen</p>
<p className="text-[11px] text-faint">
Karte mit Titel, Beschreibung und kleinem Bild über Punkten mit einer Web-Adresse. Die Seite wird
dafür vom MindSift-Server abgerufen, nicht von deinem Browser. Gilt für dieses Gerät; einzelne
Vorschauen lassen sich mit ✕ ausblenden.
</p>
</div>
<ToggleSwitch checked={linkPreviewsOn} onChange={setLinkPreviewsEnabled} title="Link-Vorschauen anzeigen" />
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
{themes.map((t) => {
const isSelected = activeTheme === t.id;