Use Case: Third-Party Web App Desktop

Build a virtual desktop with shortcuts, a custom home folder, and a library of apps — all integrated into the File Manager using shortcuts, custom viewers, and the input:// filesystem.

Live Demo

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

var s = window._ws('tpSocket');
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._tpAppCode = 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._tpSetup = function () {

// --- CSV Table Viewer ---
var csvViewerCode = window._tpAppCode((function (app, win, node, cb) {
    win.title('CSV Table'); win.icon('breeze/icons/mimetypes/64/text-csv');
    win.height(450); win.width(700); win.scrolling(true);
    var args = app.window().env().args || [];
    if (!args[0]) { cb(); return; }
    node.innerHTML = '

Parsing CSV...

'; Application.require('/files/api/modules/fs.js').then(function (fsModule) { fsModule().read(args[0], null, true).response('text').then(function (text) { var rows = text.trim().split('\n').map(function (r) { return r.split(',').map(function (c) { return c.trim(); }); }); var html = ''; rows.forEach(function (row, i) { html += ''; row.forEach(function (cell) { var tag = i === 0 ? 'th' : 'td'; var style = i === 0 ? 'background:#4a6cf7;color:#fff;padding:8px 12px;text-align:left;font-weight:600;position:sticky;top:0' : 'padding:6px 12px;border-bottom:1px solid #eee;background:' + (i % 2 ? '#f8f9fa' : '#fff'); html += '<' + tag + ' style="' + style + '">' + cell.replace(/'; }); html += ''; }); html += '
'; node.innerHTML = html; }); }); cb(); }).toString());
// --- Log Viewer (color-coded) ---
var logViewerCode = window._tpAppCode((function (app, win, node, cb) {
    win.title('Log Viewer'); win.icon('breeze/icons/apps/48/utilities-log-viewer');
    win.height(450); win.width(700); win.scrolling(true);
    var args = app.window().env().args || [];
    if (!args[0]) { cb(); return; }
    node.innerHTML = '

Loading logs...

'; Application.require('/files/api/modules/fs.js').then(function (fsModule) { fsModule().read(args[0], null, true).response('text').then(function (text) { var lines = text.split('\n').map(function (line) { var color = '#ccc'; if (line.match(/\b(error|fatal|fail)\b/i)) color = '#e74c3c'; else if (line.match(/\b(warn|warning)\b/i)) color = '#f39c12'; else if (line.match(/\b(info)\b/i)) color = '#3498db'; else if (line.match(/\b(debug)\b/i)) color = '#888'; else if (line.match(/\b(success|ok|done)\b/i)) color = '#27ae60'; return '
' + line.replace(/'; }).join(''); node.innerHTML = '
' + lines + '
'; }); }); cb(); }).toString());
// --- HTML Preview (renders HTML in iframe sandbox) ---
var htmlPreviewCode = window._tpAppCode((function (app, win, node, cb) {
    win.title('HTML Preview'); win.icon('paper/icons/apps/internet-web-browser');
    win.height(500); win.width(700);
    var args = app.window().env().args || [];
    if (!args[0]) { cb(); return; }
    node.innerHTML = '

Loading...

'; Application.require('/files/api/modules/fs.js').then(function (fsModule) { fsModule().read(args[0], null, true).response('text').then(function (text) { var iframe = document.createElement('iframe'); iframe.style.cssText = 'width:100%;height:100%;border:none'; iframe.sandbox = 'allow-scripts'; node.innerHTML = ''; node.appendChild(iframe); iframe.srcdoc = text; }); }); cb(); }).toString());
// --- Hex Viewer ---
var hexViewerCode = window._tpAppCode((function (app, win, node, cb) {
    win.title('Hex Viewer'); win.icon('breeze/icons/apps/48/okteta');
    win.height(450); win.width(700); win.scrolling(true);
    var args = app.window().env().args || [];
    if (!args[0]) { cb(); return; }
    node.innerHTML = '

Reading bytes...

'; Application.require('/files/api/modules/fs.js').then(function (fsModule) { fsModule().read(args[0], function (err, data) { if (err || !data) { node.innerHTML = '

Error reading file

'; return; } var bytes = new Uint8Array(data); var html = '
'; for (var i = 0; i < Math.min(bytes.length, 4096); i += 16) { var offset = ('0000000' + i.toString(16)).slice(-8); var hexPart = ''; var ascPart = ''; for (var j = 0; j < 16; j++) { if (i + j < bytes.length) { var b = bytes[i + j]; hexPart += ('0' + b.toString(16)).slice(-2) + ' '; ascPart += (b >= 32 && b < 127) ? String.fromCharCode(b) : '.'; } else { hexPart += ' '; ascPart += ' '; } if (j === 7) hexPart += ' '; } html += '
' + offset + ' ' + hexPart + ' ' + ascPart.replace(/
'; } if (bytes.length > 4096) html += '
... showing first 4096 of ' + bytes.length + ' bytes
'; html += '
'; node.innerHTML = html; }); }); cb(); }).toString());
// --- Color palette viewer for .colors files ---
var colorViewerCode = window._tpAppCode((function (app, win, node, cb) {
    win.title('Color Palette'); win.icon('paper/small/apps/preferences-color-symbolic');
    win.height(350); win.width(450);
    var args = app.window().env().args || [];
    if (!args[0]) { cb(); return; }
    Application.require('/files/api/modules/fs.js').then(function (fsModule) {
        fsModule().read(args[0], null, true).response('text').then(function (text) {
            var colors = text.split('\n').map(function (l) { return l.trim(); }).filter(function (l) { return l && l.charAt(0) !== '#'; });
            var html = '
'; html += '

Color Palette (' + colors.length + ' colors)

'; html += '
'; colors.forEach(function (c) { var parts = c.split(/\s+/); var color = parts[0]; var name = parts.slice(1).join(' ') || color; html += '
'; html += '
'; html += '
' + name + '
'; html += '
' + color + '
'; html += '
'; }); html += '
'; node.innerHTML = html; }); }); cb(); }).toString());
// --- Sample data files ---
var csvData = 'Name,Email,Role,Status\nAlice Smith,alice@example.com,Engineer,Active\nBob Jones,bob@example.com,Designer,Active\nCharlie Brown,charlie@example.com,Manager,On Leave\nDiana Prince,diana@example.com,Engineer,Active\nEve Wilson,eve@example.com,QA,Active\nFrank Miller,frank@example.com,DevOps,Active';
var logData = [
    '2026-04-03 08:00:01 [INFO] Server starting on port 3000',
    '2026-04-03 08:00:02 [INFO] Database connected successfully',
    '2026-04-03 08:00:03 [DEBUG] Loading middleware: cors, compression, auth',
    '2026-04-03 08:01:15 [INFO] Request: GET /api/users (200) 45ms',
    '2026-04-03 08:02:30 [WARNING] Rate limit approaching for IP 192.168.1.50',
    '2026-04-03 08:03:45 [INFO] Request: POST /api/orders (201) 120ms',
    '2026-04-03 08:05:00 [ERROR] Failed to send email: SMTP connection timeout',
    '2026-04-03 08:05:01 [INFO] Retry email send in 30s',
    '2026-04-03 08:05:31 [SUCCESS] Email sent successfully to user@example.com',
    '2026-04-03 08:10:00 [WARNING] Memory usage at 85%',
    '2026-04-03 08:15:22 [ERROR] Unhandled promise rejection in /api/export',
    '2026-04-03 08:15:23 [FATAL] Process crashed — restarting...',
    '2026-04-03 08:15:25 [INFO] Server restarted successfully',
    '2026-04-03 08:20:00 [DEBUG] Garbage collection completed: freed 128MB'
].join('\n');
var htmlPage = '\n\n\n\n\n\n

Interactive Page

\n
\n

Counter Demo

\n

Count: 0

\n \n
\n
\n

Form Demo

\n \n
\n\n'; var colorsData = '# Brand Colors\n#4a6cf7 Primary Blue\n#6a3cf7 Purple Accent\n#27ae60 Success Green\n#e74c3c Error Red\n#f39c12 Warning Orange\n#3498db Info Blue\n\n# Neutral\n#1a1a2e Dark Navy\n#333333 Text Dark\n#666666 Text Medium\n#999999 Text Light\n#f8f9fa Background';
window._tpSeq([
    ['gui:specialPaths:clear'],
    ['fs:mkdirp', 'input://apps/'],
    ['fs:mkdirp', 'input://desktop/'],
    ['fs:mkdirp', 'input://desktop/Documents/'],
    ['fs:mkdirp', 'input://desktop/Data/'],
    ['fs:mkdirp', 'input://desktop/Logs/'],
    ['fs:mkdirp', 'input://desktop/Web/'],
    ['fs:mkdirp', 'input://desktop/Design/'],
    // Write app modules
    ['fs:write', 'input://apps/csv-viewer.js', csvViewerCode],
    ['fs:write', 'input://apps/log-viewer.js', logViewerCode],
    ['fs:write', 'input://apps/html-preview.js', htmlPreviewCode],
    ['fs:write', 'input://apps/hex-viewer.js', hexViewerCode],
    ['fs:write', 'input://apps/color-viewer.js', colorViewerCode],
    // Write data files
    ['fs:write', 'input://desktop/Data/team-roster.csv', csvData],
    ['fs:write', 'input://desktop/Logs/server.log', logData],
    ['fs:write', 'input://desktop/Web/interactive-page.html', htmlPage],
    ['fs:write', 'input://desktop/Design/brand.colors', colorsData],
    ['fs:write', 'input://desktop/Documents/notes.txt', 'Meeting notes - April 2026\n\n- Discussed Q2 roadmap\n- Archive viewer shipped\n- Custom apps API released\n- Next: real-time collaboration'],
    ['fs:write', 'input://desktop/Documents/data.json', JSON.stringify({project:"webapp-desktop",version:"1.0",features:["csv-viewer","log-viewer","html-preview","hex-viewer","color-palette","shortcuts"],status:"demo"}, null, 2)],
    // Create shortcut files
    ['fs:createShortcut', 'input://desktop/Open Photo Editor.lnk', { app: 'photo-editor', args: [], name: 'Photo Editor', icon: 'paper/*/multimedia-photo-viewer.png' }],
    ['fs:createShortcut', 'input://desktop/Open Code Editor.lnk', { app: 'code-editor', args: [], name: 'Code Editor', icon: 'breeze*book-edit' }],
    // Register all custom apps
    ['apps:register', {
        'csv-viewer': { path: 'input://apps/csv-viewer.js', name: 'CSV Table Viewer', icon: 'breeze/icons/mimetypes/64/text-csv', comment: 'View CSV as styled table', mimetypes: ['text/csv'], 'file-handlers': ['\\.csv
    
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

], args: ['%U'] },\n            'log-viewer': { path: 'input://apps/log-viewer.js', name: 'Log Viewer', icon: 'breeze/icons/apps/48/utilities-log-viewer', comment: 'Color-coded log viewer', 'file-handlers': ['\\\\.log
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

], args: ['%U'] },\n            'html-preview': { path: 'input://apps/html-preview.js', name: 'HTML Preview', icon: 'paper/icons/apps/internet-web-browser', comment: 'Preview HTML in sandbox', mimetypes: ['text/html'], args: ['%U'] },\n            'hex-viewer': { path: 'input://apps/hex-viewer.js', name: 'Hex Viewer', icon: 'breeze/icons/apps/48/okteta', comment: 'View file bytes in hex', mimetypes: [], 'file-handlers': ['\\\\.bin
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

, '\\\\.dat
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

], args: ['%U'] },\n            'color-viewer': { path: 'input://apps/color-viewer.js', name: 'Color Palette', icon: 'paper/small/apps/preferences-color-symbolic', comment: 'View color palettes', mimetypes: [], 'file-handlers': ['\\\\.colors
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

, '\\\\.palette
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

], args: ['%U'] }\n        }],\n        // Setup sidebar\n        ['gui:specialPaths:add', { path: 'input://desktop/', name: 'Desktop', icon: 'breeze/icons/places/64/user-desktop', paths: [{ rule: 'Documents\\\\/', name: 'Documents' }, { rule: 'Data\\\\/', name: 'Data' }, { rule: 'Logs\\\\/', name: 'Logs' }, { rule: 'Web\\\\/', name: 'Web' }, { rule: 'Design\\\\/', name: 'Design' }], bookmark: { category: 'workspace', name: 'Desktop', icon: 'breeze/icons/places/64/user-desktop' } }],\n        ['gui:setHomePath', 'input://desktop/']\n    ], function () {\n        alert('Virtual Desktop ready!\\n\\n' +\n            'Try double-clicking:\\n' +\n            '- team-roster.csv (CSV Table Viewer)\\n' +\n            '- server.log (Color-coded Log Viewer)\\n' +\n            '- interactive-page.html (HTML Preview with sandbox)\\n' +\n            '- brand.colors (Color Palette Viewer)\\n' +\n            '- Open Photo Editor.lnk (Shortcut)\\n' +\n            '- data.json (JSON in Code Editor)\\n\\n' +\n            'Navigate via sidebar: Desktop > Documents, Data, Logs...');\n    });\n};\n<\/script>\n\n\n\n---\n\n## What Gets Created\n\n### 5 Custom Apps\n\n| App | Extensions | What It Does |\n|-----|-----------|-------------|\n| **CSV Table Viewer** | `.csv` | Parses CSV and renders as a styled table with sticky headers, zebra striping |\n| **Log Viewer** | `.log` | Color-codes log lines by severity: red (error/fatal), orange (warning), blue (info), green (success), grey (debug) |\n| **HTML Preview** | `.html` | Renders HTML in a sandboxed iframe with `allow-scripts` — interactive elements work |\n| **Hex Viewer** | `.bin`, `.dat` | Shows raw bytes in classic hex editor layout: offset + hex + ASCII (first 4KB) |\n| **Color Palette** | `.colors`, `.palette` | Parses color definitions and renders visual swatches with names and hex codes |\n\n### File Structure\n\n```\ninput://desktop/\n  Documents/\n    notes.txt           ← plain text (opens in Code Editor)\n    data.json           ← JSON (opens in Code Editor)\n  Data/\n    team-roster.csv     ← CSV (opens in CSV Table Viewer)\n  Logs/\n    server.log          ← log file (opens in Log Viewer)\n  Web/\n    interactive-page.html  ← HTML (opens in HTML Preview)\n  Design/\n    brand.colors        ← color palette (opens in Color Palette)\n  Open Photo Editor.lnk   ← shortcut (launches Photo Editor)\n  Open Code Editor.lnk    ← shortcut (launches Code Editor)\n```\n\n### Shortcuts (.lnk files)\n\nDouble-click a `.lnk` file to launch the specified app. The shortcut runner reads the JSON inside and opens the target app:\n\n```json\n{\n    \"__shortcut\": true,\n    \"app\": \"photo-editor\",\n    \"args\": [],\n    \"name\": \"Photo Editor\",\n    \"icon\": \"paper/*/multimedia-photo-viewer.png\"\n}\n```\n\n---\n\n## New API Events Used\n\n### `gui:setHomePath` -- Set Default Directory\n\nSets the starting directory when the file manager opens.\n\n```js\nsocket.fire(\"webapp::instance::request\", \"gui:setHomePath\", \"input://desktop/\");\n```\n\n### `fs:createShortcut` -- Create App Shortcuts\n\nCreates a `.lnk` file that launches a specific app when double-clicked.\n\n| Arg | Type | Description |\n|-----|------|-------------|\n| `path` | string | Where to create the shortcut (must be `input://`) |\n| `config` | object | `{ app, args?, name?, icon? }` |\n| `callback` | function | `(err)` |\n\n```js\nsocket.fire(\"webapp::instance::request\", \"fs:createShortcut\",\n    \"input://desktop/My Editor.lnk\",\n    { app: \"code-editor\", args: [], name: \"Code Editor\", icon: \"breeze*book-edit\" }\n);\n```\n\n---\n\n## App Code Patterns\n\n### CSV Table Viewer\n\nKey technique: split by newlines, split by commas, first row becomes `` headers:\n\n```js\nvar rows = text.split('\\n').map(function (r) {\n    return r.split(',').map(function (c) { return c.trim(); });\n});\n// First row = headers, rest = data rows\n```\n\n### Log Viewer\n\nKey technique: regex-match severity keywords and assign colors:\n\n```js\nif (line.match(/\\b(error|fatal)\\b/i)) color = '#e74c3c';\nelse if (line.match(/\\b(warn)\\b/i)) color = '#f39c12';\nelse if (line.match(/\\b(info)\\b/i)) color = '#3498db';\n```\n\n### HTML Preview\n\nKey technique: sandboxed iframe with `srcdoc`:\n\n```js\nvar iframe = document.createElement('iframe');\niframe.sandbox = 'allow-scripts';\niframe.srcdoc = htmlContent;\n```\n\n### Hex Viewer\n\nKey technique: read raw bytes via callback (not stream), format in 16-byte rows:\n\n```js\nfsModule().read(path, function (err, data) {\n    var bytes = new Uint8Array(data);\n    // offset  hex hex hex ... hex  ascii\n});\n```\n\n### Color Palette Viewer\n\nKey technique: parse lines as `#hexcode Name`, render colored divs:\n\n```js\nvar parts = line.split(/\\s+/);\nvar color = parts[0];  // \"#4a6cf7\"\nvar name = parts.slice(1).join(' ');  // \"Primary Blue\"\n```\n\n---\n\n## Combining Everything\n\nThese apps work together with the other use cases:\n\n- **Click a `.csv` in the CMS** → opens in CSV Table Viewer\n- **View server logs** while editing the project in the [Project Workspace](/use-cases/project-workspace.md)\n- **Preview HTML** pages from the [CMS Content Editor](/use-cases/cms-content-editor.md)\n- **Add shortcuts** to frequently used tools on the desktop\n- **Create your own format handlers** following the [Custom File Format](/use-cases/custom-file-format.md) pattern\n";window.$preloadedPath = "/webapp-docs/use-cases/thirdparty-webapps";
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

], args: ['%U'] },
        'log-viewer': { path: 'input://apps/log-viewer.js', name: 'Log Viewer', icon: 'breeze/icons/apps/48/utilities-log-viewer', comment: 'Color-coded log viewer', 'file-handlers': ['\\.log
    
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

], args: ['%U'] },\n            'log-viewer': { path: 'input://apps/log-viewer.js', name: 'Log Viewer', icon: 'breeze/icons/apps/48/utilities-log-viewer', comment: 'Color-coded log viewer', 'file-handlers': ['\\\\.log
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

], args: ['%U'] },\n            'html-preview': { path: 'input://apps/html-preview.js', name: 'HTML Preview', icon: 'paper/icons/apps/internet-web-browser', comment: 'Preview HTML in sandbox', mimetypes: ['text/html'], args: ['%U'] },\n            'hex-viewer': { path: 'input://apps/hex-viewer.js', name: 'Hex Viewer', icon: 'breeze/icons/apps/48/okteta', comment: 'View file bytes in hex', mimetypes: [], 'file-handlers': ['\\\\.bin
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

, '\\\\.dat
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

], args: ['%U'] },\n            'color-viewer': { path: 'input://apps/color-viewer.js', name: 'Color Palette', icon: 'paper/small/apps/preferences-color-symbolic', comment: 'View color palettes', mimetypes: [], 'file-handlers': ['\\\\.colors
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

, '\\\\.palette
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

], args: ['%U'] }\n        }],\n        // Setup sidebar\n        ['gui:specialPaths:add', { path: 'input://desktop/', name: 'Desktop', icon: 'breeze/icons/places/64/user-desktop', paths: [{ rule: 'Documents\\\\/', name: 'Documents' }, { rule: 'Data\\\\/', name: 'Data' }, { rule: 'Logs\\\\/', name: 'Logs' }, { rule: 'Web\\\\/', name: 'Web' }, { rule: 'Design\\\\/', name: 'Design' }], bookmark: { category: 'workspace', name: 'Desktop', icon: 'breeze/icons/places/64/user-desktop' } }],\n        ['gui:setHomePath', 'input://desktop/']\n    ], function () {\n        alert('Virtual Desktop ready!\\n\\n' +\n            'Try double-clicking:\\n' +\n            '- team-roster.csv (CSV Table Viewer)\\n' +\n            '- server.log (Color-coded Log Viewer)\\n' +\n            '- interactive-page.html (HTML Preview with sandbox)\\n' +\n            '- brand.colors (Color Palette Viewer)\\n' +\n            '- Open Photo Editor.lnk (Shortcut)\\n' +\n            '- data.json (JSON in Code Editor)\\n\\n' +\n            'Navigate via sidebar: Desktop > Documents, Data, Logs...');\n    });\n};\n<\/script>\n\n\n\n---\n\n## What Gets Created\n\n### 5 Custom Apps\n\n| App | Extensions | What It Does |\n|-----|-----------|-------------|\n| **CSV Table Viewer** | `.csv` | Parses CSV and renders as a styled table with sticky headers, zebra striping |\n| **Log Viewer** | `.log` | Color-codes log lines by severity: red (error/fatal), orange (warning), blue (info), green (success), grey (debug) |\n| **HTML Preview** | `.html` | Renders HTML in a sandboxed iframe with `allow-scripts` — interactive elements work |\n| **Hex Viewer** | `.bin`, `.dat` | Shows raw bytes in classic hex editor layout: offset + hex + ASCII (first 4KB) |\n| **Color Palette** | `.colors`, `.palette` | Parses color definitions and renders visual swatches with names and hex codes |\n\n### File Structure\n\n```\ninput://desktop/\n  Documents/\n    notes.txt           ← plain text (opens in Code Editor)\n    data.json           ← JSON (opens in Code Editor)\n  Data/\n    team-roster.csv     ← CSV (opens in CSV Table Viewer)\n  Logs/\n    server.log          ← log file (opens in Log Viewer)\n  Web/\n    interactive-page.html  ← HTML (opens in HTML Preview)\n  Design/\n    brand.colors        ← color palette (opens in Color Palette)\n  Open Photo Editor.lnk   ← shortcut (launches Photo Editor)\n  Open Code Editor.lnk    ← shortcut (launches Code Editor)\n```\n\n### Shortcuts (.lnk files)\n\nDouble-click a `.lnk` file to launch the specified app. The shortcut runner reads the JSON inside and opens the target app:\n\n```json\n{\n    \"__shortcut\": true,\n    \"app\": \"photo-editor\",\n    \"args\": [],\n    \"name\": \"Photo Editor\",\n    \"icon\": \"paper/*/multimedia-photo-viewer.png\"\n}\n```\n\n---\n\n## New API Events Used\n\n### `gui:setHomePath` -- Set Default Directory\n\nSets the starting directory when the file manager opens.\n\n```js\nsocket.fire(\"webapp::instance::request\", \"gui:setHomePath\", \"input://desktop/\");\n```\n\n### `fs:createShortcut` -- Create App Shortcuts\n\nCreates a `.lnk` file that launches a specific app when double-clicked.\n\n| Arg | Type | Description |\n|-----|------|-------------|\n| `path` | string | Where to create the shortcut (must be `input://`) |\n| `config` | object | `{ app, args?, name?, icon? }` |\n| `callback` | function | `(err)` |\n\n```js\nsocket.fire(\"webapp::instance::request\", \"fs:createShortcut\",\n    \"input://desktop/My Editor.lnk\",\n    { app: \"code-editor\", args: [], name: \"Code Editor\", icon: \"breeze*book-edit\" }\n);\n```\n\n---\n\n## App Code Patterns\n\n### CSV Table Viewer\n\nKey technique: split by newlines, split by commas, first row becomes `` headers:\n\n```js\nvar rows = text.split('\\n').map(function (r) {\n    return r.split(',').map(function (c) { return c.trim(); });\n});\n// First row = headers, rest = data rows\n```\n\n### Log Viewer\n\nKey technique: regex-match severity keywords and assign colors:\n\n```js\nif (line.match(/\\b(error|fatal)\\b/i)) color = '#e74c3c';\nelse if (line.match(/\\b(warn)\\b/i)) color = '#f39c12';\nelse if (line.match(/\\b(info)\\b/i)) color = '#3498db';\n```\n\n### HTML Preview\n\nKey technique: sandboxed iframe with `srcdoc`:\n\n```js\nvar iframe = document.createElement('iframe');\niframe.sandbox = 'allow-scripts';\niframe.srcdoc = htmlContent;\n```\n\n### Hex Viewer\n\nKey technique: read raw bytes via callback (not stream), format in 16-byte rows:\n\n```js\nfsModule().read(path, function (err, data) {\n    var bytes = new Uint8Array(data);\n    // offset  hex hex hex ... hex  ascii\n});\n```\n\n### Color Palette Viewer\n\nKey technique: parse lines as `#hexcode Name`, render colored divs:\n\n```js\nvar parts = line.split(/\\s+/);\nvar color = parts[0];  // \"#4a6cf7\"\nvar name = parts.slice(1).join(' ');  // \"Primary Blue\"\n```\n\n---\n\n## Combining Everything\n\nThese apps work together with the other use cases:\n\n- **Click a `.csv` in the CMS** → opens in CSV Table Viewer\n- **View server logs** while editing the project in the [Project Workspace](/use-cases/project-workspace.md)\n- **Preview HTML** pages from the [CMS Content Editor](/use-cases/cms-content-editor.md)\n- **Add shortcuts** to frequently used tools on the desktop\n- **Create your own format handlers** following the [Custom File Format](/use-cases/custom-file-format.md) pattern\n";window.$preloadedPath = "/webapp-docs/use-cases/thirdparty-webapps";
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

], args: ['%U'] },
        'html-preview': { path: 'input://apps/html-preview.js', name: 'HTML Preview', icon: 'paper/icons/apps/internet-web-browser', comment: 'Preview HTML in sandbox', mimetypes: ['text/html'], args: ['%U'] },
        'hex-viewer': { path: 'input://apps/hex-viewer.js', name: 'Hex Viewer', icon: 'breeze/icons/apps/48/okteta', comment: 'View file bytes in hex', mimetypes: [], 'file-handlers': ['\\.bin
    
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

], args: ['%U'] },\n            'log-viewer': { path: 'input://apps/log-viewer.js', name: 'Log Viewer', icon: 'breeze/icons/apps/48/utilities-log-viewer', comment: 'Color-coded log viewer', 'file-handlers': ['\\\\.log
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

], args: ['%U'] },\n            'html-preview': { path: 'input://apps/html-preview.js', name: 'HTML Preview', icon: 'paper/icons/apps/internet-web-browser', comment: 'Preview HTML in sandbox', mimetypes: ['text/html'], args: ['%U'] },\n            'hex-viewer': { path: 'input://apps/hex-viewer.js', name: 'Hex Viewer', icon: 'breeze/icons/apps/48/okteta', comment: 'View file bytes in hex', mimetypes: [], 'file-handlers': ['\\\\.bin
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

, '\\\\.dat
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

], args: ['%U'] },\n            'color-viewer': { path: 'input://apps/color-viewer.js', name: 'Color Palette', icon: 'paper/small/apps/preferences-color-symbolic', comment: 'View color palettes', mimetypes: [], 'file-handlers': ['\\\\.colors
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

, '\\\\.palette
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

], args: ['%U'] }\n        }],\n        // Setup sidebar\n        ['gui:specialPaths:add', { path: 'input://desktop/', name: 'Desktop', icon: 'breeze/icons/places/64/user-desktop', paths: [{ rule: 'Documents\\\\/', name: 'Documents' }, { rule: 'Data\\\\/', name: 'Data' }, { rule: 'Logs\\\\/', name: 'Logs' }, { rule: 'Web\\\\/', name: 'Web' }, { rule: 'Design\\\\/', name: 'Design' }], bookmark: { category: 'workspace', name: 'Desktop', icon: 'breeze/icons/places/64/user-desktop' } }],\n        ['gui:setHomePath', 'input://desktop/']\n    ], function () {\n        alert('Virtual Desktop ready!\\n\\n' +\n            'Try double-clicking:\\n' +\n            '- team-roster.csv (CSV Table Viewer)\\n' +\n            '- server.log (Color-coded Log Viewer)\\n' +\n            '- interactive-page.html (HTML Preview with sandbox)\\n' +\n            '- brand.colors (Color Palette Viewer)\\n' +\n            '- Open Photo Editor.lnk (Shortcut)\\n' +\n            '- data.json (JSON in Code Editor)\\n\\n' +\n            'Navigate via sidebar: Desktop > Documents, Data, Logs...');\n    });\n};\n<\/script>\n\n\n\n---\n\n## What Gets Created\n\n### 5 Custom Apps\n\n| App | Extensions | What It Does |\n|-----|-----------|-------------|\n| **CSV Table Viewer** | `.csv` | Parses CSV and renders as a styled table with sticky headers, zebra striping |\n| **Log Viewer** | `.log` | Color-codes log lines by severity: red (error/fatal), orange (warning), blue (info), green (success), grey (debug) |\n| **HTML Preview** | `.html` | Renders HTML in a sandboxed iframe with `allow-scripts` — interactive elements work |\n| **Hex Viewer** | `.bin`, `.dat` | Shows raw bytes in classic hex editor layout: offset + hex + ASCII (first 4KB) |\n| **Color Palette** | `.colors`, `.palette` | Parses color definitions and renders visual swatches with names and hex codes |\n\n### File Structure\n\n```\ninput://desktop/\n  Documents/\n    notes.txt           ← plain text (opens in Code Editor)\n    data.json           ← JSON (opens in Code Editor)\n  Data/\n    team-roster.csv     ← CSV (opens in CSV Table Viewer)\n  Logs/\n    server.log          ← log file (opens in Log Viewer)\n  Web/\n    interactive-page.html  ← HTML (opens in HTML Preview)\n  Design/\n    brand.colors        ← color palette (opens in Color Palette)\n  Open Photo Editor.lnk   ← shortcut (launches Photo Editor)\n  Open Code Editor.lnk    ← shortcut (launches Code Editor)\n```\n\n### Shortcuts (.lnk files)\n\nDouble-click a `.lnk` file to launch the specified app. The shortcut runner reads the JSON inside and opens the target app:\n\n```json\n{\n    \"__shortcut\": true,\n    \"app\": \"photo-editor\",\n    \"args\": [],\n    \"name\": \"Photo Editor\",\n    \"icon\": \"paper/*/multimedia-photo-viewer.png\"\n}\n```\n\n---\n\n## New API Events Used\n\n### `gui:setHomePath` -- Set Default Directory\n\nSets the starting directory when the file manager opens.\n\n```js\nsocket.fire(\"webapp::instance::request\", \"gui:setHomePath\", \"input://desktop/\");\n```\n\n### `fs:createShortcut` -- Create App Shortcuts\n\nCreates a `.lnk` file that launches a specific app when double-clicked.\n\n| Arg | Type | Description |\n|-----|------|-------------|\n| `path` | string | Where to create the shortcut (must be `input://`) |\n| `config` | object | `{ app, args?, name?, icon? }` |\n| `callback` | function | `(err)` |\n\n```js\nsocket.fire(\"webapp::instance::request\", \"fs:createShortcut\",\n    \"input://desktop/My Editor.lnk\",\n    { app: \"code-editor\", args: [], name: \"Code Editor\", icon: \"breeze*book-edit\" }\n);\n```\n\n---\n\n## App Code Patterns\n\n### CSV Table Viewer\n\nKey technique: split by newlines, split by commas, first row becomes `` headers:\n\n```js\nvar rows = text.split('\\n').map(function (r) {\n    return r.split(',').map(function (c) { return c.trim(); });\n});\n// First row = headers, rest = data rows\n```\n\n### Log Viewer\n\nKey technique: regex-match severity keywords and assign colors:\n\n```js\nif (line.match(/\\b(error|fatal)\\b/i)) color = '#e74c3c';\nelse if (line.match(/\\b(warn)\\b/i)) color = '#f39c12';\nelse if (line.match(/\\b(info)\\b/i)) color = '#3498db';\n```\n\n### HTML Preview\n\nKey technique: sandboxed iframe with `srcdoc`:\n\n```js\nvar iframe = document.createElement('iframe');\niframe.sandbox = 'allow-scripts';\niframe.srcdoc = htmlContent;\n```\n\n### Hex Viewer\n\nKey technique: read raw bytes via callback (not stream), format in 16-byte rows:\n\n```js\nfsModule().read(path, function (err, data) {\n    var bytes = new Uint8Array(data);\n    // offset  hex hex hex ... hex  ascii\n});\n```\n\n### Color Palette Viewer\n\nKey technique: parse lines as `#hexcode Name`, render colored divs:\n\n```js\nvar parts = line.split(/\\s+/);\nvar color = parts[0];  // \"#4a6cf7\"\nvar name = parts.slice(1).join(' ');  // \"Primary Blue\"\n```\n\n---\n\n## Combining Everything\n\nThese apps work together with the other use cases:\n\n- **Click a `.csv` in the CMS** → opens in CSV Table Viewer\n- **View server logs** while editing the project in the [Project Workspace](/use-cases/project-workspace.md)\n- **Preview HTML** pages from the [CMS Content Editor](/use-cases/cms-content-editor.md)\n- **Add shortcuts** to frequently used tools on the desktop\n- **Create your own format handlers** following the [Custom File Format](/use-cases/custom-file-format.md) pattern\n";window.$preloadedPath = "/webapp-docs/use-cases/thirdparty-webapps";
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

, '\\.dat
    
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

], args: ['%U'] },\n            'log-viewer': { path: 'input://apps/log-viewer.js', name: 'Log Viewer', icon: 'breeze/icons/apps/48/utilities-log-viewer', comment: 'Color-coded log viewer', 'file-handlers': ['\\\\.log
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

], args: ['%U'] },\n            'html-preview': { path: 'input://apps/html-preview.js', name: 'HTML Preview', icon: 'paper/icons/apps/internet-web-browser', comment: 'Preview HTML in sandbox', mimetypes: ['text/html'], args: ['%U'] },\n            'hex-viewer': { path: 'input://apps/hex-viewer.js', name: 'Hex Viewer', icon: 'breeze/icons/apps/48/okteta', comment: 'View file bytes in hex', mimetypes: [], 'file-handlers': ['\\\\.bin
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

, '\\\\.dat
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

], args: ['%U'] },\n            'color-viewer': { path: 'input://apps/color-viewer.js', name: 'Color Palette', icon: 'paper/small/apps/preferences-color-symbolic', comment: 'View color palettes', mimetypes: [], 'file-handlers': ['\\\\.colors
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

, '\\\\.palette
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

], args: ['%U'] }\n        }],\n        // Setup sidebar\n        ['gui:specialPaths:add', { path: 'input://desktop/', name: 'Desktop', icon: 'breeze/icons/places/64/user-desktop', paths: [{ rule: 'Documents\\\\/', name: 'Documents' }, { rule: 'Data\\\\/', name: 'Data' }, { rule: 'Logs\\\\/', name: 'Logs' }, { rule: 'Web\\\\/', name: 'Web' }, { rule: 'Design\\\\/', name: 'Design' }], bookmark: { category: 'workspace', name: 'Desktop', icon: 'breeze/icons/places/64/user-desktop' } }],\n        ['gui:setHomePath', 'input://desktop/']\n    ], function () {\n        alert('Virtual Desktop ready!\\n\\n' +\n            'Try double-clicking:\\n' +\n            '- team-roster.csv (CSV Table Viewer)\\n' +\n            '- server.log (Color-coded Log Viewer)\\n' +\n            '- interactive-page.html (HTML Preview with sandbox)\\n' +\n            '- brand.colors (Color Palette Viewer)\\n' +\n            '- Open Photo Editor.lnk (Shortcut)\\n' +\n            '- data.json (JSON in Code Editor)\\n\\n' +\n            'Navigate via sidebar: Desktop > Documents, Data, Logs...');\n    });\n};\n<\/script>\n\n\n\n---\n\n## What Gets Created\n\n### 5 Custom Apps\n\n| App | Extensions | What It Does |\n|-----|-----------|-------------|\n| **CSV Table Viewer** | `.csv` | Parses CSV and renders as a styled table with sticky headers, zebra striping |\n| **Log Viewer** | `.log` | Color-codes log lines by severity: red (error/fatal), orange (warning), blue (info), green (success), grey (debug) |\n| **HTML Preview** | `.html` | Renders HTML in a sandboxed iframe with `allow-scripts` — interactive elements work |\n| **Hex Viewer** | `.bin`, `.dat` | Shows raw bytes in classic hex editor layout: offset + hex + ASCII (first 4KB) |\n| **Color Palette** | `.colors`, `.palette` | Parses color definitions and renders visual swatches with names and hex codes |\n\n### File Structure\n\n```\ninput://desktop/\n  Documents/\n    notes.txt           ← plain text (opens in Code Editor)\n    data.json           ← JSON (opens in Code Editor)\n  Data/\n    team-roster.csv     ← CSV (opens in CSV Table Viewer)\n  Logs/\n    server.log          ← log file (opens in Log Viewer)\n  Web/\n    interactive-page.html  ← HTML (opens in HTML Preview)\n  Design/\n    brand.colors        ← color palette (opens in Color Palette)\n  Open Photo Editor.lnk   ← shortcut (launches Photo Editor)\n  Open Code Editor.lnk    ← shortcut (launches Code Editor)\n```\n\n### Shortcuts (.lnk files)\n\nDouble-click a `.lnk` file to launch the specified app. The shortcut runner reads the JSON inside and opens the target app:\n\n```json\n{\n    \"__shortcut\": true,\n    \"app\": \"photo-editor\",\n    \"args\": [],\n    \"name\": \"Photo Editor\",\n    \"icon\": \"paper/*/multimedia-photo-viewer.png\"\n}\n```\n\n---\n\n## New API Events Used\n\n### `gui:setHomePath` -- Set Default Directory\n\nSets the starting directory when the file manager opens.\n\n```js\nsocket.fire(\"webapp::instance::request\", \"gui:setHomePath\", \"input://desktop/\");\n```\n\n### `fs:createShortcut` -- Create App Shortcuts\n\nCreates a `.lnk` file that launches a specific app when double-clicked.\n\n| Arg | Type | Description |\n|-----|------|-------------|\n| `path` | string | Where to create the shortcut (must be `input://`) |\n| `config` | object | `{ app, args?, name?, icon? }` |\n| `callback` | function | `(err)` |\n\n```js\nsocket.fire(\"webapp::instance::request\", \"fs:createShortcut\",\n    \"input://desktop/My Editor.lnk\",\n    { app: \"code-editor\", args: [], name: \"Code Editor\", icon: \"breeze*book-edit\" }\n);\n```\n\n---\n\n## App Code Patterns\n\n### CSV Table Viewer\n\nKey technique: split by newlines, split by commas, first row becomes `` headers:\n\n```js\nvar rows = text.split('\\n').map(function (r) {\n    return r.split(',').map(function (c) { return c.trim(); });\n});\n// First row = headers, rest = data rows\n```\n\n### Log Viewer\n\nKey technique: regex-match severity keywords and assign colors:\n\n```js\nif (line.match(/\\b(error|fatal)\\b/i)) color = '#e74c3c';\nelse if (line.match(/\\b(warn)\\b/i)) color = '#f39c12';\nelse if (line.match(/\\b(info)\\b/i)) color = '#3498db';\n```\n\n### HTML Preview\n\nKey technique: sandboxed iframe with `srcdoc`:\n\n```js\nvar iframe = document.createElement('iframe');\niframe.sandbox = 'allow-scripts';\niframe.srcdoc = htmlContent;\n```\n\n### Hex Viewer\n\nKey technique: read raw bytes via callback (not stream), format in 16-byte rows:\n\n```js\nfsModule().read(path, function (err, data) {\n    var bytes = new Uint8Array(data);\n    // offset  hex hex hex ... hex  ascii\n});\n```\n\n### Color Palette Viewer\n\nKey technique: parse lines as `#hexcode Name`, render colored divs:\n\n```js\nvar parts = line.split(/\\s+/);\nvar color = parts[0];  // \"#4a6cf7\"\nvar name = parts.slice(1).join(' ');  // \"Primary Blue\"\n```\n\n---\n\n## Combining Everything\n\nThese apps work together with the other use cases:\n\n- **Click a `.csv` in the CMS** → opens in CSV Table Viewer\n- **View server logs** while editing the project in the [Project Workspace](/use-cases/project-workspace.md)\n- **Preview HTML** pages from the [CMS Content Editor](/use-cases/cms-content-editor.md)\n- **Add shortcuts** to frequently used tools on the desktop\n- **Create your own format handlers** following the [Custom File Format](/use-cases/custom-file-format.md) pattern\n";window.$preloadedPath = "/webapp-docs/use-cases/thirdparty-webapps";
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

], args: ['%U'] },
        'color-viewer': { path: 'input://apps/color-viewer.js', name: 'Color Palette', icon: 'paper/small/apps/preferences-color-symbolic', comment: 'View color palettes', mimetypes: [], 'file-handlers': ['\\.colors
    
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

], args: ['%U'] },\n            'log-viewer': { path: 'input://apps/log-viewer.js', name: 'Log Viewer', icon: 'breeze/icons/apps/48/utilities-log-viewer', comment: 'Color-coded log viewer', 'file-handlers': ['\\\\.log
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

], args: ['%U'] },\n            'html-preview': { path: 'input://apps/html-preview.js', name: 'HTML Preview', icon: 'paper/icons/apps/internet-web-browser', comment: 'Preview HTML in sandbox', mimetypes: ['text/html'], args: ['%U'] },\n            'hex-viewer': { path: 'input://apps/hex-viewer.js', name: 'Hex Viewer', icon: 'breeze/icons/apps/48/okteta', comment: 'View file bytes in hex', mimetypes: [], 'file-handlers': ['\\\\.bin
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

, '\\\\.dat
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

], args: ['%U'] },\n            'color-viewer': { path: 'input://apps/color-viewer.js', name: 'Color Palette', icon: 'paper/small/apps/preferences-color-symbolic', comment: 'View color palettes', mimetypes: [], 'file-handlers': ['\\\\.colors
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

, '\\\\.palette
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

], args: ['%U'] }\n        }],\n        // Setup sidebar\n        ['gui:specialPaths:add', { path: 'input://desktop/', name: 'Desktop', icon: 'breeze/icons/places/64/user-desktop', paths: [{ rule: 'Documents\\\\/', name: 'Documents' }, { rule: 'Data\\\\/', name: 'Data' }, { rule: 'Logs\\\\/', name: 'Logs' }, { rule: 'Web\\\\/', name: 'Web' }, { rule: 'Design\\\\/', name: 'Design' }], bookmark: { category: 'workspace', name: 'Desktop', icon: 'breeze/icons/places/64/user-desktop' } }],\n        ['gui:setHomePath', 'input://desktop/']\n    ], function () {\n        alert('Virtual Desktop ready!\\n\\n' +\n            'Try double-clicking:\\n' +\n            '- team-roster.csv (CSV Table Viewer)\\n' +\n            '- server.log (Color-coded Log Viewer)\\n' +\n            '- interactive-page.html (HTML Preview with sandbox)\\n' +\n            '- brand.colors (Color Palette Viewer)\\n' +\n            '- Open Photo Editor.lnk (Shortcut)\\n' +\n            '- data.json (JSON in Code Editor)\\n\\n' +\n            'Navigate via sidebar: Desktop > Documents, Data, Logs...');\n    });\n};\n<\/script>\n\n\n\n---\n\n## What Gets Created\n\n### 5 Custom Apps\n\n| App | Extensions | What It Does |\n|-----|-----------|-------------|\n| **CSV Table Viewer** | `.csv` | Parses CSV and renders as a styled table with sticky headers, zebra striping |\n| **Log Viewer** | `.log` | Color-codes log lines by severity: red (error/fatal), orange (warning), blue (info), green (success), grey (debug) |\n| **HTML Preview** | `.html` | Renders HTML in a sandboxed iframe with `allow-scripts` — interactive elements work |\n| **Hex Viewer** | `.bin`, `.dat` | Shows raw bytes in classic hex editor layout: offset + hex + ASCII (first 4KB) |\n| **Color Palette** | `.colors`, `.palette` | Parses color definitions and renders visual swatches with names and hex codes |\n\n### File Structure\n\n```\ninput://desktop/\n  Documents/\n    notes.txt           ← plain text (opens in Code Editor)\n    data.json           ← JSON (opens in Code Editor)\n  Data/\n    team-roster.csv     ← CSV (opens in CSV Table Viewer)\n  Logs/\n    server.log          ← log file (opens in Log Viewer)\n  Web/\n    interactive-page.html  ← HTML (opens in HTML Preview)\n  Design/\n    brand.colors        ← color palette (opens in Color Palette)\n  Open Photo Editor.lnk   ← shortcut (launches Photo Editor)\n  Open Code Editor.lnk    ← shortcut (launches Code Editor)\n```\n\n### Shortcuts (.lnk files)\n\nDouble-click a `.lnk` file to launch the specified app. The shortcut runner reads the JSON inside and opens the target app:\n\n```json\n{\n    \"__shortcut\": true,\n    \"app\": \"photo-editor\",\n    \"args\": [],\n    \"name\": \"Photo Editor\",\n    \"icon\": \"paper/*/multimedia-photo-viewer.png\"\n}\n```\n\n---\n\n## New API Events Used\n\n### `gui:setHomePath` -- Set Default Directory\n\nSets the starting directory when the file manager opens.\n\n```js\nsocket.fire(\"webapp::instance::request\", \"gui:setHomePath\", \"input://desktop/\");\n```\n\n### `fs:createShortcut` -- Create App Shortcuts\n\nCreates a `.lnk` file that launches a specific app when double-clicked.\n\n| Arg | Type | Description |\n|-----|------|-------------|\n| `path` | string | Where to create the shortcut (must be `input://`) |\n| `config` | object | `{ app, args?, name?, icon? }` |\n| `callback` | function | `(err)` |\n\n```js\nsocket.fire(\"webapp::instance::request\", \"fs:createShortcut\",\n    \"input://desktop/My Editor.lnk\",\n    { app: \"code-editor\", args: [], name: \"Code Editor\", icon: \"breeze*book-edit\" }\n);\n```\n\n---\n\n## App Code Patterns\n\n### CSV Table Viewer\n\nKey technique: split by newlines, split by commas, first row becomes `` headers:\n\n```js\nvar rows = text.split('\\n').map(function (r) {\n    return r.split(',').map(function (c) { return c.trim(); });\n});\n// First row = headers, rest = data rows\n```\n\n### Log Viewer\n\nKey technique: regex-match severity keywords and assign colors:\n\n```js\nif (line.match(/\\b(error|fatal)\\b/i)) color = '#e74c3c';\nelse if (line.match(/\\b(warn)\\b/i)) color = '#f39c12';\nelse if (line.match(/\\b(info)\\b/i)) color = '#3498db';\n```\n\n### HTML Preview\n\nKey technique: sandboxed iframe with `srcdoc`:\n\n```js\nvar iframe = document.createElement('iframe');\niframe.sandbox = 'allow-scripts';\niframe.srcdoc = htmlContent;\n```\n\n### Hex Viewer\n\nKey technique: read raw bytes via callback (not stream), format in 16-byte rows:\n\n```js\nfsModule().read(path, function (err, data) {\n    var bytes = new Uint8Array(data);\n    // offset  hex hex hex ... hex  ascii\n});\n```\n\n### Color Palette Viewer\n\nKey technique: parse lines as `#hexcode Name`, render colored divs:\n\n```js\nvar parts = line.split(/\\s+/);\nvar color = parts[0];  // \"#4a6cf7\"\nvar name = parts.slice(1).join(' ');  // \"Primary Blue\"\n```\n\n---\n\n## Combining Everything\n\nThese apps work together with the other use cases:\n\n- **Click a `.csv` in the CMS** → opens in CSV Table Viewer\n- **View server logs** while editing the project in the [Project Workspace](/use-cases/project-workspace.md)\n- **Preview HTML** pages from the [CMS Content Editor](/use-cases/cms-content-editor.md)\n- **Add shortcuts** to frequently used tools on the desktop\n- **Create your own format handlers** following the [Custom File Format](/use-cases/custom-file-format.md) pattern\n";window.$preloadedPath = "/webapp-docs/use-cases/thirdparty-webapps";
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

, '\\.palette
    
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

], args: ['%U'] },\n            'log-viewer': { path: 'input://apps/log-viewer.js', name: 'Log Viewer', icon: 'breeze/icons/apps/48/utilities-log-viewer', comment: 'Color-coded log viewer', 'file-handlers': ['\\\\.log
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

], args: ['%U'] },\n            'html-preview': { path: 'input://apps/html-preview.js', name: 'HTML Preview', icon: 'paper/icons/apps/internet-web-browser', comment: 'Preview HTML in sandbox', mimetypes: ['text/html'], args: ['%U'] },\n            'hex-viewer': { path: 'input://apps/hex-viewer.js', name: 'Hex Viewer', icon: 'breeze/icons/apps/48/okteta', comment: 'View file bytes in hex', mimetypes: [], 'file-handlers': ['\\\\.bin
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

, '\\\\.dat
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

], args: ['%U'] },\n            'color-viewer': { path: 'input://apps/color-viewer.js', name: 'Color Palette', icon: 'paper/small/apps/preferences-color-symbolic', comment: 'View color palettes', mimetypes: [], 'file-handlers': ['\\\\.colors
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

, '\\\\.palette
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

], args: ['%U'] }\n        }],\n        // Setup sidebar\n        ['gui:specialPaths:add', { path: 'input://desktop/', name: 'Desktop', icon: 'breeze/icons/places/64/user-desktop', paths: [{ rule: 'Documents\\\\/', name: 'Documents' }, { rule: 'Data\\\\/', name: 'Data' }, { rule: 'Logs\\\\/', name: 'Logs' }, { rule: 'Web\\\\/', name: 'Web' }, { rule: 'Design\\\\/', name: 'Design' }], bookmark: { category: 'workspace', name: 'Desktop', icon: 'breeze/icons/places/64/user-desktop' } }],\n        ['gui:setHomePath', 'input://desktop/']\n    ], function () {\n        alert('Virtual Desktop ready!\\n\\n' +\n            'Try double-clicking:\\n' +\n            '- team-roster.csv (CSV Table Viewer)\\n' +\n            '- server.log (Color-coded Log Viewer)\\n' +\n            '- interactive-page.html (HTML Preview with sandbox)\\n' +\n            '- brand.colors (Color Palette Viewer)\\n' +\n            '- Open Photo Editor.lnk (Shortcut)\\n' +\n            '- data.json (JSON in Code Editor)\\n\\n' +\n            'Navigate via sidebar: Desktop > Documents, Data, Logs...');\n    });\n};\n<\/script>\n\n\n\n---\n\n## What Gets Created\n\n### 5 Custom Apps\n\n| App | Extensions | What It Does |\n|-----|-----------|-------------|\n| **CSV Table Viewer** | `.csv` | Parses CSV and renders as a styled table with sticky headers, zebra striping |\n| **Log Viewer** | `.log` | Color-codes log lines by severity: red (error/fatal), orange (warning), blue (info), green (success), grey (debug) |\n| **HTML Preview** | `.html` | Renders HTML in a sandboxed iframe with `allow-scripts` — interactive elements work |\n| **Hex Viewer** | `.bin`, `.dat` | Shows raw bytes in classic hex editor layout: offset + hex + ASCII (first 4KB) |\n| **Color Palette** | `.colors`, `.palette` | Parses color definitions and renders visual swatches with names and hex codes |\n\n### File Structure\n\n```\ninput://desktop/\n  Documents/\n    notes.txt           ← plain text (opens in Code Editor)\n    data.json           ← JSON (opens in Code Editor)\n  Data/\n    team-roster.csv     ← CSV (opens in CSV Table Viewer)\n  Logs/\n    server.log          ← log file (opens in Log Viewer)\n  Web/\n    interactive-page.html  ← HTML (opens in HTML Preview)\n  Design/\n    brand.colors        ← color palette (opens in Color Palette)\n  Open Photo Editor.lnk   ← shortcut (launches Photo Editor)\n  Open Code Editor.lnk    ← shortcut (launches Code Editor)\n```\n\n### Shortcuts (.lnk files)\n\nDouble-click a `.lnk` file to launch the specified app. The shortcut runner reads the JSON inside and opens the target app:\n\n```json\n{\n    \"__shortcut\": true,\n    \"app\": \"photo-editor\",\n    \"args\": [],\n    \"name\": \"Photo Editor\",\n    \"icon\": \"paper/*/multimedia-photo-viewer.png\"\n}\n```\n\n---\n\n## New API Events Used\n\n### `gui:setHomePath` -- Set Default Directory\n\nSets the starting directory when the file manager opens.\n\n```js\nsocket.fire(\"webapp::instance::request\", \"gui:setHomePath\", \"input://desktop/\");\n```\n\n### `fs:createShortcut` -- Create App Shortcuts\n\nCreates a `.lnk` file that launches a specific app when double-clicked.\n\n| Arg | Type | Description |\n|-----|------|-------------|\n| `path` | string | Where to create the shortcut (must be `input://`) |\n| `config` | object | `{ app, args?, name?, icon? }` |\n| `callback` | function | `(err)` |\n\n```js\nsocket.fire(\"webapp::instance::request\", \"fs:createShortcut\",\n    \"input://desktop/My Editor.lnk\",\n    { app: \"code-editor\", args: [], name: \"Code Editor\", icon: \"breeze*book-edit\" }\n);\n```\n\n---\n\n## App Code Patterns\n\n### CSV Table Viewer\n\nKey technique: split by newlines, split by commas, first row becomes `` headers:\n\n```js\nvar rows = text.split('\\n').map(function (r) {\n    return r.split(',').map(function (c) { return c.trim(); });\n});\n// First row = headers, rest = data rows\n```\n\n### Log Viewer\n\nKey technique: regex-match severity keywords and assign colors:\n\n```js\nif (line.match(/\\b(error|fatal)\\b/i)) color = '#e74c3c';\nelse if (line.match(/\\b(warn)\\b/i)) color = '#f39c12';\nelse if (line.match(/\\b(info)\\b/i)) color = '#3498db';\n```\n\n### HTML Preview\n\nKey technique: sandboxed iframe with `srcdoc`:\n\n```js\nvar iframe = document.createElement('iframe');\niframe.sandbox = 'allow-scripts';\niframe.srcdoc = htmlContent;\n```\n\n### Hex Viewer\n\nKey technique: read raw bytes via callback (not stream), format in 16-byte rows:\n\n```js\nfsModule().read(path, function (err, data) {\n    var bytes = new Uint8Array(data);\n    // offset  hex hex hex ... hex  ascii\n});\n```\n\n### Color Palette Viewer\n\nKey technique: parse lines as `#hexcode Name`, render colored divs:\n\n```js\nvar parts = line.split(/\\s+/);\nvar color = parts[0];  // \"#4a6cf7\"\nvar name = parts.slice(1).join(' ');  // \"Primary Blue\"\n```\n\n---\n\n## Combining Everything\n\nThese apps work together with the other use cases:\n\n- **Click a `.csv` in the CMS** → opens in CSV Table Viewer\n- **View server logs** while editing the project in the [Project Workspace](/use-cases/project-workspace.md)\n- **Preview HTML** pages from the [CMS Content Editor](/use-cases/cms-content-editor.md)\n- **Add shortcuts** to frequently used tools on the desktop\n- **Create your own format handlers** following the [Custom File Format](/use-cases/custom-file-format.md) pattern\n";window.$preloadedPath = "/webapp-docs/use-cases/thirdparty-webapps";
    
    
    
    
    
    
    
    
    
    
    
    
sgapps.io

], args: ['%U'] }
    }],
    // Setup sidebar
    ['gui:specialPaths:add', { path: 'input://desktop/', name: 'Desktop', icon: 'breeze/icons/places/64/user-desktop', paths: [{ rule: 'Documents\\/', name: 'Documents' }, { rule: 'Data\\/', name: 'Data' }, { rule: 'Logs\\/', name: 'Logs' }, { rule: 'Web\\/', name: 'Web' }, { rule: 'Design\\/', name: 'Design' }], bookmark: { category: 'workspace', name: 'Desktop', icon: 'breeze/icons/places/64/user-desktop' } }],
    ['gui:setHomePath', 'input://desktop/']
], function () {
    alert('Virtual Desktop ready!\n\n' +
        'Try double-clicking:\n' +
        '- team-roster.csv (CSV Table Viewer)\n' +
        '- server.log (Color-coded Log Viewer)\n' +
        '- interactive-page.html (HTML Preview with sandbox)\n' +
        '- brand.colors (Color Palette Viewer)\n' +
        '- Open Photo Editor.lnk (Shortcut)\n' +
        '- data.json (JSON in Code Editor)\n\n' +
        'Navigate via sidebar: Desktop > Documents, Data, Logs...');
});

};</script>
<button onclick="window._tpSetup()" style="padding:12px 24px;font-size:16px;cursor:pointer;background:#4a6cf7;color:#fff;border:none;border-radius:6px">Setup Virtual Desktop with App Library</button>


