Control embedded SGApps web applications from your parent page using the WindowSocket postMessage bridge.
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>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:{eventName} inside the app. Each app defines its own set of API request events.
// 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");
});These events are available for all apps:
webapp::instance::embed-modeToggle 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-redrawForce a UI redraw (useful after container resize):
socket.fire("webapp::instance::require-redraw");webapp::connection::pingThe app sends periodic ping messages to the parent after loading:
socket.on("webapp::connection::ping", function (appName) {
console.log("App connected:", appName);
});webapp::instance::errorFires when the app fails to load:
socket.on("webapp::instance::error", function (errorMessage) {
console.error("App error:", errorMessage);
});Each app has its own set of api-request::instance:* events. When using socket.fire("webapp::instance::request", ...), the first argument after the event string becomes the event name suffix.
| Event Name (via socket.fire) | Args | Description |
|---|---|---|
"reset" | callback | Clear canvas |
"setDataBase64" | base64Url, callback | Load image from base64 data URL |
"getDataBase64" | type, callback, quality | Export canvas (e.g., "image/png") |
"zoom" | level, callback | Set zoom level |
"applyFilters" | filtersArray, callback | Apply image filters |
"resize" | width, height, callback | Resize image |
"crop" | x, y, w, h, callback | Crop to rectangle |
"flipH" | callback | Flip horizontally |
"flipW" | callback | Flip vertically |
"rotateCW" | callback | Rotate 90 clockwise |
"rotateCCW" | callback | Rotate 90 counter-clockwise |
"history:undo" | callback | Undo last action |
"history:redo" | callback | Redo last undone action |
"history:save" | callback | Save current state to history |
"getImageInfo" | callback | Returns { width, height, layerCount, scale } |
"loadFromURL" | url, callback | Load image from URL directly |
"getScale" | callback | Returns current zoom scale |
"fitToView" | 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
);| Event Name | Args | Description |
|---|---|---|
"loadRasterImage" | base64, callback, options | Import raster, auto-trace to SVG |
"loadSvgImage" | svgString, callback | Load SVG source |
"generateRasterImage" | callback | Export as PNG data URL |
"getSVGSource" | callback | Get SVG source string |
"getSVGDynamicSource" | callback | Get dynamic SVG source |
"getSVGAbsoluteSource" | 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);
});| Event Name | Args | Description |
|---|---|---|
"active-file" | callback | Get active tab info |
"active-file:codeEditor:value" | value?, callback | Get or set editor text |
"active-file:codeEditor:json:schema" | schema, callback | Set JSON schema |
"active-file:codeEditor:syntax" | syntax?, callback | Get or set language mode (e.g., "javascript", "html") |
"active-file:codeEditor:options" | options, callback | Set Monaco editor options (readOnly, fontSize, wordWrap, etc.) |
"active-file:codeEditor:selection" | callback | Returns { startLine, startCol, endLine, endCol, text } |
"active-file:codeEditor:insertText" | text, position?, callback | Insert text at position { line, col } or at cursor |
"active-file:codeEditor:revealLine" | lineNumber, callback | Scroll to center a specific line |
"active-file:save" | callback | Save the active file |
"active-file:markers" | markers, callback | Set error/warning markers on the editor |
"files:open" | fileConfig, callback | Open a file in a new tab |
"files:close:all" | callback | Close all tabs |
"files:list" | callback | Returns array of open tabs [{ index, name, path, type, title }] |
"files:active:set" | tabIndex, callback | Activate a tab by index |
"gui:sidebar:toggle" | 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); }
);| Event Name | Args | Description |
|---|---|---|
"play" | callback | Start playback |
"pause" | callback | Pause playback |
"stop" | callback | Pause and seek to start |
"seek" | timeSeconds, callback | Seek to position |
"volume" | level?, callback | Get or set volume (0-1) |
"mute" | muted?, callback | Get or set muted state |
"getStatus" | callback | Returns { paused, currentTime, duration, volume, muted, playbackRate, loop, readyState, videoWidth, videoHeight } |
"source" | url, callback | Set media source (URL or file path) |
"playbackRate" | rate?, callback | Get or set playback speed |
"loop" | enabled?, callback | Get or set loop mode |
"fullscreen" | 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);| Event Name | Args | Description |
|---|---|---|
"write" | data, callback | Write text to the terminal display |
"sendInput" | data, callback | Send keystrokes to the shell |
"getSize" | callback | Returns { cols, rows } |
"resize" | cols, rows, callback | Resize terminal |
"clear" | callback | Clear terminal screen |
"reset" | callback | Reset terminal state |
"scrollToBottom" | callback | Scroll to bottom |
"focus" | 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);
});Both apps share the same filesystem API. All operations work on the input:// in-memory storage without server access.
| Event Name | Args | Description |
|---|---|---|
"fs:cwd" | path?, callback | Get or set current directory |
"fs:ls" | path, callback | List directory contents |
"fs:read" | path, callback | Read file as text |
"fs:write" | path, content, callback | Create or overwrite a file |
"fs:mkdir" | path, callback | Create a directory |
"fs:mkdirp" | path, callback | Create directory with parents |
"fs:remove" | path, options?, callback | Delete a single file or empty directory |
"fs:mv" | src, dst, callback | Move or rename |
"fs:copy" | src, dst, options?, callback | Copy a single file |
"fs:archive" | sourcePath, outputPath?, format?, options?, callback | Create archive from directory (no UI) |
"fs:bulkCopy" | sources, destination, options?, callback | Copy with progress UI (bin/cp) |
"fs:bulkRemove" | paths, options?, callback | Delete with confirmation UI (bin/rm) |
"fs:bulkArchive" | sources, outputPath?, format?, options?, callback | Archive with format selection UI (bin/archive) |
"fs:exists" | path, callback | Check if path exists |
"fs:stats" | path, callback | Get file/directory metadata |
"fs:readStreamUrl" | path, callback | Get blob: URL for streaming |
"fs:readBinary" | path, callback | Read file as base64 data URL |
"fs:refresh" | callback | Refresh current view |
"fs:download" | path, filename?, callback | Trigger browser download |
"gui:sidebar:toggle" | visible, callback | Show or hide the sidebar |
"gui:specialPaths:add" | entry, callback | Add a named bookmark to sidebar (supports paths array for breadcrumb grouping) |
"gui:specialPaths:remove" | path, callback | Remove an API-managed bookmark |
"gui:specialPaths:clear" | callback | Remove all sidebar bookmarks (built-in and API-managed) |
"apps:register" | appsMap, callback | Register custom apps (see Building Custom Apps) |
"apps:unregister" | appName, callback, disableDefaultApps? | Remove an app. Pass true as 3rd arg to also remove built-in apps |
"apps:list" | callback | List all registered apps |
"gui:setHomePath" | path, callback | Set default start directory |
"fs:createShortcut" | path, config, callback | Create a .lnk shortcut file that launches an app |
"viewport:addFiles" | path?, files, callback | Inject virtual clickable icons into the file listing |
Sandbox: Allfs:*operations are restricted to theinput://protocol (browser-only in-memory storage). Server paths are not accessible via the API. App paths must start withhttps:,http:,data:,blob:, orinput:.
// 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); });| Event Name | Args | Description |
|---|---|---|
"getHTML" | callback | Get document as HTML |
"setHTML" | html, callback | Set HTML content |
"getMarkdown" | callback | Get as Markdown |
"setMarkdown" | md, callback | Set Markdown content |
"getText" | callback | Get plain text |
"clearContent" | callback | Clear document |
"setMode" | mode, callback | "wysiwyg", "markdown", "source" |
"setTheme" | theme, callback | "light", "dark", "google-docs", "word" |
"usePreset" | preset, callback | "google-docs", "wysiwyg", "minimal" |
"setZoom" | level, callback | Set zoom level |
"readOnly" | val?, callback | Get/set read-only |
"insertBlock" | type, options?, callback | Insert table, image, code, quote, etc. |
"find" | query, opts?, callback | Find text |
"replace" | query, repl, opts?, callback | Find and replace |
"undo" / "redo" | callback | History navigation |
"export" | format?, callback | Export as html/markdown/pdf/docx |
"download" | format?, filename?, callback | Download as file |
"getWordCount" | callback | Word/char/paragraph counts |
"isDirty" | callback | Check unsaved changes |
| Event Name | Args | Description |
|---|---|---|
"getValue" | ref, callback | Get cell value |
"setValue" | ref, val, callback | Set cell value |
"setFormula" | ref, expr, callback | Set formula (e.g., "=SUM(A1:A10)") |
"setCellStyle" | ref, styles, callback | Style cell (bold, color, borders) |
"getSelection" | callback | Get selected range |
"setSelection" | ref, callback | Select range |
"addSheet" / "deleteSheet" / "renameSheet" | varies | Sheet management |
"insertRow" / "insertCol" / "deleteRow" / "deleteCol" | at, count?, callback | Row/column ops |
"sort" | range, options?, callback | Sort data |
"zoom" | level?, callback | Get/set zoom |
"readOnly" | val?, callback | Get/set read-only |
"exportCSV" | sheet?, callback | Export as CSV string |
"toJSON" / "fromJSON" | varies | Full state export/import |
"undo" / "redo" | callback | History navigation |
| Event Name | Args | Description |
|---|---|---|
"reset" | -- | Clear and relaunch simulation |
<!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>| 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 |