<?xml version='1.0' encoding='UTF-8'?>
<rss xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" version="2.0">
  <channel>
    <title>TopPresenter</title>
    <item>
      <title>0.1.0-alpha.28</title>
      <sparkle:version>44</sparkle:version>
      <sparkle:shortVersionString>0.1.0-alpha.28</sparkle:shortVersionString>
      <sparkle:channel>beta</sparkle:channel>
      <description>⌘K: bare book names open the book; impossible verse numbers clamp

'apocal'/'apocalipsa' returned NOTHING — not a reference (no chapter), not
in verse text, not a song. A query that is just a book name (≥3-char name
prefix or exact abbreviation, strict — never the fuzzy word fallback) now
resolves to an open-book reference rendered as 'Apocalipsa — Deschide
cartea' (navigates to chapter 1; ⌘Enter presents chapter 1).

'apocalipsa 22 420' offered verse 420 with an empty preview — the parser
validated chapters but not verses. BookIndexEntry now carries per-chapter
max verse numbers (stamped in buildVerses): an impossible start verse falls
back to a chapter reference, an impossible range end clamps to the last
verse (22:15-420 → 22:15-21).

Tests: bare-book resolution incl. '1 ioan'/abbrev/too-short guards, verse
drop/clamp/pass-through, palette-level 'apocal' integration (159 green)</description>
      <pubDate>Sun, 12 Jul 2026 21:24:21 GMT</pubDate>
      <enclosure url="https://github.com/RobyRew/TopPresenter/releases/download/v0.1.0-alpha.28/TopPresenter-0.1.0.zip" length="11118843" type="application/octet-stream" sparkle:edSignature="H7D+qQcckhcqONHSvTeHJ4eXK9An8NajOjHOLiqBHOZ8P4qTg+Iqzb8vWmgC9gDjyroC0eaNV7JDsLvOU+1nDQ==" sparkle:version="44" sparkle:shortVersionString="0.1.0-alpha.28" />
    </item>
    <item>
      <title>0.1.0-alpha.27</title>
      <sparkle:version>43</sparkle:version>
      <sparkle:shortVersionString>0.1.0-alpha.27</sparkle:shortVersionString>
      <sparkle:channel>beta</sparkle:channel>
      <description>⌘K ranking v3: canonical order, book-aware verse boost, totals + Arată mai multe, autoscroll fix

- buildVerses sorts chapters/verses — relationship arrays are unordered, so
  the 'canonical order' tie-break was random (Matei 18:1 above Matei 1:16)
- bookHint (strict name/abbrev prefix, ANY token position, no fuzzy):
  'isus fapte' / 'fapte isus' scope 'isus' to Faptele Apostolilor — scoped
  verses rank first, global text matches follow
- Global verse fill spreads max 2/book first (isus → Matei/Marcu/Luca/Ioan,
  not 6x Matei), then relaxes so expansion reaches everything carried
- Songs rank by presentation popularity inside each bucket
  (HistoryStore.songSummaries → presentCounts, refreshed per rebuild)
- PaletteSearch carries 50/category + TOTAL counts; section headers show
  'N rezultate' + Arată mai multe/Mai puține (per-query reset), truncation
  note past the carried 50
- Autoscroll: keyboard-only via scrollTarget with minimal anchor (no more
  re-centering); hover ignored ~250 ms after an arrow press so the list
  moving under a stationary cursor can't steal the selection
- Tests: canonical builder order, bookHint positions/guards, scoped-first,
  diversity+totals, popularity (156 green)</description>
      <pubDate>Sun, 12 Jul 2026 21:02:43 GMT</pubDate>
      <enclosure url="https://github.com/RobyRew/TopPresenter/releases/download/v0.1.0-alpha.27/TopPresenter-0.1.0.zip" length="11116521" type="application/octet-stream" sparkle:edSignature="Ntl86zAlyQ61Cx9V7cUoT+LJw6XGkIIkafKy2YVrt5J+9NvdD6GJcxh/a567m3brrtgfzk0AnHwKBZewb7T1BA==" sparkle:version="43" sparkle:shortVersionString="0.1.0-alpha.27" />
    </item>
    <item>
      <title>0.1.0-alpha.26</title>
      <sparkle:version>42</sparkle:version>
      <sparkle:shortVersionString>0.1.0-alpha.26</sparkle:shortVersionString>
      <sparkle:channel>beta</sparkle:channel>
      <description>⌘K: result priority order, exact numeric matching, row-identity fix

