Skip to content

Window management

Overview

cloudWindow manages the current window: state, APIs for control, and messaging.

Import

ts
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

ts
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:

NameTypeDescription
urlstringLoaded URL

load-error

Fires when loading fails.

NameTypeDescription
urlstringURL 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.

NameTypeDescription
xnumberWindow x
ynumberWindow y
widthnumberWidth
heightnumberHeight

moving

Fires continuously while the window is moving.

NameTypeDescription
xnumberWindow x
ynumberWindow y
widthnumberWidth
heightnumberHeight

resizing

Fires continuously while resizing (drag).

NameTypeDescription
xnumberWindow x
ynumberWindow y
widthnumberWidth
heightnumberHeight

resize

Fires when resize completes.

NameTypeDescription
xnumberWindow x
ynumberWindow y
widthnumberWidth
heightnumberHeight

maximize

Fires when maximized.

NameTypeDescription
xnumberWindow x
ynumberWindow y
widthnumberWidth
heightnumberHeight

un-maximize

Fires when leaving maximized state.

NameTypeDescription
xnumberWindow x
ynumberWindow y
widthnumberWidth
heightnumberHeight

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.

NameTypeDescription
data{ fake: boolean }fake === true means hide/keep-alive; false means real close/destroy

before-close

Fires before close; call preventDefault() to cancel.

NameTypeDescription
eEventNative event; e.preventDefault() blocks close

hostChange

Fires when the requested BaseUrl changes (e.g. device address).

NameTypeDescription
dataanyChange details (e.g. new URL)

always-on-top-changed

Fires when always-on-top changes.

NameTypeDescription
valuebooleanCurrent always-on-top state

mouse-enter

Fires when the pointer enters the window.

NameTypeDescription
xnumberWindow x
ynumberWindow y
widthnumberWidth
heightnumberHeight
topbooleanWhether window is top-most

mouse-leave

Fires when the pointer leaves the window.

NameTypeDescription
xnumberWindow x
ynumberWindow y
widthnumberWidth
heightnumberHeight
topbooleanWhether window is top-most

before-help

Fires when the help button is clicked; preventDefault can block default behavior.

NameTypeDescription
eBeforeHelpEventCustomEvent; e.detail.message is help URL, e.detail.version may be null
urlstring | undefinedHelp 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).

NameTypeDescription
eEventpreventDefault() can block this show; window stays hidden

Additional notes:

  • before-show mainly corresponds to the host re-activating a cached hideOnClose window
  • will-show mainly corresponds to explicit cloudWindow.show() calls or host-side show()

message

Fires when another window uses sendTo or the desktop uses send.

NameTypeDescription
dataanyPayload
fromstringSender window id or source

wsData

In-window event from desktop WebSocket downlink.

NameTypeDescription
dataobjectWebSocket packet

Example backend payload shape:

json
{
  "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 → dragLeave

Cross-window:

Window A: dragEnter → dragOver → dragLeave
Window B: dragEnter → dragOver → drop/dragEnd → dragLeave

dragEnter

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:

NameTypeDescription
dataDragData | any | undefinedDrag payload

Sources

  1. Browser DataTransfer — if no custom payload, a standard DragData object
  2. Empty drag — neither custom data nor dataTransfer.typesundefined

DragData shape:

ts
interface DragData {
  files?: File[]
  items?: DataTransferItemList
  types?: string[]
  effectAllowed?: string
  dropEffect?: string
  text?: string
  [key: string]: any
}

Notes:

  • files: files only, not folder contents
  • items: lower-level API; webkitGetAsEntry() can recurse folders

Example:

ts
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.

NameTypeDescription
dataDragData | any | undefinedSame as dragEnter

dragEnd

Fires when the drag is released (drop) in the window.

NameTypeDescription
dataDragData | any | undefinedSame 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:

ts
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.

ts
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):

ts
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):

vue
<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 drag
  • DragData — standard browser payload
  • Any custom object — from internal drag plumbing if present

Suggested handling:

ts
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.

NameTypeRequiredDescription
widthnumberYesWidth
heightnumberYesHeight

Returns: Promise<void>

getSizeInfo()

Full geometry.

Returns: Promise<SizeInfo>

ts
interface SizeInfo {
  x: number
  y: number
  width: number
  height: number
  maxHeight: number
  maxWidth: number
  minHeight: number
  minWidth: number
  radius: number
}

setMinSize()

Minimum width/height.

NameTypeRequiredDescription
widthnumberYesMin width
heightnumberYesMin height

Returns: Promise<void>

setMaximumSize()

Max dimensions; maximize respects these instead of full screen.

NameTypeRequiredDescription
widthnumberYesMax width; 0 or negative = unlimited
heightnumberYesMax height; 0 or negative = unlimited

Returns: Promise<void>

getPosition()

Returns: Promise<{ x: number; y: number }>

