ELNOR REPO READER TEXT MIRROR Original path: Design Mockups/Archived Mockups/DOC20 Mockups and Design/DOC20 Archived Mockups/DOC20 Workspace Redo/Q_WORKSPACE_V4 (2).jsx Source repo: /Users/OpenClaw1/Elnor/Elnor Specs Git branch: main Git commit: dbaa25962edc11ab30e8d4ca1715f9ae5bf77331 Generated: 2026-06-09T01:23:58.539Z --- import { useState, useRef, useCallback, useEffect } from "react"; // ═══ TOKENS + ICONS + COMPONENTS — verbatim from Q_NOTES_FULL ═══ const font={sans:"'Söhne','Helvetica Neue',-apple-system,BlinkMacSystemFont,sans-serif"}; const R={sm:"6px",md:"10px",full:"9999px"}; const c={bgApp:"#F8F8F6",bgPanel:"#FFFFFF",bgPanelAlt:"#F9FAFB",bgCard:"#FFFFFF",bgInput:"#EFF1F3",bgSidebar:"#131820",textPri:"#1A1D21",textSec:"#5E6570",textTer:"#8B919A",accentBtn:"#31588c",warn:"#D97706",error:"#B04040",border:"#E0E2E5",borderLight:"#ECEEF0",green:"#2E8B57",agentAv:"#a1a7aa",borderDark:"#263040"}; const Ic=({d,size=18,color,sw=1.75})=>; const I={Plus:p=>,X:p=>,Check:p=>,Pin:p=>,Spark:p=>,MsgCircle:p=>,ChevD:p=>,ChevR:p=>,Search:p=>,Maximize:p=>,Save:p=>,Copy:p=>,Link:p=>,Eye:p=>,Edit:p=>,Undo:p=>,Redo:p=>,Folder:p=>,FileText:p=>,Trash:p=>,Archive:p=>,Settings:p=>,List:p=>,Bell:p=>,Calendar:p=>,Mail:p=>,Zap:p=>,}; const Dot=({color,size=8})=>; const Btn=({children,primary,ghost,small,onClick,disabled,style:s})=>; const Av=({letter,color,size=20})=>
{letter}
; const TBtn=({icon,title,active,onClick,label,dropdown})=>; const Sep=()=>
; const Toast=({msg,onDone})=>{useEffect(()=>{const t=setTimeout(onDone,2200);return()=>clearTimeout(t)},[onDone]);return
{msg}
}; const DItem=({children,onClick,active})=>; // ═══ DATA — from Q_NOTES_FULL (verbatim) ═══ const tcColors={You:{del:c.accentBtn,ins:c.accentBtn},Elnor:{del:"#B04040",ins:"#2E8B57"},Scout:{del:"#8B5E00",ins:"#2563EB"}}; const agents=[{name:"Elnor",color:c.agentAv,letter:"E"},{name:"Scout",color:"#5B5F97",letter:"S"}]; const segs=[{id:"s1",t:"p",v:"Key documents to review from discovery batch #4."},{id:"s2",t:"h2",v:"Priority Items"},{id:"s3",t:"li",v:"Email chain: Henderson → Outside Counsel (March 12-15) — 23 messages, 8 flagged"},{id:"s4",t:"li",v:"Privileged strategy memo (March 18) discussing litigation posture and settlement authority"},{id:"s5",t:"li",v:"Board minutes from Q1 2024 executive session"},{id:"s6",t:"h2",v:"Timeline Conflicts"},{id:"s7",t:"p",v:"Deposition transcript (Feb 12): initial contact January 5, but metadata suggests January 8."},{id:"s8",t:"p",v:"Cross-reference calendar, email timestamps, phone records Jan 5-8."},{id:"s9",t:"h2",v:"Next Steps"},{id:"s10",t:"p",v:"Prepare updated privilege log with all newly identified documents."}]; const initChanges=[{id:"tc1",segId:"s4",author:"Elnor",oldText:"discussing litigation posture and settlement authority",newText:"discussing litigation posture, settlement authority, and insurance coverage implications",status:"pending"},{id:"tc2",segId:"s7",author:"Elnor",oldText:"initial contact January 5, but metadata suggests January 8",newText:"initial contact January 5, but metadata and email headers suggest January 8",status:"pending"},{id:"tc3",segId:"s10",author:"You",oldText:"Prepare updated privilege log with all newly identified documents.",newText:"Prepare updated privilege log with all newly identified documents. Include Bates-stamp cross-references.",status:"pending"}]; const initComments=[{id:"c1",segId:"s3"},{id:"c2",segId:"s7"}]; // minimal for highlight tracking // ═══ NEW DATA — To-Do + Elnor Feed ═══ const initTodos=[ {id:"t1",text:"Draft expert disclosure for Christensen",done:false,pri:1,due:"Apr 4",linkedNote:"Expert Depo Prep — Dr. Smith",sub:[{id:"s1",text:"Pull Christensen CV",done:true},{id:"s2",text:"List opinions to be offered",done:false},{id:"s3",text:"Identify documents reviewed",done:false}]}, {id:"t2",text:"Review Henderson MSJ brief — Elnor's tracked changes",done:false,pri:1,due:"Mar 20",linkedNote:"Henderson Discovery Priorities",sub:[]}, {id:"t3",text:"Respond to Sparacino re: Narayanan agreement",done:false,pri:2,due:"Mar 19",sub:[]}, {id:"t4",text:"Follow up: opposing counsel on discovery extension",done:false,pri:2,due:"Mar 19",sub:[]}, {id:"t5",text:"Review Quark patent analysis — new filings flagged overnight",done:false,pri:3,linkedNote:"Quark Patent Analysis",sub:[]}, {id:"t6",text:"File Folb trial exhibit list",done:true,pri:2,due:"Mar 17",sub:[]}, ]; const elnorFeedData={ "Elnor":[{id:"e1",text:"Reviewed 3 Henderson deposition transcripts — flagged 2 citation issues in Chen depo",time:"5h ago"},{id:"e2",text:"Updated Quark patent landscape — 4 new filings",time:"4h ago"},{id:"e3",text:"Scanned SEC filings — no new Henderson-relevant 10-K amendments",time:"3h ago"},{id:"e4",text:"DOC15 red-team review completed — 3 findings flagged",time:"2h ago",accent:c.green}], "Calendar":[{id:"e5",text:"Expert disclosure deadline — Folb v. City of LA",time:"47 days",accent:c.error},{id:"e6",text:"Henderson MSJ response due",time:"12 days",accent:c.warn},{id:"e7",text:"Danny Christensen engagement call",time:"Tomorrow 2pm"}], "Email":[{id:"e8",text:"Sparacino — Narayanan agreement redline",time:"Yesterday"},{id:"e9",text:"Opposing counsel — discovery extension request",time:"2d ago",accent:c.warn}], "System":[{id:"e10",text:"OneDrive sync paused — re-auth needed",time:"6h ago",accent:c.warn}], "News":[{id:"e11",text:"SEC announces new cybersecurity disclosure rules effective Q2",time:"Today"},{id:"e12",text:"9th Circuit: new opinion on securities fraud scienter standard",time:"Yesterday",accent:c.accentBtn}], }; const elnorIcons={Elnor:,Calendar:,Email:,System:,News:}; // ═══════════════════════════════════════════ export default function WorkspaceV4(){ const idC=useRef(300);const nid=()=>"x"+(++idC.current); // Collapse toggles const [browserOpen,setBrowserOpen]=useState(true); const [noteListOpen,setNoteListOpen]=useState(true); const [todoOpen,setTodoOpen]=useState(true); const [commentsOpen,setCommentsOpen]=useState(false); const [rightTab,setRightTab]=useState("comments"); // Editor state (from Q_NOTES_FULL) const [changes,setChanges]=useState(initChanges); const [activeComment,setActiveComment]=useState(null); const [bubbleMenu,setBubbleMenu]=useState(null); const [showMarkup,setShowMarkup]=useState(true); const [findBar,setFindBar]=useState(false); const [fullScreen,setFullScreen]=useState(false); const [openDrop,setOpenDrop]=useState(null); const [toast,setToast]=useState(null); const [agentIdx]=useState(0); // Inline thread state const [threads,setThreads]=useState([ {id:"th1",collapsed:false,contextQuote:"Email chain: Henderson → Outside Counsel",messages:[ {id:"m1",author:"You",color:c.accentBtn,body:"@Elnor check if any of these 8 flagged emails fall under work-product doctrine",time:"2h ago"}, {id:"m2",author:"Elnor",color:c.agentAv,body:"Reviewed all 8. Five are clearly work-product (litigation strategy discussions). Two are borderline — they discuss business decisions but reference pending litigation. One (March 14, Henderson to CFO) is likely not privileged. Want me to draft privilege log entries for the 5 clear ones?",time:"1h ago"}, {id:"m3",author:"You",color:c.accentBtn,body:"Yes, draft those. Flag the borderline two for my review with the specific language that concerns you.",time:"45m ago"}, {id:"m4",author:"Elnor",color:c.agentAv,body:"Done — privilege log entries added as tracked changes below. The two borderline emails both contain the phrase \"given the pending lawsuit\" in otherwise business-context discussions. I've highlighted them in the Privilege Review Checklist note.",time:"30m ago"}, ]}, {id:"th2",collapsed:true,contextQuote:"Cross-reference calendar, email timestamps",messages:[ {id:"m5",author:"You",color:c.accentBtn,body:"@Elnor pull phone records for Jan 5-8 and cross-ref with the calendar entries",time:"1d ago"}, {id:"m6",author:"Elnor",color:c.agentAv,body:"Found 3 calls from Henderson's cell to outside counsel on Jan 6. Calendar shows a meeting on Jan 5 that was rescheduled to Jan 8. The metadata discrepancy may be explained by the reschedule.",time:"1d ago"}, ]}, ]); const [threadReplyId,setThreadReplyId]=useState(null); const [threadReplyText,setThreadReplyText]=useState(""); const editorRef=useRef(null); const agent=agents[agentIdx]; const pendingChanges=changes.filter(x=>x.status==="pending"); const flash=msg=>setToast(msg); // To-do state const [todos,setTodos]=useState(initTodos); const [expandedTodo,setExpandedTodo]=useState("t1"); const [todoCollapsed,setTodoCollapsed]=useState(false); const [elnorCollapsed,setElnorCollapsed]=useState(false); const [newTodoText,setNewTodoText]=useState(""); // Editor logic (from Q_NOTES_FULL verbatim) const acceptChange=id=>setChanges(p=>p.map(x=>x.id===id?{...x,status:"accepted"}:x)); const rejectChange=id=>setChanges(p=>p.map(x=>x.id===id?{...x,status:"rejected"}:x)); const acceptAll=()=>{setChanges(p=>p.map(x=>x.status==="pending"?{...x,status:"accepted"}:x));flash("All accepted")}; const rejectAll=()=>{setChanges(p=>p.map(x=>x.status==="pending"?{...x,status:"rejected"}:x));flash("All rejected")}; const handleMouseUp=useCallback(e=>{if(e.target.closest("[data-bubble]"))return;const sel=window.getSelection();if(!sel||sel.isCollapsed||!sel.toString().trim()){setBubbleMenu(null);return}const text=sel.toString().trim();if(text.length<3)return;const rect=sel.getRangeAt(0).getBoundingClientRect();const er=editorRef.current?.getBoundingClientRect();if(!er)return;setBubbleMenu({x:rect.left-er.left+rect.width/2,y:rect.top-er.top-8,text})},[]); const dismissBubble=()=>{setBubbleMenu(null);window.getSelection()?.removeAllRanges()}; // To-do logic const toggleTodo=id=>setTodos(p=>p.map(t=>t.id===id?{...t,done:!t.done}:t)); const toggleSub=(pid,sid)=>setTodos(p=>p.map(t=>t.id===pid?{...t,sub:t.sub.map(s=>s.id===sid?{...s,done:!s.done}:s)}:t)); const toggleThread=id=>setThreads(p=>p.map(t=>t.id===id?{...t,collapsed:!t.collapsed}:t)); const addThreadReply=(threadId)=>{if(!threadReplyText.trim())return;setThreads(p=>p.map(t=>t.id===threadId?{...t,messages:[...t.messages,{id:nid(),author:"You",color:c.accentBtn,body:threadReplyText.trim(),time:"just now"}]}:t));setThreadReplyId(null);setThreadReplyText("")}; const priColor=p=>p===1?c.error:p===2?c.warn:c.accentBtn; const priLabel=p=>p===1?"P1":p===2?"P2":"P3"; const activeTodos=todos.filter(t=>!t.done); const doneTodos=todos.filter(t=>t.done); // Tracked changes rendering (from Q_NOTES_FULL verbatim) const renderText=seg=>{const tc=changes.find(x=>x.segId===seg.id&&x.status==="pending");const tca=changes.find(x=>x.segId===seg.id&&x.status==="accepted");if(tca)return {seg.v.replace(tca.oldText,tca.newText)};if(!tc||!showMarkup){if(tc&&!showMarkup)return {seg.v.replace(tc.oldText,tc.newText)};return {seg.v}}const idx=seg.v.indexOf(tc.oldText);if(idx===-1)return {seg.v};const ac=tcColors[tc.author]||tcColors.Elnor;return {seg.v.slice(0,idx)}{tc.oldText}{tc.newText}{" "}{tc.author}{seg.v.slice(idx+tc.oldText.length)}}; const renderSeg=seg=>{const hasCm=initComments.some(cm=>cm.segId===seg.id);const isAct=activeComment&&initComments.find(cm=>cm.id===activeComment)?.segId===seg.id;const hl=isAct?{backgroundColor:c.accentBtn+"18",borderRadius:3,padding:"2px 4px",margin:"-2px -4px",boxShadow:`inset 0 0 0 1.5px ${c.accentBtn}40`}:hasCm?{backgroundColor:c.accentBtn+"08",borderRadius:3,padding:"1px 3px",margin:"-1px -3px"}:{};const cnt=initComments.filter(cm=>cm.segId===seg.id).length;if(seg.t==="h2")return