- Priority (user-locked): reference → song TITLE matches → verse text →
  songs matched only in lyrics („Cântece – potrivire în versuri") → media
  → sessions. PaletteHits splits songsByTitle / songsByContent.
- Numeric query tokens now match EXACTLY and never fuzz — "matei 1 2" was
  pulling every song quoting "Matei 28:19" because 1 prefix-matched 19 and
  2 matched 28. Single digits are now indexed (songbook numbers) so
  "cantarea 5" still works.
- Rows are identified by result id instead of .id(flatIndex) — the index
  identity override made lazy rows render one result's content under
  another section's header (reference row showing a song). Keyboard scroll
  follows the id.
- Tests: title-vs-lyrics bucket split, matei-1-2 regression, single-digit
  exact match, no numeric fuzz (151 green)</description>
      <pubDate>Sun, 12 Jul 2026 17:28:35 GMT</pubDate>
      <enclosure url="https://github.com/RobyRew/TopPresenter/releases/download/v0.1.0-alpha.26/TopPresenter-0.1.0.zip" length="11084831" type="application/octet-stream" sparkle:edSignature="JP27NWuBm8Y8F48Vord1JneQC00dpijteGuWvZILfKT90WrVgF1o1oq9Rt1XvdKgSp9FSU50ohgUbmpDn3DUBQ==" sparkle:version="42" sparkle:shortVersionString="0.1.0-alpha.26" />
    </item>
    <item>
      <title>0.1.0-alpha.25</title>
      <sparkle:version>41</sparkle:version>
      <sparkle:shortVersionString>0.1.0-alpha.25</sparkle:shortVersionString>
      <sparkle:channel>beta</sparkle:channel>
      <description>⌘K visual redesign: fix the floating-slab layout, Spotlight-grade styling

The panel used .frame(maxHeight: 480) — a max-height frame is GREEDY
(min(proposed, max)), so the palette rendered as a fixed 480-pt slab with
the content floating loose inside it (big dead area above the field).
The panel now hugs its content; the results area gets a fixed 356-pt
height so the size stays stable while typing.

- regularMaterial + continuous 14-pt corners + hairline border + deeper
  shadow; footer bar tinted; panel clipped so nothing pokes past corners
- Search row: 19-pt field, cleaner icon/clear affordances
- Sections: uppercase micro-labels with tracking
- Rows: 28-pt tinted icon chips, tighter type ramp, return-glyph on the
  selected row
- Preview pane rebuilt: tinted icon header + kind label, metadata rows
  (autor/carte/versiuni), quoted first-line block for songs, line-spaced
  verse text
- Idle state: compact category chips instead of a bare text line; no-results
  and searching states tightened
- Shared PaletteResult metadata (icon/tint/title/subtitle) between row and
  preview instead of duplicated switches</description>
      <pubDate>Sun, 12 Jul 2026 16:31:32 GMT</pubDate>
      <enclosure url="https://github.com/RobyRew/TopPresenter/releases/download/v0.1.0-alpha.25/TopPresenter-0.1.0.zip" length="11082706" type="application/octet-stream" sparkle:edSignature="lOmoVoy9T6gTFuHZawpCN1J3fIU1xLIJyeSv74jSakANX3EOfQ5Jm2wymRpqGZlqSxpOaNsRfQp9G/ShMrKABw==" sparkle:version="41" sparkle:shortVersionString="0.1.0-alpha.25" />
    </item>
    <item>
      <title>0.1.0-alpha.24</title>
      <sparkle:version>40</sparkle:version>
      <sparkle:shortVersionString>0.1.0-alpha.24</sparkle:shortVersionString>
      <sparkle:channel>beta</sparkle:channel>
      <description>⌘K v2: one detached search per keystroke, typo tolerance, polish; instant Bible verse clicks; system Spotlight

Palette was recomputing the FULL search ~30x per keystroke on the main
thread (computed sections + per-row runningIndexBase). Now PaletteSearch.run
executes once per debounced keystroke in a detached task over an immutable
snapshot; body renders state only.

- TokenIndex.fuzzyCandidates: prefix-Levenshtein typo fallback (0/1/2 edits
  by token length) when a token has zero exact-prefix hits
- Verse full-text gets its own TokenIndex (no more linear contains over 31k
  rows); phrase hits rank first
- Palette polish: highlighted matches (diacritic-safe), Recente on empty
  query (PaletteRecentsStore, cap 10), hover selects, indexing/search states
- Bible verse click: single tap via simultaneousGesture (no more
  double-click-interval wait), O(1) selection via selectedVerseIDs Set,
  VerseRunsCache stops per-row runsJSON decodes, headings mapped once per
  chapter; same fixes in verse grid + search results
- CoreSpotlight: songs + sessions indexed after every SearchIndex rebuild;
  Spotlight results deep-link into the app (MainWindowRoot.openFromSpotlight)
- Tests: fuzzy distance policy, typo'd song/verse queries, highlight ranges,
  Spotlight id parser (149 green)</description>
      <pubDate>Sun, 12 Jul 2026 16:13:40 GMT</pubDate>
      <enclosure url="https://github.com/RobyRew/TopPresenter/releases/download/v0.1.0-alpha.24/TopPresenter-0.1.0.zip" length="11058078" type="application/octet-stream" sparkle:edSignature="GJSrEDc4McJEw++z9SpKDboT4Wm4ZQMHMaWQBejzQzkhLbL5t6zoUi24vZmjvEU6iewUAGB/qTgyi/DcJezKAQ==" sparkle:version="40" sparkle:shortVersionString="0.1.0-alpha.24" />
    </item>
    <item>
      <title>0.1.0-alpha.23</title>
      <sparkle:version>39</sparkle:version>
      <sparkle:shortVersionString>0.1.0-alpha.23</sparkle:shortVersionString>
      <sparkle:channel>beta</sparkle:channel>
      <description>Fix six Swift 6 strict-concurrency errors

- PresentationManager: screen-change observer asserts MainActor (queue: .main)
- BibleView / MainControlView: dropped-URL collection behind OSAllocatedUnfairLock
  instead of mutating a captured var across provider callback threads
- PreviewPanelView: ForceTouch view teardown via isolated deinit (SE-0371)
- WindowNotifications: PresentationCommandRouter handlers typed @MainActor
- TextBoxLayout: transitionRow get/set typed @MainActor; pass { get() } to
  Binding — the direct function value makes swift-frontend crash emitting an
  @isolated(any) reabstraction thunk (Xcode 26.2 IRGen SmallVector abort)</description>
      <pubDate>Mon, 06 Jul 2026 19:19:15 GMT</pubDate>
      <enclosure url="https://github.com/RobyRew/TopPresenter/releases/download/v0.1.0-alpha.23/TopPresenter-0.1.0.zip" length="10999993" type="application/octet-stream" sparkle:edSignature="3cwI+MYaP1LUzOzgzIBg5zEc+d3jA22Bi9qVDE5Ebz0E1yKYcXGYW6n+inkx8ZTNTAbewS4cocj2/FyiAO0sDQ==" sparkle:version="39" sparkle:shortVersionString="0.1.0-alpha.23" />
    </item>
    <item>
      <title>0.1.0-alpha.22</title>
      <sparkle:version>38</sparkle:version>
      <sparkle:shortVersionString>0.1.0-alpha.22</sparkle:shortVersionString>
      <sparkle:channel>beta</sparkle:channel>
      <description>Search/browse performance backbone + ⌘K rewritten as an indexed palette

The library browser and the old ⌘K scaled O(everything) per keystroke: the
Songs panel flat-mapped ALL songs from all collections and re-filtered +
re-sorted them on every body evaluation (faulting media/songbook/versions
relationships per row), and the ⌘K overlay fetched EVERY BibleVerse in the
database (all translations), all songs (twice), and all modules on each
keystroke — on the main thread.

SearchIndex (Core/Search/SearchIndexService.swift):
- @ModelActor builder walks SwiftData OFF-main and produces immutable Sendable
  projections (SongIndexEntry with title/author/badges/first-line/folded blob,
  media/session entries) in set-based queries — no per-song faulting.
- Token inverted index (sorted token table + posting lists, prefix-matched via
  binary search, AND-intersected) — song search over 60k stays sub-millisecond.
- Per-sort-key cached orderings; diacritic-insensitive folding everywhere.
- Full-text verse index for the ACTIVE translation (follows module switches);
  media + session name search; debounced background rebuilds on the new
  .libraryDidChange notification (posted by every mutation site).

Songs browser on projections (SongsView):
- filtered/grouped/languages/rows/grid cells read index entries only — zero
  SwiftData work per keystroke or per cell; the real @Model is fetched on
  demand by id (predicate + fetchLimit 1) for selection and menu actions.
- "Se indexează biblioteca…" placeholder during the initial background build.

⌘K → QuickSearchPalette (Spotlight-style, old overlay deleted):
- Centered glass palette, grouped results (Referință / Cântece / Versete /
  Media / Sesiuni) with a right-side preview pane and full keyboard control.
- Enter OPENS/navigates; ⌘Enter PRESENTS live (verses set the Bible anchor,
  songs build slides with current options, media through MediaPresenter).
- New pure BibleReferenceParser: "ioan 3:16", "1 cor 13 4-7", "ps 23" —
  prefix + abbreviation + leading-digit book matching, diacritic-insensitive.

DB/query hygiene:
- #Index on Song (id, title, ccliNumber) — index-only lightweight migration.
- SessionService song resolver: ccli:NNN keys resolve via an indexed predicate
  fast-path instead of fetching the whole song table.

Tests: token index prefix/AND/folding; reference parser (simple, ranged,
abbreviations, leading digits, non-references); full suite green.</description>
      <pubDate>Mon, 06 Jul 2026 18:27:45 GMT</pubDate>
      <enclosure url="https://github.com/RobyRew/TopPresenter/releases/download/v0.1.0-alpha.22/TopPresenter-0.1.0.zip" length="10997656" type="application/octet-stream" sparkle:edSignature="fLpilaT59ujExV7McqNGkjNZqHxKMJa2JPm6U0K/A526Li/508c9VKwTJWzOikrhYmeacHnu1MrFZ47oBbyICA==" sparkle:version="38" sparkle:shortVersionString="0.1.0-alpha.22" />
    </item>
    <item>
      <title>0.1.0-alpha.21</title>
      <sparkle:version>37</sparkle:version>
      <sparkle:shortVersionString>0.1.0-alpha.21</sparkle:shortVersionString>
      <sparkle:channel>beta</sparkle:channel>
      <description>Presented-flow anchor, background-on-hide, casete reset, versions, folders

Live Bible flow (the "Efeseni 1:3" bug):
- pm.bibleLiveAnchor snapshots what's PRESENTED (translation + book/chapter +
  verse range). While live, ←/→ call stepBibleAnchor — same block size,
  cross-chapter, theme multi-verse formatting, rich casete — so browsing or
  selecting elsewhere NEVER moves the live flow; Show / double-click /
  session runner re-anchor (structured showBibleVerse params); Hide clears it.
