Saltar al contenido

Crear paneles

Referencia del SDK

Cada método de window.deska: sus argumentos, el permiso que necesita, qué responde, cómo falla y sus límites. El almacenamiento y los archivos, el portapapeles, los avisos y los enlaces, las páginas de otros sitios, además del payload de inicio, las propiedades en vivo, los eventos y los mensajes por debajo.

Cargar el SDK

Deska sirve el SDK dentro de cada panel en /__deska/sdk.js. No es un archivo de tu carpeta, y siempre coincide con la versión de Deska que ejecuta el panel, así que no hay nada que instalar ni actualizar. Cárgalo como script clásico, antes de cualquier script que use window.deska:

index.htmlhtml
<head>
  <meta charset="utf-8">
  <link rel="stylesheet" href="app.css">
  <!-- Un script clásico, antes de todo lo que use window.deska -->
  <script src="/__deska/sdk.js"></script>
  <script type="module" src="app.js"></script>
</head>
  • Un script type="module" corre después de los clásicos, así que un módulo puede usar deska de inmediato.
  • El SDK avisa que está listo apenas carga, y lo repite cada cuarto de segundo hasta que Deska responde, así que no importa qué lado empieza primero.
  • Abierta fuera de Deska (directo en un navegador), la página igual recibe un deska, pero ready() nunca se resuelve y las llamadas nunca responden. Usa la herramienta de vista previa para correr un panel fuera de Deska; consulta Pruebas y depuración.

El SDK es un contrato público que solo crece: un método que existe en la versión 1 sigue funcionando igual. sdk en panel.json dice para qué versión escribiste.

la etiquetahtml
<script src="/__deska/sdk.js"></script>

deska.ready() y el payload de inicio

app.jsjs
const init = await deska.ready()

document.documentElement.lang = init.locale
if (init.place === 'extras') document.body.classList.add('compact')
render(await deska.storage.get('state'))

deska.ready() responde en cuanto Deska conoce la página, con todo lo que el panel necesita para dibujar su primer cuadro. Es la misma promesa cada vez que la llamas. Las llamadas que hagas antes de que se resuelva esperan y se envían en ese momento.

initjs
{
  panel: { id: 'pomodoro-plus', name: 'Pomodoro Plus', version: '1.2.0', dev: false },
  place: 'panel',
  theme: { mode: 'dark', tokens: { '--deska-bg': 'rgb(r, g, b)', /* and 15 more */ } },
  visibility: { visible: true, idle: false },
  permissions: ['notify'],
  locale: 'en-US'
}
CampoTipoSignificado
panel.idstringEl id del paquete. Una carpeta cargada recibe dev- y doce caracteres hexadecimales; una página en localhost, dev: y su dirección.
panel.namestringEl nombre del paquete según panel.json. Pásalo a setTitle para mostrar solo el nombre.
panel.versionstring | nullLa versión instalada, null mientras construyes.
panel.devbooleanVerdadero para una carpeta cargada o una página en localhost.
place'panel' | 'extras'Dónde se dibuja esta página: en el escritorio, o dentro del popover Extras.
theme{ mode, tokens }mode es dark o light; tokens asocia cada nombre --deska-* a un valor resuelto. Consulta Tokens del tema.
visibility{ visible, idle }Consulta Propiedades.
permissionsstring[]Lo que este panel puede hacer en esta computadora: nunca más de lo que pide panel.json, y solo lo que la persona permitió.
localestringEl idioma y la región de la persona, como es-MX, para Intl y tus propios textos.
ConsejoUn panel puede recargarse en cualquier momento: al moverlo entre el lienzo y un dock, al dividir o reordenar pestañas, al separar una ventana, al plegar un dock, al cambiar de proyecto, con una actualización o un reinicio. Guarda lo importante en deska.storage, las imágenes y otros archivos en deska.files, y reconstruye a partir de init, nunca de la memoria.

Propiedades

Campos en vivo de deska, que ready() llena y que se mantienen al día después. Léelos cuando quieras; escucha los eventos de abajo para saber cuándo cambian.

