summaryrefslogtreecommitdiff
path: root/apps/web/app/reader/_components/highlight-selection-toolbar.tsx
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-02-07 01:42:57 -0800
committerFuwn <[email protected]>2026-02-07 01:42:57 -0800
commit5c5b1993edd890a80870ee05607ac5f088191d4e (patch)
treea721b76bcd49ba10826c53efc87302c7a689512f /apps/web/app/reader/_components/highlight-selection-toolbar.tsx
downloadasa.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.tsx80
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>
+ )
+}