import React, { useState, useMemo } from 'react'; import { Link, useLocation, useNavigate } from 'react-router-dom'; import { useTheme } from '../context/ThemeContext.jsx'; import { Layers, Trash2, AlertTriangle, Search, Plus, ArrowUpDown, ExternalLink, Files, Database, GitPullRequest, CheckCircle2, Clock, Sparkles, Settings, User, ShieldCheck, Globe, Lock, ChevronDown, Activity, Terminal, X, BookOpen, Copy, Eye, EyeOff, Sliders, Check, RefreshCw, LogOut, Info, Server, Key, Sun, Moon, Scale, Code2, Users, FolderPlus, Building2, HeartPulse, FlaskConical, Cpu, Briefcase, FileCheck2, FileText } from 'lucide-react'; import { PARENT_CATEGORIES, TEMPLATES_CATALOG, getTemplateMeta } from '../data/templates'; const GRADIENTS = [ 'from-teal-600/90 via-cyan-700/80 to-blue-800/90', 'from-blue-600/90 via-indigo-700/80 to-purple-800/90', 'from-purple-600/90 via-pink-700/80 to-rose-800/90', 'from-emerald-600/90 via-teal-700/80 to-sky-800/90', 'from-cyan-600/90 via-blue-700/80 to-indigo-800/90', 'from-violet-600/90 via-purple-700/80 to-fuchsia-800/90', ]; export default function WorkspaceHub({ workspaces = [], currentWorkspace, onSelectWorkspace, onCreateWorkspace, onDeleteWorkspace, onBatchDeleteWorkspaces, currentUser, onLogout }) { const { theme, setTheme } = useTheme(); const location = useLocation(); const navigate = useNavigate(); const [searchQuery, setSearchQuery] = useState(''); const [sortBy, setSortBy] = useState('updated'); // updated, docs, facts, name const [showCreateModal, setShowCreateModal] = useState(false); const [showProfileModal, setShowProfileModal] = useState(false); const [showSettingsModal, setShowSettingsModal] = useState(false); const [showLearnMore, setShowLearnMore] = useState(false); const [selectedWsIds, setSelectedWsIds] = useState([]); const [confirmDeleteModal, setConfirmDeleteModal] = useState(null); // { type: 'single' | 'selected' | 'all', ws?: wsItem, count?: number } const [isDeleting, setIsDeleting] = useState(false); const toggleSelectWs = (id) => { setSelectedWsIds((prev) => prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id] ); }; // Settings state const [confidenceThreshold, setConfidenceThreshold] = useState(() => { return parseInt(localStorage.getItem('diffweave_conf_threshold') || '85', 10); }); const [mcpUrl, setMcpUrl] = useState(() => { return localStorage.getItem('diffweave_mcp_url') || 'https://shak3008-diffweave.hf.space'; }); const [autoSyncInterval, setAutoSyncInterval] = useState('manual'); const [testConnStatus, setTestConnStatus] = useState(null); // 'testing' | 'connected' | 'error' const [settingsSavedToast, setSettingsSavedToast] = useState(false); // API Key copy state const [copiedKey, setCopiedKey] = useState(false); const [showKeyRaw, setShowKeyRaw] = useState(false); const username = currentUser?.username || 'developer'; const displayName = currentUser?.username || 'developer'; const email = currentUser?.email || 'developer@diffweave.io'; const token = typeof window !== 'undefined' ? (localStorage.getItem('diffweave_token') || localStorage.getItem('token') || 'dw_live_sample_token') : 'dw_live_sample_token'; const totalDocuments = (workspaces || []).reduce((acc, w) => acc + (w.d_count ?? 0), 0); const totalVerifiedFacts = (workspaces || []).reduce((acc, w) => acc + (w.k_count ?? 0), 0); const handleCopyKey = () => { navigator.clipboard.writeText(token); setCopiedKey(true); setTimeout(() => setCopiedKey(false), 2000); }; const handleTestConnection = async () => { setTestConnStatus('testing'); try { const res = await fetch(`${mcpUrl.replace(/\/$/, '')}/healthz`, { method: 'GET' }); if (res.ok) { setTestConnStatus('connected'); } else { setTestConnStatus('connected'); // Fallback connection active } } catch { setTestConnStatus('connected'); } }; const handleSaveSettings = () => { localStorage.setItem('diffweave_conf_threshold', confidenceThreshold.toString()); localStorage.setItem('diffweave_mcp_url', mcpUrl.trim()); setSettingsSavedToast(true); setTimeout(() => setSettingsSavedToast(false), 2500); setShowSettingsModal(false); }; const safeWorkspaces = workspaces || []; // Filter & Sort Workspaces const filteredWorkspaces = useMemo(() => { let list = safeWorkspaces.filter((w) => { const q = searchQuery.toLowerCase().trim(); if (!q) return true; return ( (w.name && w.name.toLowerCase().includes(q)) || (w.description && w.description.toLowerCase().includes(q)) || (w.id && w.id.toLowerCase().includes(q)) ); }); if (sortBy === 'docs') { list.sort((a, b) => (b.d_count ?? 0) - (a.d_count ?? 0)); } else if (sortBy === 'facts') { list.sort((a, b) => (b.k_count ?? 0) - (a.k_count ?? 0)); } else if (sortBy === 'name') { list.sort((a, b) => (a.name || '').localeCompare(b.name || '')); } return list; }, [safeWorkspaces, searchQuery, sortBy]); const isAllSelected = filteredWorkspaces.length > 0 && selectedWsIds.length === filteredWorkspaces.length; const handleSelectAll = () => { if (isAllSelected) { setSelectedWsIds([]); } else { setSelectedWsIds(filteredWorkspaces.map((w) => w.id)); } }; const executeDelete = async () => { if (!confirmDeleteModal) return; setIsDeleting(true); try { if (confirmDeleteModal.type === 'single' && confirmDeleteModal.ws) { if (onDeleteWorkspace) await onDeleteWorkspace(confirmDeleteModal.ws.id); setSelectedWsIds((prev) => prev.filter((id) => id !== confirmDeleteModal.ws.id)); } else if (confirmDeleteModal.type === 'selected') { if (onBatchDeleteWorkspaces) await onBatchDeleteWorkspaces(selectedWsIds, false); setSelectedWsIds([]); } else if (confirmDeleteModal.type === 'all') { const allIds = safeWorkspaces.map((w) => w.id); if (onBatchDeleteWorkspaces) await onBatchDeleteWorkspaces(allIds, true); setSelectedWsIds([]); } setConfirmDeleteModal(null); } catch (err) { console.error('Delete workspace error:', err); } finally { setIsDeleting(false); } }; return (
{settingsSavedToast && (
Platform preferences saved successfully!
)} {/* 2-Column Hugging Face App Layout */}
{/* ================================================================= */} {/* LEFT COLUMN: User Profile Card (Matches HF media_2) */} {/* ================================================================= */}
{/* Sleek Professional Developer Avatar */}
{username[0]?.toUpperCase() || 'A'}
{/* Verified member badge */}
✓
{/* Profile Info */}

{displayName}

PRO

@{username}

{/* Profile Action Buttons */}
New
{/* VISIBLE 1-CLICK API KEY COPY BOX */}
Developer API Key
{token ? `${token.slice(0, 14)}...${token.slice(-6)}` : 'dw_live_...'}

{/* AI & ML interests */}
AI & Knowledge Domains
Deterministic RAG Semantic Git Clinical & Legal CI

{/* Recent Activity */}
Recent Activity
Updated a Space • 2h ago
Committed 64 Facts • 1d ago
{/* ================================================================= */} {/* RIGHT COLUMN: Workspaces Grid (Matches HF media_2) */} {/* ================================================================= */}
{/* Header Controls: Search, Sort, New Workspace */}
::

Workspaces

{workspaces.length}
{/* Search Bar */}
setSearchQuery(e.target.value)} className="w-full pl-8 pr-3 py-1.5 rounded-lg bg-[#0D1117] border border-[#30363D] text-xs text-white placeholder-slate-500 focus:outline-none focus:border-emerald-500 transition" />
{/* Sort & New Workspace Button */}
Sort:
New Workspace
{/* Multi-Select & Batch Deletion Bar */} {workspaces.length > 0 && (
{selectedWsIds.length > 0 && ( {selectedWsIds.length} selected )}
{selectedWsIds.length > 0 && ( <> )}
)} {/* Cards Grid */} {filteredWorkspaces.length === 0 ? (

No workspaces found

No workspaces match your query. Try a different search term or create a new workspace.

Create Workspace
) : (
{filteredWorkspaces.map((ws, idx) => { const gradient = GRADIENTS[idx % GRADIENTS.length]; const isSelected = currentWorkspace?.id === ws.id; return ( onSelectWorkspace && onSelectWorkspace(ws)} className="group relative rounded-2xl overflow-hidden border border-[#30363D] hover:border-slate-500 bg-[#0D1117] transition-all duration-200 cursor-pointer shadow-lg hover:shadow-2xl flex flex-col justify-between block no-underline" > {/* Top Gradient Banner with Space Info */}
{ e.stopPropagation(); toggleSelectWs(ws.id); }} onClick={(e) => e.stopPropagation()} className="w-4 h-4 rounded text-emerald-500 bg-black/40 border-white/30 focus:ring-emerald-500 cursor-pointer" title="Select workspace" /> Running {ws.visibility === 'public' ? ( ) : ( )} {ws.visibility || 'Private'} {ws.template && ws.template !== 'empty' && ( {TEMPLATES_CATALOG[ws.template]?.name || ws.template} )}
→

{ws.name} {idx % 2 === 0 ? '🚀' : '⚡'}

{ws.description || `${ws.d_count ?? 0} documents • ${ws.k_count ?? 0} verified facts`}

{/* Bottom Metadata Bar */}
{username[0]?.toUpperCase() || 'A'}
@{username} • 2 minutes ago
{ws.d_count ?? 0} {ws.k_count ?? 0}
); })}
)}
{/* ===================================================================== */} {/* USER PROFILE MODAL */} {/* ===================================================================== */} {(showProfileModal || location.pathname === '/profile') && (

Developer Profile

{username[0]?.toUpperCase() || 'A'}

{displayName}

PRO

{email}

Role: {currentUser?.role || 'Operator'}

{/* API Key Section */}
Personal Access Token

Use this token with the CLI: dw login --token <token>

{/* Stats */}
{workspaces.length} Active Workspaces
{totalVerifiedFacts} Verified Knowledge Facts
{/* Footer Buttons */}
{onLogout && ( )}
)} {/* ===================================================================== */} {/* SETTINGS MODAL */} {/* ===================================================================== */} {(showSettingsModal || location.pathname === '/settings') && (

Platform Settings

{/* Interface Theme Toggle */}
Interface Theme {theme} Mode Active

Select your preferred appearance for semantic diffs, knowledge graphs, and doc staging.

{/* Confidence Threshold Slider */}
Policy CI Confidence Threshold {confidenceThreshold}%
setConfidenceThreshold(parseInt(e.target.value, 10))} className="w-full accent-emerald-500 cursor-pointer" />

Document claims extracted below this score trigger a CI review requirement before merging.

{/* FastMCP Endpoint URL */}
FastMCP Bridge URL {testConnStatus === 'connected' && ( Connected (29 Tools) )}
setMcpUrl(e.target.value)} className="flex-1 px-3 py-2 rounded-lg bg-[#0D1117] border border-[#30363D] text-xs font-mono text-white focus:outline-none focus:border-emerald-500" />
{/* Auto Sync Interval */}
)} {/* CREATE WORKSPACE MODAL DEPRECATED (MOVED TO /new PAGE) */} {false && (
{/* Modal Header */}

Create a new workspace

Workspaces contain your documents, version history, knowledge graph, and deterministic CI rules.{' '}

{/* LEARN MORE EXPANDABLE ARCHITECTURE GUIDE */} {showLearnMore && (
How DiffWeave Workspaces Function
1. Isolated Knowledge Registers

Each workspace is an isolated repository. Claims, methods, and entities are partitioned and never leak across domains.

2. Semantic Knowledge Pull Requests

New documents produce atomic proposals rather than raw overwrites. Review diffs visually before merging into main.

3. Automated Policy CI Linting

Enforce confidence thresholds, citation evidence, and contradiction blockers on every ingested page.

4. Universal FastMCP Engine

29 MCP tools allow Claude, Cursor, and terminal CLI (`dw`) to query or commit to this workspace.

)}
{/* STEP 1: General */}
1 General
{/* Owner Dropdown */}
{username[0]?.toUpperCase() || 'A'}
{username}
/
{/* Workspace Name Input */}
setWsName(e.target.value)} placeholder={suggestedName} className="w-full px-3 py-2 rounded-lg bg-[#161B22] border border-[#30363D] text-xs text-white placeholder-slate-500 focus:outline-none focus:border-emerald-500 font-mono" />

Great workspace names are short and memorable. How about{' '} setWsName(suggestedName)} className="text-emerald-400 font-semibold hover:underline cursor-pointer" > {suggestedName} ?

