Skip to main content
Known Participant
March 25, 2025
Question

Simple file browser with import and search (UXP, JS)

  • March 25, 2025
  • 1 reply
  • 426 views

I want screate plugin for simple image browser. Now its parially working. I can select folder, show thumbs but double click is not importing/placing image in document. I want use as my quick image library with search function. Any ideas hot to resolve this? My code right now looks like this

const fs = require("uxp").storage.localFileSystem;
const uxp = require("uxp");
const photoshop = require("photoshop").app;

let selectedFolder = null;

// When the "Select Folder" button is clicked
document.getElementById("select-btn").addEventListener("click", async () => {
    try {
        // Let the user select a folder
        selectedFolder = await fs.getFolder();
        if (!selectedFolder) {
            displayMessage("No folder selected.");
            return;
        }
        displayMessage("Folder selected: " + selectedFolder.nativePath);
        console.log("Folder selected:", selectedFolder.nativePath);

        // Clear existing results before loading new images
        clearThumbnails();

        // Load images from the selected folder
        loadImages(selectedFolder);
    } catch (error) {
        displayMessage("Error selecting folder: " + error.message);
        console.error("Error selecting folder:", error);
    }
});

// Function to display messages to the user in the UI
function displayMessage(message) {
    const resultsContainer = document.getElementById("results");
    resultsContainer.innerHTML = `<p>${message}</p>`;
}

// Function to clear thumbnails in the UI
function clearThumbnails() {
    const resultsContainer = document.getElementById("results");
    resultsContainer.innerHTML = "";  // Clear any previous results
}

// Function to load images from the selected folder
async function loadImages(folder) {
    const resultsContainer = document.getElementById("results");

    try {
        console.log("Loading images from folder:", folder.nativePath);
        const images = await getImagesFromFolder(folder);

        if (images.length === 0) {
            displayMessage("No images found in the selected folder.");
            return;
        }

        images.forEach(async (file) => {
            const fileElement = document.createElement("div");
            fileElement.classList.add("thumbnail");

            try {
                // Read file as binary data (ArrayBuffer) for image files
                const fileData = await file.read({ format: uxp.storage.formats.binary });
                console.log("File read successfully:", file.name);

                // Check the type of fileData returned by read()
                if (fileData instanceof ArrayBuffer) {
                    const blob = new Blob([fileData]);
                    const imageUrl = URL.createObjectURL(blob); // Create an object URL for the Blob
                    fileElement.innerHTML = `<img src="${imageUrl}" alt="${file.name}" style="width:50px; height:50px; cursor:pointer;">`;

                    // When double-clicking the thumbnail, open the image in Photoshop
                    fileElement.addEventListener("dblclick", async () => {
                        await importImageToPSD(file);
                    });
                    resultsContainer.appendChild(fileElement);
                } else {
                    console.error("Error: file content is not an ArrayBuffer, it's a", typeof fileData);
                    displayMessage("Error reading file: Not an ArrayBuffer, got: " + typeof fileData);
                }
            } catch (error) {
                console.error("Error reading file:", error);
                displayMessage("Error reading file: " + error.message);
            }
        });
    } catch (error) {
        displayMessage("Error retrieving entries from folder: " + error.message);
        console.error("Error retrieving entries from folder:", error);
    }
}

// Recursive function to get all images (jpg/png) from a folder and its subfolders
async function getImagesFromFolder(folder) {
    let imageFiles = [];
    const entries = await folder.getEntries();

    for (const entry of entries) {
        if (entry.isFile && (entry.name.toLowerCase().endsWith(".jpg") || entry.name.toLowerCase().endsWith(".png"))) {
            imageFiles.push(entry);  // Add image files to the array
        } else if (entry.isFolder) {
            // Recursively check inside subfolders
            const subfolderImages = await getImagesFromFolder(entry);
            imageFiles = imageFiles.concat(subfolderImages);  // Combine results from subfolders
        }
    }

    return imageFiles;
}

// Function to import the selected image to the currently open PSD document
async function importImageToPSD(file) {
    try {
        const fileData = await file.read({ format: uxp.storage.formats.binary });  // Read the file content as binary
        console.log("Importing image to PSD:", file.name);

        // Check the type of fileData returned by read()
        if (fileData instanceof ArrayBuffer) {
            // Create a Blob from the ArrayBuffer
            const blob = new Blob([fileData]);

            // Save the Blob to a temporary file in the system
            const tempFile = await saveTemporaryFile(blob, file.name);

            // Now place the image in Photoshop
            await placeImageInPhotoshop(tempFile);
        } else {
            console.error("Error: file content is not an ArrayBuffer, it's a", typeof fileData);
            displayMessage("Failed to import image: Not an ArrayBuffer");
        }
    } catch (err) {
        displayMessage("Failed to import image: " + err.message);
        console.error("Error importing image:", err);
    }
}

// Function to save the image data to a temporary file
async function saveTemporaryFile(blob, filename) {
    const tempFolder = await fs.getTemporaryFolder(); // Get the system's temporary folder
    const tempFile = await tempFolder.createFile(filename, { overwrite: true }); // Create the temp file with the specified filename

    await tempFile.write(blob); // Write the image data to the temporary file
    return tempFile;  // Return the temporary file object
}

// Function to place the image in Photoshop document using BatchPlay
async function placeImageInPhotoshop(file) {
    try {
        const doc = photoshop.activeDocument;  // Get the currently active document

        // Create the asset object that we will use for placing the image
        const asset = { file: file.nativePath };  // asset file path to the temporary file

        // Ensure we pass the path directly as a string to the placeEvent command
        const cmd = [{
            "_obj": "placeEvent",
            "null": {
                "_path": asset.file,  // The path to the temporary image file
                "_kind": "local"
            },
            "offset": {
                "_obj": "offset",
                "horizontal": {
                    "_unit": "pixelsUnit",
                    "_value": 0
                },
                "vertical": {
                    "_unit": "pixelsUnit",
                    "_value": 0
                }
            },
            "_isCommand": true
        }];

        // Run the BatchPlay command
        await photoshop.batchPlay(cmd);

        console.log("Image placed successfully.");
    } catch (err) {
        console.error("Error placing image:", err);
        displayMessage("Failed to place image: " + err.message);
    }
}

1 reply

Inspiring
March 25, 2025

I speak as a non-UXP expert, but I have been trying to learn and understand for a while. However, from what I see it seems that a token is missing, With uxp this is important, try to implement it and see if you can make it work, Another important thing is the permissions in the manifest file. If it is not a problem for you, you can share the project and see if with the help of someone you can solve it.

Known Participant
March 26, 2025

I almost finish, now I want create thumnails cache, but I dont know how ;)w

Inspiring
March 26, 2025

We are not fortune tellers and we can't help anyone if we don't know what they are looking for. You posted some code above, which is ultimately not useful. Sorry for the question, But what is the difference between your panel and going to the photoshop file open menu. Both open a folder to select images. Why is your plugin important?