Skip to content

v2026.3

Choose a tag to compare

@github-actions github-actions released this 25 Jul 23:01
· 56 commits to main since this release
Pelton2026 3-banner

Changes

chore(deps): Bump the frontend-dependencies group across 1 directory with 11 updates - @dependabot[bot] in #118

Bumps the frontend-dependencies group with 11 updates in the /frontend directory:

Package From To
@fontsource/familjen-grotesk 5.2.8 5.3.0
@fontsource/spline-sans-mono 5.2.8 5.3.0
@tabler/icons-svelte 3.44.0 3.45.0
@tiptap/core 3.27.3 3.28.0
@tiptap/extension-link 3.27.3 3.28.0
@tiptap/pm 3.27.3 3.28.0
@tiptap/starter-kit 3.27.3 3.28.0
marked 18.0.6 18.0.7
svelte 5.56.4 5.56.7
svelte-check 4.7.2 4.7.3
vite 8.1.4 8.1.5

Updates @fontsource/familjen-grotesk from 5.2.8 to 5.3.0

Commits

Updates @fontsource/spline-sans-mono from 5.2.8 to 5.3.0

Commits

Updates @tabler/icons-svelte from 3.44.0 to 3.45.0

Release notes

Sourced from @​tabler/icons-svelte's releases.

Release 3.45.0

20 new icons:

  • filled/brand-signal
  • outline/app-window-bottom-left
  • outline/app-window-bottom-right
  • outline/app-window-bottom
  • outline/app-window-center
  • outline/arrow-fork-triple
  • outline/brand-signal
  • outline/device-vision-pro-wifi
  • outline/device-workstation
  • outline/dragon
  • outline/italic-off
  • outline/tab-close
  • outline/text-outline
  • outline/text-regex-asterisk
  • outline/text-regex-end
  • outline/text-regex-plus
  • outline/text-regex-question
  • outline/text-regex-start
  • outline/underline-off
  • outline/virtual-space

New features

  • New package: @tabler/icons-astro — Astro support
  • Added sideEffects: false for better tree-shaking in Vue package
  • Spelling fixes: corrected misspelled icon names

Fixed icons: outline/flip-horizontal, outline/flip-vertical

Renamed icons:

  • filled/mood-confuzed renamed to filled/mood-confused
  • outline/brand-adobe-after-effect renamed to outline/brand-adobe-after-effects
  • outline/brand-kako-talk renamed to outline/brand-kakao-talk
  • outline/currency-rubel renamed to outline/currency-ruble
  • outline/foodsteps renamed to outline/footsteps
  • outline/gender-trasvesti renamed to outline/gender-travesti
  • outline/ikosaedr renamed to outline/icosahedron
  • outline/mood-confuzed renamed to outline/mood-confused
  • outline/physotherapist renamed to outline/physiotherapist
  • outline/sport-billard renamed to outline/sport-billiard
Commits

Updates @tiptap/core from 3.27.3 to 3.28.0

Release notes

Sourced from @​tiptap/core's releases.

v3.28.0

@​tiptap/extension-details

Patch Changes

  • 8614730: Fix the cursor moving to the details summary after typing in content at the start of a document.
  • @​tiptap/core@​3.28.0
    • @​tiptap/extension-text-style@​3.28.0
    • @​tiptap/pm@​3.28.0

@​tiptap/extension-list

Patch Changes

  • 8614730: Fix markdown parsing a line like (216) 555-1234 as an ordered list. A number followed by ) mid-line is no longer treated as a list marker.
  • @​tiptap/core@​3.28.0
    • @​tiptap/pm@​3.28.0

@​tiptap/react

Patch Changes

  • 8614730: Batch React node view portal store notifications that happen in the same microtask to avoid nested update depth warnings when many node views mount together.
  • 8614730: Bind onMount and onUnmount event handlers when initializing an Editor with useEditor hook.
  • @​tiptap/core@​3.28.0
    • @​tiptap/pm@​3.28.0

@​tiptap/extension-collaboration-caret

Patch Changes

  • 8614730: Bump @​tiptap/y-tiptap version to ensure users use latest version
  • @​tiptap/core@​3.28.0
    • @​tiptap/pm@​3.28.0

@​tiptap/extension-collaboration

Patch Changes

  • 8614730: Bump @​tiptap/y-tiptap version to ensure users use latest version
  • @​tiptap/core@​3.28.0
    • @​tiptap/pm@​3.28.0

@​tiptap/extension-drag-handle

Patch Changes

  • 8614730: Bump @​tiptap/y-tiptap version to ensure users use latest version
  • Updated dependencies [8614730]
    • @​tiptap/extension-collaboration@​3.28.0

... (truncated)

Changelog

Sourced from @​tiptap/core's changelog.

3.28.0

Patch Changes

  • @​tiptap/pm@​3.28.0

3.27.4

Patch Changes

  • @​tiptap/pm@​3.27.4
Commits

Updates @tiptap/extension-link from 3.27.3 to 3.28.0

Release notes

Sourced from @​tiptap/extension-link's releases.

v3.28.0

@​tiptap/extension-details

Patch Changes

  • 8614730: Fix the cursor moving to the details summary after typing in content at the start of a document.
  • @​tiptap/core@​3.28.0
    • @​tiptap/extension-text-style@​3.28.0
    • @​tiptap/pm@​3.28.0

@​tiptap/extension-list

Patch Changes

  • 8614730: Fix markdown parsing a line like (216) 555-1234 as an ordered list. A number followed by ) mid-line is no longer treated as a list marker.
  • @​tiptap/core@​3.28.0
    • @​tiptap/pm@​3.28.0

@​tiptap/react

Patch Changes

  • 8614730: Batch React node view portal store notifications that happen in the same microtask to avoid nested update depth warnings when many node views mount together.
  • 8614730: Bind onMount and onUnmount event handlers when initializing an Editor with useEditor hook.
  • @​tiptap/core@​3.28.0
    • @​tiptap/pm@​3.28.0

@​tiptap/extension-collaboration-caret

Patch Changes

  • 8614730: Bump @​tiptap/y-tiptap version to ensure users use latest version
  • @​tiptap/core@​3.28.0
    • @​tiptap/pm@​3.28.0

@​tiptap/extension-collaboration

Patch Changes

  • 8614730: Bump @​tiptap/y-tiptap version to ensure users use latest version
  • @​tiptap/core@​3.28.0
    • @​tiptap/pm@​3.28.0

@​tiptap/extension-drag-handle

Patch Changes

  • 8614730: Bump @​tiptap/y-tiptap version to ensure users use latest version
  • Updated dependencies [8614730]
    • @​tiptap/extension-collaboration@​3.28.0

... (truncated)

Changelog

Sourced from @​tiptap/extension-link's changelog.

3.28.0

Patch Changes

  • @​tiptap/core@​3.28.0
  • @​tiptap/pm@​3.28.0

3.27.4

Patch Changes

  • @​tiptap/core@​3.27.4
  • @​tiptap/pm@​3.27.4
Commits

Updates @tiptap/pm from 3.27.3 to 3.28.0

Release notes

Sourced from @​tiptap/pm's releases.

v3.28.0

@​tiptap/extension-details

Patch Changes

  • 8614730: Fix the cursor moving to the details summary after typing in content at the start of a document.
  • @​tiptap/core@​3.28.0
    • @​tiptap/extension-text-style@​3.28.0
    • @​tiptap/pm@​3.28.0

@​tiptap/extension-list

Patch Changes

  • 8614730: Fix markdown parsing a line like (216) 555-1234 as an ordered list. A number followed by ) mid-line is no longer treated as a list marker.
  • @​tiptap/core@​3.28.0
    • @​tiptap/pm@​3.28.0

@​tiptap/react

