diff --git a/src/components/layout/node-preview-panel.tsx b/src/components/layout/node-preview-panel.tsx index 793d180..70f7edb 100644 --- a/src/components/layout/node-preview-panel.tsx +++ b/src/components/layout/node-preview-panel.tsx @@ -1,7 +1,7 @@ "use client" import { useState, useEffect, useRef } from "react" -import { ArrowLeft, Link, Zap, Loader2, Play, Film, ExternalLink, Heart, Repeat2, ChevronDown, ChevronUp, MessageCircle, Quote, Eye, BadgeCheck, AtSign, HeartOff, X, Pencil, FlaskConical, GitMerge } from "lucide-react" +import { ArrowLeft, Link, Zap, Loader2, Play, Film, ExternalLink, Heart, Repeat2, ChevronDown, ChevronUp, MessageCircle, Quote, Eye, BadgeCheck, AtSign, HeartOff, X, Pencil, FlaskConical, GitMerge, MoreHorizontal } from "lucide-react" import { Badge } from "@/components/ui/badge" import { BoostButton } from "@/components/boost/boost-button" @@ -12,7 +12,7 @@ import { api } from "@/lib/api" import { payL402 } from "@/lib/sphinx" import { isSphinx } from "@/lib/sphinx/detect" import { buildSphinxDeepLink } from "@/lib/sphinx/deep-link" -import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from "@/components/ui/dropdown-menu" +import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator } from "@/components/ui/dropdown-menu" import { Dialog, DialogContent } from "@/components/ui/dialog" import { unlockNode } from "@/lib/unlock-node" import { isMocksEnabled, MOCK_FULL_NODES } from "@/lib/mock-data" @@ -659,6 +659,7 @@ export function NodePreviewPanel({ node, onBack, schemas }: NodePreviewPanelProp const [copied, setCopied] = useState(false) const copyTimerRef = useRef | null>(null) const scrollContentRef = useRef(null) + const boostRef = useRef(null) const [watched, setWatched] = useState(false) const [watchLoading, setWatchLoading] = useState(false) @@ -1030,94 +1031,113 @@ export function NodePreviewPanel({ node, onBack, schemas }: NodePreviewPanelProp {displayNodeType(nodeType)}
- {isSphinx() ? ( - - - {copied ? ( - Copied! - ) : ( - - )} - - - Copy link - Copy Sphinx link - - - ) : ( - - )} - {hasIdentity && ( - - )} - {ownerReference && !hideBoost && ( - - )} - {(isAdmin || hasIdentity) && ( - - )} - {isAdmin && ( - - )} + + + {/* Share — always visible */} + {isSphinx() ? ( + <> + + + Copy link + + + + Copy Sphinx link + + + ) : ( + + + Copy link + + )} + + {/* Watch */} + {hasIdentity && ( + { + if (watchLoading) return + const next = !watched + setWatched(next) + setWatchLoading(true) + try { + if (next) { + await watchNode(currentNode.ref_id) + } else { + await unwatchNode(currentNode.ref_id) + } + } catch { + setWatched(!next) + } finally { + setWatchLoading(false) + } + }} + disabled={watchLoading} + > + + {watched ? "Unwatch" : "Watch"} + + )} + + {/* Boost */} + {ownerReference && !hideBoost && ( + boostRef.current?.querySelector("button")?.click()}> + + Boost + + )} + + {/* Separator between public actions and power-user/admin actions */} + {(isAdmin || hasIdentity) && } + + {/* Add Edge */} + {(isAdmin || hasIdentity) && ( + openAddEdge(currentNode.ref_id)}> + + Add Edge + + )} + + {/* Edit node */} + {isAdmin && ( + openEdit(currentNode)}> + + Edit node + + )} + + + + {/* Close — always pinned */}