Use Case: Project Workspace

Build a complete project workspace with custom sidebar, project files, and specialized viewers — all running in the browser with no server required.
This demo creates a Node.js project structure with package.json, source files, tests, and documentation. Custom apps handle JSON and Markdown rendering. The sidebar is configured with project-specific bookmarks.

Live Demo

<iframe id="pw-demo" src="/online/webapp/file-manager" width="100%" height="650" frameborder="0" style="border:1px solid #ccc; border-radius:4px;"></iframe>
<script>window._wsConnect('pw-demo', 'pwSocket');
window._pwSeq = function (cmds, done) {

var s = window._ws('pwSocket');
if (!s) return;
var i = 0;
var next = function () {
    if (i >= cmds.length) { if (done) done(); return; }
    var cmd = cmds[i++];
    var args = cmd.concat([function () { setTimeout(next, 5); }]);
    s.fire.apply(s, ['webapp::instance::request'].concat(args));
};
next();

};
window._pwAppCode = function (renderFnStr) {

return 'module.exports = function () {\n' +
    'var app = new ApplicationPrototype();\n' +
    'var node = document.createElement("div");\n' +
    'var win = null;\n' +
    'var ready = new Application.Promise();\n' +
    'app.bind("node", function () { return node; }, "");\n' +
    'app.bind("window", function () { return win; }, "");\n' +
    'app.bind("ready", function () { return ready; });\n' +
    'app.bind("handleWindow", function (w) {\n' +
    '    win = w;\n' +
    '    w.emit("api-request::attached", []);\n' +
    '    w.on("api-request::ready", function (cb) { cb(ready); });\n' +
    '});\n' +
    'app.bind("render", function (cb) {\n' +
    '    (' + renderFnStr + ')(app, win, node, cb);\n' +
    '});\n' +
    'app.bind("destroy", function () {});\n' +
    'app.bind("init", function () {\n' +
    '    app.render(function (err) {\n' +
    '        if (err) return ready.reject(err);\n' +
    '        ready.resolve(app);\n' +
    '    });\n' +
    '    return app;\n' +
    '});\n' +
    'return app;\n' +
'};';

};
window._pwSetup = function () {

// JSON Viewer app
var jsonViewerCode = window._pwAppCode((function (app, win, node, cb) {
    win.title('JSON Viewer'); win.icon('breeze/icons/mimetypes/64/application-json');
    win.height(400); win.width(600);
    var args = app.window().env().args || [];
    if (args[0]) {
        node.innerHTML = '

Loading...

'; Application.require('/files/api/modules/fs.js').then(function (fsModule) { fsModule().read(args[0], null, true).response('text').then(function (text) { try { var json = JSON.parse(text); var pretty = JSON.stringify(json, null, 2).replace(/&/g,'&').replace(/"$1":'); pretty = pretty.replace(/: (".*?")/g, ': $1'); pretty = pretty.replace(/: (\d+)/g, ': $1'); node.innerHTML = '
' + pretty + '
'; } catch (e) { node.innerHTML = '

Parse Error

' + e.message + '

'; } }); }); } cb(); }).toString());
// Markdown Preview app
var mdPreviewCode = window._pwAppCode((function (app, win, node, cb) {
    win.title('Markdown Preview'); win.icon('breeze/icons/mimetypes/16/text-x-markdown');
    win.height(450); win.width(650);
    var args = app.window().env().args || [];
    if (args[0]) {
        node.innerHTML = '

Rendering...

'; Application.require('/files/api/modules/fs.js').then(function (fsModule) { fsModule().read(args[0], null, true).response('text').then(function (text) { var html = text .replace(/^### (.+)$/gm, '

$1

') .replace(/^## (.+)$/gm, '

$1

') .replace(/^# (.+)$/gm, '

$1

') .replace(/\*\*(.+?)\*\*/g, '$1') .replace(/\*(.+?)\*/g, '$1') .replace(/`([^`]+)`/g, '$1') .replace(/^- (.+)$/gm, '
  • $1
  • ') .replace(/\n\n/g, '

    ') .replace(/\n/g, '
    '); node.innerHTML = '

    ' + html + '

    '; }); }); } cb(); }).toString());
    var pkgJson = JSON.stringify({
        name: "my-webapp",
        version: "2.1.0",
        description: "A demo web application project",
        main: "src/index.js",
        scripts: { start: "node src/index.js", test: "jest --coverage", build: "webpack --mode production", lint: "eslint src/" },
        dependencies: { express: "^4.18.2", lodash: "^4.17.21", cors: "^2.8.5" },
        devDependencies: { jest: "^29.7.0", eslint: "^8.56.0", webpack: "^5.90.0" },
        license: "MIT"
    }, null, 2);
    var indexJs = [
        "const express = require('express');",
        "const cors = require('cors');",
        "const { greet, sum } = require('./utils');",
        "",
        "const app = express();",
        "app.use(cors());",
        "app.use(express.json());",
        "",
        "app.get('/', (req, res) => {",
        "    res.json({ message: greet('World'), status: 'running' });",
        "});",
        "",
        "app.get('/api/add/:a/:b', (req, res) => {",
        "    const result = sum(Number(req.params.a), Number(req.params.b));",
        "    res.json({ result });",
        "});",
        "",
        "const PORT = process.env.PORT || 3000;",
        "app.listen(PORT, () => {",
        "    console.log(`Server running on port ${PORT}`);",
        "});"
    ].join('\n');
    var utilsJs = [
        "/**",
        " * Greet a person by name",
        " * @param {string} name",
        " * @returns {string}",
        " */",
        "function greet(name) {",
        "    return `Hello, ${name}!`;",
        "}",
        "",
        "/**",
        " * Sum two numbers",
        " * @param {number} a",
        " * @param {number} b",
        " * @returns {number}",
        " */",
        "function sum(a, b) {",
        "    return a + b;",
        "}",
        "",
        "module.exports = { greet, sum };"
    ].join('\n');
    var testJs = [
        "const { greet, sum } = require('../src/utils');",
        "",
        "describe('greet', () => {",
        "    test('returns greeting', () => {",
        "        expect(greet('Alice')).toBe('Hello, Alice!');",
        "    });",
        "});",
        "",
        "describe('sum', () => {",
        "    test('adds numbers', () => {",
        "        expect(sum(2, 3)).toBe(5);",
        "    });",
        "    test('handles negatives', () => {",
        "        expect(sum(-1, 1)).toBe(0);",
        "    });",
        "});"
    ].join('\n');
    var readmeMd = [
        "# my-webapp",
        "",
        "A demo web application built with **Express.js**.",
        "",
        "## Quick Start",
        "",
        "```bash",
        "npm install",
        "npm start",
        "```",
        "",
        "The server starts on `http://localhost:3000`.",
        "",
        "## API Endpoints",
        "",
        "- `GET /` — Health check, returns greeting",
        "- `GET /api/add/:a/:b` — Sum two numbers",
        "",
        "## Project Structure",
        "",
        "- `src/index.js` — Express server entry point",
        "- `src/utils.js` — Utility functions",
        "- `test/utils.test.js` — Jest test suite",
        "",
        "## Development",
        "",
        "Run tests: `npm test`",
        "",
        "Lint code: `npm run lint`",
        "",
        "Build for production: `npm run build`",
        "",
        "## License",
        "",
        "MIT"
    ].join('\n');
    window._pwSeq([
        ['gui:specialPaths:clear'],
        ['gui:sidebar:toggle', false],
        ['fs:mkdirp', 'input://apps/'],
        ['fs:mkdirp', 'input://my-webapp/src/'],
        ['fs:mkdirp', 'input://my-webapp/test/'],
        ['fs:mkdirp', 'input://my-webapp/docs/'],
        ['fs:write', 'input://apps/json-viewer.js', jsonViewerCode],
        ['fs:write', 'input://apps/md-preview.js', mdPreviewCode],
        ['fs:write', 'input://my-webapp/package.json', pkgJson],
        ['fs:write', 'input://my-webapp/src/index.js', indexJs],
        ['fs:write', 'input://my-webapp/src/utils.js', utilsJs],
        ['fs:write', 'input://my-webapp/test/utils.test.js', testJs],
        ['fs:write', 'input://my-webapp/README.md', readmeMd],
        ['fs:write', 'input://my-webapp/.gitignore', 'node_modules/\ndist/\ncoverage/\n.env\n'],
        ['fs:write', 'input://my-webapp/docs/api.md', '# API Documentation\n\n## GET /\n\nReturns a JSON greeting.\n\n**Response:**\n```json\n{ "message": "Hello, World!", "status": "running" }\n```\n\n## GET /api/add/:a/:b\n\nAdds two numbers.\n\n**Example:** `/api/add/5/3` returns `{ "result": 8 }`'],
        ['apps:register', {
            'json-viewer': { path: 'input://apps/json-viewer.js', name: 'JSON Viewer', icon: 'breeze/icons/mimetypes/64/application-json', comment: 'Pretty-print JSON', mimetypes: ['application/json'], args: ['%U'] },
            'md-preview': { path: 'input://apps/md-preview.js', name: 'Markdown Preview', icon: 'breeze/icons/mimetypes/16/text-x-markdown', comment: 'Render Markdown', mimetypes: ['text/markdown', 'text/x-markdown'], args: ['%U'] }
        }],
        ['gui:specialPaths:add', { path: 'input://my-webapp/', name: 'my-webapp', icon: 'breeze/icons/places/64/folder-development', paths: [{ rule: 'src\\/', name: 'Source' }, { rule: 'test\\/', name: 'Tests' }, { rule: 'docs\\/', name: 'Docs' }], bookmark: { category: 'project', name: 'my-webapp', icon: 'breeze/icons/places/64/folder-development' } }],
        ['gui:sidebar:toggle', true],
        ['fs:cwd', 'input://my-webapp/']
    ], function () {
        alert('Project workspace ready!\n\nTry:\n- Double-click package.json (opens JSON Viewer)\n- Double-click README.md (opens Markdown Preview)\n- Double-click .js files (opens Code Editor)\n- Navigate via sidebar bookmark "my-webapp"\n- Check breadcrumbs: my-webapp > Source > ...');
    });

    };</script>
    <button onclick="window._pwSetup()" style="padding:10px 20px;font-size:15px;cursor:pointer">Setup Project Workspace</button>


    What This Demo Does

    1. Clears the sidebar and sets up a fresh workspace
    2. Creates the project structure:
      `
      input://my-webapp/
      package.json        ← opens with JSON Viewer (syntax-highlighted)
      README.md           ← opens with Markdown Preview (rendered HTML)
      .gitignore
      src/
      index.js          ← opens with Code Editor
      utils.js          ← opens with Code Editor
      test/
      utils.test.js     ← opens with Code Editor
      docs/
      api.md            ← opens with Markdown Preview
      
      `
    3. Registers two custom apps — JSON Viewer and Markdown Preview
    4. Configures the sidebar with a "my-webapp" bookmark
    5. Configures breadcrumb grouping — src/ shows as "Source", test/ as "Tests", docs/ as "Docs"

    How to Explore

    After clicking "Setup Project Workspace":

    The Code Behind It

    This workspace is built entirely via the API — no server, no uploads, no authentication. Every file exists in browser memory only.

    Key API Calls Used

    // 1. Create project structure
    socket.fire("webapp::instance::request", "fs:mkdirp", "input://my-webapp/src/");
    socket.fire("webapp::instance::request", "fs:mkdirp", "input://my-webapp/test/");
    
    // 2. Write files
    socket.fire("webapp::instance::request", "fs:write",
        "input://my-webapp/package.json", JSON.stringify(pkg, null, 2));
    
    // 3. Register custom apps for specific file types
    socket.fire("webapp::instance::request", "apps:register", {
        "json-viewer": {
            path: "input://apps/json-viewer.js",
            mimetypes: ["application/json"],
            ...
        },
        "md-preview": {
            path: "input://apps/md-preview.js",
            mimetypes: ["text/markdown", "text/x-markdown"],
            ...
        }
    });
    
    // 4. Configure sidebar with breadcrumb grouping
    socket.fire("webapp::instance::request", "gui:specialPaths:add", {
        path: "input://my-webapp/",
        name: "my-webapp",
        paths: [
            { rule: "src\\/", name: "Source" },
            { rule: "test\\/", name: "Tests" },
            { rule: "docs\\/", name: "Docs" }
        ],
        bookmark: { category: "project", name: "my-webapp", icon: "..." }
    });
    sgapps.io