Changelog

io.Connect Desktop 9.9

Release date: 13.06.2025

Components Version
Electron 36.2.0
Chromium 136.0.7103.49
Node.js 22.15.0

Breaking Changes

Workspaces

The io.Connect Workspaces have been redesigned with the goal of improving the UI and providing smoother user experience, which necessitated introducing breaking changes in the @interopio/workspaces-ui-react library.

Workspaces

⚠️ Note that the following changes will be breaking for you only if you upgrade to version 4.0 and later of the @interopio/workspaces-ui-react library and:

  • You have a custom Workspaces App in which you are using any of the affected default components provided by the @interopio/workspaces-ui-react library. In this case, your app won't build when you upgrade to the latest version of the library.
  • You are using the default Workspaces UI app and you have customized it with CSS. In this case, your styles won't be applied when you upgrade to the latest version of the @interopio/workspaces-ui-react library.

This is due to the fact that some legacy components have been deprecated, the HTML structure of others has been modified, and a new system with dedicated CSS variables has been introduced for styling the Workspaces App. If you have completely customized your Workspaces App (entirely replaced the affected default components with your own custom components), you shouldn't experience any issues when migrating to the latest version of the @interopio/workspaces-ui-react library.

If you don't upgrade the @interopio/workspaces-ui-react library, your Workspaces App will still work in newer versions of io.Connect Desktop.

The following breaking changes have been made to the @interopio/workspaces-ui-react library:

  • The <WorkspaceTabV2 /> component has been renamed to <WorkspaceTab /> and the legacy <WorkspaceTab /> (version 1) has been removed.
  • The "Add Apps" button (+ button) in the Workspace window group header has been moved to the left of the header, after the window tabs.
  • The legacy Glue42 CSS files have been removed.
  • The legacy <GlueLogo /> component has been removed.
  • The legacy "Save Workspace" system popup has been removed. "Save Workspace" is now a dialog.
  • Changed the CSS classes for the <Logo /> component, for the background of an empty Workspace, and for the <WorkspaceTab /> component.
  • Changed the tab sizing strategy.
  • Completely restructured the HTML and CSS of the "Add Apps" popup.
  • Completely restructured the HTML and CSS of the "Add Workspace" popup.

Deprecated Platform Apps

Launchpad

The legacy Windows style io.Connect launcher known as the io.Connect Launchpad is now deprecated.

⚠️ Note that a new io.Connect launcher which is also called the io.Connect Launchpad is introduced in io.Connect Desktop 9.9. The new Launchpad is a conceptually different app from the legacy Launchpad and isn't meant to be its descendent despite the name coincidence.

Legacy Launchpad

Global Search App

The legacy Global Search App is now deprecated.

Legacy Global Search

New Features

New io.Connect Launcher

The new io.Connect Launchpad is now the default io.Connect launcher. It's an easily customizable web app that acts as a central hub for all io.Connect features - apps, Workspaces, Layouts, notifications, platform preferences, and more.

The Launchpad starts in expanded mode and contains conveniently placed sections with easy access to all available Workspaces, apps, and Layouts. At the top, you can find search bar that's not just a simple item filter, but a fully functional global search implemented using the search capabilities of the io.Connect framework.

The Launchpad provides quick access to the Notification Panel, the Download Manager, the Feedback Form, and contains a menu with various platform settings. It can be moved, minimized, and collapsed to a compact horizontal toolbar in order to save screen real estate. Users who prefer the Launchpad to be always available on the screen can pin the Launchpad to the left or the right side of their monitors.

Launchpad

CSS Variables for Customizing the io.Connect Platform

io.Connect Desktop and all its default system web apps (Workspaces, web groups, Launchpad, modal windows) use the io.Connect styles provided by the @interopio/theme package. You can use the dedicated CSS variables provided by the io.Connect themes to customize the system apps of your platform.

The following image demonstrates the available CSS variables for customizing the Workspace tab of the Workspaces App:

