Releases & release notes

What changed, version by version.

Honest notes for every shipped release — what we changed, what we fixed, and the couple of decisions we want to flag. Newest first.

Full history: CHANGELOG.md on GitHub · Latest release: v1.0.23

  1. v1.0.23 published
    • Added #194 — New "Blue" theme (dark navy surfaces + blue accent). A third selectable theme alongside Dark/Light/System, with a navy slate neutral scale and a blue accent (--accent-9 #3b82f6), giving a VS Code "Dark+" feel. Added as a .theme-blue token block in src/styles/globals.css; only the raw neutral/accent scales are overridden, so the surface-elevation and legacy semantic aliases resolve through var() against them. Wired through ThemeContext (theme type + <html> class application), settingsStore (theme union), and the Appearance theme dropdown. Because the Glide results grid and Monaco editors render to <canvas>/their own DOM and can't read CSS var(), each gets a dedicated blue palette: GridView.tsx gains a navy grid palette (literal hex mirrored from the tokens), and a shared src/utils/monacoThemes.ts defines a custom queryden-blue Monaco theme (navy chrome over vs-dark syntax colors) now used by the SQL editor, Compare diff, and Definition viewer.
    • Fixed #193 — Light-theme dialog text was nearly invisible. In the Light theme, Settings/Help dialog labels and titles rendered as faint light-grey on white. index.html hardcodes a dark boot style (html, body { background:#111113; color:#edeef0 }) for the pre-React splash; AppLayout overrides it for the main app, but SettingsDialog/HelpDialog render as siblings of <AppLayout> (src/App.tsx) — outside that override — so their un-classed text inherited the dark-theme #edeef0 on light surfaces. Fixed by making body color/background theme-aware in globals.css (.theme-dark/.theme-light/.theme-blue body); the theme class is on <html>, so these win on specificity and the boot splash (which runs before the class lands) is unaffected.
    • Changed #192 — Migrated all remaining native <select>s to the shared Select primitive. Native <select>s render an OS popup list that ignores the app theme (notably unreadable on Windows dark/blue), which is why the Select primitive (Radix-backed, #151) exists. The Appearance → Theme control plus 12 others were still native: 9 in SettingsDialog (editor font, keymap preset, suggest/qualify objects, CSV delimiter & quote char, AI model, copy method & logging level), the transaction isolation selector in Toolbar, the column-type selector in CreateTableDialog, and the profile selector in ConfirmDialog. The two selectors whose "DEFAULT"/"No Profile" option uses an empty-string value (which Radix Select forbids) now map "" through a non-empty sentinel in both directions. The vestigial database "selector" in Toolbar (single option, no-op onChange) was replaced with plain text rather than migrated.
  2. v1.0.22
    • Fixed #187 — Results data grid now matches the design system. The Glide data grid in src/components/ui/GridView.tsx still used the pre-design-system palette — accentColor: #06b6d4 (old cyan), Tailwind-slate cell/header backgrounds (#0f172a/#1e293b/#334155) that read bluer than the Radix Slate neutrals, sky-blue date cells (#38bdf8), and indigo binary-cell labels (#818cf8) — so the table looked off-theme next to the now-tokenized results toolbar and buttons. Remapped the entire grid palette to the globals.css tokens (neutral scale for surfaces/text/borders, --accent-9/--accent-11 for the selection accent and date/binary text, --success-11/--danger-11 for numeric +/− coloring, --success-9/--warning-9 tints for new/modified rows), for both dark and light themes. The grid renders to <canvas> and can't read CSS var(), so the values are literal hex mirrored from the tokens with per-line comments mapping each back to its variable.
  3. v1.0.21
    • Fixed #179 — App window wouldn't close (X button / Alt+F4 did nothing). The unsaved-queries close handler from #121 registers getCurrentWindow().onCloseRequested(...), and Tauri v2's JS implementation of that listener auto-calls window.destroy() after the handler unless preventDefault() was called — so it routes every OS close through destroy(), including the common no-dirty-tabs case. destroy() requires the core:window:allow-destroy permission, but the capability file only granted core:default (whose core:window:default set is read-only getters), so the IPC was denied and the window silently stayed open. A regression: before #121 added the listener, the native close path closed the window directly. Fix adds core:window:allow-destroy to src-tauri/capabilities/default.json. Reported on Windows; affected all platforms.
    • Fixed #180 — Boot splash now matches the design system. The splash in index.html still used the pre-design-system palette — background #0b0f17 and a cyan→purple wordmark gradient (#06b6d4#8b5cf6) — which clashed with the running app, whose accent is solid Radix Cyan --accent-9 #00a2c7 with no purple anywhere. Retuned the splash to mirror the tokens in src/styles/globals.css: background #111113 (--neutral-1), text #edeef0 (--neutral-12), wordmark an in-brand cyan gradient #00a2c7#4ccce6 (--accent-9--accent-11), accent glow/drop-shadow rgba(0,162,199,…), tagline #696e77 (--neutral-9). Also aligned the native window backgroundColor in tauri.conf.json ([11,15,23][17,17,19]) so there's no color flash before the webview paints. Values are hardcoded hex because the splash renders before CSS variables exist.
  4. v1.0.20
    • Added #144 — In-app "What's New" tab in the Help dialog. Help → What's New now shows the bundled CHANGELOG.md as a list of per-version cards, newest first, with a "Current" badge on the version the user is running. CHANGELOG.md is bundled into the build via Vite's ?raw loader and parsed by a new pure helper src/utils/parseChangelog.ts (7 Vitest cases). Each version block renders via the existing react-markdown + remark-gfm deps that the updater prompt already uses, so opening a new external dep was unnecessary. Pairs with #143 — the updater prompt was already rendering update.body as markdown (lines 175-186 of UpdateNotification.tsx); #143 fills the manifest's notes field with real CHANGELOG content, and this PR adds the standalone browse surface.
    • Fixed #159 — Create Database / Create Table dialogs now focus the name field on open. After the Dialog migration the modal focused its own panel on open, immediately stealing focus from the autoFocus'd name input. Both dialogs now route the name field through Dialog's initialFocusRef, so the cursor lands in it and users can type straight away.
    • Fixed #146 — CHANGELOG.md was missing the ## [1.0.19] - 2026-05-20 heading. v1.0.19 shipped on 2026-05-20 (commit d8384e6, PR #108) but the release content sat under ## [Unreleased] without ever being promoted to its own versioned section, so the website's /changelog page misattributed v1.0.19 entries and any tooling that extracts the Unreleased block (notably the beta release-notes work in #143) would have bundled v1.0.19 content into beta notes forever. Inserted the heading at the correct boundary.
    • Fixed #142 — Docs page "Edit on GitHub" link no longer 404s. website/src/pages/docs/[...slug].astro was passing sourcePath={entry.id} to DocsLayout, but Astro 6's content collections no longer include the file extension in entry.id (the existing .replace(/\.(md\|mdx)$/, '') in getStaticPaths is a leftover no-op from earlier Astro versions). The resulting URL pointed at …/docs/getting-started/install instead of …/docs/getting-started/install.mdx, which GitHub couldn't resolve. Fix appends .mdx to the prop explicitly (and extracts the regex-stripped slug to a local so currentSlug and sourcePath stay in lockstep).
    • Changed #152 — Design-system Phase 2: core surfaces migrated to shared UI primitives + token scale. The main-pane chrome (breadcrumb, query toolbar, tab strip), the results panel (toolbar, row-op cluster, binary/detail overlays, history), and the Database Explorer (three context menus, header, tree rows, and the Backup/Restore/Move dialogs) now use the Phase-1 primitives (Button, IconButton, Input, Select, Dialog) and the 12-step token scale instead of raw <button>s and hardcoded colors. Added a presentational Menu/MenuItem/MenuSub primitive (#165) for context menus. Mostly an internal consistency pass, but it also carries through to users: native <select> dropdowns that ignored the dark theme on Windows are replaced by themed popovers (#151), and interactive controls get consistent hover/focus/cursor affordances. Shipped across #163, #164, #165, #167, #168.
    • Changed #143 — Beta releases now ship real release notes from CHANGELOG.md. Previously both the GitHub release body and beta.json's notes field used a generic placeholder ("Beta build from main. See …/commits/main for changes"), which the in-app updater would show users when offering a beta — they had to leave the app to find out what was in the build. .github/workflows/beta.yml's publish job now extracts the ## [Unreleased] section from CHANGELOG.md and uses it as the source of truth for both surfaces. If [Unreleased] is effectively empty (no entries, only section headers), the workflow falls back to a git log of commits since the last beta-latest. The release body additionally prepends the rolling-tag disclaimer plus the resolved beta version, so anyone landing on the GitHub release page knows what they're looking at. Pairs with #144 (in-app changelog viewer) — improving server-side notes is the prerequisite for showing useful content in the updater dialog.
    • Changed #140 — Beta builds now run nightly from main (03:00 UTC), instead of only when manually dispatched. Added a schedule: cron "0 3 *" trigger to .github/workflows/beta.yml plus a tiny should-build gate job that runs before the Win/Mac/Linux matrix. The gate skips the build when main HEAD already matches the commit beta-latest points to — so idle days don't burn ~3 platform-runs of CI for an unchanged manifest. Manual workflow_dispatch always bypasses the skip (the user explicitly asked for a build). No change to the manifest URL, the channel UX, or the version scheme — beta-opted users start receiving daily builds instead of waiting for a maintainer to fire the workflow.
    • Changed #17 — MongoDB removed from the connectable-engine picker; scope clarified as a mongosh launch target, not a connected engine. The driver picker previously listed MongoDB as a coming-soon tile, implying a first-class connected engine (schema browser + results grid + query editor) was on the roadmap via that path. It isn't — QueryDen has no MongoDB driver and the only Mongo touchpoint is the mongosh CLI launch flow in src-tauri/src/cli.rs (ToolKind::Mongo), which lets users open mongosh against a saved Mongo URI. Removed the tile from src/config/providers.ts (and the now-unused SiMongodb icon import) so the picker no longer over-promises; left an in-source comment pointing at the mongosh path for future contributors. The cli.rs mongosh launch path is unchanged.
    • Changed #116 — Database Explorer view-mode is now a discoverable popover, not a 3-state cycle button. Clicking the folder-icon button in the sidebar header now opens a small popover listing Folders / By type / Flat (radio dots show the active mode) plus a + New folder action — replacing the previous click-to-cycle behavior where users had to click 2–3 times to discover that folders even existed. Folders is now the default view mode (was previously auto-detected from folders.length, with new users landing on Flat); with no folders defined the render degenerates to the same flat layout, so it's not a regression. The standalone + button that only appeared in Folders mode is removed — the popover is now the one place to come back to for both switching modes and creating folders. Docs page updated.
    • Fixed #51 — Data grid detects date/time cells by SQL type, not column name. GridView previously decided whether to render the datetime-local overlay editor based on whether the column name contained the substring "date" or "time". That missed real datetime columns whose names didn't (effective_from TIMESTAMP, birth DATE, scheduled_at TIMESTAMPTZ) and false-positived on text columns whose names did (update_time_label TEXT, date_format_preference VARCHAR). Plumbed the column SQL type from the explorer's already-loaded schema introspection (tableDetails) through run-specific-queryMainContentResultsPanelGridView, and extracted the decision into a pure isDateTimeType(sqlType, columnName) helper in src/utils/columnTypes.ts that matches on the real type (DATE/TIMESTAMP/TIMESTAMPTZ/TIME/MySQL DATETIME, plus the PG timestamp without time zone / precision-suffixed forms). When the type isn't known — ad-hoc query results that aren't backed by a single introspected table — the helper falls back to the legacy name heuristic so existing behavior is preserved. 15 unit tests cover the type-resolution surface.
    • Fixed #103 — Removed the broken "Capture" / "Upload" screenshot control from Help → Log New Issue. The attach control was structurally non-functional: the submit handler opens GitHub's web issue page via ?title=…&body=… URL params (or a mailto: link in the email-fallback flow), and neither URL params nor mailto: can carry binary attachments. The Upload path was the worst offender — it read the image into local React state and then dropped it entirely on submit (no clipboard write, nothing). The Capture path did attempt a navigator.clipboard.write([ClipboardItem]) after rendering the DOM through an SVG foreignObject, but a) cross-origin stylesheets/fonts fail to inline in production WebViews so the render was unreliable, and b) the dialog never told the user that paste-on-the-GitHub-page was actually required. Net effect for the reporter (Windows + WebView2 + v1.0.18): "I want to add an image when logging the issue, but it doesn't work" — exactly correct. Replaced the capture/upload column with a static "Attach an image?" callout that tells the user to drag-and-drop (or paste) into the GitHub page after it opens, and updated the post-submit confirmation screen to repeat the instruction. No new deps, no new IPC commands, no new permissions. Larger fixes (Tauri-side multipart upload to GitHub via PAT, or a paste host) are out of scope; if we want true binary attachments later, that's a follow-up issue.
    • Fixed #11 — Removed unwired DataGrip preset bindings from Settings → Keymap. The default keymap was advertising shortcuts that the app never actually handled — pressing Shift Shift, Ctrl+Shift+C, Ctrl+Shift+N, Ctrl+Shift+H, Ctrl+Shift+V, Shift+F6, Ctrl+B, Ctrl+F12, or Ctrl+PageUp/Ctrl+PageDown did nothing because no global or per-component handler was registered for them. Removed the corresponding entries (globalSearch, openConsole, newConsole, localHistory, clipboardHistory, rename, goToDefinition, structureView, nextTab, previousTab) from defaultKeymaps[0].actions in src/store/keymapStore.ts. The Settings UI renders the preset table directly off this map, so the removals propagate automatically — settings no longer makes promises the app can't keep. Bindings that are wired (execute, executeAll, format, databaseExplorer, settings, find, findInFiles, formatCode, plus Monaco's editor-internal commands) are kept untouched. When one of the removed features ships, its binding gets added back alongside the handler.
    • Fixed #115 — Update-notification bell stops the icon swap + flashing pulse. The bell previously swapped between <Bell> and <BellDot> based on state (looked like a glitch) and laid an animate-ping ring over a small amber dot at offsets that hung outside the icon's bounding box (visually noisy + not aligned to the icon). Replaced with a single stable <Bell> icon plus a small static var(--color-accent) dot that sits inside the icon's top-right, with a ring of var(--surface) around it so it reads as a deliberate badge against the chrome. Standard "unread"-style indicator — same pattern as VS Code / Slack / GitHub.
    • Fixed #112 — macOS dropped from latest.json / beta.json — auto-updates broken on Mac. Pre-existing bug in release.yml (since the very first release that shipped an updater manifest) that PR #106 propagated into beta.yml when copying the manifest-generation block. The glob looked for aarch64.app.tar.gz, but Tauri's macOS update bundle is named QueryDen.app.tar.gz — no architecture in the filename — so MAC_TGZ stayed empty and the platform was silently dropped from both manifests. Consequence: macOS users on stable never got an in-app update offer (had to manually re-download), and macOS users on the new beta channel saw nothing at all. Discovered while sanity-checking the first beta build for #105 dogfooding. Fix: drop the arch filter to .app.tar.gz (we only build aarch64 today, so the wildcard is safe). Applied in both workflows. Takes effect the next time each manifest is regenerated — the next stable tag push and the next beta workflow run, respectively.
    • Added #121 — Confirm before quitting with unsaved queries. Closing the app window (X button, Alt+F4, Cmd+Q) when any query editor tab has unsaved changes now opens a "Unsaved queries" confirm dialog listing the dirty tabs by name (capped at 5, with "…and N more" beyond that) and offering Discard (red, closes the app) or Cancel (keeps the app open). Dirty detection (src/utils/editorDirty.ts, 8 tests) compares each tab's current query against an originalQuery snapshot captured when the tab is created and refreshed on Ctrl+S save — so empty untitled tabs stay clean, untitled tabs with typed content are dirty, and saved tabs are dirty only after the text actually diverges. Scope is intentionally narrow for this PR: only the unsaved-query case (not unsaved connections / settings / running queries), only an all-or-nothing Discard (per-tab Save is out), and autosave hookup is deliberately deferred to #122. Wired via getCurrentWindow().onCloseRequested from @tauri-apps/api/window; the existing ConfirmDialogProvider renders the prompt so no new dialog component was introduced.
    • Added #110 — Show/hide eye toggle on password inputs. Connection dialog (main DB password, SSH password, SSH key passphrase) and vault credential dialog (in Settings → Credential Vault) now wrap their password fields in a reusable <PasswordInput> component (src/components/ui/PasswordInput.tsx) that overlays an Eye/EyeOff lucide button on the right. Click toggles the input between type="password" and type="text". Visibility state is per-instance and not persisted across renders — every dialog re-open starts hidden. Toggle is in normal tab order so keyboard users can reach it. AI API key field deliberately not converted: it has a left-side icon that would clash with the right-side toggle without further refactoring; can revisit if asked.
    • Fixed #109 — Vault-vs-manual credential picker no longer fires on manual connections. DatabaseExplorer.tsx's connect path was prompting "Select Credential Profile" whenever the user had any vault credential stored, regardless of whether the clicked connection was configured as manual (isVault === false) or vault. Tightened the prompt condition to conn.isVault === true && !conn.vaultCredentialId && vaultCredentials.length > 0 — manual and legacy-untagged connections now skip straight to direct connect.
    • Fixed #102 — Wired Monaco's editor worker to silence the "Could not create web worker(s)" console warning in release builds. Added a self.MonacoEnvironment.getWorker factory in monacoSetup.ts that returns Vite's ?worker-bundled editor.worker. Diff / search / link computation now runs off the main thread; previously Monaco logged a warning and fell back to main-thread execution. CSP already permitted this via the existing worker-src 'self' blob: directive in tauri.conf.json — no policy change needed.
    • Fixed ux(settings): Update channel radio dot is now centered. The custom radio in Settings → Updates used a m-[3px] hack on the inner dot that overflowed the outer border-box (6px dot + 6px margin > 10px inner space after the 2px border), pushing the dot down-right. Replaced with flex centering on the outer span — looks correct at any size.
  5. v1.0.19

    A two-feature release

    A two-feature release. The big one is #104 hierarchical connection folders — you can finally group servers in the Database Explorer the way you've been asking for, with arbitrary nesting depth, safe cascade-reparent on folder delete (nothing is ever lost), and machine-bound encrypted persistence. The other is plumbing for the #105 beta update channel — flip Settings → Updates → Beta to opt into signed pre-release builds from main once the next beta is published. Bug-class summary: a critical SemVer-ordering bug in the beta channel pipeline (caught in review on #106) was fixed before this version shipped — betas correctly use the next-patch base so they sort above current stable. A separate data-loss chain in folder persistence (caught in review on #107) was fixed before shipping — load_folders no longer collapses parse failures into "no folders", and the save effect refuses to enable itself unless the load actually succeeded.

    • Added #105 — Beta update channel. New Settings → Updates section with a Stable/Beta toggle, defaulted to Stable. Beta-channel users receive signed pre-release builds from main via a new beta-latest rolling GitHub pre-release tag, with the manifest served at releases/download/beta-latest/beta.json. Channel selection happens Rust-side because the tauri-plugin-updater JS check() doesn't accept an endpoints override — a new check_for_update_on_channel Tauri command rebuilds the updater with the right endpoint per-call, registers the resulting Update in the same webview resources table the plugin's download/install commands read from, and returns the upstream Metadata shape so the JS Update handle keeps working unchanged. The About dialog and the Report-Issue payload now include the active channel so beta regressions can be triaged correctly. New .github/workflows/beta.yml (workflow_dispatch-only initially) builds the existing Win/Mac/Linux matrix, signs with the same key as stable, deletes-and-recreates the beta-latest tag, and regenerates beta.json. Version scheme: betas use the next-patch base (<base+1>-beta.<ts>+<sha>) — e.g. while stable is 1.0.18, betas ship as 1.0.19-beta.X. This is deliberate: SemVer says a pre-release suffix sorts below the same normal version (1.0.18-beta.X < 1.0.18), so using the current stable as the base would mean opted-in users could never actually receive a beta. Bumping the patch base keeps 1.0.18 < 1.0.19-beta.X < 1.0.19. Reverting from beta to stable is documented as requiring a manual reinstall — the Tauri updater has no downgrade path.
    • Added #104 — Connection folders. The Database Explorer now supports user-defined folders for grouping connections, with arbitrary nesting depth. New folders.json (encrypted + machine-bound, same scheme as query-history.json) stores the flat folder list; StoredConnection gains a backward-compatible optional folder_id so older connections.json files load cleanly. View toggle in the sidebar header now cycles through Flat / By type / Custom folders (replacing the previous 2-state Group-by-type button). Right-click a folder for Rename / New subfolder / Move to folder… / Delete (with confirmation that previews how many connections + subfolders will be reparented to the deleted folder's parent — nothing is ever lost). Right-click a connection for Move to folder… when folder view is active. Cycle prevention lives in a new pure helper src/utils/folderTree.ts (14 tests) and is enforced both client-side (the move picker hides self+descendants) and in the move-folder action (throws on cycle). No drag-and-drop in v1 — context menus + a + button only, deliberately kept narrow to ship.
    • Fixed #97 — Typing a bare table name (e.g. users) now surfaces schema-qualified suggestions (e.g. app.users) in the SQL editor. Latent since the initial commit but only newly visible after v1.0.18's CSP fix made Monaco's completion widget render correctly in release builds. The pre-Monaco filter in QueryEditor.tsx rejected anything whose lowercased label didn't pass startsWith(currentWord). Table/view labels are schema-qualified (app.users), so typing users failed the check and the suggestion never reached Monaco's scorer. Replaced the predicate with a new matchesQualifiedOrBareName helper in completionContext.ts that accepts either the qualified prefix (appapp.users) or the post-dot bare-name prefix (usersapp.users). Bare labels (public-schema tables, columns) keep their existing startsWith behavior. 8 new tests cover qualified-prefix, bare-name, case-insensitivity, prefix-only (no substring flooding), and the empty-word no-op.
    • Changed chore: remove inert Monaco-scoped CSS overrides from globals.css. PR #92 added a .monaco-editor box-sizing: content-box block and a form-element revert block on the theory that Tailwind v4 Preflight was corrupting Monaco's layout. That diagnosis turned out to be wrong (real cause was Tauri's CSP nonce — fixed in v1.0.18). The overrides were inert in production and the revert rules on form elements were arguably making things worse by reverting Monaco's intentional invisibility styles to UA defaults. Deleted — no replacement needed.
  6. v1.0.18

    Hotfix on top of v1.0.17

    Hotfix on top of v1.0.17. The Monaco editor fix shipped in v1.0.17 (#90) didn't actually fix anything — the diagnosis was wrong. After enabling DevTools in a local release build, the real root cause turned out to be Tauri 2's auto-injected CSP nonce on style-src, which silently disables 'unsafe-inline' and blocks every inline style="" attribute Monaco emits to position its DOM. Fixed by opting style-src out of Tauri's nonce augmentation via dangerousDisableAssetCspModification. Also dropped the always-visible Results and History tab chrome when those panels have no content (#95).

    • Fixed #90 — Monaco editor layout is REALLY fixed now (the v1.0.17 fix didn't actually work). The original v1.0.17 fix in PR #92 blamed Tailwind v4 Preflight and added scoped CSS overrides inside .monaco-editor. That theory was wrong — the bug reproduced exactly the same after install, but worked in tauri dev and in browser preview, which led me down a wild goose chase. After enabling DevTools in a local release build to inspect, the smoking gun was sitting in the console: Applying inline style violates the following Content Security Policy directive 'style-src 'self' 'unsafe-inline' … 'nonce-…''. Note that 'unsafe-inline' is ignored if either a hash or nonce value is present in the source list. Tauri 2 auto-injects a nonce into our configured style-src for hardening, and per CSP spec the presence of a nonce disables 'unsafe-inline'. Monaco uses inline style="top: 19px" on every .view-line to position text — every cursor placement, every viewport calculation, every gutter element. With inline styles blocked, Monaco's DOM positions collapse to default (0,0), text stacks on top of itself, and the normally-invisible <textarea class="ime-text-area"> (used for keyboard/IME capture) shows through at its un-positioned default location. Dev builds didn't reproduce because Vite dev's CSP is permissive; browser preview didn't reproduce because there's no Tauri-injected nonce at all. Fix: dangerousDisableAssetCspModification: ["style-src"] in tauri.conf.json tells Tauri not to augment style-src with the nonce, restoring our intended 'unsafe-inline' behavior. script-src nonce protection is kept intact. Also added http://ipc.localhost ipc: to connect-src so Tauri's IPC custom protocol doesn't fall back to the slower postMessage path on every command.
    • Changed #95 — Hide Results and History tabs in the bottom panel when they have no content. On a freshly-connected database before any query has run, the Results panel rendered three always-visible tab buttons (Messages, Results, History) plus a conditionally-rendered Optimizer tab. Results and History were dead chrome until the user ran something. Now Results renders only when results.length > 0 || multiResults?.length > 0, History renders only when history.length > 0, and the active-tab handler falls back to Messages if the current tab's content disappears (e.g., user clears history while on the History tab). Messages and Optimizer behavior unchanged.
  7. v1.0.17

    A hotfix release driven by @kix007's report that the SQL editor was visually broken in v1.0.16 — Tailwind v4's Preflight reset was bleeding into Monaco's internals and corrupting cursor positioning + form-element invisibility (#90). Same release picks up the connection-dialog cleanup also from @kix007 (#44) — denser layout that fits all fields without scrolling, a sticky feedback strip so test errors stay visible above the fold, and a standalone Test SSH Tunnel button on the SSH tab so users can verify bastion credentials before the DB connection ever runs. The pre-save Test Connection button also now correctly routes through the SSH tunnel when SSH is enabled (#46) — previously it ignored the tunnel and tried to reach the DB host directly. Empty-state polish too (#84, #86) — disconnected app no longer shows a fake "Command Center" dashboard with placeholder telemetry, and the help button no longer wears a fake unread-notification dot.

    • Changed #84 — Empty-state launcher replaces the no-connection "Command Center" dashboard. Launching the app with no active database used to render the full query chrome — the Run/Format/Explain/Compare/Clone/Activity/Multi-Query/Save/AI Assistant toolbar, the tab strip, and an empty Messages/Results/History panel at the bottom — even though every action was disabled or pointless without a target database. The middle band held a "Database Command Center" hero with six telemetry tiles whose values were mostly static placeholders (Security · AES-256 vault active, Throughput · Low-latency engine, Storage · DB WAL levels OK); one tile dispatched an open-saved-queries event that had no listener anywhere in the app, so the button had been broken since it was added. MainContent now gates the toolbar, tab strip, and results panel on activeConnection && selectedDatabase and renders a new EmptyStateLauncher full-bleed when either is missing. The launcher adapts: zero connections shows a single New Connection CTA + the supported-engine list; has-connections-but-none-active shows action cards for New Connection and Saved Queries + a sidebar hint. New window events (open-new-connection, open-files-panel) let the launcher drive the existing flows; both listeners live in AppLayout so they fire reliably regardless of which sidebar panel is visible (or whether the sidebar is collapsed entirely). The Add Connection dialog state was lifted from DatabaseExplorer to AppLayout for the same reason — DatabaseExplorer's "+" buttons now dispatch the same event, giving sidebar and launcher a single code path. DatabaseExplorer stays mounted (CSS-hidden) when the Files panel is active so its tree state survives sidebar switches. The six dashboard tiles and the DashboardTile helper are deleted.
    • Fixed #90 — Monaco editor layout broken by Tailwind v4 Preflight. Live reproducer in v1.0.16: the SQL editor rendered with a large left offset between the line-number gutter and text, broken newline advancement, and Monaco's normally-invisible internal <textarea class="inputarea"> (used for keyboard/IME capture) showing through as a visible bordered input at the cursor position. Root cause: Tailwind v4's Preflight reset (in @import "tailwindcss") applies box-sizing: border-box; margin: 0; padding: 0; border: 0 solid to every element including Monaco's internal layout divs, plus font: inherit; background-color: transparent; opacity: 1 to form elements. Monaco was authored against browser defaults (content-box, unstyled form elements with UA-default opacity rules) and breaks under those resets — the box-sizing change corrupts cursor/.view-lines positioning calculations, and the form-element reset makes Monaco's hidden inputarea visible because Monaco relied on default UA invisibility rather than explicit inline styling. Fix is a scoped reset in globals.css that restores box-sizing: content-box and reverts the form-element resets inside any .monaco-editor subtree. Also dropped the unrelated * { transition: background-color 0.2s, border-color 0.2s } rule from globals.css — applies to every element on the page including Monaco's selection/highlight changes, which caused subtle compositing artifacts on selection. 31 of our own files already use explicit Tailwind transition-colors utilities so the practical loss is negligible; any visible regression can be fixed by adding the utility class where needed.
    • Fixed #44 — Connection dialog now fits without scrolling, surfaces errors above the fold, and lets you test SSH in isolation. @kix007 reported that on a default-sized window the General tab pushed the test-failure banner below the fold — the error block lived inside the scrolling form region, so when the form overflowed the dialog (h-[650px]), the user had to scroll to see why their test failed. The SSH tab had no way to verify SSH credentials independently of the DB, so a stuck connection could mean either a bad bastion password or a wrong DB host and there was no way to tell. Three changes: (1) density pass on the details step — p-6 → p-4 outer padding, space-y-6 → space-y-3/space-y-4 → space-y-2.5 between sections, input py-2.5 text-sm → py-1.5 text-xs, labels text-xs mb-1.5 → text-[11px] mb-1, SSH username + auth-method moved into a 2-col row, sidebar w-64 p-6 → w-56 p-4. All fields now visible without scrolling at default size on 1080p, and dialog now uses max-w-[95vw] max-h-[90vh] so it adapts down on smaller displays instead of being a fixed 900×650 box. (2) Sticky feedback strip lifted out of the form's scroll container into a fixed band above the footer — error messages and test-success banners are now always visible regardless of scroll position. (3) Test SSH Tunnel button on the SSH tab, backed by a new sync test_ssh_connection Tauri command in ssh.rs that does just TCP+handshake+auth (no port forward, no DB connect) and returns a clean success/error — so users can verify SSH credentials separately from DB credentials. Also replaced the misleading "Status: Saved / Ready" pill in the left sidebar (which read "Saved / Ready" before any test had actually run) with an honest "Last test: Not run / Passed / Failed" that reflects only what's been observed in the current dialog session — matches the "stop pretending" theme of v1.0.16's release line.
    • Fixed #46 — Test Connection now routes through the SSH tunnel when SSH is enabled. The testConnection function in ConnectionDialog built its DB URL straight from formData.host/formData.port and called Database.load(...) directly — it never read formData.sshEnabled. With SSH enabled, the target DB host is typically unreachable from the user's machine (only the SSH gateway is), so every Test Connection attempt would fail with a network/timeout error — even when the configured credentials, tunnel, and target DB were all correct. Saving the connection and connecting normally worked (the production connect path in ConnectionContext.connectToDatabase did open the tunnel correctly) — only the pre-save test was broken, so users either gave up at the test step or saved-anyway-and-prayed. testConnection now mirrors the production path: when sshEnabled && sshHost && sshUsername are all set (and the engine isn't SQLite), it opens a one-shot SSH tunnel keyed by a __test_<uuid> ID, swaps actualHost/actualPort to 127.0.0.1:<local_port>, runs the SELECT 1 probe through the tunnel, then tears the tunnel down in a finally block so a successful test doesn't leak a long-lived tunnel. A test that can't open the tunnel returns the SSH error verbatim instead of a misleading DB error. No automated regression test — the function depends on Tauri IPC + the @tauri-apps/plugin-sql dynamic import, neither mockable from the current Vitest setup; component-test infra is tracked in #24.
    • Fixed #86 — Removed misleading pulse dot from the help button. The help button (top-right of AppLayout) rendered a small blue dot with animate-pulse on hover. It wasn't bound to any state — no "new help content" counter, no unread flag, just decoration. The visual language (small blue dot, top-right of a button, pulsing) is identical to a real unread-notification indicator, which set up an attention conflict with the neighbouring UpdateNotification bell that is a real notification surface. Deleted the <span>; the existing group-hover:text-[var(--color-accent)] on the HelpCircle icon stays as the hover affordance.
  8. v1.0.16

    A UX and branding polish release on top of v1.0.15's cold-start work

    A UX and branding polish release on top of v1.0.15's cold-start work. The boot splash now uses the actual QueryDen network-graph logo with a "QueryDen" wordmark instead of a generic gradient block (#79). The Windows installer .exe finally shows the correct icon — the .ico was right since v1.0.11 but Tauri's NSIS bundler wasn't picking it up (#80). The pre-paint white flash is gone via a native window background color (#75). Clicking a connection in the explorer now shows a spinner on the right tree node and the status bar stops claiming "Select a database…" mid-connect (#76). The results panel toolbar got a substantial cleanup from @kix007 — Jump-to-column is a searchable keyboard-navigable dropdown, exports collapse into a single Download menu, row actions are icon-only with a dirty-count badge on Save, and Discard now correctly reverts modified rows instead of just hiding the dirty flags (#70).

    • Fixed #75 — White flash on launch eliminated. WebView2's OS-allocated canvas defaults to white before any HTML loads, and no CSS rule on html/body can prevent it because the canvas exists before HTML parses. Set backgroundColor: [11, 15, 23] on the Tauri window config so the canvas is dark from the moment the OS shows the window.
    • Fixed #76 — Connection feedback is now accurate. Clicking a connection in the Database Explorer left the footer status bar showing "Select a database…" for the entire connect attempt, because the conditions in the status renderer weren't mutually exclusive — isConnecting was set, but selectedDatabase was null, so the misleading message visually won. Replaced isConnecting: boolean with connectingConnectionIds: Set<string> so individual server tree nodes show a spinner on the right node, and rewrote the status text as a priority-ordered single string (Connecting to <name>… → Loading database… → Loading schema… → Loading table details…). The "Select a database…" line is gone from the loading bar.
    • Fixed #70 — Discard now actually discards modifications. The PR's original onDiscard swapped the server re-fetch for a client-side filter that stripped _isNew/_isModified flags but did not revert the edited cell values — leaving a phantom-modified state where Save became a no-op. Hybrid handler now re-runs the SELECT when there are real modifications (the only way to recover original values, since nothing in the codebase preserves them), and falls back to the cheap client-side filter when only new (client-only) rows need to be dropped. Preserves the perf win for the common "user added rows, changed their mind" case.
    • Fixed #70 — Jump-to-column now uses the correct column list. Was indexing into columns while the grid renders with displayColumns || columns; in multi-result mode those diverge and clicking a column name jumped to the wrong cell.
    • Changed #79 — Boot splash redesigned. The minimal v1.0.15 splash used a generic gradient block + "Loading…" in tiny gray text — didn't use the actual product logo and looked like a placeholder. Now uses the real network-graph mark (/img/icon.png) at 84px with a subtle cyan drop-shadow and a gentle float animation, "QueryDen" wordmark at 30px with the "Den" half running the same cyan→purple brand gradient, and a small uppercase tagline ("A modern database manager"). The prefers-color-scheme rule was dropped — the app is dark-only at boot regardless of OS setting. Splash is now a sibling overlay rather than living inside #root, so it can fade out via CSS transition with a 700ms minimum on-screen time (so a fast warm boot doesn't flash it for ~80 ms — perceptible but unreadable).
    • Changed #80 — Windows installer .exe shows the QueryDen icon. The .ico file in src-tauri/icons/ held the real logo since v1.0.11, but Tauri's NSIS bundler doesn't auto-promote bundle.icon[].ico to the installer wrapper's own icon. Added bundle.windows.nsis.installerIcon: "icons/icon.ico". Note: Windows aggressively caches file icons in Explorer, so existing users may need to install once before the new icon shows up (or clear the icon cache).
    • Changed #70 — Results panel toolbar refactor (@kix007). Three meaningful UX upgrades: (1) The Jump-to-column control replaces the text input + <select> pair with a single searchable dropdown that supports keyboard navigation (Arrow up/down + Enter), with the active item highlighted and auto-scrolled into view; mouse hover and keyboard cursor stay in sync. (2) The five separate export-format icon buttons collapse into a single Download dropdown with a section header, and Escape closes it consistently with the column dropdown. (3) Row action buttons (Add, Duplicate, Remove, Save, Discard) are now icon-only for compactness, with tooltips on each; Save gains a small emerald dirty-count badge in the top-right corner (capped at "99+") so the at-a-glance signal of pending edits survives the text removal. Discard now shows a confirmation dialog before wiping local changes.
  9. v1.0.15

    A targeted cold-start fix

    A targeted cold-start fix. On Windows, app launch was taking 5–8 seconds of invisible window before the UI appeared, contradicting v1.0.13 / v1.0.14's perf claims. Two compounding causes, both fixed here (#72): every startup load_* IPC call was running a full Argon2id key derivation (~100–250 ms on Windows × 10+ calls = 2–4 s of redundant CPU work, all producing the same 32 bytes); and the window stayed hidden the entire time React was mounting and IPC-ing, so the user saw nothing. The derived key is now cached process-wide, the window is visible immediately with a CSS-only splash, and the app starts maximized.

    • Performance #72 — Argon2-derived encryption key is now cached for the session. get_encryption_key() in storage.rs ran a full Argon2id derivation on every call. Startup fires 8 parallel load_* IPC calls (load_connections, load_vault_credentials, load_settings, load_query_history, load_saved_queries, load_local_history, load_keymaps, load_templates), and load_connections adds one derivation per encrypted field per connection — 10–15 derivations in practice, every one producing the same 32 bytes because the no-vault path is deterministic on (machine_id, master_key, salt). The derived key is now memoized in a OnceLock<[u8; 32]> for the no-vault paths (machine-locked and legacy) and a Mutex<HashMap<_, _>> keyed by SHA-256 password fingerprint for the vault path. Reduces 10+ derivations to 1 per session. The cache stores only the 32-byte derived key plus a password fingerprint as a map key — no plaintext passwords are retained.
    • Performance #72 — Window is visible immediately with an HTML splash. Reverts the visible: false + RAF-driven reveal pattern from #56. That pattern was correct for ~200 ms shell flashes but actively harmful when the load was multi-second — users saw 5–8 s of nothing. WebView2 now paints a minimal CSS-only splash (gradient mark + "QueryDen" + "Loading…") the instant it renders the page, before the JS bundle parses. The splash respects prefers-color-scheme. React mount replaces it on first render. The backend 8 s force-show failsafe and src/lib/revealAppWindow.ts are no longer needed and have been deleted.
    • Changed App now starts maximized. tauri.conf.json window config sets maximized: true. Standard for desktop DB tools (DataGrip, DBeaver behave this way). First-run is maximized; subsequent launches preserve OS-level window state.
  10. v1.0.14

    A UX-collision and psql-polish release with notable cold-start and Windows perf fixes. The psql console no longer mangles SELECT 1; into a syntax error or commented-out LIMIT (#38) and now respects the \x extended-display and \c change-connection meta-commands (#45). Three editor shortcut collisions are resolved: Ctrl+D no longer fights Monaco's multi-cursor (#13), Ctrl+Shift+F is freed for global search (#12), and the toolbar Format SQL button now routes through the same sql-formatter path as Ctrl+Shift+L instead of a separate hand-rolled formatter (#16). The cold-start entry bundle drops another 4 MB → 524 kB by lazy-loading Monaco itself (#56), and the main window stays hidden until first paint so there's no half-rendered shell during boot. On Windows, a process-wide cache for the machine-ID derivation eliminates a startup hang where PowerShell was spawned 8–15 times. The connection picker hides 17 coming-soon providers behind a Show-all toggle by default (#54). Docs scoped accurately around protocol-compatible engines (#18). 13 of 15 open Dependabot alerts closed (#47).

    • Fixed #38 — psql console no longer mangles queries with trailing semicolons or comments. The auto-LIMIT helper appended LIMIT 1000 verbatim, so SELECT 1; became SELECT 1; LIMIT 1000 — two statements, the second a syntax error. Worse, SELECT 1; -- foo would put the LIMIT inside a comment that extends to end-of-line, silently bypassing the safety mechanism designed to prevent unbounded results. The helper now iteratively strips trailing whitespace, semicolons, line comments (--), and block comments (/ /) before appending — surgical to the tail, so inline comments mid-query are preserved. Extracted to a pure applyQueryLimit helper with seven Vitest cases pinning every edge. The comment-bypass class of the bug was caught in PR review before reaching main.
    • Fixed #45 — psql console honors \x extended display and \c change connection. Both were silently swallowed by the wrapper that runs queries through psql --command, so \x had no effect and \c <db> couldn't switch databases without dropping the tab. The wrapper now recognizes both meta-commands client-side and routes them appropriately.
    • Fixed #13 — Database Explorer toggle moved from Ctrl+D to Ctrl+\. Ctrl+D shadowed Monaco's built-in "add selection to next occurrence" (multi-cursor) when focus was inside the SQL editor — costly muscle memory for anyone used to VS Code / JetBrains. The new binding matches the VS Code / DataGrip sidebar-toggle convention and doesn't collide with any Monaco default. The databaseExplorer entry in the default keymap preset (which previously advertised Ctrl+Alt+S, itself a collision with Settings) is now in sync.
    • Fixed #12Ctrl+Shift+F reaches the global search bar from inside the editor. Monaco bound Ctrl+Shift+F to editor.action.formatDocument as a duplicate of the canonical Ctrl+Shift+L formatter shortcut. While focus was in the editor, the app-level global-search handler in AppLayout.tsx never saw the keystroke. Dropping the Monaco binding lets Ctrl+Shift+F bubble correctly.
    • Fixed Windows startup hang / "Not Responding". On Windows, get_machine_id() shells out to powershell.exe + WMI (Get-CimInstance Win32_ComputerSystemProduct) to derive the encryption key and fingerprint. The result was never memoized, so every storage load (connections, vault, settings, query history, saved queries, local history) and every key-derivation pass re-spawned PowerShell — ~8–15 concurrent invocations during cold start. On machines with slow WMI, aggressive EDR/AV, or a bloated WMI repo, that compounded into multi-second hangs and the OS marking the window unresponsive. Cached in a process-wide OnceLock<String> so PowerShell runs at most once per session; only real values are memoized so a transient detection failure can't poison key derivation.
    • Changed #16 — One SQL formatter, one code path. The toolbar Format SQL (Prettify) button used to call a hand-rolled formatSql() in src/utils/SqlFormatter.ts (keyword newlines + naive paren indent) while Ctrl+Shift+L and Monaco's in-editor format-document went through Monaco's DocumentFormattingEditProvider backed by the sql-formatter npm package — two formatters producing different output for the same intent. The toolbar button now dispatches the same format-sql CustomEvent and the legacy helper is deleted. Side benefit: the toolbar path now formats the live editor buffer directly instead of reading a debounce-lagged ref.
    • Changed #54 — Connection dialog hides coming-soon providers by default. The driver picker rendered all 38 provider tiles on first open — most of them coming-soon placeholders that refuse to connect. The default view now shows the 21 protocol-supported engines (PostgreSQL family, MySQL family, SQLite), with a Show all (N) toggle revealing the 17 coming-soon tiles. Search keeps working across both tiers.
    • Changed #18 — Docs scoped accurately around protocol-compatible engines. MariaDB is described as supported through the MySQL wire protocol; CockroachDB and Supabase as supported through the PostgreSQL wire protocol (Supabase adds a connection-string helper, not a separate backend). No code or driver changes — just docs and README copy so the engine list doesn't overpromise distinct backends.
    • Changed #55LocalHistoryDialog is now genuinely lazy. A static import in FilesExplorer.tsx was defeating the React.lazy(...) boundary in MainContent.tsx, so the dialog (and its Monaco diff view) loaded eagerly on every cold start despite the lazy wrapper. Converted the FilesExplorer import to React.lazy + Suspense so the chunk only loads when the dialog is opened, and the Rollup chunk-warning that previously fired on every build is gone.
    • Changed #56 — Cold start: window hidden until first paint, Monaco off the critical path. The main window now starts hidden (visible: false in tauri.conf.json) and is revealed only after the React app has rendered, so users no longer see a half-painted shell that fails to respond cleanly to maximize/fullscreen during boot. QueryEditor is also lazy-loaded from MainContent — Monaco (3.7 MB raw / 967 kB gzip) and its 146 kB stylesheet leave the entry bundle, which drops from ~4 MB → 524 kB raw (151 kB gzip). Two failsafes guarantee the window can't get stuck invisible: a 4 s frontend setTimeout falls back to a forced show if RAF-driven reveal misfires, and an 8 s backend tokio task in setup() force-shows if the JS bundle never loads at all.
    • Security #47 — 13 of 15 open Dependabot alerts patched. quinn-proto 0.11.13 → 0.11.14 in the Rust lock; website astro ^5 → ^6 (6.3.2), vitest ^2.1.9 → ^3.2.4 (which dedupes nested vite to 6.4.2 and esbuild to 0.25.12), and Monaco's nested dompurify is now pinned to ^3.4.3 via an npm overrides entry. Two transitive alerts (glib 0.18.5, rand 0.7.3) are deferred — both are pinned by tauri 2.11.1 and need an upstream Tauri bump to GTK 0.20 bindings to resolve. Astro 6 changed the prerender chunk layout, so website/src/lib/changelog.ts switched from a runtime readFileSync to a Vite ?raw import — bundled at build time, strictly more robust.
    • Known Issues #40 — PostgreSQL CLI auto-download is broken end-to-end (404 URL template; source vs binary tarball mismatch). Doesn't affect most users — anyone with a system psql on PATH never hits this code path. Workaround: install the PostgreSQL client matching your server's major version via your OS package manager.
    • Known Issues #41BIGINT and BIGINT[] values whose magnitude exceeds JavaScript's Number.MAX_SAFE_INTEGER (2^53) are silently rounded to the nearest float64 representation at the IPC boundary. Workaround: cast to text in the SELECT when exact precision matters.
  11. v1.0.13

    Three SQL editor bugs fixed plus two big size cuts

    Three SQL editor bugs fixed plus two big size cuts. Schema-qualified autocomplete (SELECT FROM app.) and alias-column completion (u. after a multi-dot line) no longer collapse to empty (#28). INT2[] / INT4[] / INT8[] result columns no longer crash the deserializer (#27). Ctrl+Shift+L now actually formats the active SQL editor (#9). Monaco's unused languages and workers are stripped from the build — dist/assets/.js drops from 14.99 MB raw / 3.59 MB gzip to 5.29 MB / 1.39 MB (-65% / -61%). The Rust release profile is tightened (lto = "fat", codegen-units = 1) and reqwest swapped to rustls-tls, shrinking the Windows binary from 16.21 MB to 13.03 MB (-19.6%).

    • Fixed #28 — SQL autocomplete no longer collapses to empty after typing . on a schema-qualified table or table alias. Monaco's default fuzzy matcher treats . as a member-access trigger and filters out completion labels that don't share a bare-name representation with the typed text. Two distinct cases broke from the same root cause: SELECT * FROM app. lost its schema-table suggestions, and SELECT u.id FROM app.users u WHERE u. lost its column suggestions on multi-dot lines (the legacy tablePrefix extractor also picked up the wrong dot on those lines). The completion provider now detects <schema>.<typed> and <alias>.<typed> contexts up-front via dedicated detectors and returns bare-name suggestions with a replacement range covering only the post-dot text, matching how DataGrip and DBeaver behave. public. also surfaces tables stored without an explicit schema prefix, and <table>. works for unaliased references like SELECT users.id FROM app.users. Logic extracted to src/components/editor/completionContext.ts and covered by 26 pure-function tests; the legacy tablePrefix branch is removed.
    • Fixed #27INT2[] / INT4[] / INT8[] result columns no longer crash the deserializer with "unsupported datatype". The patched tauri-plugin-sql already handled a handful of Postgres array types but integer arrays fell through to the generic Vec<String> fallback, which sqlx refuses to decode. Added explicit match arms that decode each width as Vec<Option<T>> so SQL NULL elements survive as JSON null, mirroring the scalar INT2/INT4/INT8 arms. Smoke script: dev/test-db/test-scripts/07-int-arrays.sql.
    • Fixed #9Ctrl+Shift+L now actually formats the current SQL editor. The global handler in App.tsx was dispatching a format-code CustomEvent, but QueryEditor.tsx listens for format-sql, so the shortcut silently did nothing. Aligned the dispatch name and extracted the shortcut mapping into a pure, unit-tested matchGlobalShortcut helper so this class of mismatch is caught by CI.
    • Changed Rust release profile and dependency cleanup. Tightened [profile.release] in src-tauri/Cargo.toml (lto = "fat", codegen-units = 1, incremental = false), dropped the unused top-level sqlx dependency (it stays as a transitive dep of the patched tauri-plugin-sql, but is no longer pulled in twice with our own feature set), and swapped reqwest from the default native-tls backend to rustls-tls. The Windows release binary shrinks from 16.21 MB → 13.03 MB (-3.18 MB / -19.6%). The updater plugin (tauri-plugin-updater) ships its own TLS stack and is unaffected.
    • Changed Pruned unused Monaco languages and workers from the build. The monaco-editor default entry pulled in ~80 basic-language Monarch tokenizers (ABAP, F#, Solidity, etc.) plus four language-service workers (JSON, TypeScript, CSS, HTML) — none of which a SQL editor uses. monacoSetup.ts now imports from monaco-editor/esm/vs/editor/edcore.main.js and re-registers only the SQL contribution; vite.config.ts's manualChunks switched to a function so it groups what we actually import instead of naming the bare package (which was forcing editor.main.js and its full graph back into the build). Total dist/assets/*.js 14.99 MB → 5.29 MB raw (-65%), 3.59 MB → 1.39 MB gzipped (-61%); biggest chunk drops are the four eliminated language workers (ts.worker 7.0 MB, css.worker 1.0 MB, html.worker 695 KB, json.worker 385 KB raw).
    • Known Issues #38 — In the psql console, queries ending with ; get a safety LIMIT 1000 appended after the semicolon, producing a syntax error. Workaround: omit the trailing ; in psql tabs. The libpq path (regular Postgres connections) is unaffected.
    • Known Issues #40 — PostgreSQL CLI auto-download is broken end-to-end (404 URL template; source vs binary tarball mismatch). Doesn't affect most users — anyone with a system psql on PATH never hits this code path. Workaround: install the PostgreSQL client matching your server's major version via your OS package manager.
    • Known Issues #41BIGINT and BIGINT[] values whose magnitude exceeds JavaScript's Number.MAX_SAFE_INTEGER (2^53) are silently rounded to the nearest float64 representation at the IPC boundary. Newly visible with this release's BIGINT[] support — scalar BIGINT has had this behaviour since first-release. Workaround: cast to text in the SELECT when exact precision matters.
  12. v1.0.12

    Two long-standing SQL editor bugs fixed plus a cold-start performance pass

    Two long-standing SQL editor bugs fixed plus a cold-start performance pass. Selecting multiple statements in the editor and hitting Run no longer errors with "cannot insert multiple commands into a prepared statement" (#20). The Query Variables dialog stops popping up on ::cast operators and inside dollar-quoted function bodies (#19). The entry JS bundle is ~17% smaller, and a couple of frontend memory leaks were resolved. A throwaway Postgres test database for contributors landed under dev/test-db/, along with the project's first Vitest pure-function suite (52 tests).

    • Fixed #19 — Query Variables dialog now ignores ::cast, string literals, dollar-quoted function bodies, and SQL comments. The previous flat regex matched any colon-prefix identifier, so value::jsonb was prompting for a :jsonb variable, CREATE FUNCTION ... $$ ... :NEW ... $$ was prompting for :NEW, and so on. Replaced with a small SQL-aware scanner shared by both extractVariables and substituteVariables.
    • Fixed #20 — Selecting multiple statements and running them no longer errors with "cannot insert multiple commands into a prepared statement". PostgreSQL's extended query protocol (which tauri-plugin-sql uses) rejects multi-statement prepared calls. QueryDen now splits selections client-side via a context-aware splitStatements() utility and feeds each statement to the existing run-all loop with proper per-statement gutter glyphs.
    • Fixed AppLayout was eagerly importing SettingsDialog and HelpDialog alongside the lazy versions in App.tsx, pulling react-markdown and the settings tree into the cold-start chunk. Header buttons and search-popup CTAs now dispatch through the same window events the lazy boundary already listens for.
    • Fixed show-local-history event listener leak in MainContentaddEventListener and removeEventListener used two distinct inline arrows, so cleanup never matched the registration. Handlers accumulated on every re-render of an effect that depended on frequently-recreated callbacks.
    • Fixed Monaco SQL completion provider was holding the previous connection's full schema (up to 50k rows on wide DBs) past disconnect. The cache is now dropped via a connection-disconnected window event.
    • Changed Cold-start bundle: eight modal dialogs (Compare, Clone, ActivityMonitor, MultiQuery, AIAssistant, PsqlWindow, LocalHistoryDialog, DefinitionModal) now load via React.lazy + Suspense and are only mounted when their open flag flips true. Three of them pull in their own Monaco instance — deferring those is the meaningful win. Entry chunk 1.01 MB → 838 KB raw (-17%), 271 KB → 234 KB gzipped (-13%).
    • Changed Removed the "Allow SQL Execution" toggle from Settings → Permissions & Rules. The setting already defaulted to true and the engine's defense-in-depth checks (MainContent.tsx, ConnectionContext.tsx) stay in place — but exposing the toggle was a footgun. Users can no longer accidentally disable SQL execution from the UI.
    • Infrastructure New dev/test-db/ directory: Docker Compose Postgres setup, schema/seed/triggers, and copy-paste SQL scripts that exercise each of the bugs fixed in this release. Documented in dev/test-db/README.md.
    • Infrastructure First colocated Vitest suite under src/: extractVariables / substituteVariables (22 tests) and the new splitStatements utility (15 tests). Total frontend tests: 52. CI runs npm test on every PR.
    • Infrastructure Documented the testing convention in CLAUDE.md: every bug fix lands with the failing test that proves it (pinned via it.fails if the fix can't ride along in the same PR).
    • Known Issues #27 — Result deserializer errors on INT4[] columns ("unsupported datatype"). The patched tauri-plugin-sql supports several array types but int[] specifically is unmapped. Workaround: cast to text in the SELECT.
    • Known Issues #28 — Autocomplete suggestions vanish after typing . on a schema-qualified table name (e.g. app.). Monaco's filter treats the dot as a member-access trigger. Workaround: type the table name without the schema prefix.
  13. v1.0.11

    App icon regenerated

    App icon regenerated. src-tauri/icons/icon.ico (and most derived PNGs/icns) still held the default Tauri "WORLD" placeholder from initial scaffolding — the source icon.png was swapped to the QueryDen network-graph design at some point but the derived assets were never re-emitted, so the v1.0.10 installer ended up embedding the Tauri globe as its file-explorer icon.

    • Fixed Re-ran npx @tauri-apps/cli icon src-tauri/icons/icon.png to rebuild the full set: multi-resolution icon.ico, icon.icns, all derived PNGs, the Windows Store Square*Logo set, and the iOS/Android icon trees.
    • Note Binary asset regen only — no code changes.
  14. v1.0.10

    Replaced the 740-line custom updater module with the official tauri-plugin-updater + tauri-plugin-process pair. v1.0.8 and v1.0.9 shipped a custom Windows install flow that pre-uninstalled the running version and then ran a silent /S install over a still-locked queryden.exe — leaving users with an orphaned binary, no registry entry, and no app to launch. The plugin handles this properly via NSIS Restart Manager on Windows, POSIX unlink-while-running on macOS, and AppImage in-place replacement on Linux.

    • Security Updates are now signed against a minisign pubkey baked into the build. CI signs each artifact via the TAURI_SIGNING_PRIVATE_KEY and TAURI_SIGNING_PRIVATE_KEY_PASSWORD repo secrets, and emits a latest.json manifest as a release asset for the plugin to consume. Clients verify the .sig companion file before installing.
    • Changed Release workflow emits .sig companions (via createUpdaterArtifacts: true in tauri.conf.json) alongside the existing .sha256 files.
    • Changed Removed src-tauri/src/updater.rs, src-tauri/windows/installer-hooks.nsh, UpdateCheckResultDto, and the four custom updater IPC commands. The only custom updater code that survives is get_build_info in lib.rs, which surfaces the QUERYDEN_BUILD_DATE env var for the About dialog.
    • Changed Added tauri-plugin-updater + tauri-plugin-process (Cargo + npm), plugins.updater.{endpoints,pubkey} in tauri.conf.json, and the updater:default / process:default / process:allow-restart capabilities.
    • Changed Rolled back the Monaco lazy-loading from v1.0.9 in MainContent.tsx — the cold-start win was not worth the editor-modal startup latency.
    • Fixed Windows: the v1.0.8/v1.0.9 broken install flow described above. The plugin's NSIS Restart Manager integration removes the need for the manual locked-file dance.
    • Fixed macOS: atomic replacement via POSIX unlink-while-running.
    • Fixed Linux: in-place replacement for the AppImage bundle.
    • Note The v1.0.9 → v1.0.10 upgrade requires a one-time manual install. v1.0.9's broken updater cannot deliver this release cleanly. From v1.0.10 onward, in-app updates go through the signed plugin path.
  15. v1.0.9
    • Changed Monaco registration moved out of the cold-start path. Editor and editor-using modals are no longer eagerly imported in the entry bundle. (Rolled back in v1.0.10 — see that release's notes.)
    • Changed General codebase hygiene pass — typed IPC boundary (src/lib/ipc.ts), tightened CSP, normalized line endings.
    • Fixed Linux .deb: pkexec policy fix so the installer no longer leaves a broken polkit action behind on uninstall.
  16. v1.0.8
    • Fixed Updater now downloads into a per-invocation random temp directory (mode 0700 on Unix) to defeat predictable-path symlink attacks.
    • Fixed SHA256 verification step uses POSIX find for macOS bash 3.2 compatibility.
    • Changed Aligned the Rust tauri crate version with @tauri-apps/api on npm to avoid IPC schema drift.
  17. v1.0.7
    • Changed Windows: cleaner in-app upgrades. The NSIS installer now silently removes the previous version before installing the new one via a preinstall hook, instead of asking the user to run the uninstaller manually. Installs are also now per-user by default (no UAC prompt) and the language selector is hidden.
    • Changed Note for current Windows users: the upgrade from v1.0.6 → v1.0.7 still requires a one-time UAC prompt because v1.0.6 was installed machine-wide; the hook detects and silently uninstalls the HKLM-scoped install. From v1.0.7 onward all installs are HKCU and all subsequent upgrades are fully seamless.
  18. v1.0.6

    No user-visible changes

    No user-visible changes. Released to exercise the in-app updater end-to-end (the v1.0.5 → v1.0.6 path is the first one ever tested in production, since v1.0.5 was the very first public release).

    • Changed README: clarified that the macOS download is Apple Silicon only and pointed Intel Mac users to #7.
  19. v1.0.5

    First public release of QueryDen

    First public release of QueryDen. Internal builds existed at versions 1.0.0–1.0.4 before open-sourcing but were never published; this is the first release available on the releases page and via the in-app updater.

    QueryDen is a multi-database desktop manager built with Tauri 2, React, and TypeScript. It supports PostgreSQL, MySQL/MariaDB, SQLite, CockroachDB, and Supabase, with SSH tunneling, an encrypted credential vault, local history, saved queries, and an integrated psql console.

    • Added Multi-database desktop manager: PostgreSQL, MySQL/MariaDB, SQLite, CockroachDB, Supabase.
    • Added Per-platform bundles: Linux (.deb, AppImage), Windows (NSIS), macOS Apple Silicon (.dmg/.app). Intel macOS is planned for v1.0.6 as a universal-binary build; for now Intel Mac users can build from source.
    • Added Local Monaco editor bundle — the SQL editor works offline, no runtime CDN dependency.
    • Added Optional AI assistant supporting OpenAI, Anthropic, Google, and Ollama. Off by default. When enabled, prompts and the user's API key are sent directly from the desktop app to the configured provider — no QueryDen-operated server in the middle. See the README "Privacy & the AI assistant" section.
    • Added Typed Tauri IPC boundary (src/lib/ipc.ts); no remaining invoke<any> call sites.
    • Security AES-256-GCM encryption with Argon2id key derivation for connections, vault credentials, query history, saved queries, and local history.
    • Security OS keyring storage for the master key (file fallback). Encryption refuses to proceed if neither persistence path is available — no silent degradation to known keys.
    • Security Machine-binding: encrypted files refuse to load on a different machine.
    • Security Brute-force protection: vault locks after 5 failed unlock attempts.
    • Security Tauri filesystem permissions scoped to app data, downloads, documents, and desktop directories.
    • Security Updater: SHA256 verification for every download. Fetches <asset>.sha256 from the GitHub release before downloading the binary and refuses to install on digest mismatch.
    • Security Updater: downloads land in a per-invocation random temp directory (mode 0700 on Unix) to defeat predictable-path symlink attacks.
    • Security Updater: HTTPS-only — non-HTTPS update URLs are rejected.
    • Security AI assistant fetch() calls use a 30-second abort timeout.
    • Infrastructure CONTRIBUTING.md, CODE_OF_CONDUCT.md, SECURITY.md, plus issue and PR templates.
    • Infrastructure CI on every push/PR: version-drift check, typecheck, Vitest unit tests, frontend build, cargo check, cargo clippy -- -D warnings, cargo test.
    • Infrastructure Release workflow on tag push: cross-platform build matrix with <asset>.sha256 companion files for the in-app updater.
    • Infrastructure Dev-only logger utility so diagnostic output stays out of production builds.