- Display-setting changes re-render the ANCHOR, not the browse selection.

Output-only Black/Freeze:
- The preview card always renders content; the output state shows as
  NEGRU / ÎNGHEȚAT badges instead of blanking the preview.

Background stays on Hide:
- pm.backgroundStaysOnHide (theme-persisted, DEFAULT ON, toggle at the top of
  Fundal): Ascunde/Golește hides only the content — the theme background stays.

Casete inherit global for real:
- „Personalizează textul" OFF now writes a fresh BoxTextStyle() (sentinels) —
  stale custom values can no longer shadow the global text parameters.

Song versions:
- Song.originalVersionID (additive): the ORIGINAL = default version —
  presented, searched first, slide source; activeVersion resolves it.
  Other versions stay selectable/presentable as before.
- Import auto-stamps „Date proprii pentru versiune" (overridesMetadata) on
  versions whose metadata differs from the first (book, key, author, …) and
  defaults the original to the first version WITH a songbook.
- GOAT round-trips "original": true; star button next to the version picker
  marks a new original (re-flattens the verse cache + re-links the songbook).

Song editor:
- Deleting a strofă works: removed from the relationship (list refreshes),
  remaining sections renumbered, focus cleared; Renunță still reverts.

Batch import folders:
- DROPPED folders (one or many) now expand exactly like the picker (the drop
  path classified folders without expanding — non-USFM folders read as
  "Unrecognized"); direct files keep their old classification.