Workspace Tab

Modal Windows

⚠️ Note that this feature is still experimental in io.Connect Desktop.

⚠️ Note that until now, the only option to show dialogs from your apps was by using the showDialog() method of an IOConnectWindow instance. This method is still supported and you can use it to show the already existing set of predefined dialogs, as well as custom ones, but it doesn't support the new set of predefined dialogs available via the new @interopio/modals-api library. Respectively, the @interopio/modals-api library doesn't support the predefined dialogs available via the showDialog() method.

io.Connect Desktop now supports a new set of predefined modal windows (alerts and dialogs), which you can use in your interop-enabled apps via the new @interopio/modals-api library.

Alerts

The new "alerts" top-level key in the system.json system configuration file of io.Connect Desktop allows you to provide configuration settings for your alert windows. The already existing dialogs configuration, found under the "dialogs" top-level key in the system.json file, applies to the newly introduced dialogs as well.

Alerts Configuration

To provide settings for alert windows, use the "alerts" top-level key in the system.json system configuration file of io.Connect Desktop:

{
    "alerts": {
        "enabled": true,
        "width": 450,
        "height": 100,
        "ttl": 7000,
        "roundedCorners": true
    }
}

The "alerts" object has the following properties:

Property Type Description
"enabled" boolean If true (default), will enable the io.Connect alerts.
"height" number Height in pixels for the io.Connect alerts. Defaults to 82.
"roundedCorners" boolean If true, the io.Connect Window containing the alerts will have rounded corners. Defaults to false.
"transparent" boolean If true (default), the io.Connect Window containing the alerts will be transparent.
"ttl" number Interval in milliseconds that defines how long the alert is displayed to the user before it automatically disappears. Defaults to 5000.
"url" string URL pointing to the location of the io.Connect alerts app. You can use this to provide a URL pointing to your custom alerts app.
"width" number Width in pixels for the io.Connect alerts. Defaults to 400.

Modals API

To enable the Modals API in your apps, you must include and initialize the @interopio/modals-api library in your project. Initialize the @interopio/desktop library and pass the globally available IOModals() factory function to the libraries array of the configuration object. The IOModals() factory function, like the IODesktop() factory function, is injected in the global window object:

import IODesktop from "@interopio/desktop";
import IOModals from "@interopio/modals-api";

// Initializing the Modals API.
const config = {
    libraries: [IOModals]
};

const io = await IODesktop(config);

// Options for displaying an alert.
const alertOptions = {
    variant: "success",
    text: "Successfully initialized the Modals API!"
};

// Displaying an alert.
await io.modals.alerts.request(alertOptions);

The following example demonstrates displaying one of the predefined dialogs via the Modals API and providing custom content for it:

// Options for displaying a dialog.
const dialogOptions = {
    templateName: "noInputsConfirmationDialog",
    variables: {
        title: "New Updates Available",
        heading: "Restart to update",
        text: "New updates are available for the io.Connect platform that require restart. Do you want to restart the platform now?",
        actionButtons: [
            { variant: "primary", text: "Restart", id: "restart" },
            { variant: "outline", text: "Remind me later", id: "remind-later" }
        ]
    }
};

// Displaying a dialog.
await io.modals.dialogs.request(dialogOptions);

Dialogs

ℹ️ For details on using the Modals API, see the Capabilities > Modals section.

Prevent Closing of Workspaces App Instances

By default, Workspaces App instances will close automatically when a Frame close operation is requested (e.g., when the user clicks the "Close" button or when you try to close a Frame programmatically).

To get notified when a Workspaces App instance is about to close and prevent it from closing automatically, use the onClosing() method of a Frame instance. The onClosing() method receives as an argument a callback that will be executed when the Frame instance is about to close. The callback receives as an argument an object with a prevent property holding as a value a function that you can use to prevent closing the Frame:

const myFrame = await io.workspaces.getMyFrame();

