diff --git a/.run/SeforimApp [run].run.xml b/.run/SeforimApp [run].run.xml
index 058c9f76b..e807760bf 100644
--- a/.run/SeforimApp [run].run.xml
+++ b/.run/SeforimApp [run].run.xml
@@ -4,7 +4,8 @@
@@ -30,4 +31,4 @@
false
-
\ No newline at end of file
+
diff --git a/CLAUDE.md b/CLAUDE.md
index dac7cc834..73d993bce 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -71,7 +71,7 @@ Configuration files:
This is a **Kotlin Multiplatform desktop application** built with:
- **UI**: Compose Multiplatform + Jewel themes for native desktop look
- **DI**: Metro dependency injection with `AppGraph` accessible via `LocalAppGraph`
-- **Navigation**: Custom RAM-efficient tab system (`TabsNavHost`)
+- **Navigation**: Custom RAM-efficient tab system (`TabsContent`)
- **Data**: SeforimLibrary composite build (core domain + dao persistence + generator tools)
- **Platform**: JVM desktop with macOS/Windows/Linux native packaging
@@ -99,11 +99,11 @@ The `SeforimLibrary` composite build provides the core functionality for Jewish
### Memory-Efficient Tab System
See `TAB_SYSTEM_README.md` for complete details. Key points:
-- Each tab owns its own `NavHostController` and stays alive while tab exists
+- Each tab owns its own `SimpleTabViewModelOwner` (ViewModel lifecycle), which stays alive while the tab exists
- `TabStateManager` persists lightweight state for cold-boot restoration
- `TabsViewModel` manages tab lifecycle (create/select/close/replace)
- Use `TabAwareViewModel` base class for screens needing state persistence
-- Classic tabs mode keeps all tabs in memory; RAM saver mode (configurable) uses single NavHost
+- `TabsContent` renders without Compose Navigation and keeps an LRU of up to `MAX_RETAINED_TAB_COMPOSITIONS` (3) tab compositions; others are torn down and rebuilt from saved state on switch
### Dependency Injection (Metro)
- App-wide graph: `AppGraph` created with `createGraph()`
@@ -170,7 +170,7 @@ See `TAB_SYSTEM_README.md` for complete details. Key points:
### State Management
- **Tab State**: Use `TabAwareViewModel` + `saveState()`/`getState()` for restoration
- **Global State**: Avoid; prefer dependency injection and proper component lifecycle
-- **RAM Optimization**: Enable via `AppSettings.setRamSaverEnabled(true)` if needed
+- **RAM Optimization**: Built-in; only the selected tab plus an LRU of `MAX_RETAINED_TAB_COMPOSITIONS` (3) compositions are kept active (`TabsContent`)
### Security & Configuration
- **Secrets**: Never commit to repository; use `local.properties` for machine-specific settings
@@ -219,7 +219,7 @@ class MyViewModel(
### Performance
- **Hot Reload**: Use `hotRunJvm` + `reload` for fast iteration
-- **Memory**: Monitor tab count; use RAM saver mode for many tabs
+- **Memory**: Bounded by design — at most the selected tab + 3 retained compositions stay active regardless of open tab count
- **Large Datasets**: Leverage Paging 3 for efficient data loading
## File Locations Reference
diff --git a/SeforimApp/build.gradle.kts b/SeforimApp/build.gradle.kts
index 85d1930f4..6b0cc65c3 100644
--- a/SeforimApp/build.gradle.kts
+++ b/SeforimApp/build.gradle.kts
@@ -1,7 +1,7 @@
+import dev.nucleusframework.desktop.application.dsl.ReleaseChannel
+import dev.nucleusframework.desktop.application.dsl.ReleaseType
+import dev.nucleusframework.desktop.application.dsl.TargetFormat
import io.github.kdroidfilter.buildsrc.Versioning
-import io.github.kdroidfilter.nucleus.desktop.application.dsl.ReleaseChannel
-import io.github.kdroidfilter.nucleus.desktop.application.dsl.ReleaseType
-import io.github.kdroidfilter.nucleus.desktop.application.dsl.TargetFormat
import org.jetbrains.compose.reload.gradle.ComposeHotRun
plugins {
@@ -80,12 +80,12 @@ kotlin {
// Settings & platform utils
implementation(libs.multiplatformSettings)
- implementation(libs.platformtools.core)
implementation(libs.nucleus.core.runtime)
+ implementation(libs.nucleus.application)
implementation(libs.nucleus.aot.runtime)
implementation(libs.nucleus.darkmode.detector)
implementation(libs.platformtools.appmanager)
- implementation(libs.platformtools.releasefetcher)
+ implementation(project(":releasefetcher"))
// FileKit
implementation(libs.filekit.core)
@@ -138,16 +138,19 @@ kotlin {
api(project(":jewel"))
implementation(project(":earthwidget"))
implementation(libs.nucleus.system.color)
- implementation(libs.nucleus.decorated.window)
+ implementation(libs.nucleus.decorated.window.core)
+ implementation(libs.nucleus.decorated.window.tao)
implementation(libs.nucleus.decorated.window.jewel)
implementation(libs.nucleus.graalvm.runtime)
implementation(libs.nucleus.updater.runtime)
+ implementation(libs.nucleus.native.http)
implementation(libs.nucleus.energy.manager)
implementation(libs.nucleus.launcher.macos)
implementation(libs.nucleus.launcher.windows)
implementation(libs.nucleus.launcher.linux)
implementation(libs.nucleus.menu.macos)
implementation(libs.nucleus.sf.symbols)
+ implementation(libs.nucleus.taskbar.progress.tao)
implementation(compose.desktop.currentOs) {
exclude(group = "org.jetbrains.compose.material")
}
@@ -257,7 +260,6 @@ nucleus.application {
// Package-time resources root; include files under OS-specific subfolders (common, macos, windows, linux)
appResourcesRootDir.set(layout.projectDirectory.dir("src/jvmMain/assets"))
- splashImage = "splash.png"
enableAotCache = true
homepage = "https://zayitapp.com"
licenseFile.set(File(project.rootDir, "LICENSE"))
@@ -292,6 +294,10 @@ nucleus.application {
vendor = "KDroidFilter"
cleanupNativeLibs = true
+ // Register the custom URL scheme so shareable deep links (zayit://book/...,
+ // zayit://search/...) are routed to the app by the OS on macOS, Windows and Linux.
+ protocol("Zayit", "zayit")
+
linux {
iconFile.set(project.file("desktopAppIcons/LinuxIcon.png"))
packageVersion = version
diff --git a/SeforimApp/proguard-rules.pro b/SeforimApp/proguard-rules.pro
index c49d1c0c8..15f51510d 100644
--- a/SeforimApp/proguard-rules.pro
+++ b/SeforimApp/proguard-rules.pro
@@ -229,74 +229,85 @@
# =============================================================================
# Nucleus decorated-window JNI (macOS)
--keep class io.github.kdroidfilter.nucleus.window.utils.macos.NativeMacBridge {
+-keep class dev.nucleusframework.window.utils.macos.NativeMacBridge {
native ;
}
--keep class io.github.kdroidfilter.nucleus.window.** { *; }
+-keep class dev.nucleusframework.window.** { *; }
# Nucleus darkmode-detector JNI (macOS)
# NativeDarkModeBridge is looked up by name from native code (FindClass + GetStaticMethodID)
--keep class io.github.kdroidfilter.nucleus.darkmodedetector.mac.NativeDarkModeBridge {
+-keep class dev.nucleusframework.darkmodedetector.mac.NativeDarkModeBridge {
native ;
static void onThemeChanged(boolean);
}
# Nucleus darkmode-detector JNI (Linux)
# NativeLinuxBridge is looked up by name from native code (FindClass + GetStaticMethodID)
--keep class io.github.kdroidfilter.nucleus.darkmodedetector.linux.NativeLinuxBridge {
+-keep class dev.nucleusframework.darkmodedetector.linux.NativeLinuxBridge {
native ;
static void onThemeChanged(boolean);
}
# Nucleus darkmode-detector JNI (Windows)
--keep class io.github.kdroidfilter.nucleus.darkmodedetector.windows.NativeWindowsBridge {
+-keep class dev.nucleusframework.darkmodedetector.windows.NativeWindowsBridge {
native ;
}
--keep class io.github.kdroidfilter.nucleus.darkmodedetector.** { *; }
+-keep class dev.nucleusframework.darkmodedetector.** { *; }
# Nucleus native-ssl JNI (macOS)
--keep class io.github.kdroidfilter.nucleus.nativessl.mac.NativeSslBridge {
+-keep class dev.nucleusframework.nativessl.mac.NativeSslBridge {
native ;
}
# Nucleus native-ssl JNI (Windows)
--keep class io.github.kdroidfilter.nucleus.nativessl.windows.WindowsSslBridge {
+-keep class dev.nucleusframework.nativessl.windows.WindowsSslBridge {
native ;
}
# Nucleus launcher-windows JNI (jump list)
--keep class io.github.kdroidfilter.nucleus.launcher.windows.NativeWindowsJumpListBridge {
+-keep class dev.nucleusframework.launcher.windows.NativeWindowsJumpListBridge {
native ;
}
--keep class io.github.kdroidfilter.nucleus.launcher.windows.** { *; }
+-keep class dev.nucleusframework.launcher.windows.** { *; }
# Nucleus launcher-linux D-Bus (quicklist)
--keep class io.github.kdroidfilter.nucleus.launcher.linux.** { *; }
+-keep class dev.nucleusframework.launcher.linux.** { *; }
--keep class io.github.kdroidfilter.nucleus.energymanager.** { *; }
+-keep class dev.nucleusframework.energymanager.** { *; }
# macOS
--keep class io.github.kdroidfilter.nucleus.systemcolor.mac.NativeMacSystemColorBridge {
+-keep class dev.nucleusframework.systemcolor.mac.NativeMacSystemColorBridge {
native ;
static void onAccentColorChanged(float, float, float);
static void onContrastChanged(boolean);
}
# Windows
--keep class io.github.kdroidfilter.nucleus.systemcolor.windows.NativeWindowsSystemColorBridge {
+-keep class dev.nucleusframework.systemcolor.windows.NativeWindowsSystemColorBridge {
native ;
static void onAccentColorChanged(int, int, int);
static void onHighContrastChanged(boolean);
}
# Linux
--keep class io.github.kdroidfilter.nucleus.systemcolor.linux.NativeLinuxSystemColorBridge {
+-keep class dev.nucleusframework.systemcolor.linux.NativeLinuxSystemColorBridge {
native ;
static void onAccentColorChanged(float, float, float);
static void onHighContrastChanged(boolean);
}
--keep class io.github.kdroidfilter.nucleus.systemcolor.** { *; }
+-keep class dev.nucleusframework.systemcolor.** { *; }
+
+# --- Fix: panel resize cursor (PointerIcon backed by AWT Cursor) not applied in release ---
+# ComposeSceneMediator.setPointerIcon checks `pointerIcon instanceof AwtCursor` and then
+# calls getCursor(). ProGuard optimization (class merging/inlining of this thin wrapper)
+# breaks that check, so custom cursors built via PointerIcon(java.awt.Cursor) — e.g. the
+# E_RESIZE/N_RESIZE handles in SplitPanes — silently fall back to the default arrow.
+# Keep the desktop PointerIcon/AwtCursor classes intact so the instanceof + getCursor path works.
+-keep class androidx.compose.ui.input.pointer.AwtCursor { *; }
+-keep class androidx.compose.ui.input.pointer.PointerIcon_desktopKt { *; }
+-keep class androidx.compose.ui.input.pointer.PointerIcon { *; }
+-keep class androidx.compose.ui.input.pointer.PointerIcon$Companion { *; }
# --- Sentry crash reporting SDK ---
# Sentry uses reflection for serialization and event processing.
diff --git a/SeforimApp/src/commonMain/composeResources/values/strings.xml b/SeforimApp/src/commonMain/composeResources/values/strings.xml
index 679717705..57aa3c3b3 100644
--- a/SeforimApp/src/commonMain/composeResources/values/strings.xml
+++ b/SeforimApp/src/commonMain/composeResources/values/strings.xml
@@ -92,6 +92,20 @@
גופנים
אודות
תנאים
+ ניהול נתונים
+ ייצוא הנתונים שלי
+ שמירת גיבוי של ההערות, ההדגשות וההגדרות.
+ ייצוא
+ מייצא…
+ ייבוא נתונים
+ שחזור הנתונים מקובץ גיבוי קיים.
+ ייבוא
+ מייבא…
+ הגיבוי נשמר בהצלחה: %1$s
+ הנתונים יובאו בהצלחה. האפליקציה מופעלת מחדש…
+ ייצוא הנתונים נכשל
+ ייבוא הנתונים נכשל
+ איפוס מלא של האפליקציה למצב ההתחלתי. פעולה זו תמחק את כל הנתונים שלך.
הוסף לשונית
דף הבית
סגור לשונית
@@ -101,6 +115,7 @@
סגור את הלשוניות מימין
סגור את הלשוניות משמאל
+ העתק קישור
ניווט
@@ -117,6 +132,18 @@
חפש בעמוד
העתק ללא ניקוד
העתק עם מקור
+ העתק קישור לקטע זה
+ סמן
+ הוסף הערה
+
+
+ הערות
+ ההערות שלי על הספר
+ כתוב הערה...
+ אין הערות שמורות על קטע זה
+ אין הערות על השורות שנבחרו. בחר טקסט ולחץ "הוסף הערה".
+ אין צורך לכתוב הערה על השורה כולה: ניתן לסמן מילים מסוימות, ללחוץ לחיצה ימנית ולבחור "הוסף הערה" כדי לכתוב הערה על קטע מדויק בלבד.
+ מחק הערה
כלים
@@ -313,6 +340,8 @@
סגור אוטומטית את עץ הספרים בעת בחירת ספר חדש
כאשר אפשרות זו מופעלת, עץ הניווט בצד ימין ייסגר אוטומטית לאחר בחירת ספר חדש, מה שמאפשר יותר מקום לקריאה.
כאשר אפשרות זו מופעלת, האפליקציה תשמור את הלשוניות הפתוחות ומיקום הקריאה שלך, ותשחזר אותם בהפעלה הבאה.
+ מנע כיבוי מסך בזמן קריאה
+ כאשר אפשרות זו מופעלת, המסך לא ייכבה ולא ייכנס למצב שינה כל עוד ספר פתוח בלשונית הנוכחית והחלון פעיל.
הצג זמנים הלכתיים בדף הבית
כאשר אפשרות זו מופעלת, יוצגו בדף הבית וידג'טים עם זמני היום ההלכתיים (נץ החמה, שקיעה, זמני תפילה וכו').
הצג תמונת רקע בדף הבית
@@ -475,16 +504,23 @@
גרסה: %1$s
- JDK: %1$s %2$s — %3$s (%4$s)
- נוצר על ידי Elie Gambache - KdroidFilter
+ נוצר על ידי אליהו גמבש
יישום זה הוא קוד פתוח תחת רישיון AGPL-3.0
ניתן להשתמש ולהפיץ בהתאם לתנאי הרישיון.
- גרסה חדשה זמינה
- גרסה חדשה של זית זמינה להורדה. לחץ כדי לעדכן.
גרסה %1$s זמינה להורדה
הורד עכשיו
+ עדכון זמין
+ עדכון זמין
+ גרסה %1$s של זית זמינה.
+ ייתכן שלאחר עדכון זה יהיה צורך לעדכן את מסד הנתונים (כ‑3.5 ג״ב).
+ מוריד…
+ התקן והפעל מחדש
+ מאוחר יותר
+ האפליקציה מעודכנת לגרסה האחרונה
+ בודק עדכונים…
+ לא ניתן לבדוק עדכונים — ודא שקיים חיבור לאינטרנט
בחר קטגוריה לסינון
diff --git a/SeforimApp/src/commonMain/kotlin/io/github/kdroidfilter/seforimapp/catalog/CatalogPresets.kt b/SeforimApp/src/commonMain/kotlin/io/github/kdroidfilter/seforimapp/catalog/CatalogPresets.kt
new file mode 100644
index 000000000..2a56a1414
--- /dev/null
+++ b/SeforimApp/src/commonMain/kotlin/io/github/kdroidfilter/seforimapp/catalog/CatalogPresets.kt
@@ -0,0 +1,245 @@
+// DO NOT EDIT.
+// This file is auto-generated by the catalog generator.
+// To regenerate: ./gradlew :cataloggen:generatePrecomputedCatalog
+// Manual changes will be lost.
+@file:Suppress("ktlint")
+
+package io.github.kdroidfilter.seforimapp.catalog
+
+import kotlin.Long
+import kotlin.String
+import kotlin.Suppress
+import kotlin.collections.List
+
+public data class BookRef(
+ public val id: Long,
+ public val title: String,
+)
+
+public data class TocQuickLink(
+ public val label: String,
+ public val tocEntryId: Long,
+ public val firstLineId: Long?,
+)
+
+public sealed interface DropdownSpec
+
+public data class CategoryDropdownSpec(
+ public val categoryId: Long,
+) : DropdownSpec
+
+public data class MultiCategoryDropdownSpec(
+ public val labelCategoryId: Long,
+ public val bookCategoryIds: List,
+) : DropdownSpec
+
+public data class TocQuickLinksSpec(
+ public val bookId: Long,
+ public val links: List,
+) : DropdownSpec
+
+public object CatalogPresets {
+ public object Ids {
+ public object Categories {
+ /**
+ * תנ״ך
+ */
+ public const val TANAKH: Long = 1L
+
+ /**
+ * תורה
+ */
+ public const val TORAH: Long = 2L
+
+ /**
+ * נביאים
+ */
+ public const val NEVIIM: Long = 3L
+
+ /**
+ * כתובים
+ */
+ public const val KETUVIM: Long = 4L
+
+ /**
+ * משנה
+ */
+ public const val MISHNA: Long = 5L
+
+ /**
+ * סדר זרעים
+ */
+ public const val MISHNA_ZERAIM: Long = 6L
+
+ /**
+ * סדר מועד
+ */
+ public const val MISHNA_MOED: Long = 7L
+
+ /**
+ * סדר נשים
+ */
+ public const val MISHNA_NASHIM: Long = 8L
+
+ /**
+ * סדר נזיקין
+ */
+ public const val MISHNA_NEZIKIN: Long = 9L
+
+ /**
+ * סדר קדשים
+ */
+ public const val MISHNA_KODASHIM: Long = 10L
+
+ /**
+ * סדר טהרות
+ */
+ public const val MISHNA_TAHAROT: Long = 11L
+
+ /**
+ * בבלי
+ */
+ public const val BAVLI: Long = 13L
+
+ /**
+ * סדר זרעים
+ */
+ public const val BAVLI_ZERAIM: Long = 14L
+
+ /**
+ * סדר מועד
+ */
+ public const val BAVLI_MOED: Long = 15L
+
+ /**
+ * סדר נשים
+ */
+ public const val BAVLI_NASHIM: Long = 16L
+
+ /**
+ * סדר נזיקין
+ */
+ public const val BAVLI_NEZIKIN: Long = 17L
+
+ /**
+ * סדר קדשים
+ */
+ public const val BAVLI_KODASHIM: Long = 18L
+
+ /**
+ * סדר טהרות
+ */
+ public const val BAVLI_TAHAROT: Long = 19L
+
+ /**
+ * ירושלמי
+ */
+ public const val YERUSHALMI: Long = 20L
+
+ /**
+ * סדר זרעים
+ */
+ public const val YERUSHALMI_ZERAIM: Long = 21L
+
+ /**
+ * סדר מועד
+ */
+ public const val YERUSHALMI_MOED: Long = 22L
+
+ /**
+ * סדר נשים
+ */
+ public const val YERUSHALMI_NASHIM: Long = 23L
+
+ /**
+ * סדר נזיקין
+ */
+ public const val YERUSHALMI_NEZIKIN: Long = 24L
+
+ /**
+ * סדר טהרות
+ */
+ public const val YERUSHALMI_TAHAROT: Long = 25L
+
+ /**
+ * משנה תורה
+ */
+ public const val MISHNE_TORAH: Long = 45L
+
+ /**
+ * טור
+ */
+ public const val TUR: Long = 61L
+
+ /**
+ * שולחן ערוך
+ */
+ public const val SHULCHAN_ARUCH: Long = 62L
+ }
+
+ public object Books {
+ /**
+ * טור
+ */
+ public const val TUR: Long = 380L
+ }
+
+ public object TocTexts {
+ /**
+ * אורח חיים
+ */
+ public const val ORACH_CHAIM: Long = 3_878L
+
+ /**
+ * יורה דעה
+ */
+ public const val YOREH_DEAH: Long = 4_521L
+
+ /**
+ * אבן העזר
+ */
+ public const val EVEN_HAEZER: Long = 4_522L
+
+ /**
+ * חושן משפט
+ */
+ public const val CHOSHEN_MISHPAT: Long = 4_523L
+ }
+ }
+
+ public object Dropdowns {
+ public val HOME: List = listOf(
+ MultiCategoryDropdownSpec(1L, listOf(2L, 3L, 4L)),
+ MultiCategoryDropdownSpec(5L, listOf(6L, 7L, 8L, 9L, 10L, 11L)),
+ MultiCategoryDropdownSpec(13L, listOf(14L, 15L, 16L, 17L, 18L, 19L)),
+ MultiCategoryDropdownSpec(20L, listOf(21L, 22L, 23L, 24L, 25L)),
+ CategoryDropdownSpec(62L),
+ TocQuickLinksSpec(380L, listOf(TocQuickLink("אורח חיים", 30_149L, 252_607), TocQuickLink("יורה דעה", 30_848L, 254_014), TocQuickLink("אבן העזר", 31_253L, 254_828), TocQuickLink("חושן משפט", 31_433L, 255_198))),
+ )
+
+ public val TANAKH: DropdownSpec = MultiCategoryDropdownSpec(1L, listOf(2L, 3L, 4L))
+
+ public val TORAH: DropdownSpec = CategoryDropdownSpec(2L)
+
+ public val NEVIIM: DropdownSpec = CategoryDropdownSpec(3L)
+
+ public val KETUVIM: DropdownSpec = CategoryDropdownSpec(4L)
+
+ public val MISHNA: DropdownSpec =
+ MultiCategoryDropdownSpec(5L, listOf(6L, 7L, 8L, 9L, 10L, 11L))
+
+ public val BAVLI: DropdownSpec =
+ MultiCategoryDropdownSpec(13L, listOf(14L, 15L, 16L, 17L, 18L, 19L))
+
+ public val YERUSHALMI: DropdownSpec =
+ MultiCategoryDropdownSpec(20L, listOf(21L, 22L, 23L, 24L, 25L))
+
+ public val SHULCHAN_ARUCH: DropdownSpec = CategoryDropdownSpec(62L)
+
+ public val MISHNE_TORAH: DropdownSpec =
+ MultiCategoryDropdownSpec(45L, listOf(46L, 47L, 48L, 49L, 50L, 51L, 52L, 53L, 54L, 55L, 59L, 56L, 57L, 58L, 60L))
+
+ public val TUR_QUICK_LINKS: DropdownSpec =
+ TocQuickLinksSpec(380L, listOf(TocQuickLink("אורח חיים", 30_149L, 252_607), TocQuickLink("יורה דעה", 30_848L, 254_014), TocQuickLink("אבן העזר", 31_253L, 254_828), TocQuickLink("חושן משפט", 31_433L, 255_198)))
+ }
+}
diff --git a/SeforimApp/src/commonMain/kotlin/io/github/kdroidfilter/seforimapp/catalog/PrecomputedCatalog.kt b/SeforimApp/src/commonMain/kotlin/io/github/kdroidfilter/seforimapp/catalog/PrecomputedCatalog.kt
deleted file mode 100644
index fc98976f1..000000000
--- a/SeforimApp/src/commonMain/kotlin/io/github/kdroidfilter/seforimapp/catalog/PrecomputedCatalog.kt
+++ /dev/null
@@ -1,947 +0,0 @@
-// DO NOT EDIT.
-// This file is auto-generated by the catalog generator.
-// To regenerate: ./gradlew :cataloggen:generatePrecomputedCatalog
-// Manual changes will be lost.
-package io.github.kdroidfilter.seforimapp.catalog
-
-import kotlin.Long
-import kotlin.String
-import kotlin.collections.List
-import kotlin.collections.Map
-
-public data class BookRef(
- public val id: Long,
- public val title: String,
-)
-
-public data class TocQuickLink(
- public val label: String,
- public val tocEntryId: Long,
- public val firstLineId: Long?,
-)
-
-public sealed interface DropdownSpec
-
-public data class CategoryDropdownSpec(
- public val categoryId: Long,
-) : DropdownSpec
-
-public data class MultiCategoryDropdownSpec(
- public val labelCategoryId: Long,
- public val bookCategoryIds: List,
-) : DropdownSpec
-
-public data class TocQuickLinksSpec(
- public val bookId: Long,
- public val tocTextIds: List,
-) : DropdownSpec
-
-public object PrecomputedCatalog {
- public val BOOK_TITLES: Map =
- mapOf(
- 1L to "בראשית",
- 2L to "שמות",
- 3L to "ויקרא",
- 4L to "במדבר",
- 5L to "דברים",
- 6L to "יהושע",
- 7L to "שופטים",
- 8L to "שמואל א",
- 9L to "שמואל ב",
- 10L to "מלכים א",
- 11L to "מלכים ב",
- 12L to "ישעיהו",
- 13L to "ירמיהו",
- 14L to "יחזקאל",
- 15L to "הושע",
- 16L to "יואל",
- 17L to "עמוס",
- 18L to "עובדיה",
- 19L to "יונה",
- 20L to "מיכה",
- 21L to "נחום",
- 22L to "חבקוק",
- 23L to "צפניה",
- 24L to "חגי",
- 25L to "זכריה",
- 26L to "מלאכי",
- 27L to "רות",
- 28L to "תהילים",
- 29L to "איוב",
- 30L to "משלי",
- 31L to "שיר השירים",
- 32L to "קהלת",
- 33L to "איכה",
- 34L to "אסתר",
- 35L to "דניאל",
- 36L to "עזרא",
- 37L to "נחמיה",
- 38L to "דברי הימים א",
- 39L to "דברי הימים ב",
- 40L to "משנה ברכות",
- 41L to "משנה פאה",
- 42L to "משנה דמאי",
- 43L to "משנה כלאים",
- 44L to "משנה שביעית",
- 45L to "משנה תרומות",
- 46L to "משנה מעשרות",
- 47L to "משנה מעשר שני",
- 48L to "משנה חלה",
- 49L to "משנה ערלה",
- 50L to "משנה ביכורים",
- 51L to "משנה שבת",
- 52L to "משנה עירובין",
- 53L to "משנה פסחים",
- 54L to "משנה שקלים",
- 55L to "משנה יומא",
- 56L to "משנה סוכה",
- 57L to "משנה ביצה",
- 58L to "משנה ראש השנה",
- 59L to "משנה תענית",
- 60L to "משנה מגילה",
- 61L to "משנה מועד קטן",
- 62L to "משנה חגיגה",
- 63L to "משנה יבמות",
- 64L to "משנה כתובות",
- 65L to "משנה נדרים",
- 66L to "משנה נזיר",
- 67L to "משנה סוטה",
- 68L to "משנה גיטין",
- 69L to "משנה קידושין",
- 70L to "משנה בבא קמא",
- 71L to "משנה בבא מציעא",
- 72L to "משנה בבא בתרא",
- 73L to "משנה סנהדרין",
- 74L to "משנה מכות",
- 75L to "משנה שבועות",
- 76L to "משנה עדיות",
- 77L to "משנה עבודה זרה",
- 78L to "משנה אבות",
- 79L to "משנה הוריות",
- 80L to "משנה זבחים",
- 81L to "משנה מנחות",
- 82L to "משנה חולין",
- 83L to "משנה בכורות",
- 84L to "משנה ערכין",
- 85L to "משנה תמורה",
- 86L to "משנה כריתות",
- 87L to "משנה מעילה",
- 88L to "משנה תמיד",
- 89L to "משנה מדות",
- 90L to "משנה קינים",
- 91L to "משנה כלים",
- 92L to "משנה אהלות",
- 93L to "משנה נגעים",
- 94L to "משנה פרה",
- 95L to "משנה טהרות",
- 96L to "משנה מקואות",
- 97L to "משנה נדה",
- 98L to "משנה מכשירין",
- 99L to "משנה זבים",
- 100L to "משנה טבול יום",
- 101L to "משנה ידים",
- 102L to "משנה עוקצים",
- 103L to "ברכות",
- 104L to "שבת",
- 105L to "עירובין",
- 106L to "פסחים",
- 107L to "יומא",
- 108L to "סוכה",
- 109L to "ביצה",
- 110L to "ראש השנה",
- 111L to "תענית",
- 112L to "מגילה",
- 113L to "מועד קטן",
- 114L to "חגיגה",
- 115L to "יבמות",
- 116L to "כתובות",
- 117L to "נדרים",
- 118L to "נזיר",
- 119L to "סוטה",
- 120L to "גיטין",
- 121L to "קידושין",
- 122L to "בבא קמא",
- 123L to "בבא מציעא",
- 124L to "בבא בתרא",
- 125L to "סנהדרין",
- 126L to "מכות",
- 127L to "שבועות",
- 128L to "עבודה זרה",
- 129L to "הוריות",
- 130L to "זבחים",
- 131L to "מנחות",
- 132L to "חולין",
- 133L to "בכורות",
- 134L to "ערכין",
- 135L to "תמורה",
- 136L to "כריתות",
- 137L to "מעילה",
- 138L to "תמיד",
- 139L to "נדה",
- 140L to "תלמוד ירושלמי ברכות",
- 141L to "תלמוד ירושלמי פאה",
- 142L to "תלמוד ירושלמי דמאי",
- 143L to "תלמוד ירושלמי כלאים",
- 144L to "תלמוד ירושלמי שביעית",
- 145L to "תלמוד ירושלמי תרומות",
- 146L to "תלמוד ירושלמי מעשרות",
- 147L to "תלמוד ירושלמי מעשר שני",
- 148L to "תלמוד ירושלמי חלה",
- 149L to "תלמוד ירושלמי ערלה",
- 150L to "תלמוד ירושלמי בכורים",
- 151L to "תלמוד ירושלמי שבת",
- 152L to "תלמוד ירושלמי עירובין",
- 153L to "תלמוד ירושלמי פסחים",
- 154L to "תלמוד ירושלמי שקלים",
- 155L to "תלמוד ירושלמי יומא",
- 156L to "תלמוד ירושלמי סוכה",
- 157L to "תלמוד ירושלמי ביצה",
- 158L to "תלמוד ירושלמי ראש השנה",
- 159L to "תלמוד ירושלמי תענית",
- 160L to "תלמוד ירושלמי מגילה",
- 161L to "תלמוד ירושלמי מועד קטן",
- 162L to "תלמוד ירושלמי חגיגה",
- 163L to "תלמוד ירושלמי יבמות",
- 164L to "תלמוד ירושלמי כתובות",
- 165L to "תלמוד ירושלמי נדרים",
- 166L to "תלמוד ירושלמי נזיר",
- 167L to "תלמוד ירושלמי סוטה",
- 168L to "תלמוד ירושלמי גיטין",
- 169L to "תלמוד ירושלמי קידושין",
- 170L to "תלמוד ירושלמי בבא קמא",
- 171L to "תלמוד ירושלמי בבא מציעא",
- 172L to "תלמוד ירושלמי בבא בתרא",
- 173L to "תלמוד ירושלמי סנהדרין",
- 174L to "תלמוד ירושלמי מכות",
- 175L to "תלמוד ירושלמי שבועות",
- 176L to "תלמוד ירושלמי עבודה זרה",
- 177L to "תלמוד ירושלמי הוריות",
- 178L to "תלמוד ירושלמי נדה",
- 293L to "משנה תורה, מסירת תורה שבעל פה",
- 294L to "משנה תורה, מצוות לא תעשה",
- 295L to "משנה תורה, מצוות עשה",
- 296L to "משנה תורה, תוכן החיבור",
- 297L to "משנה תורה, הלכות יסודי התורה",
- 298L to "משנה תורה, הלכות דעות",
- 299L to "משנה תורה, הלכות תלמוד תורה",
- 300L to "משנה תורה, הלכות עבודה זרה וחוקות הגויים",
- 301L to "משנה תורה, הלכות תשובה",
- 302L to "משנה תורה, הלכות קריאת שמע",
- 303L to "משנה תורה, הלכות תפילה וברכת כהנים",
- 304L to "משנה תורה, הלכות תפילין ומזוזה וספר תורה",
- 305L to "משנה תורה, הלכות ציצית",
- 306L to "משנה תורה, הלכות ברכות",
- 307L to "משנה תורה, הלכות מילה",
- 308L to "משנה תורה, סדר התפילה",
- 309L to "משנה תורה, הלכות שבת",
- 310L to "משנה תורה, הלכות עירובין",
- 311L to "משנה תורה, הלכות שקלים",
- 312L to "משנה תורה, הלכות קידוש החודש",
- 313L to "משנה תורה, הלכות תעניות",
- 314L to "משנה תורה, הלכות מגילה וחנוכה",
- 315L to "משנה תורה, הלכות שופר וסוכה ולולב",
- 316L to "משנה תורה, הלכות שביתת יום טוב",
- 317L to "משנה תורה, הלכות שביתת עשור",
- 318L to "משנה תורה, הלכות חמץ ומצה",
- 319L to "משנה תורה, הלכות אישות",
- 320L to "משנה תורה, הלכות גירושין",
- 321L to "משנה תורה, הלכות יבום וחליצה",
- 322L to "משנה תורה, הלכות סוטה",
- 323L to "משנה תורה, הלכות נערה בתולה",
- 324L to "משנה תורה, הלכות מאכלות אסורות",
- 325L to "משנה תורה, הלכות שחיטה",
- 326L to "משנה תורה, הלכות איסורי ביאה",
- 327L to "משנה תורה, הלכות נדרים",
- 328L to "משנה תורה, הלכות נזירות",
- 329L to "משנה תורה, הלכות ערכים וחרמין",
- 330L to "משנה תורה, הלכות שבועות",
- 331L to "משנה תורה, הלכות תרומות",
- 332L to "משנה תורה, הלכות מעשרות",
- 333L to "משנה תורה, הלכות מעשר שני ונטע רבעי",
- 334L to "משנה תורה, הלכות שמיטה ויובל",
- 335L to "משנה תורה, הלכות מתנות עניים",
- 336L to "משנה תורה, הלכות כלאים",
- 337L to "משנה תורה, הלכות ביכורים ושאר מתנות כהונה שבגבולין",
- 338L to "משנה תורה, הלכות בית הבחירה",
- 339L to "משנה תורה, הלכות כלי המקדש והעובדין בו",
- 340L to "משנה תורה, הלכות איסורי המזבח",
- 341L to "משנה תורה, הלכות ביאת מקדש",
- 342L to "משנה תורה, הלכות מעשה הקרבנות",
- 343L to "משנה תורה, הלכות עבודת יום הכפורים",
- 344L to "משנה תורה, הלכות פסולי המוקדשין",
- 345L to "משנה תורה, הלכות תמידים ומוספין",
- 346L to "משנה תורה, הלכות מעילה",
- 347L to "משנה תורה, הלכות בכורות",
- 348L to "משנה תורה, הלכות שגגות",
- 349L to "משנה תורה, הלכות מחוסרי כפרה",
- 350L to "משנה תורה, הלכות תמורה",
- 351L to "משנה תורה, הלכות קרבן פסח",
- 352L to "משנה תורה, הלכות חגיגה",
- 353L to "משנה תורה, הלכות נזקי ממון",
- 354L to "משנה תורה, הלכות גזילה ואבידה",
- 355L to "משנה תורה, הלכות גניבה",
- 356L to "משנה תורה, הלכות חובל ומזיק",
- 357L to "משנה תורה, הלכות רוצח ושמירת נפש",
- 358L to "משנה תורה, הלכות מכירה",
- 359L to "משנה תורה, הלכות זכייה ומתנה",
- 360L to "משנה תורה, הלכות שלוחין ושותפין",
- 361L to "משנה תורה, הלכות עבדים",
- 362L to "משנה תורה, הלכות שכנים",
- 363L to "משנה תורה, הלכות מלווה ולווה",
- 364L to "משנה תורה, הלכות שכירות",
- 365L to "משנה תורה, הלכות נחלות",
- 366L to "משנה תורה, הלכות טוען ונטען",
- 367L to "משנה תורה, הלכות שאלה ופיקדון",
- 368L to "משנה תורה, הלכות שאר אבות הטומאות",
- 369L to "משנה תורה, הלכות טומאת מת",
- 370L to "משנה תורה, הלכות טומאת צרעת",
- 371L to "משנה תורה, הלכות מטמאי משכב ומושב",
- 372L to "משנה תורה, הלכות טומאת אוכלים",
- 373L to "משנה תורה, הלכות פרה אדומה",
- 374L to "משנה תורה, הלכות מקואות",
- 375L to "משנה תורה, הלכות כלים",
- 376L to "משנה תורה, הלכות סנהדרין והעונשין המסורין להם",
- 377L to "משנה תורה, הלכות עדות",
- 378L to "משנה תורה, הלכות ממרים",
- 379L to "משנה תורה, הלכות אבל",
- 380L to "משנה תורה, הלכות מלכים ומלחמות",
- 381L to "טור",
- 382L to "שולחן ערוך, הקדמה",
- 383L to "שולחן ערוך, אורח חיים",
- 384L to "שולחן ערוך, יורה דעה",
- 385L to "שולחן ערוך, אבן העזר",
- 386L to "שולחן ערוך, חושן משפט",
- 2_533L to "פרי מגדים על אורח חיים",
- )
-
- public val CATEGORY_TITLES: Map =
- mapOf(
- 1L to "תנ״ך",
- 2L to "תורה",
- 3L to "נביאים",
- 4L to "כתובים",
- 5L to "משנה",
- 6L to "סדר זרעים",
- 7L to "סדר מועד",
- 8L to "סדר נשים",
- 9L to "סדר נזיקין",
- 10L to "סדר קדשים",
- 11L to "סדר טהרות",
- 13L to "תלמוד בבלי",
- 14L to "סדר זרעים",
- 15L to "סדר מועד",
- 16L to "סדר נשים",
- 17L to "סדר נזיקין",
- 18L to "סדר קדשים",
- 19L to "סדר טהרות",
- 20L to "תלמוד ירושלמי",
- 21L to "סדר זרעים",
- 22L to "סדר מועד",
- 23L to "סדר נשים",
- 24L to "סדר נזיקין",
- 25L to "סדר טהרות",
- 45L to "משנה תורה",
- 46L to "הקדמה",
- 47L to "ספר מדע",
- 48L to "ספר אהבה",
- 49L to "ספר זמנים",
- 50L to "ספר נשים",
- 51L to "ספר קדושה",
- 52L to "ספר הפלאה",
- 53L to "ספר זרעים",
- 54L to "ספר עבודה",
- 55L to "ספר קורבנות",
- 56L to "ספר נזיקין",
- 57L to "ספר קניין",
- 58L to "ספר משפטים",
- 59L to "ספר טהרה",
- 60L to "ספר שופטים",
- 61L to "טור",
- 62L to "שולחן ערוך",
- )
-
- public val CATEGORY_BOOKS: Map> =
- mapOf(
- 1L to listOf(),
- 2L to listOf(BookRef(1L, "בראשית"), BookRef(2L, "שמות"), BookRef(3L, "ויקרא"), BookRef(4L, "במדבר"), BookRef(5L, "דברים")),
- 3L to
- listOf(
- BookRef(6L, "יהושע"),
- BookRef(7L, "שופטים"),
- BookRef(8L, "שמואל א"),
- BookRef(9L, "שמואל ב"),
- BookRef(10L, "מלכים א"),
- BookRef(11L, "מלכים ב"),
- BookRef(12L, "ישעיהו"),
- BookRef(13L, "ירמיהו"),
- BookRef(14L, "יחזקאל"),
- BookRef(15L, "הושע"),
- BookRef(16L, "יואל"),
- BookRef(17L, "עמוס"),
- BookRef(18L, "עובדיה"),
- BookRef(19L, "יונה"),
- BookRef(20L, "מיכה"),
- BookRef(21L, "נחום"),
- BookRef(22L, "חבקוק"),
- BookRef(23L, "צפניה"),
- BookRef(24L, "חגי"),
- BookRef(25L, "זכריה"),
- BookRef(26L, "מלאכי"),
- ),
- 4L to
- listOf(
- BookRef(28L, "תהילים"),
- BookRef(30L, "משלי"),
- BookRef(29L, "איוב"),
- BookRef(31L, "שיר השירים"),
- BookRef(27L, "רות"),
- BookRef(33L, "איכה"),
- BookRef(32L, "קהלת"),
- BookRef(34L, "אסתר"),
- BookRef(35L, "דניאל"),
- BookRef(36L, "עזרא"),
- BookRef(37L, "נחמיה"),
- BookRef(38L, "דברי הימים א"),
- BookRef(39L, "דברי הימים ב"),
- ),
- 5L to listOf(),
- 6L to
- listOf(
- BookRef(40L, "ברכות"),
- BookRef(41L, "פאה"),
- BookRef(42L, "דמאי"),
- BookRef(43L, "כלאים"),
- BookRef(44L, "שביעית"),
- BookRef(45L, "תרומות"),
- BookRef(46L, "מעשרות"),
- BookRef(47L, "מעשר שני"),
- BookRef(48L, "חלה"),
- BookRef(49L, "ערלה"),
- BookRef(50L, "ביכורים"),
- ),
- 7L to
- listOf(
- BookRef(51L, "שבת"),
- BookRef(52L, "עירובין"),
- BookRef(53L, "פסחים"),
- BookRef(54L, "שקלים"),
- BookRef(55L, "יומא"),
- BookRef(56L, "סוכה"),
- BookRef(57L, "ביצה"),
- BookRef(58L, "ראש השנה"),
- BookRef(59L, "תענית"),
- BookRef(60L, "מגילה"),
- BookRef(61L, "מועד קטן"),
- BookRef(62L, "חגיגה"),
- ),
- 8L to
- listOf(
- BookRef(63L, "יבמות"),
- BookRef(64L, "כתובות"),
- BookRef(65L, "נדרים"),
- BookRef(66L, "נזיר"),
- BookRef(67L, "סוטה"),
- BookRef(68L, "גיטין"),
- BookRef(69L, "קידושין"),
- ),
- 9L to
- listOf(
- BookRef(70L, "בבא קמא"),
- BookRef(71L, "בבא מציעא"),
- BookRef(72L, "בבא בתרא"),
- BookRef(73L, "סנהדרין"),
- BookRef(74L, "מכות"),
- BookRef(75L, "שבועות"),
- BookRef(76L, "עדיות"),
- BookRef(77L, "עבודה זרה"),
- BookRef(78L, "אבות"),
- BookRef(79L, "הוריות"),
- ),
- 10L to
- listOf(
- BookRef(80L, "זבחים"),
- BookRef(81L, "מנחות"),
- BookRef(82L, "חולין"),
- BookRef(83L, "בכורות"),
- BookRef(84L, "ערכין"),
- BookRef(85L, "תמורה"),
- BookRef(86L, "כריתות"),
- BookRef(87L, "מעילה"),
- BookRef(88L, "תמיד"),
- BookRef(89L, "מדות"),
- BookRef(90L, "קינים"),
- ),
- 11L to
- listOf(
- BookRef(91L, "כלים"),
- BookRef(92L, "אהלות"),
- BookRef(93L, "נגעים"),
- BookRef(94L, "פרה"),
- BookRef(95L, "טהרות"),
- BookRef(96L, "מקואות"),
- BookRef(97L, "נדה"),
- BookRef(98L, "מכשירין"),
- BookRef(99L, "זבים"),
- BookRef(100L, "טבול יום"),
- BookRef(101L, "ידים"),
- BookRef(102L, "עוקצים"),
- ),
- 13L to listOf(),
- 14L to listOf(BookRef(103L, "ברכות")),
- 15L to
- listOf(
- BookRef(104L, "שבת"),
- BookRef(105L, "עירובין"),
- BookRef(106L, "פסחים"),
- BookRef(110L, "ראש השנה"),
- BookRef(107L, "יומא"),
- BookRef(108L, "סוכה"),
- BookRef(109L, "ביצה"),
- BookRef(111L, "תענית"),
- BookRef(112L, "מגילה"),
- BookRef(113L, "מועד קטן"),
- BookRef(114L, "חגיגה"),
- ),
- 16L to
- listOf(
- BookRef(115L, "יבמות"),
- BookRef(116L, "כתובות"),
- BookRef(117L, "נדרים"),
- BookRef(118L, "נזיר"),
- BookRef(119L, "סוטה"),
- BookRef(120L, "גיטין"),
- BookRef(121L, "קידושין"),
- ),
- 17L to
- listOf(
- BookRef(122L, "בבא קמא"),
- BookRef(123L, "בבא מציעא"),
- BookRef(124L, "בבא בתרא"),
- BookRef(125L, "סנהדרין"),
- BookRef(126L, "מכות"),
- BookRef(127L, "שבועות"),
- BookRef(128L, "עבודה זרה"),
- BookRef(129L, "הוריות"),
- ),
- 18L to
- listOf(
- BookRef(130L, "זבחים"),
- BookRef(131L, "מנחות"),
- BookRef(132L, "חולין"),
- BookRef(133L, "בכורות"),
- BookRef(134L, "ערכין"),
- BookRef(135L, "תמורה"),
- BookRef(136L, "כריתות"),
- BookRef(137L, "מעילה"),
- BookRef(138L, "תמיד"),
- ),
- 19L to listOf(BookRef(139L, "נדה")),
- 20L to listOf(),
- 21L to
- listOf(
- BookRef(140L, "ירושלמי ברכות"),
- BookRef(141L, "ירושלמי פאה"),
- BookRef(142L, "ירושלמי דמאי"),
- BookRef(143L, "ירושלמי כלאים"),
- BookRef(144L, "ירושלמי שביעית"),
- BookRef(145L, "ירושלמי תרומות"),
- BookRef(146L, "ירושלמי מעשרות"),
- BookRef(147L, "ירושלמי מעשר שני"),
- BookRef(148L, "ירושלמי חלה"),
- BookRef(149L, "ירושלמי ערלה"),
- BookRef(150L, "ירושלמי בכורים"),
- ),
- 22L to
- listOf(
- BookRef(151L, "ירושלמי שבת"),
- BookRef(152L, "ירושלמי עירובין"),
- BookRef(153L, "ירושלמי פסחים"),
- BookRef(155L, "ירושלמי יומא"),
- BookRef(154L, "ירושלמי שקלים"),
- BookRef(156L, "ירושלמי סוכה"),
- BookRef(158L, "ירושלמי ראש השנה"),
- BookRef(157L, "ירושלמי ביצה"),
- BookRef(159L, "ירושלמי תענית"),
- BookRef(160L, "ירושלמי מגילה"),
- BookRef(162L, "ירושלמי חגיגה"),
- BookRef(161L, "ירושלמי מועד קטן"),
- ),
- 23L to
- listOf(
- BookRef(163L, "ירושלמי יבמות"),
- BookRef(167L, "ירושלמי סוטה"),
- BookRef(164L, "ירושלמי כתובות"),
- BookRef(165L, "ירושלמי נדרים"),
- BookRef(166L, "ירושלמי נזיר"),
- BookRef(168L, "ירושלמי גיטין"),
- BookRef(169L, "ירושלמי קידושין"),
- ),
- 24L to
- listOf(
- BookRef(170L, "ירושלמי בבא קמא"),
- BookRef(171L, "ירושלמי בבא מציעא"),
- BookRef(172L, "ירושלמי בבא בתרא"),
- BookRef(173L, "ירושלמי סנהדרין"),
- BookRef(175L, "ירושלמי שבועות"),
- BookRef(176L, "ירושלמי עבודה זרה"),
- BookRef(174L, "ירושלמי מכות"),
- BookRef(177L, "ירושלמי הוריות"),
- ),
- 25L to listOf(BookRef(178L, "ירושלמי נדה")),
- 45L to listOf(),
- 46L to
- listOf(
- BookRef(293L, "מסירת תורה שבעל פה"),
- BookRef(295L, "מצוות עשה"),
- BookRef(294L, "מצוות לא תעשה"),
- BookRef(296L, "תוכן החיבור"),
- ),
- 47L to
- listOf(
- BookRef(297L, "הלכות יסודי התורה"),
- BookRef(298L, "הלכות דעות"),
- BookRef(299L, "הלכות תלמוד תורה"),
- BookRef(300L, "הלכות עבודה זרה וחוקות הגויים"),
- BookRef(301L, "הלכות תשובה"),
- ),
- 48L to
- listOf(
- BookRef(302L, "הלכות קריאת שמע"),
- BookRef(303L, "הלכות תפילה וברכת כהנים"),
- BookRef(304L, "הלכות תפילין ומזוזה וספר תורה"),
- BookRef(305L, "הלכות ציצית"),
- BookRef(306L, "הלכות ברכות"),
- BookRef(307L, "הלכות מילה"),
- BookRef(308L, "סדר התפילה"),
- ),
- 49L to
- listOf(
- BookRef(309L, "הלכות שבת"),
- BookRef(310L, "הלכות עירובין"),
- BookRef(317L, "הלכות שביתת עשור"),
- BookRef(316L, "הלכות שביתת יום טוב"),
- BookRef(318L, "הלכות חמץ ומצה"),
- BookRef(315L, "הלכות שופר וסוכה ולולב"),
- BookRef(311L, "הלכות שקלים"),
- BookRef(312L, "הלכות קידוש החודש"),
- BookRef(313L, "הלכות תעניות"),
- BookRef(314L, "הלכות מגילה וחנוכה"),
- ),
- 50L to
- listOf(
- BookRef(319L, "הלכות אישות"),
- BookRef(320L, "הלכות גירושין"),
- BookRef(321L, "הלכות יבום וחליצה"),
- BookRef(323L, "הלכות נערה בתולה"),
- BookRef(322L, "הלכות סוטה"),
- ),
- 51L to listOf(BookRef(326L, "הלכות איסורי ביאה"), BookRef(324L, "הלכות מאכלות אסורות"), BookRef(325L, "הלכות שחיטה")),
- 52L to
- listOf(
- BookRef(330L, "הלכות שבועות"),
- BookRef(327L, "הלכות נדרים"),
- BookRef(328L, "הלכות נזירות"),
- BookRef(329L, "הלכות ערכים וחרמין"),
- ),
- 53L to
- listOf(
- BookRef(336L, "הלכות כלאים"),
- BookRef(335L, "הלכות מתנות עניים"),
- BookRef(331L, "הלכות תרומות"),
- BookRef(332L, "הלכות מעשרות"),
- BookRef(333L, "הלכות מעשר שני ונטע רבעי"),
- BookRef(337L, "הלכות ביכורים ושאר מתנות כהונה שבגבולין"),
- BookRef(334L, "הלכות שמיטה ויובל"),
- ),
- 54L to
- listOf(
- BookRef(338L, "הלכות בית הבחירה"),
- BookRef(339L, "הלכות כלי המקדש והעובדין בו"),
- BookRef(341L, "הלכות ביאת מקדש"),
- BookRef(340L, "הלכות איסורי המזבח"),
- BookRef(342L, "הלכות מעשה הקרבנות"),
- BookRef(345L, "הלכות תמידים ומוספין"),
- BookRef(344L, "הלכות פסולי המוקדשין"),
- BookRef(343L, "הלכות עבודת יום הכפורים"),
- BookRef(346L, "הלכות מעילה"),
- ),
- 55L to
- listOf(
- BookRef(351L, "הלכות קרבן פסח"),
- BookRef(352L, "הלכות חגיגה"),
- BookRef(347L, "הלכות בכורות"),
- BookRef(348L, "הלכות שגגות"),
- BookRef(349L, "הלכות מחוסרי כפרה"),
- BookRef(350L, "הלכות תמורה"),
- ),
- 56L to
- listOf(
- BookRef(353L, "הלכות נזקי ממון"),
- BookRef(355L, "הלכות גניבה"),
- BookRef(354L, "הלכות גזילה ואבידה"),
- BookRef(356L, "הלכות חובל ומזיק"),
- BookRef(357L, "הלכות רוצח ושמירת נפש"),
- ),
- 57L to
- listOf(
- BookRef(358L, "הלכות מכירה"),
- BookRef(359L, "הלכות זכייה ומתנה"),
- BookRef(362L, "הלכות שכנים"),
- BookRef(360L, "הלכות שלוחין ושותפין"),
- BookRef(361L, "הלכות עבדים"),
- ),
- 58L to
- listOf(
- BookRef(364L, "הלכות שכירות"),
- BookRef(367L, "הלכות שאלה ופיקדון"),
- BookRef(363L, "הלכות מלווה ולווה"),
- BookRef(366L, "הלכות טוען ונטען"),
- BookRef(365L, "הלכות נחלות"),
- ),
- 59L to
- listOf(
- BookRef(369L, "הלכות טומאת מת"),
- BookRef(373L, "הלכות פרה אדומה"),
- BookRef(370L, "הלכות טומאת צרעת"),
- BookRef(371L, "הלכות מטמאי משכב ומושב"),
- BookRef(368L, "הלכות שאר אבות הטומאות"),
- BookRef(372L, "הלכות טומאת אוכלים"),
- BookRef(375L, "הלכות כלים"),
- BookRef(374L, "הלכות מקואות"),
- ),
- 60L to
- listOf(
- BookRef(376L, "הלכות סנהדרין והעונשין המסורין להם"),
- BookRef(377L, "הלכות עדות"),
- BookRef(378L, "הלכות ממרים"),
- BookRef(379L, "הלכות אבל"),
- BookRef(380L, "הלכות מלכים ומלחמות"),
- ),
- 61L to listOf(BookRef(381L, "טור")),
- 62L to
- listOf(
- BookRef(382L, "הקדמה"),
- BookRef(383L, "אורח חיים"),
- BookRef(384L, "יורה דעה"),
- BookRef(385L, "אבן העזר"),
- BookRef(386L, "חושן משפט"),
- BookRef(2_533L, "פרי מגדים על אורח חיים"),
- ),
- )
-
- public val TOC_BY_TOC_TEXT_ID: Map> =
- mapOf(
- 381L to
- mapOf(
- 3_768L to TocQuickLink("אורח חיים", 30_015L, 252_674),
- 4_411L to TocQuickLink("יורה דעה", 30_714L, 254_081),
- 4_412L to TocQuickLink("אבן העזר", 31_119L, 254_895),
- 4_413L to TocQuickLink("חושן משפט", 31_299L, 255_265),
- ),
- )
-
- public object Ids {
- public object Categories {
- /**
- * תנ״ך
- */
- public const val TANAKH: Long = 1L
-
- /**
- * תורה
- */
- public const val TORAH: Long = 2L
-
- /**
- * נביאים
- */
- public const val NEVIIM: Long = 3L
-
- /**
- * כתובים
- */
- public const val KETUVIM: Long = 4L
-
- /**
- * משנה
- */
- public const val MISHNA: Long = 5L
-
- /**
- * סדר זרעים
- */
- public const val MISHNA_ZERAIM: Long = 6L
-
- /**
- * סדר מועד
- */
- public const val MISHNA_MOED: Long = 7L
-
- /**
- * סדר נשים
- */
- public const val MISHNA_NASHIM: Long = 8L
-
- /**
- * סדר נזיקין
- */
- public const val MISHNA_NEZIKIN: Long = 9L
-
- /**
- * סדר קדשים
- */
- public const val MISHNA_KODASHIM: Long = 10L
-
- /**
- * סדר טהרות
- */
- public const val MISHNA_TAHAROT: Long = 11L
-
- /**
- * תלמוד בבלי
- */
- public const val BAVLI: Long = 13L
-
- /**
- * סדר זרעים
- */
- public const val BAVLI_ZERAIM: Long = 14L
-
- /**
- * סדר מועד
- */
- public const val BAVLI_MOED: Long = 15L
-
- /**
- * סדר נשים
- */
- public const val BAVLI_NASHIM: Long = 16L
-
- /**
- * סדר נזיקין
- */
- public const val BAVLI_NEZIKIN: Long = 17L
-
- /**
- * סדר קדשים
- */
- public const val BAVLI_KODASHIM: Long = 18L
-
- /**
- * סדר טהרות
- */
- public const val BAVLI_TAHAROT: Long = 19L
-
- /**
- * תלמוד ירושלמי
- */
- public const val YERUSHALMI: Long = 20L
-
- /**
- * סדר זרעים
- */
- public const val YERUSHALMI_ZERAIM: Long = 21L
-
- /**
- * סדר מועד
- */
- public const val YERUSHALMI_MOED: Long = 22L
-
- /**
- * סדר נשים
- */
- public const val YERUSHALMI_NASHIM: Long = 23L
-
- /**
- * סדר נזיקין
- */
- public const val YERUSHALMI_NEZIKIN: Long = 24L
-
- /**
- * סדר טהרות
- */
- public const val YERUSHALMI_TAHAROT: Long = 25L
-
- /**
- * משנה תורה
- */
- public const val MISHNE_TORAH: Long = 45L
-
- /**
- * טור
- */
- public const val TUR: Long = 61L
-
- /**
- * שולחן ערוך
- */
- public const val SHULCHAN_ARUCH: Long = 62L
- }
-
- public object Books {
- /**
- * טור
- */
- public const val TUR: Long = 381L
- }
-
- public object TocTexts {
- /**
- * אורח חיים
- */
- public const val ORACH_CHAIM: Long = 3_768L
-
- /**
- * יורה דעה
- */
- public const val YOREH_DEAH: Long = 4_411L
-
- /**
- * אבן העזר
- */
- public const val EVEN_HAEZER: Long = 4_412L
-
- /**
- * חושן משפט
- */
- public const val CHOSHEN_MISHPAT: Long = 4_413L
- }
- }
-
- public object Dropdowns {
- public val HOME: List =
- listOf(
- MultiCategoryDropdownSpec(1L, listOf(2L, 3L, 4L)),
- MultiCategoryDropdownSpec(5L, listOf(6L, 7L, 8L, 9L, 10L, 11L)),
- MultiCategoryDropdownSpec(13L, listOf(14L, 15L, 16L, 17L, 18L, 19L)),
- MultiCategoryDropdownSpec(20L, listOf(21L, 22L, 23L, 24L, 25L)),
- CategoryDropdownSpec(62L),
- TocQuickLinksSpec(381L, listOf(3_768L, 4_411L, 4_412L, 4_413L)),
- )
-
- public val TANAKH: DropdownSpec = MultiCategoryDropdownSpec(1L, listOf(2L, 3L, 4L))
-
- public val TORAH: DropdownSpec = CategoryDropdownSpec(2L)
-
- public val NEVIIM: DropdownSpec = CategoryDropdownSpec(3L)
-
- public val KETUVIM: DropdownSpec = CategoryDropdownSpec(4L)
-
- public val MISHNA: DropdownSpec =
- MultiCategoryDropdownSpec(5L, listOf(6L, 7L, 8L, 9L, 10L, 11L))
-
- public val BAVLI: DropdownSpec =
- MultiCategoryDropdownSpec(13L, listOf(14L, 15L, 16L, 17L, 18L, 19L))
-
- public val YERUSHALMI: DropdownSpec =
- MultiCategoryDropdownSpec(20L, listOf(21L, 22L, 23L, 24L, 25L))
-
- public val SHULCHAN_ARUCH: DropdownSpec = CategoryDropdownSpec(62L)
-
- public val MISHNE_TORAH: DropdownSpec =
- MultiCategoryDropdownSpec(45L, listOf(46L, 47L, 48L, 49L, 50L, 51L, 52L, 53L, 54L, 55L, 59L, 56L, 57L, 58L, 60L))
-
- public val TUR_QUICK_LINKS: DropdownSpec =
- TocQuickLinksSpec(381L, listOf(3_768L, 4_411L, 4_412L, 4_413L))
- }
-}
diff --git a/SeforimApp/src/commonMain/sqldelight/io/github/kdroidfilter/seforimapp/db/UserHighlights.sq b/SeforimApp/src/commonMain/sqldelight/io/github/kdroidfilter/seforimapp/db/UserHighlights.sq
new file mode 100644
index 000000000..7e00b4e94
--- /dev/null
+++ b/SeforimApp/src/commonMain/sqldelight/io/github/kdroidfilter/seforimapp/db/UserHighlights.sq
@@ -0,0 +1,38 @@
+-- Position-based user highlights.
+-- Keyed by (bookId, lineId, offsets): Line ids are stable across DB rebuilds,
+-- so highlights survive books-DB delta updates.
+CREATE TABLE IF NOT EXISTS user_highlights (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ bookId INTEGER NOT NULL,
+ lineId INTEGER NOT NULL,
+ startOffset INTEGER NOT NULL,
+ endOffset INTEGER NOT NULL,
+ colorArgb INTEGER NOT NULL,
+ createdAt INTEGER NOT NULL
+);
+
+CREATE INDEX IF NOT EXISTS idx_highlights_book ON user_highlights(bookId);
+CREATE INDEX IF NOT EXISTS idx_highlights_line ON user_highlights(bookId, lineId);
+
+selectAllForBook:
+SELECT * FROM user_highlights WHERE bookId = ?;
+
+selectForLine:
+SELECT * FROM user_highlights WHERE bookId = ? AND lineId = ?;
+
+insert:
+INSERT INTO user_highlights(bookId, lineId, startOffset, endOffset, colorArgb, createdAt)
+VALUES (?, ?, ?, ?, ?, ?);
+
+lastInsertedId:
+SELECT last_insert_rowid();
+
+deleteOverlapping:
+DELETE FROM user_highlights
+WHERE bookId = ? AND lineId = ? AND startOffset < :endOffset AND endOffset > :startOffset;
+
+deleteById:
+DELETE FROM user_highlights WHERE id = ?;
+
+deleteForBook:
+DELETE FROM user_highlights WHERE bookId = ?;
diff --git a/SeforimApp/src/commonMain/sqldelight/io/github/kdroidfilter/seforimapp/db/UserNotes.sq b/SeforimApp/src/commonMain/sqldelight/io/github/kdroidfilter/seforimapp/db/UserNotes.sq
new file mode 100644
index 000000000..07d982eee
--- /dev/null
+++ b/SeforimApp/src/commonMain/sqldelight/io/github/kdroidfilter/seforimapp/db/UserNotes.sq
@@ -0,0 +1,42 @@
+-- Position-based user notes: several notes per line, each anchored to a character range
+-- (a single word or a whole phrase), keyed by (bookId, lineId, offsets) — same scheme as
+-- user_highlights, so notes survive books-DB delta updates and stay valid across diacritics
+-- display changes.
+-- [quote] stores the anchored passage (the selected text) so the notes pane keeps showing it,
+-- including after the note is saved. Defaulted so the column can be added to pre-existing DBs.
+CREATE TABLE IF NOT EXISTS user_notes (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ bookId INTEGER NOT NULL,
+ lineId INTEGER NOT NULL,
+ startOffset INTEGER NOT NULL,
+ endOffset INTEGER NOT NULL,
+ note TEXT NOT NULL,
+ quote TEXT NOT NULL DEFAULT '',
+ createdAt INTEGER NOT NULL,
+ updatedAt INTEGER NOT NULL
+);
+
+CREATE INDEX IF NOT EXISTS idx_user_notes_book ON user_notes(bookId);
+CREATE INDEX IF NOT EXISTS idx_user_notes_line ON user_notes(bookId, lineId);
+
+selectAllForBook:
+SELECT * FROM user_notes WHERE bookId = ?;
+
+selectForLine:
+SELECT * FROM user_notes WHERE bookId = ? AND lineId = ?;
+
+insert:
+INSERT INTO user_notes(bookId, lineId, startOffset, endOffset, note, quote, createdAt, updatedAt)
+VALUES (?, ?, ?, ?, ?, ?, ?, ?);
+
+lastInsertRowId:
+SELECT last_insert_rowid();
+
+updateNote:
+UPDATE user_notes SET note = ?, updatedAt = ? WHERE id = ?;
+
+deleteById:
+DELETE FROM user_notes WHERE id = ?;
+
+deleteForBook:
+DELETE FROM user_notes WHERE bookId = ?;
diff --git a/SeforimApp/src/jvmMain/kotlin/io/github/kdroidfilter/seforimapp/core/MainAppState.kt b/SeforimApp/src/jvmMain/kotlin/io/github/kdroidfilter/seforimapp/core/MainAppState.kt
index a592ec41b..3613b582d 100644
--- a/SeforimApp/src/jvmMain/kotlin/io/github/kdroidfilter/seforimapp/core/MainAppState.kt
+++ b/SeforimApp/src/jvmMain/kotlin/io/github/kdroidfilter/seforimapp/core/MainAppState.kt
@@ -10,12 +10,12 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
/**
- * Application-level state holder for theme, onboarding, and update status.
+ * Application-level state holder for theme and onboarding.
* Injected as a singleton via Metro DI.
*/
@Stable
class MainAppState {
- private val _theme = MutableStateFlow(IntUiThemes.System)
+ private val _theme = MutableStateFlow(AppSettings.getThemeMode())
val theme: StateFlow = _theme.asStateFlow()
fun setTheme(theme: IntUiThemes) {
@@ -45,20 +45,4 @@ class MainAppState {
fun setShowOnBoarding(value: Boolean?) {
_showOnboarding.value = value
}
-
- // App update state
- private val _updateAvailable = MutableStateFlow(null)
- val updateAvailable: StateFlow = _updateAvailable.asStateFlow()
-
- private val _updateCheckDone = MutableStateFlow(false)
- val updateCheckDone: StateFlow = _updateCheckDone.asStateFlow()
-
- fun setUpdateAvailable(version: String?) {
- _updateAvailable.value = version
- _updateCheckDone.value = true
- }
-
- fun markUpdateCheckDone() {
- _updateCheckDone.value = true
- }
}
diff --git a/SeforimApp/src/jvmMain/kotlin/io/github/kdroidfilter/seforimapp/core/annotations/HighlightSelectionResolver.kt b/SeforimApp/src/jvmMain/kotlin/io/github/kdroidfilter/seforimapp/core/annotations/HighlightSelectionResolver.kt
new file mode 100644
index 000000000..2157c2feb
--- /dev/null
+++ b/SeforimApp/src/jvmMain/kotlin/io/github/kdroidfilter/seforimapp/core/annotations/HighlightSelectionResolver.kt
@@ -0,0 +1,98 @@
+package io.github.kdroidfilter.seforimapp.core.annotations
+
+import io.github.kdroidfilter.seforimapp.core.presentation.text.stripNikudTeamimWithMap
+
+/**
+ * Resolves the character range a highlight should cover, given the selected text and a
+ * line's plain text.
+ *
+ * Offsets are returned in the line's ORIGINAL coordinates (with diacritics), so a stored
+ * highlight stays valid regardless of the current diacritics display setting.
+ *
+ * [linePlainText] MUST be the plain text of the rendered line WITH diacritics (i.e.
+ * `buildAnnotatedFromHtml(line.content, ...).text`) so offsets align with what
+ * [io.github.kdroidfilter.seforimapp.core.presentation.text.applyUserHighlights] applies.
+ *
+ * Returns null when the selection cannot be located within the line (e.g. a multi-line
+ * selection whose text is longer than this line); the caller decides how to degrade.
+ */
+fun resolveHighlightRange(
+ linePlainText: String,
+ selectedText: String,
+ showDiacritics: Boolean,
+): IntRange? {
+ val needle = selectedText.trim()
+ if (needle.isEmpty() || linePlainText.isEmpty()) return null
+
+ if (showDiacritics) {
+ val start = linePlainText.indexOf(needle)
+ if (start < 0) return null
+ return start until (start + needle.length)
+ }
+
+ // Diacritics hidden: the selection is in stripped space (matching the rendered text, which
+ // keeps ׳ ״). Locate it there, then map the bounds back to original coordinates for storage.
+ val (strippedText, strippedToOriginal) = stripNikudTeamimWithMap(linePlainText)
+ val start = strippedText.indexOf(needle)
+ if (start < 0) return null
+ val end = start + needle.length
+ val originalStart = if (start < strippedToOriginal.size) strippedToOriginal[start] else linePlainText.length
+ val originalEnd = if (end < strippedToOriginal.size) strippedToOriginal[end] else linePlainText.length
+ if (originalEnd <= originalStart) return null
+ return originalStart until originalEnd
+}
+
+/** A resolved highlight range for one line, in original (with-diacritics) coordinates. */
+data class LineHighlightRange(
+ val lineId: Long,
+ val range: IntRange,
+)
+
+/**
+ * Resolves the highlight ranges for a (possibly multi-line) selection.
+ *
+ * The platform joins selected lines with '\n', so [selectedText] is split into per-line
+ * segments. [sortedVisibleLines] are all currently materialized lines as
+ * (lineId, plain-text-with-diacritics), ordered by line index. The first segment is anchored
+ * on the visible line that ends with it; each following segment maps to the next consecutive
+ * line. Each (line, segment) pair is then resolved with [resolveHighlightRange], so the first
+ * line keeps its selected suffix, inner lines are full, and the last line keeps its prefix.
+ *
+ * Offsets are returned in original (with-diacritics) coordinates.
+ */
+fun resolveHighlightRangesForSelection(
+ sortedVisibleLines: List>,
+ selectedText: String,
+ showDiacritics: Boolean,
+): List {
+ if (sortedVisibleLines.isEmpty()) return emptyList()
+
+ val segments = selectedText.split('\n').map { it.trim() }.filter { it.isNotEmpty() }
+ if (segments.isEmpty()) return emptyList()
+
+ fun displayed(plain: String): String = if (showDiacritics) plain else stripNikudTeamimWithMap(plain).first
+
+ if (segments.size == 1) {
+ val seg = segments.first()
+ val line = sortedVisibleLines.firstOrNull { displayed(it.second).contains(seg) } ?: return emptyList()
+ val range = resolveHighlightRange(line.second, seg, showDiacritics) ?: return emptyList()
+ return listOf(LineHighlightRange(line.first, range))
+ }
+
+ // Anchor the first segment on the line whose displayed text ends with it (full first line
+ // ends with the whole segment too); fall back to containment.
+ val firstSeg = segments.first()
+ var startIndex = sortedVisibleLines.indexOfFirst { displayed(it.second).trimEnd().endsWith(firstSeg) }
+ if (startIndex < 0) startIndex = sortedVisibleLines.indexOfFirst { displayed(it.second).contains(firstSeg) }
+ if (startIndex < 0) return emptyList()
+
+ val result = mutableListOf()
+ for (offset in segments.indices) {
+ val lineIndex = startIndex + offset
+ if (lineIndex > sortedVisibleLines.lastIndex) break
+ val (lineId, plain) = sortedVisibleLines[lineIndex]
+ val range = resolveHighlightRange(plain, segments[offset], showDiacritics) ?: (0 until plain.length)
+ result += LineHighlightRange(lineId, range)
+ }
+ return result
+}
diff --git a/SeforimApp/src/jvmMain/kotlin/io/github/kdroidfilter/seforimapp/core/annotations/HighlightStore.kt b/SeforimApp/src/jvmMain/kotlin/io/github/kdroidfilter/seforimapp/core/annotations/HighlightStore.kt
new file mode 100644
index 000000000..83d1d3690
--- /dev/null
+++ b/SeforimApp/src/jvmMain/kotlin/io/github/kdroidfilter/seforimapp/core/annotations/HighlightStore.kt
@@ -0,0 +1,152 @@
+package io.github.kdroidfilter.seforimapp.core.annotations
+
+import androidx.compose.runtime.Stable
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.toArgb
+import androidx.compose.ui.text.TextRange
+import io.github.kdroidfilter.seforimapp.db.UserSettingsDb
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.withContext
+
+/**
+ * A single position-based user highlight.
+ *
+ * @property id Database row id (0 before persistence).
+ * @property lineId Id of the line carrying this highlight.
+ * @property startOffset Inclusive start offset in the line's plain text.
+ * @property endOffset Exclusive end offset in the line's plain text.
+ * @property color Highlight color.
+ */
+@Stable
+data class UserHighlight(
+ val id: Long,
+ val lineId: Long,
+ val startOffset: Int,
+ val endOffset: Int,
+ val color: Color,
+) {
+ val textRange: TextRange get() = TextRange(startOffset, endOffset)
+}
+
+/** Palette offered to the user. [Transparent] acts as the "clear highlight" action. */
+object HighlightColors {
+ val Yellow = Color(0xFFFFEB3B)
+ val Green = Color(0xFF4CAF50)
+ val Blue = Color(0xFF2196F3)
+ val Pink = Color(0xFFE91E63)
+ val Orange = Color(0xFFFF9800)
+
+ val all = listOf(Yellow, Green, Blue, Pink, Orange)
+ val allWithClear = all + Color.Transparent
+}
+
+/**
+ * Persists position-based highlights in the local user database and exposes them
+ * as a per-book in-memory cache for the render hot path.
+ *
+ * Reads hit SQLite only once per book ([loadBook]); the rendering path reads the
+ * cached [StateFlow] map (O(1) per line). Writes are rare, explicit user actions.
+ */
+class HighlightStore(
+ database: UserSettingsDb,
+) {
+ private val queries = database.userHighlightsQueries
+
+ private val _highlightsByBook = MutableStateFlow