github dmellok/tesserae v0.463.0

2 hours ago

Added

  • A widget can say the least Tesserae it needs. tesserae_min in a
    plugin's manifest and on its catalog entry ("0.463.0"). A host at or
    above this version refuses to install or update past it, says so on the
    Browse card and in the plugin list, and leaves the installed version in
    place. Older hosts ignore the field, so a widget that uses it still has
    to load there and say what it is missing.

  • Artwork: pictures a widget makes and keeps, once for every widget.
    app.artwork.Collection takes the function that makes one picture and
    does the rest: the store with earlier versions and panel-converted copies,
    a budget in US dollars at the models' listed prices, the queue and its
    workers (resumed after a restart), the hold while a dashboard is being
    edited, what each dashboard asked for, the job tray cards, and an admin
    page at /plugins/<id>/artwork/<name>/ with Render now, Paint the held,
    Purge, a live ticker and per-picture Repaint, Delete and versions.
    app.fal is the one fal.ai client behind it. Widgets get
    ctx["cell_id"] and ctx["settled"] so the hold no longer guesses from
    a timer. See "Pictures a widget makes and keeps" in the widget docs.

  • History folds follow-ups under the send they finish. A page sent
    again once its paintings landed now sits under the original push as
    small dots on the rail, with its outcome and timing, and one line says
    when the finished page reached the panel. Deleting the send deletes
    the chain; the follow-up chip still filters to them on their own.

  • Widgets can be run ahead of a scheduled send. A widget whose work
    takes longer than a fetch may (an AI painting) declares
    render.warm_ahead_s in its manifest, and the scheduler runs its page
    that long before each projected send (schedules, rotation steps, the
    page's own refresh cadence), polling until the page stops asking for a
    refresh, so the panel paints the finished page once. The lead learns:
    a widget says how many pieces of work are pending (live_pending), the
    server works out seconds per item from how long the page took to settle,
    keeps a typical count per send, and sizes the next lead from the two,
    30 s to 15 min, asking for more after a warm that ran out of time.
    Nothing reaches a panel from a warm.

  • A page still painting is sent again when it is done. A widget that is
    waiting on something (an AI painting in flight, say) already tells the
    editors to fetch it again in a few seconds through live_refresh_s. A push
    now honours the same hint: the composed page carries it as a <meta>, the
    capture reads it back, and the push pipeline sends the page to the same
    devices again after the wait, up to eight follow-ups in a row, one pending
    follow-up per page. A scheduled send of an Overhead or Specimen Sheet page
    no longer leaves the panel showing the page as it was caught mid-work.

Fixed

  • A big picture no longer reaches the panel as an empty box. The
    renderer waited for every image to load, not to decode, and Chromium
    decodes a large one (a widget's own data-URL JPEG, a camera frame) after
    the load event; a frame captured in between had the picture's box empty.
    The image wait now decodes each loaded picture and lets a frame through
    before the screenshot, and says in the log when a picture would not
    decode or the wait gave up.

  • Dashboard previews no longer show oversized fonts and icons on large
    panels.
    Previews were laid out at an 800px-capped size, so a 1200x1600
    panel was laid out at 600x800 and its widget text and icons looked twice
    too big once shown at panel size. Previews now lay out at the panel's real
    size, exactly as a push does, and the dashboards-list thumbnail is shrunk
    afterwards. The editor's Panel view also renders each bound panel at its
    own size instead of the largest panel's. Covers the dashboards list, the
    decks lineup and the companion app's /dashboards/<id>/preview. (#364)

Added

  • Custom CSS on a dashboard (#366). The dashboard editor's Appearance
    section has a Custom CSS box. Its rules apply to the page and, since each
    widget renders inside its own shadow root, are copied inside every widget
    on the dashboard as well, so [data-widget="weather_now"] .w-title { display: none; } hides that widget's title row. The live preview picks
    the box up like any other field, and a closing style tag in the text is
    neutralised on save. Documented under Dashboards in the docs.
  • A button of a job's own in the job tray. jobs.report takes
    action=(label, callable): the card shows a button with that label and
    pressing it runs the callable through POST /jobs/act. The Specimen Sheet
    uses it for "Paint now" on the paintings its editor previews hold back.
  • Rolling weeks and wrapped titles on the month widget. calendar_month
    has a new Rolling weeks option: the calendar month as before (the
    default), or 1 to 6 weeks starting at the week that holds today and
    rolling forward week by week, so late in the month the grid shows what is
    coming instead of the past. The title follows today's month, so the days
    of the next month fade as they do in the month view, and the 1st of a
    month shows its month name (1 Nov). A new Event title lines option
    (1 to 4, shown with the text event style) wraps event titles onto that
    many lines instead of cutting them at one; the +N chip stays visible
    however tall the titles get.
  • Examples on a textarea option. A widget's textarea cell option can
    carry presets ([{label, value}]): the editor shows an Examples
    dropdown above the box, and picking one fills the box with that text to
    be edited from there. The Specimen Sheet's scene uses it.

Changed

  • Paper is the default design. New installs, and installs that never
    touched the design switch, now get Paper; the switch in the header (and
    the phone menu) goes back to the classic design in one click, and an
    install that already chose classic stays on it. Opt-in since 0.437,
    Paper was chosen by about half of the installs that could see it and
    nobody who tried it went back. The heartbeat's ui_chosen flag tells a
    switch back from an install that never saw the other design.

Added

  • Live previews while a widget has background work. A widget whose data
    carries live_refresh_s (2 to 120 seconds) is fetched again by the page
    editor's preview and the Panels editor after that long, and the cell
    repaints when the data changed, so pictures and cut-outs appear as they
    finish instead of on the next edit. Polling stops when the field goes, the
    cell leaves the page, or the tab is hidden (documented in the widget guide).

  • Background job tray. Anything working in the background now shows a
    progress card at the bottom right of every admin page: a title, a detail
    line, an animated bar (a sweeping block when the fraction is unknown), a
    Stop button for jobs that can be stopped, and a check, warning or stop mark
    when it ends. Jobs live in the server, so the tray carries across page
    navigation without replaying its entrance; it folds to a "2 jobs running"
    pill and stacks above the agent toast. A widget's server.py reports with
    from app import jobs and jobs.report(source, key, title, progress=..., detail=..., stop=...), then jobs.finish, jobs.fail or jobs.cancel
    (documented under "Background jobs" in the widget guide). Polled at
    /jobs/jobs.json, quick while a job runs and paused in a hidden tab.

  • Bloomin8 Canvas support (#344). A new device kind speaks the Canvas's
    own Schedule Pull API: point its upstream URL at <tesserae>/api/bloomin8
    with the token from the device card and it pulls the latest frame as a
    full-colour JPEG on each wake, sleeping until the time Tesserae gives back.
    Wakes follow the page's schedule within the configured interval, a frame
    the Canvas already painted isn't sent again, and the battery level it
    reports shows on the device card and in Home Assistant. Built against
    Bloomin8's spec and not yet tested on a Canvas; see the new quickstart.

  • Widget options that only show when they apply. A widget can mark an
    option with show_if (for example, "only for the cluster layout") and the
    grid editor and the canvas Configure drawer hide it until it applies,
    updating as you change the other options. Hidden options keep their value.
    See "Fields that only apply sometimes" in the widget docs.

  • Upload a widget from the Widgets page. "Upload widget" takes a .zip or
    .tar.gz of a widget folder and installs it into the data folder beside
    Tesserae Studio pushes, so a widget written for one install needs neither
    the catalog nor file access (the Home Assistant App keeps its data where
    Samba and SSH can't reach it). It is usable straight away; one with an admin
    page asks for a restart. A zip made with Finder's Compress works as is.
    Uploaded and pushed widgets get a Remove button on the same page.

  • Yesterday's solar total on the HA energy widget (#362). A new switch,
    "Show yesterday's solar production", adds a chip with yesterday's total next
    to today's, read from the history of the daily solar-energy entity. Off by
    default, so existing widgets look the same until it is turned on.

  • Heartbeat: was the design chosen? The daily heartbeat's ui field
    (classic or Paper) gains ui_chosen, true when somebody set the design by
    hand rather than leaving the default. It lets a later change of default be
    measured by people switching back, instead of by who happens to be on
    which design. Privacy doc updated.

  • What panel is this for? Making a dashboard on an install with no panel
    registered now asks for the size to design at: the common panel shapes
    (7.5" landscape and portrait, 4.2", Inky Impression 4", M5Paper, 6"
    e-reader, 10.3", 13.3" both ways). A grid dashboard keeps the chosen size
    as its own panel and a canvas dashboard gets it as its artboard, instead of
    the server's default panel or 600×400. Binding a device later switches it
    to the device's size.

  • Server name and colour (#350), for anyone running more than one
    server. Settings → Server → This server takes a short name and a colour:
    one of five presets (the Spectra 6 inks toned to match the admin's red,
    each with a lighter step for dark mode) or a custom colour. The colour
    paints a 4px stripe across the top of every admin page and the canvas
    editor, tints the tab icon and the phone's toolbar, and becomes the
    accent, in Paper and the classic design alike. The name shows as a chip
    under the wordmark and leads the tab title (dev · Dashboards), and the
    companion API reports it as the instance name. Nothing changes until one
    is set, and panel renders never show either.

  • Touch wake for the reTerminal E1003 and reTerminal Sticky (#327): a
    third touch setting next to Touch input and Touch linger. Any tap keeps
    the touch controller scanning through deep sleep as before; Double tap or
    swipe
    parks it in its gesture mode, about 1 mA instead of several, where a
    single first tap does nothing, a double tap or a swipe wakes the panel, and
    taps then work as usual while it is awake. Sent to the device as
    touch_wake (tap or gesture) in the status config block; needs device
    firmware 1.44.0 or later, older firmware ignores it.

  • Colour e-readers (E Ink Kaleido 3). A KOReader reader that pairs with
    gamut: "kaleido3" (the plugin announces it for a Kobo Libra Colour or
    Clara Colour with colour rendering on) is served a 24-bit RGB PNG with
    every channel dithered to 16 levels, 4096 colours, through the new
    kaleido_png renderer; /frame says format: "png" and the plugin
    blits it in colour. The gamut is accepted at register and discover, the
    editor's Panel view previews it, a reader that first paired as greyscale
    moves over when its plugin re-pairs with the new gamut (and back when
    colour rendering is turned off), and hardware entries for the Kobo Libra
    Colour and Clara Colour list the renderer first. Not yet tested on a
    colour reader.

  • Credits name esptool-js, which Tesserae Cloud's setup page uses to flash
    firmware over USB; docs/credits.md and the cloud's /credits are built from
    the same list.

  • Search, filters and sorting on every list. One shared toolbar (a
    search box, one or two filters, a count) now heads Settings › Devices,
    Dashboards, History, Events, Lineups, Widgets › Installed, the Themes
    strip, Rooms, Firmware, Cloud relay remote panels, Companion app paired
    apps, Device batteries, the Stats display list and System backups. Table
    columns sort from their heads (ascending, descending, then back to the
    list's own order, with a caret on the sorted column); lists without
    column heads get a Sort menu. Numbers, sizes and times sort by value, and
    the last sort is remembered per list in the browser. History and Events
    filter the rows the page loaded and say so; their server-side filters are
    unchanged. Live rows (Events, the Lineups refresh) join the current
    search and sort as they arrive.

  • Paper's sidebar folds to an icon rail. A Collapse button at the foot
    of the desktop sidebar narrows it to 64px: the logo and one icon per
    item, each named by a tooltip, with Widgets opening as a flyout to the
    right and the theme and design switches as icons. The choice is
    remembered in the browser and applied before the page paints; the page,
    the Settings column and the batteries pill follow the new width. The
    phone drawer and the classic design are unchanged.

  • Paper, an opt-in redesign of the web UI. A switch in the header
    ("Try the new design"; in the phone menu as "New design") turns it on for
    the install, and the same switch turns it off again. Paper moves the
    navigation into a left sidebar on desktop (a left drawer on phones),
    swaps the teal for warm paper, ink and a red accent, uses Archivo and
    Martian Mono (already bundled), flattens shadows and gradients to 1px
    hairlines, recolours the Tesserae mark, and adds a light page-enter
    animation that switches off under reduced motion. Dark mode follows. The
    classic design is unchanged and stays the default. With online features
    on, the daily heartbeat now says which of the two designs the install
    runs (ui), so adoption can be counted; see docs/privacy.md.

Changed

  • Dashboards are grouped by display again, inside the table. Each
    display gets a full-width group row with its status dot, name, size,
    what it is showing, a count and a link to its device page, with "Not on
    a panel" last. A dashboard on several displays is listed under each, and
    bulk select and the count take it once. Groups fold one by one
    (remembered) or all at once from the toolbar, and search, the Panel
    filter and Active / Archived work across them. The Panel column is gone
    since the group names the display; a dashboard also on other displays
    shows "+N panels".

  • The canvas editor is laid out again, and works on a phone. It now
    follows the app: the Paper or classic design, and the app's light, dark or
    system theme (its own dark-mode button is gone). In Paper the app's icon
    rail sits on the left. The left column is one sheet with Layers, Add and
    Widgets tabs; the old Appearance card becomes the inspector's Page view,
    shown while nothing is selected. The inspector folds an element's fields
    into Content, Position and size, Style, Data and Touch sections that
    remember whether they are open, and moves Front, Back, Duplicate and Delete
    into its "…" menu. The agent becomes a one-line strip above the inspector
    (what it is doing, steps, how long it has been quiet) that opens onto the
    steps, the nudge box and Follow; the toolbar pill reads "Agent working".
    The selected element shows its name and size above it, the zoom control
    gains a Fit button, a tap on an Add tile drops it in the middle of the
    canvas, and the less-used toolbar buttons fold behind More on narrower
    screens. On a phone the "Best on a bigger screen" notice is gone: the
    canvas fits the width, a finger selects and drags elements, pans, and
    pinches to zoom, and a bottom sheet with Add, Layers, Element and Agent
    tabs opens half way or folds down to its tabs, with the agent floating
    under the canvas while it works. The code editor opens full screen there.

  • The canvas editor, the MCP API and the template marketplace are now
    regular features, on by default.
    Settings › System › Experiments becomes
    a Features card with the same three switches, so any of them can still be
    turned off, and a feature someone already switched off stays off.
    TESSERAE_EXPERIMENT_<NAME>=0 still turns one off for a whole deployment.
    The canvas editor drops its "experimental" label and first-open notice; it
    is reached from Dashboards by creating a Freeform canvas dashboard or
    opening one. The template marketplace still needs Online features. The MCP
    API still needs its token from any other machine, and a request relayed
    through a reverse proxy (or carrying a forwarding header) now needs the
    token too, since a forwarded 127.0.0.1 could otherwise pass for a local
    caller. The admin pages only watch for agent activity once an agent has
    connected, and the heartbeat's mcp flag now means an agent has connected
    rather than that the API is enabled. The calibration page's "Experimental:
    edge handling" section is now just "Edge handling".

  • The Dashboards list is a table. It works like Settings › Devices: a
    toolbar with search (name, panel or id), a Panel filter (each display, or
    "Not on a panel"), the Active / Archived switch and a count; one row per
    dashboard with a status dot (solid while a panel shows it, hollow while a
    lineup holds it), a thumbnail, its panel, size, update and wake cadence,
    last push and state. A row opens in place onto a larger preview, four
    figures (on the panel, last pushed, updates, lineups) and its controls:
    Updates, Wake, Push, settings, Duplicate, Archive or Restore, Delete and
    Open in editor. Bulk select gains a select-all box for the rows shown. The
    per-display groups give way to the Panel column and filter, and the create
    form now opens from New dashboard beside Send now in the page head. Columns
    drop as the table narrows (Wake, then Updates, Size and Last pushed), rows
    become two lines on phones, and nothing scrolls sideways, in both designs.

  • Paper follows the approved mockups more closely. Sections, tables,
    stats and empty states sit on raised sheets (a lighter surface with a 1px
    edge and 2px corners) on a slightly darker page; Settings gets a second
    sidebar for its sections on wide screens, with labels on the left and
    controls on the right; sidebar items are square and the selected item is
    a deeper shade with no accent bar; a small red square before each section
    title is the one splash of colour; and every form control is one height
    (40px, 44px on phones, 32px inside list rows).

Fixed

  • A page's Auto-update lands on the clock. A page refreshing every N
    minutes now renders once per N-minute slot of the wall clock, just after it
    starts, and the scheduler checks on the clock rather than 30 s after its
    last check finished. Before, each refresh came N minutes after the previous
    render ended, so a 1-minute clock page crept a few seconds every minute and
    skipped a minute every few.

  • Touch-v3 labels draw as text, not as boxes (#360). The server packed
    touch atlases the overlay's way (0 = black) while touch-v3 reads 0 as paper,
    so every button label showed as white letters in a black box. Touch atlases
    are now packed ink over paper as the atlas schema says, and a server that
    had the old ones cached rebuilds them once.

  • Glyph atlases are built ahead of time and kept (#359). The render prune
    deleted them every 50 renders, so the next device poll went out without
    label text until a rebuild. They are now spared, keyed on an atlas format
    number and the Tesserae version, built at startup (touch-v3) and right after
    a render (overlay sizes), and a device request never builds one inline.

  • The Docker image and Home Assistant App start on older x86-64 CPUs
    (#361). numpy 2.4 needs an x86-64-v2 processor, so a pre-2009 CPU, or a VM
    given a basic CPU model such as Proxmox's kvm64, stopped at "numpy failed
    to import". The image pins numpy<2.4, and the startup check now names the
    CPU cause or prints numpy's own error instead of "the image is broken".

  • Picture, NASA APOD shows the real picture again. NASA moved APOD to
    science.nasa.gov on 2026-09-29, and the api.nasa.gov/planetary/apod
    route the widget used now answers every date with the NASA logo. It now
    reads science.nasa.gov/wp-json/wp/v2/apod-basic, which needs no API key
    and has no quota, so the NASA API key setting is gone (a stored key is
    ignored). One request for the last 14 entries replaces the day-by-day walk
    past videos, falling back to a video's still frame when no image is in
    reach; the HTML credit is flattened to text and shown without a © mark,
    since it mixes agency credits with personal copyrights; the image is
    requested at most 1200px through the asset host's resizer; and the
    entry's alt text is carried to the <img>. The NASA APOD credit now links
    to science.nasa.gov/apod.

  • History's Clear history control no longer asks "Delete the selected
    history?" when it is about to delete everything older than the chosen
    cutoff; the confirm now names the cutoff. With rows ticked, the same
    button deletes the selection instead of reporting that it deleted 0
    entries (#346).

  • Settings › Rooms: the "widget isn't installed" notice drew the dark
    theme's text on a fixed cream card; it now uses the warn tokens in both
    themes. Its Install Room Status button goes to the catalog with the
    search prefilled, where the widget can actually be installed, instead of
    the installed-widgets settings; the calendar prompt does the same when
    the calendar connector is missing (#346).

  • Settings › Themes says that these are the palettes dashboards render in
    on the panel, and that the admin UI's own light and dark mode is the
    sidebar toggle, after a report that picking one was expected to switch
    the UI (#346).

Changed (bridge)

  • Settings › System › MCP now expects the tesserae-mcp bridge 0.18.1, whose
    messages no longer mention an "mcp experiment"; pipx upgrade tesserae-mcp
    picks it up.

Security

  • uv.lock resolves Pillow 12.3 (and pillow-heif 1.8), urllib3 2.8 and
    Werkzeug 3.1.9, which carry the fixes for the advisories pip-audit
    reported against 11.3, 2.7 and 3.1.8. The version caps were already lifted;
    the lock had kept the old versions. Every renderer and every dither mode on
    every gamut renders pixel-identical output on Pillow 11.3 and 12.3. The
    pip-audit step in CI is now a hard gate (#142).
  • A client could set X-Forwarded-For to pass for this machine or for
    the local network. From the network, claiming 127.0.0.1 opened the
    pages kept for the built-in renderer (/compose/, the theme
    stylesheets) without signing in; from the internet, claiming a LAN
    address got past the network check on installs with the password
    turned off. Only a reverse proxy on this machine or the local network
    may now say who the real client is, and "this machine" means a direct
    connection with no forwarding headers.

Don't miss a new tesserae release

NewReleases is sending notifications on new releases.