- Depth capped everywhere at the selected folder + TWO subfolder levels
  (shared walk + the song enumerator); folder-depth test tightened.

AGENTS.md: Swift 6 patterns, signing invariant, anchor/background/versions/
folder rules documented.</description>
      <pubDate>Mon, 06 Jul 2026 02:04:17 GMT</pubDate>
      <enclosure url="https://github.com/RobyRew/TopPresenter/releases/download/v0.1.0-alpha.21/TopPresenter-0.1.0.zip" length="10909254" type="application/octet-stream" sparkle:edSignature="SgivBC1YyRreplMtID1+BorgEg992OUYJBu2k4HGUxH4bldolLbGha5yGEjlhkdEtqU5IMfetBT5DHvT/E14DA==" sparkle:version="37" sparkle:shortVersionString="0.1.0-alpha.21" />
    </item>
    <item>
      <title>0.1.0-alpha.20</title>
      <sparkle:version>36</sparkle:version>
      <sparkle:shortVersionString>0.1.0-alpha.20</sparkle:shortVersionString>
      <sparkle:channel>beta</sparkle:channel>
      <description>Swift 6 mode + off-main batch import (crash fix) + CI parity overhaul

Swift 6 language mode (SWIFT_VERSION 6.0, all targets):
- Pure data layers opted out of the default MainActor isolation with native
  `nonisolated`: Constants enums, JSON blob helpers, Color hex math, the whole
  Bible import parse layer (importers, rich-text/GBF/USFM helpers, value types).
