API Communication

Control embedded SGApps web applications from your parent page using the WindowSocket postMessage bridge.

Setup

Include the WindowSocket library and create a connection to the iframe:

<iframe id="app" src="https://sgapps.io/online/webapp/photo-editor"></iframe>

<script src="https://sgapps.io/components/window-socket/index.js"></script>
<script>
    var iframe = document.getElementById('app');
    var socket = new WindowSocket(iframe.contentWindow);
    socket.start();
</script>

Sending Commands

Send commands to the app using socket.fire():

socket.fire("webapp::instance::request", eventName, arg1, arg2, ..., callback);

The eventName is mapped to api-request::instance:&#x7B;eventName&#x7D; inside the app. Each app defines its own set of API request events.

Examples

// Reset the photo editor canvas
socket.fire("webapp::instance::request", "reset", function (err) {
    console.log("Done");
});

// Get image data from photo editor
socket.fire("webapp::instance::request", "getDataBase64", "image/png", function (err, dataUrl) {
    // dataUrl is a base64 PNG
    document.getElementById("preview").src = dataUrl;
});

// Set editor content in code editor
socket.fire("webapp::instance::request", "active-file:codeEditor:value", "console.log('hello');", function () {
    console.log("Content set");
});

Built-in Events

These events are available for all apps:

webapp::instance::embed-mode

Toggle embed mode (hide/show window chrome):

// Enable: hides title bar, maximizes app
socket.fire("webapp::instance::embed-mode", true, function (err, isEmbedded) {
    console.log("Embedded:", isEmbedded); // true
});

// Disable: restores window chrome
socket.fire("webapp::instance::embed-mode", false, function (err, isEmbedded) {
    console.log("Embedded:", isEmbedded); // false
});

webapp::instance::require-redraw

Force a UI redraw (useful after container resize):

socket.fire("webapp::instance::require-redraw");

webapp::connection::ping

The app sends periodic ping messages to the parent after loading:

socket.on("webapp::connection::ping", function (appName) {
    console.log("App connected:", appName);
});

webapp::instance::error

Fires when the app fails to load:

socket.on("webapp::instance::error", function (errorMessage) {
    console.error("App error:", errorMessage);
});

App-Specific Events

