Skip to content

feat: migrate to Nucleus 2.0, Compose 1.11, Jewel 0.37#443

Merged
kdroidFilter merged 72 commits into
masterfrom
feat/nucleus-2-compose-1.11-upgrade
Jun 5, 2026
Merged

feat: migrate to Nucleus 2.0, Compose 1.11, Jewel 0.37#443
kdroidFilter merged 72 commits into
masterfrom
feat/nucleus-2-compose-1.11-upgrade

Conversation

@kdroidFilter
Copy link
Copy Markdown
Owner

Summary

  • Migrate all Nucleus dependencies from io.github.kdroidfilterdev.nucleusframework (v1.14.2 → 2.0.0-alpha)
  • Replace application {} + manual SingleInstanceManager + AotRuntime with nucleusApplication { aotTraining(duration) }; deep-link single-instance bridge replaced with onDeepLink {}
  • Split nucleus-decorated-window into three artifacts: core, tao, jewel
  • Bump Compose 1.10.3 → 1.11.0, Jewel 0.35 → 0.37, IntelliJ Platform Icons 253 → 262
  • Add LocalTextContextMenu capture workaround for Jewel 0.37 / Compose 1.11 NoSuchMethodError on TextManager.getCut()
  • Move window minimumSize from LaunchedEffect to JewelDecoratedWindow declarative param
  • Fix Key.HomeKey.MoveHome for Alt+Home navigation shortcut
  • Host SettingsWindow inside JewelDecoratedWindow scope to satisfy Nucleus 2.0 NucleusApplicationScope receiver

Test plan

  • App launches and reaches main window (no crash on startup)
  • Single-instance enforcement: opening a second instance focuses existing window and relays deep link
  • AOT training mode exits after ~45 s
  • Settings dialog opens and closes correctly
  • Alt+Home (Windows) / Cmd+Shift+H (macOS) navigates to Home tab
  • Window cannot be resized below 600×300
  • Dark/light theme switch works (darkmode-detector still functional)
  • Native menu bar (macOS), jump list (Windows), quicklist (Linux) still render

- Rename all nucleus deps from io.github.kdroidfilter → dev.nucleusframework
- Replace application{} + SingleInstanceManager + AotRuntime with nucleusApplication { aotTraining() }
- Replace manual deep-link single-instance bridge with onDeepLink {}
- Split nucleus-decorated-window into core + tao + jewel artifacts
- Add LocalTextContextMenu workaround for Jewel 0.37 / Compose 1.11 NoSuchMethodError
- Move window minimumSize from LaunchedEffect to JewelDecoratedWindow param
- Fix Key.Home → Key.MoveHome for Alt+Home shortcut
- Bump Compose 1.10.3 → 1.11.0, Jewel 0.35 → 0.37, IntelliJ icons 253 → 262
Remove sortedBy calls in source panels that were overriding SQL ORDER BY ranking.
Ensure sources appear in declared-base priority order: Tanakh → Mishnah → Bavli →
Yerushalmi. Dedup source lines in TOC heading selections. Update to SeforimLibrary
with improved source ranking and density-based link chaining.
Replace large pre-computed maps in cataloggen with hybrid approach:
- Slim down generated CatalogPresets.kt from 41 KB to 6 KB (IDs and dropdown specs only)
- Introduce CatalogAccess for lazy-loaded catalog access with display transformations
- Apply Talmud prefixing for Bavli/Yerushalmi, book filtering, and ancestor label stripping
- Embed TocQuickLink data directly in TocQuickLinksSpec instead of separate maps
- Reduce memory footprint and eliminate drift risk from stable DB IDs