Patch Changes

  • 8614730: Batch React node view portal store notifications that happen in the same microtask to avoid nested update depth warnings when many node views mount together.
  • 8614730: Bind onMount and onUnmount event handlers when initializing an Editor with useEditor hook.
  • @​tiptap/core@​3.28.0
    • @​tiptap/pm@​3.28.0

@​tiptap/extension-collaboration-caret

Patch Changes

  • 8614730: Bump @​tiptap/y-tiptap version to ensure users use latest version
  • @​tiptap/core@​3.28.0
    • @​tiptap/pm@​3.28.0

@​tiptap/extension-collaboration

Patch Changes

  • 8614730: Bump @​tiptap/y-tiptap version to ensure users use latest version
  • @​tiptap/core@​3.28.0
    • @​tiptap/pm@​3.28.0

@​tiptap/extension-drag-handle

Patch Changes

  • 8614730: Bump @​tiptap/y-tiptap version to ensure users use latest version
  • Updated dependencies [8614730]
    • @​tiptap/extension-collaboration@​3.28.0

... (truncated)

Changelog

Sourced from @​tiptap/pm's changelog.

3.28.0

3.27.4

Commits

Updates @tiptap/starter-kit from 3.27.3 to 3.28.0

Release notes

Sourced from @​tiptap/starter-kit's releases.

v3.28.0

@​tiptap/extension-details

Patch Changes

  • 8614730: Fix the cursor moving to the details summary after typing in content at the start of a document.
  • @​tiptap/core@​3.28.0
    • @​tiptap/extension-text-style@​3.28.0
    • @​tiptap/pm@​3.28.0

@​tiptap/extension-list

Patch Changes

  • 8614730: Fix markdown parsing a line like (216) 555-1234 as an ordered list. A number followed by ) mid-line is no longer treated as a list marker.
  • @​tiptap/core@​3.28.0
    • @​tiptap/pm@​3.28.0

@​tiptap/react

Patch Changes

  • 8614730: Batch React node view portal store notifications that happen in the same microtask to avoid nested update depth warnings when many node views mount together.
  • 8614730: Bind onMount and onUnmount event handlers when initializing an Editor with useEditor hook.
  • @​tiptap/core@​3.28.0
    • @​tiptap/pm@​3.28.0

@​tiptap/extension-collaboration-caret

Patch Changes

  • 8614730: Bump @​tiptap/y-tiptap version to ensure users use latest version
  • @​tiptap/core@​3.28.0
    • @​tiptap/pm@​3.28.0

@​tiptap/extension-collaboration

Patch Changes

  • 8614730: Bump @​tiptap/y-tiptap version to ensure users use latest version
  • @​tiptap/core@​3.28.0
    • @​tiptap/pm@​3.28.0

@​tiptap/extension-drag-handle

Patch Changes

  • 8614730: Bump @​tiptap/y-tiptap version to ensure users use latest version
  • Updated dependencies [8614730]
    • @​tiptap/extension-collaboration@​3.28.0

... (truncated)

Changelog

Sourced from @​tiptap/starter-kit's changelog.

3.28.0

Patch Changes

  • Updated dependencies [c1254c1]
    • @​tiptap/extension-list@​3.28.0
    • @​tiptap/extension-list-item@​3.28.0
    • @​tiptap/extension-list-keymap@​3.28.0
    • @​tiptap/extension-bullet-list@​3.28.0
    • @​tiptap/extension-ordered-list@​3.28.0
    • @​tiptap/extension-dropcursor@​3.28.0
    • @​tiptap/extension-gapcursor@​3.28.0
    • @​tiptap/core@​3.28.0
    • @​tiptap/extension-blockquote@​3.28.0
    • @​tiptap/extension-bold@​3.28.0
    • @​tiptap/extension-code@​3.28.0
    • @​tiptap/extension-code-block@​3.28.0
    • @​tiptap/extension-document@​3.28.0
    • @​tiptap/extension-hard-break@​3.28.0
    • @​tiptap/extension-heading@​3.28.0
    • @​tiptap/extension-horizontal-rule@​3.28.0
    • @​tiptap/extension-italic@​3.28.0
    • @​tiptap/extension-link@​3.28.0
    • @​tiptap/extension-paragraph@​3.28.0
    • @​tiptap/extension-strike@​3.28.0
    • @​tiptap/extension-text@​3.28.0
    • @​tiptap/extension-underline@​3.28.0
    • @​tiptap/extensions@​3.28.0
    • @​tiptap/pm@​3.28.0

3.27.4

Patch Changes

  • Updated dependencies [d2983cd]
  • Updated dependencies [53f8e57]
  • Updated dependencies [6238a3c]
  • Updated dependencies [c28d888]
    • @​tiptap/extensions@​3.27.4
    • @​tiptap/extension-blockquote@​3.27.4
    • @​tiptap/extension-list@​3.27.4
    • @​tiptap/extension-dropcursor@​3.27.4
    • @​tiptap/extension-gapcursor@​3.27.4
    • @​tiptap/extension-list-item@​3.27.4
    • @​tiptap/extension-list-keymap@​3.27.4
    • @​tiptap/extension-bullet-list@​3.27.4
    • @​tiptap/extension-ordered-list@​3.27.4
    • @​tiptap/core@​3.27.4
    • @​tiptap/extension-bold@​3.27.4
    • @​tiptap/extension-code@​3.27.4

... (truncated)

Commits

Updates marked from 18.0.6 to 18.0.7

Release notes

Sourced from marked's releases.

v18.0.7

18.0.7 (2026-07-21)