Each app has its own set of api-request::instance:* events. When using socket.fire(&#x22;webapp::instance::request&#x22;, ...), the first argument after the event string becomes the event name suffix.

Photo Editor

Event Name (via socket.fire) Args Description
&#x22;reset&#x22; callback Clear canvas
&#x22;setDataBase64&#x22; base64Url, callback Load image from base64 data URL
&#x22;getDataBase64&#x22; type, callback, quality Export canvas (e.g., &#x22;image/png&#x22;)
&#x22;zoom&#x22; level, callback Set zoom level
&#x22;applyFilters&#x22; filtersArray, callback Apply image filters
&#x22;resize&#x22; width, height, callback Resize image
&#x22;crop&#x22; x, y, w, h, callback Crop to rectangle
&#x22;flipH&#x22; callback Flip horizontally
&#x22;flipW&#x22; callback Flip vertically
&#x22;rotateCW&#x22; callback Rotate 90 clockwise
&#x22;rotateCCW&#x22; callback Rotate 90 counter-clockwise
&#x22;history:undo&#x22; callback Undo last action
&#x22;history:redo&#x22; callback Redo last undone action
&#x22;history:save&#x22; callback Save current state to history
&#x22;getImageInfo&#x22; callback Returns &#x7B; width, height, layerCount, scale &#x7D;
&#x22;loadFromURL&#x22; url, callback Load image from URL directly
&#x22;getScale&#x22; callback Returns current zoom scale
&#x22;fitToView&#x22; callback Fit image to viewport and center

// Apply sepia + brightness filter chain
socket.fire("webapp::instance::request", "applyFilters", [
    { name: "sepia", params: { level: 0.8 } },
    { name: "brightness", params: { level: 0.2 } }
], function () {
    console.log("Filters applied");
});

// Export as JPEG at 80% quality
socket.fire("webapp::instance::request", "getDataBase64", "image/jpeg",
    function (err, dataUrl) {
        // upload dataUrl to your server
    },
    0.8
);

SVG Editor

Event Name Args Description
&#x22;loadRasterImage&#x22; base64, callback, options Import raster, auto-trace to SVG
&#x22;loadSvgImage&#x22; svgString, callback Load SVG source
&#x22;generateRasterImage&#x22; callback Export as PNG data URL
&#x22;getSVGSource&#x22; callback Get SVG source string
&#x22;getSVGDynamicSource&#x22; callback Get dynamic SVG source
&#x22;getSVGAbsoluteSource&#x22; callback Get SVG with absolute coords

// Get SVG source from the editor
socket.fire("webapp::instance::request", "getSVGSource", function (err, svgString) {
    console.log("SVG:", svgString);
});

Code Editor

Event Name Args Description
&#x22;active-file&#x22; callback Get active tab info
&#x22;active-file:codeEditor:value&#x22; value&#x3F;, callback Get or set editor text
&#x22;active-file:codeEditor:json:schema&#x22; schema, callback Set JSON schema
&#x22;active-file:codeEditor:syntax&#x22; syntax&#x3F;, callback Get or set language mode (e.g., &#x22;javascript&#x22;, &#x22;html&#x22;)
&#x22;active-file:codeEditor:options&#x22; options, callback Set Monaco editor options (readOnly, fontSize, wordWrap, etc.)
&#x22;active-file:codeEditor:selection&#x22; callback Returns &#x7B; startLine, startCol, endLine, endCol, text &#x7D;
&#x22;active-file:codeEditor:insertText&#x22; text, position&#x3F;, callback Insert text at position &#x7B; line, col &#x7D; or at cursor
&#x22;active-file:codeEditor:revealLine&#x22; lineNumber, callback Scroll to center a specific line
&#x22;active-file:save&#x22; callback Save the active file
&#x22;active-file:markers&#x22; markers, callback Set error/warning markers on the editor
&#x22;files:open&#x22; fileConfig, callback Open a file in a new tab
&#x22;files:close:all&#x22; callback Close all tabs
&#x22;files:list&#x22; callback Returns array of open tabs [&#x7B; index, name, path, type, title &#x7D;]
&#x22;files:active:set&#x22; tabIndex, callback Activate a tab by index
&#x22;gui:sidebar:toggle&#x22; visible, callback Show or hide the sidebar

// Set code content
socket.fire("webapp::instance::request",
    "active-file:codeEditor:value",
    "function hello() { return 'world'; }",
    function () { console.log("Set"); }
);

// Read code content
socket.fire("webapp::instance::request",
    "active-file:codeEditor:value",
    function (value) { console.log("Code:", value); }
);

// Change syntax to Python
socket.fire("webapp::instance::request", "active-file:codeEditor:syntax", "python");

// Make editor read-only
socket.fire("webapp::instance::request", "active-file:codeEditor:options", { readOnly: true });

// Insert text at line 5
socket.fire("webapp::instance::request", "active-file:codeEditor:insertText",
    "// inserted comment\n", { line: 5, col: 1 }
);

// Set error markers
socket.fire("webapp::instance::request", "active-file:markers", [
    { startLine: 10, startCol: 1, endLine: 10, endCol: 20, message: "Unused variable", severity: 4 }
]);

// Get current selection
socket.fire("webapp::instance::request", "active-file:codeEditor:selection",
    function (err, sel) { console.log("Selected:", sel.text); }
);

Media Player

Event Name Args Description
&#x22;play&#x22; callback Start playback
&#x22;pause&#x22; callback Pause playback
&#x22;stop&#x22; callback Pause and seek to start
&#x22;seek&#x22; timeSeconds, callback Seek to position
&#x22;volume&#x22; level&#x3F;, callback Get or set volume (0-1)
&#x22;mute&#x22; muted&#x3F;, callback Get or set muted state
&#x22;getStatus&#x22; callback Returns &#x7B; paused, currentTime, duration, volume, muted, playbackRate, loop, readyState, videoWidth, videoHeight &#x7D;
&#x22;source&#x22; url, callback Set media source (URL or file path)
&#x22;playbackRate&#x22; rate&#x3F;, callback Get or set playback speed
&#x22;loop&#x22; enabled&#x3F;, callback Get or set loop mode
&#x22;fullscreen&#x22; callback Enter fullscreen mode

// Play a video from URL
socket.fire("webapp::instance::request", "source", "https://example.com/video.mp4");
socket.fire("webapp::instance::request", "play");

// Get playback status
socket.fire("webapp::instance::request", "getStatus", function (err, status) {
    console.log("Time:", status.currentTime, "/", status.duration);
});

// Seek to 30 seconds, set volume to 50%
socket.fire("webapp::instance::request", "seek", 30);
socket.fire("webapp::instance::request", "volume", 0.5);

XTerm (Terminal)

Event Name Args Description
&#x22;write&#x22; data, callback Write text to the terminal display
&#x22;sendInput&#x22; data, callback Send keystrokes to the shell
&#x22;getSize&#x22; callback Returns &#x7B; cols, rows &#x7D;
&#x22;resize&#x22; cols, rows, callback Resize terminal
&#x22;clear&#x22; callback Clear terminal screen
&#x22;reset&#x22; callback Reset terminal state
&#x22;scrollToBottom&#x22; callback Scroll to bottom
&#x22;focus&#x22; callback Focus the terminal

// Send a command to the shell
socket.fire("webapp::instance::request", "sendInput", "ls -la\r");

// Write text directly to the display
socket.fire("webapp::instance::request", "write", "\x1b[32mHello from parent page!\x1b[0m\r\n");

// Get terminal size
socket.fire("webapp::instance::request", "getSize", function (err, size) {
    console.log("Terminal:", size.cols, "x", size.rows);
});

File Manager & Archive Viewer

Both apps share the same filesystem API. All operations work on the input:// in-memory storage without server access.

Event Name Args Description
&#x22;fs:cwd&#x22; path&#x3F;, callback Get or set current directory
&#x22;fs:ls&#x22; path, callback List directory contents
&#x22;fs:read&#x22; path, callback Read file as text
&#x22;fs:write&#x22; path, content, callback Create or overwrite a file
&#x22;fs:mkdir&#x22; path, callback Create a directory
&#x22;fs:mkdirp&#x22; path, callback Create directory with parents
&#x22;fs:remove&#x22; path, options&#x3F;, callback Delete a single file or empty directory
&#x22;fs:mv&#x22; src, dst, callback Move or rename
&#x22;fs:copy&#x22; src, dst, options&#x3F;, callback Copy a single file
&#x22;fs:archive&#x22; sourcePath, outputPath&#x3F;, format&#x3F;, options&#x3F;, callback Create archive from directory (no UI)
&#x22;fs:bulkCopy&#x22; sources, destination, options&#x3F;, callback Copy with progress UI (bin/cp)
&#x22;fs:bulkRemove&#x22; paths, options&#x3F;, callback Delete with confirmation UI (bin/rm)
&#x22;fs:bulkArchive&#x22; sources, outputPath&#x3F;, format&#x3F;, options&#x3F;, callback Archive with format selection UI (bin/archive)
&#x22;fs:exists&#x22; path, callback Check if path exists
&#x22;fs:stats&#x22; path, callback Get file/directory metadata
&#x22;fs:readStreamUrl&#x22; path, callback Get blob: URL for streaming
&#x22;fs:readBinary&#x22; path, callback Read file as base64 data URL
&#x22;fs:refresh&#x22; callback Refresh current view
&#x22;fs:download&#x22; path, filename&#x3F;, callback Trigger browser download
&#x22;gui:sidebar:toggle&#x22; visible, callback Show or hide the sidebar
&#x22;gui:specialPaths:add&#x22; entry, callback Add a named bookmark to sidebar (supports paths array for breadcrumb grouping)
&#x22;gui:specialPaths:remove&#x22; path, callback Remove an API-managed bookmark
&#x22;gui:specialPaths:clear&#x22; callback Remove all sidebar bookmarks (built-in and API-managed)
&#x22;apps:register&#x22; appsMap, callback Register custom apps (see Building Custom Apps)
&#x22;apps:unregister&#x22; appName, callback, disableDefaultApps&#x3F; Remove an app. Pass true as 3rd arg to also remove built-in apps
&#x22;apps:list&#x22; callback List all registered apps
&#x22;gui:setHomePath&#x22; path, callback Set default start directory
&#x22;fs:createShortcut&#x22; path, config, callback Create a .lnk shortcut file that launches an app
&#x22;viewport:addFiles&#x22; path&#x3F;, files, callback Inject virtual clickable icons into the file listing

Sandbox: All fs:* operations are restricted to the input:// protocol (browser-only in-memory storage). Server paths are not accessible via the API. App paths must start with https:, http:, data:, blob:, or input:.
// Create a project structure in memory
socket.fire("webapp::instance::request", "fs:mkdirp", "input://my-project/src/");
socket.fire("webapp::instance::request", "fs:write",
    "input://my-project/index.js", "console.log('hello');");
socket.fire("webapp::instance::request", "fs:cwd", "input://my-project/");

// List files
socket.fire("webapp::instance::request", "fs:ls", "input://my-project/",
    function (err, files) { console.log(files); });

Writer

Event Name Args Description
&#x22;getHTML&#x22; callback Get document as HTML
&#x22;setHTML&#x22; html, callback Set HTML content
&#x22;getMarkdown&#x22; callback Get as Markdown
&#x22;setMarkdown&#x22; md, callback Set Markdown content
&#x22;getText&#x22; callback Get plain text
&#x22;clearContent&#x22; callback Clear document
&#x22;setMode&#x22; mode, callback &#x22;wysiwyg&#x22;, &#x22;markdown&#x22;, &#x22;source&#x22;
&#x22;setTheme&#x22; theme, callback &#x22;light&#x22;, &#x22;dark&#x22;, &#x22;google-docs&#x22;, &#x22;word&#x22;
&#x22;usePreset&#x22; preset, callback &#x22;google-docs&#x22;, &#x22;wysiwyg&#x22;, &#x22;minimal&#x22;
&#x22;setZoom&#x22; level, callback Set zoom level
&#x22;readOnly&#x22; val&#x3F;, callback Get/set read-only
&#x22;insertBlock&#x22; type, options&#x3F;, callback Insert table, image, code, quote, etc.
&#x22;find&#x22; query, opts&#x3F;, callback Find text
&#x22;replace&#x22; query, repl, opts&#x3F;, callback Find and replace
&#x22;undo&#x22; / &#x22;redo&#x22; callback History navigation
&#x22;export&#x22; format&#x3F;, callback Export as html/markdown/pdf/docx
&#x22;download&#x22; format&#x3F;, filename&#x3F;, callback Download as file
&#x22;getWordCount&#x22; callback Word/char/paragraph counts
&#x22;isDirty&#x22; callback Check unsaved changes

Spreadsheet

Event Name Args Description
&#x22;getValue&#x22; ref, callback Get cell value
&#x22;setValue&#x22; ref, val, callback Set cell value
&#x22;setFormula&#x22; ref, expr, callback Set formula (e.g., &#x22;=SUM(A1:A10)&#x22;)
&#x22;setCellStyle&#x22; ref, styles, callback Style cell (bold, color, borders)
&#x22;getSelection&#x22; callback Get selected range
&#x22;setSelection&#x22; ref, callback Select range
&#x22;addSheet&#x22; / &#x22;deleteSheet&#x22; / &#x22;renameSheet&#x22; varies Sheet management
&#x22;insertRow&#x22; / &#x22;insertCol&#x22; / &#x22;deleteRow&#x22; / &#x22;deleteCol&#x22; at, count&#x3F;, callback Row/column ops
&#x22;sort&#x22; range, options&#x3F;, callback Sort data
&#x22;zoom&#x22; level&#x3F;, callback Get/set zoom
&#x22;readOnly&#x22; val&#x3F;, callback Get/set read-only
&#x22;exportCSV&#x22; sheet&#x3F;, callback Export as CSV string
&#x22;toJSON&#x22; / &#x22;fromJSON&#x22; varies Full state export/import
&#x22;undo&#x22; / &#x22;redo&#x22; callback History navigation

GCode Editor

Event Name Args Description
&#x22;reset&#x22; -- Clear and relaunch simulation

Complete Integration Example

<!DOCTYPE html>
<html>
<head>
    <title>My Image Editor</title>
    <style>
        #editor-frame { width: 100%; height: 500px; border: 1px solid #ccc; }
        .toolbar { padding: 10px; display: flex; gap: 8px; }
    </style>
</head>
<body>
    <div class="toolbar">
        <button id="btn-sepia">Apply Sepia</button>
        <button id="btn-export">Export PNG</button>
        <button id="btn-embed">Toggle Embed Mode</button>
    </div>

    <iframe id="editor-frame"
        src="https://sgapps.io/online/webapp/photo-editor"
        allow="clipboard-write"
    ></iframe>

    <img id="preview" style="max-width: 300px; margin: 10px;">

    <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 embedded = false;

        document.getElementById('btn-sepia').addEventListener('click', function () {
            socket.fire("webapp::instance::request", "applyFilters", [
                { name: "sepia", params: { level: 0.9 } }
            ], function () {
                console.log("Sepia applied");
            });
        });

        document.getElementById('btn-export').addEventListener('click', function () {
            socket.fire("webapp::instance::request", "getDataBase64", "image/png",
                function (err, dataUrl) {
                    document.getElementById('preview').src = dataUrl;
                }
            );
        });

        document.getElementById('btn-embed').addEventListener('click', function () {
            embedded = !embedded;
            socket.fire("webapp::instance::embed-mode", embedded, function (err, state) {
                console.log("Embed mode:", state);
            });
        });
    </script>
</body>
</html>

WindowSocket Reference

Method Description
new WindowSocket(targetWindow) Create socket to a window/iframe
socket.start() Begin listening for messages
socket.fire(event, ...args, callback) Send event with optional callback
socket.on(event, handler) Listen for events from the app
socket.off(event, handler) Remove event listener