setPosition()

NameTypeRequiredDescription
xnumberYesx
ynumberYesy

Returns: Promise<void>

center()

Center in the work area.

Returns: Promise<void>

getRelativePos()

Maps pageX/pageY to system-relative coordinates.

NameTypeRequiredDefaultDescription
eventobjectYesObject with pageX, pageY
offset{ x: number; y: number }No{ x: 0, y: 0 }Offset
reservebooleanNofalseInvert 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()

NameTypeRequiredDescription
urlstringYesURL 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 hideOnClose to false
  • use when content is corrupted and must reload
ts
cloudWindow.damaged()
cloudWindow.close()

setMovable()

NameTypeRequiredDescription
statebooleanYesAllow drag-move

Returns: Promise<void>

setResizable()

NameTypeRequiredDescription
resizablebooleanYesAllow resize

Returns: Promise<void>

setMinimizable()

NameTypeRequiredDefaultDescription
openbooleanYesAllow minimize
showIconbooleanNofalseStill show minimize icon when not allowed

Returns: Promise<void>

setMaximizable()

NameTypeRequiredDefaultDescription
openbooleanYesAllow maximize
showIconbooleanNofalseStill show maximize icon when not allowed

Returns: Promise<void>

setClosable()

NameTypeRequiredDescription
closeablebooleanYesAllow close

Returns: Promise<void>

setAlwaysOnTop()

NameTypeRequiredDescription
valbooleanYesAlways 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.

NameTypeRequiredDescription
titlestringYesTitle

Returns: Promise<void>

getIcon()

Returns: Promise<string | null> — icon from create options

setIcon()

Sets the window icon.

NameTypeRequiredDescription
iconstringYesIcon URL

Returns: Promise<void>

setBackgroundColor()

NameTypeRequiredDescription
colorstringYesCSS color; transparent for transparent

Returns: Promise<void>

setShadowColor()

NameTypeRequiredDescription
colorstring | booleanYesShadow CSS; true default; false none

Returns: Promise<void>

setRadius()

NameTypeRequiredDescription
radiusnumberYesCorner radius (px)

Returns: Promise<void>

setStyle()

Custom styles on the window element; numeric values get px.

NameTypeRequiredDescription
objRecord<string, string | number>YesStyle 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

ts
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.

NameTypeRequiredDefaultDescription
handleHTMLElement | ElementYesDrag handle element
focusbooleanNofalseReplace previous handle

Returns: Promise<Function | 0> — usually a cleanup function; 0 in legacy-compat paths

setActionPosition()

Custom control-button position; forces frame = false.

NameTypeRequiredDescription
ynumberYesButton Y
xnumberNoButton X (Mac only)

Returns: Promise<number>1 on success

setActionVisible()

Show/hide window chrome buttons.

NameTypeRequiredDescription
actionRangeArray<'mini' | 'close' | 'restore' | 'help'>YesWhich buttons
statebooleanYestrue show

Returns: Promise<void>

ts
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.

NameTypeRequiredDescription
infostring | undefined | nullYesHelp URL or text

Returns: Promise<void>

setSkipTaskbar()

Controls whether the window participates in taskbar aggregation.

NameTypeRequiredDescription
skipbooleanYesWhether 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().

NameTypeRequiredDescription
callback(message: any, from: string) => voidYesHandler

Returns: disposer function

Note: This is not the receive channel for send() / sendTo().

send()

Send data to the desktop shell.

NameTypeRequiredDefaultDescription
dataanyYesPayload
eventstringNo''Custom event name; must not clash with built-ins

Returns: Promise<void>

sendEvent()

Send an event to the shell.

NameTypeRequiredDefaultDescription
eventstringYesCustom event name
dataanyNoundefinedPayload

Returns: Promise<void>

sendTo()

Send to a window; target listens with on('message', ...).

NameTypeRequiredDefaultDescription
winIdstringYesTarget id
dataanyNoundefinedPayload
eventstringNo''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, ...).

NameTypeRequiredDescription
winIdstringYesTarget
eventstringYesCustom event name
dataanyNoPayload

Returns: Promise<void>

sendEventToGroup()

Broadcast to windows sharing config.group. Use carefully.

NameTypeRequiredDescription
groupstring | string[]YesGroup name(s)
eventstringYesCustom event name
dataanyNoPayload

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).

NameTypeRequiredDescription
eventstringYesCustom event name
dataanyNoPayload

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.

NameTypeRequiredDefaultDescription
capNamestringYesCapability name
dataanyNoArguments
timeoutnumberNo0ms; 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.

NameTypeRequiredDescription
groupstringYesGroup
winNamestringYesWindow 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”.

ts
const exists = await cloudWindow.queryIsExist('main-group', 'settings-window')

if (exists) {
  console.log('Settings window already open')
} else {
  // ...
}