From 7375ddc7e30b0f8b530156e4b210785a75d02601 Mon Sep 17 00:00:00 2001 From: Jure <44338+hoornet@users.noreply.github.com> Date: Fri, 10 Apr 2026 11:33:13 +0200 Subject: [PATCH] Fix multi-image upload in article editor; add thumbnail lightbox MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - All selected images now upload and insert correctly (was: only last image kept due to stale content closure in loop) - Images inserted as a block with proper newline padding - Thumbnail strip thumbnails are now clickable — opens a lightbox; click overlay to dismiss --- src/components/article/ArticleEditor.tsx | 19 +++++++++++++++++-- src/components/article/MarkdownToolbar.tsx | 21 +++++++++++++-------- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/components/article/ArticleEditor.tsx b/src/components/article/ArticleEditor.tsx index 959c83d..655778d 100644 --- a/src/components/article/ArticleEditor.tsx +++ b/src/components/article/ArticleEditor.tsx @@ -54,6 +54,7 @@ export function ArticleEditor() { const [zenMode, setZenMode] = useState(false); const [zenHint, setZenHint] = useState(false); const zenTextareaRef = useRef(null); + const [lightboxUrl, setLightboxUrl] = useState(null); const toggleZen = useCallback(async () => { const win = getCurrentWindow(); @@ -426,11 +427,11 @@ export function ArticleEditor() {
{inlineImages.length} {inlineImages.length === 1 ? "image" : "images"} {inlineImages.map((img, i) => ( -
+
setLightboxUrl(img.url)}> {img.alt} { (e.target as HTMLImageElement).style.display = "none"; }} />
@@ -438,6 +439,20 @@ export function ArticleEditor() {
)} + {/* Lightbox */} + {lightboxUrl && ( +
setLightboxUrl(null)} + > + e.stopPropagation()} + /> +
+ )} + {/* Content area */}
{mode === "write" ? ( diff --git a/src/components/article/MarkdownToolbar.tsx b/src/components/article/MarkdownToolbar.tsx index 60e6a98..7f2c457 100644 --- a/src/components/article/MarkdownToolbar.tsx +++ b/src/components/article/MarkdownToolbar.tsx @@ -151,19 +151,24 @@ export function MarkdownToolbar({ textareaRef, content, setContent, setUploading setUploading?.(true); setError?.(null); try { + const mimeMap: Record = { + jpg: "image/jpeg", jpeg: "image/jpeg", png: "image/png", gif: "image/gif", + webp: "image/webp", svg: "image/svg+xml", + }; + const snippets: string[] = []; for (const filePath of paths) { const bytes = await readFile(filePath); const fileName = filePath.split(/[\\/]/).pop() || "image.png"; const ext = fileName.split(".").pop()?.toLowerCase() || "png"; - const mimeMap: Record = { - jpg: "image/jpeg", jpeg: "image/jpeg", png: "image/png", gif: "image/gif", - webp: "image/webp", svg: "image/svg+xml", - }; const url = await uploadBytes(new Uint8Array(bytes), fileName, mimeMap[ext] || "image/png"); - const textarea = textareaRef.current; - if (textarea) { - applyMarkdown(textarea, "image", content, setContent, `![${fileName}](${url})`); - } + snippets.push(`![${fileName}](${url})`); + } + const textarea = textareaRef.current; + if (textarea && snippets.length > 0) { + const cursorPos = textarea.selectionStart ?? content.length; + const needsLeadingNewline = cursorPos > 0 && content[cursorPos - 1] !== "\n"; + const block = (needsLeadingNewline ? "\n\n" : "") + snippets.join("\n\n") + "\n\n"; + applyMarkdown(textarea, "image", content, setContent, block); } } finally { setUploading?.(false);