diff --git a/packages/kbot/cat.png b/packages/kbot/cat.png deleted file mode 100644 index 87113636..00000000 Binary files a/packages/kbot/cat.png and /dev/null differ diff --git a/packages/kbot/dist-in/commands/images.js b/packages/kbot/dist-in/commands/images.js index edc6d0dd..b16467c2 100644 --- a/packages/kbot/dist-in/commands/images.js +++ b/packages/kbot/dist-in/commands/images.js @@ -2,7 +2,7 @@ import { z } from 'zod'; import * as path from 'node:path'; import { sync as write } from '@polymech/fs/write'; import { sync as exists } from '@polymech/fs/exists'; -import { readFileSync } from 'node:fs'; +import { readFileSync, statSync, unlinkSync } from 'node:fs'; import { variables } from '../variables.js'; import { resolve } from '@polymech/commons'; import { isArray, isString } from '@polymech/core/primitives'; @@ -135,7 +135,7 @@ async function launchGuiAndGetPrompt(argv) { cmd: 'forward_image_to_frontend', base64, mimeType, - filename + filename: imagePath }; tauriProcess.stdin?.write(JSON.stringify(imageResponse) + '\n'); logger.info(`📤 Sent image data: ${filename} (${Math.round(base64.length / 1024)}KB)`); @@ -146,13 +146,75 @@ async function launchGuiAndGetPrompt(argv) { } } } + else if (message.type === 'delete_request') { + logger.info('📨 Received delete request from GUI'); + const pathToDelete = message.path; + if (pathToDelete && isString(pathToDelete)) { + try { + if (exists(pathToDelete)) { + unlinkSync(pathToDelete); + logger.info(`✅ File deleted successfully: ${pathToDelete}`); + const successResponse = { + cmd: 'file_deleted_successfully', + path: pathToDelete + }; + tauriProcess.stdin?.write(JSON.stringify(successResponse) + '\n'); + } + else { + logger.warn(`⚠️ File not found for deletion: ${pathToDelete}`); + const errorResponse = { + cmd: 'file_deletion_error', + path: pathToDelete, + error: 'File not found on server.' + }; + tauriProcess.stdin?.write(JSON.stringify(errorResponse) + '\n'); + } + } + catch (error) { + logger.error(`❌ Failed to delete file: ${pathToDelete}`, error.message); + const errorResponse = { + cmd: 'file_deletion_error', + path: pathToDelete, + error: error.message + }; + tauriProcess.stdin?.write(JSON.stringify(errorResponse) + '\n'); + } + } + else { + logger.error('Invalid delete request from GUI, path is missing.'); + } + } else if (message.type === 'generate_request') { logger.info('📨 Received generation request from GUI'); // Process the generation request using our existing image generation logic try { const genPrompt = message.prompt; const genFiles = message.files || []; - const genDst = message.dst; + // --- New logic for destination path --- + let dstDir; + if (argv.dst) { + const absoluteDst = path.resolve(argv.dst); + const dstStat = exists(absoluteDst) ? statSync(absoluteDst) : null; + if (dstStat && dstStat.isDirectory()) { + dstDir = absoluteDst; + } + else { + dstDir = path.dirname(absoluteDst); + } + } + else if (genFiles.length > 0) { + dstDir = path.dirname(genFiles[0]); + } + else { + dstDir = process.cwd(); // fallback to current working dir + } + const baseFileName = genFiles.length > 0 + ? path.basename(genFiles[0], path.extname(genFiles[0])) + : 'generated'; + const newFileName = `${baseFileName}_gen_0.png`; + const finalDstPath = path.resolve(dstDir, newFileName); + logger.info(`📝 Determined destination path for generated image: ${finalDstPath}`); + // --- End new logic --- logger.info(`🎨 Starting image generation: "${genPrompt}"`); let imageBuffer = null; if (genFiles.length > 0) { @@ -162,7 +224,7 @@ async function launchGuiAndGetPrompt(argv) { ...argv, prompt: genPrompt, include: genFiles, - dst: genDst + dst: finalDstPath // Use the new path }); imageBuffer = await editImage(genPrompt, genFiles, parsedOptions); } @@ -172,21 +234,23 @@ async function launchGuiAndGetPrompt(argv) { const parsedOptions = ImageOptionsSchema().parse({ ...argv, prompt: genPrompt, - dst: genDst + dst: finalDstPath // Use the new path }); imageBuffer = await createImage(genPrompt, parsedOptions); } if (imageBuffer) { + write(finalDstPath, imageBuffer); + logger.info(`✅ Image saved to: ${finalDstPath}`); // Send the generated image back to the GUI (chat mode) const base64Result = imageBuffer.toString('base64'); const imageResponse = { cmd: 'forward_image_to_frontend', base64: base64Result, mimeType: 'image/png', - filename: path.basename(genDst) + filename: finalDstPath }; tauriProcess.stdin?.write(JSON.stringify(imageResponse) + '\n'); - logger.info(`✅ Generated image sent to GUI: ${genDst}`); + logger.info(`✅ Generated image sent to GUI: ${path.basename(finalDstPath)}`); } else { logger.error('❌ Failed to generate image'); @@ -315,4 +379,4 @@ export const imageCommand = async (argv) => { logger.error('Failed to parse options or generate image:', error.message, error.issues, error.stack); } }; -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW1hZ2VzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvbW1hbmRzL2ltYWdlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsQ0FBQyxFQUFFLE1BQU0sS0FBSyxDQUFDO0FBQ3hCLE9BQU8sS0FBSyxJQUFJLE1BQU0sV0FBVyxDQUFDO0FBQ2xDLE9BQU8sRUFBRSxJQUFJLElBQUksS0FBSyxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDbkQsT0FBTyxFQUFFLElBQUksSUFBSSxNQUFNLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUNyRCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBQ3ZDLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUM1QyxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFFNUMsT0FBTyxFQUFFLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUU5RCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDakQsT0FBTyxFQUFFLFdBQVcsRUFBRSxTQUFTLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUNqRSxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ3hDLE9BQU8sRUFBRSxNQUFNLElBQUksYUFBYSxFQUFFLE1BQU0sY0FBYyxDQUFDO0FBQ3ZELE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUMzQyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sY0FBYyxDQUFDO0FBRTFDLFNBQVMsYUFBYTtJQUVsQixzRUFBc0U7SUFDdEUsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLEdBQUcsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ2xFLG9GQUFvRjtJQUNw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 \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW1hZ2VzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvbW1hbmRzL2ltYWdlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsQ0FBQyxFQUFFLE1BQU0sS0FBSyxDQUFDO0FBQ3hCLE9BQU8sS0FBSyxJQUFJLE1BQU0sV0FBVyxDQUFDO0FBQ2xDLE9BQU8sRUFBRSxJQUFJLElBQUksS0FBSyxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDbkQsT0FBTyxFQUFFLElBQUksSUFBSSxNQUFNLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUNyRCxPQUFPLEVBQ0gsWUFBWSxFQUNaLFFBQVEsRUFDUixVQUFVLEVBQ2IsTUFBTSxTQUFTLENBQUM7QUFDakIsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQzVDLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUU1QyxPQUFPLEVBQUUsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBRTlELE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUNqRCxPQUFPLEVBQUUsV0FBVyxFQUFFLFNBQVMsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ2pFLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDeEMsT0FBTyxFQUFFLE1BQU0sSUFBSSxhQUFhLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFDdkQsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQzNDLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFFMUMsU0FBUyxhQUFhO0lBRWxCLHNFQUFzRTtJQUN0RSxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksR0FBRyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsUUFBUSxDQUFDLENB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 \ No newline at end of file diff --git a/packages/kbot/dist/win-64/tauri-app.exe b/packages/kbot/dist/win-64/tauri-app.exe index f4749854..c52a2ab4 100644 Binary files a/packages/kbot/dist/win-64/tauri-app.exe and b/packages/kbot/dist/win-64/tauri-app.exe differ diff --git a/packages/kbot/fat_cat.png b/packages/kbot/fat_cat.png deleted file mode 100644 index ab07da1b..00000000 Binary files a/packages/kbot/fat_cat.png and /dev/null differ diff --git a/packages/kbot/gui/tauri-app/src-tauri/src/lib.rs b/packages/kbot/gui/tauri-app/src-tauri/src/lib.rs index 7f0431f9..85e245fd 100644 --- a/packages/kbot/gui/tauri-app/src-tauri/src/lib.rs +++ b/packages/kbot/gui/tauri-app/src-tauri/src/lib.rs @@ -278,6 +278,22 @@ fn forward_image_to_frontend(base64: String, mime_type: String, filename: String Ok(()) } +#[tauri::command] +fn request_file_deletion(path: String) -> Result<(), String> { + eprintln!("[RUST LOG]: request_file_deletion command called."); + eprintln!("[RUST LOG]: - Path: {}", path); + + let request = serde_json::json!({ + "type": "delete_request", + "path": path, + }); + + println!("{}", serde_json::to_string(&request).unwrap()); + eprintln!("[RUST LOG]: Deletion request sent to images.ts"); + + Ok(()) +} + #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { let app = tauri::Builder::default() @@ -302,7 +318,8 @@ pub fn run() { request_config_from_images, forward_config_to_frontend, forward_image_to_frontend, - generate_image_via_backend + generate_image_via_backend, + request_file_deletion ]) .setup(|app| { #[cfg(debug_assertions)] // only include this code on debug builds @@ -405,6 +422,25 @@ pub fn run() { eprintln!("[RUST LOG]: Generation complete emitted successfully"); } } + "file_deleted_successfully" => { + if let Some(path) = command.get("path").and_then(|v| v.as_str()) { + eprintln!("[RUST LOG]: Received confirmation of file deletion: {}", path); + if let Err(e) = app_handle.emit("file-deleted-successfully", &serde_json::json!({ "path": path })) { + eprintln!("[RUST LOG]: Failed to emit file-deleted-successfully: {}", e); + } + } + } + "file_deletion_error" => { + if let (Some(path), Some(error)) = ( + command.get("path").and_then(|v| v.as_str()), + command.get("error").and_then(|v| v.as_str()) + ) { + eprintln!("[RUST LOG]: Received file deletion error for {}: {}", path, error); + if let Err(e) = app_handle.emit("file-deletion-error", &serde_json::json!({ "path": path, "error": error })) { + eprintln!("[RUST LOG]: Failed to emit file-deletion-error: {}", e); + } + } + } _ => { eprintln!("[RUST LOG]: Unknown command: {}", cmd); } diff --git a/packages/kbot/gui/tauri-app/src/App.tsx b/packages/kbot/gui/tauri-app/src/App.tsx index 272fafea..bc7d5bdc 100644 --- a/packages/kbot/gui/tauri-app/src/App.tsx +++ b/packages/kbot/gui/tauri-app/src/App.tsx @@ -29,6 +29,12 @@ function App() { const [messageToSend, setMessageToSend] = useState(""); const [generationTimeoutId, setGenerationTimeoutId] = useState(null); + const deleteFilePermanently = async (pathToDelete: string) => { + addDebugMessage('info', `Requesting deletion of file: ${pathToDelete}`); + // This will be the new tauri command + await tauriApi.requestFileDeletion({ path: pathToDelete }); + }; + const generateDefaultDst = (fileCount: number, firstFilePath?: string) => { if (fileCount === 1 && firstFilePath) { const parsedPath = firstFilePath.split(/[/\\]/).pop() || 'image'; @@ -91,23 +97,17 @@ function App() { const addFiles = async (newPaths: string[]) => { const uniqueNewPaths = newPaths.filter(newPath => !files.some(f => f.path === newPath)); const newImageFiles: ImageFile[] = []; - + for (const path of uniqueNewPaths) { try { - const relativePath = await tauriApi.resolvePathRelativeToHome(path); - if (!relativePath) { - console.warn(`Could not resolve relative path for: ${path}`); - continue; - } - - const buffer = await tauriApi.fs.readFile(relativePath, { baseDir: tauriApi.fs.BaseDirectory().Home }); - + const buffer = await tauriApi.fs.readFile(path); + const base64 = arrayBufferToBase64(Array.from(buffer)); - const mimeType = path.toLowerCase().endsWith('.png') ? 'image/png' : - path.toLowerCase().endsWith('.jpg') || path.toLowerCase().endsWith('.jpeg') ? 'image/jpeg' : + const mimeType = path.toLowerCase().endsWith('.png') ? 'image/png' : + path.toLowerCase().endsWith('.jpg') || path.toLowerCase().endsWith('.jpeg') ? 'image/jpeg' : 'image/png'; const src = `data:${mimeType};base64,${base64}`; - + newImageFiles.push({ path, src }); } catch (e) { const errorMessage = e instanceof Error ? e.message : JSON.stringify(e); @@ -115,7 +115,7 @@ function App() { tauriApi.logErrorToConsole(`[Frontend Error] Failed to read file ${path}: ${errorMessage}`); } } - + setFiles(prevFiles => [...prevFiles, ...newImageFiles]); }; @@ -128,9 +128,9 @@ function App() { }; const toggleImageSelection = (imagePath: string) => { - setFiles(prev => - prev.map(file => - file.path === imagePath + setFiles(prev => + prev.map(file => + file.path === imagePath ? { ...file, selected: !file.selected } : file ) @@ -183,7 +183,7 @@ function App() { setIsGenerating(true); addDebugMessage('info', `🎨 Starting image generation via backend: "${promptText}"`); - + // Add placeholder image with spinner to the files grid const placeholderFile: ImageFile = { path: `generating_${Date.now()}`, @@ -198,43 +198,43 @@ function App() { `) }; - + setFiles(prev => [...prev, placeholderFile]); - + try { // Use the images.ts backend instead of direct API calls const filePaths = includeImages.map(img => img.path); const genDst = dst || `generated_${Date.now()}.png`; - + addDebugMessage('info', 'Sending generation request to images.ts backend', { prompt: promptText, files: filePaths, dst: genDst }); - + // Send generation request via Tauri command await tauriApi.generateImageViaBackend({ - prompt: promptText, + prompt: promptText, files: filePaths, dst: genDst }); - + addDebugMessage('info', '📤 Generation request sent to backend'); - + // Clear any existing timeout if (generationTimeoutId) { clearTimeout(generationTimeoutId); } - + const timeoutId = setTimeout(() => { addDebugMessage('warn', '⏰ Generation timeout - resetting state'); setIsGenerating(false); setFiles(prev => prev.filter(file => !file.path.startsWith('generating_'))); setGenerationTimeoutId(null); }, 30000); - + setGenerationTimeoutId(timeoutId); - + } catch (error) { addDebugMessage('error', 'Failed to send generation request', { error: error instanceof Error ? error.message : JSON.stringify(error) @@ -267,7 +267,7 @@ function App() { const toggleTheme = () => { const newDarkMode = !isDarkMode; setIsDarkMode(newDarkMode); - + if (newDarkMode) { document.documentElement.classList.add('dark'); localStorage.setItem('theme', 'dark'); @@ -292,13 +292,13 @@ function App() { }, []); async function submit() { - + if (apiKey) { // Generate image via backend (always chat mode now) // Use selected images if any, otherwise use all files const selectedImages = getSelectedImages(); const imagesToUse = selectedImages.length > 0 ? selectedImages : files.filter(f => !f.path.startsWith('generating_')); - + await generateImage(prompt, imagesToUse); // Don't clear prompt - let user iterate } else { @@ -309,17 +309,17 @@ function App() { const clearDebugMessages = async () => { setDebugMessages([]); await tauriApi.clearDebugMessages(); - addDebugMessage('info', 'Debug messages cleared'); + addDebugMessage('info', 'Debug messages cleared'); }; const sendIPCMessage = async (messageType: string, data: any) => { await tauriApi.sendIPCMessage(messageType, data); - addDebugMessage('info', `IPC message sent: ${messageType}`, data); + addDebugMessage('info', `IPC message sent: ${messageType}`, data); }; const sendMessageToImages = async () => { if (!messageToSend.trim()) return; - + const message = { message: messageToSend, timestamp: Date.now(), @@ -333,7 +333,7 @@ function App() { const errorMessage = error instanceof Error ? error.message : JSON.stringify(error); addDebugMessage('error', `Failed to send message: ${errorMessage}`); } - + // Clear the input setMessageToSend(''); }; @@ -351,7 +351,7 @@ function App() { const fileArray = Array.from(target.files); const newImageFiles: ImageFile[] = []; let loadedCount = 0; - + fileArray.forEach(file => { const reader = new FileReader(); reader.onload = (e) => { @@ -373,13 +373,13 @@ function App() { input.click(); return; } - + try { if (!tauriApi.dialog.open) { console.error('Open function not available'); return; } - + const selected = await tauriApi.dialog.open({ multiple: true, filters: [{ @@ -391,7 +391,7 @@ function App() { addFiles(selected); } } catch (e) { - console.error('File picker error:', e); + console.error('File picker error:', e); tauriApi.logErrorToConsole(`[Frontend Error] File picker error: ${JSON.stringify(e)}`); } } @@ -400,7 +400,7 @@ function App() { try { // Extract current filename from dst for default, or use smart default const currentFilename = dst.split(/[/\\]/).pop() || generateDefaultDst(files.length, files[0]?.path); - + const selected = await tauriApi.dialog.save({ defaultPath: currentFilename, filters: [{ @@ -408,12 +408,12 @@ function App() { extensions: ['png', 'jpg'] }] }); - + if (selected) { setDst(selected); } } catch (e) { - console.error('Save dialog error:', e); + console.error('Save dialog error:', e); tauriApi.logErrorToConsole(`[Frontend Error] Save dialog error: ${JSON.stringify(e)}`); } } @@ -425,7 +425,7 @@ function App() {
- +
{/* Debug Panel */} diff --git a/packages/kbot/gui/tauri-app/src/components/ImageGallery.tsx b/packages/kbot/gui/tauri-app/src/components/ImageGallery.tsx index 97536bb0..25db5cb7 100644 --- a/packages/kbot/gui/tauri-app/src/components/ImageGallery.tsx +++ b/packages/kbot/gui/tauri-app/src/components/ImageGallery.tsx @@ -5,6 +5,7 @@ interface ImageGalleryProps { images: ImageFile[]; onImageSelect?: (imagePath: string) => void; onImageRemove?: (imagePath: string) => void; + onImageDelete?: (imagePath: string) => void; showSelection?: boolean; } @@ -12,6 +13,7 @@ export default function ImageGallery({ images, onImageSelect, onImageRemove, + onImageDelete, showSelection = false }: ImageGalleryProps) { const [currentIndex, setCurrentIndex] = useState(0); @@ -74,10 +76,12 @@ export default function ImageGallery({ setCurrentIndex(index); // If it's a generated image and selection is enabled, also toggle selection + /* const isGenerated = imagePath.startsWith('generated_'); - if (showSelection && isGenerated && onImageSelect) { + if (showSelection && onImageSelect) { onImageSelect(imagePath); } + */ }; if (images.length === 0) { @@ -202,11 +206,29 @@ export default function ImageGallery({ onImageRemove(image.path); }} className="absolute top-1 right-1 bg-red-500/90 hover:bg-red-600 text-white rounded-full w-5 h-5 flex items-center justify-center text-xs opacity-0 group-hover:opacity-100 transition-all duration-200" - title="Remove" + title="Remove from view" > × )} + + {/* Delete button (permanent) */} + {!thumbIsGenerating && onImageDelete && ( + + )} ); })} diff --git a/packages/kbot/gui/tauri-app/src/components/PromptForm.tsx b/packages/kbot/gui/tauri-app/src/components/PromptForm.tsx index 755b5cb4..c5765019 100644 --- a/packages/kbot/gui/tauri-app/src/components/PromptForm.tsx +++ b/packages/kbot/gui/tauri-app/src/components/PromptForm.tsx @@ -18,6 +18,7 @@ interface PromptFormProps { saveAndClose: () => void; submit: () => void; addImageFromUrl: (url: string) => void; + onImageDelete?: (path: string) => void; } const PromptForm: React.FC = ({ @@ -36,7 +37,10 @@ const PromptForm: React.FC = ({ saveAndClose, submit, addImageFromUrl, + onImageDelete }) => { + const selectedCount = getSelectedImages().length; + return (
= ({

Images ({files.length}) - {getSelectedImages().length > 0 && ( + {selectedCount > 0 && ( - • {getSelectedImages().length} selected + • {selectedCount} selected )}

@@ -137,6 +141,7 @@ const PromptForm: React.FC = ({ onImageSelect={toggleImageSelection} onImageRemove={removeFile} showSelection={true} + onImageDelete={onImageDelete} />
diff --git a/packages/kbot/gui/tauri-app/src/constants.ts b/packages/kbot/gui/tauri-app/src/constants.ts index dd2f2bd9..30c4823a 100644 --- a/packages/kbot/gui/tauri-app/src/constants.ts +++ b/packages/kbot/gui/tauri-app/src/constants.ts @@ -11,6 +11,7 @@ export enum TauriCommand { CLEAR_DEBUG_MESSAGES = 'clear_debug_messages', SEND_IPC_MESSAGE = 'send_ipc_message', SEND_MESSAGE_TO_STDOUT = 'send_message_to_stdout', + REQUEST_FILE_DELETION = 'request_file_deletion', } export enum TauriEvent { @@ -18,4 +19,6 @@ export enum TauriEvent { IMAGE_RECEIVED = 'image-received', GENERATION_ERROR = 'generation-error', GENERATION_COMPLETE = 'generation-complete', + FILE_DELETED_SUCCESSFULLY = 'file-deleted-successfully', + FILE_DELETION_ERROR = 'file-deletion-error', } diff --git a/packages/kbot/gui/tauri-app/src/hooks/useTauriListeners.ts b/packages/kbot/gui/tauri-app/src/hooks/useTauriListeners.ts index 15e69b42..9f3d5a3c 100644 --- a/packages/kbot/gui/tauri-app/src/hooks/useTauriListeners.ts +++ b/packages/kbot/gui/tauri-app/src/hooks/useTauriListeners.ts @@ -31,114 +31,126 @@ export function useTauriListeners({ prompt }: TauriListenersProps) { useEffect(() => { - const initializeApp = async () => { + let unlistenConfig: (() => void) | undefined; + let unlistenImage: (() => void) | undefined; + let unlistenError: (() => void) | undefined; + let unlistenComplete: (() => void) | undefined; + let unlistenDeleted: (() => void) | undefined; + let unlistenDeleteError: (() => void) | undefined; + + const setupListeners = async () => { await tauriApi.ensureTauriApi(); if (tauriApi.isTauri()) { addDebugMessage('info', 'IPC system initialized successfully'); } - const setupTauriEventListeners = async () => { - if (tauriApi.isTauri()) { - try { - await tauriApi.listen(TauriEvent.CONFIG_RECEIVED, (event: any) => { - const data = event.payload; - if (data.prompt) setPrompt(data.prompt); - if (data.dst) setDst(data.dst); - if (data.apiKey) setApiKey(data.apiKey); - setIpcInitialized(true); - addDebugMessage('info', '📨 Config received from images.ts', { - hasPrompt: !!data.prompt, - hasDst: !!data.dst, - hasApiKey: !!data.apiKey, - fileCount: data.files?.length || 0, - }); - }); + const listeners = await Promise.all([ + tauriApi.listen(TauriEvent.CONFIG_RECEIVED, (event: any) => { + const data = event.payload; + if (data.prompt) setPrompt(data.prompt); + if (data.dst) setDst(data.dst); + if (data.apiKey) setApiKey(data.apiKey); + setIpcInitialized(true); + addDebugMessage('info', '📨 Config received from images.ts', { + hasPrompt: !!data.prompt, + hasDst: !!data.dst, + hasApiKey: !!data.apiKey, + fileCount: data.files?.length || 0, + }); + }), + tauriApi.listen(TauriEvent.IMAGE_RECEIVED, (event: any) => { + const imageData = event.payload; + addDebugMessage('debug', '🖼️ Processing image data', { + filename: imageData.filename, + mimeType: imageData.mimeType, + base64Length: imageData.base64?.length, + hasValidData: !!(imageData.base64 && imageData.mimeType && imageData.filename), + }); - await tauriApi.listen(TauriEvent.IMAGE_RECEIVED, (event: any) => { - const imageData = event.payload; - addDebugMessage('debug', '🖼️ Processing image data', { - filename: imageData.filename, - mimeType: imageData.mimeType, - base64Length: imageData.base64?.length, - hasValidData: !!(imageData.base64 && imageData.mimeType && imageData.filename), + if (imageData.base64 && imageData.mimeType && imageData.filename) { + const src = `data:${imageData.mimeType};base64,${imageData.base64}`; + const hasGeneratingPlaceholder = document.querySelector('[src^="data:image/svg+xml"]'); // A bit hacky, but avoids depending on files state + const isGeneratedImage = isGenerating || hasGeneratingPlaceholder || imageData.filename.includes('_out') || imageData.filename.includes('generated_'); + + if (isGeneratedImage) { + const generatedImageFile: ImageFile = { path: `generated_${imageData.filename}`, src }; + setFiles(prev => { + const withoutPlaceholder = prev.filter(file => !file.path.startsWith('generating_') && !file.path.endsWith(imageData.filename) && file.path !== `generated_${imageData.filename}`); + return [...withoutPlaceholder, generatedImageFile]; }); - if (imageData.base64 && imageData.mimeType && imageData.filename) { - const src = `data:${imageData.mimeType};base64,${imageData.base64}`; - const hasGeneratingPlaceholder = document.querySelector('[src^="data:image/svg+xml"]'); // A bit hacky, but avoids depending on files state - const isGeneratedImage = isGenerating || hasGeneratingPlaceholder || imageData.filename.includes('_out') || imageData.filename.includes('generated_'); - - if (isGeneratedImage) { - const generatedImageFile: ImageFile = { path: `generated_${imageData.filename}`, src }; - setFiles(prev => { - const withoutPlaceholder = prev.filter(file => !file.path.startsWith('generating_') && !file.path.endsWith(imageData.filename) && file.path !== `generated_${imageData.filename}`); - return [...withoutPlaceholder, generatedImageFile]; - }); - - if (generationTimeoutId) { - clearTimeout(generationTimeoutId); - setGenerationTimeoutId(null); - } - setIsGenerating(false); - addDebugMessage('info', '✅ Generated image added to files', { filename: imageData.filename, prompt }); - } else { - const newImageFile = { path: imageData.filename, src }; - setFiles(prevFiles => { - const exists = prevFiles.some(f => f.path === imageData.filename); - if (!exists) { - addDebugMessage('info', `📁 Adding input image: ${imageData.filename}`); - return [...prevFiles, newImageFile]; - } - addDebugMessage('warn', `🔄 Image already exists: ${imageData.filename}`); - return prevFiles; - }); - } - } else { - addDebugMessage('error', '❌ Invalid image data received', { - hasBase64: !!imageData.base64, - hasMimeType: !!imageData.mimeType, - hasFilename: !!imageData.filename, - }); + if (generationTimeoutId) { + clearTimeout(generationTimeoutId); + setGenerationTimeoutId(null); } - }); - - await tauriApi.listen(TauriEvent.GENERATION_ERROR, (event: any) => { - const errorData = event.payload; - addDebugMessage('error', '❌ Generation failed', errorData); setIsGenerating(false); - setFiles(prev => prev.filter(file => !file.path.startsWith('generating_'))); - }); - - await tauriApi.listen(TauriEvent.GENERATION_COMPLETE, (event: any) => { - const completionData = event.payload; - addDebugMessage('info', '✅ Simple mode: Image generation completed', { - dst: completionData.dst, - prompt: completionData.prompt - }); - setIsGenerating(false); - setFiles(prev => prev.filter(file => !file.path.startsWith('generating_'))); - }); - - addDebugMessage('info', 'Tauri event listeners set up'); - - try { - await tauriApi.requestConfigFromImages(); - addDebugMessage('info', 'Config request sent to images.ts'); - } catch (e) { - addDebugMessage('error', `Failed to request config: ${e}`); + addDebugMessage('info', '✅ Generated image added to files', { filename: imageData.filename, prompt }); + } else { + const newImageFile = { path: imageData.filename, src }; + setFiles(prevFiles => { + const exists = prevFiles.some(f => f.path === imageData.filename); + if (!exists) { + addDebugMessage('info', `📁 Adding input image: ${imageData.filename}`); + return [...prevFiles, newImageFile]; + } + addDebugMessage('warn', `🔄 Image already exists: ${imageData.filename}`); + return prevFiles; + }); } - } catch (error) { - addDebugMessage('error', `Failed to set up event listeners: ${error}`); + } else { + addDebugMessage('error', '❌ Invalid image data received', { + hasBase64: !!imageData.base64, + hasMimeType: !!imageData.mimeType, + hasFilename: !!imageData.filename, + }); } - } else { - addDebugMessage('warn', 'Tauri event listeners not available - running in browser mode'); - } - }; + }), + tauriApi.listen(TauriEvent.GENERATION_ERROR, (event: any) => { + const errorData = event.payload; + addDebugMessage('error', '❌ Generation failed', errorData); + setIsGenerating(false); + setFiles(prev => prev.filter(file => !file.path.startsWith('generating_'))); + }), + tauriApi.listen(TauriEvent.GENERATION_COMPLETE, (event: any) => { + const completionData = event.payload; + addDebugMessage('info', '✅ Simple mode: Image generation completed', { + dst: completionData.dst, + prompt: completionData.prompt + }); + setIsGenerating(false); + setFiles(prev => prev.filter(file => !file.path.startsWith('generating_'))); + }), + tauriApi.listen(TauriEvent.FILE_DELETED_SUCCESSFULLY, (event: any) => { + const deletedPath = event.payload.path; + addDebugMessage('info', `✅ File deleted successfully: ${deletedPath}`); + setFiles(prevFiles => prevFiles.filter(file => file.path !== deletedPath)); + }), + tauriApi.listen(TauriEvent.FILE_DELETION_ERROR, (event: any) => { + const { path, error } = event.payload; + addDebugMessage('error', `Failed to delete file: ${path}`, { error }); + }) + ]); - setTimeout(setupTauriEventListeners, 500); + [unlistenConfig, unlistenImage, unlistenError, unlistenComplete, unlistenDeleted, unlistenDeleteError] = listeners; + + try { + await tauriApi.requestConfigFromImages(); + addDebugMessage('info', 'Config request sent to images.ts'); + } catch (e) { + addDebugMessage('error', `Failed to request config: ${e}`); + } }; - setTimeout(initializeApp, 200); + setupListeners(); + + return () => { + unlistenConfig?.(); + unlistenImage?.(); + unlistenError?.(); + unlistenComplete?.(); + unlistenDeleted?.(); + unlistenDeleteError?.(); + }; }, []); // Empty dependency array to run only once on mount } diff --git a/packages/kbot/gui/tauri-app/src/lib/tauriApi.ts b/packages/kbot/gui/tauri-app/src/lib/tauriApi.ts index 33ce6977..8afa34a0 100644 --- a/packages/kbot/gui/tauri-app/src/lib/tauriApi.ts +++ b/packages/kbot/gui/tauri-app/src/lib/tauriApi.ts @@ -141,4 +141,6 @@ export const tauriApi = { sendIPCMessage: (messageType: string, data: any) => safeInvoke(TauriCommand.SEND_IPC_MESSAGE, { messageType, data }), + requestFileDeletion: (data: { path: string }) => + safeInvoke(TauriCommand.REQUEST_FILE_DELETION, data), }; diff --git a/packages/kbot/gui/tauri-app/tsconfig.json b/packages/kbot/gui/tauri-app/tsconfig.json index 9bdaa778..7604d620 100644 --- a/packages/kbot/gui/tauri-app/tsconfig.json +++ b/packages/kbot/gui/tauri-app/tsconfig.json @@ -16,8 +16,8 @@ /* Linting */ "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, + "noUnusedLocals": false, + "noUnusedParameters": false, "noFallthroughCasesInSwitch": true }, "include": ["src"], diff --git a/packages/kbot/src/commands/images.ts b/packages/kbot/src/commands/images.ts index aa6b1f41..bdad6dba 100644 --- a/packages/kbot/src/commands/images.ts +++ b/packages/kbot/src/commands/images.ts @@ -2,7 +2,11 @@ import { z } from 'zod'; import * as path from 'node:path'; import { sync as write } from '@polymech/fs/write'; import { sync as exists } from '@polymech/fs/exists'; -import { readFileSync } from 'node:fs'; +import { + readFileSync, + statSync, + unlinkSync +} from 'node:fs'; import { variables } from '../variables.js'; import { resolve } from '@polymech/commons'; @@ -162,7 +166,7 @@ async function launchGuiAndGetPrompt(argv: any): Promise { cmd: 'forward_image_to_frontend', base64, mimeType, - filename + filename: imagePath }; tauriProcess.stdin?.write(JSON.stringify(imageResponse) + '\n'); @@ -172,6 +176,40 @@ async function launchGuiAndGetPrompt(argv: any): Promise { logger.error(`Failed to send image: ${imagePath}`, error.message); } } + } else if (message.type === 'delete_request') { + logger.info('📨 Received delete request from GUI'); + const pathToDelete = message.path; + if (pathToDelete && isString(pathToDelete)) { + try { + if (exists(pathToDelete)) { + unlinkSync(pathToDelete); + logger.info(`✅ File deleted successfully: ${pathToDelete}`); + const successResponse = { + cmd: 'file_deleted_successfully', + path: pathToDelete + }; + tauriProcess.stdin?.write(JSON.stringify(successResponse) + '\n'); + } else { + logger.warn(`⚠️ File not found for deletion: ${pathToDelete}`); + const errorResponse = { + cmd: 'file_deletion_error', + path: pathToDelete, + error: 'File not found on server.' + }; + tauriProcess.stdin?.write(JSON.stringify(errorResponse) + '\n'); + } + } catch (error) { + logger.error(`❌ Failed to delete file: ${pathToDelete}`, error.message); + const errorResponse = { + cmd: 'file_deletion_error', + path: pathToDelete, + error: error.message + }; + tauriProcess.stdin?.write(JSON.stringify(errorResponse) + '\n'); + } + } else { + logger.error('Invalid delete request from GUI, path is missing.'); + } } else if (message.type === 'generate_request') { logger.info('📨 Received generation request from GUI'); @@ -179,7 +217,33 @@ async function launchGuiAndGetPrompt(argv: any): Promise { try { const genPrompt = message.prompt; const genFiles = message.files || []; - const genDst = message.dst; + + // --- New logic for destination path --- + let dstDir: string; + + if (argv.dst) { + const absoluteDst = path.resolve(argv.dst); + const dstStat = exists(absoluteDst) ? statSync(absoluteDst) : null; + if (dstStat && dstStat.isDirectory()) { + dstDir = absoluteDst; + } else { + dstDir = path.dirname(absoluteDst); + } + } else if (genFiles.length > 0) { + dstDir = path.dirname(genFiles[0]); + } else { + dstDir = process.cwd(); // fallback to current working dir + } + + const baseFileName = genFiles.length > 0 + ? path.basename(genFiles[0], path.extname(genFiles[0])) + : 'generated'; + + const newFileName = `${baseFileName}_gen_0.png`; + + const finalDstPath = path.resolve(dstDir, newFileName); + logger.info(`📝 Determined destination path for generated image: ${finalDstPath}`); + // --- End new logic --- logger.info(`🎨 Starting image generation: "${genPrompt}"`); @@ -192,7 +256,7 @@ async function launchGuiAndGetPrompt(argv: any): Promise { ...argv, prompt: genPrompt, include: genFiles, - dst: genDst + dst: finalDstPath // Use the new path }); imageBuffer = await editImage(genPrompt, genFiles, parsedOptions); } else { @@ -201,12 +265,15 @@ async function launchGuiAndGetPrompt(argv: any): Promise { const parsedOptions = ImageOptionsSchema().parse({ ...argv, prompt: genPrompt, - dst: genDst + dst: finalDstPath // Use the new path }); imageBuffer = await createImage(genPrompt, parsedOptions); } if (imageBuffer) { + write(finalDstPath, imageBuffer); + logger.info(`✅ Image saved to: ${finalDstPath}`); + // Send the generated image back to the GUI (chat mode) const base64Result = imageBuffer.toString('base64'); @@ -214,11 +281,11 @@ async function launchGuiAndGetPrompt(argv: any): Promise { cmd: 'forward_image_to_frontend', base64: base64Result, mimeType: 'image/png', - filename: path.basename(genDst) + filename: finalDstPath }; tauriProcess.stdin?.write(JSON.stringify(imageResponse) + '\n'); - logger.info(`✅ Generated image sent to GUI: ${genDst}`); + logger.info(`✅ Generated image sent to GUI: ${path.basename(finalDstPath)}`); } else { logger.error('❌ Failed to generate image'); diff --git a/packages/kbot/tests/assets/DSC05427.JPG b/packages/kbot/tests/assets/DSC05427.JPG new file mode 100644 index 00000000..fe3c2937 Binary files /dev/null and b/packages/kbot/tests/assets/DSC05427.JPG differ diff --git a/packages/kbot/tests/assets/DSC05427_gen_0.png b/packages/kbot/tests/assets/DSC05427_gen_0.png new file mode 100644 index 00000000..f0c8f6fc Binary files /dev/null and b/packages/kbot/tests/assets/DSC05427_gen_0.png differ diff --git a/packages/kbot/tests/assets/MOMO_1704489700094.png b/packages/kbot/tests/assets/MOMO_1704489700094.png new file mode 100644 index 00000000..a5b9c3c1 Binary files /dev/null and b/packages/kbot/tests/assets/MOMO_1704489700094.png differ diff --git a/packages/kbot/tests/assets/katfucked.jpg b/packages/kbot/tests/assets/katfucked.jpg new file mode 100644 index 00000000..7391cd7b Binary files /dev/null and b/packages/kbot/tests/assets/katfucked.jpg differ diff --git a/packages/kbot/tests/assets/m1.jpg b/packages/kbot/tests/assets/m1.jpg new file mode 100644 index 00000000..87c199e7 Binary files /dev/null and b/packages/kbot/tests/assets/m1.jpg differ diff --git a/packages/kbot/tests/assets/m1_gen_0.png b/packages/kbot/tests/assets/m1_gen_0.png new file mode 100644 index 00000000..661da3d7 Binary files /dev/null and b/packages/kbot/tests/assets/m1_gen_0.png differ diff --git a/packages/kbot/tests/assets/m2.jpg b/packages/kbot/tests/assets/m2.jpg new file mode 100644 index 00000000..acc5e774 Binary files /dev/null and b/packages/kbot/tests/assets/m2.jpg differ diff --git a/packages/kbot/tests/assets/m2_gen_0.png b/packages/kbot/tests/assets/m2_gen_0.png new file mode 100644 index 00000000..9d47d1ee Binary files /dev/null and b/packages/kbot/tests/assets/m2_gen_0.png differ diff --git a/packages/kbot/tests/assets/replicate-prediction-z59addc6b1rmc0crk3386vchrr.jpg b/packages/kbot/tests/assets/replicate-prediction-z59addc6b1rmc0crk3386vchrr.jpg new file mode 100644 index 00000000..852c1796 Binary files /dev/null and b/packages/kbot/tests/assets/replicate-prediction-z59addc6b1rmc0crk3386vchrr.jpg differ