1
Fork 0
mirror of https://github.com/thegeneralist01/archivr synced 2026-10-09 12:55:00 +02:00

Merge branch 'feat/archive-text-with-title' into integration-all-three

# Conflicts:
#	crates/archivr-server/static/assets/index-C4rpqY7g.js
#	crates/archivr-server/static/index.html
This commit is contained in:
archivr-qa 2026-08-23 19:03:25 +02:00
commit 73b0010d60
No known key found for this signature in database
6 changed files with 112 additions and 51 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -6,8 +6,8 @@
<title>Archivr</title> <title>Archivr</title>
<link rel="icon" type="image/svg+xml" href="/favicon.svg"> <link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="icon" type="image/x-icon" href="/favicon.ico"> <link rel="icon" type="image/x-icon" href="/favicon.ico">
<script type="module" crossorigin src="/assets/index-CO8p3FL2.js"></script> <script type="module" crossorigin src="/assets/index-C4rpqY7g.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BLxoi9rt.css"> <link rel="stylesheet" crossorigin href="/assets/index-BV7QPxEc.css">
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

View file

@ -2,10 +2,12 @@ import VideoPreview from './VideoPreview';
import IframePreview from './IframePreview'; import IframePreview from './IframePreview';
import ImagePreview from './ImagePreview'; import ImagePreview from './ImagePreview';
import TweetPreview from './TweetPreview'; import TweetPreview from './TweetPreview';
import TextPreview from './TextPreview';
const VIDEO_EXTS = new Set(['mp4', 'webm', 'mov', 'mkv', 'avi', 'm4v', 'ogv']); const VIDEO_EXTS = new Set(['mp4', 'webm', 'mov', 'mkv', 'avi', 'm4v', 'ogv']);
const AUDIO_EXTS = new Set(['mp3', 'ogg', 'm4a', 'opus', 'wav', 'flac', 'aac']); const AUDIO_EXTS = new Set(['mp3', 'ogg', 'm4a', 'opus', 'wav', 'flac', 'aac']);
const IMAGE_EXTS = new Set(['jpg', 'jpeg', 'png', 'gif', 'webp', 'avif', 'svg', 'bmp']); const IMAGE_EXTS = new Set(['jpg', 'jpeg', 'png', 'gif', 'webp', 'avif', 'svg', 'bmp']);
const TEXT_EXTS = new Set(['md', 'markdown', 'txt']);
const CONTENT_TYPES = { const CONTENT_TYPES = {
mp4: 'video/mp4', webm: 'video/webm', mov: 'video/quicktime', mp4: 'video/mp4', webm: 'video/webm', mov: 'video/quicktime',
mkv: 'video/x-matroska', avi: 'video/x-msvideo', m4v: 'video/mp4', ogv: 'video/ogg', mkv: 'video/x-matroska', avi: 'video/x-msvideo', m4v: 'video/mp4', ogv: 'video/ogg',
@ -131,6 +133,19 @@ export default function PreviewPanel({ archiveId, entry, detail, fullPage, onXAr
); );
} }
// 6b. Plain text / Markdown
if (TEXT_EXTS.has(ext)) {
return (
<div className="preview-panel" style={{ flex: 1, minHeight: 0, overflow: 'auto' }}>
<TextPreview
src={primaryMediaUrl}
mime={primaryArtifact.mime_type}
title={summary.title}
/>
</div>
);
}
// 7. Image // 7. Image
if (IMAGE_EXTS.has(ext)) { if (IMAGE_EXTS.has(ext)) {
return ( return (

View file

@ -0,0 +1,46 @@
import { useEffect, useState } from 'react'
/**
* Renders the primary_media artifact of a text/document entry as plain text.
*
* v1 intentionally does NOT render Markdown — we don't want a Markdown parser
* dep just to unblock the "no preview available" fallback, and monospace text
* with visible fences reads fine for the note-length content this feature
* captures. Bump to a real renderer if/when Markdown-authored entries grow.
*/
export default function TextPreview({ src, mime, title }) {
const [text, setText] = useState(null)
const [error, setError] = useState(null)
useEffect(() => {
let cancelled = false
setText(null)
setError(null)
fetch(src, { credentials: 'same-origin' })
.then(r => {
if (!r.ok) throw new Error(`HTTP ${r.status}`)
return r.text()
})
.then(t => { if (!cancelled) setText(t) })
.catch(e => { if (!cancelled) setError(e.message || String(e)) })
return () => { cancelled = true }
}, [src])
if (error) {
return (
<div className="text-preview text-preview--error">
Failed to load text: {error}
</div>
)
}
if (text === null) {
return <div className="text-preview text-preview--loading">Loading…</div>
}
return (
<div className="text-preview">
{title && <h1 className="text-preview__title">{title}</h1>}
<pre className="text-preview__body">{text}</pre>
{mime && <div className="text-preview__mime">{mime}</div>}
</div>
)
}

View file

@ -3285,3 +3285,51 @@ body.has-audio-bar { padding-bottom: 56px; }
border: 1px solid var(--line); border: 1px solid var(--line);
border-radius: 4px; border-radius: 4px;
} }
/* ── Text / Markdown preview ────────────────────────────────────────────── */
.text-preview {
padding: 32px 40px 48px;
max-width: 780px;
margin: 0 auto;
font-family: var(--sans);
color: var(--ink);
overflow: auto;
height: 100%;
box-sizing: border-box;
}
.text-preview--loading,
.text-preview--error {
padding: 24px;
color: var(--muted);
font-size: 13px;
}
.text-preview--error { color: var(--accent); }
.text-preview__title {
font-family: var(--serif, var(--sans));
font-size: 22px;
font-weight: 600;
margin: 0 0 20px;
line-height: 1.25;
color: var(--ink);
}
.text-preview__body {
white-space: pre-wrap;
overflow-wrap: anywhere;
word-break: break-word;
font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
font-size: 13.5px;
line-height: 1.65;
color: var(--ink);
background: var(--paper);
border: 1px solid var(--line);
border-radius: var(--r2);
padding: 16px 18px;
margin: 0;
}
.text-preview__mime {
margin-top: 12px;
font-size: 11px;
color: var(--muted);
letter-spacing: .04em;
text-align: right;
}