ELNOR REPO READER TEXT MIRROR Original path: Design Mockups/Archived Mockups/DOC20 Mockups and Design/DOC20 Archived Mockups/DOC20 Workspace Redo/Q_WORKSPACE_V4 (1).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); // 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); 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 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}}

    }; return (
    {setOpenDrop(null)}}> {toast&&setToast(null)}/>} {/* ═══ SIDEBAR ICON RAIL ═══ */}
    Q
    {/* ═══ BROWSER — placeholder, collapsible ═══ */} {browserOpen&&
    Browser
    Q_BROWSER_R13.jsx
    plugs in here verbatim

    260px · collapsible
    Full scope/type/filter/sort/folder overlay/places/saved views
    } {/* ═══ NOTES SIDEBAR — placeholder, collapsible ═══ */} {noteListOpen&&!fullScreen&&
    Notes sidebar from
    Q_NOTES_FULL.jsx
    plugs in here

    220px · collapsible
    Folder tree · search · sort
    +Folder · +Note · archive
    Right-click context menu

    New: To-Do Lists tab
    Saved lists with icons + counts
    } {/* ═══ MAIN WORKSPACE ═══ */}
    {/* ─── Full toolbar (from Q_NOTES_FULL verbatim) ─── */}
    B}/>I}/>U}/> }/>}/> } title="Copy all" onClick={()=>flash("Copied")}/>
    } label="Save As…" dropdown onClick={e=>{e.stopPropagation();setOpenDrop(openDrop==="save"?null:"save")}}/> {openDrop==="save"&&
    e.stopPropagation()} style={{position:"absolute",top:28,left:0,backgroundColor:c.bgCard,border:`1px solid ${c.border}`,borderRadius:R.sm,boxShadow:"0 8px 24px rgba(0,0,0,0.12)",zIndex:999,minWidth:180,padding:4}}> {flash("Export MD");setOpenDrop(null)}}>Export as Markdown {flash("Export DOCX");setOpenDrop(null)}}>Export as DOCX {flash("Export PDF");setOpenDrop(null)}}>Export as PDF
    {flash("Save as Prompt");setOpenDrop(null)}}>Save as Prompt
    }
    } label="Ref" onClick={()=>flash("Ref copied")}/> } active={findBar} onClick={()=>setFindBar(!findBar)}/>} active={fullScreen} onClick={()=>setFullScreen(!fullScreen)}/> } label="2" active={commentsOpen} onClick={()=>setCommentsOpen(!commentsOpen)}/> {pendingChanges.length>0&&<>} label={showMarkup?"Markup":"Clean"} active={showMarkup} onClick={()=>setShowMarkup(!showMarkup)}/>
    } label={`${pendingChanges.length}`} dropdown active onClick={e=>{e.stopPropagation();setOpenDrop(openDrop==="review"?null:"review")}}/> {openDrop==="review"&&
    e.stopPropagation()} style={{position:"absolute",top:28,right:0,backgroundColor:c.bgCard,border:`1px solid ${c.border}`,borderRadius:R.sm,boxShadow:"0 8px 24px rgba(0,0,0,0.12)",zIndex:999,minWidth:160,padding:4}}> {acceptAll();setOpenDrop(null)}}>✓ Accept All ({pendingChanges.length}) {rejectAll();setOpenDrop(null)}}>✗ Reject All ({pendingChanges.length})
    }
    }
    Saved 2m ago
    {findBar&&
    0/0
    } {/* ─── CONTENT: To-Do | Editor | Comments ─── */}
    {/* ═══ TO-DO + ELNOR COLUMN (real, left of editor) ═══ */} {todoOpen&&!fullScreen&&
    {/* My Tasks header */}
    setTodoCollapsed(!todoCollapsed)} style={{padding:"8px 10px",display:"flex",alignItems:"center",gap:6,cursor:"pointer",backgroundColor:c.bgPanel,borderBottom:`1px solid ${c.borderLight}`}}>
    Today {activeTodos.length}
    {/* Tasks */} {!todoCollapsed&&
    {activeTodos.map(todo=>
    setExpandedTodo(expandedTodo===todo.id?null:todo.id)}> toggleTodo(todo.id)} onClick={e=>e.stopPropagation()} style={{width:15,height:15,accentColor:c.accentBtn,marginTop:1,flexShrink:0}}/>
    {todo.text}
    {priLabel(todo.pri)} {todo.due&&{todo.due}} {todo.linkedNote&&{e.stopPropagation();flash(`Opening "${todo.linkedNote}"`)}}>Note} {todo.sub.length>0&&{todo.sub.filter(s=>s.done).length}/{todo.sub.length}}
    {expandedTodo===todo.id&&
    {todo.sub.map(s=>
    toggleSub(todo.id,s.id)} style={{width:13,height:13,accentColor:c.accentBtn}}/> {s.text}
    )}
    flash("@Elnor…")}>@Elnor Sub flash(todo.linkedNote?`Open "${todo.linkedNote}"`:"Link a note…")}>{todo.linkedNote?"Open Note":"Link Note"}
    }
    )} {/* Add task */}
    setNewTodoText(e.target.value)} placeholder="Add task… (type @Elnor to assign)" onKeyDown={e=>{if(e.key==="Enter"&&newTodoText.trim()){setTodos(p=>[...p,{id:"t"+Date.now(),text:newTodoText,done:false,pri:3,sub:[]}]);setNewTodoText("")}}} style={{flex:1,border:"none",outline:"none",fontSize:12,fontFamily:font.sans,backgroundColor:"transparent",color:c.textPri}}/>
    {/* Done */} {doneTodos.length>0&&
    Done ({doneTodos.length})
    {doneTodos.map(t=>
    toggleTodo(t.id)} style={{width:14,height:14,accentColor:c.accentBtn}}/> {t.text}
    )}
    }
    } {/* ═══ ELNOR FEED (with category headers) ═══ */}
    setElnorCollapsed(!elnorCollapsed)} style={{padding:"8px 10px",display:"flex",alignItems:"center",gap:6,cursor:"pointer",backgroundColor:c.bgPanel}}>
    Elnor {Object.values(elnorFeedData).flat().length}
    {!elnorCollapsed&&
    {Object.entries(elnorFeedData).map(([cat,items])=>
    {elnorIcons[cat]} {cat} ({items.length})
    {items.map(item=>
    e.currentTarget.style.backgroundColor=c.bgInput} onMouseLeave={e=>e.currentTarget.style.backgroundColor="transparent"}> {item.accent&&} {item.text} {item.time}
    )}
    )}
    }
    } {/* ═══ EDITOR (from Q_NOTES_FULL — real, working) ═══ */}
    {if(bubbleMenu&&!e.target.closest("[data-bubble]"))setBubbleMenu(null)}}>
    Henderson Discovery Priorities
    2h ago · 2 comments · 3 pending changes
    {segs.map(renderSeg)}
    {/* Bubble menu (from Q_NOTES_FULL verbatim) */} {bubbleMenu&&
    {agent.name}
    {["Rewrite","Expand","Shorten"].map(a=>)}
    }
    {/* ═══ COMMENTS PANEL — placeholder, collapsible ═══ */} {commentsOpen&&!fullScreen&&
    Comments + Send to Agent
    from Q_NOTES_FULL.jsx
    plugs in here verbatim

    260–290px · right of editor
    Full comment cards
    Reply/Resolve/Edit/Delete
    Per-comment ✨ Send
    Agent/Chat selector
    Context scope radios
    Output mode + result format

    New: @mention support
    Type @Elnor in any text field
    (in specs, not visual here)
    }
    {/* ═══ CHAT INDICATOR ═══ */}
    CHAT →
    Henderson
    ); }