// Use the `prevent()` function to prevent closing the Frame.
const handler = ({ prevent }) => prevent();

// Get notified when the Frame is about to be closed.
myFrame.onClosing(handler);

Preventing Frame Close

The close() method of a Frame instance now accepts a FrameCloseOptions object as an argument. Use this argument to specify whether to allow the Frame to prevent closing and whether to show a confirmation dialog to the user:

const options = {
    // This will allow the Frame to prevent closing.
    allowPrevent: true,
    // A confirmation dialog for closing the Frame will be displayed to the user.
    // This property will override the dialog setting in the Workspaces App definition.
    showDialog: true
};

await myFrame.close(options);

By default, a confirmation dialog for closing the Workspaces App instance will be displayed to the user if the Frame close operation has been prevented. To configure the default behavior for displaying this dialog, use the "dialogs" object under the "details" top-level key in the Workspaces App definition. To disable showing this dialog, set the "preventClose" property of the "enableDefaultDialogs" object to false:

{
    "dialogs": {
        "enableDefaultDialogs": {
            "preventClose": false
        }
    }
}

⚠️ Note that this configuration can be overridden programmatically by passing a FrameCloseOptions object as an argument to the close() method of a Frame instance.

Taskbar Icons for Workspaces App Instances

To retrieve and set the taskbar icons for individual Workspaces App instances, use the getIcon() and setIcon() methods of a Frame instance respectively. This allows you to use different taskbar icons for the different Workspaces App instances. The icon must be provided as a Base64 string inside an Icon object:

const myFrame = await io.workspaces.getMyFrame();

// Retrieve the current Frame icon.
const currentIcon = await myFrame.getIcon();

console.log(currentIcon.base64Image);

// Set a new icon for the Frame.
const newIcon = {
    base64Image: "my-new-icon-as-base64-string"
};

await myFrame.setIcon(newIcon);

Dragging Windows

To start dragging a window programmatically when the left mouse button is pressed, use the dragMove() method of an IOConnectWindow instance. Optionally, provide as an argument a DragMoveOptions object holding the location of the mouse cursor within the window at the time the dragging operation was initiated. The io.Connect platform will use that location to place the window under the mouse cursor when the mouse is moving (e.g., in case the mouse has already moved several pixels in any direction before the dragging operation has been initiated by the platform).

The following example demonstrates how to initiate a window dragging operation when the user holds down the left mouse button inside a predefined element of an app designated as a move area:

const myMoveArea = document.getElementById("my-move-area");
const myWindow = io.windows.my();
const handler = (event) => {
    event.stopPropagation();

    // Check whether the left mouse button was pressed.
    if (event.button === 0) {
        // Retrieve the coordinates of the mouse cursor when the left mouse button was pressed.
        const options = {
            location: {
                x: event.pageX,
                y: event.pageY
            }
        };

        // Start dragging the window.
        myWindow.dragMove(options);
    };
};

myMoveArea.addEventListener("mousedown", handler);

⚠️ Note that the dragMove() method works only when the left mouse button is pressed. Attempting to use this method with any other mouse button will cause the platform to throw an error.

Layouts

The following Layout options and events are now available in the Layouts API.

Ignoring Contexts when Saving Layouts

The NewLayoutOptions() object passed as an argument to the save() method was expanded with an ignoreContexts property. Use this property to instruct the io.Connect platform to skip saving any window or Workspace contexts when saving a Layout:

const options = {
    name: "My Layout",
    ignoreContexts: true
};

const savedLayout = await io.layouts.save(options);

Default Global Layout Changed

To get notified when the default Global Layout has been changed, use the onDefaultGlobalChanged() method and provide a callback for handling the event. The callback will receive as an argument an object with a name property holding the name of the newly selected default Global Layout. If the event was fired because the default Global Layout was cleared, the argument will be undefined:

const handler = (layout) => {
    if (layout) {
        console.log(`The default Global Layout was changed to "${layout.name}".`);
    } else {
        console.log ("The default Global Layout was cleared.");
    };
};