PropiedadTipoSignificado
deska.theme{ mode, tokens } | nullEl último tema que envió Deska; null antes de ready().
deska.visiblebooleanVerdadero mientras el panel está en pantalla: su proyecto y su worktree son los que se muestran, está a la vista y la vista lejana no lo tapa con una tarjeta.
deska.idlebooleanVerdadero mientras la ventana de Deska no tiene el foco: la persona está en otra app.
deska.place'panel' | 'extras' | nullDónde se dibuja esta página; null antes de ready().

Un panel oculto sigue corriendo. visible: false es tu señal para detener temporizadores, animaciones y consultas periódicas, y ponerte al día cuando vuelva a verse; el audio puede seguir sonando.

Cómo responden las llamadas

Cada método devuelve una promesa. Se resuelve con la respuesta, y se rechaza con un Error cuyo code (y message) es uno de diez códigos. Responde a los que la persona puede resolver y registra el resto:

app.jsjs
try {
  await deska.clipboard.write(text)
  say('Copiado')
} catch (err) {
  // err es un Error cuyo code dice por qué
  if (err.code === 'not_focused') say('Haz clic en el panel y vuelve a copiar')
  else if (err.code === 'too_large') say('Es demasiado largo para copiar')
  else console.warn('copy failed:', err.code)
}
  • Los permisos se revisan primero: una llamada sin su permiso se rechaza con permission_denied, sean cuales sean sus argumentos.
  • Los argumentos deben ser datos simples. Un valor que postMessage no puede copiar (una función, un nodo del DOM) se rechaza con invalid_args sin salir de la página. Los métodos de archivos son la excepción: files.put, files.save y clipboard.writeImage reciben un Blob, que se mide con el límite de 32 MB por archivo en lugar del de 64 KB.
  • Cada llamada cuenta para el límite del marco de 20 por segundo. Consulta Códigos de error y límites para ver cada código y cada límite.

deska.storage

Donde perduran los datos de un panel. La página corre con un origen opaco, así que localStorage, indexedDB y las cookies fallan u olvidan. Los valores son JSON, guardados en esta computadora, por paquete: cada copia del panel, en cualquier escritorio y en Extras, lee y escribe las mismas claves. Para imágenes y cualquier cosa grande, usa deska.files y guarda aquí solo el id del archivo.

app.jsjs
const saved = await deska.storage.get('places')   // null la primera vez
const places = saved ?? ['Europe/Berlin']

places.push('Asia/Tokyo')
try {
  await deska.storage.set('places', places)
} catch (err) {
  if (err.code === 'storage_full') showFullNotice()
}

await deska.storage.keys()          // ['places']
await deska.storage.remove('places')

deska.storage.get(key)

ArgumentoTipoNotas
keystringDe 1 a 256 caracteres.
Permiso
Ninguno
Devuelve
El valor guardado, o null si la clave no existe.
ErrorCuándo
invalid_argsLa clave no es un texto, está vacía o tiene más de 256 caracteres.

deska.storage.set(key, value)

Si dos copias del panel escriben la misma clave, queda la última escritura: el almacenamiento no tiene bloqueos. Lee, cambia y escribe en un solo paso, y vuelve a leer cuando una copia se muestra otra vez (Habit Streaks hace las dos cosas).

ArgumentoTipoNotas
keystringDe 1 a 256 caracteres.
valueany JSON valueSe guarda como JSON. Se rechaza undefined, una función, un BigInt o un valor que se contiene a sí mismo; NaN e Infinity se vuelven null, como en JSON.stringify.
Permiso
Ninguno
Devuelve
true en cuanto el valor está en el almacenamiento del panel. Llega al disco en menos de medio segundo, y al cerrar la página.
Límites
  • 256 KB por paquete: el tamaño de todas sus claves y valores como JSON, en UTF-8.
  • 2 MB para todos los paneles de la comunidad de la computadora juntos.
  • Una escritura que no hace crecer los datos siempre pasa, así que un paquete por encima del límite puede volver a quedar por debajo.
  • Una llamada lleva como máximo 64 KB, así que un valor más grande se guarda en partes, con varias claves.
ErrorCuándo
invalid_argsUna clave inválida, o un valor sin forma JSON.
storage_fullLa escritura llevaría al paquete por encima de 256 KB, o a todos los paquetes de esta computadora juntos por encima de 2 MB.
too_largeLa llamada lleva más de 64 KB, o anida más de 32 niveles.