- Fresh importer instance per import (the shared XML importer instances with
  mutable parser state were a latent data race).
- SE-0371 `isolated deinit` for MainActor teardown (audio timer, screen
  observers, command router tokens, video observers/security scope).
- VersionedSchema identifiers are `let`; test suites annotated @MainActor.

Batch Bible import crash (SIGABRT malloc_zone_error on Thread 3):
- New BackgroundImportActor (@ModelActor) — batch imports run entirely off the
  main actor on its OWN serialized ModelContext; the view's context no longer
  crosses thread hops mid-import.
- ImportService.importBible is nonisolated(nonsending) — runs on the caller's
  isolation, saves per BOOK (chunked) inside autoreleasepool, progress hops to
  the main actor via @MainActor @Sendable handler.
- BatchImportSheet: re-entry guard + per-run status reset (fixes the glitched
  reopened sheet); bibles route through the actor.

CI/CD parity + modernization:
- Runner macos-26 + Xcode 26.3 (17C529) — EXACT match with the dev Mac; the old
  macos-15/Xcode 16.0 pair ignored the project's Swift-6-era settings.
- All actions on current majors (checkout v7, upload-artifact v7,
  download-artifact v8, configure-pages v6, upload-pages-artifact v5,
  deploy-pages v5, gh-release v3) — no more Node 20 deprecation warnings.
