Window management
Overview
cloudWindow manages the current window: state, APIs for control, and messaging.
Import
import cloudWindow from '@ugreen-nas/core/cloudWindow'Messaging concepts
Messages
Regular messages sent via send / sendTo are received by the target window through cloudWindow.on('message', ...); cloudWindow.listenWindowData is mainly for data injected during create/activate, plus data delivered by host-side sendDataTo(). Callbacks receive the source for every message/event.
Note: When using send(data, event), sendTo(winId, data, event), etc., custom event names must not collide with built-in window events listed in this doc (e.g. load-finish, close, focus). Otherwise the Promise is rejected with an error.
Events
cloudWindow.on('xxx', (data, from) => {
// data: payload
// from: source
})Window events
Windows created with cloudWindowMgr.create() expose the following events.
load-finish
Fires when the window finishes loading.
Callback arguments:
| Name | Type | Description |
|---|---|---|
| url | string | Loaded URL |
load-error
Fires when loading fails.
| Name | Type | Description |
|---|---|---|
| url | string | URL that failed |
focus
Fires when the window gains focus.
Note: No payload.
blur
Fires when the window loses focus.
Note: No payload.
move
Fires when the window finishes moving.
| Name | Type | Description |
|---|---|---|
| x | number | Window x |
| y | number | Window y |
| width | number | Width |
| height | number | Height |
moving
Fires continuously while the window is moving.
| Name | Type | Description |
|---|---|---|
| x | number | Window x |
| y | number | Window y |
| width | number | Width |
| height | number | Height |
resizing
Fires continuously while resizing (drag).
| Name | Type | Description |
|---|---|---|
| x | number | Window x |
| y | number | Window y |
| width | number | Width |
| height | number | Height |
resize
Fires when resize completes.
| Name | Type | Description |
|---|---|---|
| x | number | Window x |
| y | number | Window y |
| width | number | Width |
| height | number | Height |
maximize
Fires when maximized.
| Name | Type | Description |
|---|---|---|
| x | number | Window x |
| y | number | Window y |
| width | number | Width |
| height | number | Height |
un-maximize
Fires when leaving maximized state.
| Name | Type | Description |
|---|---|---|
| x | number | Window x |
| y | number | Window y |
| width | number | Width |
| height | number | Height |
minimize
Fires when minimized.
Note: No payload.
restore
Fires when restored from minimized.
Note: No payload.
close
Fires after the window closes; cannot be cancelled.
| Name | Type | Description |
|---|---|---|
| data | { fake: boolean } | fake === true means hide/keep-alive; false means real close/destroy |
before-close
Fires before close; call preventDefault() to cancel.
| Name | Type | Description |
|---|---|---|
| e | Event | Native event; e.preventDefault() blocks close |
hostChange
Fires when the requested BaseUrl changes (e.g. device address).
| Name | Type | Description |
|---|---|---|
| data | any | Change details (e.g. new URL) |
always-on-top-changed
Fires when always-on-top changes.
| Name | Type | Description |
|---|---|---|
| value | boolean | Current always-on-top state |
mouse-enter
Fires when the pointer enters the window.
| Name | Type | Description |
|---|---|---|
| x | number | Window x |
| y | number | Window y |
| width | number | Width |
| height | number | Height |
| top | boolean | Whether window is top-most |
mouse-leave
Fires when the pointer leaves the window.
| Name | Type | Description |
|---|---|---|
| x | number | Window x |
| y | number | Window y |
| width | number | Width |
| height | number | Height |
| top | boolean | Whether window is top-most |
before-help
Fires when the help button is clicked; preventDefault can block default behavior.
| Name | Type | Description |
|---|---|---|
| e | BeforeHelpEvent | CustomEvent; e.detail.message is help URL, e.detail.version may be null |
| url | string | undefined | Help URL (shortcut matching e.detail.message) |
before-show
Fires before a keep-alive window is shown/activated.
Note: No payload.
show
Fires when the window becomes visible (from hidden or show becomes true).
Note: No payload.
hide
Fires when hidden (show becomes false).
Note: No payload.
will-show
Fires before transitioning from hidden to visible (host cloudWindowMgr.show() sets config.show to true after this synchronous channel).
| Name | Type | Description |
|---|---|---|
| e | Event | preventDefault() can block this show; window stays hidden |
Additional notes:
before-showmainly corresponds to the host re-activating a cachedhideOnClosewindowwill-showmainly corresponds to explicitcloudWindow.show()calls or host-sideshow()
message
Fires when another window uses sendTo or the desktop uses send.
| Name | Type | Description |
|---|---|---|
| data | any | Payload |
| from | string | Sender window id or source |
wsData
In-window event from desktop WebSocket downlink.
| Name | Type | Description |
|---|---|---|
| data | object | WebSocket packet |
Example backend payload shape:
{
"app_id": "com.xxxx",
"data": {
"body": {},
"event": "xxxx"
},
"role": "",
"type": "app-event"
}Drag-and-drop events
Based on HTML5 Drag and Drop:
- Drag files from the OS into the window
- Drag elements to other windows
- Drag from browser into the window
- Cross-window drags
Order
Normal flow:
dragEnter → dragOver (repeats) → drop/dragEnd → dragLeaveCross-window:
Window A: dragEnter → dragOver → dragLeave
Window B: dragEnter → dragOver → drop/dragEnd → dragLeavedragEnter
Fires when a drag enters the window.
When:
- External files/content enter the window
- Another window drags into this one
- Child element drags over parent in the same window
Callback arguments:
| Name | Type | Description |
|---|---|---|
| data | DragData | any | undefined | Drag payload |
Sources
- Browser
DataTransfer— if no custom payload, a standardDragDataobject - Empty drag — neither custom data nor
dataTransfer.types→undefined
DragData shape:
interface DragData {
files?: File[]
items?: DataTransferItemList
types?: string[]
effectAllowed?: string
dropEffect?: string
text?: string
[key: string]: any
}Notes:
files: files only, not folder contentsitems: lower-level API;webkitGetAsEntry()can recurse folders
Example:
cloudWindow.on('dragEnter', (data) => {
if (!data) {
console.log('Empty drag')
return
}
if (typeof data === 'object' && !data.types) {
console.log('Custom payload:', data)
}
if (data.files) {
console.log('Files:', data.files)
}
if (data.text) {
console.log('Text:', data.text)
}
})dragOver
Fires continuously while dragging over the window.
| Name | Type | Description |
|---|---|---|
| data | DragData | any | undefined | Same as dragEnter |
dragEnd
Fires when the drag is released (drop) in the window.
| Name | Type | Description |
|---|---|---|
| data | DragData | any | undefined | Same as dragEnter |
When:
- Mouse released inside the window
- Released on empty chrome not swallowed by a child
drop
Note: If a child calls e.stopPropagation(), dragEnd may not fire; dragEnd is a bubbling fallback.
Example:
cloudWindow.on('dragEnd', (data) => {
if (data?.files) {
console.log('Files:', data.files)
} else if (data) {
console.log('Custom:', data)
}
})dragLeave
Fires when the drag leaves the window.
Note: Counter-based to avoid spurious child-boundary events. No payload.
Drag scenarios
Scenario 1: cross-window custom data
Sender: in dragstart, use dataTransfer (setData / effectAllowed) or your pipeline.
function startDrag(e: DragEvent) {
if (!e.dataTransfer) return
e.dataTransfer.setData('text/plain', JSON.stringify({ id: 123, name: 'Item from test2' }))
e.dataTransfer.effectAllowed = 'copy'
}Receiver (JSON in text/plain → parse data.text):
cloudWindow.on('dragEnd', (data) => {
if (data?.text) {
try {
const payload = JSON.parse(data.text)
console.log('Cross-window payload:', payload)
} catch {
/* treat as plain text */
}
}
})Scenario 2: custom drop zone
Use native HTML5 on a zone (e.g. upload box):
<template>
<div
class="drop-zone"
@dragenter="handleDragEnter"
@dragleave="handleDragLeave"
@dragover.prevent="handleDragOver"
@drop.prevent.stop="handleDrop"
>
Drop files here
</div>
</template>
<script>
export default {
methods: {
handleDrop(e) {
// Stop bubbling so cloudWindow dragEnd does not fire
e.stopPropagation()
const files = e.dataTransfer.files
console.log('Drop zone files:', files)
}
}
}
</script>Note: Custom zones should call e.preventDefault() and e.stopPropagation(); the latter prevents bubbling to cloudWindow’s dragEnd.
Drag notes
Payload may be:
undefined— empty/invalid dragDragData— standard browser payload- Any custom object — from internal drag plumbing if present
Suggested handling:
cloudWindow.on('dragEnd', (data) => {
if (!data) {
console.log('Invalid drag')
return
}
if (data.files) {
// handle files
} else if (data.id) {
// handle custom payload
}
})Static properties
cloudWindow.winId
Read-only unique id of the current window.
Type: string
Methods
Child windows
createSubWin()
Creates a child window; options match Window configuration.
Only with explicit belowGroup: null is it treated as non-subordinate (and may inherit icon); otherwise belowGroup is set to this window’s winId. Passed parentNode is not forwarded into child-window creation.
Returns: Promise<string> resolving to the child options.name
Size and position
getSize()
Current window size.
Returns: Promise<{ width: number; height: number }>
setSize()
Sets width/height. If maximized, exits maximize first.
| Name | Type | Required | Description |
|---|---|---|---|
| width | number | Yes | Width |
| height | number | Yes | Height |
Returns: Promise<void>
getSizeInfo()
Full geometry.
Returns: Promise<SizeInfo>
interface SizeInfo {
x: number
y: number
width: number
height: number
maxHeight: number
maxWidth: number
minHeight: number
minWidth: number
radius: number
}setMinSize()
Minimum width/height.
| Name | Type | Required | Description |
|---|---|---|---|
| width | number | Yes | Min width |
| height | number | Yes | Min height |
Returns: Promise<void>
setMaximumSize()
Max dimensions; maximize respects these instead of full screen.
| Name | Type | Required | Description |
|---|---|---|---|
| width | number | Yes | Max width; 0 or negative = unlimited |
| height | number | Yes | Max height; 0 or negative = unlimited |
Returns: Promise<void>
getPosition()
Returns: Promise<{ x: number; y: number }>
setPosition()
| Name | Type | Required | Description |
|---|---|---|---|
| x | number | Yes | x |
| y | number | Yes | y |
Returns: Promise<void>
center()
Center in the work area.
Returns: Promise<void>
getRelativePos()
Maps pageX/pageY to system-relative coordinates.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| event | object | Yes | — | Object with pageX, pageY |
| offset | { x: number; y: number } | No | { x: 0, y: 0 } | Offset |
| reserve | boolean | No | false | Invert mode |
Returns: Promise<{ pageX: number; pageY: number }>
Window state
maximize()
Returns: Promise<void>
unmaximize()
Returns: Promise<void>
isMaximized()
Returns: Promise<boolean>
minimize()
Returns: Promise<void>
restore()
Bring to front and exit minimize (not the same as the title-bar restore toggle for maximize).
Returns: Promise<void>
isMinimized()
Returns: Promise<boolean>
show()
Sets show to true; fires show when becoming visible. If display is blocked in will-show, the promise resolves to true.
Returns: Promise<boolean | void>
hide()
Sets show to false. Only hideOnClose windows actually accept this hide request; after hiding, the window can be shown again with show().
Returns: Promise<void>
isVisible()
Checks whether the current window is visible.
Returns: Promise<boolean>
Window control
close()
Triggers before-close (cancellable) then close. If hideOnClose is enabled, this close may only hide the cached window instead of destroying it.
Returns: void
loadUrl()
| Name | Type | Required | Description |
|---|---|---|---|
| url | string | Yes | URL to load |
Returns: Promise<void>
damaged()
Marks the window as damaged so the next close is forced down the real-destroy path instead of reusing cache.
Returns: Promise<void>
Note:
- does not close the window immediately
- sets
hideOnClosetofalse - use when content is corrupted and must reload
cloudWindow.damaged()
cloudWindow.close()setMovable()
| Name | Type | Required | Description |
|---|---|---|---|
| state | boolean | Yes | Allow drag-move |
Returns: Promise<void>
setResizable()
| Name | Type | Required | Description |
|---|---|---|---|
| resizable | boolean | Yes | Allow resize |
Returns: Promise<void>
setMinimizable()
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| open | boolean | Yes | — | Allow minimize |
| showIcon | boolean | No | false | Still show minimize icon when not allowed |
Returns: Promise<void>
setMaximizable()
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| open | boolean | Yes | — | Allow maximize |
| showIcon | boolean | No | false | Still show maximize icon when not allowed |
Returns: Promise<void>
setClosable()
| Name | Type | Required | Description |
|---|---|---|---|
| closeable | boolean | Yes | Allow close |
Returns: Promise<void>
setAlwaysOnTop()
| Name | Type | Required | Description |
|---|---|---|---|
| val | boolean | Yes | Always on top |
Returns: Promise<void>
isAlwaysOnTop()
Returns: Promise<boolean>
focus()
Returns: Promise<void>
blur()
Returns: Promise<void>
isFocus()
Returns: Promise<boolean>
Appearance
getTitle()
Returns: Promise<string>
setTitle()
Sets title bar (and taskbar title). Not available on desktop shell in some builds.
| Name | Type | Required | Description |
|---|---|---|---|
| title | string | Yes | Title |
Returns: Promise<void>
getIcon()
Returns: Promise<string | null> — icon from create options
setIcon()
Sets the window icon.
| Name | Type | Required | Description |
|---|---|---|---|
| icon | string | Yes | Icon URL |
Returns: Promise<void>
setBackgroundColor()
| Name | Type | Required | Description |
|---|---|---|---|
| color | string | Yes | CSS color; transparent for transparent |
Returns: Promise<void>
setShadowColor()
| Name | Type | Required | Description |
|---|---|---|---|
| color | string | boolean | Yes | Shadow CSS; true default; false none |
Returns: Promise<void>
setRadius()
| Name | Type | Required | Description |
|---|---|---|---|
| radius | number | Yes | Corner radius (px) |
Returns: Promise<void>
setStyle()
Custom styles on the window element; numeric values get px.
| Name | Type | Required | Description |
|---|---|---|---|
| obj | Record<string, string | number> | Yes | Style map |
Returns: Promise<void>
Blocked properties (layout/shell protection): width, height, left, top, bottom, right, position, padding, margin, z-index, background, background-image, box-shadow, border-radius, display
await cloudWindow.setStyle({
border: '2px solid #409eff',
borderStyle: 'dashed'
})
await cloudWindow.setStyle({
opacity: 0.95,
transition: 'all 0.3s ease'
})registerHeader()
Registers a custom drag/title region; forces frame = false; unregistering does not restore frame.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| handle | HTMLElement | Element | Yes | — | Drag handle element |
| focus | boolean | No | false | Replace previous handle |
Returns: Promise<Function | 0> — usually a cleanup function; 0 in legacy-compat paths
setActionPosition()
Custom control-button position; forces frame = false.
| Name | Type | Required | Description |
|---|---|---|---|
| y | number | Yes | Button Y |
| x | number | No | Button X (Mac only) |
Returns: Promise<number> — 1 on success
setActionVisible()
Show/hide window chrome buttons.
| Name | Type | Required | Description |
|---|---|---|---|
| actionRange | Array<'mini' | 'close' | 'restore' | 'help'> | Yes | Which buttons |
| state | boolean | Yes | true show |
Returns: Promise<void>
await cloudWindow.setActionVisible(['mini', 'help'], false)
await cloudWindow.setActionVisible(['mini', 'close', 'restore', 'help'], true)setHelpInfo()
Update the help button; undefined, null, or an empty string can hide it.
| Name | Type | Required | Description |
|---|---|---|---|
| info | string | undefined | null | Yes | Help URL or text |
Returns: Promise<void>
setSkipTaskbar()
Controls whether the window participates in taskbar aggregation.
| Name | Type | Required | Description |
|---|---|---|---|
| skip | boolean | Yes | Whether to hide this window from the taskbar |
Returns: Promise<void>
Messaging
listenWindowData()
Listen for host-injected data, mainly data from create, data passed when re-activating an existing window, and payloads sent from host-side sendDataTo().
| Name | Type | Required | Description |
|---|---|---|---|
| callback | (message: any, from: string) => void | Yes | Handler |
Returns: disposer function
Note: This is not the receive channel for send() / sendTo().
send()
Send data to the desktop shell.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| data | any | Yes | — | Payload |
| event | string | No | '' | Custom event name; must not clash with built-ins |
Returns: Promise<void>
sendEvent()
Send an event to the shell.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| event | string | Yes | — | Custom event name |
| data | any | No | undefined | Payload |
Returns: Promise<void>
sendTo()
Send to a window; target listens with on('message', ...).
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| winId | string | Yes | — | Target id |
| data | any | No | undefined | Payload |
| event | string | No | '' | Custom event name |
Returns: Promise<void>
Note: When event is empty, the target receives it through the message event; when event is not empty, the target receives it through the custom event with the same name.
sendEventTo()
Send event to a window; target uses on(event, ...).
| Name | Type | Required | Description |
|---|---|---|---|
| winId | string | Yes | Target |
| event | string | Yes | Custom event name |
| data | any | No | Payload |
Returns: Promise<void>
sendEventToGroup()
Broadcast to windows sharing config.group. Use carefully.
| Name | Type | Required | Description |
|---|---|---|---|
| group | string | string[] | Yes | Group name(s) |
| event | string | Yes | Custom event name |
| data | any | No | Payload |
Returns: Promise<number> — windows actually notified
Note: The current implementation also counts cached hideOnClose windows that have not yet been destroyed.
sendEventToMyGroup()
Same as group broadcast but for the current window’s group (excludes self count semantics per implementation).
| Name | Type | Required | Description |
|---|---|---|---|
| event | string | Yes | Custom event name |
| data | any | No | Payload |
Returns: Promise<number> — number of other windows in the group (same as sendEventTo dispatch count)
Note: Counting is based on the host-side group member registry, so cached windows may also be included.
useCapacity()
Desktop open capabilities — see Desktop open capabilities.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| capName | string | Yes | — | Capability name |
| data | any | No | — | Arguments |
| timeout | number | No | 0 | ms; 0 = no timeout |
Returns: Promise<any>
Window groups
getGroup()
Returns: Promise<string> — '' if none
getGroupList()
Peers in the same group excluding this window.
Returns: Promise<string[]>
Note: Cached hideOnClose windows that have not been destroyed still appear in this list.
getGroupPosition()
Index in the full member list (includes self); unlike getGroupList().
Returns: Promise<number> — -1 if N/A
Note: This full member list includes the current window itself, and may also include cached windows that have not yet been destroyed.
queryIsExist()
Whether a named window exists in a group.
| Name | Type | Required | Description |
|---|---|---|---|
| group | string | Yes | Group |
| winName | string | Yes | Window name |
Returns: Promise<boolean>
Note: The check is based on the host-side group member registry, and cached hideOnClose windows that have not yet been destroyed are also treated as “existing”.
const exists = await cloudWindow.queryIsExist('main-group', 'settings-window')
if (exists) {
console.log('Settings window already open')
} else {
// ...
}