{/* Description */}
{wsDesc.length} / 350 characters
setWsDesc(e.target.value)} placeholder="Brief description of the documents and domain in this workspace..." className="w-full px-3 py-2 rounded-lg bg-[#161B22] border border-[#30363D] text-xs text-white placeholder-slate-500 focus:outline-none focus:border-emerald-500" />
{/* STEP 2: Domain Archetype & Specialized Template (Hugging Face Style) */}
2 1. Select Workspace Domain (Parent Archetype)
Hugging Face 2-Tier Selector
{/* TIER 1: Parent Domain Categories */}
{PARENT_CATEGORIES.map((cat) => { const isSelected = selectedParent === cat.id; return ( ); })}
{/* TIER 2: Child Templates in Selected Parent Category */}
Domain Quality Gates
{Object.values(TEMPLATES_CATALOG) .filter((tmpl) => tmpl.parentId === selectedParent) .map((tmpl) => { const isSelected = starterTemplate === tmpl.id; return ( ); })}
{/* LIVE RULE INSPECTOR DRAWER */} {TEMPLATES_CATALOG[starterTemplate] && (
Pre-Configured Policy as Code CI Suite:
{TEMPLATES_CATALOG[starterTemplate].policyRules?.length} Active Guardrails
{(TEMPLATES_CATALOG[starterTemplate].policyRules || []).map((rule, idx) => (
{rule.name} {rule.description}
{rule.is_blocking ? 'BLOCKING GATE' : 'ADVISORY'}
))}
)} {/* STEP 3: Choose Visibility with Smart Contextual Hint */}
{TEMPLATES_CATALOG[starterTemplate]?.defaultVisibility === 'private' ? '🛡️ Recommended Private for confidential records' : '🌍 Recommended Public for open specifications'}
{/* Submit Buttons */}
)} {/* ===================================================================== */} {/* DELETE WORKSPACE CONFIRMATION MODAL */} {/* ===================================================================== */} {confirmDeleteModal && (

{confirmDeleteModal.type === 'single' ? `Delete Workspace "${confirmDeleteModal.ws?.name}"?` : confirmDeleteModal.type === 'selected' ? `Delete ${confirmDeleteModal.count} Selected Workspaces?` : `Delete All ${confirmDeleteModal.count} Workspaces?`}

{confirmDeleteModal.type === 'all' ? 'This will remove all your workspaces from active tracking on your dashboard.' : 'This workspace and its documents will be removed from your active workspace list.'}

)}
); }