{seg.v}

;if(seg.t==="li")return
  • {renderText(seg)}{cnt>0&&{const cm=initComments.find(x=>x.segId===seg.id);if(cm)setActiveComment(cm.id)}}>💬{cnt}}
  • ;return

    {renderText(seg)}{cnt>0&&{const cm=initComments.find(x=>x.segId===seg.id);if(cm)setActiveComment(cm.id)}}>💬{cnt}}

    }; // ─── Inline Thread Component ─── const InlineThread=({th})=>{ if(th.collapsed) return (
    toggleThread(th.id)} style={{margin:"10px 0",padding:"8px 12px",borderRadius:R.sm,border:`1px solid ${c.borderLight}`,backgroundColor:c.bgPanelAlt,cursor:"pointer",display:"flex",alignItems:"center",gap:8}} onMouseEnter={e=>e.currentTarget.style.borderColor=c.accentBtn+"40"} onMouseLeave={e=>e.currentTarget.style.borderColor=c.borderLight}> Elnor · {th.messages.length} messages {th.messages[th.messages.length-1]?.time}
    ); return (
    {/* Thread header */}
    toggleThread(th.id)} style={{padding:"6px 12px",display:"flex",alignItems:"center",gap:6,cursor:"pointer",backgroundColor:c.accentBtn+"06",borderBottom:`1px solid ${c.accentBtn}15`}}> Inline thread · {th.messages.length} messages {th.contextQuote&&re: "{th.contextQuote}"}
    {/* Messages */} {th.messages.map((msg,i)=>
    {msg.author} {msg.time} {msg.author==="You"&&{e.stopPropagation();flash("Edit message…")}}>edit} {msg.author==="You"&&{e.stopPropagation();flash("Delete message…")}}>delete}
    {msg.body}
    )} {/* Reply input */} {threadReplyId===th.id?