File Manager

Full-featured file browser with dual-pane layout, upload, clipboard operations, view modes, and a complete filesystem API for programmatic control.

Live Demo

<iframe id="fm-demo" src="/online/webapp/file-manager" width="100%" height="500" frameborder="0" style="border:1px solid #ccc; border-radius:4px;"></iframe>
<script>window._wsConnect('fm-demo', 'fmSocket');</script>

Embed

<iframe src="https://sgapps.io/online/webapp/file-manager"
    width="100%" height="600" frameborder="0"></iframe>

Security

External API filesystem operations are sandboxed to input:// only. Any attempt to read, write, navigate, or delete outside this path via the API returns an access denied error.


Filesystem Events Reference

These events let you create, read, write, delete, rename files and folders programmatically — perfect for building custom UIs on top of the embedded file manager.


fs:cwd -- Get or Set Current Directory

Navigate the file manager to a different directory, or get the current path.

Arg Type Description
path string (optional) Directory path to navigate to
callback function (err, currentPath: string)

// Navigate to input://
socket.fire("webapp::instance::request", "fs:cwd", "input://", function (err, cwd) {
    console.log("Now at:", cwd);
});

// Get current directory
socket.fire("webapp::instance::request", "fs:cwd", function (err, cwd) {
    console.log("Current:", cwd);
});

<button onclick="window._ws('fmSocket')&&window.fmSocket.fire('webapp::instance::request','fs:cwd','input://',function(e,p){if(e)alert('Error: '+e);else console.log('Navigated to:',p)})">Try: Navigate to input://</button><button onclick="window._ws('fmSocket')&&window.fmSocket.fire('webapp::instance::request','fs:cwd',function(e,p){alert('Current path: '+p)})">Try: Get Current Path</button>


fs:mkdirp -- Create Directory (with parents)

Creates a directory and any missing parent directories. Works like mkdir -p.

Arg Type Description
path string Directory path to create
callback function (err)

socket.fire("webapp::instance::request", "fs:mkdirp", "input://my-project/src/components/", function (err) {
    console.log(err || "Directories created");
});

<button onclick="window._ws('fmSocket')&&window.fmSocket.fire('webapp::instance::request','fs:mkdirp','input://demo-project/src/components/',function(e){if(!e){window.fmSocket.fire('webapp::instance::request','fs:cwd','input://demo-project/');alert('Created input://demo-project/src/components/')}else alert('Error: '+e)})">Try: Create demo-project/src/components/</button>


fs:write -- Write a File

Creates or overwrites a file with the given content.

Arg Type Description
path string File path
content string File content
callback function (err)

socket.fire("webapp::instance::request", "fs:write",
    "input://demo-project/README.md",
    "# My Project\n\nThis is a demo project created via the API.",
    function (err) { console.log(err || "File written"); }
);

<button onclick="window._ws('fmSocket')&&window.fmSocket.fire('webapp::instance::request','fs:mkdirp','input://demo-project/',function(){window.fmSocket.fire('webapp::instance::request','fs:write','input://demo-project/README.md','# Demo Project\n\nCreated via the File Manager API.\n\n## Features\n- In-memory storage\n- No server required\n- Destroyed on refresh',function(e){if(!e){window.fmSocket.fire('webapp::instance::request','fs:cwd','input://demo-project/');alert('README.md created!')}else alert('Error: '+e)})})">Try: Create README.md</button><button onclick="window._ws('fmSocket')&&window.fmSocket.fire('webapp::instance::request','fs:write','input://demo-project/src/index.js','console.log('Hello from the API!');\n\nfunction add(a, b) {\n return a + b;\n}\n\nmodule.exports = { add };',function(e){if(!e){window.fmSocket.fire('webapp::instance::request','fs:refresh');alert('index.js created!')}else alert('Error: '+e)})">Try: Create src/index.js</button>


fs:read -- Read a File

Reads the content of a file as text.

Arg Type Description
path string File path
callback function (err, content: string)

