Widgets
- new — widgets can keep their own refresh clock. The machinery that kept the custom widget current was gated to that one type by name; it is now a table of which types poll and how fast, so a built-in widget can refresh itself. One clock per tile however often it is redrawn, nothing at all for a tile that draws from figures the dashboard already holds, and the custom widget's own
ttland 30-second floor untouched. A hidden tab still asks nothing. - fix — one bad widget no longer empties the page. Saving a page's widgets shared its cleaning with the code that reads them, which drops whatever it cannot draw so a file written by another version still opens. On a write that meant a request carrying one unknown type had every other widget silently discarded and was answered
200— a dashboard losing its tiles to a single bad entry. Writes now refuse: the page is left exactly as it was, the response is a400naming the type it could not store, and the panel repeats that instead of "Could not save the widgets". Too many widgets for one page is refused the same way rather than quietly truncated. Reading keeps its tolerance, which is what it was written for. - new — the widgets tab works like the bookmarks tab. The same toolbar: a search across names and types, a page picker, a sort, and the one button that adds something. Sorting defaults to grouped, under the same headings the catalogue uses — fourteen rows in one column is a list to read, four short groups is a thing to scan — with order on the page, name and type as the alternatives.
- docs — the system widgets are written up in full. Config → Help gains a panel of their own covering which mount each needs and why, the three variables, the Unraid template rows, and what the tiles will and will not tell you. The manual carries the same as a reference section with the full variable table, and the README's Quick Start now shows the compose block and an Unraid table beside it. The counts that said "thirteen kinds" and "the only widget that talks outside" are corrected wherever they appeared.
- new — every page's widgets in one list. The page picker gains All pages, which answers "what have I actually got" without visiting each tab in turn — a widget on the page you are not looking at is the one you forget you are running. Each row says which page it lives on, and an edit is written back to that page rather than to whichever one the tab was showing. Adding is not offered there, since it has no answer to "where".
- new — widgets can be moved to another page, and acted on in bulk. Tick any number of rows and show, hide, move or delete them at once. A move writes the destination first, so a failure leaves the widgets where they were rather than nowhere; ids travel with them, so a moved widget keeps the sign-in and the collapsed state filed under its id. Ticks are held by id, not row position, so re-sorting cannot retarget them.
- new — ↺ resets a widget to its defaults. One click clears every setting in the panel, into the draft, so Save or Discard still decides. Offered only when something is actually off its default. The title and the Shown box are left alone: those are not settings with defaults, and quietly renaming a widget is not what a reset should mean.
- new — the ℹ is on every kind of widget setting. It was wired to number fields alone, so a tickbox or a choice that needed a paragraph had nowhere to put one. Now on all of them, and still only where there is text behind it.
- fix — a change that saves without a Save button now says so. Renaming a widget and the Shown box write straight to the server, while the settings panel below has its own Save. Two ways of saving in one row, one of them silent, is what made the tab feel like it might not have taken.
- new — a containers widget. How many containers run against how many exist, with the figures you pick: running, stopped, paused, unhealthy, total, images. Tick nothing and you get them all, so a figure added later is not hidden from everyone who once saved the panel. Two more toggles name what is failing and what just restarted — a count sends you looking, a name does not, and a container up for minutes while the rest have run for days is the shape of a crashloop. Deliberately no per-container CPU or memory: one
/statscall measures a full second, so twenty containers would be twenty seconds of polling a beat. It needs the Docker socket, and the panel says plainly what read-only access still grants before you add it. - fix — a row of figures no longer leaves empty ground. Every stat grid was laid out in four columns on a wide tile, which is right for four figures and wrong for two, three or six: a widget whose figures the reader chooses left half a row blank, or two patches under a row of six. The column count now divides the figures it was given.
- new — a memory widget. How much memory is really in use, against the total, with a bar and the figures beside it. Used counts what the machine cannot hand out — the page cache is reported separately, as the spare room it actually is, rather than folded into "used" the way that makes a healthy Linux box look permanently full. Swap gets its own line and bar when asked for, and a machine without swap says so instead of drawing an empty one. Given two columns the figures sit abreast instead of wrapping.
- new — a disks widget. Free space per disk you name, with the pool as the headline and used, free, total and percentage beside it. Each disk carries a bar and, optionally, how full its file table is — inodes run out on their own, so a disk can refuse a write with gigabytes showing free. Free counts what can actually be written: reserved blocks belong to root and are neither used nor available. A disk that cannot be read says so on its own row rather than blanking the tile, which is what an array with a drive spun down needs. Given two columns the totals spread across the width instead of stretching. Its settings carry the mount to add.
- new — a processor widget. How busy the CPU is and the load average behind it, as a built-in widget with its own refresh interval (one second at the fastest), and optional load-average and core-count figures. Given two columns the tile fills the width with more figures rather than stretching the same ones. It reads the host through an opt-in read-only
/procmount (NEXTDASH_HOST_PROC): nextDash runs in a container, so without that it would be reporting on itself. Unmounted, the tile names the setup step instead of showing zeros, and the percentage stays blank until a second sample exists rather than claiming an idle machine.
Health
- new — the review offer can be put away for a month. The "10 links to review" card had one way out, "Not today", which expires overnight — right for a postponement, wrong for someone who is not doing maintenance this month. Remind me in 30 days stamps
nextdashHealthReviewSnoozeUntiland the card stays away until it passes, then returns on its own. Deliberately not called ignore: the health view's per-row Ignore "Broken" mutes what a bookmark reports, server-side, while this touches no bookmark at all and only decides whether the corner asks. It sits as a quiet text link rather than a third button — a long label wraps onto its own row, where a bordered full-width button would read as the heaviest choice on a card whose primary action is Start. - new —
quietaction styling on notice cards.NoticeCard.define()acceptsquiet: truebesideprimary, rendering an action as an underlined text link. For an answer that belongs on the card but not in the row of choices. - fix — the settings item in the ⋯ menu carries a capital, like Export rows, Retest all and Checking off beside it. English, Dutch and French; German already had one and Chinese has no case to change.
Dashboard
- fix — adding a bookmark works before the search bundle has arrived. The
:newform travels in the lazily fetched search stack, and the keys that fetch it are>,:,?,*and/. Add-a-bookmark is none of them, and the toolbar button is not on the hover-prefetch list either — so for the first seconds of a visit+, Shift+B, Ctrl+Shift+A and the button each found no handler and returned: no form, no message, and the press was not queued, so it stayed lost once the bundle landed a moment later. Measured rather than argued: with the bundle held back the click opened nothing, and the bundle had arrived by the time the test gave up. All four routes now ask the loader for the stack and open when it is there — which is the promise the loader's own comment already made for the keys it did cover.
Inbox
- fix — Escape on the snooze picker no longer leaves the inbox with it. One press closed the picker and the view underneath. The guard that stops a view from eating an Escape meant for something layered over it named the menus it knew about, and the picker — added later — was on nobody's list.
Under the hood
- new — one place decides who owns Escape. Every view binds a document-level Escape handler in the capture phase when it opens, before anything inside it exists, so the view always sees the key first; each then had its own hand-written list of what might be on top, and a menu added later was on none of them. That is four bugs so far.
shared/escape-owner.jsturns it around: anything that can be layered says so while it is up, and a view asks one question instead of naming the answers. Two roles, because the views already told them apart — an owner was opened deliberately and keeps the key, something ambient appeared on its own and is dismissed by the press without keeping it. The inbox and health are migrated and the setting promo registers as ambient. Config's chain stays where it is on purpose: its order interleaves the search, inline-edit and typing guards between the menu and the selections, which a flat registry cannot say, and the attempt to make it fit cost the test that pins exactly that ordering. - fix — the CI actions are off Node 20.
checkout,setup-node,setup-goandupload-artifactwere all being forced onto Node 24, with the same eight deprecation warnings on every job. All four are on their current majors and the warnings are gone.
Tests
The suite went green and stayed there — the first green run since 30 June, 418 runs earlier, and seven more behind it. Almost every cause was one shape: a fixed pause where a wait belonged, or a measurement taken before the thing being measured had settled.
- fix — five overlays and one release tag. The config intro tip shows for fourteen seconds in the bottom-right corner, which is where the activity chart's last bars are, and it arrives after the overlay dismissal has been and gone: four hover tests across two spec files were racing it. The what's-new modal decides to open before it fetches its own module, so on a release whose cache token has just moved it lands after the dismissal too. A setting promo anchored in the bookmarks panel covered the sort chip. And the version pattern on the newest release tag ended at three parts, so it called every four-part release the project has ever shipped malformed — 16 of 169 tags, including the one that broke it.
- fix — reads that landed on a replaced element.
getComputedStyleon a detached node answers""for every property without complaining, so a chart test failed as though both series were the same colour when the panel it measured had simply been swapped out underneath it. Held the repaint at the door to prove it: 20 failures out of 25, every one carryingconnected:falsewhile the live document had the colour. - fix — seeds that waited for the view to notice them. Two inbox specs POSTed through the API and then waited for a background refresh to spot the rows. They now ask for the list. The oldest flake on the board went from failing under load to 56 of 56.
- fix — four helpers read
searchComponentbefore it existed. Three wrotequickAddWidget?.open?.() ?? searchComponent…, andopen()returns nothing on success — so the fallback was never a fallback, it was the path, and it only passed when the lazily fetched bundle happened to be there already. - new — a failure says which overlay was in the way. A modal that arrives mid-test intercepts every click and hover, and Playwright names the element but elides the part that identifies it. Four flaky tests in one run shared that line and nothing else. The harness now asks the page while it is still there — what is on top at the centre of the viewport, what
#app-modalholds, any notification — and logs it beside the failure. - fix — six seconds of sleeping per test, in three config helpers. They waited out Quickstart's favicon sweep;
waitForFaviconPrefetchalready waits for exactly that by asking. One file went from 2.8 minutes to 1.2. Two more helpers with the same sleep were left alone: they each failed once in the runs with the change, in blocks that fail on the current code too, and where a pre-existing flake could not be told apart from a settle that had been removed, the sleep stays.