deska.storage.remove(key)

ArgumentoTipoNotas
keystringDe 1 a 256 caracteres.
Permiso
Ninguno
Devuelve
true, también cuando la clave no existía.
ErrorCuándo
invalid_argsUna clave inválida.

deska.storage.keys()

Permiso
Ninguno
Devuelve
Todas las claves del paquete, como un arreglo de textos.

El almacenamiento pertenece a la computadora y al paquete. No sigue a la cuenta a otra computadora, y quitar el panel de Deska lo borra. Una carpeta cargada tiene su propio almacenamiento, separado de la versión instalada del mismo panel.

deska.files

Archivos que el panel guarda en esta computadora: una imagen que alguien pegó, un dibujo que hizo, una exportación. Como el almacenamiento, los archivos pertenecen al paquete: cada copia del panel, en cualquier escritorio y en Extras, ve los mismos. Guarda el id de cada archivo en deska.storage para volver a encontrarlo. No necesita ningún permiso.

app.jsjs
// Guarda una imagen, y en el almacenamiento solo su id
try {
  const kept = await deska.files.put(file)   // { id, type, size, createdAt }
  const ids = (await deska.storage.get('shots')) ?? []
  await deska.storage.set('shots', [...ids, kept.id])
} catch (err) {
  if (err.code === 'too_large') say('Elige una imagen de menos de 32 MB')
  if (err.code === 'storage_full') say('Borra algunas imágenes para hacer espacio')
}

// Vuelve a mostrarla después de una recarga
const blob = await deska.files.get(id)       // null si ya se borró
if (blob) img.src = URL.createObjectURL(blob)

// Guarda una copia donde la persona elija, desde el clic
saveButton.addEventListener('click', async () => {
  const saved = await deska.files.save(blob, 'screenshot.png')
  if (saved) say('Guardado')                   // false: canceló
})

deska.files.put(file)

ArgumentoTipoNotas
fileBlob | ArrayBuffer | typed arrayHasta 32 MB. Un File de un selector, de pegar o de soltar es un Blob. Los bytes se envuelven en uno.
typestringSolo con bytes: el tipo MIME, como image/png. Un Blob trae el suyo.
Permiso
Ninguno
Devuelve
{ id, type, size, createdAt }: el id que debes guardar, el tipo MIME (el del archivo cuando se lee como uno, si no application/octet-stream), el tamaño en bytes y cuándo se guardó, en milisegundos desde 1970.
Límites
  • 32 MB por archivo.
  • 256 MB y 2,000 archivos por paquete.
  • 1 GB para todos los paneles de la comunidad de la computadora juntos.
ErrorCuándo
invalid_argsEl archivo no es un Blob ni bytes.
too_largeEl archivo pasa de 32 MB.
storage_fullEl archivo llevaría al paquete por encima de 256 MB o de 2,000 archivos, o a todos los paquetes de esta computadora juntos por encima de 1 GB.

deska.files.get(id)

Muestra una imagen con URL.createObjectURL(blob); la política del panel acepta imágenes y medios blob:. Llama a URL.revokeObjectURL cuando la imagen ya no esté en la página.

ArgumentoTipoNotas
idstringUn id de put o de list.
Permiso
Ninguno
Devuelve
El archivo como un Blob de su tipo, o null si ningún archivo tiene ese id.
ErrorCuándo
invalid_argsEl id no es uno que Deska pudiera haber dado.

deska.files.list()

Permiso
Ninguno
Devuelve
Todos los archivos que guarda el paquete, como un arreglo de { id, type, size, createdAt }.

deska.files.remove(id)

ArgumentoTipoNotas
idstringUn id de put o de list.
Permiso
Ninguno
Devuelve
true, también cuando ningún archivo tenía ese id.
ErrorCuándo
invalid_argsEl id no es uno que Deska pudiera haber dado.

deska.files.save(file, name)

Deska muestra el diálogo de guardar del sistema, con el nombre del panel como título, y la persona elige la carpeta y el nombre. El diálogo es su consentimiento, así que no hace falta ningún permiso, y la página nunca sabe dónde quedó el archivo. Es la forma en que un panel guarda un archivo: las descargas, incluido <a download>, se rechazan en el sandbox.

