From de5ebbc4422b458c685a653b8b8fbaac6e6af5f4 Mon Sep 17 00:00:00 2001 From: Mohamed Bassem Date: Sun, 23 Nov 2025 11:02:27 +0000 Subject: feat: Add notes feature to highlights (#2154) * feat: Add note-taking functionality to highlights This commit adds the ability to add and edit notes on highlights, allowing users to capture their thoughts alongside highlighted text. Changes: - Updated zUpdateHighlightSchema to include optional note field - Modified Highlight.update() method to persist notes to database - Added note button (MessageSquare icon) beside color picker in highlight menu - Implemented note dialog with textarea for adding/editing notes - Updated Highlight interface to include optional note field - Modified ReaderView to pass notes through create and update operations - Enhanced HighlightCard to display notes below highlighted text The note button appears alongside the color picker for both new and existing highlights. Notes are displayed in a smaller, muted font below the highlighted text in the HighlightCard component. * refactor: Convert highlight UI to inline form Changed the highlight creation/editing interface from a modal dialog to an inline form for a more streamlined user experience. Changes: - Replaced ColorPickerMenu and Dialog with a unified HighlightForm component - Form now displays color selector and note textarea together inline - Added labels for "Color" and "Note" sections - Textarea is now optional and inline (placeholder: "Add a note (optional)...") - Consolidated Save and Cancel buttons in the form - Delete button appears only for existing highlights - Removed MessageSquare icon - notes are now always visible in the form - Simplified state management by removing separate dialog state The form appears in a popover when text is selected or an existing highlight is clicked, showing both color options and note input in a single 320px wide form. * fixes --------- Co-authored-by: Claude --- .../dashboard/highlights/HighlightCard.tsx | 21 +++- .../dashboard/preview/BookmarkHtmlHighlighter.tsx | 129 ++++++++++++++------- .../components/dashboard/preview/ReaderView.tsx | 3 +- 3 files changed, 105 insertions(+), 48 deletions(-) (limited to 'apps') diff --git a/apps/web/components/dashboard/highlights/HighlightCard.tsx b/apps/web/components/dashboard/highlights/HighlightCard.tsx index 1bba0b47..51421e0f 100644 --- a/apps/web/components/dashboard/highlights/HighlightCard.tsx +++ b/apps/web/components/dashboard/highlights/HighlightCard.tsx @@ -44,16 +44,24 @@ export default function HighlightCard({ }); }; - const Wrapper = ({ children }: { children: React.ReactNode }) => + const Wrapper = ({ + className, + children, + }: { + className?: string; + children: React.ReactNode; + }) => clickable ? ( - + ) : ( -
{children}
+
{children}
); return (
- +

{highlight.text}

+ {highlight.note && ( + + {highlight.note} + + )}
{!readOnly && (
diff --git a/apps/web/components/dashboard/preview/BookmarkHtmlHighlighter.tsx b/apps/web/components/dashboard/preview/BookmarkHtmlHighlighter.tsx index e0f20ea2..63098ac0 100644 --- a/apps/web/components/dashboard/preview/BookmarkHtmlHighlighter.tsx +++ b/apps/web/components/dashboard/preview/BookmarkHtmlHighlighter.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useRef, useState } from "react"; import { ActionButton } from "@/components/ui/action-button"; import { Button } from "@/components/ui/button"; import { Popover, PopoverContent } from "@/components/ui/popover"; +import { Textarea } from "@/components/ui/textarea"; import { cn } from "@/lib/utils"; import { PopoverAnchor } from "@radix-ui/react-popover"; import { Check, Trash2 } from "lucide-react"; @@ -13,23 +14,38 @@ import { import { HIGHLIGHT_COLOR_MAP } from "./highlights"; -interface ColorPickerMenuProps { +interface HighlightFormProps { position: { x: number; y: number } | null; - onColorSelect: (color: ZHighlightColor) => void; - onDelete?: () => void; selectedHighlight: Highlight | null; onClose: () => void; + onSave: (color: ZHighlightColor, note: string | null) => void; + onDelete?: () => void; isMobile: boolean; } -const ColorPickerMenu: React.FC = ({ +const HighlightForm: React.FC = ({ position, - onColorSelect, - onDelete, selectedHighlight, onClose, + onSave, + onDelete, isMobile, }) => { + const [selectedColor, setSelectedColor] = useState( + selectedHighlight?.color || "yellow", + ); + const [noteText, setNoteText] = useState(selectedHighlight?.note || ""); + + // Update state when selectedHighlight changes + useEffect(() => { + setSelectedColor(selectedHighlight?.color || "yellow"); + setNoteText(selectedHighlight?.note || ""); + }, [selectedHighlight]); + + const handleSave = () => { + onSave(selectedColor, noteText || null); + }; + return ( = ({ /> - {SUPPORTED_HIGHLIGHT_COLORS.map((color) => ( - - ))} - {selectedHighlight && ( - - - - )} +
+ +
+ {SUPPORTED_HIGHLIGHT_COLORS.map((color) => ( + + ))} +
+
+
+ +