const unsubscribe = io.layouts.onDefaultGlobalChanged(handler);

Layout Renamed

The callback passed to the onRenamed() method now accepts as a second argument an object with a name property holding the previous name of the renamed Layout:

const handler = (currentLayout, previousLayout) => {
    console.log (`Layout "${previousLayout.name}" was renamed to "${currentLayout.name}".`)
};

const unsubscribe = io.layouts.onRenamed(handler);

Window Placement

The PlacementSettings object passed as an argument to the place() method of the Window Management API has been extended with an optional saveBounds property enabling you to save the last known bounds of the io.Connect Window when performing a placement operation with snapping enabled. A new clearPlacement() method has been added that allows you to clear the placement settings for the window after a placement operation with snapping enabled, and optionally restore its previously saved bounds or provide new bounds:

// Placing a window on the screen.
const placementSettings = {
    // Instruct the platform to always place this window at the specified position
    // on the display even after resolution, scaling, and monitor changes.
    snapped: true,
    verticalAlignment: "top",
    saveBounds: true
};

await myWindow.place(placementSettings);

// Clearing the window placement and restoring the last known window bounds.
const clearPlacementSettings = {
    restoreBounds: true
};

await myWindow.clearPlacement(clearPlacementSettings);

⚠️ Note that the clearPlacement() method will work only after placement operations with snapping enabled (i.e., snapped is set to true).

Windows Runtime Configuration

The WindowConfiguration that is passed as a required argument to the configure() method of an IOConnectWindow instance has new properties and some of its existing ones have been modified.

To enable or disable the web page search feature for a window at runtime, use the search property of the WindowConfiguration object:

const config = {
    search: {
        enabled: true
    }
};

await myWindow.configure(config);

Size & Move Areas for Windows

The hasSizeAreas property of the WindowConfiguration object is now valid for all window modes (flat, tab, HTML, and frameless):

const config = {
    hasSizeAreas: false
};

await myWindow.configure(config);

⚠️ Note that the hasSizeAreas property is valid only for single windows. Also, setting hasSizeAreas to false will remove the drop shadows of flat, tab, and HTML windows.

The hasMoveAreas property of the WindowConfiguration object is now valid for frameless and HTML windows:

const config = {
    hasMoveAreas: false
};

await myWindow.configure(config);

Taskbar Icon

To control the visibility of the taskbar icon for the window, use the showInTaskbar property of the WindowConfiguration object:

const config = {
    showInTaskbar: false
};

await myWindow.configure(config);

Transparency for Frameless Windows

To specify transparency settings for frameless window opened via the browser native window.open() method, use the transparent option:

const url = "https://example.com";
// Opening a transparent frameless window.
const options = "mode=frameless, transparent=true";

window.open(url, undefined, options);

Rounded Corners for Frameless Windows

To specify that a frameless window should have rounded corners, use the "roundedCorners" property of the "details" top-level key in the app definition:

{
    "name": "my-frameless-window",
    "type": "window",
    "details": {
        "url": "https://example.com/my-frameless-app",
        "mode": "frameless",
        "roundedCorners": true
    }
}

⚠️ Note that the "roundedCorners" property is valid only for frameless windows. In Windows versions older than Windows 11 build 22000 this property won't have any effect and frameless windows won't have rounded corners.

Preventing Overrides of App Definition Properties

You can now use the "allowOverrides" property of the "details" top-level key in the app definition to prevent apps from overriding programmatically the values for the "url" (for web apps) and "command" (for native apps) properties. This enables you to additionally improve your platform security by ensuring that it won't be possible for apps within the platform to load other unknown apps that may execute malicious code.

The following example demonstrates using the "allowOverrides" property of the "details" top-level key in the app definition to prevent apps from overriding the "url" property in a web app definition when attempting to start this app programmatically via the io.Connect APIs:

{
    "details": {
        "url": "https://my-org.com/my-app",
        "allowOverrides": {
            // This is set to `true` by default and it's highly recommended to explicitly set it to `false`.
            "url": false
        }
    }
}

The following example demonstrates using the "allowOverrides" property of the "details" top-level key in the app definition to prevent apps from overriding the "command" property in a native app definition when attempting to start this app programmatically via the io.Connect APIs:

{
    "details": {
        "command": "MyApp.exe",
        "allowOverrides": {
            // This is set to `true` by default and it's highly recommended to explicitly set it to `false`.
            "command": false
        }
    }
}

Alternate Window Management Behavior

By default, when you move an io.Connect Window close enough to another io.Connect Window and drop it, it will automatically stick to it. If you move a window over a tabbed window group and drop it, it will be added to that group. The same is valid for dropping windows and window groups in Workspaces. To prevent these automatic operations, you can hold the ALT key while moving an io.Connect Window or a window group.

Now, it's possible to reverse these default window management behaviors by using the "alternateBehavior" property of the "windowManagement" object in the system.json system configuration file of io.Connect Desktop.

The following example demonstrates how to instruct the io.Connect platform to stick windows together and add dropped windows to tab groups and Workspaces only when the ALT key is pressed:

{
    "alternateBehavior": {
        "snapping": {
            "reverse": true
        },
        "dropInTabGroup": {
            "reverse": true
        },
        "dropInWorkspace": {
            "reverse": true
        }
    }
}

Capturing Client App Errors

To instruct io.Connect Desktop to capture console messages, network request errors, and any unhandled errors originating from your web app, use the "logging" property of the "details" top-level key in the app definition:

{
    "details": {
        "logging": {
            "consoleMessages": {
                "enabled": true,
                "level": "info"
            },
            "networkRequestErrors": {
                "enabled": true
            },
            "unhandledErrors": {
                "enabled": false
            }
        }
    }
}

The captured errors will be logged in the application.log file of io.Connect Desktop located in the <installation_location>/interop.io/io.Connect Desktop/UserData/<ENV>-<REG>/logs folder, where <ENV>-<REG> represents the environment and region of io.Connect Desktop (e.g., DEMO-INTEROP.IO).

Capturing these errors is disabled by default. The "logging" property accepts a Boolean or an object as a value. If set to true, all console messages, networks request errors, and unhandled errors will be logged. You can use an object as a value to provide settings for the types of errors you want to capture.

The "logging" object has the following properties:

Property Type Description
"consoleMessages" object Settings for capturing console messages.
"networkRequestErrors" object Settings for capturing network request errors.
"unhandledErrors" object Settings for capturing unhandled errors (e.g., unhandled Promise rejections, errors not logged in the console).

The "consoleMessages" object has the following properties:

Property Type Description
"enabled" boolean If true, will allow capturing all console messages. Defaults to false.
"level" "trace" | "debug" | "info" | "warn" | "error" Sets the level at which to capture console messages. The platform will capture all console messages at the specified level and above. Defaults to "trace".

The "networkRequestErrors" object has the following properties:

Property Type Description
"enabled" boolean If true, will allow capturing all network request errors. Defaults to false.

The "unhandledErrors" object has the following properties:

Property Type Description
"enabled" boolean If true, will allow capturing all unhandled errors. Defaults to false.

Combining Taskbar Icons

When the user sticks two or more io.Connect web apps together to form a window group, the individual taskbar icons are combined automatically into one icon representing the window group. The taskbar icon used for window groups is customizable and you can use the same icon for the window groups as for any of your other apps. Until now, however, the taskbar icons of window groups and individual app instances wouldn't be combined in the taskbar even if you were using the same icon for both.

