import { useState, useCallback, createContext, useContext } from 'react' import { motion, AnimatePresence } from 'framer-motion' import { Eye, Search, FileText, Dna, Copy, Shield, ArrowRight, Lock, Unlock, RefreshCw, Code, Scale, Brain, Hash, ExternalLink, Zap, } from 'lucide-react' const ContextMenuContext = createContext(null) export function useContextMenu() { return useContext(ContextMenuContext) } const GLYPH = { inspect: '◉', query: '⌕', dossier: '§', intel: '◇', copy: '⎘', revoke: '⊘', lock: '🔒', unlock: '🔓', reindex: '↻', api: '⌁', profile: '⚖', ml: '⟡', kpi: '#', link: '↗', password: '🗝', launch: '▶', } const SEPARATOR = { separator: true } function buildFileItems(file, actions) { if (!file) return [] return [ { glyph: GLYPH.inspect, icon: Eye, label: 'Inspect', action: () => actions.inspect?.(file) }, { glyph: GLYPH.query, icon: Search, label: 'Query File', action: () => actions.query?.(file) }, { glyph: GLYPH.dossier, icon: FileText, label: 'Open Dossier', action: () => actions.dossier?.(file) }, { glyph: GLYPH.intel, icon: Dna, label: 'File Intelligence', action: () => actions.intel?.(file) }, SEPARATOR, { glyph: GLYPH.kpi, icon: Hash, label: 'Extract KPIs', action: () => actions.kpi?.(file) }, { glyph: GLYPH.ml, icon: Brain, label: 'ML Insights', action: () => actions.ml?.(file) }, { glyph: GLYPH.profile, icon: Scale, label: 'Semantic Profile', action: () => actions.profile?.(file) }, SEPARATOR, { glyph: GLYPH.copy, icon: Copy, label: 'Copy File ID', action: () => navigator.clipboard?.writeText(file.file_id) }, { glyph: GLYPH.link, icon: ExternalLink, label: 'Copy Meta URL', action: () => actions.copyUrl?.(file) }, { glyph: GLYPH.reindex, icon: RefreshCw, label: 'Reindex', action: () => actions.reindex?.(file) }, SEPARATOR, { glyph: file.has_password ? GLYPH.unlock : GLYPH.lock, icon: file.has_password ? Unlock : Lock, label: file.has_password ? 'Password Set' : 'Set Password', action: () => actions.password?.(file) }, { glyph: GLYPH.revoke, icon: Shield, label: 'Revoke Access', action: () => actions.revoke?.(file), danger: true }, ] } function buildNavItems(item, actions) { return [ { glyph: GLYPH.launch, icon: ArrowRight, label: `Open ${item.label}`, action: () => actions.navigate?.(item.id) }, SEPARATOR, { glyph: GLYPH.copy, icon: Copy, label: 'Copy Panel Name', action: () => navigator.clipboard?.writeText(item.label) }, ] } function buildGenericItems(actions) { return [ { glyph: GLYPH.launch, icon: Zap, label: 'Command Palette', shortcut: '⌘K', action: () => actions.commandPalette?.() }, SEPARATOR, { glyph: GLYPH.api, icon: Code, label: 'API Reference', action: () => actions.navigate?.('api') }, { glyph: GLYPH.dossier, icon: FileText, label: 'Dashboard', action: () => actions.navigate?.('dashboard') }, ] } export function ContextMenuProvider({ children, actions }) { const [menu, setMenu] = useState(null) const open = useCallback((e, items) => { e.preventDefault() e.stopPropagation() const x = Math.min(e.clientX, window.innerWidth - 260) const y = Math.min(e.clientY, window.innerHeight - 420) setMenu({ x, y, items }) }, []) const close = useCallback(() => setMenu(null), []) const openFileMenu = useCallback((e, file) => { open(e, buildFileItems(file, actions)) }, [open, actions]) const openNavMenu = useCallback((e, item) => { open(e, buildNavItems(item, actions)) }, [open, actions]) const openGenericMenu = useCallback((e) => { open(e, buildGenericItems(actions)) }, [open, actions]) const value = { openFileMenu, openNavMenu, openGenericMenu, close } return ( {children} {menu && ( <>
{ e.preventDefault(); close() }} /> {menu.items.map((item, i) => { if (item.separator) { return
} const Icon = item.icon return ( ) })}
◆ Jorki Context right-click / ctrl+tap
)} ) }