Benefits: JAR -35 KB, -1-3 MB RAM, centralized display logic, fully testable transformations.
Performance: ~30-80ms one-shot build on first catalog access.
- Revert Compose 1.11.0 → 1.10.3 (restores compatibility with Jewel 0.35 API)
- Remove LocalTextContextMenu workaround added for Jewel 0.37/Compose 1.11 NoSuchMethodError
- Restore Key.Home for Alt+Home shortcut (reverts Key.MoveHome change)
- Revert ContextMenuItem constructor to pre-1.11 signature (label, action)
- Ship systemTheme.svg locally since IntelliJ icons 262 dropped it; use PathIconKey
- Keep Nucleus 2.0, Jewel 0.37, minimumSize on JewelDecoratedWindow (Nucleus API)
The java.awt.Cursor was a temporary constraint during the Compose 1.11
migration attempt. Revert to skiko.Cursor which is the proper import.
The scroll position saving logic used Flow.sample(200) to periodically
persist position during active scrolling. However, fixedPeriodTicker (used
internally by sample) ticks continuously even when the flow doesn't emit,
causing 5 Hz wakeups of the FlushCoroutineDispatcher on every tab's
BookContentView composable. Each wakeup triggered a frame redraw, leading
to 10-15 redraws/sec at idle (confirmed by JFR profiling).

Gate the sample ticker behind isScrollInProgress so the ticker only exists
during active scroll, terminating with emptyFlow when idle. This eliminates
the continuous frame invalidation.

Also:
- Add .catch {} to all flow.collect chains to prevent unhandled exceptions
  from killing LaunchedEffect scopes
- Auto-format via ktlint
- Pass 1: explicit corpus anchors (על התלמוד, על התנ״ך) have top priority
- Pass 2: hard-coded families (חברותא, מילונים, מחברי זמננו)
- Pass 3: מפרשים detection with upstream ראשונים resolution to corpus
- Pass 4: bare ראשונים/אחרונים with corpus ancestor lookup and ה-prefixing
- Add integration test verifying cross-corpus commentators excluded from Talmud
- Fix: rif/rosh sub-commentaries now correctly roll up to ראשונים על התלמוד
- Fix: ensure proper corpus labels (התלמוד, התנ״ך) instead of bare corpus names
- Optimize tab visibility by rendering only the selected tab, reducing unnecessary composable updates
- Replace `derivedStateOf` with `rememberUpdatedState` for improved state handling
- Add `knownTabIds` for more efficient tab cleanup logic
- Remove unused modifiers such as `graphicsLayer` and `zIndex` to simplify the layout
- Consolidate `maybeSave` function and streamline scroll data persistence
- Remove unused `splashImage` configuration in `build.gradle.kts`
- Add `persist` flag to `CommentaryColumnScrolled` events to trigger state persistence only after scrolling settles.
- Cache `LazyListState` and `PagingData` flows by commentator ID for efficient handling and restoration of scroll states.
- Introduce debounce and idle gating for scroll state updates, reducing unnecessary redraws.
- Adjust `beyondViewportPageCount` for smoother paging transitions.
- Wrap `CommentatorCell` in `key` blocks to optimize recompositions.
…on logic

- Add LRU caching for retained tab compositions to reduce switching latency.
- Optimize scroll position restoration by incorporating saved anchor indices.
- Refine initial book open logic to handle positioning edge cases.
- Adjust tab rendering to support better memory management and composable visibility toggling.
- Introduce `AsyncHtmlAnnotation` composables for processing HTML content to annotated strings asynchronously.
- Add `StableAnnotatedCache` for efficient caching of processed HTML annotations.
- Replace synchronous HTML parsing in `BookContentView` with async annotation caching for improved responsiveness.
- Introduce pointer zoom state tracking (`LocalBookContentZoomInProgress`) to manage book zoom interactions.
- Refactor pointer zoom handling for fine-grained control, batching updates, and better performance.
- Adjust `compositionLocalOf` for zoom progress state, integrating it across views like `LineTargumView`.
- Consolidate `htmlAnnotationCacheKey` logic for consistency in cache generation.
- Improve prefetching of annotated lines for smoother scrolling.
- Remove redundant brackets and align formatting across functions.
- Enhance test readability through structured updates in assertions.
Replace platformtools.getOperatingSystem() with nucleus Platform.Current enum
and platformtools.getAppVersion() with NucleusApp.version.

