diff --git a/app/templates/reader.html b/app/templates/reader.html
index ef16a08..aec120c 100644
--- a/app/templates/reader.html
+++ b/app/templates/reader.html
@@ -3,31 +3,37 @@
{% block title %}{{ title }} | BookWorm Reader{% endblock %}
{% block content %}
-
+
-
+
-
-
-
- We use cookies to save your Focus Garden progress. This helps your plants grow across reading sessions.
-
-
-
-
-
+
+
+
+
+
+
+
+
+ We use cookies to save your Focus Garden progress. This helps your plants grow across reading sessions.
+
+
+
+
+
{% endblock %}
{% block footer %}
@@ -132,6 +138,32 @@
{
- annotations.push({
- text: annotation.dataset.text,
- note: annotation.querySelector('.annotation-note').value
- });
- });
- return annotations;
- }
let currentFontSize = parseInt(window.getComputedStyle(readerContent).fontSize);
let currentLineHeight = 1.6;
@@ -668,7 +690,7 @@ ${match}`);
- readerContent.innerHTML = annotatedContent;
- const annotationElement = document.createElement('div');
- annotationElement.className = 'annotation-box';
- annotationElement.innerHTML = `
-
-
-
-
- `;
- document.body.appendChild(annotationElement);
- annotationElement.querySelector('.delete-annotation').addEventListener('click', function () {
- annotationElement.remove();
- removeAnnotation(text);
- savePreferences();
- });
- annotationElement.querySelector('.edit-annotation').addEventListener('click', function () {
- annotationElement.querySelector('.annotation-note').disabled = false;
- });
- annotationElement.querySelector('.save-annotation').addEventListener('click', function () {
- annotationElement.querySelector('.annotation-note').disabled = true;
- savePreferences();
- });
- savePreferences();
+// CREATE ANNOTATION
+// Optional third parameter "existingId": if provided, we use it rather than generating a new one.
+function addAnnotation(text, note = '', existingId) {
+ console.log('Adding annotation for text:', text);
+
+ // 1) Generate or re-use a unique ID for this annotation
+ const annotationId = existingId || ('annotation-' + Date.now() + '-' + Math.random().toString(36).substr(2, 5));
+
+ // 2) Highlight the text in the article.
+ // We only replace the first occurrence not already wrapped.
+ const originalHTML = readerContent.innerHTML;
+ const safeText = text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // escape regex chars
+ let replaced = false;
+ const annotatedHTML = originalHTML.replace(new RegExp(safeText, 'gi'), function(match) {
+ if (!replaced) {
+ replaced = true;
+ return `${match}`;
}
-
- function removeAnnotation(text) {
- const content = readerContent.innerHTML;
- const unannotatedContent = content.replace(
- /(.*?)<\/span>/gi, (match, p1,
- p2) => {
- return p1.includes(text) ? p2 : match;
- });
- readerContent.innerHTML = unannotatedContent;
+ return match;
+ });
+ readerContent.innerHTML = annotatedHTML;
+
+ // 3) Ensure the sidebar is visible and relatively positioned.
+ const annotationPanel = document.getElementById('annotation-panel');
+ annotationPanel.classList.remove('hidden');
+ annotationPanel.style.position = 'relative';
+
+ // 4) Create the annotation box in the sidebar.
+ const annotationElement = document.createElement('div');
+ annotationElement.className = 'annotation-box p-2 bg-white dark:bg-[#333] shadow rounded';
+ annotationElement.style.position = 'absolute';
+ // Store unique ID and text for later reference.
+ annotationElement.dataset.id = annotationId;
+ annotationElement.dataset.text = text;
+
+ annotationElement.innerHTML = `
+
+ Annotated text:
+
+ ${text}
+
+
+
+
+
+
+ `;
+ annotationPanel.appendChild(annotationElement);
+
+ // 5) Position the annotation box in the sidebar
+ // Use the highlight span (found by its unique ID) to get the vertical offset.
+ const highlightSpan = document.querySelector(`span.annotation[data-id="${annotationId}"]`);
+ let offsetTop = 0;
+ if (highlightSpan) {
+ const highlightRect = highlightSpan.getBoundingClientRect();
+ const panelRect = annotationPanel.getBoundingClientRect();
+ offsetTop = (highlightRect.top + window.scrollY) - (panelRect.top + window.scrollY);
+ if (offsetTop < 0) offsetTop = 0;
+ }
+ annotationElement.style.top = offsetTop + 'px';
+ annotationElement.style.right = '0';
+
+ // 6) Initialize the note textarea as read-only by default.
+ const noteTextarea = annotationElement.querySelector('.annotation-note');
+ noteTextarea.disabled = true;
+
+ // 7) Wire up annotation actions.
+ annotationElement.querySelector('.delete-annotation').addEventListener('click', function () {
+ annotationElement.remove();
+ removeAnnotation(annotationId);
+ savePreferences();
+ // If no annotations remain, hide the sidebar.
+ if (annotationPanel.childElementCount === 0) {
+ annotationPanel.classList.add('hidden');
}
+ });
+ annotationElement.querySelector('.edit-annotation').addEventListener('click', function () {
+ noteTextarea.disabled = false;
+ noteTextarea.focus();
+ });
+ annotationElement.querySelector('.save-annotation').addEventListener('click', function () {
+ noteTextarea.disabled = true;
+ savePreferences();
+ });
+
+ // 8) Scroll the annotation panel so the new annotation is in view.
+ annotationElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
+
+ // 9) Persist changes (e.g., to localStorage).
+ savePreferences();
+}
+
+// REMOVE ANNOTATION (by unique ID)
+function removeAnnotation(annotationId) {
+ const content = readerContent.innerHTML;
+ const unannotatedContent = content.replace(
+ /(.*?)<\/span>/gi,
+ (match, p1, p2, p3) => p1 === annotationId ? p3 : match
+ );
+ readerContent.innerHTML = unannotatedContent;
+}
+
+// GET ALL ANNOTATIONS from the sidebar.
+function getAnnotations() {
+ const annotationBoxes = document.querySelectorAll('#annotation-panel .annotation-box');
+ const annotations = [];
+ annotationBoxes.forEach(box => {
+ const text = box.dataset.text;
+ const note = box.querySelector('.annotation-note').value;
+ const id = box.dataset.id;
+ annotations.push({ id, text, note });
+ });
+ return annotations;
+}
+
+
});
const colorBlindDropdown = document.getElementById('color-blind');