SDK
Page Desk apps: HTML, CSS and JS on the Miblo Desk screen
The standard Desk app from Miblo 1.45 is a web page: manifest, window.miblo, quality presets, files, network, screen.page() and the limitations, one by one.
What it is
From Miblo 1.45 on, the standard Desk app is a web page. You write HTML, CSS and JavaScript; Miblo Hub renders the page at 240x240 on the computer and streams only the pixels that changed to the Miblo Desk over the local network. The computer draws, the Miblo Desk only paints.
Desk apps made the old way (cards, frames, animations and live charts) are deprecated, not removed: they keep working. A new Desk app should be a page; the reference for the old way is in The App screen and Animations.
This page covers a page Desk app from end to end: how to start one, the manifest.json, what main.js and the page do, the quality presets, the files, the network, the SDK equivalent and, at the end, the limitations, which are worth reading before you start.
Start one
miblo apps new "My clock" --page
miblo apps dev my-clockThe folder has manifest.json, main.js, page.html and a README.md. The template is a clock with how much of the workday has passed, and one setting (the end of the workday). The simulator shows the page live, next to the screen, with the same window.miblo the Miblo Desk's page gets, and loads it again on every save. The other commands are in Develop without a Miblo.
The manifest
{
"id": "my-clock",
"api": 4,
"display": "page",
"page": "page.html",
"network": [],
"intervalMs": 60000
}(The usual fields, such as name, promise and settings, still apply: Publish.)
display: "page"andpage: a.htmlfile of the Desk app's folder. A subfolder is fine (web/index.html).api: 4(Miblo 1.45). A manifest withdisplay: "page"and anapibelow 4 is refused.A page Desk app cannot be alert-only (
screen: false).network: the only hosts the page reaches (fetch, images, fonts). It is the same rule asctx.fetchJson; anything else is blocked and said once in the Desk app's log. See The network.
main.js gives the data, the page draws it
main.js is still the program Miblo runs every intervalMs. Instead of a card, run() returns { data }: JSON, at most 16 KB.
export async function run(ctx) {
return { data: { temp: 29, city: 'Recife' } }; // JSON, at most 16 KB
}The page receives the data and draws it:
<script>
function show(d) { if (d) document.querySelector('#t').textContent = d.temp + '°'; }
show(window.miblo?.data);
window.miblo?.on('data', show);
</script>Miblo hands the data to the page: the first run() opens the page with it already, and every one that changes arrives after. Data equal to the last is not sent again.
window.miblo in the page:
miblo.data | the last object run() returned (null before the first) |
miblo.on('data', fn) / miblo.off('data', fn) | hears each new object (it only comes when it changed) |
miblo.lang | 'pt' or 'en' |
miblo.quality(q) | asks for a quality (see below) |
Draw at 240x240 and animate by time
The page's viewport is exactly 240x240 CSS pixels. Animate by time (CSS animations, the requestAnimationFrame timestamp), never by counting frames: the page renders faster than the Miblo Desk paints.
Quality
| Quality | Frames per second (target) | What for |
|---|---|---|
verylow | 30 | fast motion over the whole screen (games, video), the lowest detail |
low | 25 | motion, reduced detail |
medium | 20 | everyday pages with animation (where auto starts) |
high | 15 | detailed pages, little motion |
ultra | about 10 | maximum detail, still pages, photos |
auto (the default) starts at medium, steps down when the stream stalls and up after about 5 s clean, never above max. The page can switch at any time with miblo.quality(q): a still layout at ultra, a video background at low.
The page's files
CSS, images, fonts, scripts and videos sit next to the page and load by relative path.
| Kind | Formats |
|---|---|
| Images | PNG, JPEG, GIF, WebP, SVG |
| Fonts | WOFF, WOFF2, TTF, OTF |
| Scripts and styles | .js, .css |
| Videos | WebM, MP4 (see Video) |
Each file is at most 256 KB; a video can be as large as the package allows (2 MB in all). To publish to the Desk App Store, see Publish.
The network
The page loads only files from its own folder and the hosts the manifest's network allows, over https. Anything not on the list is blocked, and the block is said once in the Desk app's log. main.js fetches from the internet with ctx.fetchJson, under the same rule. No WebSocket, WebTransport or WebRTC (see the limitations).
Without a Desk app: the SDK
A program that is not a Desk app does the same through the SDK, on its tool's screen:
const screen = new MibloStatus().screen({ tool: 'weather' });
await screen.page('weather.html', { data: { city: 'Recife', temp: 29 }, quality: 'auto', max: 'high' });
await screen.data({ city: 'Recife', temp: 30 }); // the page hears it with miblo.on('data')
await screen.quality('ultra'); // or the page itself: miblo.quality('ultra')
await screen.clear();page(file, { data, quality, max }):fileis a.htmlfile relative to the program's working folder (an installed Desk app's page is always a file of its own package).datais JSON, at most 16 KB.qualityis'auto'(the default),'verylow','low','medium','high'or'ultra';maxlimits how highautogoes.data(obj): sends the page new data.quality(q): switches the quality now.New error:
no_page(status 409), whendata()orquality()come beforepage().
For now only the @miblo/status package (TypeScript) has these three methods. The Python package does not yet, but any language reaches the POST /sdk/v1/screen/page route (and screen/data, screen/quality) through the reference.
Limitations
What a page can and cannot do, plainly:
The computer draws. A page Desk app shows only while the computer is on, awake, running Miblo Hub and on the same local network as the Miblo Desk. When the computer sleeps or leaves, the page stops and the Miblo Desk goes back to its own screens (sessions, limits, alerts).
Miblo Desk 1.45 or newer. An older Miblo Desk cannot show a page Desk app (the Desk App Store and
miblo appssay "needs a firmware update"); the deprecated card, frame and animation paths keep working on older units.Frame rate is a target, not a promise.
verylowaims at 30 fps,ultraat about 10; the Wi-Fi decides what arrives (about ±3 fps between runs at home). Fast motion over the whole screen costs detail; fine text reads best athighorultra. On macOS, AirDrop, Handoff and the Wi-Fi scans for Location Services cause stalls of about 100 ms in the radio, seen as hitches. The fix: AirDrop to "No One", Handoff off and Location Services → Wi-Fi networking off (System Settings → Privacy & Security → Location Services → System Services).The panel. 240x240 pixels, 65 thousand colours (RGB565); the picture travels as compressed 4x4 blocks, so the lower qualities soften detail and colours band slightly in smooth gradients.
Video. WebM (VP9, AV1) always plays. H.264/AAC MP4 plays only when the computer has Google Chrome or Microsoft Edge (the Chromium Miblo Hub downloads when there is none has no licensed codecs). No sound: the Miblo Desk has no speaker, and pages are muted.
No input. The page gets no clicks, touches or keys from the Miblo Desk; it reacts to the data its Desk app sends and to time.
One page at a time on each Miblo Desk. An alert always takes the screen at once (the page pauses and comes back after it).
Data and network. What
run()returns is at most 16 KB of JSON; the page loads only files from its own folder and the hosts the manifest'snetworkallows, overhttps. No WebSocket, WebTransport or WebRTC: they do not exist in a page Desk app (nor in its frames and workers), and its popups, shared workers and service workers never run.The computer's cost. One headless browser for all pages (about 1-3 ms of CPU per frame decoded); when no Chrome, Edge or Chromium is installed, Miblo Hub downloads one (about 100 MB) once, silently, the first time a page Desk app runs.