ArgumentoTipoNotas
fileBlobHasta 32 MB. Su tipo decide la extensión.
namestringOpcional, hasta 120 caracteres: el nombre que se sugiere. Deska lo vuelve seguro para el sistema y le agrega la extensión que pide el tipo.
Permiso
Ninguno
Devuelve
true en cuanto el archivo se escribe, false si la persona canceló.
Límites
  • Solo mientras el panel tiene el foco.
  • Un guardado a la vez.
ErrorCuándo
invalid_argsEl archivo no es un Blob, o el nombre no es un texto o pasa de 120 caracteres.
too_largeEl archivo pasa de 32 MB.
not_focusedEl panel no tiene el foco.
declinedYa hay abierto un guardado de este panel u otra pregunta de Deska.
host_errorNo se pudo escribir el archivo.

Los archivos pertenecen a la computadora y al paquete, como el almacenamiento. No siguen a la cuenta a otra computadora, una actualización los conserva, y quitar el panel de Deska los borra. Una carpeta cargada tiene sus propios archivos, separados de la versión instalada del mismo panel.

Recibir imágenes

Traer una imagen a un panel no necesita ningún método: la página es una página web. Un selector de archivos, pegar y soltar le entregan objetos File, que deska.files.put recibe tal cual.

app.jsjs
// Un selector: <input type="file" accept="image/*" multiple>
picker.addEventListener('change', () => keep(picker.files))

// Pegar mientras el panel tiene el foco
document.addEventListener('paste', (event) => keep(event.clipboardData.files))

// Soltar sobre el panel
document.addEventListener('dragover', (event) => event.preventDefault())
document.addEventListener('drop', (event) => {
  event.preventDefault()
  keep(event.dataTransfer.files)
})

async function keep(list) {
  for (const file of list) {
    if (file.type.startsWith('image/')) await deska.files.put(file)
  }
}
  • Un selector: <input type="file"> abre el diálogo de archivos del sistema.
  • Pegar: el propio evento paste de la página trae lo que pegó la persona en clipboardData.files.
  • Soltar: llama a preventDefault() en dragover y luego lee dataTransfer.files en drop.

Todo esto empieza con la persona. Leer el portapapeles por su cuenta (navigator.clipboard.read) sigue rechazado, así que ningún panel ve lo que se copió hasta que alguien lo pega ahí.

deska.setTitle

deska.setTitle(title)

Una línea corta bajo el nombre del panel en el lienzo, mostrada como texto no confiable después del nombre del paquete: Pomodoro Plus: Focus 12:31. No hay forma de borrarlo; pon el nombre del propio panel, init.panel.name, para mostrar solo el nombre.

En todos los demás lugares (Dex, la Paleta de comandos, el índice de paneles, un teléfono u otra computadora que mira el escritorio) el panel se llama como su paquete, o como lo nombró la persona, nunca por su título.

ArgumentoTipoNotas
titlestringHasta 60 caracteres, una vez quitados los caracteres invisibles y juntados los espacios seguidos.
Permiso
Ninguno
Devuelve
true
Límites
  • Se escribe como máximo cuatro veces por segundo; gana el último título pedido.
  • Solo se muestra en el lienzo, no en una ventana propia de panel o de dock.
ErrorCuándo
invalid_argsnull, un texto vacío, solo espacios, algo que no es texto, o más de 60 caracteres. Deska nunca recorta un título.

deska.notify

app.jsjs
await deska.notify({ title: 'Break time', body: 'Twenty five minutes done.' })
// La campana muestra "Pomodoro Plus: Break time"

deska.notify({ title, body })

Un aviso en la campana de Deska, con el nombre del panel para que nunca se confunda con uno de Deska. Desde una ventana propia de panel o de dock llega a la campana de la ventana principal. Funciona tenga o no el foco el panel: es la forma en que un panel pide atención.

ArgumentoTipoNotas
titlestringObligatorio, hasta 80 caracteres una vez limpio.
bodystringOpcional, hasta 240 caracteres. Omítelo o pasa null para no mostrarlo.
Permiso
notify
Devuelve
true
Límites
  • Un aviso cada 10 segundos por panel. Junta varios cambios en un solo aviso.
ErrorCuándo
permission_deniedpanel.json no pide notify, o la persona no lo permitió.
invalid_argsEl argumento no es un objeto, el título falta, está vacío o pasa de 80, o el cuerpo no es texto o pasa de 240.
rate_limitedEl panel envió un aviso hace menos de diez segundos.

deska.openUrl

app.jsjs
link.addEventListener('click', async (event) => {
  event.preventDefault()
  try {
    await deska.openUrl(link.href)
  } catch (err) {
    if (err.code === 'not_focused') return     // el clic no llegó al panel
    if (err.code === 'declined') return        // la persona dijo que no; no vuelvas a preguntar
  }
})

deska.openUrl(url)

Abre el enlace en el navegador de la persona. La primera vez que el panel abre un host, Deska pregunta (“¿Abrir example.com?”), con el nombre del panel y el enlace; su botón Abrir enlace no acepta clics durante medio segundo, para que un botón bajo el cursor no pueda responder por la persona. Después de un Abrir enlace, los enlaces a ese host se abren sin preguntar. Después de un Cancelar, Deska no pregunta nada más hasta que la página vuelve a empezar.

El enlace no tiene que estar en network: esa lista es a lo que el panel se conecta, no lo que puede mostrarle a la persona.

ArgumentoTipoNotas
urlstringUna dirección https completa, de hasta 2048 caracteres, sin usuario ni contraseña antes del host.
Permiso
open-url
Devuelve
true en cuanto el enlace pasa al navegador.
Límites
  • Un enlace por segundo.
  • Solo mientras el panel tiene el foco.
ErrorCuándo
permission_deniedpanel.json no pide open-url, o la persona no lo permitió.
invalid_argsNo es https, no es una dirección completa, es demasiado larga, o lleva usuario o contraseña.
not_focusedEl panel no tiene el foco: la llamada vino de un temporizador, o la persona ya se fue a otra cosa.
rate_limitedSe abrió un enlace hace menos de un segundo.
declinedLa persona pulsó Cancelar, había otra pregunta de Deska abierta, o esta página ya recibió un no.

deska.clipboard

deska.clipboard.write(text)

Pone texto en el portapapeles. Es la única forma en que un panel copia: navigator.clipboard se rechaza en el sandbox, y sin este permiso el document.execCommand('copy') de la página no hace nada. Copiar una selección con el teclado funciona en todos los paneles, como en cualquier página. Ningún panel puede leer el portapapeles.

ArgumentoTipoNotas
textstringHasta 64 KB en UTF-8.
Permiso
clipboard-write
Devuelve
true
Límites
  • Solo mientras el panel tiene el foco, así que llámalo desde el mismo clic o tecla.
ErrorCuándo
permission_deniedpanel.json no pide clipboard-write, o la persona no lo permitió.
invalid_argsEl texto no es un texto.
too_largeEl texto pasa de 64 KB.
not_focusedEl panel no tiene el foco.
host_errorEl portapapeles del sistema rechazó la escritura.
app.jsjs
copyButton.addEventListener('click', async () => {
  const blob = await deska.files.get(id)       // un PNG o JPEG
  try {
    await deska.clipboard.writeImage(blob)
    say('Imagen copiada')
  } catch (err) {
    if (err.code === 'invalid_args') say('Solo se pueden copiar imágenes PNG y JPEG')
    else if (err.code === 'not_focused') say('Haz clic en el panel y vuelve a copiar')
  }
})

deska.clipboard.writeImage(image)

Pone una imagen en el portapapeles, lista para pegarla en cualquier app. Necesita el mismo permiso que copiar texto. Dibuja en un canvas y copia lo que te da canvas.toBlob(), o copia un archivo que guarda el panel.

ArgumentoTipoNotas
imageBlob | ArrayBuffer | typed arrayUn PNG o JPEG, de hasta 32 MB. Los bytes se envuelven como image/png.
Permiso
clipboard-write
Devuelve
true
Límites
  • Solo mientras el panel tiene el foco.
  • Solo PNG y JPEG.
ErrorCuándo
permission_deniedpanel.json no pide clipboard-write, o la persona no lo permitió.
invalid_argsNo es un Blob, no es un PNG ni un JPEG, o es una imagen que no se puede decodificar.
too_largeLa imagen pasa de 32 MB.
not_focusedEl panel no tiene el foco.

deska.extras.set

app.jsjs
await deska.extras.set({
  state: 'Focus, 12 min left',
  progress: 0.52,
  until: Date.now() + 12 * 60_000,
  control: 'pause',
})

await deska.extras.set({ progress: null, until: null })   // borra dos campos, conserva el resto

deska.extras.set({ state, progress, until, control })

Llena la fila del paquete en Extras. Lo que pones se queda después de que se cierra cada marco del paquete. Consulta Extras para ver ese lugar completo.

ArgumentoTipoNotas
statestring | nullLa línea de estado de la fila, hasta 60 caracteres una vez limpia.
progressnumber | nullDe 0 a 1 dibuja un anillo alrededor del ícono.
untilnumber | nullUna hora en milisegundos desde 1970. Deska muestra el tiempo que falta y sigue contando sin ningún marco vivo.
control'play' | 'pause' | 'start' | 'stop' | nullEl botón redondo de la fila. Al pulsarlo se envía extras.control.
Permiso
Ninguno, pero panel.json debe declarar contributes.extras
Devuelve
true
Límites
  • Los campos que omites conservan su valor; null borra uno.
ErrorCuándo
permission_deniedpanel.json no tiene contributes.extras.
invalid_argsNo es un objeto, trae una clave distinta de las cuatro, un estado de más de 60, un progreso fuera de 0 a 1, una hora que no es una fecha válida, o un control desconocido.

deska.embed

Muestra la página de otro sitio dentro del panel, como un reproductor de video, un mapa o un tablero público. El panel nunca la enmarca por su cuenta: Deska dibuja un marco propio sobre el panel, donde la página lo pide, sin sesión iniciada y separado del panel. El host de la página debe estar en embeds, que necesita "sdk": 2; consulta Páginas de otros sitios y qué permite el marco.

app.jsjs
// panel.json declara "embeds": ["www.youtube-nocookie.com"] y "sdk": 2
// index.html tiene <div id="player" style="aspect-ratio: 16 / 9"></div>
const slot = document.getElementById('player')
let player = null

async function play(videoId) {
  if (player) await player.remove()
  try {
    player = await deska.embed.attach(slot, `https://www.youtube-nocookie.com/embed/${videoId}`)
  } catch (err) {
    if (err.code === 'permission_denied') say('Este panel no puede mostrar ese sitio')
    else if (err.code === 'invalid_args') say('Cierra un reproductor primero')
  }
}

play('dQw4w9WgXcQ')

deska.embed.attach(element, url)

La forma fácil. Muestra la página sobre el elemento y lo sigue: cuando el elemento cambia de tamaño, la página se desplaza o la ventana cambia de tamaño, el SDK vuelve a leer la caja del elemento, como mucho una vez por cuadro, y mueve el embed solo cuando la caja cambió. Un elemento oculto a 0 por 0 oculta el embed sin quitarlo.

ArgumentoTipoNotas
elementElementEl espacio de tu página que cubre el embed. Dale un tamaño.
urlstringUna dirección https de un host en embeds, en el puerto por defecto, sin usuario.
Permiso
Ninguno, pero el host de la URL debe estar en embeds
Devuelve
Promise<{ id, remove() }>: el id del embed, y remove(), que lo oculta y deja de seguir al elemento.
Límites
  • Cuatro embeds a la vez por página del panel.
ErrorCuándo
permission_deniedEl host no está en embeds, o la dirección es http simple o usa otro puerto.
invalid_argsEl elemento no es un elemento, o el panel ya muestra cuatro embeds.

deska.embed.show(id, url, rect)

Lo mismo a mano. Mostrar de nuevo un id lo apunta a la dirección nueva con un marco nuevo. Los embeds se van con la página: una recarga o un panel cerrado los quita, así que vuelve a mostrarlos después de ready().

ArgumentoTipoNotas
idstringTu nombre para el embed: de 1 a 40 de A-Za-z0-9_-.
urlstringUna dirección https de un host en embeds, en el puerto por defecto, sin usuario.
rect{ x, y, width, height }Dónde dibujarlo, en píxeles CSS de la página. Lados de 0 a 8000, desplazamientos dentro de 8000 hacia cualquier lado.
Permiso
Ninguno, pero el host de la URL debe estar en embeds
Devuelve
true
Límites
  • Cuatro embeds a la vez por página del panel.
ErrorCuándo
permission_deniedEl host no está en embeds, o la dirección es http simple o usa otro puerto.
invalid_argsUn id o un rect inválido, o un quinto embed mientras se muestran cuatro.

deska.embed.move(id, rect)

Envíalo cada vez que la página mueve el espacio de un embed que mostraste a mano. attach lo envía por ti.

ArgumentoTipoNotas
idstringUn id que la página muestra.
rect{ x, y, width, height }El lugar nuevo, en píxeles CSS de la página.
Permiso
Ninguno
Devuelve
Nada. Un movimiento no tiene respuesta.
Límites
  • No cuenta para las 20 llamadas por segundo, así que puede seguir el desplazamiento; sí cuenta para los 400 mensajes por segundo que detienen un marco.
  • Un movimiento para un id que la página no muestra, o con un rect inválido, se descarta.

deska.embed.hide(id)

Quita el embed y libera su lugar entre los cuatro.

ArgumentoTipoNotas
idstringDe 1 a 40 de A-Za-z0-9_-.
Permiso
Ninguno
Devuelve
true, también cuando ningún embed tenía ese id.
ErrorCuándo
invalid_argsEl id no es de 1 a 40 de A-Za-z0-9_-.

Eventos

app.jsjs
const stop = deska.on('visibility', ({ visible, idle }) => {
  if (visible && !idle) startTicking()
  else stopTicking()
})

deska.on('theme', ({ mode }) => redrawChart(mode))

deska.on('extras.control', ({ control }) => {
  if (control === 'play') play()
  if (control === 'pause') pause()
})

stop()   // deja de escuchar
EventoPayloadCuándo
theme{ mode, tokens }Cambió el tema o el acento. El SDK ya escribió los tokens nuevos en la página, así que el CSS que usa var(--deska-*) no necesita escuchar; escucha para volver a dibujar un canvas o un gráfico.
visibility{ visible, idle }El panel entró o salió de la vista, o la ventana de Deska ganó o perdió el foco.
extras.control{ control }La persona pulsó el botón de la fila de Extras. Lo recibe cada marco abierto del paquete.
  • deska.on(name, fn) devuelve una función que deja de escuchar. Pasar algo que no es una función no hace nada.
  • Los eventos llegan solo después de ready(). Un listener que lanza un error queda registrado en la consola y no detiene a los demás.

Los mensajes por debajo

El SDK es una capa delgada sobre postMessage entre el marco y Deska. Nunca la necesitas, pero explica lo que ves en un depurador. Deska solo lee mensajes de la propia ventana del marco, y solo responde a la página que terminó el saludo inicial; las respuestas van a cualquier origen porque el del marco es opaco, y ninguna lleva nada privado.

js
// Frame to Deska
{ type: 'deska:ready', sdk: 1 }
{ type: 'deska:call', id: 'c1', method: 'storage.get', args: ['zone'] }

// Deska to the frame
{ type: 'deska:init', payload: { panel, place, theme, visibility, permissions, locale } }
{ type: 'deska:result', id: 'c1', ok: true, value: 'Europe/Berlin' }
{ type: 'deska:result', id: 'c2', ok: false, error: 'permission_denied' }
{ type: 'deska:theme', payload: { mode, tokens } }
{ type: 'deska:visibility', payload: { visible, idle } }
{ type: 'deska:event', name: 'extras.control', payload: { control: 'play' } }

Una llamada antes de deska:ready responde not_ready. Los ids de llamada tienen hasta 64 caracteres; un mensaje sin un id válido se descarta sin respuesta.

deska.embed.move no es una llamada sino un mensaje propio, { type: 'deska:embed-place', id, rect }, que no recibe respuesta y queda fuera del límite de llamadas.