What Gets Created

5 Custom Apps

App Extensions What It Does
CSV Table Viewer .csv Parses CSV and renders as a styled table with sticky headers, zebra striping
Log Viewer .log Color-codes log lines by severity: red (error/fatal), orange (warning), blue (info), green (success), grey (debug)
HTML Preview .html Renders HTML in a sandboxed iframe with allow-scripts — interactive elements work
Hex Viewer .bin, .dat Shows raw bytes in classic hex editor layout: offset + hex + ASCII (first 4KB)
Color Palette .colors, .palette Parses color definitions and renders visual swatches with names and hex codes

File Structure

input://desktop/
  Documents/
    notes.txt           ← plain text (opens in Code Editor)
    data.json           ← JSON (opens in Code Editor)
  Data/
    team-roster.csv     ← CSV (opens in CSV Table Viewer)
  Logs/
    server.log          ← log file (opens in Log Viewer)
  Web/
    interactive-page.html  ← HTML (opens in HTML Preview)
  Design/
    brand.colors        ← color palette (opens in Color Palette)
  Open Photo Editor.lnk   ← shortcut (launches Photo Editor)
  Open Code Editor.lnk    ← shortcut (launches Code Editor)

Shortcuts (.lnk files)

Double-click a .lnk file to launch the specified app. The shortcut runner reads the JSON inside and opens the target app:

{
    "__shortcut": true,
    "app": "photo-editor",
    "args": [],
    "name": "Photo Editor",
    "icon": "paper/*/multimedia-photo-viewer.png"
}

New API Events Used

gui:setHomePath -- Set Default Directory

Sets the starting directory when the file manager opens.

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

fs:createShortcut -- Create App Shortcuts

Creates a .lnk file that launches a specific app when double-clicked.

Arg Type Description
path string Where to create the shortcut (must be input://)
config object &#x7B; app, args&#x3F;, name&#x3F;, icon&#x3F; &#x7D;
callback function (err)

socket.fire("webapp::instance::request", "fs:createShortcut",
    "input://desktop/My Editor.lnk",
    { app: "code-editor", args: [], name: "Code Editor", icon: "breeze*book-edit" }
);

App Code Patterns

CSV Table Viewer

Key technique: split by newlines, split by commas, first row becomes &#x3C;th&#x3E; headers:

var rows = text.split('\n').map(function (r) {
    return r.split(',').map(function (c) { return c.trim(); });
});
// First row = headers, rest = data rows

Log Viewer

Key technique: regex-match severity keywords and assign colors:

if (line.match(/\b(error|fatal)\b/i)) color = '#e74c3c';
else if (line.match(/\b(warn)\b/i)) color = '#f39c12';
else if (line.match(/\b(info)\b/i)) color = '#3498db';

HTML Preview

Key technique: sandboxed iframe with srcdoc:

var iframe = document.createElement('iframe');
iframe.sandbox = 'allow-scripts';
iframe.srcdoc = htmlContent;

Hex Viewer

Key technique: read raw bytes via callback (not stream), format in 16-byte rows:

fsModule().read(path, function (err, data) {
    var bytes = new Uint8Array(data);
    // offset  hex hex hex ... hex  ascii
});

Color Palette Viewer

Key technique: parse lines as #hexcode Name, render colored divs:

var parts = line.split(/\s+/);
var color = parts[0];  // "#4a6cf7"
var name = parts.slice(1).join(' ');  // "Primary Blue"

Combining Everything

These apps work together with the other use cases:

sgapps.io ], args: ['%U'] },\n 'log-viewer': { path: 'input://apps/log-viewer.js', name: 'Log Viewer', icon: 'breeze/icons/apps/48/utilities-log-viewer', comment: 'Color-coded log viewer', 'file-handlers': ['\\\\.log sgapps.io ], args: ['%U'] },\n 'html-preview': { path: 'input://apps/html-preview.js', name: 'HTML Preview', icon: 'paper/icons/apps/internet-web-browser', comment: 'Preview HTML in sandbox', mimetypes: ['text/html'], args: ['%U'] },\n 'hex-viewer': { path: 'input://apps/hex-viewer.js', name: 'Hex Viewer', icon: 'breeze/icons/apps/48/okteta', comment: 'View file bytes in hex', mimetypes: [], 'file-handlers': ['\\\\.bin sgapps.io , '\\\\.dat sgapps.io ], args: ['%U'] },\n 'color-viewer': { path: 'input://apps/color-viewer.js', name: 'Color Palette', icon: 'paper/small/apps/preferences-color-symbolic', comment: 'View color palettes', mimetypes: [], 'file-handlers': ['\\\\.colors sgapps.io , '\\\\.palette sgapps.io ], args: ['%U'] }\n }],\n // Setup sidebar\n ['gui:specialPaths:add', { path: 'input://desktop/', name: 'Desktop', icon: 'breeze/icons/places/64/user-desktop', paths: [{ rule: 'Documents\\\\/', name: 'Documents' }, { rule: 'Data\\\\/', name: 'Data' }, { rule: 'Logs\\\\/', name: 'Logs' }, { rule: 'Web\\\\/', name: 'Web' }, { rule: 'Design\\\\/', name: 'Design' }], bookmark: { category: 'workspace', name: 'Desktop', icon: 'breeze/icons/places/64/user-desktop' } }],\n ['gui:setHomePath', 'input://desktop/']\n ], function () {\n alert('Virtual Desktop ready!\\n\\n' +\n 'Try double-clicking:\\n' +\n '- team-roster.csv (CSV Table Viewer)\\n' +\n '- server.log (Color-coded Log Viewer)\\n' +\n '- interactive-page.html (HTML Preview with sandbox)\\n' +\n '- brand.colors (Color Palette Viewer)\\n' +\n '- Open Photo Editor.lnk (Shortcut)\\n' +\n '- data.json (JSON in Code Editor)\\n\\n' +\n 'Navigate via sidebar: Desktop > Documents, Data, Logs...');\n });\n};\n<\/script>\n\n\n\n---\n\n## What Gets Created\n\n### 5 Custom Apps\n\n| App | Extensions | What It Does |\n|-----|-----------|-------------|\n| **CSV Table Viewer** | `.csv` | Parses CSV and renders as a styled table with sticky headers, zebra striping |\n| **Log Viewer** | `.log` | Color-codes log lines by severity: red (error/fatal), orange (warning), blue (info), green (success), grey (debug) |\n| **HTML Preview** | `.html` | Renders HTML in a sandboxed iframe with `allow-scripts` — interactive elements work |\n| **Hex Viewer** | `.bin`, `.dat` | Shows raw bytes in classic hex editor layout: offset + hex + ASCII (first 4KB) |\n| **Color Palette** | `.colors`, `.palette` | Parses color definitions and renders visual swatches with names and hex codes |\n\n### File Structure\n\n```\ninput://desktop/\n Documents/\n notes.txt ← plain text (opens in Code Editor)\n data.json ← JSON (opens in Code Editor)\n Data/\n team-roster.csv ← CSV (opens in CSV Table Viewer)\n Logs/\n server.log ← log file (opens in Log Viewer)\n Web/\n interactive-page.html ← HTML (opens in HTML Preview)\n Design/\n brand.colors ← color palette (opens in Color Palette)\n Open Photo Editor.lnk ← shortcut (launches Photo Editor)\n Open Code Editor.lnk ← shortcut (launches Code Editor)\n```\n\n### Shortcuts (.lnk files)\n\nDouble-click a `.lnk` file to launch the specified app. The shortcut runner reads the JSON inside and opens the target app:\n\n```json\n{\n \"__shortcut\": true,\n \"app\": \"photo-editor\",\n \"args\": [],\n \"name\": \"Photo Editor\",\n \"icon\": \"paper/*/multimedia-photo-viewer.png\"\n}\n```\n\n---\n\n## New API Events Used\n\n### `gui:setHomePath` -- Set Default Directory\n\nSets the starting directory when the file manager opens.\n\n```js\nsocket.fire(\"webapp::instance::request\", \"gui:setHomePath\", \"input://desktop/\");\n```\n\n### `fs:createShortcut` -- Create App Shortcuts\n\nCreates a `.lnk` file that launches a specific app when double-clicked.\n\n| Arg | Type | Description |\n|-----|------|-------------|\n| `path` | string | Where to create the shortcut (must be `input://`) |\n| `config` | object | `{ app, args?, name?, icon? }` |\n| `callback` | function | `(err)` |\n\n```js\nsocket.fire(\"webapp::instance::request\", \"fs:createShortcut\",\n \"input://desktop/My Editor.lnk\",\n { app: \"code-editor\", args: [], name: \"Code Editor\", icon: \"breeze*book-edit\" }\n);\n```\n\n---\n\n## App Code Patterns\n\n### CSV Table Viewer\n\nKey technique: split by newlines, split by commas, first row becomes `` headers:\n\n```js\nvar rows = text.split('\\n').map(function (r) {\n return r.split(',').map(function (c) { return c.trim(); });\n});\n// First row = headers, rest = data rows\n```\n\n### Log Viewer\n\nKey technique: regex-match severity keywords and assign colors:\n\n```js\nif (line.match(/\\b(error|fatal)\\b/i)) color = '#e74c3c';\nelse if (line.match(/\\b(warn)\\b/i)) color = '#f39c12';\nelse if (line.match(/\\b(info)\\b/i)) color = '#3498db';\n```\n\n### HTML Preview\n\nKey technique: sandboxed iframe with `srcdoc`:\n\n```js\nvar iframe = document.createElement('iframe');\niframe.sandbox = 'allow-scripts';\niframe.srcdoc = htmlContent;\n```\n\n### Hex Viewer\n\nKey technique: read raw bytes via callback (not stream), format in 16-byte rows:\n\n```js\nfsModule().read(path, function (err, data) {\n var bytes = new Uint8Array(data);\n // offset hex hex hex ... hex ascii\n});\n```\n\n### Color Palette Viewer\n\nKey technique: parse lines as `#hexcode Name`, render colored divs:\n\n```js\nvar parts = line.split(/\\s+/);\nvar color = parts[0]; // \"#4a6cf7\"\nvar name = parts.slice(1).join(' '); // \"Primary Blue\"\n```\n\n---\n\n## Combining Everything\n\nThese apps work together with the other use cases:\n\n- **Click a `.csv` in the CMS** → opens in CSV Table Viewer\n- **View server logs** while editing the project in the [Project Workspace](/use-cases/project-workspace.md)\n- **Preview HTML** pages from the [CMS Content Editor](/use-cases/cms-content-editor.md)\n- **Add shortcuts** to frequently used tools on the desktop\n- **Create your own format handlers** following the [Custom File Format](/use-cases/custom-file-format.md) pattern\n";window.$preloadedPath = "/webapp-docs/use-cases/thirdparty-webapps"; sgapps.io