v2026.07.19
Privacy-friendly, anonymous usage analytics — nextDash now measures which features are used and where the app can be improved. Never who you are, never what you bookmark. On by default, off in two clicks. Also: a right-click menu on bookmarks, and long categories that can finally be expanded from the keyboard.
Privacy
-
new Privacy-friendly analytics — nextDash records anonymous usage statistics through a self-hosted Umami instance (
stats.nextdash.cc).Why. The project has had no idea how it is actually used. Which views do people open? Does anyone use finders, the tag cloud, the inbox? Where do people abandon the add-bookmark form? Without answers, every improvement is guesswork. These statistics exist to answer exactly that — which features get used, and what can be made better — and nothing more. They are explicitly not for following individual users: it is abstract, technical measurement of flow and feature usage, aggregated across everyone.
How to turn it off. Config → General → Advanced → Privacy → clear Privacy-friendly analytics. It applies after the page reloads. When off, the tracker script is not emitted into the page at all, so it is never even downloaded and no request leaves your machine. The setting is stored per user in
settings.jsonasenableUsageAnalytics, so the choice follows you across devices.Or from the keyboard.
:telemetryshows the current state with on / off rows;:telemetry offand:telemetry ongo straight there. It writes the sameenableUsageAnalyticssetting as the Privacy checkbox and reloads the page, since the tracker<script>is emitted server-side and only a fresh page can load or unload it.Instance-wide kill switch. Set
DISABLE_TELEMETRY=true(also1,yes,on) to switch analytics off for the whole server, whatever individual users have configured. The tracker is never emitted,POST /api/settingsand the:telemetrycommand cannot turn it back on, and the Privacy checkbox renders disabled with a note pointing at the environment variable. Stored per-user preferences are deliberately left untouched, so they return unchanged if the variable is later unset. Commented out in both compose files.The command says so too. With the kill switch on,
:telemetryno longer offers an on row that would reload the page and silently change nothing. It shows a single row — off — disabled for this server byDISABLE_TELEMETRY— and pressing Enter does nothing, matching the note the Privacy checkbox already carried. The dashboard learns this from anextdash-telemetry-lockedmeta tag, the same way it already receives the write token.What is measured. Page views; opening the health and inbox views; which config tab you land on; opening search, commands, finders, the cheat sheet, the tag cloud, what's-new, and the add-bookmark form; bookmark opens with their source (dashboard, search, or recent); which command palette command was run, validated against the built-in command list so free text is never sent; bookmark maintenance (edit started and saved, with its surface; delete; category move with a bucketed size; drag reorder); inbox triage and health actions; and the outcome of adding a bookmark (created, duplicate, shortcut conflict, invalid, error). Once per page load a snapshot records which features are switched on, as plain booleans and small enums.
What is never measured. No bookmark names, URLs, search queries, page or category names, notes, or tags. No cookies, no personal profile, no cross-site tracking. Counts that could be identifying are bucketed (for example
2-5instead of an exact number). The instance is self-hosted, so nothing goes to an advertising network.
Getting started
-
new Starting point step on first run — the quick-start setup card gained a fourth step asking whether to keep the example bookmarks or begin with an empty dashboard. Keeping them is the default, so clicking straight through behaves exactly as before. Choosing Start from scratch wipes every bookmark on every page (reusing the delete-all endpoint) while leaving pages, categories, finders, and settings untouched, and no default bookmarks are recreated.
showStatusis switched off at the same time, since status checks have nothing left to monitor. On a first run the wording says "example bookmarks"; on a rare wizard re-entry with your own bookmarks present it drops that word, so the prompt never promises to only clear samples. -
fix Quick-start checklist no longer ticks itself — Add your first bookmark and Tag a bookmark were both shown as done before the user had done anything: they tested for any bookmarks and any tags at all, and a fresh install ships seven example bookmarks that already carry tags. The checklist now records a baseline (how many bookmarks and tagged bookmarks existed when setup finished) and only ticks on growth past it. Captured in
refresh()rather than at render time, since the checklist can appear before the first page load resolves and a count of zero taken then would credit every seeded bookmark to the user; the baseline defaults to-1("not captured") in the server defaults and a migration, because Go's zero value would read as a real baseline of none. The tagging step is dropped altogether — it is not a first-run task — along with itsanyTagged()helper anditemTag/itemTagHintstrings in all four locales.
New installs
-
new Favicon harmonisation is on for fresh installs — new dashboards now start with favicon harmonisation enabled (
muted, intensity0.5), so site favicons that clash with the theme are desaturated to match instead of shouting over it. The setting is keyed by the displayed theme id, and auto dark mode — also on by default — swaps between the dark and light Moss & Stone, so both variants are seeded; with only one entry harmonisation would silently apply for half the day. Existing dashboards keep whatever they had: the defaults only apply when nothemeIconStylingmap is stored, and turning it off survives a restart. Change or disable it under Config → Theme. -
new Moss & Stone is the default theme — a fresh install now starts on
moss-stone-dark, a desaturated earthy olive-grey palette, instead of Midnight Ink. Set in both server defaults and the three client-side fallbacks so they cannot drift apart. Existing dashboards keep their theme: the only other theme writes are fallbacks for an invalid id, not a migration.
Config
-
new Delete all bookmarks only — Config → General → Reset now offers a second, amber button beside the red full reset. It empties every page's bookmarks while keeping pages, categories, finders, and all settings, and — unlike the full reset — does not recreate the default sample bookmarks. Guarded by a confirmation dialog followed by a typed
DELETE. Backed by a newDeleteAllBookmarks()store method andPOST /api/bookmarks/delete-all(confirmation required, health cache invalidated, activity-logged). -
fix Config → Help caught up with the app — Help's own sections still described an older nextDash: several shipped features were only ever mentioned in the What's new list, never in the sections that actually explain them. Added across EN/NL/DE/FR: privacy-friendly analytics and how to turn them off, including
:telemetry(Self-hosting & security); both reset buttons and what each one keeps (Data & backups); the first-run Starting point step and the empty-dashboard state (Getting started); the per-category item limit shipped in v2026.07.18 and how to reach its toggle from the keyboard (Appearance); the bookmark right-click menu (Organizing); and the occasional keyboard tips, alongside the what's-new and quick-start controls they share a settings section with (Getting started). All of it is searchable through the Help filter like the rest of Help. -
fix Help described the quick-start card incorrectly — Getting started said the card walks through "three short steps" and that the checklist covers "add a bookmark, tag one, open config, and look at the keyboard shortcuts". Neither matched the app: the card has had four steps since Starting point was added, and the tagging item was dropped from the checklist in this release. The opening sentence also contradicted the same section's own later paragraph. Corrected in all four languages.
-
fix The cheat sheet listed two commands that no longer exist —
:tourand:promowere removed in v2026.07.17 along with every tour, spotlight, and promo balloon, but the keyboard cheat sheet kept advertising them, so the app's own shortcut reference pointed at commands that answer No matches found. Every command the cheat sheet lists was checked against the palette's registrations and aliases; these two were the only dead entries. -
fix Bookmarks filter field was never translated — the search box on Config → Bookmarks referenced a
config.filterBookmarksstring that existed in no locale file, so its label and placeholder stayed English in Dutch, German, and French. Of the 682data-i18nreferences in the templates it was the only one missing. The placeholder gets its own key rather than reusing the label's, because it carries a trailing ellipsis that the translation pass would otherwise have dropped. -
fix Stale French-only Help text — the French translation still carried two paragraphs describing the per-tab guided tours and spotlights removed in v2026.07.17: a whole paragraph in Pages, categories & bookmarks, and "visites guidées" in the System & tools summary under Configuring. EN/NL/DE were updated at the time; French was missed. All four languages now match again.
Dashboard
-
new Occasional keyboard tips — the dashboard now surfaces a single cheat-sheet tip as a toast, with a Cheat sheet button that opens the full list. The tips are not a new catalogue: they are read from
window.ConfigHelpTips.TIP_GROUPS, the same translated strings Config → Help → Tips & tricks renders, so the two cannot drift apart. Given that v2026.07.17 deliberately removed every tour, spotlight, and promo balloon, this is kept deliberately rare: a tip only appears after a multi-day gap (discoverabilityState.tipsNotBefore, server-side so it holds across devices and browsers), each tip is shown once ever (discoverabilityState.seenTips), and it is suppressed during first-run onboarding, on touch, while a modal or the inline editor is open, and whenever a quick-start or analytics card is already on screen.enableSessionTips(default on) turns it off under Config → General → Advanced → Onboarding & tips.AppNotification.show()gained an opt-inallowHtmlflag for this, since tip strings carry<code>markup — it staystextContentby default because ordinary toasts interpolate bookmark names. -
new "New shortcuts" section in What's new — releases can now carry a section with
"kind": "keys", rendered as key/description pairs tinted with--accent-warninginstead of the usual green new / grey fix badges, so the keys that shipped in a release stand out from the prose. v2026.07.19 lists right-click,Enteron + N more, and:telemetry. -
new
:favicons fetchre-downloads every bookmark icon — the palette could toggle favicon visibility but never fetch them; the only ways to pull icons were the per-bookmark action in health, a bulk button on a config selection, or the automatic prefetch that runs after an import.:favicons fetchwalks every page and re-fetches the icon of every bookmark, replacing the ones already stored so stale icons are refreshed too, with the same progress overlay the post-import prefetch uses. It reusesConfigFaviconPrefetchrather than adding a second implementation; the class is now exported onwindowand its overlay CSS moved out of the config-only stylesheet intocss/favicon-prefetch.cssso both pages share it.Also runs once after first-run setup. Finishing (or skipping) the quick-start card with the example bookmarks kept triggers the same full fetch, so a new dashboard shows real site icons straight away rather than whatever the seed data shipped with. Skipping lands here too — it calls the same
finishSetup()andstartEmptystays false — while choosing Start from scratch skips it, since there are no bookmarks left to fetch for. It starts 400 ms after the card's fade-out so the progress overlay is not hidden behind it, and a failure only warns: setup is already complete by then.POST /api/bookmarks/prefetch-iconsgainedrefreshAllandoffset. Progress in the default mode is measured by how many bookmarks still lack an icon, which cannot work when re-fetching (a refreshed bookmark still has one), sorefreshAllwalks the candidate list by offset instead.PrefetchIconUpdategained anOverwriteflag: the merge step deliberately skips bookmarks that already have an icon so the background prefetch can never clobber a user-chosen one, and only this command sets it. -
new Right-click menu on a bookmark — right-clicking a bookmark row opens a menu with Open in new tab, Copy URL, Edit, Tags…, Move to…, and Delete. Nothing here is new behaviour: every action already existed in the command palette, the inline editor, or config, but the row itself offered only open (click) and inline edit (long-press), so the rest was undiscoverable. Each item delegates to the existing implementation rather than duplicating it — copy reuses
_copyUrlToClipboardwith its green row flash andexecCommandfallback, edit callsopenBookmarkInlineEditor(), tags and move open the existing popovers. Delete routes throughshowDeletePopover()rather thandeleteBookmarkInline(), so a single menu click is never one gesture away from irreversible data loss. Arrow keys andEnternavigate the menu,Escapeand outside clicks close it, and scroll or resize dismisses it (a pointer-anchored menu cannot meaningfully reposition). It stays out of the way where the browser's own menu matters: not while the inline editor is open, not over a modal, and never onShift+ right-click. Works on smart-collection rows too, which carry nodata-bookmark-indexand are resolved by URL instead. Built on the existing.move-popoversurface so it inherits theme variables with no colours of its own, and each item reportsbookmark:context-menu {action}. -
new The "+ N more" toggle is reachable from the keyboard — the per-category item limit from v2026.07.18 hid its overflow behind a toggle that only a mouse could reach, so a long category was a dead end for keyboard users. The toggle now takes part in grid navigation: arrow onto it, press
EnterorSpace, and the category expands or collapses. Afterwards the selection lands on the last bookmark above the toggle, so arrowing down continues into the rows that were just revealed instead of jumping elsewhere.getCategoryRows()excludes the toggle soHome/Endkeep meaning "first/last bookmark" rather than landing on the trailing button, andsyncRovingTabStops()treats the button as its own focus target since it has no innera.bookmark-opento delegate to. Bookmark-only actions (Shift+M/D/T,;,[,Delete,Ctrl+C) already bailed on a missing open link, so they no-op on the toggle and leave the selection intact. -
fix Keyboard selection could land on invisible rows —
_isNavigableRow()did not filteris-overflow-hidden, so rows trimmed by the category item limit stayed in the navigable list even though they aredisplay: none(9 of them on a stock dashboard). Arrowing down through a capped category moved the selection onto a 0×0 element: the highlight appeared to vanish and further presses were blind. Hidden rows are now excluded, and the show-more toggle is what the selection stops on instead. -
fix Danger styling in row popovers ignored the theme — the
.move-popover-item.is-dangerrules referencedvar(--accent-danger, #c0392b), but--accent-dangeris not defined by any theme, so the delete entries in the move, tag, and delete popovers always rendered the hardcoded fallback red. They now resolve--accent-errorfirst, which every theme defines, and follow the palette in both light and dark. -
fix A narrow desktop window is no longer treated as a phone — resizing a browser window used to switch nextDash to the mobile layout: the footer dropped to search-only, the toolbar lost commands and finders, modals turned into full-screen sheets, and a banner suggested opening nextDash on a computer while you were already on one.
isMobileLayout()tested width alone, and the portrait-tablet rule was especially easy to hit because a narrowed window is usually taller than it is wide. All three entry points now require an actual touch device (hover: noneandpointer: coarse). Phones and tablets are unchanged; dashboard column stacking below 767px stays, since that is ordinary responsive layout rather than a device assumption. -
fix Empty dashboard no longer fails to load — with zero bookmarks,
SearchComponent.buildShortcutsMap()ran.forEachon a null list during init, so the whole dashboard bootstrap threw and rendered Failed to load dashboard, leaving the page unresponsive. The search bookmark source now falls back to[]in bothdashboard-setup.jsandsearch.js. The fresh empty state also reads properly now: an Add a bookmark link that opens the modal, plus Manage bookmarks in config (/config#bookmarks) and Import your data, styled locally since the global.btnrules are config-only and bare anchors rendered as raw browser links. -
fix Stale pages after an update — a browser could keep serving an HTML shell from its cache and run the previous release's JavaScript, so new features stayed invisible until the cache was cleared by hand; Safari hit this most often because the shell had no validator to revalidate against. The dashboard and config shells now carry a content-based
ETagand honourIf-None-Match(304 when unchanged, a fresh 200 when the assets change). A smallapp-version-guard.jsadditionally reloads once when a page is restored from the back/forward cache, or when the shell's baked-in asset fingerprint no longer matches/api/app-version— guarded bysessionStorageso it can never loop.