diff options
| author | Fuwn <[email protected]> | 2026-02-07 01:42:57 -0800 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-02-07 01:42:57 -0800 |
| commit | 5c5b1993edd890a80870ee05607ac5f088191d4e (patch) | |
| tree | a721b76bcd49ba10826c53efc87302c7a689512f /apps/web/app/reader/_components/highlight-selection-toolbar.tsx | |
| download | asa.news-5c5b1993edd890a80870ee05607ac5f088191d4e.tar.xz asa.news-5c5b1993edd890a80870ee05607ac5f088191d4e.zip | |
feat: asa.news RSS reader with developer tier, REST API, and webhooks
Full-stack RSS reader SaaS: Supabase + Next.js + Go worker.
Includes three subscription tiers (free/pro/developer), API key auth,
read-only REST API, webhook push notifications, Stripe billing with
proration, and PWA support.
Diffstat (limited to 'apps/web/app/reader/_components/highlight-selection-toolbar.tsx')
| -rw-r--r-- | apps/web/app/reader/_components/highlight-selection-toolbar.tsx | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/apps/web/app/reader/_components/highlight-selection-toolbar.tsx b/apps/web/app/reader/_components/highlight-selection-toolbar.tsx new file mode 100644 index 0000000..42522bf --- /dev/null +++ b/apps/web/app/reader/_components/highlight-selection-toolbar.tsx @@ -0,0 +1,80 @@ +"use client" + +import { useState } from "react" + +interface HighlightSelectionToolbarProperties { + selectionRect: DOMRect + containerRect: DOMRect + onHighlight: (note: string | null) => void + onDismiss: () => void +} + +export function HighlightSelectionToolbar({ + selectionRect, + onHighlight, + onDismiss, +}: HighlightSelectionToolbarProperties) { + const [showNoteInput, setShowNoteInput] = useState(false) + const [noteText, setNoteText] = useState("") + + const toolbarLeft = selectionRect.left + selectionRect.width / 2 + const toolbarTop = selectionRect.top - 8 + + function handleHighlightClick() { + if (showNoteInput) { + onHighlight(noteText.trim() || null) + } else { + onHighlight(null) + } + } + + return ( + <div + className="fixed z-[100] -translate-x-1/2 -translate-y-full" + style={{ left: toolbarLeft, top: toolbarTop }} + > + <div className="border border-border bg-background-secondary p-1"> + {showNoteInput ? ( + <div className="flex items-center gap-1"> + <input + type="text" + value={noteText} + onChange={(event) => setNoteText(event.target.value)} + onKeyDown={(event) => { + if (event.key === "Enter") handleHighlightClick() + if (event.key === "Escape") onDismiss() + }} + placeholder="add a note..." + className="border border-border bg-background-primary px-2 py-1 text-xs text-text-primary outline-none" + autoFocus + /> + <button + type="button" + onClick={handleHighlightClick} + className="px-2 py-1 text-xs text-text-secondary transition-colors hover:bg-background-tertiary hover:text-text-primary" + > + save + </button> + </div> + ) : ( + <div className="flex items-center gap-1"> + <button + type="button" + onClick={handleHighlightClick} + className="px-2 py-1 text-xs text-text-secondary transition-colors hover:bg-background-tertiary hover:text-text-primary" + > + highlight + </button> + <button + type="button" + onClick={() => setShowNoteInput(true)} + className="px-2 py-1 text-xs text-text-dim transition-colors hover:bg-background-tertiary hover:text-text-secondary" + > + + note + </button> + </div> + )} + </div> + </div> + ) +} |