tauri fixes
This commit is contained in:
parent
002542ac34
commit
e3dd519686
File diff suppressed because one or more lines are too long
BIN
packages/kbot/dist/win-64/tauri-app.exe
vendored
BIN
packages/kbot/dist/win-64/tauri-app.exe
vendored
Binary file not shown.
@ -28,6 +28,7 @@ function App() {
|
||||
const [ipcInitialized, setIpcInitialized] = useState(false);
|
||||
const [messageToSend, setMessageToSend] = useState("");
|
||||
const [generationTimeoutId, setGenerationTimeoutId] = useState<NodeJS.Timeout | null>(null);
|
||||
const [currentIndex, setCurrentIndex] = useState(0);
|
||||
|
||||
const deleteFilePermanently = async (pathToDelete: string) => {
|
||||
addDebugMessage('info', `Requesting deletion of file: ${pathToDelete}`);
|
||||
@ -127,19 +128,30 @@ function App() {
|
||||
|
||||
const addFiles = async (newPaths: string[]) => {
|
||||
const uniqueNewPaths = newPaths.filter(newPath => !files.some(f => f.path === newPath));
|
||||
const newImageFiles: ImageFile[] = [];
|
||||
|
||||
if (uniqueNewPaths.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update destination directory
|
||||
const firstPath = uniqueNewPaths[0];
|
||||
const lastSeparatorIndex = Math.max(firstPath.lastIndexOf('/'), firstPath.lastIndexOf('\\'));
|
||||
const newDir = firstPath.substring(0, lastSeparatorIndex);
|
||||
const currentFilename = dst.split(/[/\\]/).pop() || generateDefaultDst(1, firstPath);
|
||||
const newDst = `${newDir}${firstPath.includes('\\') ? '\\' : '/'}${currentFilename}`;
|
||||
setDst(newDst);
|
||||
|
||||
// Read files
|
||||
const newImageFiles: ImageFile[] = [];
|
||||
for (const path of uniqueNewPaths) {
|
||||
try {
|
||||
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' :
|
||||
'image/png';
|
||||
const src = `data:${mimeType};base64,${base64}`;
|
||||
|
||||
newImageFiles.push({ path, src });
|
||||
newImageFiles.push({ path, src, selected: false, isGenerated: false });
|
||||
} catch (e) {
|
||||
const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);
|
||||
console.error(`Failed to read file: ${path}`, e);
|
||||
@ -147,7 +159,20 @@ function App() {
|
||||
}
|
||||
}
|
||||
|
||||
setFiles(prevFiles => [...prevFiles, ...newImageFiles]);
|
||||
const lastPath = uniqueNewPaths[uniqueNewPaths.length - 1];
|
||||
|
||||
setFiles(prevFiles => {
|
||||
const combinedFiles = [...prevFiles, ...newImageFiles];
|
||||
const newIndex = combinedFiles.findIndex(f => f.path === lastPath);
|
||||
if (newIndex !== -1) {
|
||||
setCurrentIndex(newIndex);
|
||||
}
|
||||
|
||||
return combinedFiles.map(file => ({
|
||||
...file,
|
||||
selected: file.path === lastPath
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
const removeFile = (pathToRemove: string) => {
|
||||
@ -159,7 +184,7 @@ function App() {
|
||||
};
|
||||
|
||||
const handleImageSelection = (imagePath: string, isMultiSelect: boolean) => {
|
||||
setFiles(prev =>
|
||||
setFiles(prev =>
|
||||
prev.map(file => {
|
||||
if (file.path === imagePath) {
|
||||
// For multi-select, toggle the current state. For single-select, always select it.
|
||||
@ -490,6 +515,8 @@ function App() {
|
||||
onImageDelete={deleteFilePermanently}
|
||||
onImageSaveAs={saveImageAs}
|
||||
addFiles={addFiles}
|
||||
currentIndex={currentIndex}
|
||||
setCurrentIndex={setCurrentIndex}
|
||||
/>
|
||||
|
||||
{/* Debug Panel */}
|
||||
|
||||
@ -8,6 +8,8 @@ interface ImageGalleryProps {
|
||||
onImageDelete?: (imagePath: string) => void;
|
||||
onImageSaveAs?: (imagePath: string) => void;
|
||||
showSelection?: boolean;
|
||||
currentIndex: number;
|
||||
setCurrentIndex: (index: number) => void;
|
||||
}
|
||||
|
||||
export default function ImageGallery({
|
||||
@ -16,9 +18,10 @@ export default function ImageGallery({
|
||||
onImageRemove,
|
||||
onImageDelete,
|
||||
onImageSaveAs,
|
||||
showSelection = false
|
||||
showSelection = false,
|
||||
currentIndex,
|
||||
setCurrentIndex
|
||||
}: ImageGalleryProps) {
|
||||
const [currentIndex, setCurrentIndex] = useState(0);
|
||||
const [lightboxOpen, setLightboxOpen] = useState(false);
|
||||
const [lightboxLoaded, setLightboxLoaded] = useState(false);
|
||||
|
||||
@ -27,7 +30,7 @@ export default function ImageGallery({
|
||||
if (images.length > 0 && currentIndex >= images.length) {
|
||||
setCurrentIndex(Math.max(0, images.length - 1));
|
||||
}
|
||||
}, [images.length, currentIndex]);
|
||||
}, [images.length, currentIndex, setCurrentIndex]);
|
||||
|
||||
// ESC key handler for lightbox
|
||||
useEffect(() => {
|
||||
@ -51,7 +54,7 @@ export default function ImageGallery({
|
||||
document.addEventListener('keydown', handleKeyDown);
|
||||
return () => document.removeEventListener('keydown', handleKeyDown);
|
||||
}
|
||||
}, [lightboxOpen, currentIndex, images.length]);
|
||||
}, [lightboxOpen, currentIndex, images.length, setCurrentIndex]);
|
||||
|
||||
const preloadImage = (index: number) => {
|
||||
if (images.length === 0 || index < 0 || index >= images.length) return;
|
||||
|
||||
@ -22,6 +22,8 @@ interface PromptFormProps {
|
||||
onImageDelete?: (path: string) => void;
|
||||
onImageSaveAs?: (path: string) => void;
|
||||
addFiles: (paths: string[]) => void;
|
||||
currentIndex: number;
|
||||
setCurrentIndex: (index: number) => void;
|
||||
}
|
||||
|
||||
const PromptForm: React.FC<PromptFormProps> = ({
|
||||
@ -42,7 +44,9 @@ const PromptForm: React.FC<PromptFormProps> = ({
|
||||
addImageFromUrl,
|
||||
onImageDelete,
|
||||
onImageSaveAs,
|
||||
addFiles
|
||||
addFiles,
|
||||
currentIndex,
|
||||
setCurrentIndex
|
||||
}) => {
|
||||
const selectedCount = getSelectedImages().length;
|
||||
const { ref: dropZoneRef, dragIn } = useDropZone({ onDrop: addFiles });
|
||||
@ -67,6 +71,12 @@ const PromptForm: React.FC<PromptFormProps> = ({
|
||||
placeholder="Describe the image you want to generate or edit..."
|
||||
className="w-full glass-input p-4 rounded-xl min-h-[120px] resize-none"
|
||||
rows={4}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
|
||||
e.preventDefault();
|
||||
submit();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -152,6 +162,8 @@ const PromptForm: React.FC<PromptFormProps> = ({
|
||||
onImageSaveAs={onImageSaveAs}
|
||||
showSelection={true}
|
||||
onImageDelete={onImageDelete}
|
||||
currentIndex={currentIndex}
|
||||
setCurrentIndex={setCurrentIndex}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -15,7 +15,8 @@ const useDropZone = ({ onDrop }: { onDrop: (paths: string[]) => void }) => {
|
||||
useEffect(() => {
|
||||
const unlisten = listen<TauriDragDropEvent>("tauri://drag-drop", (e) => {
|
||||
const { x, y } = e.payload.position;
|
||||
if (document.elementFromPoint(x, y) === ref.current) {
|
||||
const element = document.elementFromPoint(x, y);
|
||||
if (ref.current && element && ref.current.contains(element)) {
|
||||
onDrop(e.payload.paths);
|
||||
setDragIn(false);
|
||||
}
|
||||
@ -29,7 +30,8 @@ const useDropZone = ({ onDrop }: { onDrop: (paths: string[]) => void }) => {
|
||||
useEffect(() => {
|
||||
const unlisten = listen<TauriDragDropEvent>("tauri://drag-over", (e) => {
|
||||
const { x, y } = e.payload.position;
|
||||
if (document.elementFromPoint(x, y) === ref.current) {
|
||||
const element = document.elementFromPoint(x, y);
|
||||
if (ref.current && element && ref.current.contains(element)) {
|
||||
setDragIn(true);
|
||||
} else {
|
||||
setDragIn(false);
|
||||
|
||||
@ -19,6 +19,50 @@ import { prompt as resolvePrompt } from '../prompt.js';
|
||||
import { spawn } from 'node:child_process';
|
||||
import { loadConfig } from '../config.js';
|
||||
|
||||
function generateUniqueFilename(dst: string | undefined, genFiles: string[]): string {
|
||||
let dstDir: string;
|
||||
|
||||
if (dst) {
|
||||
const absoluteDst = path.resolve(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
|
||||
}
|
||||
|
||||
let baseFileName;
|
||||
let i = 0;
|
||||
|
||||
if (genFiles.length > 0) {
|
||||
const originalBaseName = path.basename(genFiles[0], path.extname(genFiles[0]));
|
||||
const match = originalBaseName.match(/_gen_(\d+)$/);
|
||||
if (match && match.index) {
|
||||
baseFileName = originalBaseName.substring(0, match.index);
|
||||
i = parseInt(match[1], 10) + 1;
|
||||
} else {
|
||||
baseFileName = originalBaseName;
|
||||
}
|
||||
} else {
|
||||
baseFileName = 'generated';
|
||||
}
|
||||
|
||||
let newFileName;
|
||||
let finalDstPath;
|
||||
do {
|
||||
newFileName = `${baseFileName}_gen_${i}.png`;
|
||||
finalDstPath = path.resolve(dstDir, newFileName);
|
||||
i++;
|
||||
} while (exists(finalDstPath));
|
||||
|
||||
return finalDstPath;
|
||||
}
|
||||
|
||||
function getGuiAppPath(): string {
|
||||
|
||||
// Get the directory of this script file, then navigate to the GUI app
|
||||
@ -217,39 +261,10 @@ async function launchGuiAndGetPrompt(argv: any): Promise<string | null> {
|
||||
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';
|
||||
|
||||
let i = 0;
|
||||
let newFileName;
|
||||
let finalDstPath;
|
||||
do {
|
||||
newFileName = `${baseFileName}_gen_${i}.png`;
|
||||
finalDstPath = path.resolve(dstDir, newFileName);
|
||||
i++;
|
||||
} while (exists(finalDstPath));
|
||||
|
||||
const finalDstPath = generateUniqueFilename(genDst, genFiles);
|
||||
logger.info(`📝 Determined destination path for generated image: ${finalDstPath}`);
|
||||
// --- End new logic ---
|
||||
|
||||
logger.info(`🎨 Starting image generation: "${genPrompt}"`);
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.7 MiB |
Loading…
Reference in New Issue
Block a user