- Update PlatformInfo to use nucleus Platform instead of OperatingSystem
- Replace OperatingSystem.MACOS/WINDOWS with Platform.MacOS/Windows
- Use NucleusApp.version for Sentry release tag
- Remove platformtools-core dependency from build.gradle and libs.versions.toml
kdroidFilter and others added 28 commits May 31, 2026 22:56
Add a new General settings option to prevent screen sleep while a book
is open in the current tab and the window is focused. Enabled by default,
uses EnergyManager.keepScreenAwake() from Nucleus framework.

Changes:
- AppSettings: new setting with StateFlow (default true)
- GeneralSettingsEvents/State/ViewModel: wired setting to UI toggle
- UI: SettingCard in GeneralSettingsScreen with Hebrew labels
- main.kt: LaunchedEffect to manage screen awake state based on:
  * setting enabled
  * window focused (state.isActive)
  * current tab showing BookContent destination
- Updated GeneralSettingsEventsTest and GeneralSettingsStateTest

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extract shared installer window chrome, navigation helper, progress bar
controller, database parts picker and download progress UI. Unify the
offline part01/part02 selection on the auto-detect behavior.
Add nucleus.taskbar-progress-tao and drive the taskbar/dock indicator
from InstallerWindow, mirroring the in-app progress for both the
onboarding and database-update flows.
Implements a complete auto-update flow with graduated behavior per OS and update level:
- PATCH updates: pre-download at startup; silent install on close (Win/macOS),
  prompted (Linux, requires pkexec interaction)
- MINOR/MAJOR updates: prompted via titlebar icon + dialog with progress bar
  and database update warning (~3.5 GB)

Key components:
- AppUpdateService: orchestrates Nucleus updater with pure decision functions
  (mode, needsDbWarning, shouldPreDownload) for testability
- UpdateDialog: JewelDecoratedDialog with download progress, state-dependent UI
- UrlOpener: non-blocking URL opener (fixes Desktop.browse freeze on Linux)
- Settings: update status banner (up-to-date/available/error/checking)
- Tests: N1 (pure logic), N2 (orchestration), N4 (HTTP feed) — 12/12 passing

Also includes:
- Nucleus native SSL client for secure downloads on all platforms
- Titlebar update icon (visible only for prompted updates)
- State persistence via TabStateManager for download/install state
- Dev hooks: FORCE_VERSION, FEED_URL, DRY_RUN, FAKE_STATE for testing

Removed obsolete AppUpdateChecker and related code.
…pdate framework

- Update Nucleus updater to latest alpha build
- Remove AppUpdateChecker.kt (superseded by Nucleus framework integration)
- Add e2e update test scripts and throttled feed server for testing
- Update SeforimLibrary submodule with related changes
Add per-line text highlighting backed by a dedicated, read-only-books-independent
user database (UserSettingsDb), reworking PR #340 from an in-memory global object
into a DI-injected, persistence-backed store.

- UserHighlights.sq / LineNotes.sq: new tables in the local user DB (added for
  existing users via CREATE TABLE IF NOT EXISTS, no migration needed)
- HighlightStore: per-book in-memory cache (StateFlow) with write-through to SQLite;
  Color<->Int; offsets keyed by stable lineId so highlights survive DB delta updates
- HighlightSelectionResolver: resolves a (multi-line) selection to per-line ranges by
  splitting on line breaks and anchoring segments to visible lines; diacritics-aware
- Highlighting.applyUserHighlights + HebrewSearch helpers (stripNikudTeamimWithMap)
  aligned with removeAllDiacritics so offsets match the rendered text (keeps geresh/
  gershayim), without touching the search-shared stripDiacriticsWithMap
- BookContentScreen: context-menu color-swatch picker (+ clear) writing to the store
- BookContentView: per-line highlights grouped once and passed as the stable
  emptyList() singleton for unhighlighted lines, so a toggle only recomposes the
  affected line (cuts recomposition allocations ~95%)
- DI: single shared UserSettingsDb instance injected into all user stores, replacing
  the previous per-store driver creation
