Skip to content

feat(highlights): position-based text highlighting#340

Closed
kdroidFilter wants to merge 1 commit into
masterfrom
feat/position-based-highlights
Closed

feat(highlights): position-based text highlighting#340
kdroidFilter wants to merge 1 commit into
masterfrom
feat/position-based-highlights

Conversation

@kdroidFilter
Copy link
Copy Markdown
Owner

@kdroidFilter kdroidFilter commented Feb 2, 2026

Summary

  • Replace text-matching highlight system with position-based approach
  • Highlights now stored with lineId + character offsets instead of matching text
  • Only the specific selected instance is highlighted, not all occurrences of the same text
  • Add LineTextRegistry to track visible line content for position lookup
  • Use deterministic iteration (sorted by lineId) for consistent behavior

Technical Details

  • HighlightStore.kt: Refactored to store UserHighlight(lineId, startOffset, endOffset, color)
  • LineTextRegistry.kt: New registry tracking plain text of visible lines per tab
  • Highlighting.kt: Updated applyUserHighlights to use position offsets
  • BookContentView.kt: Filter highlights per line, register line text with registry
  • BookContentScreen.kt: Use registry to find position when adding highlights

Note

Highlights are currently in-memory only and will be lost when the app closes.

Follow-up: Persistence Implementation

To persist highlights to UserSettingsDb, the following changes are needed:

1. Create UserHighlights.sq

Location: SeforimApp/src/commonMain/sqldelight/io/github/kdroidfilter/seforimapp/db/

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
);

CREATE INDEX IF NOT EXISTS idx_highlights_book ON user_highlights(bookId);

selectAllForBook:
SELECT * FROM user_highlights WHERE bookId = ?;

insert:
INSERT INTO user_highlights(bookId, lineId, startOffset, endOffset, colorArgb)
VALUES (?, ?, ?, ?, ?);

deleteOverlapping:
DELETE FROM user_highlights 
WHERE bookId = ? AND lineId = ? 
AND startOffset < ? AND endOffset > ?;

deleteForBook:
DELETE FROM user_highlights WHERE bookId = ?;

2. Create HighlightRepository.kt

Follow the pattern of CategoryDisplaySettingsStore:

  • Inject UserSettingsDb
  • Async methods for CRUD operations
  • Convert ColorInt (using color.toArgb() and Color(argb))

3. Modify HighlightStore.kt

  • Inject HighlightRepository
  • Load highlights from DB on first access per book
  • Persist changes in addHighlight() / removeHighlight()
  • Keep in-memory cache for reactive UI updates

Test plan

  • Select text and apply highlight color from context menu
  • Verify only the selected instance is highlighted, not all occurrences
  • Test with text that appears multiple times in the document
  • Verify highlight removal works correctly

@kdroidFilter kdroidFilter marked this pull request as draft February 2, 2026 14:00
@kdroidFilter kdroidFilter force-pushed the feat/position-based-highlights branch 6 times, most recently from d1e0ed4 to b5e14a5 Compare February 2, 2026 20:57
- Add character offset tracking for precise highlight positioning
- Preserve highlight position when toggling nikud/taamim visibility
- Improve highlight color picker UI in context menu
- Use proper menu item colors for icons and text
@kdroidFilter kdroidFilter force-pushed the feat/position-based-highlights branch from c88c85f to ae66dda Compare February 2, 2026 21:22
@kdroidFilter
Copy link
Copy Markdown
Owner Author

Réimplémenté proprement : surlignage position-based persisté dans une BDD utilisateur locale dédiée (store injecté via DI au lieu de l'objet global en mémoire), sélection multi-lignes et mapping diacritiques gérés. Remplacé par le commit fb0a2b9.

@kdroidFilter
Copy link
Copy Markdown
Owner Author

Reimplemented cleanly: position-based highlights are now persisted in a dedicated local user DB (DI-injected store instead of the in-memory global object), with multi-line selection and diacritics-offset mapping handled. Superseded by commit fb0a2b9.

kdroidFilter added a commit that referenced this pull request Jun 4, 2026
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)
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