Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | 62x 62x 62x 1x 62x 2x 2x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 62x 3x 3x 2x 2x 2x 1x 1x 1x 2x 1x 1x 2x 62x | import { useRef, useCallback, useState } from "react";
type FileEntry = { name: string; path?: string; content: string };
interface UseFileManagerOptions {
tabs?: Array<{ name: string; path?: string; content: string }>;
onFilesLoaded?: (files: FileEntry[], replaceAll: boolean) => void;
toast?: (params: { title: string; description?: string; variant?: string }) => void;
}
export function useFileManager({ tabs = [], onFilesLoaded, toast }: UseFileManagerOptions = {}) {
const fileInputRef = useRef<HTMLInputElement | null>(null);
const [lastLoadedFiles, setLastLoadedFiles] = useState<FileEntry[] | null>(null);
const onLoadFiles = useCallback(() => {
fileInputRef.current?.click();
}, []);
const downloadAllFiles = useCallback(async (providedTabs?: Array<{ name: string; path?: string; content: string }>) => {
const which = providedTabs ?? tabs ?? [];
if (!which || which.length === 0) {
try {
toast?.({ title: "Nothing to download", description: "There are no open files to download" });
} catch {}
return;
}
which.forEach((tab, index) => {
setTimeout(() => {
const element = document.createElement("a");
element.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(tab.content));
element.setAttribute("download", tab.name);
(element as any).style.display = "none";
document.body.appendChild(element);
(element as any).click();
element.remove();
}, index * 200);
});
setTimeout(() => {
try {
toast?.({ title: "Download started", description: `${which.length} file(s) will be downloaded` });
} catch {}
}, which.length * 200 + 100);
}, [tabs, toast]);
const handleHiddenFileInput = useCallback(async (e: React.ChangeEvent<HTMLInputElement>) => {
const fl = e.target.files;
if (!fl || fl.length === 0) return;
const files: FileEntry[] = [];
for (const f of Array.from(fl)) {
if (!f.name.endsWith(".ino") && !f.name.endsWith(".h")) continue;
try {
const txt = await f.text();
files.push({ name: f.name, path: f.name, content: txt });
} catch {}
}
if (files.length > 0) {
setLastLoadedFiles(files);
onFilesLoaded?.(files, false);
}
// clear input value to allow re-upload of same file
Iif (fileInputRef.current) fileInputRef.current.value = "";
}, [onFilesLoaded]);
return {
fileInputRef,
onLoadFiles,
downloadAllFiles,
handleHiddenFileInput,
lastLoadedFiles,
} as const;
}
|