mirror of
https://github.com/hoornet/vega.git
synced 2026-06-08 06:01:57 -07:00
Fix multi-image upload in article editor; add thumbnail lightbox
- 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
This commit is contained in:
@@ -54,6 +54,7 @@ export function ArticleEditor() {
|
||||
const [zenMode, setZenMode] = useState(false);
|
||||
const [zenHint, setZenHint] = useState(false);
|
||||
const zenTextareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
const [lightboxUrl, setLightboxUrl] = useState<string | null>(null);
|
||||
|
||||
const toggleZen = useCallback(async () => {
|
||||
const win = getCurrentWindow();
|
||||
@@ -426,11 +427,11 @@ export function ArticleEditor() {
|
||||
<div className="flex items-center gap-2 px-6 py-2 border-b border-border bg-bg-raised/50 overflow-x-auto shrink-0">
|
||||
<span className="text-text-dim text-[10px] shrink-0">{inlineImages.length} {inlineImages.length === 1 ? "image" : "images"}</span>
|
||||
{inlineImages.map((img, i) => (
|
||||
<div key={i} className="relative shrink-0 group">
|
||||
<div key={i} className="relative shrink-0 group cursor-zoom-in" onClick={() => setLightboxUrl(img.url)}>
|
||||
<img
|
||||
src={img.url}
|
||||
alt={img.alt}
|
||||
className="h-12 w-auto rounded-sm border border-border object-cover"
|
||||
className="h-12 w-auto rounded-sm border border-border object-cover group-hover:border-accent transition-colors"
|
||||
onError={(e) => { (e.target as HTMLImageElement).style.display = "none"; }}
|
||||
/>
|
||||
</div>
|
||||
@@ -438,6 +439,20 @@ export function ArticleEditor() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Lightbox */}
|
||||
{lightboxUrl && (
|
||||
<div
|
||||
className="fixed inset-0 z-50 bg-black/80 flex items-center justify-center cursor-zoom-out"
|
||||
onClick={() => setLightboxUrl(null)}
|
||||
>
|
||||
<img
|
||||
src={lightboxUrl}
|
||||
className="max-w-[90vw] max-h-[90vh] rounded-md shadow-2xl object-contain"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Content area */}
|
||||
<div className="flex-1 overflow-y-auto px-6 pb-6">
|
||||
{mode === "write" ? (
|
||||
|
||||
@@ -151,19 +151,24 @@ export function MarkdownToolbar({ textareaRef, content, setContent, setUploading
|
||||
setUploading?.(true);
|
||||
setError?.(null);
|
||||
try {
|
||||
const mimeMap: Record<string, string> = {
|
||||
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<string, string> = {
|
||||
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, ``);
|
||||
}
|
||||
snippets.push(``);
|
||||
}
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user