- Unit tests are a real gate (removed `|| true`; -only-testing unit suite).
- Uniform inside-out ad-hoc re-sign of Sparkle (XPCs, Autoupdate, Updater.app,
  framework) + the app, preserving entitlements, then strict codesign verify —
  fixes the DYLD "different Team IDs" launch crash on other Macs.
- Pages config cleaned (dangling deleted-branch source removed).</description>
      <pubDate>Mon, 06 Jul 2026 01:28:26 GMT</pubDate>
      <enclosure url="https://github.com/RobyRew/TopPresenter/releases/download/v0.1.0-alpha.20/TopPresenter-0.1.0.zip" length="10865168" type="application/octet-stream" sparkle:edSignature="rmBTRvoeSWIwPzFCjpvFJX5UMyWqGA5W4bJ1gM6aEUQXEHTlmP6fuwniUIIkrEJGUu0sINHYiBpn2yaJbrpfAw==" sparkle:version="36" sparkle:shortVersionString="0.1.0-alpha.20" />
    </item>
    <item>
      <title>0.1.0-alpha.18</title>
      <sparkle:version>34</sparkle:version>
      <sparkle:shortVersionString>0.1.0-alpha.18</sparkle:shortVersionString>
      <sparkle:channel>beta</sparkle:channel>
      <description>Fix actor-isolation warnings (Swift 6-ready) + trailing-closure deprecation

The project's SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor silently actor-bound
a set of pure helpers, producing "called from outside the actor" warnings
(errors in Swift 6 mode):
- HistoryExportService.csvEscape (used as a function value in .map)
- ImportService.detectBibleFormat / detectSongFormat (file inspection)
- DragDropImportHandler's whole classification chain (isImportableFile,
  classify, expandToImportableFiles, classifyExpanded) — it runs on a
  background Task.detached so folder walks never block the UI
- the MediaExtensions / bibleSongExtensions Set constants they read

Mark them all `nonisolated` (pure functions + immutable Sendable constants;
no main-actor state touched). Also label the importSongItems progress
closure explicitly (backward trailing-closure matching deprecation).</description>
      <pubDate>Thu, 02 Jul 2026 22:17:45 GMT</pubDate>
      <enclosure url="https://github.com/RobyRew/TopPresenter/releases/download/v0.1.0-alpha.18/TopPresenter-0.1.0.zip" length="10495931" type="application/octet-stream" sparkle:edSignature="kDKoB68c1sfB7F3kTshINNA3EwZYiBd6lhD13MZ0YEzdcRJAqsfaykhyH8R1hVK+PKmv4CUSVjBeW36G2oEMAQ==" sparkle:version="34" sparkle:shortVersionString="0.1.0-alpha.18" />
    </item>
    <item>
      <title>0.1.0-alpha.17</title>
      <sparkle:version>33</sparkle:version>
      <sparkle:shortVersionString>0.1.0-alpha.17</sparkle:shortVersionString>
      <sparkle:channel>beta</sparkle:channel>
      <description>Commit pinned Package.resolved (fix "Missing package product 'Sparkle'")

Package.resolved was gitignored, so a fresh checkout / reset package cache
had nothing pinning Sparkle and Xcode failed with "Missing package product
'Sparkle'". Track the app's Package.resolved (Sparkle 2.9.3) for
deterministic resolution locally and in CI.</description>
      <pubDate>Thu, 02 Jul 2026 14:18:57 GMT</pubDate>
      <enclosure url="https://github.com/RobyRew/TopPresenter/releases/download/v0.1.0-alpha.17/TopPresenter-0.1.0.zip" length="10495915" type="application/octet-stream" sparkle:edSignature="KV3WHhwKCeeL0ZAho3ZUkYyTSE9Z+lVyp48lsCpvP9gtRAiW61dWg2H/HiH5SbctX6bfKF1QVFbd510RlvQrBg==" sparkle:version="33" sparkle:shortVersionString="0.1.0-alpha.17" />
    </item>
    <item>
      <title>0.1.0-alpha.16</title>
      <sparkle:version>32</sparkle:version>
      <sparkle:shortVersionString>0.1.0-alpha.16</sparkle:shortVersionString>
      <sparkle:channel>beta</sparkle:channel>
      <description>CI: deploy appcast via GitHub Actions Pages (drop gh-pages branch)

