Closing the Workspaces window doesn't exit io.Connect Desktop

When a Workspace is the main working area, users sometimes expect the X on the Workspaces window to shut the whole platform down. On io.Connect Desktop it does not: the X closes only that window, and io.Connect Desktop keeps running. That’s by design. The platform exits when the last shell app closes, and your toolbar or Launchpad is a shell app that stays open. No setting changes it.

How do I shut down the whole platform?

Users can already exit from the tray icon. Its Exit shuts io.Connect Desktop down completely, and by default it asks first (dialogs.enableDefaultDialogs.preventShutdown).

Can the X on the Workspaces window shut the whole platform down?

Yes, if you catch the close and call exit. Whichever way you catch it, ask the user first, then finish with:

// keep the saved layout as it was, and skip io.Connect Desktop's own prompt
await io.appManager.exit({ autoSave: false, showDialog: false });

On 9.9 and later: prevent the close with frame.onClosing

From 9.9, the Workspaces API’s frame.onClosing lets you stop the close and ask. Call prevent() and return straight away, then show your confirmation: by default the platform waits only 2 seconds for the handler before closing anyway. This is confirmed in the code but not yet tested.

Stock Workspaces app: react from a hidden helper app

With the stock Workspaces app, a hidden helper app can ask once the Workspaces window has closed. It catches the taskbar and Alt+F4 too, but because the window is already gone, answering “no” starts a fresh, empty Workspaces window.

Its definition needs appManager: "full"; in the SDK’s default mode it never sees the Workspaces window close:

{
    "name": "<your-helper-name>",
    "type": "window",
    "autoStart": true,
    "hidden": true,
    "details": {
        "url": "<your-helper-url>",
        "hidden": true,
        "autoInjectAPI": { "enabled": true, "autoInit": { "appManager": "full" } }
    }
}
let platformShuttingDown = false;
io.appManager.onShuttingDown(() => {
    platformShuttingDown = true; // fires before the platform closes any window
    return { prevent: false };
});

io.appManager.onInstanceStopped(async (instance) => {
    const app = instance.application;
    if (platformShuttingDown || app.name !== "workspaces-demo") return; // the stock app's name

    const remaining = io.appManager.instances()
        .filter((i) => i.application.name === app.name && i.id !== instance.id);
    if (remaining.length > 0) return;

    if (await confirmExit()) { // your own confirmation window
        await io.appManager.exit({ autoSave: false, showDialog: false });
    } else {
        await app.start();
    }
});

Making the Workspaces app a shell app doesn’t help

Closing it still wouldn’t exit while the toolbar or Launchpad runs, and shell apps are left out of saved Global Layouts.

What about a custom Workspaces app?

In your own Workspaces app, the X is part of your header, so your code runs before the window closes: ask there, then call exit as above. Point the app definition’s details.url at your build and supply a SystemButtonsComponent through the <Workspaces /> header components. It replaces the minimize button too; the frame wires an element with the id workspaces-minimize-frame-button for you.

Give your close button its own id and class. The frame binds the id workspaces-close-frame-button and the class lm_close to its own close, so a button that reuses either shows your confirmation and closes the window anyway. Closes from the taskbar, Alt+F4 or the last workspace tab don’t go through your button.

Note: Closing without exiting changes the saved layout

If the Workspaces window is closed while io.Connect Desktop keeps running, the next Global Layout save records it as absent. See Global Layout comes back without the Workspaces window, or with it empty, after a restart.

*The exit call and the helper app were tested on 9.10.1.1