From 019283d4ca5aba56b4cb799d380598ed29f2e40c Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 21 Sep 2025 07:21:31 +0200 Subject: [PATCH] tauri - lightbox prompt | file history | store | icons --- .../tauri-app/src/components/ImageGallery.tsx | 127 +++++++++++++++++- 1 file changed, 121 insertions(+), 6 deletions(-) diff --git a/packages/kbot/gui/tauri-app/src/components/ImageGallery.tsx b/packages/kbot/gui/tauri-app/src/components/ImageGallery.tsx index 7b49d9a2..83aeb5ef 100644 --- a/packages/kbot/gui/tauri-app/src/components/ImageGallery.tsx +++ b/packages/kbot/gui/tauri-app/src/components/ImageGallery.tsx @@ -40,6 +40,9 @@ export default function ImageGallery({ const [lightboxLoaded, setLightboxLoaded] = useState(false); const [isPanning, setIsPanning] = useState(false); const [lightboxPrompt, setLightboxPrompt] = useState(''); + const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); + const [skipDeleteConfirm, setSkipDeleteConfirm] = useState(false); + const [rememberChoice, setRememberChoice] = useState(false); const panStartRef = useRef<{ x: number; y: number } | null>(null); // Sync lightbox prompt with history navigation @@ -49,6 +52,65 @@ export default function ImageGallery({ } }, [historyIndex, promptHistory, lightboxOpen]); + // Handle keyboard events for lightbox + useEffect(() => { + if (!lightboxOpen) return; + + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === 'Delete' && !isGenerating && onImageDelete) { + e.preventDefault(); + const safeIndex = Math.max(0, Math.min(currentIndex, images.length - 1)); + if (images[safeIndex]) { + handleDeleteImage(images[safeIndex].path); + } + } + }; + + window.addEventListener('keydown', handleKeyDown); + return () => window.removeEventListener('keydown', handleKeyDown); + }, [lightboxOpen, currentIndex, images, isGenerating, onImageDelete]); + + const handleDeleteImage = (imagePath: string) => { + if (skipDeleteConfirm) { + // Skip confirmation and delete immediately + onImageDelete?.(imagePath); + // Close lightbox if this was the last image or adjust index + if (images.length <= 1) { + setLightboxOpen(false); + } else { + // Adjust current index if needed + const deletingIndex = images.findIndex(img => img.path === imagePath); + if (deletingIndex === currentIndex && currentIndex >= images.length - 1) { + setCurrentIndex(Math.max(0, currentIndex - 1)); + } + } + } else { + // Show confirmation dialog + setShowDeleteConfirm(true); + } + }; + + const confirmDelete = (remember: boolean) => { + const safeIndex = Math.max(0, Math.min(currentIndex, images.length - 1)); + if (images[safeIndex]) { + if (remember) { + setSkipDeleteConfirm(true); + } + onImageDelete?.(images[safeIndex].path); + + // Close lightbox if this was the last image or adjust index + if (images.length <= 1) { + setLightboxOpen(false); + } else { + // Adjust current index if needed + if (currentIndex >= images.length - 1) { + setCurrentIndex(Math.max(0, currentIndex - 1)); + } + } + } + setShowDeleteConfirm(false); + }; + // Reset current index if images change, preventing out-of-bounds errors useEffect(() => { if (images.length > 0 && currentIndex >= images.length) { @@ -414,10 +476,17 @@ export default function ImageGallery({ rows={2} className="w-full bg-white/10 border border-white/30 rounded-lg px-3 py-2 text-white placeholder-white/60 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent backdrop-blur-sm disabled:opacity-50 resize-none" onKeyDown={(e) => { - if (e.key === 'Enter' && lightboxPrompt.trim() && !isGenerating) { - onLightboxPromptSubmit(lightboxPrompt, images[safeIndex].path); - setLightboxPrompt(''); - // Keep lightbox open to show generation progress + if (e.key === 'Enter' && !e.shiftKey && lightboxPrompt.trim() && !isGenerating) { + if (e.ctrlKey) { + // Ctrl+Enter: Submit and keep prompt for iteration + e.preventDefault(); + onLightboxPromptSubmit(lightboxPrompt, images[safeIndex].path); + } else { + // Enter: Submit and clear prompt + e.preventDefault(); + onLightboxPromptSubmit(lightboxPrompt, images[safeIndex].path); + setLightboxPrompt(''); + } } else if (e.key === 'Escape') { setLightboxPrompt(''); } else if (e.ctrlKey && e.key === 'ArrowUp' && navigateHistory) { @@ -427,6 +496,7 @@ export default function ImageGallery({ e.preventDefault(); navigateHistory('down'); } + // Shift+Enter: Allow new line (default textarea behavior) }} onClick={(e) => e.stopPropagation()} /> @@ -471,7 +541,7 @@ export default function ImageGallery({ }} disabled={!lightboxPrompt.trim() || isGenerating} className="bg-blue-600 hover:bg-blue-700 disabled:bg-gray-600 text-white p-2 rounded-lg transition-colors duration-200 disabled:opacity-50 flex items-center justify-center group relative self-start" - title="Generate (Enter)" + title="Generate (Enter to clear, Ctrl+Enter to keep)" > {isGenerating ? (
@@ -497,10 +567,55 @@ export default function ImageGallery({ Generating edit... • ESC to close ) : ( - `${images[safeIndex].path.split(/[/\\]/).pop()} • ${safeIndex + 1} of ${images.length} • Scroll to zoom • Double-click to reset • ESC to close` + `${images[safeIndex].path.split(/[/\\]/).pop()} • ${safeIndex + 1} of ${images.length} • Enter: generate & clear • Ctrl+Enter: generate & keep • Del: delete • ESC to close` )} )} + + {/* Delete Confirmation Dialog */} + {showDeleteConfirm && ( +
+
+

+ Delete Image? +

+

+ Are you sure you want to delete "{images[Math.max(0, Math.min(currentIndex, images.length - 1))]?.path.split(/[/\\]/).pop()}"? +

+
+ +
+ + +
+
+
+
+ )} )}