Publish the Sparkle appcast straight from CI with actions/deploy-pages
instead of pushing to a gh-pages branch — main is now the only branch.
publish_appcast.sh builds ./site/appcast.xml (seeding from the live feed
so prior items survive) and the workflow uploads + deploys it; Pages
source is set to GitHub Actions. Still guarded on SPARKLE_PRIVATE_KEY.</description>
      <pubDate>Thu, 02 Jul 2026 13:02:45 GMT</pubDate>
      <enclosure url="https://github.com/RobyRew/TopPresenter/releases/download/v0.1.0-alpha.16/TopPresenter-0.1.0.zip" length="10495916" type="application/octet-stream" sparkle:edSignature="5+cCEf+Ygg78zQ3UW6lTgebB2hDKG1VM+BwtCPA3wJcRi8mYAYnXsJ2axT6Tk1VvdK9EpxxCaenH62oyjkSCDQ==" sparkle:version="32" sparkle:shortVersionString="0.1.0-alpha.16" />
    </item>
    <item>
      <title>0.1.0-alpha.15</title>
      <sparkle:version>31</sparkle:version>
      <sparkle:shortVersionString>0.1.0-alpha.15</sparkle:shortVersionString>
      <sparkle:channel>beta</sparkle:channel>
      <description>Add Sparkle auto-updates; move Multi-Verse into themes; UI polish

Auto-updates (Sparkle 2, reusable for future apps):
- SPUStandardUpdaterController wrapper (UpdateController) with launch +
  interval checks, opt-out, cadence, beta channel, silent auto-download,
  and a "Check for Updates…" menu command; guarded so it only starts once
  a real EdDSA key is set (never in the test host).
- Settings ▸ Actualizări tab + in-app Versions picker (install / roll back
  to any release through Sparkle, signature-verified).
- Info.plist Sparkle keys (real SUPublicEDKey), sandbox entitlements
  (network.client + Sparkle mach-lookup exceptions), Sparkle SPM dep.
- CI: ad-hoc sign + monotonic CFBundleVersion; sign &amp; publish a signed
  appcast to GitHub Pages (scripts/publish_appcast.sh + appcast_upsert.py),
  guarded on the SPARKLE_PRIVATE_KEY secret so CI stays green until set.
- UPDATES.md runbook (setup, ad-hoc caveat, Developer ID upgrade path).

Multi-Verse options now live in the theme (verse cassette), not globals:
- ContentOptions gains multiVerseLayout / showNumbers / customEnabled /
  customText; edited in the Layout Editor on the verse cassette, carried by
  theme export/import, and read by every previewer and the live output.
- Custom text supports {verses} {ref} {n} tokens when 2+ verses show.

Right sidebar / settings cleanup:
- Removed the dead "Afișează verse numbers/cross-refs/footnotes" toggles
  (they bound to keys nothing read); kept the working interlinear toggle.
- Rewired the Settings footnotes toggle to the key BibleView actually uses.
- Preview panel ~10% wider; button labels no longer wrap to two lines.

Library / Bible polish:
- Song source file + web link in the DB and header; clickable info chips;
  redesigned song detail header; subtle section headings in list &amp; grid.
- Bible tab title shows the language before the version, e.g. "(RO) EDC100".</description>
      <pubDate>Thu, 02 Jul 2026 11:59:47 GMT</pubDate>
      <enclosure url="https://github.com/RobyRew/TopPresenter/releases/download/v0.1.0-alpha.15/TopPresenter-0.1.0.zip" length="10495927" type="application/octet-stream" sparkle:edSignature="CwBZxHIu0iO/J9Vli5ckHh8Z1z++uRT3NOKKDD7lMuOk2c7CXCslh8Qhg1Pnj0j07w7pnRd+XmM1trEUuPvMCA==" sparkle:version="31" sparkle:shortVersionString="0.1.0-alpha.15" />
    </item>
  </channel>
</rss>