Bug Fixes

  • Avoid O(n^2) backtracking in HTML block close and tilde interrupt regexes (#4014) (f945fc5), closes #3991
  • Avoid O(n^2) masked source rebuild in inline tokenizer (#4017) (9154f8f)
  • keep empty list after blockquote as a sibling block (#4004) (3f144a0)
  • preserve code spans adjacent to tildes (#4012) (0de7188)
  • Recognize setext headings whose first line starts with # (#4015) (f056437), closes #1
  • treat a line of only tabs as a blank line between paragraphs (#4007) (bc2f121)
Commits
  • a8971a1 chore(release): 18.0.7 [skip ci]
  • d899c2e chore(deps): bump actions/setup-node from 6 to 7 (#4025)
  • 7fbf82e chore(deps-dev): bump semantic-release from 25.0.7 to 25.0.8 (#4026)
  • 738edf2 chore(deps-dev): bump brace-expansion from 5.0.2 to 5.0.6 (#4027)
  • 9154f8f fix: Avoid O(n^2) masked source rebuild in inline tokenizer (#4017)
  • f945fc5 fix: Avoid O(n^2) backtracking in HTML block close and tilde interrupt regexe...
  • 3f144a0 fix: keep empty list after blockquote as a sibling block (#4004)
  • 0de7188 fix: preserve code spans adjacent to tildes (#4012)
  • f056437 fix: Recognize setext headings whose first line starts with # (#4015)
  • 12bfa94 chore(deps-dev): bump semantic-release from 25.0.5 to 25.0.7 (#4020)
  • Additional commits viewable in compare view

Updates svelte from 5.56.4 to 5.56.7

Release notes

Sourced from svelte's releases.

svelte@5.56.7

Patch Changes

  • chore: provide indent option for print (#18474)

svelte@5.56.6

Patch Changes

  • perf: skip unnecessary blocker analysis when compiling components without top-level await (#18548)

  • fix: rerun derived that had an abort controller on reconnection (#18551)

svelte@5.56.5

Patch Changes

  • chore: drop dead code that make TSGO fail (#18496)

  • fix: don't (re)connect deriveds when read inside branch/root effects (#18527)

  • fix: skip unnecessary derived effect in earlier batch (#18525)

  • fix: avoid declaration tag warning in event handlers (#18500)

  • fix: abort deriveds own AbortSignal when it disconnects (#18400)

  • fix: ensure $state.eager() is correctly transormed for SSR output (#18530)

  • fix: correctly transform declaration tags during SSR (#18492)

  • fix: transform computed keys in keyed {#each} destructuring patterns (#18521)

  • fix: chain preprocessor sourcemaps with an empty sources[0] instead of dropping them (#18518)

  • fix: clear previous_task reference after abort in Tween to prevent memory leak on interrupted tweens (#18541)

  • fix: don't treat declaration tags as parts inside each blocks (#18507)

Changelog

Sourced from svelte's changelog.

5.56.7

Patch Changes

  • chore: provide indent option for print (#18474)

5.56.6

Patch Changes

  • perf: skip unnecessary blocker analysis when compiling components without top-level await (#18548)

  • fix: rerun derived that had an abort controller on reconnection (#18551)

5.56.5

Patch Changes

  • chore: drop dead code that make TSGO fail (#18496)

  • fix: don't (re)connect deriveds when read inside branch/root effects (#18527)

  • fix: skip unnecessary derived effect in earlier batch (#18525)

  • fix: avoid declaration tag warning in event handlers (#18500)

  • fix: abort deriveds own AbortSignal when it disconnects (#18400)

  • fix: ensure $state.eager() is correctly transormed for SSR output (#18530)

  • fix: correctly transform declaration tags during SSR (#18492)

  • fix: transform computed keys in keyed {#each} destructuring patterns (#18521)

  • fix: chain preprocessor sourcemaps with an empty sources[0] instead of dropping them (#18518)

  • fix: clear previous_task reference after abort in Tween to prevent memory leak on interrupted tweens (#18541)

  • fix: don't treat declaration tags as parts inside each blocks (#18507)

Commits

Updates svelte-check from 4.7.2 to 4.7.3

Release notes

Sourced from svelte-check's releases.

svelte-check@4.7.3

Patch Changes

  • feat: zero-config +error.svelte props (#3076)
Commits

Updates vite from 8.1.4 to 8.1.5

Release notes

Sourced from vite's releases.

v8.1.5

Please refer to CHANGELOG.md for details.

Changelog

Sourced from vite's changelog.

8.1.5 (2026-07-16)

Bug Fixes

  • bundled-dev: avoid duplicated buildEnd (#22931) (8100320)
  • client: overlay error message format align rolldown (#22869) (5a72b87)
  • deps: update all non-major dependencies (#22921) (fef682d)
  • deps: update rolldown-related dependencies (#22922) (3c345e4)
  • module-runner: don't crash stack-trace source mapping when globalThis.Buffer is absent (#22945) (f8b38e3)
  • optimizer: respect importer module format for dynamic import interop with CJS deps (#22951) (6c08c39)
  • ssr: scope switch-case declarations to the switch, not the function (#22893) (b59a73f)

Documentation

  • build: fix incorrect @default for build.cssMinify (#22948) (c88c236)
  • build: fix incorrect @default for build.lib.formats (#22911) (369ed60)

Tests

  • avoid scanner scanning all files under __tests__ (#22912) (c961cae)
Commits
  • 5e7fe12 release: v8.1.5
  • 6c08c39 fix(optimizer): respect importer module format for dynamic import interop wit...
  • 5a72b87 fix(client): overlay error message format align rolldown (#22869)
  • f8b38e3 fix(module-runner): don't crash stack-trace source mapping when globalThis.Bu...
  • 8100320 fix(bundled-dev): avoid duplicated buildEnd (#22931)
  • c88c236 docs(build): fix incorrect @default for build.cssMinify (#22948)
  • b59a73f fix(ssr): scope switch-case declarations to the switch, not the function (#22...
  • fef682d fix(deps): update all non-major dependencies (#22921)
  • 3c345e4 fix(deps): update rolldown-related dependencies (#22922)
  • 369ed60 docs(build): fix incorrect @default for build.lib.formats (#22911)
  • Additional commits viewable in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore <dependency name> major version will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself)
  • @dependabot ignore <dependency name> minor version will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself)
  • @dependabot ignore <dependency name> will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself)
  • @dependabot unignore <dependency name> will remove all of the ignore conditions of the specified dependency
  • @dependabot unignore <dependency name> <ignore condition> will remove the ignore condition of the specified dependency and ignore conditions
chore(deps): Bump the go-dependencies group across 1 directory with 4 updates - @dependabot[bot] in #114

Bumps the go-dependencies group with 3 updates in the / directory: github.com/yuin/goldmark, golang.org/x/crypto and modernc.org/sqlite.

Updates github.com/yuin/goldmark from 1.8.2 to 1.8.4

Release notes

Sourced from github.com/yuin/goldmark's releases.

v1.8.4

fix: disable svg in data:image urls

v1.8.3

Full Changelog: yuin/goldmark@v1.8.2...v1.8.3

Commits

Updates golang.org/x/crypto from 0.52.0 to 0.54.0

Commits
  • cdce021 go.mod: update golang.org/x dependencies
  • d9474cc openpgp: make the deprecation message more explicit
  • 7626c50 ssh: verify declared key type matches decoded key in authorized_keys
  • 0471e79 ssh/agent: enforce strict limits on DSA key parameters
  • 6435c37 ssh: sanitize client disconnect messages
  • 7d695da ssh/agent: drain channel stderr in agent forwarders
  • 5b7f841 acme/autocert: fix data race in Manager.createCert
  • 0b316e7 argon2: update RFC 9106 parameter recommendations
  • 55aec0a x509roots/fallback: update bundle
  • 5f2de1a internal: remove wycheproof tests
  • Additional commits viewable in compare view

Updates golang.org/x/net from 0.55.0 to 0.56.0

Commits
  • 9e7fdbf internal/http3: fix wrong argument being given when validating header value
  • b686e5f internal/http3: add gzip support to transport
  • 8a34885 go.mod: update golang.org/x dependencies
  • 72eaf98 dns/dnsmessage: correctly validate SVCB record parameter order
  • 82e7868 dns/dnsmessage: avoid panic when parsing SVCB record with truncated data
  • b64f1fa internal/http3: add server support for "Trailer:" magic prefix
  • 2707ee2 internal/http3: implement HTTP/3 clientConn methods
  • 31358cc internal/http3: snapshot response headers at WriteHeader time
  • 8ecbaa9 html: don't adjust xml:base
  • 8ae811a html: properly handle end script tag in fragment mode
  • Additional commits viewable in compare view

Updates modernc.org/sqlite from 1.53.0 to 1.54.0

Changelog

Sourced from modernc.org/sqlite's changelog.

Changelog

  • 2026-07-20 v1.55.0:

    • Add github.com/mattn/go-sqlite3-compatible shorthand DSN query parameters to ease migration from that driver: _busy_timeout/_timeout, _foreign_keys/_fk, _journal_mode/_journal, _synchronous/_sync, _auto_vacuum/_vacuum, and _query_only, each setting the correspondingly named PRAGMA. Values are validated against the same set mattn/go-sqlite3 accepts (case-insensitive) and an unrecognized value fails the connection with an error, so a typo such as _synchronous=fu1l or _foreign_keys=yes_please is reported rather than silently downgrading durability or dropping foreign-key enforcement. The keys are applied in a fixed order independent of their order in the DSN — _busy_timeout and _auto_vacuum before any _pragma values (auto_vacuum must be set before the database is first written), the rest after, and _query_only last — and where a key and its alias are both supplied the alias wins, matching mattn/go-sqlite3; selection is by presence rather than by value, so supplying the alias empty (_foreign_keys=on&_fk=) suppresses the PRAGMA rather than deferring to the primary key, again matching that driver. Behavior change to note: prior releases ignored these keys entirely, so a DSN carried over from a mattn/go-sqlite3 setup changes in two ways. A recognized key that previously did nothing now takes effect — _foreign_keys=on begins enforcing constraints against data that may already violate them, _journal_mode=wal persistently converts the database file, and _query_only=1 makes the connection read-only. And a value outside the accepted set now fails the connection with an error where the same DSN previously opened successfully — for example a duration-style _busy_timeout=5s or _timeout=5000ms, neither of which is the integer that key requires. Review such DSNs before upgrading. _pragma is unchanged and no pre-existing parameter changes meaning, though see the following entry for a change in when all of them are validated.
    • See [GitLab merge request #134](https://gitlab.com/cznic/sqlite/-/merge_requests/134), thanks Toni Spets (@​beeper-hifi) and Ian Chechin!
    • Validate every DSN query parameter before applying any of them. Parameters were previously checked as each was reached, so a DSN whose later parameter was rejected had already executed the PRAGMAs ahead of it. Because PRAGMA journal_mode and PRAGMA auto_vacuum are persistent changes to the database file, a DSN such as file:x.db?_journal_mode=wal&_synchronous=bogus failed the connection and yet left x.db converted to WAL. A failed Open now leaves the database as it found it. This covers the pre-existing _txlock, _timezone, _time_format, _time_integer_format, _inttotime and _texttotime parameters as well as the shorthand keys above: all of them were validated only after the _pragma list had already run, so the same DSN shape — a valid _pragma=journal_mode=wal alongside a misspelled _txlock — converted the file before reporting the error. Only the values accepted for each parameter are unchanged; a DSN that opened successfully before still opens, and one that failed still fails with the same error. _pragma remains the sole exception, since its values are executed verbatim and cannot be checked in advance: a malformed _pragma is still rejected by SQLite as it runs, after any earlier _pragma in the list has taken effect.
  • 2026-07-15 v1.54.0:

    • Upgrade to SQLite 3.53.3. This also bumps the pinned modernc.org/libc to v1.74.1; as always, downstream modules must pin the exact same modernc.org/libc version this module's go.mod pins (see [GitLab issue #177](https://gitlab.com/cznic/sqlite/-/issues/177)).
    • Under the opt-in _texttotime DSN parameter, best-effort parse date-shaped TEXT values from columns SQLite reports with an empty declared type — aggregates and expressions over a date column (MAX(d), COALESCE(d, ...), upper(d), d || ''), subqueries, and typeless real columns (CREATE TABLE t(x)) — into time.Time, instead of delivering them as a raw string that Scan cannot store into a *time.Time. The existing declared DATE/DATETIME/TIME/TIMESTAMP path is unchanged; this only adds the empty-decltype case. The conversion is strictly best-effort: a value that does not parse as a time falls through to the original string, so no Scan that worked before can newly fail. ColumnTypeScanType continues to report string for empty-decltype columns, since the declared type cannot prove the column is temporal. Without _texttotime the behavior is byte-for-byte unchanged. Resolves [GitLab issue #248](https://gitlab.com/cznic/sqlite/-/issues/248).
    • See [GitLab merge request #133](https://gitlab.com/cznic/sqlite/-/merge_requests/133), thanks Ian Chechin!
  • 2026-06-21 v1.53.0:

    • Add experimental netbsd/amd64 support, resolving the long-standing build break in [GitLab issue #246](https://gitlab.com/cznic/sqlite/-/issues/246). This target is intentionally not yet listed among the supported platforms in the package documentation: the port had been broken for years and is only now revived, and there is as yet no real-world experience running it under production workloads. Green CI is not the same as battle-tested — so while the full test suite (including the pcache and vec packages and the -race concurrency test) passes on NetBSD 10.1 / Go 1.26.3, and the entire upstream toolchain (libc, cc, ccgo, libz, libtcl8.6, libsqlite3, libsqlite_vec) is green on the NetBSD CI builder, the target is offered for evaluation only. If you run NetBSD, please exercise it with your own workloads and report back via #246; the intent is to promote it to a fully supported platform after a period of broader real-world testing (on the order of a month) elapses without surprises.
    • Implementation notes: the previously shipped lib/sqlite_netbsd_amd64.go was a stale old-generator transpile that no longer compiled (the mu.enter/mu.leave break in #246); it is replaced by a fresh new-generator transpile consistent with every other platform, and modernc.org/sqlite/vec (sqlite-vec) is vendored and auto-registers on netbsd. Correct operation requires the matching pinned modernc.org/libc, which carries two NetBSD-specific fixes found during this work: the mmap(2) PAD-argument ABI (without it, concurrent WAL access faults with SIGBUS in the WAL-index shared memory) and a working abort(3) (the prior stub left SQLite's crash-recovery writecrash test unable to terminate by signal). As usual, downstream modules must pin the exact modernc.org/libc version this module's go.mod pins.
    • See [GitLab merge request #82](https://gitlab.com/cznic/sqlite/-/merge_requests/82), thanks Leonardo Taccari (@​iamleot) and Thomas Klausner (@wiz)!
    • Add experimental freebsd/386 and freebsd/arm support. As with the netbsd/amd64 target above, these two 32-bit FreeBSD ports are intentionally not yet listed among the supported platforms in the package documentation: freebsd/386 previously shipped a stale, effectively untested SQLite 3.41 transpile, and freebsd/arm is entirely new, so neither has real-world production mileage yet. Both are now freshly transpiled at SQLite 3.53.2 consistent with every other platform, build cleanly, and pass the full test suite (core, WAL/concurrency, and the vec package) on the FreeBSD CI builders; they are offered for evaluation only. If you run 32-bit FreeBSD, please exercise these targets with your own workloads and report back — the intent is to promote freebsd/386, freebsd/arm, and netbsd/amd64 to fully supported platforms in a future release cycle, once a period of broader real-world testing elapses without surprises.
    • Implementation notes: correct operation on freebsd/arm requires the matching pinned modernc.org/libc (v1.73.4), which fixes the per-arch mmap(2) off_t encoding for 32-bit FreeBSD; without it the WAL shared-memory mapping faults with SIGBUS under concurrent access, the same class of bug found on the netbsd port. As usual, downstream modules must pin the exact modernc.org/libc version this module's go.mod pins.
    • See [GitLab merge request #119](https://gitlab.com/cznic/sqlite/-/merge_requests/119), thanks Olivier Cochard-Labbé (@​ocochard)!
    • Add a Go-facing wrapper for SQLITE_CONFIG_PCACHE2. PageCache is the factory and Cache the per-database instance, both idiomatic Go interfaces; Page exposes the raw Buf and Extra pointers that SQLite reads through the C pcache contract. RegisterPageCache and MustRegisterPageCache install the module process-globally before the first sql.Open; subsequent Open calls are gated through a one-shot Xsqlite3_config(SQLITE_CONFIG_PCACHE2) so a too-late Register returns ErrPageCacheTooLate rather than silently falling through to the built-in pcache1. The binding owns the sqlite3_pcache_page stub and re-consults the implementation on every Fetch, reusing the stub only when the returned Page value is unchanged, which keeps a bounded/evicting purgeable cache safe by construction.
    • See [GitLab merge request #126](https://gitlab.com/cznic/sqlite/-/merge_requests/126), thanks Ian Chechin!
    • Add modernc.org/sqlite/pcache, the reference page-cache implementation that accompanies the #126 SQLITE_CONFIG_PCACHE2 wrapper. pcache.New returns a *Pool satisfying the PageCache interface; register it once with sqlite.MustRegisterPageCache(pcache.New()) and every connection opened afterwards draws its pages from it. Each Pool.Create mints a fresh per-database Cache: a bounded, LRU-evicting page store that honours the PRAGMA cache_size soft cap and releases the least-recently-unpinned page when it must make room. Page memory — the Buf and Extra buffers SQLite reads through — is allocated with libc.Xmalloc/libc.Xcalloc and therefore lives off the Go heap, which keeps SQLite's interior pointer arithmetic on the page extras from tripping the race detector's checkptr enforcement. Pool.Stats reports aggregate lifetime counters (hits, misses, allocs, evictions, rekeys, truncates, caches) across every cache a Pool has created, so hit/miss/eviction behaviour is observable without instrumenting individual caches. Cross-connection page sharing is out of scope for now; each Create returns an independent per-database cache.
    • Validated end-to-end against the #126 stress workload (cache_size=16, 4000 BLOB rows with DELETE and incremental_vacuum, integrity_check clean under -race) and benchmarked for the memory-utilization goal tracked in [GitLab issue #204](https://gitlab.com/cznic/sqlite/-/issues/204).
    • See [GitLab merge request #127](https://gitlab.com/cznic/sqlite/-/merge_requests/127), thanks Ian Chechin!
    • Tighten the modernc.org/sqlite/pcache reference implementation per cznic's !127 review follow-ups. Adds Stats.EasyRefusals, a per-Pool counter for the cases where FetchCreateEasy returns nil at cap; SQLite reacts to a refusal by spilling dirty pages and retrying with FetchCreateForce, so the new field is a direct proxy for the I/O pressure the strict Easy contract imposes vs pcache1's recycle-without-spill behavior. BenchmarkPoolEvictionChurn was reworked to drive a rotating-residue DELETE (k % 3 = i % 3) and re-insert a matching batch each cycle so the spill pressure recurs and easy-refusals/op scales with b.N instead of capping at the seed's one-time first-cycle cost; both existing benchmarks now report easy-refusals/op alongside the page-allocs/evictions metrics. Stats.Evictions documentation was tightened to match the actual behavior (counts LRU eviction, Unpin(discard=true), Shrink releases, and Unpin(discard=false) trimming back to target after a FetchCreateForce overcommit; bulk frees from Truncate, Rekey collisions, and Destroy are not counted). The TestPoolRoundTripIntegrity comment claiming the workload exercises xRekey ~15 times has been corrected; the SQL surface does not reliably emit xRekey here, and that codepath is covered by the unit tests instead.
    • See [GitLab merge request #130](https://gitlab.com/cznic/sqlite/-/merge_requests/130), thanks Ian Chechin!
    • Make modernc.org/sqlite/pcache -race-clean under SQLite's cache=shared mode. The pool already runs correctly under shared-cache because every callback into a given Cache is serialised internally by SQLite's sqlite3BtreeEnter on the BtShared mutex; verified empirically with a lock-free in-flight probe (max-in-flight = 1 on the canonical two-connection workload, 4 on a positive control with goroutines hitting the cache directly). However the Go race detector does not recognise SQLite's libc mutex as a happens-before edge and reports false-positive races on Fetch vs Unpin reads/writes of the per-cache state, which surfaces as DATA RACE failures for any user who registers the pool and runs their suite under -race. A sync.Mutex on the cache type is now taken on every public method (SetSize, PageCount, Fetch, Unpin, Rekey, Truncate, Destroy, Shrink), always. On the common non-shared-cache path the lock is uncontended (one atomic CAS per Lock/Unlock pair, negligible next to the SQLite work it bookends); on the shared-cache path it just rubber-stamps the order SQLite's BtShared mutex already established. A new e2e_test.go TestSharedCacheTwoConns_Integrity drives two sql.Conn against the same cache=shared URI with concurrent writers and asserts PRAGMA integrity_check = ok under -race; passes cleanly with the lock, would surface the false-positive without it. Design notes live in pcache/sharing.go.
    • See [GitLab merge request #131](https://gitlab.com/cznic/sqlite/-/merge_requests/131), thanks Ian Chechin!
    • Add a Go wrapper for sqlite3_db_status, the per-connection runtime counters (cache hit/miss/write/spill rates, schema and prepared-statement memory, lookaside usage, deferred foreign keys). DBStatus is an interface implemented by the driver connection and reached through the database/sql escape hatch (*sql.Conn).Raw(), mirroring the existing FileControl surface; DBStatusOp is a distinct typed enum of the SQLITE_DBSTATUS_* verbs so a counter from a different op family will not compile in its place. Status(op, reset) returns the (current, high) pair and optionally resets the counter. This also lets modernc.org/sqlite/pcache measure real I/O instead of the EasyRefusals proxy: the new BenchmarkPoolSpillIO reads the pager-level SQLITE_DBSTATUS_CACHE_SPILL/_CACHE_WRITE counters, which the pager maintains identically for pcache1 and the pool, making the pcache1-vs-pool comparison cznic raised on the !127 review a genuine apples-to-apples measurement. On the rotating-residue eviction-churn workload at cache_size=16 the pool spills ~3.5x more than pcache1 (cache-spill/op 31.96 vs 8.96) for ~3% more page writes (cache-write/op 450 vs 436) at identical hit/miss, quantifying the I/O cost of the strict Easy contract that EasyRefusals only proxied.
    • See [GitLab merge request #132](https://gitlab.com/cznic/sqlite/-/merge_requests/132), thanks Ian Chechin!
    • Add an opt-in _dqs DSN query parameter that disables SQLite's double-quoted string literal compatibility quirk on a per-connection basis. When _dqs=0 (or any strconv.ParseBool false value) is supplied, the driver calls sqlite3_db_config with SQLITE_DBCONFIG_DQS_DDL and SQLITE_DBCONFIG_DQS_DML set to off before any statement is prepared, so a double-quoted identifier that fails to resolve raises a parse error instead of silently falling back to a string literal. Absence of the parameter, or _dqs=1, leaves SQLite's default behavior unchanged; existing DSNs continue to work byte-for-byte. Resolves [GitLab issue #61](https://gitlab.com/cznic/sqlite/-/issues/61).
    • See [GitLab merge request #128](https://gitlab.com/cznic/sqlite/-/merge_requests/128), thanks Ian Chechin!
    • Add an opt-in _error_rc DSN query parameter for clearer error reporting on open-time failures. When _error_rc=1 (or any strconv.ParseBool true value) is supplied, error strings synthesised from a (rc, db) pair only append sqlite3_errmsg(db) when sqlite3_extended_errcode(db) is consistent with the operation rc (full match first, primary code &0xff as fallback). On mismatch the canonical sqlite3_errstr(rc) is used alone, so an open-time SQLITE_CANTOPEN no longer carries the temporary handle's stale "out of memory" errmsg. Absence of the parameter, or _error_rc=0, preserves the legacy "errstr: errmsg" form byte-for-byte; existing callers that parse error strings are unaffected. The driver's *Error.Code() returns the same SQLite result code in both modes. Parsed before sqlite3_open_v2 so open-time errors are covered. Resolves [GitLab issue #230](https://gitlab.com/cznic/sqlite/-/issues/230).
    • See [GitLab merge request #129](https://gitlab.com/cznic/sqlite/-/merge_requests/129), thanks Ian Chechin!
  • 2026-06-06 v1.52.0:

    • Upgrade to SQLite 3.53.2.
    • Add Backup.Remaining and Backup.PageCount, thin wrappers around the existing sqlite3_backup_remaining and sqlite3_backup_pagecount C symbols. Together they expose the per-Step progress counters that the underlying backup object already maintains, enabling progress reporting during online backups without dropping to modernc.org/sqlite/lib directly.
    • See [GitLab merge request #122](https://gitlab.com/cznic/sqlite/-/merge_requests/122), thanks Ian Chechin!
    • Drop the redundant second copy in (*conn).columnText, the path that backs every Rows.Scan into a Go string for a TEXT column. The value's bytes are still copied once out of SQLite-owned memory into a fresh Go buffer; that buffer is then reinterpreted as the result string with unsafe.String rather than copied a second time by the implicit string([]byte) conversion. This removes one allocation per TEXT value per row and roughly halves the bytes allocated on that path; on the new BenchmarkColumnTextScan cases it is ~13–20% faster for payloads of 256 B and larger, with no measurable change for very short strings. Purely internal: no API or behavioral change, and the returned string never aliases SQLite's buffer.
    • See [GitLab merge request #123](https://gitlab.com/cznic/sqlite/-/merge_requests/123), thanks Ian Chechin!
    • Cache each result column's declared type once per result set in newRows instead of recomputing it on every row. The TEXT branch of Rows.Next calls ColumnTypeDatabaseTypeName for every TEXT column on every row (independent of any DSN flag), which previously did a libc.GoString + strings.ToUpper each time; that lookup is now a single index into a cached, pre-uppercased []string, and ColumnTypeScanType reads the same cache and drops its per-call strings.ToLower. The declared type is fixed for the lifetime of a prepared statement, so the C round-trip is paid once per column rather than once per column per row, removing exactly 1 alloc + 8 B per TEXT column per row from the Next hot path. The new BenchmarkTextToTimeScan cases show ~7% faster on a 1000-row DATETIME SELECT under _texttotime=1. Purely internal: ColumnTypeDatabaseTypeName and ColumnTypeScanType return identical values, no API or behavioral change.
    • See [GitLab merge request #124](https://gitlab.com/cznic/sqlite/-/merge_requests/124), thanks Ian Chechin!
    • Cache, per result column, the parseTimeFormats index that first parsed a TEXT-stored DATE/DATETIME/TIMESTAMP value, and try that format first on later rows instead of re-walking the list from the top. (*conn).parseTime previously ran time.Parse down the format list on every such row; for the canonical SQLite TEXT datetime format every row paid two failed time.Parse attempts — each allocating a *time.ParseError — before the match. On a 1000-row DATETIME TEXT SELECT this cuts ~50% of allocs/op and ~57% of B/op and is ~37% faster. The fall-through chain is preserved exactly: the seven formats are mutually exclusive, so the cached hint can never select a different match than the in-order scan, and the parsed driver.Value is identical to before. Purely internal: no API or behavioral change.
    • See [GitLab merge request #125](https://gitlab.com/cznic/sqlite/-/merge_requests/125), thanks Ian Chechin!
  • 2026-05-28 v1.51.0:

    • Pool the []driver.Value slice passed to scalar/aggregate UDF callbacks and to vtab Filter/Insert/Update callbacks, eliminating the dominant per-row allocation on UDF-heavy queries. Benchmarks on a 1000-row, 3-arg noop scalar UDF show ~40% fewer bytes/op and ~15% fewer allocs/op.
    • Document the matching "arguments are not valid past return" contract on vtab.Cursor.Filter and vtab.Updater.Insert/Update, consistent with the existing rule for FunctionImpl.Scalar / AggregateFunction.Step / WindowInverse.
    • Resolves [GitLab issue #226](https://gitlab.com/cznic/sqlite/-/issues/226). See [GitLab merge request #114](https://gitlab.com/cznic/sqlite/-/merge_requests/114), thanks Ian Chechin!

... (truncated)

Commits
  • 693ff38 upgrade to SQLite 3.53.3
  • 5d24346 Merge branch 'texttotime-aggregates' into 'master'
  • 892d847 sqlite: document _texttotime empty-decltype upgrade, widen #248 comment, add ...
  • f2c8758 sqlite: _texttotime best-effort parse for empty-decltype TEXT columns (#248)
  • See full diff in compare view

feat: browse themes button in settings - @TRC-Loop in #122

Closes #121

Adds a "Browse themes" button to the Themes settings category, between "Import theme..." and "Open folder". It opens https://themes.pelton.app in the user's browser via BrowserOpenURL, the same way About opens links.

Nothing is fetched into the webview, so no third-party content reaches the app. Downloads come back in through the existing import flow.

2 new i18n keys in all five locales.

feat: choose which parts of a theme to import - @TRC-Loop in #120

Closes #106

When a .peltontheme carries both colors and custom CSS, the import modal now asks which parts to bring in. Both are checked by default, so a plain import is still one click.

Behavior

  • The choice only appears when the theme actually has both parts. A colors-only or CSS-only theme has nothing to choose, so nothing is shown.
  • Deselecting CSS also hides the remote-reference warning, since no stylesheet is being installed.
  • Install is disabled if both are unchecked.

Implementation

  • themepack.SelectParts(keepTokens, keepCSS) drops the deselected part's files from the container and rewrites manifest.json, so the copy written into the themes folder is exactly what was picked. Nothing is filtered at apply time.
  • ConfirmThemeImport takes the two flags and applies the selection before WriteContainer.
  • PreviewThemeImport reports tokenCount so the modal knows whether the theme defines colors at all.
  • Wails bindings hand-edited to match.

Tests

internal/themepack/parts_test.go covers all three selections through a write/read round trip: dropping CSS keeps tokens and icons, dropping tokens keeps CSS, keeping both changes nothing.

6 new i18n keys in all five locales.

feat: customizable in-app menu bar - @TRC-Loop in #117

Closes #73.

Lets users customize the in-app menu bar directly on the bar, via an in-place editor mode entered from Settings > Interface.

What

  • Editor mode on the bar: Settings has an Edit menu bar button that flips the live bar into an editor (and closes settings so you edit in place). A Done button exits.
  • Reorder top-level menus (drag the chips) and items (drag within a menu), show/hide anything, and delete custom pieces. Built-ins are never destroyed, so Reset always restores them.
  • + to add a top-level menu on the bar; + inside a menu to add an item, a submenu, or a separator.
  • Submenus (one level deep) render as hover flyouts in the normal bar.
  • Custom entries: click a custom item or submenu to edit its label, action (from the catalog) and icon inline. Icon picker shows theme icons first, then a searchable Tabler picker over the full set.
  • Choose how items shipped by future updates join a saved layout: shown immediately (default) or hidden until added.

How

  • Layout persists as one JSON setting (menu_bar_layout), so it rides the existing config export/import with no backend changes.
  • resolveBar() drives the normal bar (incl. submenu flyouts); the editor renders the raw layout and writes straight to the store, so edits preview live. A merge step folds in newly shipped built-ins and drops removed ones.
  • Drag-and-drop via svelte-dnd-action (menus, items, and per-submenu item zones).
  • Tabler geometry (~2 MB) is a Vite-aliased lazy chunk, loaded only when the icon picker opens; a chosen icon's geometry is stored on the item so the always-on bar renders it without that dataset. macOS-only items stay valid across installs and are filtered per platform.
  • New deps: svelte-dnd-action, @tabler/icons (icon node data). i18n added to all 5 locales.

svelte-check and vite build pass. The drag/click GUI interaction still wants a manual make run smoke test.

feat: offer to set Pelton as the default mail client - @TRC-Loop in #146

Closes #119 (the default-mail-client half; the mailto: handling half is #145).

Lets Pelton register as the system's default mailto: handler, offered quietly: one skippable onboarding step, and a passive line in the About section. Nothing nags.

What changed

Backend (internal/desktop/defaultclient*.go) — a small cross-platform surface (DefaultMailClientStatus, SetDefaultMailClient) over per-OS implementations:

  • macOS (_darwin.go, cgo/CoreServices): detect with LSCopyDefaultHandlerForURLScheme, set with LSSetDefaultHandlerForURLScheme (which shows the system's own confirmation sheet). Compares against the bundle id sh.arne.pelton.
  • Linux (_linux.go): xdg-settings get/set default-url-scheme-handler mailto pelton.desktop.
  • Windows (_windows.go): opens ms-settings:defaultapps (Windows does not allow setting it programmatically); detection returns "unknown" until the installer registers a ProgId, so the About line stays hidden rather than guessing wrong.
  • Other (_other.go): reports unknown / unsupported.

Detection returns a known flag; where a platform cannot answer reliably the UI shows nothing.

Frontend:

  • Onboarding: a new defaultmail step with a single "Set as default" action, skippable via the nav and the global skip. It is dropped from the flow entirely when Pelton is already the default (or the platform can't say), so it never shows a pointless offer.
  • About section: a low-key line + "Set as default" button, shown only when known && !isDefault. No banner, no modal, no repeat prompting.
  • New strings across all five locales (en/de/fr/es/nl).

Notes

  • The macOS build already links cgo (Wails/WebKit), so the CoreServices code adds no new build requirement; the deprecated LS* calls remain the only supported non-private way to read/set a scheme handler.
  • The OS integration (the macOS sheet, xdg association, Windows settings page, and default detection) needs manual verification from a packaged build per platform; the binding surface, onboarding flow and About line are typechecked and the backend builds/vets/tests clean on darwin plus linux/windows cross-compiles.

Verified: go build ./..., go vet, go test ./internal/desktop/, linux+windows cross-compiles all pass; svelte-check clean; all 5 locales at key parity; go.mod/go.sum unchanged after wails generate module.

feat: open mailto: links in a prefilled compose - @TRC-Loop in #145

Part of #119 (the mailto: handling half). Clicking a mailto: link anywhere on the machine now opens a Pelton compose window with the recipient, subject and body prefilled.

This is the first of two PRs for #119. The default mail client half (onboarding step + passive About line + per-platform set/detect) is a separate change and will follow; #119 stays open until then.

What changed

  • RFC 6068 parser (internal/desktop/mailto.go) for to (path + query), cc, bcc, subject, body, with additional to values merged. Unknown headers are ignored; a malformed URL yields an empty draft (blank compose) rather than an error. Percent-decoding uses PathUnescape, so + is a literal plus per the spec (not a space) and %0D%0A in the body is preserved. Fully unit-tested (mailto_test.go).
  • Single instance (SingleInstanceLock): a second launch (e.g. another mailto: click) hands its argv to the running app and surfaces its window instead of spawning a second Pelton. The dev build (PELTON_DEV) uses a separate lock id so it never collides with an installed Pelton.
  • Per-platform delivery:
    • Linux/Windows: the URL arrives as argv (the .desktop file already declares MimeType=x-scheme-handler/mailto; and Exec=pelton %u); it is stashed at launch and picked up on first mount, or handed over via the single-instance path when already running.
    • macOS: mac.Options.OnUrlOpen (the URL arrives as an Apple event, not argv). wails.json now declares the mailto protocol so Info.plist renders CFBundleURLTypes.
  • Frontend: consumePendingMailto() picks up a launch draft after the sidebar loads; onMailtoCompose handles links opened while running. Both route through openComposeWith, which prefills the compose and reveals the cc/bcc rows only when they carry a value.
  • Onboarding-first: if a mailto: arrives before any mailbox exists, the draft is held and opened once onboarding finishes or a mailbox is added, rather than dropped.

Notes / not in this PR

  • No new user-facing strings, so no locale changes here.
  • Windows registry registration (SOFTWARE\Clients\Mail, Capabilities\URLAssociations) is installer work per the issue and is not part of this change.
  • The native OS wiring (macOS Apple event, single-instance handoff, Linux .desktop association) needs manual verification from a packaged build; the parser and the delivery/onboarding logic are covered by tests and typecheck.

Verified: go build ./..., go vet, go test (mailto suite) all pass; cross-compiles for linux and windows succeed; svelte-check clean; go.mod confirmed unchanged after wails generate module.

feat: read-only MCP server for AI agents - @TRC-Loop in #131

Closes #77

A read-only Model Context Protocol server so external AI agents can browse, read and search Pelton's cached mail. Off by default.

Design decisions (agreed in waves)

  • Transport: streamable HTTP bound to 127.0.0.1 only. Pelton runs continuously, so an agent connects to a URL rather than spawning it. A requireLoopback guard makes binding to a routable interface impossible.
  • Library: the official github.com/modelcontextprotocol/go-sdk.
  • Scope: read-only. Tools: list_accounts, list_folders, list_messages, get_message, search_messages. get_message returns headers, message metadata (Message-ID, size), the plain-text body, the HTML body when present (most mail has one), and attachment metadata (name, type, size) but never attachment bytes. No tool sends, moves, flags or deletes; that guarantee rests on the Mailbox interface having no mutating method. Write actions are tracked in #127.
  • Auth: a bearer token Pelton generates. Every request is checked with a constant-time comparison before it reaches the MCP handler, so an unauthorized caller cannot even enumerate the tools.
  • Settings home: a new External category (the shared home for external-service integrations; VirusTotal #129 will live here too), with an enable toggle, editable port, the token with copy + regenerate, and a ready-to-paste client config snippet.

Backend

internal/mcpserver holds the protocol plumbing behind the narrow Mailbox interface; internal/desktop/bind_mcp.go adapts the store and search index to it and owns the lifecycle. The server starts at boot when enabled, and any settings change (enable, port, token) stops and restarts it under one lock. Settings persist as mcp_enabled / mcp_port / mcp_token.

Tests

internal/mcpserver/mcpserver_test.go: every tool round-trips over an in-memory transport and returns its data (including that list_messages honours the limit and search params are forwarded); typed output is exposed as structured content, not only JSON text; the bearer middleware rejects missing/wrong/prefix-less tokens and admits the correct one; Start refuses to run without a token; requireLoopback accepts loopback and rejects routable addresses.

Verification

go build ./..., full go test ./internal/..., go vet, gofmt clean; svelte-check 0 errors, frontend build clean. New i18n keys added to all five locales.

Summary

  • Added an optional, disabled-by-default, read-only MCP server for browsing, reading, and searching cached mail.
  • Exposed the server over loopback-only streamable HTTP with bearer-token authentication.
  • Added MCP tools for accounts, folders, messages, message details, and search, including body and attachment metadata.
  • Added External settings UI with enablement, port configuration, token regeneration, connection details, and localized translations.
  • Integrated persistent settings, startup, shutdown, and lifecycle restart handling.
  • Added adapters for Pelton’s mailbox store and search index.
  • Added tests covering tool behavior, JSON results, authentication, token requirements, and loopback validation.

AI usage

The change appears Very Likely to have used AI assistance, based on the provided AI-generated file summaries and highly structured implementation. There is no evidence here that it was fully agentic or used 100%; AI was likely used to help with implementation and documentation.

feat: theme creator color picker, metadata and advanced section - @TRC-Loop in #124

Closes #107

All three parts of the issue.

Color picker

Replaces the native <input type="color"> well with ColorPicker.svelte: a saturation/value area, hue and alpha sliders, and typed hex plus R/G/B fields. The color model lives in frontend/src/theme/color.ts and parses #rgb, #rgba, #rrggbb, #rrggbbaa, rgb() and rgba(), in comma or space form.

  • Alpha is a first-class control, since the built-in border tokens are rgba(...). Output is hex while opaque and rgba() once it is not, matching how the built-in palettes are written.
  • Values the model cannot parse (color-mix(), named colors) still round-trip: the text field stays authoritative and the picker only overwrites the token once you actually pick something.
  • The popover is fixed-positioned and flips above the well near the bottom of the screen, because the modal body scrolls and would clip an absolutely positioned panel.

Editable metadata

Author and version join name in the editor and are written into manifest.json, so exported .peltontheme files carry correct data. Editing an existing theme now loads through a new GetThemeDraft binding, which returns the theme in editor form rather than apply form.

Advanced section

A collapsed <details> holding:

  • Raw tokens: the rest of the themeable surface (accent, link, selection, radii, fonts, type scale, elevation), labelled by token name, since this is the raw view. The derived -bg partners of the status colors stay out, as the save computes those.
  • Custom CSS: a textarea applied on top of the tokens, previewing live like every other field.

CSS is stored as one file the editor owns (css/custom.css), so editing a theme that already ships stylesheets adds to them instead of overwriting the author's work. Remote references are stripped on save: CSS typed or pasted into the editor is held to the same no-network rule as an import, and unlike an import there is no author to ask. Capped at 256 KB with a clear error, well under the container's own limit.

Tests

internal/desktop/bind_theme_editor_test.go covers the stylesheet handling: appended once and never duplicated, remote references and @import stripped, clearing removes both the file and the manifest entry while leaving the theme's own stylesheets alone, oversized input rejected.

11 new i18n keys in all five locales.

feat: verbose sync shows current mailbox in status line - @TRC-Loop in #144

Closes #128.

Adds an opt-in verbose sync mode so a running sync says which mailbox it is working on instead of a plain "Syncing", which made large or slow syncs look stuck.

What changed

  • New verbose_sync preference (off by default), wired through UIPrefsDTO, the prefs store, and SettingKeys.
  • The frontend now subscribes to the sync:progress events the backend already emitted (folder name + done/total). A new syncFolder store holds the mailbox currently being synced; it clears on the trailing progress event and when the sync ends.
  • The status line renders "Syncing {mailbox}…" when verbose sync is on and a folder is in progress, falling back to the plain label otherwise.
  • Toggle added to the Power settings section, next to the auto-sync interval.

Notes

  • No new backend event was needed, the per-folder progress was already being emitted from syncFolders, it just had no consumer.
  • i18n keys added to all five locales (en/de/fr/es/nl).
feat: views (preset searches) - @TRC-Loop in #135

What

Adds Views (preset searches): user-defined saved searches surfaced as their own entries, per the decisions on the issue.

A View = name + icon + color + a filter (free text, from/to/subject, relative date window, and scope: unread-only / flagged-only / has-attachment / account or all).

Behaviour

  • Eager-run for all views: message bodies are already fully cached at sync time, so there is no per-view prefetch. Instead every view's query runs on startup, after each sync, and after local read/flag/delete changes, in the background, feeding a live count badge and instant open.
  • Placement (hidden by default): a setting under Sidebar chooses how Views appear: Hidden (feature off), In sidebar (a group above the account trees), or Separate tab (a Mail/Views toggle that swaps the sidebar body).
  • Creation: a "Save as view" button in the search bar (promotes the current query) and a dedicated New view builder. Views are reorderable by drag.
  • Reachability: sidebar rows, a New view action in the customizable menu bar, and assignable keyboard shortcuts (new-view, next-view, prev-view).

Backend

  • views table (migration 0011) + CRUD + reorder (internal/storage/views.go).
  • View execution engine (internal/desktop/bind_views.go): text criteria go through the full-text index, pure-scope views read the store, then scope/date/hygiene filters apply in Go. Results cap at 500 newest-first. Bound methods ListViews/SaveView/DeleteView/ReorderViews; ListMessages gains a savedView kind. views:changed event drives badge refresh.

Frontend

  • Types, api, a views store (+ global editor state), sidebar SavedViews group and tab toggle, ViewEditorModal, curated icon/color palette, viewsPlacement preference, and the menu/shortcut wiring.
  • i18n across all five locales.

Tests / checks

  • internal/storage view CRUD + reorder tests; go build + desktop/storage/imap suites pass.
  • svelte-check: 0 errors / 0 warnings; vite build succeeds.

Notes

  • Command palette (the powerful fuzzy reachability) is tracked separately in #134.
  • Curated view icon set (not the full tabler dataset) keeps the feature light.

Closes #103

feat: VIP senders with native new-mail notifications - @TRC-Loop in #142

Adds VIP senders and native OS new-mail notifications (#126).

What it does

  • Mark any sender as a VIP: a star toggle in the reading-pane header and a context-menu action on any message row. VIPs are managed in Settings > Notifications (add by address, remove).
  • New mail from a VIP raises a native OS notification (macOS Notification Center, Windows toast, Linux dbus, via beeep) even when general new-mail notifications are off, so important senders cut through.
  • A separate general "Notify on new mail" toggle (off by default) notifies for all new inbox mail.
  • VIP senders show a star next to the sender in the list, reading pane and search results. The star updates live the moment a sender is marked, backed by a client-side store.
  • Notifications fire only for INBOX (a full sync of Sent/Archive never notifies) and skip already-seen mail.

Matching

Exact address only. Stored lowercased; a changing display name never breaks the match (bareAddress helper on both sides).

How it's built

  • Sync engine now surfaces the storage ids of freshly fetched messages (FolderSyncResult.NewIDs); the desktop layer loads them and decides what to notify.
  • New notification layer (notify.go) with a Go-side locale table, mirroring the native-menu i18n approach since notifications are built before the webview and beeep takes plain strings.
  • VIP storage/logic (bind_vip.go) with unit tests; general toggle in UIPrefs; senderVip on the message summary DTO.
  • Frontend: vip store, VIPSendersModal, star surfaces, settings section; i18n across all five locales.

Known limitation

beeep has no per-notification click callback, so clicking a notification does not open that exact message. Exact click-to-open would need platform-specific code and is worth a separate issue.

Closes #126

fix: rename settings category Network to Proxy - @TRC-Loop in #137

Renames the user-facing settings category label from Network to Proxy in all five locales, since the category only holds proxy settings. Internal category id and the network.proxy.* key namespace are unchanged.

Closes #116

fix: resync off the idle connection so new mail arrives promptly - @TRC-Loop in #133

What

New mail was fetched over the connection that was still parked in IMAP IDLE, so the fetch could not run until IDLE stopped, which only happened on go-imap's 28-minute idle restart (or app shutdown). New mail therefore took minutes to appear instead of seconds.

Fix

  • internal/imap: add IdleUntil(ctx) which parks on IDLE, blocks until the server pushes an update / ctx is cancelled / the connection drops, and always stops IDLE before returning so the connection is free to FETCH. Removed the now-unused Updates() accessor and the old Idle() that held IDLE open for the whole session (exposing it invited exactly this misuse).
  • internal/desktop: idleSession is now a single-goroutine loop: IdleUntil -> on update take syncMu, resync INBOX, release, idle again. No concurrent FETCH on the idling connection; syncMu is held only for the brief resync, so manual/background syncs no longer stall behind it.
  • cmd/imaptest: updated to the new loop API.

Tests

  • TestStopIdle (close/wait error reaping) and TestDrainUpdates in internal/imap.
  • Existing imap + desktop suites pass.

Closes #125

fix: stop reading pane jitter on fractional display scaling - @TRC-Loop in #141

The reading pane iframe is sized to its content height via a ResizeObserver that writes each measured body height back onto the iframe. On fractional display scaling (125%/150%, common on Fedora's WebKitGTK) the applied height snaps to a device pixel differently from how getBoundingClientRect measures it back, so the body flips between two adjacent pixel heights forever. That continuous 1px oscillation is the visible shaking that makes mail unreadable.

Fix: add hysteresis in measure() so a new reading only takes effect when it differs from the applied height by more than a pixel. This settles the feedback loop while still tracking real content growth and shrink. No behavior change on integer scaling, where diffs were already 0.

Closes #123

Full Changelog: v1.0.9...v2026.3