Containers and bookmarks to self-hosted apps get real app icons from two open icon sets, dashboard-icons and selfh.st/icons. A container is matched by its image and name, a bookmark by its host, and the icon follows the theme between its light and dark variant. An icon picker with suggestions sits behind the pencil in the bookmark form and in the container drawer. Nothing is bundled: the server fetches the indexes and the icons, and the browser never talks to the CDN. The Custom widget knows sixteen more services, signs in for a token where a service hands one out, can count entries in an answer, and every preset is now tested against its service's recorded answer. Alerts can go through Apprise, and so to mail, Matrix, Signal and a hundred more. The container drawer's charts move to uPlot, the first of the charts to do so. Seven Unraid widgets read an Unraid server's array, parity, shares, VMs, UPS and notifications, and its alerts reach the same channels as everything else. The search panel can search the web through your own SearXNG or the Brave Search API, the look studio gains a theme editor that saves what is on screen as a theme of your own, the remaining charts in Health and Statistics move to uPlot, Config → Overview becomes panels, and a bookmark can be shown as a QR code to open it on a phone. A third bug hunt fixed 81 bugs across all of it, each with a test that fails without its fix.
App icons
- new — two icon sets, read by the server.
icon_sets.gofetches dashboard-icons'metadata.jsonand selfh.st'sindex.jsonfrom jsDelivr once a week with a conditional GET, keeps the mirrors indata/icon-sets/and keeps the old copy when a fetch fails or reads as nothing.icon_sets_index.gomerges them into one lookup: names first, then display names, then aliases, dashboard-icons before selfh.st in each round, and a variant always from the entry's own set.DISABLE_ICON_SETS=1switches it all off;NEXTDASH_ICON_SETS_FIXTUREreads a directory instead, for the tests. - new — icons served from a cache of their own.
/data/icon-sets/<set>/<file>(icon_sets_cache.go, a new case indataFileHandler) fetches a file the index names on first use, through the icon guards (2 MiB, magic bytes, SVG sanitised), and serves it from disk after that, asking again after a week. A name the index does not know never leaves the server; a failed fetch is remembered for an hour; the cache holds at most 3000 files per set, since the route is open without the token; an SVG that wraps a PNG is refused, since sanitising leaves it empty.data/icon-sets/stays out of backups, like cached previews. - new — matching by name (
icon_match.go): the image's repository name, the container name, both with packaging words taken off (binhex-,-docker,vpn,-agent, …), without trailing digits, then shorter prefixes (jellyfin-newsletter→jellyfin). A container whose tag moved on reports an image ID and matches on its name. Bookmarks match on the leftmost label of a host with three or more labels, on a one-label host, or on two labels with a private suffix (sonarr.lan,plex.local); a match reads names only, aliases are for search and suggestions;github.comand IP addresses keep their favicon, unless the bookmark is linked to a container.icon_match_test.goruns 62 containers from a real Unraid server. - new — the icon follows the theme.
icon-set-auto.jspicks the variant for a dark background on a dark theme and the one for a light background on a light theme, from--ink-dir, and swaps it ontheme-changedwithout a reload. - new — an icon picker (
icon-set-picker.js, lazy): a search over both sets in a grid, with the variants of the chosen icon underneath. ↓ goes into the grid, the arrows move, Alt+←/→ picks the variant, Enter chooses, Escape closes and gives focus back. Choosing adopts the icon:POST /api/icon-sets/adoptcopies it intodata/icons/(sonarr.svg,sonarr-2.svgwhen the name is taken by other bytes), behind the write token, and from then on it is an ordinary icon of yours.GET /api/icon-sets/search,/suggestandPOST /api/icon-sets/matchanswer the page. - new — credits in About → Colophon for dashboard-icons (Apache-2.0) and selfh.st/icons (CC BY 4.0), with links to both and to the licence.
- fix — a loose alias word no longer picks the icon. An alias such as "maps" or "music" matched any host or container with that word, so maps.google.com got Apple Maps, music.youtube.com Anghami and a container called
appMiro, and "Refresh all favicons" then cleared the favicon those bookmarks had. Matching now reads names only (iconSetIndex.aliasKeys); search and the form's suggestions still offer aliases. - fix — the bookmark form uses the page's own icon. It read the preview's
icon, which is empty on a fresh fetch and a local cached path after it, so every fetch fell back to/favicon.ico; it readsiconSourcenow. - fix — a reload no longer leaves set icons as letters for an hour. A cancelled page load failed every queued icon fetch and stored each as a miss; fetches now run on their own, a cancelled one is not a miss, and different files fetch side by side instead of in one line.
- fix — Refresh all favicons keeps an icon picked in the app-icon picker. It cleared it on a matched address and overwrote it with the site favicon on any other.
- fix — the picker saves the variant it shows. In a dark theme the grid drew the light icon and saved the black one; a click also kept the previous item's variant, and an item without it failed with "Could not use this icon".
- fix — no crash before the icon sets are fetched. The alias fix read the index without checking that there was one.
- fix — an icon the sets cannot deliver lets the favicon through. The match alone blocked the favicon, so a file the CDN would not give left the row on a letter; a variant that fails falls back to the base drawing first.
- fix —
sonarr.lanandplex.localmatch. Two-label names on a private suffix count, as three-label ones do. - fix — the icon cache is capped at 3000 files per set. The route is open to readers without the token, and a walk over the whole index could fill the disk.
- fix — more than 500 rows, or a failed match request, no longer leave rows on letters for the session: asked in batches, and a failed answer is not remembered as "no app".
- fix — two adopts of the same name at once each keep their own drawing.
- fix — cached set icons are asked again after a week, so a redrawn icon or a sanitiser fix arrives; the old file is served if the CDN does not answer.
Containers
- new — an app icon ahead of every container name, from the server's
iconfield on/api/docker/containers; no match shows the letter, and so does an icon that fails to load. nextDash's own container shows the nextDash logo. - new — choose a container's icon in the drawer. The pencil on the drawer's icon offers Choose app icon…, Use letter and Automatic, stored per container name in
Settings.DockerContainerIcons(letteror a file indata/icons/).removeUnusedIconFilekeeps a file a container still uses. - new — Config → Containers in four tabs. Connection, View, Updates and Alerts: the nine panels that stood in one column are grouped by what they are for, with the same strip, keys and memory as Config → Inbox; a setting found by search, a deep link (
#config/containers/<tab>) and the buttons that lead here open the right tab. The Notifications note names the tab its CPU-and-memory switch is on, and the host-address check is bound to its field, so a visit to View no longer adds a listener.
Bookmarks
- new — a bookmark without an icon shows its app's set icon on the dashboard; the rows ask in one batch per render.
- new — the set icon wins over a favicon. When the sets know the address and can deliver its icon, no favicon is fetched: not by the form, the
:newmodal, the browser extension (bookmark-preview-service.js, synced) or the background fill, which no longer counts such a bookmark as missing an icon./api/bookmark-previewsays so insetIcon, worked out per answer and never stored in the preview cache. Fetch again and Refresh all icons drop the favicon a known app had. An icon you chose or uploaded is never replaced. - new — suggestions in the bookmark form. Up to three app icons appear under the address, one click to choose; the card shows the set icon while the bookmark has none of its own; Choose app icon… heads the pencil menu. The inbox keeps fetching favicons: it does not draw set icons.
- new — a bookmark as a QR code, to open it on a phone.
Shift + Jon a row, QR code in the right-click menu (an inbox row gives the item's own link, as Share does), and in the Bookmarks view the row menu and Details → Address. The window shows the name, the code and the address, with Copy URL. The code is drawn in the browser by qrcode-generator 1.4.4 (MIT, vendored instatic/vendor/qrcode-generator/, lazy-loaded on first use), so the address goes nowhere; it is always black on white with a four-module quiet zone, whatever the theme, and an address too long for a QR code says so (bookmark-qr.js,tests/bookmark-qr.spec.js). - new — a bookmark's icon has a container's three choices. A pencil on the icon in the Bookmarks view's side panel opens Choose app icon…, Use letter and Automatic, the one in force ticked — the same pencil and menu as the container drawer's head. Automatic is no icon of its own, as before: the app's set icon, else the favicon. Use letter is a new field,
Bookmark.IconMode("letter", else empty): the dashboard row keeps the letter instead of asking for a set icon, the favicon fill and Refresh all skip it (collectIconCandidates), and the bookmark form shows the letter and fetches no icon unless Fetch again is pressed. An icon of its own always wins: the server drops the mode whenever one is set (normalizeBookmarkIconMode, on every save and patch path).bookmark_icon_mode_test.go,tests/bookmark-icon-mode.spec.js. - new — the side panel's ⋯ menu under headings, as the Collection menu has them: Checks (detect redirect, reporting, snooze, merge), Refresh (title, favicon), Copies (archived, local) and Elsewhere (share, show on dashboard), with Delete apart at the foot. A group with nothing in it is left out; Refresh title is there for a bookmark without a finding too (
renderBmMoreMenuItems,renderBmHealthActionstakesonly). - fix —
:removeputs the bookmark in the trash. The command palette deleted it for good once the toast closed, and its undo posted the page as it was before, so a bookmark added in those seconds vanished. It is now recorded in the trash and undone from there, as a delete on the page is; the remote delete from the inline editor is recorded too. - fix —
:pin,:tagand:notesay so when nothing was written. A bookmark not found on its page was posted back unchanged and reported done;:noteshowed "Note saved." from another save. - fix — an inbox item's QR code opens the saved page, not this dashboard's inbox link.
- fix — the QR window's label keeps a
$&in the address as written. - fix — bulk Fetch icons leaves a chosen letter alone.
- fix — Refresh favicon and Fetch icons know an app that shows its set icon. It said "No favicon found" and every such row counted as failed; the "Without an icon" filter leaves out a chosen letter and an app with its set icon.
- fix — Automatic in a bookmark's icon menu fetches the favicon back for an app the sets do not know; it only cleared the icon.
Widgets
- new — sixteen more services for the Custom widget: Whisparr, LazyLibrarian, NZBHydra2, Komga, PhotoPrism, Jellystat and Mylar3 under Media; Nginx Proxy Manager and Tailscale under Network; Duplicati under System; and a Monitoring group with Beszel, Netdata, Gatus, Uptime Kuma, Scrutiny and Healthchecks (
dashboard-widget-presets.js). qBittorrent 5.2 gets a preset for its API key beside the sign-in one. - new — services that sign in for a token (
health_credentials.go). A sign-in can post JSON and read a token out of the answer:CredentialSessiongainsformat,tokenPath,tokenHeader,tokenPrefixandextra. The token travels in the header the preset names, with or without a prefix, and is kept and renewed like the cookie (once more after a 401 or 403). An answer without a token -- a wrong password that said 200, a two-factor account -- reads "could not sign in" and is not retried in a loop. Nginx Proxy Manager, Pi-hole v6, Duplicati and Beszel use it; a password-only sign-in asks for no username. - new — counting in a figure's path (
customWidgetLookup).list#is the length of a list (or the keys of an object),list[key=value]#the number of entries that match, and[-1]the last entry. A selector key may be a short path ([results.0.success=false]). Still one value per path; a count is the last step. - fix — Proxmox's CPU read as about 0%.
data.cpuis a share from 0 to 1 and is now read as one. - fix — the *arr queue's second figure is "matched", not "downloading": Sonarr, Radarr and Lidarr count queue items matched to the library there.
- fix — Pi-hole v6 signs in itself. It took a pasted session id that lapsed after half an hour; it now signs in with the password and keeps the id.
- new — the presets follow their services. Overseerr/Jellyseerr is Seerr (same API, same id). Tautulli takes its key in a header instead of the address, Nextcloud reads with its monitoring token (
NC-Token, with the OCS header sent for you), Bazarr counts wanted episodes and films and throttled providers (/api/badges), ntfy shows its message count (/v1/stats). Speedtest Tracker shows whether the last test was healthy and when it ran; SABnzbd its time left; Traefik its middlewares; Glances its load; Paperless its tags; Seerr its total. The Immich note says the key must be an admin's. - new — retired presets. Readarr (stopped upstream), Pi-hole v5 (API removed in v6) and TrueNAS (REST API removed in 26) are no longer offered; a widget already started from one keeps working and still shows its choice.
- fix — a widget's own key survives Save. After Health had loaded the credential names, Config → Widgets read the widget's key as "none" and the next Save deleted it; re-picking the Plex preset and saving without a key typed replaced the stored key with its Accept header alone.
- fix — a key typed again keeps the preset's fixed header. After a reload, a new Plex or Nextcloud key went out without
AcceptorOCS-APIRequest, and the stored entry lost it. - fix —
"NaN"and"Infinity"are not numbers. A NaN on a meter made the whole answer fail to encode, an empty reply cached for the widget's TTL. - fix — Ask now with a wrong password fails. The draft reused the session signed in with the stored password; the password is now part of the session key.
- fix — a sign-in session is renewed after an hour. Beszel (PocketBase) answers an expired token with an empty list rather than a refusal, and the tile showed "0 systems" for good.
- fix — a million reads as 1000000, not 1e+06, in text figures, in
[key=value]matches and in lists, which now print objects as JSON. - fix — counting a list that came as
nullgives 0, not "not in the answer". - fix — a tile's error is held for 30 seconds, as on the server, not for the widget's whole refresh interval.
- fix — a widget's own key is deleted after the page save. Deleted first, a failed save left the widget pointing at a key that was gone; this holds for Delete, bulk delete and switching the sign-in off.
- fix — places keep a temperature's unit and leave a duration or a date in words. With decimals set, 21.5 lost its °C and a day showed as 86400.0.
- fix — a relative date reads
2024-05-01 12:00:00and Unix seconds written as text. - fix — widgets refused at once sign in once between them. Each threw the shared session away and signed in again, a seat each on Pi-hole.
- fix — a widget's session no longer follows a redirect to another host or port.
- fix — typing a new key turns Save on. The key alone did not count as a change, so a rotated key could not be saved without touching something else.
Alerts
- new — Apprise as an alert service. Downtime alerts, certificate warnings and container notices can go to apprise-api, and from there to everything Apprise reaches: mail, Matrix, Signal, Teams and a hundred more. Pick Apprise under Status → Downtime alerts and give the notify URL of a configuration key (
http://apprise:8000/notify/<key>); the passwords and tokens of those services stay in Apprise. An optional tag (Settings.MonitorNotifyAppriseTag) sends only to the destinations that carry it. The body is Apprise's own (title,body,type,format: text), with an outage asfailure, a recovery assuccessand the rest aswarning(formatAppriseNotification,health_notify_presets.go). Send test alert says what Apprise's refusals mean: 424, it could not deliver to a destination or none matched the tag; 404, there is no configuration under that key. - fix — "back online" after a Re-check. When a Re-check, Retest all or the hourly re-check saw a recovery first, its up sample made the next monitor round compare up with up, and the recovery was never sent. Those paths now announce it before storing the sample (
announceRecoveries). - fix — ntfy alerts keep
?auth=. The button form posted to the server root without the query, and a protected topic refused it. - fix — a backup without tokens and passwords leaves out the alert secrets too.
settings.jsonwent into the ZIP whole, with the alert address (bot token), Pushover's keys, the archive keys and the iCal address; it is written redacted, and restoring such a backup keeps the secrets in use. - fix — muting any copy of a monitored URL mutes it. Alerts go per URL, and a mute on the second page's copy did nothing.
- fix — Retest all writes one sample per URL. A URL monitored on two pages got two per retest, counted twice and with each copy's rules mixed in.
- fix — an unreachable alert service is logged by host. The log quoted the whole address, Telegram bot token included.
- fix — the health picker leaves out sign-ins a check never sends. A widget's session login was offered, and the monitor then checked anonymously and reported down.
- fix — the large view's heatmap and Today bars follow the clock across a DST switch; the 23:00 hour landed in the next day's column.
- fix — Collection health lets go of its charts when it closes, and Statistics no longer keeps every chart it drew.
- fix — the outage card's count and downtime per monitor cover every outage, not only the 25 newest listed (
incidentTotals).
Charts
- new — the container drawer's charts can be read (Containers → a container → Resources). CPU, memory, network and disk I/O over the last hour are drawn with uPlot: one cursor across the four, a tooltip in the chart under the pointer, a time axis, and a drag to zoom (double-click or
0to go back). The arrow keys walk the points with the value read out under the chart,+and−zoom around it, and a screen reader gets the hour as a table. A theme change draws them again in its colours. - new — the collection's course over time reads the same way (Bookmarks → Collection health → Monitors & trend). The 90-day chart, in any of its series, is drawn with uPlot: a date axis and a value axis, both drawn as lines, a tooltip per day, a drag to zoom into a week, the arrow keys with the day read out, and a table for a screen reader. The plain chart stays when uPlot cannot be loaded.
- new — one bookmark's health in large, with uPlot (a bookmark → Health → Open charts, the row menu, or Shift+H). Response time over the period is a line with the p95 as a dashed line beside it, and uptime per day (per hour for today) is bars in the colour of their share. Both have a time axis and a value axis, a tooltip, a drag to zoom, and the arrow keys with the point read out under the chart: while a chart has focus ← and → walk its points, elsewhere in the modal they still go to the previous and next bookmark. A bar says its share and how many checks it holds; a day without checks says so. The period list redraws them, and the plain charts stay when uPlot cannot be loaded. Date axes name the day without the weekday, so the labels no longer run into each other.
- new — outages read per monitor (Collection health → Monitors & trend). The card was a list of every outage, each a name and a run of date, duration and reason that broke across lines. It now says first which monitors went down, how often and for how long in all (worst first), then draws the 30 days as a lane per monitor with each outage a bar as long as it lasted, so outages that fall together line up. The list stays, by day with time, monitor, reason and duration in columns, behind "Show list" (
renderBmHealthModalOutagesCard). - new — every monitor, per day (Collection health → Monitors & trend, under the four cards). The course behind the 24 hours / 7 days / 30 days figures: a bar a day for the uptime of all monitors together, in the colour of its share, and the day's mean response as a line on its own axis on the right. The value axis runs from 90% unless a day fell below it, so a day at 99.4% does not look the same as one at 100%. Hover or the arrow keys read a day out ("22 sep: 87.5% · 300 ms · 400 checks"), and the card's name says the month's average and its worst day. The report carries the days for it (
FleetStats.Days: checks, answered and mean response per UTC day, every monitor pooled by check, maintenance left out). The trend chart above it is a little lower, so the tab still fits on one screen. - new — a monitor's response chart with uPlot (a bookmark → Health → Monitor & history). Response time per bucket with its average as a dashed line beside it, a time axis and a value axis, a tooltip, a drag to zoom, and the arrow keys with the bucket read out under the chart: its time, response, number of checks and state. That line takes the place of the readout the plain chart had, and the plain chart stays, with its readout, when uPlot cannot be loaded.
- new — the score over time with uPlot (Collection health → Overview). The collection's healthy share on a 0–100 axis with dates under it, the lowest day marked in red, and every day read out by the pointer or the keys ("lowest" on the low one). Kept compact, and the overview is fitted again once it is drawn, so it still fits on one screen.
- new — the healthy share in Statistics with uPlot (Statistics → Overview and Health). The same fixed 0–100 axis and the same gaps for days without a report, plus a date axis, a tooltip and the arrow keys; the chart's own table replaces the old one. The sentence above it is unchanged.
- new — the bar charts in Statistics with uPlot (bookmarks used and opens over time, the collection's growth, the inbox flow per day, and the small one on the Overview). A bar per period with the period's name on the axis, spaced by the widest label so weekly ranges do not run together; the inbox's added and triaged side by side; a running total (the collection's size, the inbox backlog) as a line on an axis of its own on the right. A tooltip with every series at once, a drag to zoom, the arrow keys with the period read out, and the chart's own table for a screen reader. The axis names beside and under each chart stay, and the plain charts stay when uPlot cannot be loaded.
- new —
NdChart(static/js/shared/nd-chart.js): one wrapper around uPlot for charts over time, so every chart gets the same hover, zoom, keys, table and theme handling. uPlot 1.6.32 (MIT) ships instatic/vendor/uplot/1.6.32/with its licence, fetched only where a chart is drawn (lazyLoadedAssets); its stylesheet andnd-chart.cssare in the views bundle. No CDN, no CSP change. When it cannot be loaded, the drawer draws the plain charts it always did. About → Colophon credits uPlot. The screen-reader table sits in a hidden wrapper, because a table ignores the 1px height and would otherwise stretch Collection health into a long empty scroll. - fix — a URL monitored on two pages counts once in Collection health and Statistics: monitors, outages, uptime and the per-day chart counted it twice.
- fix — the 30-day collection figures and "Every monitor, per day" read the day summaries, not only the checks kept one by one (about a week at a 5-minute interval).
- fix — a bookmark's large view counts its whole 30 days. "Uptime, last 30 days" covered only the checks kept one by one; the incident count stopped at 5, downtime summed those 5, and the list showed the oldest first.
Unraid
- new — an Unraid server to read, set once.
Settings.UnraidServersholds one server (address, self-signed switch, read and alert switches); its API key lives apart indata/unraid-secrets.jsonat 0600 and travels in a backup only with "Tokens and passwords" (unraid_settings.go,backup.go). Test answers recorded by hand underinternal/app/testdata/unraid/. - new — the Unraid API, asked without a library.
unraid_client.goposts one GraphQL query with the key inx-api-key, through the same guarded transport as other outgoing requests, 8 seconds and 1 MB at most, no redirects (one to another address, such as https or myunraid.net, is named so it can be typed instead); a refused field (FORBIDDEN) keeps the rest of the answer, a query the schema refuses is told apart, and the key never appears in an error.NEXTDASH_UNRAID_FIXTUREanswers from files, for the tests. - new — queries built from what the server knows.
unraid_schema.goasks the API once a day which fields its types have, and builds each query from those, so a field a newer or older Unraid lacks is left out instead of failing the whole answer; an area that cannot be drawn without it says so. - new — the answer, turned into what a tile draws.
unraid_model.goconverts Unraid's kilobytes to bytes, treats a sleeping disk's missing temperature as asleep rather than an error, and decides once what is a problem — disabled or any other state but OK, missing, read errors, hot (45 °C for a spinning disk, 60 °C for an SSD or NVMe), full (the disk's own usage warning, else 90 %) — so the tiles and the alerts agree. - new — one answer per area, shared.
/api/unraid/area/{area}(array, parity, shares, VMs, UPS, notifications, info and an overview built from them, side by side) asks the server at most every 30 seconds per area for every viewer together, keeps the last good answer with its age when the server does not answer, and waits longer after a 429./api/unraid/settingsand/api/unraid/testset and try the connection behind the write token; the key is never in an answer (unraid_poll.go,handlers_unraid.go). - new — Unraid alerts reach you. With "Send Unraid alerts through the alert channels" on, Unraid's own ALERT notifications, the array stopping, a parity check that finished with errors and a disk whose error count went up go through the same channels as downtime and container alerts — ntfy, Apprise, push and the rest — bundled the same way; the first look after a start only remembers, and so does the first after the address changes or a switch goes off and on, so one server's history is never sent as another's news; a finished parity check is read from the history entry of that run, since Unraid clears the check's own error count when it completes (
unraid_notify.go). Checked against a real Unraid 7.3.2 server, whose answers are recorded, anonymised, underinternal/app/testdata/unraid/recorded-*.json. - new — seven Unraid widgets in the catalogue, in a group of their own (How is the Unraid server doing?): Unraid (an overview), Unraid array, Parity, Shares, VMs, UPS and Unraid notifications. Each has only how it draws — refresh (30 seconds at least), rows where it lists, and whether a click opens the page in Unraid; the server they read is set once, under Config → Unraid.
- new — the Unraid overview and array tiles. The overview reads a line per subject — array, used, parity, disks, alerts, VMs, UPS — with the worst problem in colour, and leaves out what the key may not read; when the server is out of reach it shows the last reading with its age, and it says so when the key is refused. Wide, it adds the newest alert. The array tile, wide, lists every disk grouped as parity, array and cache with its fill and temperature or its problem, a sleeping disk grey; narrow, only the problems, or "N disks fine" (
dashboard-widget-unraid.js). - new — Parity, Shares, VMs, UPS and Unraid notifications tiles. Parity shows the last check, or the running one with its progress, speed and time left, and its history when wide; Shares groups the shares by where they live — the array, a cache pool, or both — with each place's fill and the shares on it, since Unraid reports a share's used and free as those of its place (twenty-six identical percentages on a real server); VMs says how many run and which are paused or stopped; UPS shows charge, runtime and load and turns amber on battery, or says the server has no UPS; notifications list the unread ones newest first, the alerts red, and a notification's link opens on the server only when it is a path there (
//hostis not). Each says so when the key may not read it or this Unraid version lacks it. - new — Config → Unraid, a section of its own in the config menu, since only the Unraid widgets read this server and the Containers view does not. It explains itself: How it works draws nextDash and the server with data running between them and ticks off the three steps (a Viewer key, the connection tested and saved, a widget on a page) from what the server says; What you get shows the seven widgets in miniature, each with an Add… that opens Widgets → Types on that kind with its Add focused (
dashboard-config-unraid.js). One place for the Unraid server every Unraid widget reads: its address (the Docker bridge gateway is suggested when nextDash runs in a container), an API key with the role Viewer, a switch for a self-signed certificate, one for reading the server and one for sending its alerts. Test connection names the server, its Unraid and API versions and what the key may read; a new address asks for the key again. - new — a script to record the API.
scripts/unraid-record.pyasks a real Unraid server for each area with a Viewer key and writes anonymised answers tointernal/app/testdata/unraid/recorded-<area>.json: hostname, share and VM names, notification texts and the UPS model replaced; disk names (disk1, parity, cache), sizes, states and counts kept. It stops at a redirect rather than send the key on. - new — the Unraid server is set in one place only. A settings import, and the dashboard's own settings save, leave it as it is; only Config → Unraid changes it, so no file can point the saved key at another address. An overview the key may read nothing of now says so, instead of "not in this version"; a server name is cut by characters, not bytes; a server without virtual machines says so on the VMs tile.
- fix — a one-column Shares tile cut its percentage off. The figure after the bar was pushed past the tile's edge, and "array + cache" was shortened to "array +…". On a narrow tile the figure now takes only the room it needs and the bar gives way first (
widget-unraid.spec.jsmeasures every figure inside a 290px tile). - docs — a short Unraid section in the README and the manual: what nextDash reads, the seven widgets, the alerts and the one read-only connection, and that deeper Unraid integration is planned. The README shows it with two screenshots from demo data: the seven widgets on a homelab page (Tarnished Brass) and Config → Unraid (Nordic Frost).
- docs — themes up front. README opens with what the look studio offers — 320 themes in 160 families, twelve characters, 26 backdrops, twelve looks, Compare, 🎲 and Apply, favourites and the theme editor — and the manual's table at the top points to it.
- docs — sixteen new README screenshots, each in a theme of its own and none of them used before, from the same demo data: containers with their app icons, the dashboard and a homelab page, the Bookmarks view and its Broken filter, Collection health, search, tags and the command palette, the inbox and triage, the look studio, and Config → Appearance and Statistics. The captions follow what each view does now; the old screenshots are replaced.
- docs — Config → Help catches up, in all six languages: 41 Custom-widget services with the new Monitoring group, twenty-nine widget kinds with a paragraph on the seven for Unraid, and Config → Containers' four tabs with a pointer to Config → Unraid.
- fix — a paused parity check is not a finished one. The API leaves
runningnull and saysPAUSEDin the status; read as done, the pause sent "Parity check finished with N errors" and the tile showed the previous run. - fix — a failed resolver no longer reads as a stopped array. The API answers it with HTTP 200 and the field null; read as an empty answer, it alerted "The Unraid array stopped" and blanked the tiles. It now counts as unreachable, and the last good answer stays.
- fix — a second Unraid push no longer replaces the first without a sound: each alert has its own tag.
- fix — a disk's warning is its usage threshold, not a temperature. A per-disk warning of 70 % made the disk "hot" at 70 °C; it now marks the disk full at 70 %.
- fix — an address without
http://is refused. Save stored it as blank, dropped the key and said Saved. - fix — emptying the address no longer saves the Docker gateway. The suggestion filled the field on every save, so clearing a saved address pointed nextDash at another server and dropped the key without the warning.
- fix — an Unraid notice without a subject is named by its title or description, not "Unraid is offline".
- fix — a restore forgets the previous Unraid server's answers and schema, and drops a key the backup did not carry when the restored address is another one.
- fix — Test connection says what is wrong when the server answered: a key that may not read the server's info, or an Unraid version whose API lacks what nextDash reads, no longer reads as "The server did not answer".
- fix — the schema is kept per address. A lookup still running against the old address after a save was joined, and cached for a day, for the new one.
- fix — the address and its key change together. A poll between the two writes of a save could send the old key to the new address.
Web search
- new — search the web from the search panel. Shift+Enter, or a click on the last row (Search the web: …), sends the query to
/api/web-search(web_search.go), which asks SearXNG's JSON API (web_search_searxng.go) or the Brave Search API (web_search_brave.go). The engine sees the server, never the browser. Nothing goes out while typing, and a command, a finder or a query with a filter in it never goes out at all. Five seconds per answer, at most 20 results, and an answer cached for a minute per address, category and query. - new — results under From the web (
search-web.js, loaded on first use), below the bookmarks: Web, News and Video, plus IT with SearXNG, on Shift+←/→; → opens a column with a result's title, address, date and text, ← closes it; a result from a host you keep says In your bookmarks: ‹name› and Alt+Enter opens the bookmark; an infobox becomes a card. A result loads nothing until opened — no favicons, no thumbnails. On an error or an empty answer the panel names the reason and offers Open in ‹engine›. - new — Recent on the web. The last eight web queries, in this browser only (
nextdash.webSearchRecent), as a folded group in the empty panel with a row to clear them. A web search is not written to the activity log, and the server keeps no record of it. - new — Config → Behavior → Keyboard & search → Web search. Engine (Off by default, SearXNG, Brave Search API), the SearXNG address, the Brave key and Test connection, which searches for test and reports the count or the reason.
Settings.WebSearchEngineandWebSearchSearxngURL; the key lives apart indata/web-search-secrets.json, is never sent back to the browser, and travels in a backup only with Tokens and passwords. - new — the address bar can search the web. While an engine is on, the page links
/opensearch-web.xml, a second engine named ‹title› Web that opens/#search?web={searchTerms}with the web results already asked for; with the engine off the description answers 404. - fix — Web search answers follow the SearXNG address. The cache ignored it, so Test said Working for a broken new address and the panel showed the old instance's results.
- fix — a row picked while web results load keeps the selection. The answer moved it to the first web result, and Enter opened a web page.
- fix — one failed status check no longer removes the web search tabs until a reload.
- fix — SearXNG engines that could not answer are a failure, not "Nothing found" cached for a minute.
- fix — Shift+←/→ also leaves a web category that failed.
- fix — no "Open in SearXNG" row without a SearXNG address; it opened Brave.
- fix — the two OpenSearch engines keep different names under a long title.
- fix — the cheat sheet lists the web search keys (Shift+Enter, Shift+←/→, Alt+Enter) when an engine is on.
- fix — the search keys try again after the search bundle failed to load, instead of staying dead until a reload.
Theme editor
- new — every backdrop in the theme editor, as tiles. The backdrop row of Shape & character offered nine of the patterns in a list; it now shows Automatic and all 26 as tiles drawn in the colours of the theme being edited. The list comes from the server (
/api/themes/metagainsbackdrops), so a new recipe appears without touching the editor. - new — your own themes stand out. In the theme browser they come first under Your themes, with a yours badge, a Yours segment and the search word
yours; a packaged theme whose colours you changed wears recoloured. The Theme picker in Appearance lists Your themes and Built in as two groups, and says · yours when one of yours is in use.themeMetagainsown,recolouredandlook. - new — a theme of your own can carry a look.
ThemeColors.Look(theme_look.go) holds what a built-in look sets: backdrop and its sliders, card glass, depth, headers, type and spacing. The server keeps only parts the page can draw and drops a look on a packaged theme; a theme file carries it. - new — picking a theme brings its look. A card in the theme browser (saved on Apply), the Theme picker and
:themeapply it; switching halves with Quick mode or the system's dark mode does not, and neither does Random theme. Use this theme's look above the grid picks a theme without it, remembered per browser. Theme looks and built-in looks go through one function,applyLookAnswers. - new — the theme editor in the theme browser. ✎ Edit on a card of your own, ✎ Recolour on a packaged one, or
eon a focused card opens the editor in the panel, with ← Themes back to the grid and a link to it on the other tabs. Colour changes show on the page as you type and are saved on Apply; Cancel, Compare and Reset tab include them. A theme of your own gets This theme's look: Take the look on screen and Clear, written to both halves of a pair. - new — Save as theme… at the bottom of the theme browser turns what is on screen into a theme of your own: the colours of the theme in use (a recolour included), the look if you like, and its other half from the real packaged half when it has one. It is saved at once and opens in the editor; picking it still waits for Apply.
- new — the theme browser widens from its left edge. Drag the edge to make the panel wider, or focus it and use ← / → (Shift for bigger steps); a double-click gives the default width back. It never goes narrower than the default, where the six tabs fit, and always leaves 240 px of the page beside it. The width is kept per browser; on a phone the panel stays a bottom sheet.
- new — the theme browser's keys on the cheat sheet. A section of its own in the
!modal and on the printed sheet: ← / → for the tabs, the arrows over the cards, Enter to choose,eto edit or recolour,\held to compare, ⌘/Ctrl + Enter to apply and Esc to cancel. In all six languages. - fix — the printed cheat sheet named its font by this computer's path.
generate-cheatsheet.cjswrote an absolutefile://URL intonextDash-cheatsheet.html; it is now relative to the sheet, so the HTML is the same wherever it is generated. - docs — the theme editor in the manual and in Help, in all six languages: §16.1 and the theme browser section describe the badges, the editor in the panel, the look switch and Save as theme…; §16.5 the 26 backdrop tiles, a theme's look and the picker groups.
- tests — the theme editor: Go for the meta's backdrops,
ownandrecoloured, andsanitizeThemeLook(ranges, unknown words, an empty look, looks on packaged themes); Playwright inconfig-custom-theme-character.spec.js(27 tiles, a look through export and import),config-theme-browser-yours.spec.js,theme-look-on-pick.spec.js,look-studio-theme-editor.spec.jsandlook-studio-save-as-theme.spec.js. - fix — saving the colours keeps the Neutrals collection. One save emptied the chip for good; installs already stripped are repaired on load.
- fix — deleting the theme in use falls back to a real theme. It set
default, which is no theme id, and the page lost every colour; the half shown under Follow system counts as in use too. - fix — Save as theme and Duplicate keep the backdrop on screen. A built-in's recipe is chosen by its id, and the copy drew another.
- fix — a new or edited theme shows its depth, glow and effects at once, not after a reload: the cache of
/api/themes/metais refreshed after a colours save. - fix —
:theme, the random theme and an OS light/dark switch bring the theme's surfaces, not the previous theme's depth, glow, backdrop and card glass. - fix — surface changes for "this theme" show under Follow system and a random theme. They were stored under the chosen theme and drawn from the one on screen.
- fix — Save as theme gives a typed name a number when it is taken, and caps it, as a rename does.
- fix —
:themelists themes made or deleted since the page loaded. - fix — a light/dark pair keeps the theme's own surfaces, and deleting a theme clears its rows in the settings.
- fix — a look picked outside the studio draws its backdrop variant at once, not after a reload.
- fix — the other half made by Save as theme brings the look too.
- fix — an imported look with part of its backdrop tuning takes the defaults for the rest, not zero.
Config
- new — Config → Overview as panels. The section was eight figure tiles, four explained blocks, a news band and a colophon line, all framed and titled alike. It is now one attention line and two zones. Your install has a panel each for bookmarks, the inbox (unread, arrivals per day over two weeks, the oldest waiting link), containers (running, stopped, unhealthy, updates; from the list search keeps, left out without a Docker socket), health (the ring and the four states) and statistics (opens this week from the open log with two weeks of bars, the most-opened link, the busiest page and the cleanup score with its heaviest deduction). From nextDash holds the three newest posts from nextdash.cc, the three newest announced features with the running release and Show what's new under them, and a tip of the day from Help → Tips that steps back and forward in place. Every panel has the same shape — title with a state dot, a way in, its own warning in the foot — and Needs attention is a row of chips (now with container updates) instead of a block of sentences. (
renderOverviewand therenderOverview*Widgetfamily indashboard-config.js,config-view.css). - fix — the sub-tab strip no longer jumps between sections. The band's line under the section name is cut to one line (the whole sentence on hover) and keeps its height when a section has none, so a long line such as Bookmarks' no longer pushed the rail and the tabs down. Statistics' tabs sat 2px lower and its rule 3px lower, beside a scope picker taller than the tabs; both line up with the other sections now.
- tests — the overview spec follows the panels: counts, the attention chips and where they lead, the clean line, the cleanup reason, the four health states, the containers panel from stubbed Docker routes and its absence without a socket, the newest dated features and where the first one leads, the tip stepping in place and All tips landing on Help → Tips, and All news landing on About → News.
- fix — Keep settings on this device only leaves web search and container icons to the server. An old device copy turned web search off for every browser or put old icons back; the Docker keys not yet on the list are added too.
- fix — Config → Unraid can be reached from the command palette (
:config unraid). - fix — clearing the cheat sheet filter opens the section of the view it came from again.
- fix — Only changed and the settings filter stay in sections with a bar for them; carried into Containers they hid settings with no way back.
- fix — the Overview tip buttons keep keyboard focus.
- fix — no What's new explanation starts mid-sentence. An item whose bold part carries on ("X, and Y." or "X — Y.") was split at the bold, and the line under the title opened with a comma, a dash or a lower-case word, in 143 items across 33 releases.
splitItemTextnow drops a dash or colon and capitalises what follows, and joins a comma or a lower-case continuation to the title up to its full stop. - fix — the arrow keys keep walking Appearance's tabs. Appearance repaints more than once after its tab changes, and every repaint after the first left the focus on the page: the second arrow press did nothing. A strip drawn again just after a keyboard switch now gives the focus back to the tab.
Tours and tips
- new — the dashboard tour opens on what is new in this release, and every reader sees it once more. Four steps come first, each with a moving drawing: web search (a search, your bookmark, the web results with their tabs, and Shift+Enter, Shift+←/→ and Alt+Enter), app icons (letters giving way to the apps' icons, and the ✎ menu), the theme editor (the panel widening from its left edge, a recolour, Save as theme…) and the Unraid widgets (the array with fill and temperature, a running parity check, the shares). The theme browser step loses its "New:". Twenty steps in all, in six languages; the scenes rest on their finished picture with less motion, as before. The tip id moves to
dashboardTutorialV3everywhere it is named (the tour,DashboardPromos, the Onboarding list in config and search, the test fixtures and helpers), so a reader who finished V2 is offered it again, after What's new.dashboard-tutorial.spec.jschecks the twenty titles, that a reader who saw V2 gets it opening on web search, and that the new scenes carry no English. MANUAL's tour line follows. - new — the Containers tour covers v1.17, and runs once more for everyone. Two new steps, each with a moving drawing: every container with its app's icon (letters giving way to icons, and the ✎ menu in the side panel) and the Resources charts (a cursor walking an hour of CPU and memory with its values, a stretch dragged out to zoom, and the keys). The Config step is redrawn for the four tabs — a row each for Connection, View, Updates and Alerts, with the selection walking down them — and its text follows. Fifteen steps instead of thirteen, in six languages; a label longer than its room is drawn narrower (
fitLabel) rather than over its neighbour. The tip id moves tocontainersTutorialV3everywhere it is named, andPREVIOUS_TIP_IDSreplacesPREVIOUS_TIP_ID: a step'ssince(2 or 3) against the newest earlier tour the reader took decides which steps carry New, and the update note lists what came since — a V1 reader gets both lists.containers-tutorial.spec.jschecks the fifteen titles and the marks for a V2 and a V1 reader; the contrast spec walks fifteen steps. - new — a tip for the QR code. Want a link on your phone? under Editing and organising in Config → Help → Tips (
tipEditQrCode, six languages):Shift+Jor QR code in the right-click menu, drawn in the browser so the address goes nowhere. MANUAL gets the same as a tip under the right-click menu, where QR code was already listed. - fix — the cheat sheet catches up. Four rows it lacked, in the
!modal and on the printed sheet, in six languages: the theme browser's left edge (←/→, Shift, Home, End), a chart's keys in the Bookmarks view and in Containers → Resources (←/→,+/−,0), and the app icon picker (↓into the grid, the arrows,Alt+←/→for the variant). The web search rows get short print labels, andnextDash-cheatsheet.html/.pdfare regenerated — the printed sheet had not had the web search keys at all.
Translations
- docs — web search in all six languages, in Help, Tips and the manual. The 38 strings of web search and the three cheat-sheet rows for its keys, English since the feature landed, are in Dutch, German, French, Chinese and Spanish; the not configured message names the real tab, Behavior → Keyboard & search. Config → Help: Searching the web and the second address-bar engine under Searching your bookmarks, web search in Behavior's Keyboard & search line, A wider panel in Themes, and alert secrets left out of a backup without tokens in Data — all six languages. Tips: web search under Finding things and a wider theme browser under Making it yours. MANUAL: Searching the web in §8.3, the web engine in §8.4 and the address table, the Keyboard & search row, the theme browser's width, the alert secrets and the Unraid and Brave keys under what a backup carries,
:removeinto the trash, an Unraid disk full at its own usage warning (hot at 45 °C or 60 °C), an Unraid address that needs its scheme and what Test connection reports, and the icon sets' private names, weekly re-ask, 3000-file cap and favicon fallback. - docs — the overview panels and the icon menus in all six languages. The 35 strings of Config → Overview's panels, attention line and tip, and the three new headings of the side panel's ⋯ menu, are in Dutch, German, French, Chinese and Spanish; they had been English fallbacks in the code, with no locale key behind them.
- docs — Help, README and the manual describe them. Config → Help: the Overview line under Configuring, the side panel's head (the ✎ on the icon with its three choices, the ⋯ under its headings) in the Bookmarks view tab, and choose an app icon in the bookmark form's ✎ — in all six languages. MANUAL §11.3 and §17.1/§17.8 rewritten for the panels and the two menus, §19.4 names the side panel's pencil; README gains a Config → Overview line and the app-icons line says where the pencil is.
- docs — every new string in all six languages. The 138 strings this release added in English only — the QR code, the Unraid widgets and Config → Unraid, and Containers' tabs — are in Dutch, German, French, Chinese and Spanish, and two that changed (the right-click menu's cheat-sheet line, the container notices' note) are brought up to date;
validate:locale-parity, the placeholder and cheat-sheet checks pass. - fix — thirty strings that never reached a locale file. The app icon picker and the container drawer's icon menu, the Apprise fields, the charts and outage timeline in Collection health, and the chart and icon credits under About called for keys that did not exist, so they stood in English in every language. They are in
en.jsonand translated. - docs — README and the manual describe this release. Twenty-nine widget kinds with the seven for Unraid, 41 Custom-widget services (table rebuilt from the presets, the retired ones named), Apprise among the alert services, app icons and
DISABLE_ICON_SETS, the Custom widget's counting paths and token sign-ins, the charts and per-monitor outages in Collection health, and the two test-only fixture variables. - i18n — Refresh favicon's "shows its set icon" message in all six languages.
Docs
- docs —
static/data/whats-new/v1.17.0.jsonand its index entry, leading the What's new window;whats-new-stub.js'sDASHBOARD_RELEASEmoved to2026.10-dashboard-release-v1.17.0andNEXTDASH_WHATS_NEW_DATA_VERSIONtowhats-new-v315;whats-new-hidden-release.spec.jsfollows. An Overview spotlight for the Unraid widgets, withsincev1.17.0, in six languages.go generaterefreshedasset_hashes_gen.go.
Tests
- tests — Go: the index (rounds, aliases, variants, search), the 62 containers and the bookmark hosts, refresh with ETag, stale copy and back-off, the cache route (unknown names, traversal, one fetch, sanitising, wrapped PNG, the hour of misses), adopt, the API handlers, the container override and the background fill.
- tests — every monitor, per day:
bookmarks-health-modal.spec.jsreads the days through the keys (share, response and checks, a day without checks) and keeps the plain bars when uPlot is blocked; Go pins the pooling per UTC day, maintenance left out, a failed check without a time, and the days carried in the report. - tests — charts, phase 3:
bookmarks-health-tab.spec.jsreads a monitor's buckets through the keys (response, checks, state), checks the average line and the plain chart with its readout without uPlot;bookmarks-health-modal.spec.jsthe score's low point, marked and read out, and its plain chart;config-stats-trend-scope.spec.jsthe healthy share read by the keys, a missing day as a gap and not a zero, and the plain line without uPlot. - tests — charts, phase 4: the Statistics specs that measured the plain bars (
config-stats,-axis-labels,-inbox-trend,-panels) now read the uPlot chart: a row a period in its table, the value axis from 0, the tick labels' places on the canvas (no collisions, none past an edge), the tooltip under the pointer at the top of a column, the keys and their readout, both series' colours, the backlog line on its own axis, and the plain bars when uPlot is blocked.config-stats-panelsalso reads the healthy-share gap from the new chart; it still looked for the old polylines after the phase 3 change. - tests — charts:
docker-drawer-charts.spec.jsandbookmarks-health-modal.spec.jscheck the charts through the pointer and the keys: the tooltip only in the pointed chart while the others follow, the arrow keys and their readout,+and0, the table, a redraw on a theme change, and the plain charts when uPlot is blocked.bookmarks-health-large.spec.jschecks the large modal: the p95 in the readout, ← and → kept in a focused chart, a bar's share and checks, the bars per period, and the plain charts without uPlot.config-about.spec.jschecks the credit, andbookmarks-health-modal.spec.jsthe outages card: the per-monitor order, a lane per monitor with a bar per outage, and the list opened from Show list. - tests — Apprise:
notify-apprise.spec.jspicks Apprise in the panel, fills in the URL and a tag and presses Send test alert against a stub apprise-api, checking the body that arrives and the sentence shown for a 424; Go tables cover the type per event, the tag and the refusals. - tests — presets against recorded answers:
widget-presets-recorded.spec.jspoints every preset still offered at a local stub that answers with its service's documented response (tests/fixtures/widget-presets/, source in each), through the route Ask now uses, and fails on a figure that finds nothing or finds a whole object. The fixture holds the service's side of the sign-in, so a wrong header or prefix in a preset fails too. Through the panel itself: retired presets are not offered, and Pi-hole v6 asks for a password only and signs in on Ask now. Go: counting and the last entry, and the token sign-in (prefix, no prefix, password only, renewal, no token). - tests — Playwright:
icon-set-picker.spec.js(a suggestion, the picker by keyboard, Escape, offline, Fetch again, dashboard rows),docker-icons.spec.js(row icons, Use letter across a reload, Automatic, the picker, the theme switch) and a credits test inconfig-about.spec.js. - tests — the P1s of bug hunt round 3: a paused parity check, a failed resolver, a recovery seen by a Re-check, an alias match,
:removethrough the trash, and a widget key kept across Save after Health and after a re-picked Plex preset. - tests — the P2s of bug hunt round 3, each one failing without its fix;
config-docker-usage-alertsopens the Alerts tab since Containers has tabs. - tests — a selection of the P3s of bug hunt round 3, each one failing without its fix.
- tests — the remaining P3s of bug hunt round 3:
config-carried-state.spec.js, and Go tests for the Unraid, icon, widget, web search and look fixes. - tests — CI follows the release: Containers tabs in
docker-auto-updateanddocker-drawer-charts, twenty tour steps indashboard-tutorial-contrast, Unraid indashboard-command-palette-config, the backdrop switched off through the setting intheme-backdrop-variety, the favicon outcome stubbed inhealth-bulk-fetching, andconfig-subtab-keyboardwaits for the focus before the next key.