It's now possible to combine these icons by using the "iconURL" property of the "group" object in the themes.json themes configuration file of io.Connect Desktop to provide the URL of the icon as specified in the definitions of your apps. This URL won't be used by the platform to retrieve the icon itself, but only to create an icon ID that the OS needs for grouping icons in the taskbar. You still have to provide the file name of the desired group icon by using the "icon" property of the "group" object and have to make sure that your custom icon is present in the <installation_location>/interop.io/io.Connect Desktop/Desktop/assets/images folder.

To define an icon for your app, use the "icon" top-level key in the app definition file:

{
    "name":"my-app",
    "title":"My App",
    // Defining an icon for your app.
    "icon": "https://example.com/my-universal-icon.ico",
    "type": "window",
    "details":{
        "url":"https://example.com/my-app"
    }
}

To use the same icon as a taskbar icon for window groups, place the icon file in the <installation_location>/interop.io/io.Connect Desktop/Desktop/assets/images folder and define the icon inside the "group" object in the themes.json file of io.Connect Desktop for all available themes:

[
    {
        "name": "dark",
        "displayName": "Night",
        "properties": {
            "group": {
                "icon": "my-universal-icon.ico",
                "iconURL": "https://example.com/my-universal-icon.ico"
            }
        }
    }
    // Do this for all the other available themes.
]

Now, all your apps and window groups that are using the same icon will be automatically combined in the Windows taskbar.

Logging for Child Windows of Web Apps

It's now possible for child windows of web apps opened via the browser native window.open() method to use the Logger API to create log entries. The log entries created by child windows are located in the parent app log file. Each log entry by a child app contains specific details about the child app (indication that the log entry was made by a child app, instance ID and URL of the child app) allowing you to easily find and extract the relevant information. The following example demonstrates the shape of an actual log entry by a child app:

[2025-06-16T10:12:12.476] [INFO] [15308_13] [child:15308_14|https://interop.io/] - Log entry by a child app.

Environment Variables from Remote Configuration Stores

To apply environment variables from a remote configuration store, or to override any environment variables set in your local gilding configuration, you must define them in an env.json file in io.Manager or in your custom REST service:

// In `env.json`.
{
    "set": {
        // These will override any existing environment variables with the same names in your local `gilding.json`.
        // To remove an environment variable, provide an empty string as a value.
          "MY_APP_PROD": "https://my-app.com",
        "MY_APP_DEV": "https://dev.my-app.com",
        "REMOVED_VAR": ""
    }
}

Defining Additional Metrics Attributes

You can now define additional resource and metrics attributes to be included by io.Connect Desktop when publishing metrics. To define additional attributes, use the "additionalResourceAttributes" and "additionalAttributes" properties of the "metrics" object under the "otel" top-level key in the system.json system configuration file of io.Connect Desktop. You can use environment variables as values. Environment variables must start and end with a percent sign (e.g., %MyEnvVar%), and can contain a fallback value (e.g., %MyEnvVar?literalDefaultValue%).

The following example demonstrates defining additional metrics and resource-level attributes:

{
    "otel": {
        "enabled": true,
        "metrics": {
            "enabled": true,
            "url": "https://otlp-http.interop.io/v1/metrics",
            // Additional attributes for each published metric.
            "additionalAttributes": {
                "platformName": "MyPlatform",
                "envVar": "%MyEnvVar%/config"
            },
            // Additional resource-level attributes.
            "additionalResourceAttributes": {
                "service.name": "MyService",
                "service.namespace": "MyServiceNamespace"
            }
        }
    }
}

Improvements & Bug Fixes

  • Upgraded to Electron 36.2.0 (Chromium 136).

  • Improved handling of environment variables defined in the "customProperties" and "userAgent" properties in an app definition.

  • Improved the behavior for invoking the notification onclick and onaction handlers.

  • Improved the window placement behavior in cases of resolution and monitor changes.

  • Fixed a bug related to applying Workspace size constraints.

  • Fixed a race condition related to saving Layouts.

  • Fixed bugs related to using multiple Channels.

  • Fixed a race condition related to subscribing for the onClosing() event for window groups.