0.4.5 plans to add four optional arrays to contributions so a plugin can appear somewhere other than a sidebar panel: a standalone window, a top-level page, the profile bar and the editor. All of them refer to a declared workspaces[].id, load that workspace's entry page and inherit its permission grants. The install page gains a group headed "This plugin will appear in…" that lists these places one by one.
New contributions at a glance
| Field | Maximum | Where it appears |
|---|---|---|
windows | 8 | A standalone window |
pages | 1 | An entry in the top-level navigation, plus a full page |
profile_items | 2 | A status item and menu in the profile bar |
editors | 8 | An editor that opens files by extension (declared only in 0.4.5) |
First-phase limits
- No Agent binding and no
agent.trigger: callingagent.*from a new surface returnsPLUGIN_WORKSPACE_PERMISSION_DENIED. Keep features that work with the Agent in a sidebar workspace. - A window loads only pages from the package: in 0.4.5 a window can open only a workspace page declared in the manifest; loading an external web page waits for 0.4.6. Use
ui.open_externalfor external links. - editors is declaration only: 0.4.5 validates
editorsand shows them on the install page under "will appear in…", while really opening a file with a plugin waits for 0.4.6.
windows: standalone windows
{
"id": "float",
"title": "Floating timer",
"workspace": "main",
"chrome": "none",
"resizable": false,
"taskbar": true,
"always_on_top": true,
"transparent": true,
"shadow": false,
"size": { "width": 320, "height": 200 },
"min_size": { "width": 64, "height": 64 },
"max_size": { "width": 640, "height": 400 },
"position": "bottom_right",
"remember_bounds": true,
"single_instance": true,
"layouts": [
{ "id": "orb", "size": { "width": 64, "height": 64 }, "anchor": "bottom_right" },
{ "id": "card", "size": { "width": 320, "height": 200 }, "anchor": "bottom_right" }
]
}
| Field | Values | Notes |
|---|---|---|
id | local identifier | Unique within the plugin. |
title | 1–80 | Window title. |
workspace | a workspace id | The workspace the window loads; it must already be declared. |
chrome | app | none | app: a title bar in the app's own style. none: frameless, so every pixel except the host's close badge belongs to the plugin. |
resizable | default true | Whether the window can be resized. |
taskbar | default true | Whether to show a taskbar item; setting it to false requires ui.window.hide_taskbar. |
always_on_top | default false | Whether the window stays on top; setting it to true requires ui.window.always_on_top. |
transparent | default false | Whether the window is transparent; it can only be set to true when chrome is none, and that requires ui.window.transparent. |
shadow | default true | Whether the window has a shadow. |
size / min_size / max_size | { width, height } | Initial, minimum and maximum size in logical pixels. |
position | cascade (default) | center | top_left | top_right | bottom_left | bottom_right | Initial position: cascade from the window that opened it, centred, or one of the four screen corners. Absolute coordinates are not accepted. |
remember_bounds | default true | Restore the last position and size when the window reopens; fall back to position when it would land off screen. |
single_instance | default true | Opening it again just focuses the existing window. |
layouts | ≤8 items of { id, size, anchor } | Preset layouts you can switch between; anchor is the corner or center that stays put when switching. |
Size bounds
- With
chromeset tonone, the width and height ofsize,min_size,max_sizeandlayouts[].sizerange from 48 to 3840; withapp(and the pendingsystem) they range from 120 to 3840. min_size≤size≤max_sizemust hold, andlayouts[].sizemust sit betweenmin_sizeandmax_sizetoo. A small floating ball needs a small enoughmin_size.- At runtime the host also keeps a window inside the work area of its display, and never lets it cover a full-screen app.
- A plugin can open at most 4 windows at once, and 12 in total across the app.
The host's safety net
A window with chrome set to none keeps a small hot zone in its top right corner that the plugin cannot cover, and the host's close badge appears when the pointer enters it. Every plugin window, including one with its taskbar item hidden, appears in the host's "Plugin windows" list, where you can find it and close it. Disabling, uninstalling or upgrading a plugin, or changing its permissions, closes its windows at once; signing out closes every plugin window.
pages: top-level pages
At most 1 entry: { id, title, workspace, icon? }. title is 1–32 characters, and icon refers to a resource id in resources. The host adds an entry to the top-level navigation and renders that workspace as a full page.
profile_items: the profile bar
At most 2 entries: { id, label, icon?, menu }. label is 1–32 characters; menu holds at most 6 entries, each { id, label, opens: { kind, id } }, where kind is workspace, window or page and id must refer to a matching declared item. Status text is updated at runtime through profile.set_status, stays within 48 characters, and the host shows it as plain text without embedding an iframe.
editors: editors (declaration only)
At most 8 entries: { id, title, workspace, extensions, priority, mode }. extensions holds 1–16 extensions (^[a-z0-9]{1,16}$); priority is default or option; mode is readonly or readwrite.
readwrite passes validation but is treated as read-only. Opening a file with a plugin really arrives in 0.4.6.New permissions
All four are workspace-scoped permissions written with worker set to host, and the grant is recorded against the workspace the window refers to. The three sensitive ones are best declared optional; the install page ticks them separately and warns about the risk of imitating system or host interface elements.
| Permission | When it is needed | Without the grant |
|---|---|---|
ui.window | windows is not empty | window.open returns PLUGIN_WINDOW_PERMISSION_DENIED |
ui.window.always_on_top | A window sets always_on_top: true | Degrades to a window that does not stay on top |
ui.window.hide_taskbar | A window sets taskbar: false | Degrades to showing the taskbar item |
ui.window.transparent | A window sets transparent: true | Degrades to an opaque window |
Declaring a field that needs a sensitive permission without requesting that permission makes the install fail with PLUGIN_MANIFEST_INVALID. You can query what actually took effect through window.capabilities.
Window API
| Bridge method | SDK (stabledgePlugin.window.*) | Notes |
|---|---|---|
window.open | window.open | Parameters { window_id, user_activated }, and it must be called inside a user action; with single_instance an already open window is focused and its handle returned. |
window.close | window.close | Omit the handle to close the calling window; a plugin can only close its own windows. |
window.focus | window.focus | Needs user activation, so a plugin cannot grab focus. |
window.set_title | window.setTitle | Up to 80 characters of plain text, which also changes the taskbar title. |
window.set_layout | window.setLayout | Can only switch to a layout declared in layouts. |
window.start_drag | window.startDragging | Starts dragging the window. |
window.capabilities | window.capabilities | Returns what actually took effect: platform, chrome, transparent, always_on_top, taskbar, rounded_corners and layouts. |
profile.set_status | — | Parameters { item_id, text }, updating the status text in the profile bar. |
Inside a window the SDK treats elements with the [data-se-drag-region] attribute as drag regions, except those with [data-se-no-drag]. A window handle is an opaque string.
Events (subscribe with window.on): window.opened, window.closed (where reason is user, plugin, disabled, logout, host_exit or crashed) and window.focus_changed.
Error codes: PLUGIN_WINDOW_NOT_DECLARED, PLUGIN_WINDOW_LIMIT_REACHED, PLUGIN_WINDOW_PERMISSION_DENIED, PLUGIN_WINDOW_NOT_FOUND, PLUGIN_WINDOW_LAYOUT_NOT_DECLARED, PLUGIN_WINDOW_USER_ACTIVATION_REQUIRED and PLUGIN_WINDOW_OPEN_FAILED.
Reserved plugin names
At install and upgrade a plugin's name (display names in every language included) is normalized: surrounding whitespace stripped, full-width and half-width forms and case unified, and spaces and common separators removed. The result is compared against two lists of reserved names:
- Level A: app names (StablEdge and its Chinese name) and system window names (Task Manager, System Settings, User Account Control and their English names). A name that is identical to one of them, starts with one, or ends with one is refused.
- Level B: short words from the host interface (Settings, Plugins, Skills, Knowledge base, Sign in and their English forms). Only an exact match is refused, so a normal name such as "Translation plugin" is unaffected.
A hit makes the install fail with PLUGIN_MANIFEST_INVALID, naming the reserved name that was hit. Bundled plugins and plugins whose signature checks out against the official public key are exempt. The check applies to new installs and upgrades only, and is not applied retroactively to plugins already installed.
Open questions
chromeset to"system"(using the native system title bar); transparency is not allowed with that value. TBD- The
ui.window.positionpermission, which would let a plugin set window coordinates. TBD - The host prefixing every window title with "plugin name · ", leaving the plugin to change only the second half. TBD