diff --git a/app/main.py b/app/main.py index ad1b855..f1b6acb 100644 --- a/app/main.py +++ b/app/main.py @@ -1113,6 +1113,65 @@ def update_settings(req: UpdateSettingsRequest): save_settings(settings) return {"status": "saved", "settings": settings} + +class CreateDirRequest(BaseModel): + parent_path: str + name: str + +@app.get("/api/system/browse-dir") +def browse_dir(path: str = ""): + """Browse directories on the system""" + if not path: + path = os.path.expanduser("~") + + path = os.path.abspath(path) + + if not os.path.exists(path): + raise HTTPException(status_code=404, detail="Path does not exist") + + if not os.path.isdir(path): + raise HTTPException(status_code=400, detail="Path is not a directory") + + try: + entries = [] + # Add parent directory entry if not at root + parent = os.path.dirname(path) + if parent != path: + entries.append({ + "name": "..", + "path": parent, + "is_dir": True + }) + + for name in sorted(os.listdir(path)): + full_path = os.path.join(path, name) + try: + if os.path.isdir(full_path): + entries.append({ + "name": name, + "path": full_path, + "is_dir": True + }) + except PermissionError: + continue + + return { + "current_path": path, + "entries": entries + } + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + +@app.post("/api/system/create-dir") +def create_dir(req: CreateDirRequest): + """Create a new directory""" + full_path = os.path.abspath(os.path.join(req.parent_path, req.name)) + try: + os.makedirs(full_path, exist_ok=True) + return {"status": "ok", "path": full_path} + except Exception as e: + raise HTTPException(status_code=400, detail=str(e)) + @app.get("/api/uploads/{filename}") def get_upload_file(filename: str): file_path = os.path.join(upscaler.UPLOAD_DIR, filename) diff --git a/static/app.js b/static/app.js index 93058c8..a5a055d 100644 --- a/static/app.js +++ b/static/app.js @@ -2171,6 +2171,220 @@ async function initializeApp() { } }); } + + // Wire up folder browser modals + const browseJobTempBtn = document.getElementById("browse-job-temp-btn"); + const browseDefaultTempBtn = document.getElementById("browse-default-temp-btn"); + const folderModalCloseBtn = document.getElementById("folder-modal-close-btn"); + const folderCancelBtn = document.getElementById("folder-cancel-btn"); + const folderSelectBtn = document.getElementById("folder-select-btn"); + const folderUpBtn = document.getElementById("folder-up-btn"); + const createFolderBtn = document.getElementById("create-folder-btn"); + const newFolderNameInput = document.getElementById("new-folder-name"); + + if (browseJobTempBtn) { + browseJobTempBtn.addEventListener("click", () => { + openFolderBrowser(document.getElementById("temp-dir-input")); + }); + } + + if (browseDefaultTempBtn) { + browseDefaultTempBtn.addEventListener("click", () => { + openFolderBrowser(document.getElementById("default-temp-dir-input")); + }); + } + + if (folderModalCloseBtn) { + folderModalCloseBtn.addEventListener("click", closeFolderBrowser); + } + + if (folderCancelBtn) { + folderCancelBtn.addEventListener("click", closeFolderBrowser); + } + + // Close modal when clicking outside card + const folderModal = document.getElementById("folder-modal"); + if (folderModal) { + folderModal.addEventListener("click", (e) => { + if (e.target === folderModal) { + closeFolderBrowser(); + } + }); + } + + if (folderSelectBtn) { + folderSelectBtn.addEventListener("click", () => { + if (activeTargetInput && currentBrowsePath) { + activeTargetInput.value = currentBrowsePath; + activeTargetInput.dispatchEvent(new Event("change")); + activeTargetInput.dispatchEvent(new Event("input")); + } + closeFolderBrowser(); + }); + } + + if (folderUpBtn) { + folderUpBtn.addEventListener("click", () => { + if (currentBrowsePath) { + const parts = currentBrowsePath.split("/"); + if (parts.length > 1) { + parts.pop(); + const parent = parts.join("/") || "/"; + loadFolderDir(parent); + } + } + }); + } + + if (createFolderBtn) { + createFolderBtn.addEventListener("click", createFolder); + } + + if (newFolderNameInput) { + newFolderNameInput.addEventListener("keydown", (e) => { + if (e.key === "Enter") { + createFolder(); + } + }); + } +} + +/* ---------------------------------------------------- + Folder Browser Modal & API Helpers + ---------------------------------------------------- */ +let activeTargetInput = null; +let currentBrowsePath = ""; + +function openFolderBrowser(targetInput) { + activeTargetInput = targetInput; + const modal = document.getElementById("folder-modal"); + if (!modal) return; + + const statusDiv = document.getElementById("folder-modal-status"); + if (statusDiv) statusDiv.textContent = ""; + + let initialPath = targetInput ? targetInput.value.trim() : ""; + + modal.style.display = "flex"; + document.body.style.overflow = "hidden"; + + loadFolderDir(initialPath); +} + +function closeFolderBrowser() { + const modal = document.getElementById("folder-modal"); + if (modal) { + modal.style.display = "none"; + document.body.style.overflow = ""; + } + activeTargetInput = null; +} + +async function loadFolderDir(path) { + const listContainer = document.getElementById("folder-list"); + const currentPathInput = document.getElementById("folder-current-path"); + const statusDiv = document.getElementById("folder-modal-status"); + if (!listContainer) return; + + listContainer.innerHTML = '
+
Specify a custom location for temporary image frames to prevent running out of primary drive space.