Documentation/Source example
Example source

samples/basic/FilesystemUtils.js

"use strict";

window.DefineScript("Filesystem Utils");

include("docs/Flags.js");
include("docs/Helpers.js");

/*
 * Filesystem utilities sample
 *
 * Demonstrates:
 *   - utils.GetDrives()
 *   - utils.GetDriveInfo()
 *   - utils.GetShortPath()
 *   - utils.GetFolderSize()
 *   - utils.GetFolderSizeAsync()
 */

const sampleDir = fb.ProfilePath + "filesystem-utils-sample\\";
const sampleFile = sampleDir + "sample.txt";

const fontTitle = gdi.Font("Segoe UI", 18, 1);
const fontSection = gdi.Font("Segoe UI", 13, 1);
const fontNormal = gdi.Font("Segoe UI", 12);
const fontMono = gdi.Font("Consolas", 11);

const colors = {
    background: RGB(28, 28, 28),
    surface: RGB(38, 38, 38),
    text: RGB(235, 235, 235),
    muted: RGB(170, 170, 170),
    accent: RGB(110, 175, 235),
    success: RGB(125, 205, 145),
    error: RGB(235, 125, 125),
    border: RGB(60, 60, 60)
};

const driveTypeNames = [
    "Unknown",
    "Removable",
    "Fixed",
    "Network",
    "CD-ROM",
    "RAM Disk"
];

let ww = 0;
let wh = 0;
let folderSizeTaskId = 0;
let folderSizeSync = 0;
let folderSizeAsync = null;
let folderSizeAsyncError = "";
let shortPath = "";
let drives = [];

function formatBytes(value) {
    if (!Number.isFinite(value) || value < 0) return "n/a";

    const units = ["B", "KB", "MB", "GB", "TB", "PB"];
    let size = value;
    let unit = 0;

    while (size >= 1024 && unit < units.length - 1) {
        size /= 1024;
        ++unit;
    }

    const digits = unit === 0 ? 0 : size >= 100 ? 0 : size >= 10 ? 1 : 2;
    return size.toFixed(digits) + " " + units[unit];
}

function driveTypeName(type) {
    return driveTypeNames[type] || "Unknown";
}

function initializeSample() {
    utils.CreateFolder(sampleDir);
    utils.WriteTextFile(sampleFile, "12345678", false);

    drives = utils.GetDrives()
        .map((root) => utils.GetDriveInfo(root))
        .filter((info) => !!info);

    shortPath = utils.GetShortPath(sampleDir);
    folderSizeSync = utils.GetFolderSize(sampleDir);
    folderSizeTaskId = utils.GetFolderSizeAsync(sampleDir);
}

function drawText(gr, text, font, color, x, y, w, h, flags) {
    gr.GdiDrawText(text, font, color, x, y, w, h, flags | DT_NOPREFIX);
}

function drawLabelValue(gr, label, value, x, y, w, valueColor) {
    const labelWidth = 112;
    drawText(gr, label, fontNormal, colors.muted, x, y, labelWidth, 22, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
    drawText(gr, value, fontNormal, valueColor || colors.text, x + labelWidth, y, w - labelWidth, 22, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
}

function on_size() {
    ww = window.Width;
    wh = window.Height;
}

function on_paint(gr) {
    gr.FillSolidRect(0, 0, ww, wh, colors.background);

    const margin = 20;
    const contentWidth = Math.max(0, ww - margin * 2);
    let y = 18;

    drawText(gr, "Filesystem utilities", fontTitle, colors.text, margin, y, contentWidth, 30, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
    y += 40;

    drawText(gr, "Logical drives", fontSection, colors.accent, margin, y, contentWidth, 24, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
    y += 30;

    for (const info of drives) {
        const cardHeight = 106;
        gr.FillSolidRect(margin, y, contentWidth, cardHeight, colors.surface);
        gr.DrawRect(margin, y, contentWidth - 1, cardHeight - 1, 1, colors.border);

        const innerX = margin + 12;
        const innerW = Math.max(0, contentWidth - 24);
        const label = info.VolumeName
            ? info.Root + "  —  " + info.VolumeName
            : info.Root;

        drawText(gr, label, fontSection, colors.text, innerX, y + 8, innerW, 22, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
        drawText(
            gr,
            driveTypeName(info.DriveType) + (info.FileSystem ? "  •  " + info.FileSystem : "") + (info.IsReady ? "" : "  •  Not ready"),
            fontNormal,
            info.IsReady ? colors.muted : colors.error,
            innerX,
            y + 31,
            innerW,
            20,
            DT_LEFT | DT_VCENTER | DT_SINGLELINE
        );

        drawLabelValue(gr, "Total", formatBytes(info.TotalSize), innerX, y + 55, innerW);
        drawLabelValue(gr, "Free / available", formatBytes(info.FreeSpace) + " / " + formatBytes(info.AvailableSpace), innerX, y + 77, innerW);

        y += cardHeight + 10;
    }

    y += 4;
    drawText(gr, "Path and folder size", fontSection, colors.accent, margin, y, contentWidth, 24, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
    y += 30;

    gr.FillSolidRect(margin, y, contentWidth, 118, colors.surface);
    gr.DrawRect(margin, y, contentWidth - 1, 117, 1, colors.border);

    const innerX = margin + 12;
    const innerW = Math.max(0, contentWidth - 24);

    drawText(gr, "Short path", fontNormal, colors.muted, innerX, y + 8, innerW, 20, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
    drawText(gr, shortPath || "Unavailable", fontMono, shortPath ? colors.text : colors.error, innerX, y + 30, innerW, 22, DT_LEFT | DT_VCENTER | DT_SINGLELINE);

    drawLabelValue(gr, "Synchronous", formatBytes(folderSizeSync), innerX, y + 58, innerW);

    let asyncText = "Calculating…  (task " + folderSizeTaskId + ")";
    let asyncColor = colors.muted;

    if (folderSizeAsync !== null) {
        asyncText = formatBytes(folderSizeAsync);
        asyncColor = colors.success;
    }
    else if (folderSizeAsyncError) {
        asyncText = folderSizeAsyncError;
        asyncColor = colors.error;
    }

    drawLabelValue(gr, "Asynchronous", asyncText, innerX, y + 82, innerW, asyncColor);
}

function on_get_folder_size_done(task_id, success, size, error_text) {
    if (task_id !== folderSizeTaskId) return;

    if (success) {
        folderSizeAsync = size;
    }
    else {
        folderSizeAsyncError = error_text || "Failed";
    }

    utils.RemovePath(sampleDir);
    window.Repaint();
}

function on_script_unload() {
    utils.RemovePath(sampleDir);
}

initializeSample();