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

Sent via send / sendTo; the target listens with cloudWindow.listenWindowData. 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

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 and parent are set to this window’s winId.

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.

Returns: Promise<void>

hide()

Sets show to false; window stays in memory.

Returns: Promise<void>

Window control

close()

Triggers before-close (cancellable) then close.

Returns: void

loadUrl()

NameTypeRequiredDescription
urlstringYesURL to load

Returns: Promise<void>

damaged()

Marks the window broken and clears cache-related state.

Returns: Promise<void>

Note: sets cacheTime to 0 and 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

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 help button; undefined hides it.

NameTypeRequiredDescription
infostring | undefinedYesHelp URL or text

Returns: Promise<void>

Messaging

listenWindowData()

Listen for create data and send payloads.

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

Returns: disposer function

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>

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

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)

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[]>

getGroupPosition()

Index in the full member list (includes self); unlike getGroupList().

Returns: Promise<number>-1 if N/A

queryIsExist()

Whether a named window exists in a group.

NameTypeRequiredDescription
groupstringYesGroup
winNamestringYesWindow name

Returns: Promise<boolean>

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

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