- LineNoteStore + schema: foundation for upcoming per-line notes (not yet wired to UI)
Render and create position-based highlights in the commentaries pane, keyed by the
commentary's own (targetBookId, targetLineId) so a highlight made on a commentary also
shows when that commentary is opened as a main book.

- CommentaryItem: applies applyUserHighlights (diacritics-aware) before search highlight;
  per-line lists grouped once with the stable emptyList() singleton for non-highlighted
  lines so a toggle only recomposes the affected line
- SelectionContext.activeCommentaryColumn: visible commentary lines of the right-clicked
  column (empty = main pane), used to anchor highlights; cleared on a main-pane right-click
- applyHighlightFromSelection: branches on the active commentary column — comments-pane
  selections resolve over that column (single + multi-line, stored under its book) with a
  single set of HTML parses; main-pane selections resolve over the main visible lines
…ple per line)

Replace the "one note per line" model with a position-based (startOffset, endOffset)
design mirroring the highlights feature. A line may carry several notes (a word,
a phrase, overlapping).

- UserNotes.sq: new table with (bookId, lineId, startOffset, endOffset, note)
- NoteStore: in-memory per-book cache (StateFlow) + CRUD ops (add/update/remove)
- Replaces LineNoteStore (deleted)
- DI: AppGraph + AppCoreBindings updated
- Tests: 5 new tests covering ranged notes, multiple per line, isolation per book
…field keys

- Center "no notes saved" message above info banner
- Justify quoted text in note cards for better readability
- Use unique key for draft editor (lineId + offsets) to properly reset
  field when navigating between lines or changing selections
- Fix: textfield now empties when moving to different line or selection
- Restart via Nucleus AppRestarter instead of platformtools appmanager
- New :releasefetcher module porting GitHubReleaseFetcher with a
  Nucleus native-SSL Ktor client as the default
- Remove platformtools dependencies from the catalog
…p reset

New data settings category for managing user data:
- Database export to ~/Downloads/Zayit with timestamp
- Database import from file selection dialog
- App reset functionality (moved from general settings)
- Improved UI with cards, banners, and confirmation dialogs
- Data settings positioned before about/conditions in menu

Includes Metro DI registration for new DataSettingsViewModel.
Replace event-based approach with direct function calls. Use FileKit dialog
picker instead of Swing JFileChooser for better integration. Simplify state
management and remove intermediate event class.
…d pane

- NoteStore.addNote returns the new row id via lastInsertRowId(); drops the
  fragile offset-match heuristic and the redundant refresh after insert
- NotesPanel uses the returned id directly; null signals a skipped insert
- Hide the bottom "no notes saved" hint when no line is selected so it no
  longer stacks with the centered "no selection" message
- NotesPanel loads its own book (idempotent) instead of relying on BookContentView
- Translate updateNotes doc comment to English
An explicit draft (created from a partial text selection via the context menu)
stayed anchored to its original line, so the editor field kept showing the old
draft after navigating to a different line. Capture the primary line at draft
creation and clear the draft once the selected line diverges from it.
Under the Tao backend, Dispatchers.Main is the single GTK event-loop thread;
the picker's blocking D-Bus (xdg-desktop-portal) work froze it. Mirror the
onboarding pickDatabaseParts fix and run the pickers on Dispatchers.IO.
Nucleus AppRestarter.restartApp() did not actually restart the app. Switch the
app reset and DB import flows back to platform-tools restartApplication(). The
:releasefetcher module stays on the Nucleus-based Ktor client.
The reset button's onReset was a no-op stub, so nothing happened. Port the
full reset (books DB, search indexes, user data, custom DB location) into
DataSettingsViewModel.resetApp() and wire the button to it, then restart.
Remove the now-dead ResetApp event/handler/resetDone from general settings
and update the related tests.
@kdroidFilter kdroidFilter merged commit 6f6e930 into master Jun 5, 2026
6 of 7 checks passed
@kdroidFilter kdroidFilter deleted the feat/nucleus-2-compose-1.11-upgrade branch June 5, 2026 07:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant