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:
<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 usardeskade 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, peroready()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.
<script src="/__deska/sdk.js"></script>deska.ready() y el payload de inicio
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.
{
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'
}| Campo | Tipo | Significado |
|---|---|---|
panel.id | string | El id del paquete. Una carpeta cargada recibe dev- y doce caracteres hexadecimales; una página en localhost, dev: y su dirección. |
panel.name | string | El nombre del paquete según panel.json. Pásalo a setTitle para mostrar solo el nombre. |
panel.version | string | null | La versión instalada, null mientras construyes. |
panel.dev | boolean | Verdadero 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. |
permissions | string[] | 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ó. |
locale | string | El idioma y la región de la persona, como es-MX, para Intl y tus propios textos. |
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.
| Propiedad | Tipo | Significado |
|---|---|---|
deska.theme | { mode, tokens } | null | El último tema que envió Deska; null antes de ready(). |
deska.visible | boolean | Verdadero 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.idle | boolean | Verdadero mientras la ventana de Deska no tiene el foco: la persona está en otra app. |
deska.place | 'panel' | 'extras' | null | Dó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:
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
postMessageno puede copiar (una función, un nodo del DOM) se rechaza coninvalid_argssin salir de la página. Los métodos de archivos son la excepción:files.put,files.saveyclipboard.writeImagereciben unBlob, 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.
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)
| Argumento | Tipo | Notas |
|---|---|---|
key | string | De 1 a 256 caracteres. |
- Permiso
- Ninguno
- Devuelve
- El valor guardado, o
nullsi la clave no existe.
| Error | Cuándo |
|---|---|
invalid_args | La 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).
| Argumento | Tipo | Notas |
|---|---|---|
key | string | De 1 a 256 caracteres. |
value | any JSON value | Se 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
trueen 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.
| Error | Cuándo |
|---|---|
invalid_args | Una clave inválida, o un valor sin forma JSON. |
storage_full | La 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_large | La llamada lleva más de 64 KB, o anida más de 32 niveles. |
deska.storage.remove(key)
| Argumento | Tipo | Notas |
|---|---|---|
key | string | De 1 a 256 caracteres. |
- Permiso
- Ninguno
- Devuelve
true, también cuando la clave no existía.
| Error | Cuándo |
|---|---|
invalid_args | Una 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.
// 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)
| Argumento | Tipo | Notas |
|---|---|---|
file | Blob | ArrayBuffer | typed array | Hasta 32 MB. Un File de un selector, de pegar o de soltar es un Blob. Los bytes se envuelven en uno. |
type | string | Solo 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 noapplication/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.
| Error | Cuándo |
|---|---|
invalid_args | El archivo no es un Blob ni bytes. |
too_large | El archivo pasa de 32 MB. |
storage_full | El 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.
| Argumento | Tipo | Notas |
|---|---|---|
id | string | Un id de put o de list. |
- Permiso
- Ninguno
- Devuelve
- El archivo como un
Blobde su tipo, onullsi ningún archivo tiene ese id.
| Error | Cuándo |
|---|---|
invalid_args | El 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)
| Argumento | Tipo | Notas |
|---|---|---|
id | string | Un id de put o de list. |
- Permiso
- Ninguno
- Devuelve
true, también cuando ningún archivo tenía ese id.
| Error | Cuándo |
|---|---|
invalid_args | El 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.
| Argumento | Tipo | Notas |
|---|---|---|
file | Blob | Hasta 32 MB. Su tipo decide la extensión. |
name | string | Opcional, 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
trueen cuanto el archivo se escribe,falsesi la persona canceló.- Límites
- Solo mientras el panel tiene el foco.
- Un guardado a la vez.
| Error | Cuándo |
|---|---|
invalid_args | El archivo no es un Blob, o el nombre no es un texto o pasa de 120 caracteres. |
too_large | El archivo pasa de 32 MB. |
not_focused | El panel no tiene el foco. |
declined | Ya hay abierto un guardado de este panel u otra pregunta de Deska. |
host_error | No 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.
// 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
pastede la página trae lo que pegó la persona enclipboardData.files. - Soltar: llama a
preventDefault()endragovery luego leedataTransfer.filesendrop.
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.
| Argumento | Tipo | Notas |
|---|---|---|
title | string | Hasta 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.
| Error | Cuándo |
|---|---|
invalid_args | null, 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
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.
| Argumento | Tipo | Notas |
|---|---|---|
title | string | Obligatorio, hasta 80 caracteres una vez limpio. |
body | string | Opcional, 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.
| Error | Cuándo |
|---|---|
permission_denied | panel.json no pide notify, o la persona no lo permitió. |
invalid_args | El 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_limited | El panel envió un aviso hace menos de diez segundos. |
deska.openUrl
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.
| Argumento | Tipo | Notas |
|---|---|---|
url | string | Una dirección https completa, de hasta 2048 caracteres, sin usuario ni contraseña antes del host. |
- Permiso
open-url- Devuelve
trueen cuanto el enlace pasa al navegador.- Límites
- Un enlace por segundo.
- Solo mientras el panel tiene el foco.
| Error | Cuándo |
|---|---|
permission_denied | panel.json no pide open-url, o la persona no lo permitió. |
invalid_args | No es https, no es una dirección completa, es demasiado larga, o lleva usuario o contraseña. |
not_focused | El panel no tiene el foco: la llamada vino de un temporizador, o la persona ya se fue a otra cosa. |
rate_limited | Se abrió un enlace hace menos de un segundo. |
declined | La 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.
| Argumento | Tipo | Notas |
|---|---|---|
text | string | Hasta 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.
| Error | Cuándo |
|---|---|
permission_denied | panel.json no pide clipboard-write, o la persona no lo permitió. |
invalid_args | El texto no es un texto. |
too_large | El texto pasa de 64 KB. |
not_focused | El panel no tiene el foco. |
host_error | El portapapeles del sistema rechazó la escritura. |
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.
| Argumento | Tipo | Notas |
|---|---|---|
image | Blob | ArrayBuffer | typed array | Un 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.
| Error | Cuándo |
|---|---|
permission_denied | panel.json no pide clipboard-write, o la persona no lo permitió. |
invalid_args | No es un Blob, no es un PNG ni un JPEG, o es una imagen que no se puede decodificar. |
too_large | La imagen pasa de 32 MB. |
not_focused | El panel no tiene el foco. |
deska.extras.set
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 restodeska.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.
| Argumento | Tipo | Notas |
|---|---|---|
state | string | null | La línea de estado de la fila, hasta 60 caracteres una vez limpia. |
progress | number | null | De 0 a 1 dibuja un anillo alrededor del ícono. |
until | number | null | Una hora en milisegundos desde 1970. Deska muestra el tiempo que falta y sigue contando sin ningún marco vivo. |
control | 'play' | 'pause' | 'start' | 'stop' | null | El 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.
| Error | Cuándo |
|---|---|
permission_denied | panel.json no tiene contributes.extras. |
invalid_args | No 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.
// 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.
| Argumento | Tipo | Notas |
|---|---|---|
element | Element | El espacio de tu página que cubre el embed. Dale un tamaño. |
url | string | Una 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, yremove(), que lo oculta y deja de seguir al elemento.- Límites
- Cuatro embeds a la vez por página del panel.
| Error | Cuándo |
|---|---|
permission_denied | El host no está en embeds, o la dirección es http simple o usa otro puerto. |
invalid_args | El 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().
| Argumento | Tipo | Notas |
|---|---|---|
id | string | Tu nombre para el embed: de 1 a 40 de A-Za-z0-9_-. |
url | string | Una 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.
| Error | Cuándo |
|---|---|
permission_denied | El host no está en embeds, o la dirección es http simple o usa otro puerto. |
invalid_args | Un 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.
| Argumento | Tipo | Notas |
|---|---|---|
id | string | Un 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.
| Argumento | Tipo | Notas |
|---|---|---|
id | string | De 1 a 40 de A-Za-z0-9_-. |
- Permiso
- Ninguno
- Devuelve
true, también cuando ningún embed tenía ese id.
| Error | Cuándo |
|---|---|
invalid_args | El id no es de 1 a 40 de A-Za-z0-9_-. |
Eventos
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| Evento | Payload | Cuá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.
// 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.