Skip to content

v2026.07.19

Choose a tag to compare

@jordibrouwer jordibrouwer released this 20 Jul 20:25
· 1436 commits to main since this release

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.json as enableUsageAnalytics, so the choice follows you across devices.

    Or from the keyboard. :telemetry shows the current state with on / off rows; :telemetry off and :telemetry on go straight there. It writes the same enableUsageAnalytics setting 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 (also 1, yes, on) to switch analytics off for the whole server, whatever individual users have configured. The tracker is never emitted, POST /api/settings and the :telemetry command 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, :telemetry no longer offers an on row that would reload the page and silently change nothing. It shows a single row — off — disabled for this server by DISABLE_TELEMETRY — and pressing Enter does nothing, matching the note the Privacy checkbox already carried. The dashboard learns this from a nextdash-telemetry-locked meta 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-5 instead 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. showStatus is 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 itselfAdd 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 its anyTagged() helper and itemTag/itemTagHint strings in all four locales.

New installs

  • new Favicon harmonisation is on for fresh installs — new dashboards now start with favicon harmonisation enabled (muted, intensity 0.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 no themeIconStyling map 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 onlyConfig → 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 new DeleteAllBookmarks() store method and POST /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 incorrectlyGetting 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:tour and :promo were 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.filterBookmarks string that existed in no locale file, so its label and placeholder stayed English in Dutch, German, and French. Of the 682 data-i18n references 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-in allowHtml flag for this, since tip strings carry <code> markup — it stays textContent by 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-warning instead 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, Enter on + N more, and :telemetry.

  • new :favicons fetch re-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 fetch walks 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 reuses ConfigFaviconPrefetch rather than adding a second implementation; the class is now exported on window and its overlay CSS moved out of the config-only stylesheet into css/favicon-prefetch.css so 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() and startEmpty stays 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-icons gained refreshAll and offset. 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), so refreshAll walks the candidate list by offset instead. PrefetchIconUpdate gained an Overwrite flag: 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 _copyUrlToClipboard with its green row flash and execCommand fallback, edit calls openBookmarkInlineEditor(), tags and move open the existing popovers. Delete routes through showDeletePopover() rather than deleteBookmarkInline(), so a single menu click is never one gesture away from irreversible data loss. Arrow keys and Enter navigate the menu, Escape and 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 on Shift + right-click. Works on smart-collection rows too, which carry no data-bookmark-index and are resolved by URL instead. Built on the existing .move-popover surface so it inherits theme variables with no colours of its own, and each item reports bookmark: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 Enter or Space, 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 so Home/End keep meaning "first/last bookmark" rather than landing on the trailing button, and syncRovingTabStops() treats the button as its own focus target since it has no inner a.bookmark-open to 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 filter is-overflow-hidden, so rows trimmed by the category item limit stayed in the navigable list even though they are display: 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-danger rules referenced var(--accent-danger, #c0392b), but --accent-danger is 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-error first, 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: none and pointer: 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 .forEach on 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 both dashboard-setup.js and search.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 .btn rules 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 ETag and honour If-None-Match (304 when unchanged, a fresh 200 when the assets change). A small app-version-guard.js additionally 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 by sessionStorage so it can never loop.