socket.fire("webapp::instance::request", "fs:read", "input://demo-project/README.md",
    function (err, content) {
        console.log("Content:", content);
    }
);

<button onclick="window._ws('fmSocket')&&window.fmSocket.fire('webapp::instance::request','fs:read','input://demo-project/README.md',function(e,c){if(c)alert('File content:\n\n'+c);else alert('Error: '+(e||'file not found'))})">Try: Read README.md</button>


fs:ls -- List Directory Contents

Returns an array of files and folders in the specified directory.

Arg Type Description
path string Directory path
callback function (err, files: Array&#x3C;&#x7B;filename, length, metadata&#x7D;&#x3E;)

socket.fire("webapp::instance::request", "fs:ls", "input://demo-project/",
    function (err, files) {
        files.forEach(function (f) {
            console.log(f.metadata._isDirectory ? "[DIR]" : "[FILE]", f.filename, f.length + " bytes");
        });
    }
);

<button onclick="window.ws('fmSocket')&&window.fmSocket.fire('webapp::instance::request','fs:ls','input://demo-project/',function(e,f){if(f){var msg=f.map(function(x){return (x.metadata.isDirectory?'[DIR] ':'[FILE] ')+x.filename.replace(/^.*\/(?=[^\/]+\/?$)/,'')}).join('\n');alert('Contents:\n\n'+msg)}else alert('Error: '+(e||'not found'))})">Try: List demo-project/</button>


fs:mv -- Move or Rename

Moves or renames a file or directory within the same protocol.

Arg Type Description
src string Source path
dst string Destination path
callback function (err)

socket.fire("webapp::instance::request", "fs:mv",
    "input://demo-project/README.md",
    "input://demo-project/ABOUT.md",
    function (err) { console.log(err || "Renamed"); }
);

<button onclick="window._ws('fmSocket')&&window.fmSocket.fire('webapp::instance::request','fs:mv','input://demo-project/README.md','input://demo-project/ABOUT.md',function(e){if(!e){window.fmSocket.fire('webapp::instance::request','fs:refresh');alert('Renamed README.md to ABOUT.md')}else alert('Error: '+e)})">Try: Rename README.md to ABOUT.md</button>


fs:remove -- Delete a File

Removes a single file or empty directory. Lightweight, no UI.

Arg Type Description
path string Path to remove
options object (optional) &#x7B; autoConfirm: true &#x7D; — accepted for API consistency
callback function (err)

socket.fire("webapp::instance::request", "fs:remove", "input://demo-project/ABOUT.md",
    function (err) { console.log(err || "Deleted"); }
);

// With options
socket.fire("webapp::instance::request", "fs:remove", "input://temp/file.txt",
    { autoConfirm: true },
    function (err) { console.log(err || "Deleted"); }
);

fs:copy -- Copy a File

Copies a single file. Reads source as blob, writes to destination. Lightweight, no UI.

Arg Type Description
src string Source file path
dst string Destination file path
options object (optional) &#x7B; autoConfirm: true &#x7D; — accepted for API consistency
callback function (err)

// Simple copy
socket.fire("webapp::instance::request", "fs:copy",
    "input://project/config.json",
    "input://backup/config.json",
    function (err) { console.log(err || "Copied"); }
);

// With options (accepted, currently same behavior)
socket.fire("webapp::instance::request", "fs:copy",
    "input://project/config.json",
    "input://backup/config.json",
    { autoConfirm: true },
    function (err) { console.log(err || "Copied"); }
);

fs:archive -- Create Archive (no UI)

Creates an archive from a directory using Packer. No window, no progress UI — runs silently in background.

Arg Type Description
sourcePath string Directory to archive
outputPath string or null Where to save (null = returns blob URL in callback)
format string &#x22;zip&#x22;, &#x22;tar&#x22;, &#x22;tar.gz&#x22;, &#x22;7z&#x22; (default: &#x22;zip&#x22;)
options object (optional) &#x7B; autoConfirm: true &#x7D; — accepted for API consistency
callback function (err, result: &#x7B; size, path&#x3F;, url&#x3F; &#x7D;)

// Archive and save to filesystem
socket.fire("webapp::instance::request", "fs:archive",
    "input://project/", "input://project.zip", "zip",
    function (err, result) {
        console.log("Archive:", result.size, "bytes");
    }
);

// Archive without saving (get blob URL for download)
socket.fire("webapp::instance::request", "fs:archive",
    "input://project/", null, "tar.gz",
    function (err, result) {
        console.log("Blob URL:", result.url);
    }
);

Bulk Operations (with UI)

These open a bin app window with progress bars, error handling, and conflict resolution. Use for batch operations or when you want the user to see progress.

fs:bulkCopy

Arg Type Description
sources string or Array Source path(s)
destination string Destination directory
options object (optional) &#x7B; autoConfirm: true &#x7D; to auto-resolve conflicts
callback function (err) — fires when bin/cp window closes

socket.fire("webapp::instance::request", "fs:bulkCopy",
    ["input://src/file1.txt", "input://src/file2.txt"],
    "input://backup/",
    { autoConfirm: true },
    function (err) { console.log(err || "Bulk copy done"); }
);

fs:bulkRemove

Arg Type Description
paths string or Array Path(s) to delete
options object (optional) &#x7B; autoConfirm: true &#x7D; to skip confirmation
callback function (err)

socket.fire("webapp::instance::request", "fs:bulkRemove",
    ["input://old-dir/", "input://temp.txt"],
    { autoConfirm: true },
    function (err) { console.log(err || "Bulk delete done"); }
);

fs:bulkArchive

Arg Type Description
sources string or Array Path(s) to archive
outputPath string or null Where to save (null = download)
format string &#x22;zip&#x22;, &#x22;tar&#x22;, &#x22;tar.gz&#x22;, &#x22;7z&#x22;
options object (optional) &#x7B; autoConfirm: true &#x7D; to skip format selection
callback function (err)

socket.fire("webapp::instance::request", "fs:bulkArchive",
    "input://project/", "input://project.zip", "zip",
    { autoConfirm: true },
    function (err) { console.log(err || "Bulk archive done"); }
);
When to use which: Use fs:copy, fs:remove, fs:archive for single-file operations or scripted pipelines. Use fs:bulkCopy, fs:bulkRemove, fs:bulkArchive when you want the user to see progress, resolve conflicts, or when operating on many files at once.

fs:exists -- Check if Path Exists

Arg Type Description
path string Path to check
callback function (err, exists: boolean)

socket.fire("webapp::instance::request", "fs:exists", "input://demo-project/",
    function (err, exists) { console.log("Exists:", exists); }
);

fs:stats -- Get File/Directory Info

Arg Type Description
path string Path to inspect
callback function (err, stats: object)


fs:readStreamUrl -- Get Streaming URL

Returns a URL that can be used in &#x3C;img&#x3E;, &#x3C;video&#x3E;, &#x3C;a href&#x3E; etc. For input:// files, this returns a blob: URL.

Arg Type Description
path string File path
callback function (err, url: string)


fs:refresh -- Refresh View

Refreshes the file manager's current directory listing.

socket.fire("webapp::instance::request", "fs:refresh");

gui:sidebar:toggle -- Show or Hide Sidebar

Toggle the sidebar panel visibility. Pass false to hide, true to show.

Arg Type Description
visible boolean true to show, false to hide
callback function (err)

socket.fire("webapp::instance::request", "gui:sidebar:toggle", false); // hide
socket.fire("webapp::instance::request", "gui:sidebar:toggle", true);  // show

<button onclick="window._ws('fmSocket')&&window.fmSocket.fire('webapp::instance::request','gui:sidebar:toggle',false)">Try: Hide Sidebar</button><button onclick="window._ws('fmSocket')&&window.fmSocket.fire('webapp::instance::request','gui:sidebar:toggle',true)">Try: Show Sidebar</button>


gui:specialPaths:add -- Add Custom Path to Sidebar

Adds a named bookmark to the path bar / sidebar. Useful for creating labeled shortcuts to directories within your sandbox. These entries are tagged as __apiManaged and can be removed later.

Arg Type Description
entry object &#x7B; path, name, icon&#x3F;, description&#x3F;, paths&#x3F;, bookmark&#x3F; &#x7D;
callback function (err)

Icon Format

The icon field supports three formats:

Format Example Description
Icon library name &#x22;breeze/icons/places/64/folder-blue&#x22; Resolved via Application.icon() to /scripts/modules/icons/...
Absolute URL &#x22;https://example.com/icon.png&#x22; Used as-is (also http://, //, data:)
Wildcard match &#x22;paper*folder&#x22; * matches any path segment in the icon library

socket.fire("webapp::instance::request", "gui:specialPaths:add", {
    path: "input://my-project/",
    name: "My Project",
    icon: "breeze/icons/places/64/folder-blue",        // icon library name
    description: "Project files",
    bookmark: {
        category: "personal",
        name: "My Project",
        icon: "breeze/icons/places/64/folder-blue"
    }
}, function (err) { console.log(err || "Path added"); });

// Using an external icon URL
socket.fire("webapp::instance::request", "gui:specialPaths:add", {
    path: "input://uploads/",
    name: "Uploads",
    icon: "https://example.com/my-custom-icon.svg",    // absolute URL
    bookmark: { category: "personal", name: "Uploads", icon: "https://example.com/my-custom-icon.svg" }
});

<button onclick="window._ws('fmSocket')&&window.fmSocket.fire('webapp::instance::request','gui:specialPaths:add',{path:'input://demo-project/',name:'Demo Project',description:'Files created via API',bookmark:{category:'personal',name:'Demo Project'}},function(e){alert(e||'Special path added! Check the sidebar.')})">Try: Add "Demo Project" to Sidebar</button>

gui:specialPaths:remove -- Remove Custom Path

Removes a previously added API-managed special path by its path string.

Arg Type Description
path string The path that was added
callback function (err)

socket.fire("webapp::instance::request", "gui:specialPaths:remove",
    "input://my-project/",
    function (err) { console.log(err || "Removed"); }
);

<button onclick="window._ws('fmSocket')&&window.fmSocket.fire('webapp::instance::request','gui:specialPaths:remove','input://demo-project/',function(e){alert(e||'Special path removed')})">Try: Remove "Demo Project" from Sidebar</button>


gui:specialPaths:clear -- Clear All Sidebar Bookmarks

Removes all existing bookmarks from the sidebar (both built-in and API-managed). Useful when you want to set up a clean custom sidebar for your embedded widget.

// Clear everything, then add your own bookmarks
socket.fire("webapp::instance::request", "gui:specialPaths:clear", function () {
    socket.fire("webapp::instance::request", "gui:specialPaths:add", {
        path: "input://my-app/",
        name: "My App Files",
        icon: "breeze/icons/places/64/folder-blue",
        bookmark: { category: "project", name: "My App Files", icon: "breeze/icons/places/64/folder-blue" }
    });
});

<button onclick="window._ws('fmSocket')&&window.fmSocket.fire('webapp::instance::request','gui:specialPaths:clear',function(e){alert(e||'Sidebar cleared!')})">Try: Clear All Sidebar Bookmarks</button>


Path Bar Breadcrumb Grouping

Special paths can include a paths array with sub-rules that group multiple directory segments into a single named breadcrumb. This is how the navigation bar shows "My Project" instead of input:// / demo-project /.
Each sub-rule has:

socket.fire("webapp::instance::request", "gui:specialPaths:add", {
    path: "input://demo-project/",
    name: "Demo Project",
    icon: "/scripts/modules/icons/breeze/icons/places/64/folder-red.svg",
    // Sub-path grouping rules for the breadcrumb bar
    paths: [
        {
            rule: "src\\/",
            name: "Source Code",
            icon: "/scripts/modules/icons/breeze/icons/places/64/folder-green.svg"
        },
        {
            rule: "test\\/",
            name: "Tests"
        },
        {
            rule: "docs\\/",
            name: "Documentation"
        }
    ],
    bookmark: {
        category: "personal",
        name: "Demo Project",
        icon: "breeze/icons/places/64/folder-blue"
    }
});

With this configuration, the breadcrumb bar shows:

Without the paths rules, it would show: Demo Project / src / components (each dir is a separate breadcrumb).
<button onclick="window._ws('fmSocket')&&window.fmSocket.fire('webapp::instance::request','gui:specialPaths:clear',function(){window.fmSocket.fire('webapp::instance::request','gui:specialPaths:add',{path:'input://demo-project/',name:'Demo Project',icon:'/scripts/modules/icons/breeze/icons/places/64/folder-red.svg',paths:[{rule:'src\/',name:'Source Code', icon:'/scripts/modules/icons/breeze/icons/places/64/folder-green.svg'},{rule:'test\/',name:'Tests'},{rule:'docs\/',name:'Documentation'}],bookmark:{category:'project',name:'Demo Project',icon:'breeze/icons/places/64/folder-blue'}},function(e){if(!e){window.fmSocket.fire('webapp::instance::request','fs:mkdirp','input://demo-project/src/components/',function(){window.fmSocket.fire('webapp::instance::request','fs:mkdirp','input://demo-project/test/',function(){window.fmSocket.fire('webapp::instance::request','fs:mkdirp','input://demo-project/docs/',function(){window.fmSocket.fire('webapp::instance::request','fs:cwd','input://demo-project/');alert('Project created with grouped breadcrumbs! Navigate to src/, test/, docs/ to see the named breadcrumbs.')})})})}})})">Try: Create Project with Grouped Breadcrumbs</button>


Custom App Injection

Register your own applications that open files by MIME type. Apps can be loaded from URLs, blob URLs, or inline data URIs. See the full guide: Building Custom Apps.

apps:register -- Register Custom Apps

socket.fire("webapp::instance::request", "apps:register", {
    "my-viewer": {
        path: "https://example.com/apps/viewer.js",
        name: "My Viewer",
        icon: "paper/icons/apps/accessories-text-editor",
        comment: "Custom text viewer",
        mimetypes: ["text/plain", "text/csv"],
        args: ["%U"]
    }
}, function (err, registered) {
    console.log("Registered:", registered);
});

Inline App (Data URI)

var appCode = ';((' + (function () {
    module.exports = function () {
        var app = new ApplicationPrototype();
        var node = document.createElement("div");
        var win = null;
        var ready = new Application.Promise();
        app.bind("node", function () { return node; }, "");
        app.bind("window", function () { return win; }, "");
        app.bind("ready", function () { return ready; });
        app.bind("handleWindow", function (w) {
            win = w; w.emit("api-request::attached", []);
            w.on("api-request::ready", function (cb) { cb(ready); });
        });
        app.bind("render", function (cb) {
            win.title("Hello App");
            win.height(300); win.width(400);
            node.innerHTML = "<div style='padding:20px;text-align:center'>" +
                "<h2>Hello!</h2><p>Args: " + (app.window().env().args || []).join(", ") + "</p></div>";
            cb();
        });
        app.bind("destroy", function () {});
        app.bind("init", function () {
            app.render(function (err) {
                if (err) return ready.reject(err);
                ready.resolve(app);
            });
            return app;
        });
        return app;
    };
}).toString() + ')());';

socket.fire("webapp::instance::request", "apps:register", {
    "hello-app": {
        path: "data:application/javascript;base64," + btoa(appCode),
        name: "Hello App",
        icon: "paper/icons/apps/system-software-install",
        mimetypes: [],
        args: []
    }
});

apps:unregister -- Remove a Registered App

Arg Type Description
appName string Name of the app to remove
callback function (err)
disableDefaultApps boolean If true, allows removing built-in apps too (not just API-injected ones)

By default, only apps registered via the API (__apiInjected: true) can be unregistered. Pass true as the third argument to also remove built-in apps like code-editor, photo-editor, etc. This is useful when you want to fully customize the app environment.

// Remove a custom app
socket.fire("webapp::instance::request", "apps:unregister", "my-viewer", function (err) {
    console.log(err || "Removed");
});

// Remove a built-in app (e.g., disable the default code editor)
socket.fire("webapp::instance::request", "apps:unregister", "code-editor", function (err) {
    console.log(err || "Removed");
}, true);

// Replace a built-in app with your own
socket.fire("webapp::instance::request", "apps:unregister", "code-editor", function () {
    socket.fire("webapp::instance::request", "apps:register", {
        "code-editor": {
            path: "input://apps/my-editor.js",
            name: "My Code Editor",
            icon: "paper/icons/apps/accessories-text-editor",
            mimetypes: ["text/plain", "application/x-javascript", "text/html", "text/css"],
            args: ["%U"]
        }
    });
}, true);

apps:list -- List All Registered Apps

socket.fire("webapp::instance::request", "apps:list", function (err, apps) {
    Object.keys(apps).forEach(function (name) {
        var app = apps[name];
        console.log(name, app.__apiInjected ? "(custom)" : "(built-in)", app.name);
    });
});

Shortcuts (.lnk files)

Create clickable shortcut files that launch any app with predefined arguments. Double-clicking a .lnk file reads its JSON content and opens the specified app.

fs:createShortcut -- Create a Shortcut

Arg Type Description
path string File path for the shortcut (must end in .lnk, must be input://)
config object &#x7B; app, args&#x3F;, name&#x3F;, icon&#x3F; &#x7D;
callback function (err)

The config fields:
Field Type Description
app string App name from the registry (e.g., &#x22;photo-editor&#x22;, &#x22;code-editor&#x22;, or a custom app name)
args array Arguments to pass to the app (optional, default [])
name string Display name (optional)
icon string Icon name (optional)

// Create a shortcut to the Photo Editor
socket.fire("webapp::instance::request", "fs:createShortcut",
    "input://desktop/Photo Editor.lnk",
    {
        app: "photo-editor",
        args: [],
        name: "Photo Editor",
        icon: "paper/*/multimedia-photo-viewer.png"
    },
    function (err) { console.log(err || "Shortcut created"); }
);

// Create a shortcut that opens a specific file in Code Editor
socket.fire("webapp::instance::request", "fs:createShortcut",
    "input://desktop/Edit Config.lnk",
    {
        app: "code-editor",
        args: ["input://project/config.json"],
        name: "Edit Config"
    }
);

// Create a shortcut to a custom registered app
socket.fire("webapp::instance::request", "fs:createShortcut",
    "input://desktop/View Logs.lnk",
    {
        app: "log-viewer",
        args: ["input://logs/server.log"],
        name: "Server Logs"
    }
);

How It Works

  1. fs:createShortcut writes a JSON file at the given path:
    `json
    &#x7B;
    &#x22;__shortcut&#x22;: true,
    &#x22;app&#x22;: &#x22;photo-editor&#x22;,
    &#x22;args&#x22;: [],
    &#x22;name&#x22;: &#x22;Photo Editor&#x22;,
    &#x22;icon&#x22;: &#x22;paper/*/multimedia-photo-viewer.png&#x22;
    &#x7D;
    
    `
  1. A built-in shortcut runner app is auto-registered for .lnk files via file-handlers
  1. When you double-click the .lnk file, xdg-open matches it to the shortcut runner, which: - Reads the JSON content - Extracts app and args - Calls windowManager.open(&#x7B; run: [app].concat(args) &#x7D;) - Closes itself

Live Demo

<button onclick="window._ws('fmSocket')&&(function(s){s.fire('webapp::instance::request','fs:mkdirp','input://shortcuts/',function(){s.fire('webapp::instance::request','fs:createShortcut','input://shortcuts/Open Photo Editor.lnk',{app:'photo-editor',args:[],name:'Photo Editor',icon:'paper//multimedia-photo-viewer.png'},function(){s.fire('webapp::instance::request','fs:createShortcut','input://shortcuts/Open Code Editor.lnk',{app:'code-editor',args:[],name:'Code Editor',icon:'breezebook-edit'},function(){s.fire('webapp::instance::request','fs:createShortcut','input://shortcuts/Open Recorder.lnk',{app:'recorder',args:[],name:'Recorder',icon:'breeze/icons/actions/16/media-record'},function(){s.fire('webapp::instance::request','fs:cwd','input://shortcuts/',function(){alert('Shortcuts created! Double-click any .lnk file to launch the app.')})})})})})})(window.fmSocket)">Try: Create App Shortcuts</button>
This creates 3 shortcuts in input://shortcuts/:

Double-click any of them to launch the corresponding app in a new window.

gui:setHomePath -- Set Default Start Directory

Sets the starting directory when the file manager opens or when navigating "home".

Arg Type Description
path string Directory path (must be input://)
callback function (err)

socket.fire("webapp::instance::request", "gui:setHomePath", "input://my-workspace/");

<button onclick="window._ws('fmSocket')&&window.fmSocket.fire('webapp::instance::request','gui:setHomePath','input://shortcuts/',function(e){alert(e||'Home path set to input://shortcuts/')})">Try: Set Home to input://shortcuts/</button>


Virtual File Icons (viewport:addFiles)

Inject custom clickable icons into the file manager's current view. These are purely visual — they don't create actual files in the filesystem. Each icon can launch an app, open a URL, or show a custom context menu.

viewport:addFiles -- Add Virtual Icons to Current View

Arg Type Description
path string (optional) Directory path to show the icons in (defaults to current directory)
files Array Array of virtual file definitions
callback function (err, count)

Each file definition:
Field Type Description
name string Display name (required)
icon string Icon name or URL
title string Tooltip text
app string App to launch on double-click
args array Arguments for the app
url string URL to open on double-click (alternative to app)
contextmenu array Custom right-click menu items: [&#x7B; text, icon, app&#x3F;, args&#x3F;, url&#x3F; &#x7D;]

// Add app launcher icons to the current directory
socket.fire("webapp::instance::request", "viewport:addFiles", [
    {
        name: "Open Photo Editor",
        icon: "paper/*/multimedia-photo-viewer.png",
        title: "Launch the Photo Editor",
        app: "photo-editor",
        args: []
    },
    {
        name: "Visit SGApps.IO",
        icon: "paper/icons/apps/internet-web-browser",
        title: "Open website in new tab",
        url: "https://sgapps.io"
    },
    {
        name: "Project Tools",
        icon: "breeze/icons/categories/32/applications-development",
        title: "Development tools",
        app: "code-editor",
        contextmenu: [
            { text: "Open Code Editor", icon: "breeze*book-edit", app: "code-editor" },
            { text: "Open Photo Editor", icon: "paper*multimedia-photo-viewer", app: "photo-editor" },
            { text: "Open Recorder", icon: "breeze/icons/actions/16/media-record", app: "recorder" }
        ]
    }
]);

// Add icons to a specific path
socket.fire("webapp::instance::request", "viewport:addFiles",
    "input://my-folder/",
    [{ name: "My App", icon: "paper/icons/apps/system-software-install", app: "my-custom-app" }]
);

Virtual icons are:

<button onclick="window._ws('fmSocket')&&window.fmSocket.fire('webapp::instance::request','fs:mkdirp','input://desktop/',function(){window.fmSocket.fire('webapp::instance::request','viewport:addFiles','input://desktop/',[{name:'Photo Editor',icon:'paper//multimedia-photo-viewer.png',title:'Launch Photo Editor',app:'photo-editor',args:[]},{name:'Code Editor',icon:'breezebook-edit',title:'Launch Code Editor',app:'code-editor',args:[]},{name:'Recorder',icon:'breeze/icons/actions/16/media-record',title:'Launch Recorder',app:'recorder',args:[]},{name:'SGApps Website',icon:'paper/icons/apps/internet-web-browser',title:'Open sgapps.io',url:'https://sgapps.io'},{name:'Tools',icon:'breeze/icons/categories/32/applications-development',title:'Dev tools menu',app:'code-editor',contextmenu:[{text:'Code Editor',icon:'breezebook-edit',app:'code-editor'},{text:'Photo Editor',icon:'papermultimedia-photo-viewer',app:'photo-editor'},{text:'File Manager',icon:'breeze/icons/apps/48/system-file-manager',app:'file-manager'}]}],function(e,n){window.fmSocket.fire('webapp::instance::request','fs:cwd','input://desktop/');alert(e||('Added '+n+' virtual icons to desktop! Double-click any icon to launch the app. Right-click Tools for a menu.'))})})">Try: Add App Icons to Desktop</button>


Complete Example: Build a Project Structure via API

This example creates a complete project with multiple files and directories using only the API:

<!DOCTYPE html>
<html>
<head><title>File Manager API Demo</title></head>
<body>
    <div style="display:flex;gap:8px;padding:10px;flex-wrap:wrap;">
        <button id="create-project">Create Project</button>
        <button id="list-files">List Files</button>
        <button id="read-file">Read package.json</button>
    </div>

    <iframe id="fm" src="https://sgapps.io/online/webapp/file-manager"
        width="100%" height="500" frameborder="0"></iframe>

    <pre id="output" style="background:#f6f8fa;padding:10px;max-height:200px;overflow:auto;"></pre>

    <script src="https://sgapps.io/components/application-prototype/ApplicationPrototype.js"></script>
    <script src="https://sgapps.io/components/window-socket/index.js"></script>
    <script>
        var socket = new WindowSocket();
        socket.start();
        var ready = false;
        socket.on("webapp::connection::ping", function () { ready = true; });

        function req(event) {
            var args = Array.prototype.slice.call(arguments, 1);
            return new Promise(function (resolve, reject) {
                args.push(function (err, result) {
                    if (err) reject(err); else resolve(result);
                });
                socket.fire.apply(socket, ["webapp::instance::request", event].concat(args));
            });
        }

        document.getElementById('create-project').onclick = async function () {
            if (!ready) return alert("Not ready yet");
            await req("fs:mkdirp", "input://my-app/src/");
            await req("fs:mkdirp", "input://my-app/test/");
            await req("fs:write", "input://my-app/package.json",
                JSON.stringify({ name: "my-app", version: "1.0.0", main: "src/index.js" }, null, 2));
            await req("fs:write", "input://my-app/src/index.js",
                "module.exports = function greet(name) {\n    return 'Hello, ' + name + '!';\n};");
            await req("fs:write", "input://my-app/test/test.js",
                "var greet = require('../src/index');\nconsole.log(greet('World'));");
            await req("fs:write", "input://my-app/README.md",
                "# my-app\n\nA demo project created via the File Manager API.");
            await req("fs:cwd", "input://my-app/");
            document.getElementById('output').textContent = "Project created!";
        };

        document.getElementById('list-files').onclick = async function () {
            if (!ready) return;
            var files = await req("fs:ls", "input://my-app/");
            document.getElementById('output').textContent = files.map(function (f) {
                return (f.metadata._isDirectory ? "[DIR] " : "[FILE] ") + f.filename;
            }).join("\n");
        };

        document.getElementById('read-file').onclick = async function () {
            if (!ready) return;
            var content = await req("fs:read", "input://my-app/package.json");
            document.getElementById('output').textContent = content;
        };
    </script>
</body>
</html>

Features