Q_TASK_MODULAR_CANVAS_V11_ALT_D_OUTCOME_FORUM_VISUAL_AID.jsx
Design Mockups/DOC23 Mockups/Q_TASK_MODULAR_CANVAS_V11_ALT_D_OUTCOME_FORUM_VISUAL_AID.jsx
ELNOR REPO READER TEXT MIRROR
Original path: Design Mockups/DOC23 Mockups/Q_TASK_MODULAR_CANVAS_V11_ALT_D_OUTCOME_FORUM_VISUAL_AID.jsx
Source repo: /Users/OpenClaw1/Elnor/Elnor Specs
Git branch: main
Git commit: dbaa25962edc11ab30e8d4ca1715f9ae5bf77331
Generated: 2026-06-09T01:23:58.539Z
---
import React, { useState, useCallback, useRef } from "react";
// ═══════════════════════════════════════════════════════════════
// Q TASK SYSTEM — MODULAR CANVAS V11 ALT D — DOC20 SHELL + INSPECTOR REFINEMENT
// Experimental Mode: DOC20 main-area integration, graph⇄inspector navigation,
// module output links, artifact/delivery access, context inspector, evaluator feedback routing, and Task Agent side panel
// ═══════════════════════════════════════════════════════════════
const c = {
bg:"#0f1117", bgCard:"#161920", bgHover:"#1c1f2a", bgSel:"#1e2233",
bgPanel:"#131620", bgPopup:"#1a1e2b", border:"#262a36",
text:"#e2e4ea", textSec:"#8b8fa3", textTer:"#5d6178",
accent:"#5b8af5", green:"#34d399", red:"#f87171",
amber:"#fbbf24", purple:"#a78bfa", cyan:"#22d3ee", orange:"#fb923c",
};
const sans = "-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif";
const mono = "'SF Mono','Cascadia Code','Fira Code', monospace";
const catColor = { trigger:c.amber, step:c.accent, utility:c.purple, output:c.green, source:c.cyan, environment:"#8b9cf5", system:c.orange };
const catLabel = { trigger:"TRIGGER", step:"ACTION", utility:"UTILITY", output:"OUTPUT", source:"SOURCE", environment:"ENVIRON", system:"SYSTEM" };
const catIcon = { trigger:"⚡", step:"◆", utility:"⑂", output:"→", source:"◇", environment:"◎", system:"⊙" };
// ── CONFIG SCHEMAS ──
const MC = {
"trigger.manual":[{t:"n",v:"Fires when you click ▶ Run. You can optionally provide launch data (text or files) in the Run dialog."},{t:"n",v:"Resuming a paused task does NOT re-fire this trigger — resume continues from wherever execution was paused."},{t:"hd",l:"Manual Fire"},{t:"n",v:"Use the button below to fire this trigger independently (e.g., when the task is running and you want to re-inject at this point)."},{t:"btn",l:"▶ Fire Now"}],
"trigger.schedule":[
{t:"s",l:"Schedule Type",o:["Recurring interval","Day of week + time","Specific date & time (one-shot)"],v:"Recurring interval"},
{t:"hd",l:"Recurring Interval"},
{t:"row",items:[{t:"i",l:"Every",v:"1"},{t:"s",l:"Unit",o:["minutes","hours","days","weeks"],v:"days"}]},
{t:"hd",l:"Day of Week + Time"},
{t:"n",v:"Select one or more days:"},
{t:"tags",l:"Days",v:["Sun"]},
{t:"row",items:[{t:"i",l:"Time",v:"16:00",p:"HH:MM"},{t:"s",l:"Timezone",o:["America/Los_Angeles","America/New_York","America/Chicago","UTC"],v:"America/Los_Angeles"}]},
{t:"hd",l:"One-Shot"},
{t:"i",l:"Date & Time 📅",v:"",p:"calendar + time picker in app"},
{t:"n",v:"All date/time fields render as calendar picker + hours/minutes/AM-PM selector in the built app. ISO format shown in mockup only."},
{t:"hd",l:"Common"},
{t:"i",l:"End Date",v:"",p:"blank = no end"},
{t:"c",l:"Enabled",ck:true}],
"trigger.email":[
{t:"n",v:"Fires when a matching email arrives. Wire outputs to route the email body, attachments, or both to downstream modules. Use the output port selector to choose what data to send."},
{t:"s",l:"Condition Logic",o:["ALL must match (AND)","ANY can match (OR)"],v:"ALL must match (AND)"},
{t:"n",v:"Blank fields are ignored. Supports boolean: term AND term, term OR term, \"exact phrase\"."},
{t:"hd",l:"Recipient Filter"},
{t:"i",l:"Email Address",v:"",p:"will@schall.com"},
{t:"c",l:"Match in To",ck:true},
{t:"c",l:"Match in CC",ck:true},
{t:"c",l:"Match in BCC",ck:false},
{t:"hd",l:"Sender & Content Filters"},
{t:"i",l:"From",v:"",p:"*@opposing.com"},
{t:"i",l:"Subject Contains",v:"",p:"keyword AND keyword, \"exact phrase\""},
{t:"i",l:"Subject Exact",v:"",p:"exact subject line"},
{t:"i",l:"Body Contains",v:"",p:"keyword OR keyword"},
{t:"hd",l:"Attachment Filters"},
{t:"c",l:"Must have attachment",ck:false},
{t:"i",l:"Attachment Type",v:"",p:"*.pdf, *.docx"},
{t:"i",l:"Attachment Name Contains",v:"",p:"filename keyword AND keyword"},
{t:"i",l:"Attachment Content Contains",v:"",p:"search inside attachments, term OR term"},
{t:"n",v:"Content search: text-extractable files only (PDF, DOCX, TXT). First 50 pages searched."},
{t:"c",l:"Ignore auto-replies",ck:true}],
"trigger.file_watcher":[
{t:"s",l:"Watch For",o:["New file","Modified","Deleted","Any change"],v:"New file"},
{t:"i",l:"Folder Path 📁",v:"",p:"click 📁 to browse or type path"},
{t:"n",v:"📁 opens Finder to select a folder. Add multiple paths to watch several folders."},
{t:"tags",l:"Additional Paths",v:[]},
{t:"i",l:"File Type Filter",v:"",p:"*.pdf, *.docx"},
{t:"i",l:"Ignore Pattern",v:"",p:"~$*, .tmp, .DS_Store"},
{t:"hd",l:"Content Filters (text files only)"},
{t:"i",l:"Title Contains",v:"",p:"keyword AND keyword, \"exact phrase\""},
{t:"i",l:"Document Contains",v:"",p:"term OR term, \"exact phrase\""}],
"trigger.discord":[
{t:"i",l:"Channel",v:"",p:"channel name or ID (blank = any)"},
{t:"i",l:"Keyword Filter",v:"",p:"comma-separated keywords"},
{t:"i",l:"User Filter",v:"",p:"username (blank = any)"},
{t:"s",l:"Attachments",o:["Any","Must have","Must not have"],v:"Any"}],
"trigger.task_complete":[
{t:"s",l:"Listen For",o:["Task completed","Output received from task"],v:"Task completed"},
{t:"s",l:"Wait Mode",o:["Any listed task","ALL listed tasks"],v:"Any listed task"},
{t:"ta",l:"Task References",v:"",h:50,p:"task IDs, one per line"},
{t:"i",l:"Output Module Ref",v:"",p:"paste output.task module ref (for 'output received')"},
{t:"c",l:"Send my result back to calling task when I finish",ck:false},
{t:"n",v:"'Task completed' passes the completed task's final output as data_out. 'Output received' passes whatever the sending output.task module included. Position determines behavior — first module = new run, mid-chain = resume."}],
"trigger.forum_panel":[
{t:"i",l:"Target Panel/Forum ID",v:"",p:"blank = any"},
{t:"i",l:"Keyword Filter",v:"",p:"searches panel posts (not output document)"},
{t:"s",l:"Event Type",o:["Panel complete (any outcome)","Consensus reached","Post created","Any event"],v:"Panel complete (any outcome)"},
{t:"hd",l:"Pass Through"},
{t:"s",l:"Data to pass downstream",o:["Panel output document","Panel posts (all rounds)","Both (output + posts)","Summary of posts"],v:"Panel output document"},
{t:"n",v:"Panel output document = the consensus/final result. Panel posts = the full discussion. 'Both' bundles them. 'Summary' runs a lightweight agent summary of the discussion."}],
"trigger.webhook":[
{t:"i",l:"Webhook Path",v:"filing-processor-hook",p:"auto-generated"},
{t:"n",v:"URL: https://elnor.local/hooks/{path}"},
{t:"ta",l:"Expected Fields (JSON)",v:"",h:50,p:'{"filing_id":"string"}'},
{t:"i",l:"HMAC Secret",v:"",p:"optional verification secret"}],
"step.agent_task":[
{t:"hd",l:"Agent"},
{t:"s",l:"Agent Mode",o:["Task default","Named agent","Spawn new"],v:"Task default"},
{t:"n",v:"'Task default' = inherits from Task Settings (all fields below disabled). 'Named agent' = pick a registered agent. 'Spawn new' = one-off agent, agent dropdown hidden, model required."},
{t:"s",l:"Agent (for Named agent mode)",o:["Elnor","Nova","Prism","Atlas","Sage"],v:"Elnor"},
{t:"row",items:[{t:"s",l:"Model",o:["(agent default)","claude-opus-4-6","claude-sonnet-4-6","gemini-2.5-pro"],v:"(agent default)"},{t:"s",l:"Think Level",o:["(default)","off","minimal","low","medium","high","xhigh"],v:"(default)"}]},
{t:"row",items:[{t:"s",l:"Fallback Model",o:["(global chain)","claude-sonnet-4-6","gemini-2.5-pro"],v:"(global chain)"},{t:"i",l:"Cost Limit ($)",v:"10.00"}]},
{t:"ta",l:"Instruction",v:"",h:100,p:"What should the agent do with the input?"},
{t:"tags",l:"📎 Attachments",v:["filing_2026-03-15.pdf"]},
{t:"s",l:"If Data Exceeds Context Budget",o:["Fail (safest)","Chunk & iterate","Truncate with warning"],v:"Fail (safest)"},
{t:"hd",l:"Chain History"},
{t:"s",l:"History for this module",o:["Use environment default","Last N steps","None"],v:"Use environment default"},
{t:"i",l:"Last N Steps",v:"",p:"blank = environment default"},
{t:"c",l:"Summarize older history (vs drop entirely)",ck:true},
{t:"hd",l:"Retry & Timeout"},
{t:"row",items:[{t:"i",l:"Max Retries",v:"0"},{t:"i",l:"Backoff (min)",v:"1",p:"minutes — supports decimals"}]},
{t:"s",l:"Retry On",o:["Retryable only (timeout, rate limit)","All errors"],v:"Retryable only (timeout, rate limit)"},
{t:"i",l:"Call Timeout (min)",v:"",p:"blank = 20 min (agent default). Panel/Red Team = no timeout."},
{t:"hd",l:"Named File Outputs"},
{t:"tags",l:"Outputs",v:["Analysis","Prompt"]},
{t:"btn",l:"+ Add Output"},
{t:"n",v:"Each name becomes a wireable output port on this module. CIL auto-instructs the agent to name files matching these labels. Wire each named output to its destination — e.g., '📄 Analysis' → Email body, '📄 Prompt' → next agent's instruction_in."},
{t:"hd",l:"Session Continuity"},
{t:"c",l:"Continue prior agent session",ck:false},
{t:"n",v:"When enabled, this module dispatches into the upstream agent module's existing session instead of opening a new one. The agent retains full conversation context — including its prior reasoning and decisions — from the upstream step. Requires same model. Only works with direct upstream agent connections (not through Junctions)."}],
"step.transform":[
{t:"s",l:"Transform Type",o:["Summarize","Extract fields","Format / convert","Merge inputs","Redact","Split"],v:"Summarize"},
{t:"hd",l:"Summarize (LLM call)"},
{t:"ta",l:"Summarize Instructions",v:"",h:60,p:"Executive summary of key findings, bullet point deadlines, one paragraph for a partner"},
{t:"s",l:"Agent Mode",o:["Task default","Named agent","Spawn new"],v:"Task default"},
{t:"row",items:[{t:"s",l:"Agent",o:["Elnor","Nova","Prism","Atlas","Sage"],v:"Elnor"},{t:"i",l:"Target Word Count",v:"",p:"blank = auto"}]},
{t:"hd",l:"Extract"},
{t:"s",l:"Extract Mode",o:["Agent extract (describe fields)","Structured JSONPath (JSON input only)"],v:"Agent extract (describe fields)"},
{t:"ta",l:"Fields to Extract",v:"",h:50,p:"Filing date, Case number, Parties, Deadline dates"},
{t:"s",l:"Extract Agent Mode",o:["Task default","Named agent","Spawn new"],v:"Task default"},
{t:"s",l:"Extract Agent",o:["Elnor","Nova","Prism","Atlas","Sage"],v:"Elnor"},
{t:"i",l:"JSONPath Expression",v:"",p:"$.decision.date (structured mode only)"},
{t:"hd",l:"Format"},
{t:"s",l:"Output Format",o:["DOCX","PDF","MD","TXT"],v:"MD"},
{t:"hd",l:"Redact"},
{t:"ta",l:"Redact Patterns",v:"",h:40,p:"Regex or exact strings, one per line. Matched text → [REDACTED]"},
{t:"n",v:"Examples: \\d{3}-\\d{2}-\\d{4} (SSN), John Henderson (name), \\d{3}-\\d{3}-\\d{4} (phone)"},
{t:"hd",l:"Split (no LLM — deterministic)"},
{t:"s",l:"Split Mode",o:["Text delimiter (split text by markers)","File pattern (route files by name)"],v:"Text delimiter (split text by markers)"},
{t:"i",l:"Delimiters (for text)",v:"",p:"===ANALYSIS===, ===PROMPT=== (each becomes a named output port)"},
{t:"n",v:"Text split: sections between delimiters route to separate output ports named after the delimiter. File split: FileRefs route by filename pattern."},
{t:"tags",l:"File Patterns (for file split)",v:[]},
{t:"n",v:"Add name patterns. Each becomes a named output port. FileRefs with matching filenames route to that port. Unmatched files go to Data Out."}],
"step.user_review_gate":[
{t:"ta",l:"Review Instructions",v:"",h:70,p:"What should the reviewer evaluate? Shown alongside the output."},
{t:"hd",l:"Notifications"},
{t:"c",l:"Dashboard notification",ck:true},
{t:"c",l:"Discord notification",ck:false},
{t:"c",l:"Email notification",ck:true},
{t:"i",l:"Email To",v:"will@schall.com",p:"who gets the review email"},
{t:"i",l:"Email Subject",v:"Review needed: {{task_name}} — {{step_name}}",p:"customizable subject"},
{t:"c",l:"Include full output in email",ck:true},
{t:"c",l:"Include review instructions in email",ck:true},
{t:"c",l:"Accept revision instructions via email reply",ck:true},
{t:"n",v:"Reply to the notification email to send revision instructions. Reply with APPROVE or REJECT to take those actions directly."},
{t:"hd",l:"On Rejection"},
{t:"s",l:"Reject Action",o:["Pause task","Redirect to module","Abort task"],v:"Pause task"},
{t:"i",l:"Redirect Target",v:"",p:"module ID (if redirect)"},
{t:"i",l:"Max Redirects",v:"3",p:"safety cap for redirect loops"},
{t:"hd",l:"On Revision"},
{t:"n",v:"Revision instructions and file attachments are submitted via the Review Card tab. Your text + attached files route through revision_out to the receiving step."},
{t:"i",l:"Max Revisions",v:"3",p:"after this, forces final approve/reject"},
{t:"s",l:"Revision Delivery",o:["Route through revision_out (wire to target)","Auto-send to previous step"],v:"Route through revision_out (wire to target)"}],
"step.agent_review_gate":[
{t:"hd",l:"Reviewing Agent"},
{t:"s",l:"Agent Mode",o:["Task default","Named agent","Spawn new"],v:"Task default"},
{t:"row",items:[{t:"s",l:"Agent",o:["Elnor","Nova","Prism","Atlas","Sage"],v:"Nova"},{t:"s",l:"Model",o:["(agent default)","claude-opus-4-6","claude-sonnet-4-6"],v:"(agent default)"}]},
{t:"row",items:[{t:"s",l:"Think Level",o:["(default)","off","minimal","low","medium","high","xhigh"],v:"medium"},{t:"i",l:"Cost Limit ($)",v:"5.00"}]},
{t:"ta",l:"Review Criteria / Instructions",v:"",h:80,p:"What should the reviewer check? How should it evaluate the content?"},
{t:"hd",l:"Decision Mode"},
{t:"s",l:"How the agent decides pass/fail",o:["LLM judgment (default)","Keyword match","Always pass (log only)"],v:"LLM judgment (default)"},
{t:"i",l:"Pass Threshold",v:"",p:"e.g. no critical issues, all citations verified"},
{t:"n",v:"LLM judgment: agent makes holistic assessment. Keyword match: scan response for pass/fail keywords (cheaper, deterministic). Always pass: agent runs analysis but always routes to approved_out."},
{t:"tags",l:"Pass Keywords",v:[]},
{t:"tags",l:"Fail Keywords (checked first)",v:[]},
{t:"hd",l:"On Rejection"},
{t:"ta",l:"Feedback agent should provide on rejection",v:"",h:50,p:"Explain what failed, suggest specific revisions, produce a redlined version if applicable"},
{t:"n",v:"The reviewing agent's feedback + any revised documents flow through the failed_out port. Wire failed_out to the originating step's instruction_in to send revisions back, or to a notification, human review, or any handler."},
{t:"s",l:"If Data Exceeds Context Budget",o:["Fail (safest)","Truncate with warning"],v:"Fail (safest)"},
{t:"hd",l:"Retry (optional)"},
{t:"row",items:[{t:"i",l:"Max Retries",v:"0"},{t:"i",l:"Backoff (min)",v:"1"}]}],
"step.panel":[
{t:"s",l:"Panel Mode",o:["Create new panel","Use existing panel","Paste panel reference ID"],v:"Use existing panel"},
{t:"i",l:"Panel ID / Reference",v:"",p:"select or paste panel ID"},
{t:"n",v:"'Create new' opens the panel setup page — configure agents, rounds, consensus there. Saves and links back automatically. 'Use existing' shows a list of your panels."},
{t:"hd",l:"Output Timing"},
{t:"s",l:"Output On",o:["Panel complete (final result only)","Each round (fires per round)","Both (round_out + complete_out ports)"],v:"Panel complete (final result only)"},
{t:"n",v:"'Each round' fires data_out after every round — downstream modules run once per round. 'Both' gives two output ports: Round Output and Final Result. Wire each where needed."},
{t:"ta",l:"Additional Instructions",v:"",h:50,p:"Context or instructions beyond what the panel preset defines"},
{t:"i",l:"Cost Cap ($)",v:"5.00"}],
"step.pass_through":[{t:"n",v:"Forwards input data unchanged. Use as a waypoint or annotation node."}],
"step.red_team":[
{t:"s",l:"Red Team Preset",o:["Select preset...","truth_seeking_standard","high_stakes_review","exploratory_broad"],v:"Select preset..."},
{t:"i",l:"Or Paste Preset ID",v:"",p:"preset reference ID"},
{t:"n",v:"Presets define the full red team configuration — reviewers, rounds, finding limits, evidence mode, adjudication policy. Configure presets in the Red Team settings page."},
{t:"ta",l:"Additional Instructions / Context",v:"",h:50,p:"Optional: focus areas, special concerns, context beyond the preset"},
{t:"hd",l:"Documents"},
{t:"n",v:"Drag additional reference files from Browser or Finder."},
{t:"tags",l:"Attached",v:[]},
{t:"i",l:"Cost Cap ($)",v:"",p:"blank = preset default"}],
"step.coding":[
{t:"ta",l:"Instruction",v:"",h:80,p:"What should the coding agent(s) build, fix, or modify?"},
{t:"hd",l:"Agents"},
{t:"n",v:"Assign 1-5 ACP-connected coding agents. Each agent gets the instruction + its role-specific override. Drag to reorder (affects sequential/review_chain execution order)."},
{t:"row",items:[{t:"i",l:"Role",v:"builder",p:"builder, reviewer, tester..."},{t:"s",l:"Agent",o:["Task Default","claude-code","codex","gemini-code","+ New Agent"],v:"claude-code"}]},
{t:"s",l:"ACP Profile Override",o:["(use module default)","dev-sandbox","prod-readonly","full-access"],v:"(use module default)"},
{t:"ta",l:"Role Instructions",v:"",h:40,p:"Role-specific instruction appended to shared instruction"},
{t:"btn",l:"+ Add Agent",v:"add_agent"},
{t:"hd",l:"Workspace"},
{t:"s",l:"ACP Profile",o:["Select profile...","dev-sandbox","prod-readonly","full-access"],v:"Select profile..."},
{t:"i",l:"Working Directory",v:"",p:"/path/to/workspace"},
{t:"n",v:"Reference Roots — additional read-only paths for spec docs, reference code."},
{t:"tags",l:"Reference Roots",v:[]},
{t:"hd",l:"Session"},
{t:"row",items:[{t:"s",l:"Style",o:["Persistent","One-shot"],v:"Persistent"},{t:"s",l:"Completion",o:["Agent Signals","Tests Pass","Iteration Limit"],v:"Agent Signals"}]},
{t:"i",l:"Test Command",v:"",p:"npm test, pytest, etc. (visible when Tests Pass)"},
{t:"i",l:"Max Iterations",v:"",p:"blank = no limit"},
{t:"hd",l:"Limits"},
{t:"row",items:[{t:"i",l:"Cost Limit ($)",v:"",p:"blank = task-level"},{t:"i",l:"Time Limit (min)",v:"",p:"blank = ACP default"}]},
{t:"hd",l:"Output Capture"},
{t:"c",l:"Capture Diff",ck:true},
{t:"c",l:"Capture Test Results",ck:true}],
"system.experiment":[
{t:"n",v:"Run 2-4 variant configurations against the same input. Compare models, agents, instructions, or any combination. Wire the target cable from an existing module to auto-copy its config as the baseline."},
{t:"hd",l:"Target Module"},
{t:"n",v:"Wire the target cable FROM an existing module to auto-populate. Or configure manually below."},
{t:"s",l:"Target Type",o:["(auto from target cable)","step.agent_task","step.transform","step.coding"],v:"(auto from target cable)"},
{t:"hd",l:"Variants"},
{t:"n",v:"Configure 2-4 variants. Variant A is the baseline (matches target module). Use the ↗ Expand button above for side-by-side variant editing."},
{t:"s",l:"Variant A Label",o:["Variant A (Baseline)"],v:"Variant A (Baseline)"},
{t:"s",l:"Variant B Agent",o:["(same as A)","Elnor","Nova","Atlas","Sage","+ Configure..."],v:"Nova"},
{t:"i",l:"Variant B Label",v:"Nova",p:"Display label"},
{t:"c",l:"Variant B — same instruction as A",ck:true},
{t:"hd",l:"Run Mode"},
{t:"s",l:"Execution",o:["Parallel (all at once)","Sequential (one at a time)"],v:"Parallel (all at once)"},
{t:"btn",l:"↗ Open Expanded View"}],
"step.judge":[
{t:"n",v:"Score outputs on configurable dimensions. Wire target_in from any module. Wire comparison_in from an Experiment module for A/B comparison. Wire evidence_in from red team or review modules."},
{t:"hd",l:"Scoring Preset"},
{t:"s",l:"Load Preset",o:["(custom)","Save current...","Manage presets..."],v:"(custom)"},
{t:"hd",l:"Dimensions"},
{t:"i",l:"Dimension 1 Name",v:"Accuracy",p:"e.g., Accuracy, Completeness, Clarity"},
{t:"s",l:"Method",o:["Claim Verification","Checklist","Consistency Check","Subjective Analysis"],v:"Claim Verification"},
{t:"ta",l:"Criteria",v:"List every factual assertion. Verify each against evidence sources. Count supported vs contradicted.",h:50},
{t:"i",l:"Dimension 2 Name",v:"Completeness",p:""},
{t:"s",l:"Method",o:["Claim Verification","Checklist","Consistency Check","Subjective Analysis"],v:"Checklist"},
{t:"ta",l:"Criteria",v:"Required: scope definition, methodology, key findings, recommendations, limitations",h:40},
{t:"i",l:"Dimension 3 Name",v:"Overall Quality",p:""},
{t:"s",l:"Method",o:["Claim Verification","Checklist","Consistency Check","Subjective Analysis"],v:"Subjective Analysis"},
{t:"ta",l:"Criteria",v:"Holistic assessment considering all evidence. Identify specific strengths and weaknesses.",h:40},
{t:"btn",l:"+ Add Dimension"},
{t:"hd",l:"Judge Agent"},
{t:"s",l:"Agent",o:["Task default","Elnor","Nova","Prism","Atlas","Sage"],v:"Elnor"},
{t:"row",items:[{t:"s",l:"Model",o:["(agent default)","claude-opus-4-6","claude-sonnet-4-6"],v:"claude-opus-4-6"},{t:"s",l:"Think Level",o:["(default)","high","xhigh"],v:"high"}]},
{t:"hd",l:"DSPy Optimization"},
{t:"c",l:"Enable prompt optimization",ck:false},
{t:"n",v:"When enabled, the judge's scoring dimensions become the DSPy metric function. After scoring, click 'Suggest Improvements' to optimize the target module's instruction."},
{t:"s",l:"Optimizer",o:["GEPA (recommended)","MIPROv2 (lighter)"],v:"GEPA (recommended)"},
{t:"s",l:"Reflection Agent",o:["claude-opus-4-6","claude-sonnet-4-6"],v:"claude-opus-4-6"},
{t:"s",l:"Intensity",o:["Light (~$2)","Medium (~$5)","Heavy (~$15)"],v:"Medium (~$5)"},
{t:"i",l:"Min Examples Required",v:"10",p:"10"},
{t:"btn",l:"↗ Open Expanded View"}],
"utility.junction":[
{t:"s",l:"Mode",o:["AND — Wait for all inputs","OR — Route based on options below","MERGE — Combine into one document"],v:"AND — Wait for all inputs"},
{t:"n",v:"MERGE joins all inputs into one text document with section headers per source. No AI — for intelligent synthesis, wire output to an Agent Task."},
{t:"i",l:"Section Header Format",v:"## {module_name}",p:"use {module_name} variable"},
{t:"row",items:[{t:"i",l:"Timeout (min)",v:"",p:"none"},{t:"s",l:"On Timeout",o:["Fire with partial data","Error"],v:"Fire with partial data"}]},
{t:"hd",l:"OR Mode Options"},
{t:"s",l:"When additional inputs arrive",o:["Ignore (first input only)","Fire again for each"],v:"Ignore (first input only)"},
{t:"n",v:"Output is always text/markdown. Mixed input types (PDF text, agent output, JSON) are converted to text. For specific file formats, wire output to a Transform module."}],
"utility.switch":[
{t:"hd",l:"Routing Conditions"},
{t:"n",v:"First match wins. Each condition routes to its own output port. Select a condition to edit its fields below."},
{t:"s",l:"Active Condition",o:["Condition 1 → Route A","Condition 2 → Route B","Condition 3 → Route C","Condition 4 → Route D","+ Add Condition"],v:"Condition 1 → Route A"},
{t:"hd",l:"Condition Settings (for selected condition)"},
{t:"s",l:"Type",o:["Contains (boolean)","Does not contain","Pattern match (regex)","Character count >","Character count <","Equals exactly","Agent evaluates"],v:"Contains (boolean)"},
{t:"i",l:"Value / Search Term",v:"",p:"term AND term, \"exact phrase\", NOT term"},
{t:"n",v:"Contains / Does not contain: supports boolean syntax — AND, OR, NOT, \"exact phrase\", (groups). Character count: enter a number — total characters in the primary content. Equals exactly: entire content must be this string and nothing else (e.g., agent outputs 'APPROVED')."},
{t:"hd",l:"Agent Eval (when type = Agent evaluates)"},
{t:"s",l:"Agent Mode",o:["Task default","Named agent","Spawn new"],v:"Task default"},
{t:"s",l:"Eval Agent",o:["Elnor","Nova","Prism","Atlas","Sage"],v:"Elnor"},
{t:"s",l:"Think Level",o:["(default)","off","minimal","low","medium","high","xhigh"],v:"(default)"},
{t:"ta",l:"Agent Instruction",v:"",h:60,p:"Read the input and decide: does this indicate the motion was granted? Answer YES or NO."},
{t:"n",v:"Per-condition: each condition can use a different agent and instruction. Agent reads input, returns yes/no judgment. Adds cost per evaluation."},
{t:"hd",l:"Default"},
{t:"s",l:"If no conditions match",o:["Error (task fails)","Route to Default output"],v:"Route to Default output"},
{t:"n",v:"Default is a separate output port. If nothing matches and no default is wired, the module errors."}],
"utility.delay":[
{t:"s",l:"Delay Mode",o:["Duration","Until specific time"],v:"Duration"},
{t:"hd",l:"Duration"},
{t:"row",items:[{t:"i",l:"Delay",v:"30"},{t:"s",l:"Unit",o:["minutes","hours","days"],v:"minutes"}]},
{t:"hd",l:"Until Time (when mode = Until specific time)"},
{t:"s",l:"Schedule Type",o:["Specific date & time","Day of week + time","Time only (every day)"],v:"Day of week + time"},
{t:"i",l:"Date",v:"",p:"2026-04-01 (for specific date)"},
{t:"row",items:[{t:"i",l:"Time",v:"09:00",p:"HH:MM"},{t:"s",l:"Timezone",o:["America/Los_Angeles","America/New_York","America/Chicago","UTC"],v:"America/Los_Angeles"}]},
{t:"n",v:"Days (select one or more):"},
{t:"tags",l:"Days",v:["Mon","Wed","Fri"]},
{t:"c",l:"Batch mode — collect all inputs during the delay window and release together as one bundle",ck:false}],
"utility.hold":[
{t:"s",l:"Hold Mode",o:["Until release signal","Until specific time","Until signal AND time (both required)","Until signal OR time (whichever first)"],v:"Until release signal"},
{t:"n",v:"'Signal AND time' = both must be met before release. Signal arrives early? Waits for time. Time passes? Waits for signal.\n'Signal OR time' = whichever comes first releases the data."},
{t:"hd",l:"Release Signal Options"},
{t:"i",l:"Max wait for release signal (min)",v:"",p:"blank = wait indefinitely"},
{t:"s",l:"If signal doesn't arrive in time",o:["Release data anyway","Error"],v:"Release data anyway"},
{t:"hd",l:"Scheduled Release (when mode includes time)"},
{t:"s",l:"Schedule Type",o:["Specific date & time","Day of week + time","Time only (every day)"],v:"Day of week + time"},
{t:"i",l:"Date",v:"",p:"2026-04-01 (for specific date)"},
{t:"row",items:[{t:"i",l:"Time",v:"09:00",p:"HH:MM"},{t:"s",l:"Timezone",o:["America/Los_Angeles","America/New_York","America/Chicago","UTC"],v:"America/Los_Angeles"}]},
{t:"n",v:"Days (select one or more):"},
{t:"tags",l:"Days",v:["Mon","Tue","Wed","Thu","Fri"]}],
"utility.loop":[
{t:"i",l:"Max Iterations",v:"5"},
{t:"s",l:"Stop Condition",o:["Fixed count","Agent judgment","External signal"],v:"Fixed count"},
{t:"ta",l:"Stop / Judgment Criteria",v:"",h:50,p:"Fixed count: enter a number. Agent judgment: describe what 'done' looks like. External: leave blank."},
{t:"hd",l:"Judgment Agent (when Stop = Agent judgment)"},
{t:"s",l:"Agent Mode",o:["Task default","Named agent","Spawn new"],v:"Task default"},
{t:"row",items:[{t:"s",l:"Agent",o:["Elnor","Nova","Prism","Atlas","Sage"],v:"Elnor"},{t:"s",l:"Model",o:["(agent default)","claude-opus-4-6","claude-sonnet-4-6"],v:"(agent default)"}]},
{t:"row",items:[{t:"s",l:"Think Level",o:["(default)","off","minimal","low","medium","high","xhigh"],v:"(default)"},{t:"i",l:"Cost Limit ($)",v:"2.00"}]},
{t:"i",l:"Loop Timeout (min)",v:"",p:"none"},
{t:"hd",l:"Loop Instruction"},
{t:"s",l:"Instructions for pass",o:["Pass 1","Pass 2","Pass 3","Pass 4","Pass 5"],v:"Pass 1"},
{t:"ta",l:"Agent instruction for this pass",v:"",h:70,p:"Analyze this filing for all CCP deadlines."},
{t:"n",v:"Select each pass from the dropdown and write its instruction. Blank passes inherit the previous pass's instruction. The system auto-prepends iteration context (e.g., 'This is pass 2 of 5. The content below is your prior output.'). Wire instruction_out → agent's instruction_in."},
{t:"c",l:"Include original input as reference on every pass",ck:false},
{t:"n",v:"When checked, each pass receives the original input (from pass 1) alongside the latest output — useful for comparing revisions against the source document."},
{t:"hd",l:"Error Handling"},
{t:"s",l:"On Iteration Error",o:["Stop loop","Skip to next iteration","Retry with delay"],v:"Stop loop"},
{t:"row",items:[{t:"s",l:"Retries",o:["1","2","3","4"],v:"2"},{t:"i",l:"Delay Between (min)",v:"1",p:"minutes"}]},
{t:"n",v:"Stop: terminates loop, fires error_out. Skip: discards failed result, tries next with last good data. Retry: retries N times with delay — all fail → stops loop, fires error_out."},
{t:"hd",l:"Wiring"},
{t:"n",v:"Wire loop_out → data_in on the module to repeat. Wire instruction_out → instruction_in on the same module. Wire that module's data_out → return_in. Each pass, the agent gets fresh data + your instructions + iteration context."},
{t:"n",v:"stop_in is a latch. Signal on stop_in = finish current iteration, then route return_in data to done_out (no more loops). Data on stop_in = that data goes to done_out immediately. Use with a Switch: route 'needs work' → return_in, route 'approved' signal → stop_in."}],
"utility.context_filter":[
{t:"hd",l:"How agent context works"},
{t:"n",v:"Each agent step receives three types of input:\n• Operative data — the previous step's output (what the agent works on)\n• Chain history — outputs from earlier steps, auto-accumulated\n• Explicit context — anything you wired to context_in ports\n\nThis filter controls the first two. Explicit context is never filtered — you wired it on purpose."},
{t:"hd",l:"Operative Data"},
{t:"c",l:"Keep operative data (the output from the previous step)",ck:true},
{t:"hd",l:"Chain History"},
{t:"c",l:"Keep chain history (outputs from earlier steps, auto-carried by CIL)",ck:false},
{t:"i",l:"Max chain history items",v:"",p:"blank = keep all"},
{t:"i",l:"Drop history older than N steps back",v:"",p:"blank = no limit"},
{t:"c",l:"Summarize older history to reference-only (instead of dropping)",ck:true},
{t:"n",v:"When checked, items beyond the kept window are compressed to title + one-line summary. When unchecked, they're dropped entirely. Overrides the environment-level chain history policy at this point in the chain."},
{t:"hd",l:"Filter by Specific Module"},
{t:"i",l:"Remove history from these modules",v:"",p:"module IDs, comma-separated"},
{t:"i",l:"Keep ONLY history from these modules",v:"",p:"module IDs, comma-separated"},
{t:"n",v:"Copy a module's ID from the bottom of its detail panel. 'Remove from' strips specific modules' outputs from the history. 'Keep only from' discards everything except those modules' outputs. Use one or the other, not both."}],
"utility.manual_switch":[
{t:"s",l:"Default Position",o:["A","B","C","D"],v:"A"},
{t:"n",v:"Which output is active when the task starts. Signals on switch_a/b/c/d flip the routing — they don't pass data through. Data on data_in goes to whichever output is currently active."},
{t:"hd",l:"How It Works"},
{t:"n",v:"Wire signals from other modules (e.g., Agent Review approved_out, Switch signal_out, Human Review) to switch_a/b/c/d to control which path data takes. Wire the content to data_in. The switch remembers its position until a new signal flips it."},
{t:"n",v:"Example: Agent Review approved_out → switch_b flips routing to B. Next data on data_in goes to out_b (the 'done' path) instead of out_a (the 'revise' path)."}],
"output.email":[
{t:"i",l:"From Account",v:"",p:"elnor@schall.com (type or select from settings)"},
{t:"n",v:"Which email account Elnor sends from. Leave blank for the default sending account."},
{t:"i",l:"To",v:"will@schall.com"},
{t:"row",items:[{t:"i",l:"CC",v:"",p:"cc@"},{t:"i",l:"BCC",v:"",p:"bcc@"}]},
{t:"i",l:"Subject",v:"{{task_name}} — {{step_name}}"},
{t:"hd",l:"Email Body — check what to include"},
{t:"n",v:"Check one or more. Custom message is always first in the body. If none checked, email has no body (subject + attachments only)."},
{t:"c",l:"Custom message (always appears first)",ck:false},
{t:"ta",l:"Custom Message",v:"",h:60,p:"Hi Will,\n\nPlease see the attached analysis.\n\nTask: {{task_name}} · {{date}}"},
{t:"n",v:"Supports {{task_name}}, {{step_name}}, {{date}}, {{output_summary}}. Exactly what you type is sent."},
{t:"c",l:"Include body input content (from data_in port)",ck:true},
{t:"c",l:"Agent composes body (agent writes or rewrites using instruction below)",ck:false},
{t:"n",v:"When 'Agent composes' is checked with other items, the agent sees all checked body items as context and produces the final body. Agent instructions from instruction_in port are always included when wired — no checkbox needed."},
{t:"s",l:"Agent Mode",o:["Task default","Named agent","Spawn new"],v:"Task default"},
{t:"row",items:[{t:"s",l:"Agent (for Named agent)",o:["Elnor","Nova","Prism","Atlas","Sage"],v:"Elnor"},{t:"s",l:"Think Level",o:["(default)","off","minimal","low","medium","high","xhigh"],v:"(default)"}]},
{t:"ta",l:"Instructions to Agent",v:"",h:50,p:"Write a professional summary. Keep it under 3 paragraphs."},
{t:"hd",l:"Attachments — check what to attach"},
{t:"c",l:"Attach files from attachment_in port (attached as-is, original format)",ck:false},
{t:"c",l:"Attach body input as file (converts data_in text to selected format)",ck:false},
{t:"s",l:"Attachment Format (for body-as-file)",o:["DOCX","PDF","MD","TXT"],v:"PDF"},
{t:"n",v:"Both can be checked — explicit file attachments AND a converted text attachment. All body/attachment content is auto-stripped of internal metadata."}],
"output.file":[
{t:"hd",l:"File Format"},
{t:"s",l:"Format",o:["Preserve original (no conversion)","DOCX","PDF","MD","TXT"],v:"Preserve original (no conversion)"},
{t:"n",v:"'Preserve original' keeps the file in whatever format it arrived (e.g., a .docx from an agent stays .docx). If input is text with no file format, falls back to MD."},
{t:"hd",l:"File Naming"},
{t:"s",l:"Name Source",o:["Custom template","Use original file name as base","Use name from specific module","Agent determines name"],v:"Custom template"},
{t:"i",l:"Filename Template (for Custom)",v:"",p:"{{task_name}}_{{step_name}}_{{date}}"},
{t:"i",l:"Module Ref (for Use name from module)",v:"",p:"paste module ref ID — traces FileRef provenance"},
{t:"n",v:"'Original name' = takes the file name from the input FileRef (after filtering out email signatures, inline images, vCards). If multiple files remain after filtering, falls back to template. 'From module' traces the FileRef provenance chain back to that module's output to find the original name."},
{t:"c",l:"Append date to file name",ck:false},
{t:"hd",l:"Agent Naming (when Name Source or Directory = Agent determines)"},
{t:"s",l:"Agent Mode",o:["Task default","Named agent","Spawn new"],v:"Task default"},
{t:"row",items:[{t:"s",l:"Agent",o:["Elnor","Nova","Prism","Atlas","Sage"],v:"Elnor"},{t:"s",l:"Think Level",o:["(default)","off","minimal","low","medium","high","xhigh"],v:"(default)"}]},
{t:"ta",l:"Naming Instructions",v:"",h:50,p:"Name the file based on the case number and filing type from the document content."},
{t:"n",v:"One agent handles both file naming and directory decisions. Naming instructions and directory instructions are both sent to this agent."},
{t:"hd",l:"Save Location"},
{t:"s",l:"Directory Source",o:["Fixed path","Agent determines"],v:"Fixed path"},
{t:"i",l:"Directory Path 📁",v:"",p:"click 📁 to browse or type path"},
{t:"ta",l:"Directory Instructions (for Agent determines)",v:"",h:50,p:"Save in the case folder matching the case number. Create a subfolder if it doesn't exist."},
{t:"i",l:"Top-Level Directory Constraint 📁",v:"",p:"agent can only create within this boundary"},
{t:"n",v:"For 'Agent determines': the agent chooses the folder based on your instructions, but cannot save outside the top-level directory. Security constraint."},
{t:"hd",l:"If File Already Exists (never overwrites)"},
{t:"s",l:"Versioning",o:["Add version number (V2, V3...)","Add date-time suffix","Error (don't save)"],v:"Add version number (V2, V3...)"},
{t:"n",v:"Existing files are NEVER overwritten. If a file already exists at the save path, the selected versioning rule applies automatically."}],
"output.chat":[
{t:"s",l:"Mode",o:["New chat","Existing chat"],v:"New chat"},
{t:"i",l:"Chat ID",v:"",p:"for existing chat"},
{t:"hd",l:"Agent"},
{t:"s",l:"Agent Mode",o:["Task default","Named agent","Spawn new"],v:"Task default"},
{t:"s",l:"Post As",o:["Elnor","Nova","Prism","Atlas","Sage"],v:"Elnor"},
{t:"hd",l:"Content Format"},
{t:"s",l:"How to deliver",o:["Inline message (text in chat)","Document attachment","Both (summary inline + full doc attached)"],v:"Inline message (text in chat)"},
{t:"n",v:"'Inline' = output appears as a chat message. 'Attachment' = output sent as a document file in the chat. 'Both' = short summary inline + full output attached."},
{t:"hd",l:"Chat Naming (for New Chat)"},
{t:"c",l:"Auto-name chat after task",ck:true},
{t:"i",l:"Name Template",v:"{{task_name}} — {{date}}",p:"{{task_name}} — {{date}}"},
{t:"n",v:"For repetitive tasks, each run creates a new chat with the run date appended."}],
"output.notify":[
{t:"tags",l:"Channels",v:["dashboard","email"]},
{t:"s",l:"Severity",o:["Info","Warning","Urgent"],v:"Info"},
{t:"ta",l:"Message",v:"",h:50,p:"{{task_name}} complete. {{output_summary}}"},
{t:"c",l:"Include full content",ck:false}],
"output.memory":[
{t:"s",l:"Memory Tier",o:["Active (relevant topics)","Core (always available)"],v:"Active (relevant topics)"},
{t:"tags",l:"Tags",v:["filing-analysis"]},
{t:"c",l:"Include source attribution",ck:true}],
"output.webhook":[
{t:"i",l:"Target URL",v:"",p:"https://api.example.com/"},
{t:"s",l:"Method",o:["POST","PUT","PATCH"],v:"POST"},
{t:"ta",l:"Headers (JSON)",v:"",h:40,p:'{"Authorization":"Bearer ..."}'},
{t:"ta",l:"Payload Template",v:"",h:50,p:'{"data":"{{data}}"}'}],
"output.imessage":[
{t:"i",l:"Recipient",v:"",p:"phone or contact name"},
{t:"ta",l:"Message Template",v:"",h:60,p:"{{task_name}}: {{output_summary}}"}],
"output.forum":[
{t:"s",l:"Mode",o:["New thread","Existing thread","New panel"],v:"New thread"},
{t:"i",l:"Thread/Panel ID",v:"",p:"for existing"},
{t:"i",l:"Title",v:"",p:"thread title"},
{t:"tags",l:"Tags",v:[]}],
"output.task":[
{t:"i",l:"Target Task ID",v:"",p:"task to send data to"},
{t:"c",l:"Include this step's data in the payload",ck:true},
{t:"hd",l:"Link"},
{t:"n",v:"Copy this module's ref ID and paste it into the receiving task's Task Event trigger 'Output Module Ref' field."}],
"source.file":[
{t:"i",l:"File Path / Note ID 📁",v:"",p:"click 📁 to browse or type path"},
{t:"ta",l:"Typed Content",v:"",h:50,p:"Enter text directly as source content. Can be used alongside or instead of a file."},
{t:"n",v:"If you provide both a file AND typed content, they are combined per the Combine mode below."},
{t:"ta",l:"About This Source",v:"",h:35,p:"Background info shown to the agent alongside this material. E.g., 'Henderson motion filed 3/20.'"},
{t:"ta",l:"Agent Instructions",v:"",h:35,p:"Tells the agent how to use this material. E.g., 'Use Section 4 for CCP deadline calculations.'"},
{t:"n",v:"'About This Source' describes what it is (shown as context). 'Agent Instructions' tells the agent what to do with it (framed in the instruction section of the prompt)."},
{t:"s",l:"Combine With Incoming",o:["Append","Prepend","Merge","Replace"],v:"Append"},
{t:"n",v:"Combine mode controls how this source's content merges with data arriving on data_in. Only affects this module's output — does not touch context routing on downstream modules. 'Replace' discards incoming data entirely."},
{t:"hd",l:"Global Availability"},
{t:"c",l:"Make available to ALL agent steps (via reference listing)",ck:false},
{t:"n",v:"When enabled, this source appears in every agent step's 'Available References' listing. Agents can fetch it via tool call — content is NOT auto-injected into prompts. Wire to context_in for explicit inclusion."},
{t:"i",l:"Reference Title (optional)",v:"",p:"defaults to module name"},
{t:"ta",l:"Reference Description (optional)",v:"",h:30,p:"defaults to 'About This Source' text above"}],
"source.bucket":[
{t:"i",l:"Bucket ID",v:"",p:"select or enter bucket"},
{t:"ta",l:"About This Source",v:"",h:40,p:"Background info shown to the agent. E.g., 'California CCP rules — use for deadline calculations.'"},
{t:"s",l:"Combine With Incoming",o:["Append","Prepend","Merge","Replace"],v:"Append"},
{t:"hd",l:"Global Availability"},
{t:"c",l:"Make available to ALL agent steps (via reference listing)",ck:false}],
"source.project":[
{t:"i",l:"Project ID",v:"",p:"select project"},
{t:"c",l:"Include project's context buckets",ck:true},
{t:"c",l:"Include project's background instructions",ck:true},
{t:"s",l:"Combine With Incoming",o:["Append","Prepend","Merge","Replace"],v:"Append"}],
"environment.config":[
{t:"hd",l:"Global Instructions"},
{t:"n",v:"Injected directly into every agent prompt. Keep concise — this text is in every step's context window."},
{t:"ta",l:"Objective",v:"",h:60,p:"High-level task goal. First line of every agent prompt."},
{t:"ta",l:"Behavioral Directives",v:"",h:50,p:"Cite CCP sections. Formal legal writing."},
{t:"i",l:"Agent Role",v:"",p:"Litigation paralegal for Henderson v. City of LA"},
{t:"hd",l:"Project Context"},
{t:"i",l:"Project 📁",v:"",p:"select project (inherits project buckets + instructions)"},
{t:"hd",l:"Context Buckets (DOC7)"},
{t:"tags",l:"Attached Buckets",v:["california_ccp_rules","henderson_case_docs"]},
{t:"n",v:"Buckets always available to all agent steps. Content accessed via tool calls, not injected into prompts."},
{t:"hd",l:"Reference Library"},
{t:"n",v:"NOT injected into prompts. Listed by name + description only. Agents fetch full content via tool calls."},
{t:"tags",l:"Reference Items 📁",v:["CCP_Rules.pdf","Henderson_docket.csv","calendaring_memo.md"]},
{t:"n",v:"Drag from Browser or Finder, or click +. Items stored as references — full content resolved at runtime."},
{t:"hd",l:"Chain History Policy"},
{t:"s",l:"Mode",o:["Progressive decay (default)","Keep last N steps only","Off (no chain history)"],v:"Progressive decay (default)"},
{t:"row",items:[{t:"i",l:"Full Fidelity Steps",v:"2",p:"2"},{t:"i",l:"Summarized Through Step",v:"5",p:"5"}]},
{t:"i",l:"Keep Last N Steps",v:"",p:"blank = use decay rules above"},
{t:"c",l:"Summarize older items to reference-only (instead of dropping)",ck:true},
{t:"c",l:"Show available buckets in agent prompts",ck:true},
{t:"i",l:"Max Pinned (📌) Modules",v:"5",p:"5"},
{t:"n",v:"Progressive decay: steps within 'Full Fidelity' range are uncompressed. Steps through 'Summarized' range get ~50% compression. Beyond that: listed-only (title + summary, retrievable via tool). 'Off' = no chain history — each module sees only wired inputs. Max Pinned controls how many modules can have 📌 Carry Forward enabled (max 10)."}],
"step.outcome_evaluator":[
{t:"hd",l:"Outcome / Quality Criteria"},
{t:"n",v:"Domain-agnostic evaluator. Can score, gate, route, emit repair instructions, create research/source needs, post to the Task Forum, and produce process-gap signals. It is not a hidden orchestrator."},
{t:"s",l:"Evaluator Mode",o:["Score only","Pass/fail","Pass/fail + repair","Gate + loop","Route by result","Compare candidates","Final acceptance"],v:"Pass/fail + repair"},
{t:"ta",l:"Desired Outcome",v:"Output fully satisfies the segment criteria and is ready for the next step.",h:48,p:"Describe what success looks like."},
{t:"ta",l:"Criteria",v:"• Required issues covered\n• Source/reference support adequate\n• User guidance followed\n• No unresolved blockers",h:72,p:"One criterion per line."},
{t:"tags",l:"Hard Blockers",v:["unsupported assertion","missing required issue","format unusable"]},
{t:"hd",l:"Methods"},
{t:"tags",l:"Check Methods",v:["LLM judge","source lookup","deterministic check","human review if needed"]},
{t:"s",l:"Evaluator Isolation",o:["Output + required sources only","Output + source workspace + board digest","Full audit context","Output only"],v:"Output + source workspace + board digest"},
{t:"hd",l:"Feedback Routing"},
{t:"c",l:"Emit structured feedback bundle",ck:true},
{t:"c",l:"Emit repair_instruction_out when criteria fail",ck:true},
{t:"c",l:"Emit research_need_out when more sources are needed",ck:true},
{t:"c",l:"Post blockers to Task Forum if wired",ck:true},
{t:"c",l:"Create process-gap signal for Task Agent on repeated failure",ck:true},
{t:"s",l:"Downstream Visibility",o:["None","Same segment only","Selected modules","All future modules"],v:"Same segment only"},
{t:"hd",l:"Correction Lifecycle"},
{t:"n",v:"Evaluator findings are evidence, not gospel. Findings may be proposed, accepted, user-approved, tool-verified, contested, superseded or expired."},
],
"step.source_research":[
{t:"hd",l:"Source / Research Goal"},
{t:"n",v:"Generic source-gathering module. Legal research is one configuration; this can also research financial filings, technical docs, restaurants, products, APIs, etc."},
{t:"ta",l:"Research Goal",v:"Find and verify sources needed to answer the open research needs.",h:55,p:"What should this module find, check, update, or verify?"},
{t:"tags",l:"Allowed Source Types",v:["document library","web/API","connector","manual upload"]},
{t:"tags",l:"Tools / Connectors",v:["DOC25 retrieval","CourtListener API","SEC/EDGAR","DOC73 library"]},
{t:"hd",l:"Source Workspace"},
{t:"s",l:"Documentation Level",o:["None","Lookup receipt only","Source references","Source notes","Full source cards for high-value sources","Full source workspace"],v:"Source notes"},
{t:"c",l:"Add material findings to shared Source Workspace",ck:true},
{t:"c",l:"Make updates available to future modules",ck:true},
{t:"c",l:"Emit ResearchAnswer / SourceUpdate outputs",ck:true},
{t:"hd",l:"Freshness / Verification"},
{t:"c",l:"Check freshness/currentness where relevant",ck:true},
{t:"c",l:"Verify references/quotes if requested",ck:true},
{t:"c",l:"Post unresolved needs to Task Forum if wired",ck:true},
],
"system.source_workspace":[
{t:"hd",l:"Shared Task Source Workspace"},
{t:"n",v:"Optional, task-scoped source/research substrate. It is a shared resource, not required for simple tasks. Modules can add source refs, notes, cards, research needs, verification receipts, and current guidance."},
{t:"s",l:"Default Documentation Level",o:["None","Lookup receipt only","Source references","Source notes","Full source cards for high-value sources","Full source workspace"],v:"Source notes"},
{t:"c",l:"Visible to user during run",ck:true},
{t:"c",l:"Available to future modules by listing/retrieval",ck:true},
{t:"c",l:"Promote final source workspace to library candidate",ck:false},
{t:"c",l:"Save compact summary/link to DOC72 after run",ck:true},
{t:"hd",l:"Availability"},
{t:"s",l:"Prompt Mode",o:["Available listing only","Compact summary","Full context when requested"],v:"Available listing only"},
{t:"n",v:"Source content is not dumped into every prompt. DOC24/CIL render a scoped listing/digest or retrieval tools depending on module config and budget."},
],
"system.task_forum":[
{t:"hd",l:"Task Forum / Run Board"},
{t:"n",v:"Module-shaped coordination surface. The graph remains primary. Tasks can run without it. Passive board telemetry exists for all runs; this active module enables configured posts, questions, digests and cross-module coordination."},
{t:"s",l:"Mode",o:["Passive board only","Structured forum","Moderated forum"],v:"Structured forum"},
{t:"s",l:"Moderator",o:["None","Deterministic router","Task Agent advisory","Domain moderator agent"],v:"Deterministic router"},
{t:"hd",l:"Participant Defaults"},
{t:"s",l:"Default Read",o:["None","Mentions only","Segment only","Relevant posts","Full board"],v:"Relevant posts"},
{t:"s",l:"Default Write",o:["None","Status only","Findings/artifacts","All structured posts"],v:"Findings/artifacts"},
{t:"c",l:"Generate role-specific board digests",ck:true},
{t:"c",l:"Classify free-text posts into structured post types",ck:true},
{t:"hd",l:"Participant Table Preview"},
{t:"n",v:"Source Research: read/write research needs + source updates.\nOutcome Evaluators: post findings + repair instructions.\nDraft/Revision modules: read board digest, post questions if allowed.\nTask Agent: advisory process-gap review only unless configured otherwise."},
],
"system.monitor":[
{t:"n",v:"Optional — basic notifications (task complete, task error, review gates) work without this module. Monitor adds extra channels, verbosity control, and custom error handling."},
{t:"hd",l:"Extra Notification Channels"},
{t:"tags",l:"Also notify via",v:["discord"]},
{t:"n",v:"Dashboard notifications are always on. Add channels here for system events beyond what individual modules already send."},
{t:"hd",l:"Activity Feed"},
{t:"s",l:"Verbosity",o:["Errors only","Milestones","All events"],v:"Milestones"},
{t:"n",v:"Controls what appears in the Run Inspector activity feed."},
{t:"hd",l:"Unhandled Error Policy"},
{t:"s",l:"When error_out fires but isn't wired",o:["Notify only (task fails)","Pause & notify (resumable)"],v:"Notify only (task fails)"},
{t:"n",v:"Without this module, unhandled errors fail the task with a dashboard notification. This lets you override that to pause (resumable) instead."}],
};
const universalTrigger = [
{t:"hd",l:"Trigger Options"},
{t:"c",l:"Delay before running",ck:false},
{t:"row",items:[{t:"i",l:"Delay",v:"",p:"0"},{t:"s",l:"Unit",o:["minutes","hours","days"],v:"minutes"}]},
{t:"c",l:"Keep listening (repeat)",ck:true},
{t:"c",l:"Cooldown between runs",ck:false},
{t:"row",items:[{t:"i",l:"Cooldown",v:"",p:"0"},{t:"s",l:"Unit",o:["minutes","hours"],v:"minutes"}]},
];
// ── CABLE FAMILIES ──
const cableFamilies = {
data:["#1e3a8a","#3b5fc0","#5b8af5","#88b0ff","#b8d4ff","#dbeaff","#2850b0"],
signal:["#92400e","#b87a08","#fbbf24","#fde68a","#d4a017","#eab020"],
error:["#991b1b","#f87171","#fca5a5"],
context:["#0e7490","#22d3ee","#a5f3fc"],
};
function getCableColor(cable,idx){const f=cableFamilies[cable.role]||cableFamilies.data;return f[idx%f.length];}
// ── PORT POPUP CHOICES ──
const outputChoices = {
"trigger.email":[{id:"data_out",label:"Full Email",desc:"Body + metadata + attachments (FileRefs)",color:c.accent},{id:"body_out",label:"Email Body",desc:"Body text only — no attachments, no metadata",color:c.cyan},{id:"attachment_out",label:"Attachments",desc:"Attachment FileRefs only",color:c.cyan},{id:"signal_out",label:"⚡ Signal",color:c.amber}],
"trigger.file_watcher":[{id:"data_out",label:"Data Out",desc:"File content + event metadata",color:c.accent},{id:"file_only_out",label:"File Only",desc:"FileRef to watched file — no metadata",color:c.cyan},{id:"signal_out",label:"⚡ Signal",color:c.amber}],
"trigger.discord":[{id:"data_out",label:"Data Out",color:c.accent},{id:"attachment_out",label:"Attachments",desc:"Message attachments (images, files)",color:c.cyan},{id:"signal_out",label:"⚡ Signal",color:c.amber}],
"trigger.webhook":[{id:"data_out",label:"Data Out",desc:"Parsed JSON payload",color:c.accent},{id:"signal_out",label:"⚡ Signal",color:c.amber}],
"trigger.forum_panel":[{id:"data_out",label:"Data Out",color:c.accent},{id:"signal_out",label:"⚡ Signal",color:c.amber}],
"trigger.task_complete":[{id:"data_out",label:"Data Out",color:c.accent},{id:"file_only_out",label:"Files Only",desc:"FileRefs from the completed task's output",color:c.cyan},{id:"signal_out",label:"⚡ Signal",color:c.amber}],
"step.agent_task":[{id:"data_out",label:"Data Out",desc:"Full output — text, file refs, chain history",color:c.accent},{id:"file_only_out",label:"File Only",desc:"FileRef(s) from created/edited files — no text, no history",color:c.cyan},{id:"signal_out",label:"⚡ Signal",color:c.amber},{id:"error_out",label:"⚠ Error",color:c.red}],
"step.transform":[{id:"data_out",label:"Data Out",color:c.accent},{id:"file_only_out",label:"File Only",desc:"Converted file output",color:c.cyan},{id:"signal_out",label:"⚡ Signal",color:c.amber},{id:"error_out",label:"⚠ Error",desc:"LLM or conversion failure",color:c.red}],
"step.red_team":[{id:"data_out",label:"Data Out",color:c.accent},{id:"file_only_out",label:"File Only",desc:"Annotated document file",color:c.cyan},{id:"signal_out",label:"⚡ Signal",color:c.amber},{id:"error_out",label:"⚠ Error",color:c.red}],
"step.coding":[{id:"data_out",label:"Summary",desc:"Session summary — what was done, decisions, final state",color:c.accent},{id:"diff_out",label:"Diff",desc:"Structured changeset — files created/modified/deleted",color:c.cyan},{id:"test_results_out",label:"Test Results",desc:"Pass/fail with stdout/stderr",color:c.green},{id:"files_out",label:"Files",desc:"FileRefs to workspace files changed",color:c.amber},{id:"file_only_out",label:"Files Only",desc:"Same FileRefs in ContextBundle format",color:c.dim},{id:"signal_out",label:"⚡ Signal",color:c.amber},{id:"receipt_out",label:"Receipt",desc:"Session metadata — keys, duration, cost",color:c.dim},{id:"error_out",label:"⚠ Error",color:c.red}],
"system.experiment":[{id:"variant_a_out",label:"Variant A",desc:"Baseline result",color:"#5b8af5"},{id:"variant_b_out",label:"Variant B",desc:"Variant B result",color:c.amber},{id:"variant_c_out",label:"Variant C",desc:"Variant C result (if configured)",color:c.purple},{id:"comparison_out",label:"⚖ Comparison",desc:"All variants bundled for Judge auto-config",color:c.cyan},{id:"signal_out",label:"⚡ Signal",color:c.amber},{id:"error_out",label:"⚠ Error",color:c.red}],
"step.judge":[{id:"scores_out",label:"Scores",desc:"Structured dimension scores with audit trail",color:c.green},{id:"analysis_out",label:"Analysis",desc:"Qualitative assessment text",color:c.cyan},{id:"recommendation_out",label:"Recommendation",desc:"Which variant is stronger and why",color:c.accent},{id:"optimization_out",label:"DSPy Candidate",desc:"Optimized prompt body (when DSPy enabled)",color:c.purple},{id:"signal_out",label:"⚡ Signal",color:c.amber},{id:"error_out",label:"⚠ Error",color:c.red}],
"step.outcome_evaluator":[{id:"approved_out",label:"Satisfied / Approved",desc:"Outcome criteria satisfied; continue.",color:c.green},{id:"needs_revision_out",label:"Needs Revision",desc:"Candidate failed criteria; route repair.",color:c.red},{id:"repair_instruction_out",label:"Repair Instructions",desc:"Structured changes needed to improve output.",color:c.purple},{id:"research_need_out",label:"Source / Research Need",desc:"Open source or information need.",color:c.amber},{id:"feedback_bundle_out",label:"Feedback Bundle",desc:"Findings + evidence + decision + routing recommendation.",color:c.cyan},{id:"process_gap_out",label:"Process Gap",desc:"Signal for Task Agent/task assessment when graph cannot handle feedback.",color:c.orange},{id:"signal_out",label:"⚡ Signal",color:c.amber},{id:"error_out",label:"⚠ Error",color:c.red}],
"step.source_research":[{id:"source_update_out",label:"Source Update",desc:"Source refs, notes, or cards added to workspace.",color:c.green},{id:"research_answer_out",label:"Research Answer",desc:"Answer to a ResearchNeed or assistance request.",color:c.accent},{id:"research_need_out",label:"Follow-up Need",desc:"New or unresolved research/source need.",color:c.amber},{id:"verification_out",label:"Verification Result",desc:"Reference, quote, freshness, or source check.",color:c.cyan},{id:"signal_out",label:"⚡ Signal",color:c.amber},{id:"error_out",label:"⚠ Error",color:c.red}],
"system.source_workspace":[{id:"workspace_snapshot_out",label:"Workspace Snapshot",desc:"Current source workspace summary for modules/evaluators.",color:c.green},{id:"source_update_out",label:"Source Update",color:c.cyan},{id:"open_needs_out",label:"Open Needs",color:c.amber},{id:"accepted_guidance_out",label:"Run Guidance",color:c.purple},{id:"signal_out",label:"⚡ Signal",color:c.amber}],
"system.task_forum":[{id:"board_digest_out",label:"Board Digest",desc:"Scoped role-specific summary of board/forum activity.",color:c.accent},{id:"post_out",label:"Post",desc:"Structured board/forum post.",color:c.cyan},{id:"assistance_request_out",label:"Assistance Request",desc:"Typed request to a module/source/human/forum target.",color:c.amber},{id:"repair_broadcast_out",label:"Repair Broadcast",desc:"Current repair/correction guidance for configured participants.",color:c.purple},{id:"signal_out",label:"⚡ Signal",color:c.amber},{id:"error_out",label:"⚠ Error",color:c.red}],
"step.panel":[{id:"complete_out",label:"Final Result",color:c.green},{id:"round_out",label:"Round Output",color:c.cyan},{id:"individual_out",label:"Individual Responses",color:c.purple},{id:"signal_out",label:"⚡ Signal",color:c.amber},{id:"error_out",label:"⚠ Error",color:c.red}],
"step.user_review_gate":[{id:"approved_out",label:"If Approved",color:c.green},{id:"rejected_out",label:"If Rejected",color:c.red},{id:"revision_out",label:"If Revision",color:c.amber},{id:"signal_out",label:"⚡ Signal",color:c.amber}],
"step.agent_review_gate":[{id:"approved_out",label:"If Passed",color:c.green},{id:"failed_out",label:"If Failed",color:c.red},{id:"signal_out",label:"⚡ Signal",color:c.amber},{id:"error_out",label:"⚠ Error",desc:"Agent failed to produce review",color:c.red}],
"utility.loop":[{id:"loop_out",label:"Loop Out",desc:"Data to the module that repeats",color:c.cyan},{id:"instruction_out",label:"Loop Instruction",desc:"Dynamic instruction for each pass",color:c.purple},{id:"done_out",label:"Done Out",desc:"Final result after last iteration",color:c.green},{id:"signal_out",label:"⚡ Signal",color:c.amber},{id:"error_out",label:"⚠ Error",color:c.red}],
"utility.switch":[{id:"out_a",label:"Route A"},{id:"out_b",label:"Route B"},{id:"out_c",label:"Route C"},{id:"out_d",label:"Route D"},{id:"default_out",label:"Default (no match)"},{id:"signal_any",label:"⚡ Signal: Any route decided",color:c.amber},{id:"signal_a",label:"⚡ Signal: Route A decided",color:c.amber},{id:"signal_b",label:"⚡ Signal: Route B decided",color:c.amber},{id:"signal_c",label:"⚡ Signal: Route C decided",color:c.amber},{id:"signal_d",label:"⚡ Signal: Route D decided",color:c.amber}],
"utility.manual_switch":[{id:"out_a",label:"Output A"},{id:"out_b",label:"Output B"},{id:"out_c",label:"Output C"},{id:"out_d",label:"Output D"},{id:"signal_out",label:"⚡ Signal",color:c.amber}],
"trigger.manual":[{id:"data_out",label:"Data Out",color:c.accent},{id:"signal_out",label:"⚡ Signal",color:c.amber}],
"trigger.schedule":[{id:"data_out",label:"Data Out",color:c.accent},{id:"signal_out",label:"⚡ Signal",color:c.amber}],
"step.pass_through":[{id:"data_out",label:"Data Out",color:c.accent},{id:"signal_out",label:"⚡ Signal",color:c.amber}],
"utility.junction":[{id:"data_out",label:"Data Out",color:c.accent},{id:"signal_out",label:"⚡ Signal",color:c.amber},{id:"error_out",label:"⚠ Error",color:c.red}],
"utility.delay":[{id:"data_out",label:"Data Out",color:c.accent},{id:"signal_out",label:"⚡ Signal",color:c.amber},{id:"error_out",label:"⚠ Error",desc:"Invalid timezone, config error",color:c.red}],
"utility.hold":[{id:"data_out",label:"Data Out",color:c.accent},{id:"signal_out",label:"⚡ Signal",color:c.amber},{id:"error_out",label:"⚠ Error",desc:"Timeout with error action",color:c.red}],
"utility.context_filter":[{id:"data_out",label:"Data Out",color:c.accent},{id:"signal_out",label:"⚡ Signal",color:c.amber}],
"output.email":[{id:"data_out",label:"Pass-through",desc:"Original data_in forwarded",color:c.accent},{id:"receipt_out",label:"Receipt",desc:"Delivery confirmation",color:c.green},{id:"signal_out",label:"⚡ Signal",color:c.amber},{id:"error_out",label:"⚠ Error",color:c.red}],
"output.file":[{id:"data_out",label:"Pass-through",color:c.accent},{id:"receipt_out",label:"Receipt",desc:"Save record",color:c.green},{id:"signal_out",label:"⚡ Signal",color:c.amber},{id:"error_out",label:"⚠ Error",color:c.red}],
"output.chat":[{id:"data_out",label:"Pass-through",color:c.accent},{id:"receipt_out",label:"Receipt",color:c.green},{id:"signal_out",label:"⚡ Signal",color:c.amber},{id:"error_out",label:"⚠ Error",color:c.red}],
"output.notify":[{id:"data_out",label:"Pass-through",color:c.accent},{id:"receipt_out",label:"Receipt",color:c.green},{id:"signal_out",label:"⚡ Signal",color:c.amber},{id:"error_out",label:"⚠ Error",color:c.red}],
"output.memory":[{id:"data_out",label:"Pass-through",color:c.accent},{id:"receipt_out",label:"Receipt",color:c.green},{id:"signal_out",label:"⚡ Signal",color:c.amber}],
"output.webhook":[{id:"data_out",label:"Pass-through",color:c.accent},{id:"receipt_out",label:"Receipt",desc:"Response payload",color:c.green},{id:"signal_out",label:"⚡ Signal",color:c.amber},{id:"error_out",label:"⚠ Error",color:c.red}],
"output.imessage":[{id:"data_out",label:"Pass-through",color:c.accent},{id:"receipt_out",label:"Receipt",color:c.green},{id:"signal_out",label:"⚡ Signal",color:c.amber},{id:"error_out",label:"⚠ Error",color:c.red}],
"output.forum":[{id:"data_out",label:"Pass-through",color:c.accent},{id:"receipt_out",label:"Receipt",color:c.green},{id:"signal_out",label:"⚡ Signal",color:c.amber},{id:"error_out",label:"⚠ Error",color:c.red}],
"output.task":[{id:"data_out",label:"Pass-through",color:c.accent},{id:"receipt_out",label:"Receipt",desc:"Envelope confirmation",color:c.green},{id:"signal_out",label:"⚡ Signal",color:c.amber},{id:"error_out",label:"⚠ Error",color:c.red}],
"source.file":[{id:"data_out",label:"Data Out",color:c.accent},{id:"signal_out",label:"⚡ Signal",color:c.amber},{id:"error_out",label:"⚠ Error",desc:"File not found, permission denied",color:c.red}],
"source.bucket":[{id:"data_out",label:"Data Out",color:c.accent},{id:"signal_out",label:"⚡ Signal",color:c.amber},{id:"error_out",label:"⚠ Error",desc:"Bucket unavailable",color:c.red}],
"source.project":[{id:"data_out",label:"Data Out",color:c.accent},{id:"signal_out",label:"⚡ Signal",color:c.amber},{id:"error_out",label:"⚠ Error",color:c.red}],
};
const loopInputChoices=[
{id:"data_in",label:"Initial Data",desc:"First iteration data",color:c.accent},
{id:"return_in",label:"Continue Looping",desc:"Output from looped module — loop iterates again (unless stop latched)",color:c.cyan},
{id:"stop_in",label:"Stop Looping (latch)",desc:"Signal = finish current pass, then done. Data = done immediately.",color:c.amber},
];
const holdInputChoices=[
{id:"data_in",label:"Data to Hold",desc:"Content to hold until released",color:c.accent},
{id:"release_in",label:"Release Signal",desc:"When this fires, held data passes through",color:c.amber},
];
const manualSwitchInputChoices=[
{id:"data_in",label:"Data to Route",desc:"Content routed to whichever output is currently active",color:c.accent},
{id:"switch_a",label:"Switch to A",desc:"Flip routing to output A (signal only — no data passes through)",color:c.amber},
{id:"switch_b",label:"Switch to B",desc:"Flip routing to output B",color:c.amber},
{id:"switch_c",label:"Switch to C",desc:"Flip routing to output C",color:c.amber},
{id:"switch_d",label:"Switch to D",desc:"Flip routing to output D",color:c.amber},
];
const outcomeEvaluatorInputChoices=[
{id:"candidate_in",label:"Candidate / Work Product",desc:"Artifact, segment output, draft, source workspace, or other item being evaluated.",color:c.accent},
{id:"outcome_spec_in",label:"Outcome Criteria",desc:"OutcomeSpec / saved criteria / evaluator preset that defines success.",color:c.green},
{id:"evidence_in",label:"Evidence / Sources",desc:"Source workspace snapshot, prior outputs, board digest, examples, or verification results.",color:c.amber},
{id:"feedback_in",label:"Prior Feedback",desc:"Earlier evaluator feedback or human comments to consider.",color:c.purple},
];
const sourceResearchInputChoices=[
{id:"research_goal_in",label:"Research Goal",desc:"Question, claim, issue, or source need to investigate.",color:c.accent},
{id:"source_workspace_in",label:"Source Workspace",desc:"Shared task source/research workspace to read/update.",color:c.green},
{id:"context_in",label:"Background Context",desc:"Task blueprint, board digest, matter context, or prior research.",color:c.cyan},
{id:"feedback_in",label:"Evaluator Feedback",desc:"Findings or repair instructions that created this research need.",color:c.purple},
];
const sourceWorkspaceInputChoices=[
{id:"source_update_in",label:"Source Update",desc:"New references, notes, verification results, or research answers.",color:c.green},
{id:"research_need_in",label:"Research Need",desc:"Open question or missing-source request from a module/evaluator/user.",color:c.amber},
{id:"accepted_guidance_in",label:"Run Guidance",desc:"Current correction, warning, or constraint that should follow the run.",color:c.purple},
];
const taskForumInputChoices=[
{id:"post_in",label:"Board / Forum Post",desc:"Status update, comment, question, artifact reference, or structured post.",color:c.accent},
{id:"evaluation_result_in",label:"Evaluator Result",desc:"Findings, scores, gate decision, or feedback bundle to show on the board.",color:c.green},
{id:"repair_instruction_in",label:"Repair Instruction",desc:"Actionable fix from an evaluator or human reviewer.",color:c.purple},
{id:"research_need_in",label:"Research / Source Need",desc:"Open question or request to a configured source/research target.",color:c.amber},
{id:"source_update_in",label:"Source Workspace Update",desc:"Sources added, verified, superseded, or flagged.",color:c.cyan},
];
function needsInputPopup(t){return ["step.agent_task","step.agent_review_gate","step.panel","step.red_team","step.coding","step.judge","step.outcome_evaluator","step.source_research","system.source_workspace","system.task_forum","system.experiment","utility.loop","utility.hold","utility.manual_switch","utility.switch","output.email","output.file","output.chat","output.notify","output.forum"].includes(t);}
function getInputChoices(t){
if(t==="utility.loop") return loopInputChoices;
if(t==="utility.hold") return holdInputChoices;
if(t==="utility.manual_switch") return manualSwitchInputChoices;
if(t==="utility.switch") return switchInputChoices;
if(t==="step.agent_review_gate") return agentReviewInputChoices;
if(t==="step.coding") return codingInputChoices;
if(t==="system.experiment") return experimentInputChoices;
if(t==="step.judge") return judgeInputChoices;
if(t==="step.outcome_evaluator") return outcomeEvaluatorInputChoices;
if(t==="step.source_research") return sourceResearchInputChoices;
if(t==="system.source_workspace") return sourceWorkspaceInputChoices;
if(t==="system.task_forum") return taskForumInputChoices;
if(t==="step.panel") return panelInputChoices;
if(t==="output.email") return emailOutputInputChoices;
if(t==="output.chat") return chatOutputInputChoices;
if(t==="output.file") return fileOutputInputChoices;
if(t==="output.notify") return notifyInputChoices;
if(t==="output.forum") return forumOutputInputChoices;
return inputChoices;
}
const switchInputChoices=[
{id:"data_in",label:"Content to Evaluate",desc:"Primary content the conditions are evaluated against.",color:c.accent},
{id:"context_in",label:"Background Context",desc:"Reference material for agent_eval conditions. Files the agent should inspect.",color:c.cyan},
];
const agentReviewInputChoices=[
{id:"data_in",label:"Content to Review",desc:"Primary material the reviewing agent evaluates.",color:c.accent},
{id:"context_in",label:"Background Context",desc:"Reference material, standards, criteria documents for the reviewer.",color:c.cyan},
{id:"attachment_in",label:"File Attachment",desc:"File(s) the reviewer should inspect alongside the primary content.",color:c.amber},
{id:"instruction_in",label:"Agent Instructions",desc:"Dynamic instructions for the reviewing agent.",color:c.purple},
];
const codingInputChoices=[
{id:"data_in",label:"Coding Task",desc:"Primary instruction/specification — what to build, fix, or modify.",color:c.accent},
{id:"context_in",label:"Reference Material",desc:"Spec documents, existing code, architecture notes. Add multiple for separate sources.",color:c.cyan},
{id:"instruction_in",label:"Dynamic Instruction",desc:"Per-activation instruction overlay — e.g., revision feedback from a review loop.",color:c.purple},
];
const experimentInputChoices=[
{id:"target",label:"⚗ Target Module",desc:"Wire FROM an existing module to auto-copy its config as baseline. The module continues running normally.",color:c.orange},
{id:"data_in",label:"Input Data",desc:"Override input data. If unwired, uses the target module's data_in.",color:c.accent},
{id:"context_in",label:"Shared Context",desc:"Reference material shared across all variants.",color:c.cyan},
{id:"instruction_in",label:"Shared Instruction",desc:"Dynamic instruction applied to all variants.",color:c.purple},
];
const judgeInputChoices=[
{id:"target_in",label:"Target Output",desc:"The primary output being evaluated. Required.",color:c.accent},
{id:"comparison_in",label:"⚖ Comparison",desc:"A/B comparison bundle from Experiment, or a second output for comparison.",color:c.cyan},
{id:"evidence_in",label:"Evidence",desc:"Red team findings, review outputs, reference material. Add multiple evidence sources.",color:c.amber},
];
const emailOutputInputChoices=[
{id:"data_in",label:"Email Body Content",desc:"Text/data that becomes the email body.",color:c.accent},
{id:"recipient_in",label:"Dynamic Recipient",desc:"Email address(es) — overrides the static To field. Wire from a trigger or agent output.",color:c.green},
{id:"attachment_in",label:"File Attachment",desc:"File(s) to attach to this email. FileRefs attached as-is.",color:c.amber},
{id:"context_in",label:"Delivery Context",desc:"Reference material (e.g., original file for name matching).",color:c.cyan},
{id:"instruction_in",label:"Agent Instructions",desc:"Dynamic instructions for how to compose the email.",color:c.purple},
];
const chatOutputInputChoices=[
{id:"data_in",label:"Chat Content",desc:"Text/data posted to the chat.",color:c.accent},
{id:"attachment_in",label:"File Attachment",desc:"File(s) to attach in the chat conversation.",color:c.amber},
{id:"context_in",label:"Delivery Context",desc:"Reference material for formatting or context.",color:c.cyan},
{id:"instruction_in",label:"Agent Instructions",desc:"Dynamic instructions for how to format the post.",color:c.purple},
];
const panelInputChoices=[
{id:"data_in",label:"Discussion Topic",desc:"Content the panel will discuss.",color:c.accent},
{id:"context_in",label:"Background Context",desc:"Reference material for panelists.",color:c.cyan},
];
const fileOutputInputChoices=[
{id:"data_in",label:"Content to Save",desc:"Text or file to save to disk.",color:c.accent},
{id:"attachment_in",label:"Files to Save",desc:"FileRefs to save alongside or instead of data_in.",color:c.amber},
{id:"context_in",label:"Naming Context",desc:"Original file for name retention, formatting examples.",color:c.cyan},
{id:"instruction_in",label:"Naming Instructions",desc:"Dynamic instructions for agent-determined naming/directory.",color:c.purple},
];
const notifyInputChoices=[
{id:"data_in",label:"Content to Deliver",desc:"Primary material included in the notification.",color:c.accent},
{id:"context_in",label:"Delivery Context",desc:"Additional context for notification formatting.",color:c.cyan},
];
const forumOutputInputChoices=[
{id:"data_in",label:"Content to Post",desc:"Text/data posted to the forum or panel.",color:c.accent},
{id:"context_in",label:"Delivery Context",desc:"Reference material for the post.",color:c.cyan},
{id:"instruction_in",label:"Agent Instructions",desc:"Dynamic instructions for how to compose the post.",color:c.purple},
];
const outputInputChoices=[
{id:"data_in",label:"Content to Deliver",desc:"Primary material to format and send.",color:c.accent},
{id:"context_in",label:"Delivery Context",desc:"Reference material (e.g., original file for name matching, formatting examples).",color:c.cyan},
{id:"instruction_in",label:"Agent Instructions",desc:"Dynamic instructions for how to compose/format the delivery.",color:c.purple},
];
const inputChoices=[
{id:"data_in",label:"Process",desc:"Primary material to work on. Chain history from prior steps included automatically.",color:c.accent},
{id:"context_in",label:"Background Context",desc:"Reference material, separate from main flow. Framed as 'for reference' in prompt.",color:c.cyan},
{id:"instruction_in",label:"Agent Instructions",desc:"Dynamic instructions that add to this step's static instruction.",color:c.purple},
];
// ── PALETTE DATA ──
const palTypes = {
trigger:[
{t:"trigger.manual",n:"Manual",d:"Fire on click or command"},
{t:"trigger.schedule",n:"Schedule",d:"Timer / recurring interval"},
{t:"trigger.email",n:"Email In",d:"Watch inbox for emails"},
{t:"trigger.file_watcher",n:"File Watcher",d:"Watch folder for changes"},
{t:"trigger.discord",n:"Discord In",d:"Watch channel for messages"},
{t:"trigger.task_complete",n:"Task Event",d:"Another task completes or sends output"},
{t:"trigger.forum_panel",n:"Forum / Panel",d:"Forum post or panel consensus"},
{t:"trigger.webhook",n:"Webhook / API",d:"Receive external POST"},
],
step:[
{t:"step.agent_task",n:"Agent Task",d:"LLM agent processes input"},
{t:"step.transform",n:"Transform",d:"Deterministic processing"},
{t:"step.user_review_gate",n:"Human Review",d:"Pause for your approval"},
{t:"step.agent_review_gate",n:"Agent Review",d:"Automated quality check"},
{t:"step.panel",n:"Panel Discussion",d:"Multi-agent deliberation"},
{t:"step.red_team",n:"Red Team",d:"Send to red team preset"},
{t:"step.coding",n:"Coding Session",d:"ACP coding agent session"},
{t:"step.judge",n:"Judge",d:"Score outputs on configurable dimensions"},
{t:"step.outcome_evaluator",n:"Outcome Evaluator",d:"Evaluate outcome + emit repair"},
{t:"step.source_research",n:"Source Research",d:"Gather/update/verify sources"},
{t:"step.pass_through",n:"Pass-through",d:"Forward data unchanged"},
],
utility:[
{t:"utility.junction",n:"Junction",d:"Combine inputs (AND/OR/MERGE)"},
{t:"utility.switch",n:"Switch / Router",d:"Conditional routing"},
{t:"utility.delay",n:"Delay",d:"Hold signal for a duration"},
{t:"utility.loop",n:"Loop Controller",d:"Iterate until condition"},
{t:"utility.context_filter",n:"Context Filter",d:"Control what chain history passes through"},
{t:"utility.hold",n:"Hold",d:"Hold data until release signal"},
{t:"utility.manual_switch",n:"Manual Switch",d:"Signal-controlled routing (A/B/C/D)"},
],
output:[
{t:"output.email",n:"Email Out",d:"Send email"},
{t:"output.file",n:"File Out",d:"Save to filesystem"},
{t:"output.chat",n:"Chat Out",d:"Post to Q chat"},
{t:"output.notify",n:"Notify",d:"Send notification"},
{t:"output.memory",n:"Memory Out",d:"Save to ELNOR memory"},
{t:"output.webhook",n:"Webhook Out",d:"POST to external URL"},
{t:"output.imessage",n:"iMessage Out",d:"Send via Messages"},
{t:"output.forum",n:"Forum Out",d:"Post to forum"},
{t:"output.task",n:"Task Out",d:"Send data to another task"},
],
source:[
{t:"source.file",n:"File / User Input",d:"Provide a file or typed content"},
{t:"source.bucket",n:"Bucket",d:"DOC7 context bucket"},
{t:"source.project",n:"Project",d:"Project context"},
],
environment:[
{t:"environment.config",n:"Environment",d:"Objective, role, skills, buckets + reference library"},
],
system:[
{t:"system.monitor",n:"Monitor",d:"Telemetry & error catch-all"},
{t:"system.source_workspace",n:"Source Workspace",d:"Shared task source/research substrate"},
{t:"system.task_forum",n:"Task Forum",d:"Run board + optional coordination"},
{t:"system.experiment",n:"Experiment",d:"Compare 2-4 variant configs side by side"},
],
};
const typeNames = {};
Object.values(palTypes).flat().forEach(p => { typeNames[p.t] = p.n; });
const typesByCat = {};
Object.entries(palTypes).forEach(([cat, items]) => { typesByCat[cat] = items.map(i => i.t); });
// ── DEMO DATA ──
const initModules = [
{id:"env1",cat:"environment",type:"environment.config",name:"Opposition Task Environment",x:60,y:70},
{id:"m1",cat:"trigger",type:"trigger.manual",name:"Upload MTD + Record",x:60,y:300},
{id:"sw1",cat:"system",type:"system.source_workspace",name:"Source Workspace",x:260,y:85},
{id:"forum1",cat:"system",type:"system.task_forum",name:"Task Forum / Run Board",x:520,y:85},
{id:"r1",cat:"step",type:"step.source_research",name:"Deep Source Research",x:260,y:270,runCount:2},
{id:"oe1",cat:"step",type:"step.outcome_evaluator",name:"Research Sufficiency",x:520,y:270,runCount:1,outcomeState:"needs sources"},
{id:"d1",cat:"step",type:"step.agent_task",name:"Draft Opposition",x:760,y:270,runCount:1,pinned:true},
{id:"oe2",cat:"step",type:"step.outcome_evaluator",name:"Source Accuracy Gate",x:1010,y:270,runCount:1,outcomeState:"needs verification"},
{id:"rev1",cat:"step",type:"step.agent_task",name:"Revision Pass",x:760,y:495,runCount:2,sessionContinue:true},
{id:"rt1",cat:"step",type:"step.agent_review_gate",name:"Red Team / Strategy Review",x:1010,y:495,runCount:1},
{id:"exp1",cat:"system",type:"system.experiment",name:"Compare Draft Variants",x:1240,y:250,expVariants:["A","B","Hybrid"]},
{id:"j1",cat:"step",type:"step.judge",name:"Final Outcome Judge",x:1240,y:495,judgeScores:{a:0.88,b:0.81,c:0.91}},
{id:"m7",cat:"output",type:"output.file",name:"Save Brief Package",x:1510,y:382},
];
const initCables = [
{id:"c1",from:"m1",fromPort:"data_out",to:"sw1",toPort:"source_update_in",role:"data"},
{id:"c2",from:"m1",fromPort:"data_out",to:"r1",toPort:"research_goal_in",role:"data"},
{id:"c3",from:"r1",fromPort:"source_update_out",to:"sw1",toPort:"source_update_in",role:"data"},
{id:"c4",from:"r1",fromPort:"signal_out",to:"oe1",toPort:"candidate_in",role:"signal"},
{id:"c5",from:"sw1",fromPort:"workspace_snapshot_out",to:"oe1",toPort:"evidence_in",role:"context"},
{id:"c6",from:"oe1",fromPort:"approved_out",to:"d1",toPort:"data_in",role:"data"},
{id:"c7",from:"oe1",fromPort:"research_need_out",to:"r1",toPort:"research_goal_in",role:"data"},
{id:"c8",from:"oe1",fromPort:"feedback_bundle_out",to:"forum1",toPort:"evaluation_result_in",role:"data"},
{id:"c9",from:"sw1",fromPort:"workspace_snapshot_out",to:"d1",toPort:"context_in",role:"context"},
{id:"c10",from:"forum1",fromPort:"board_digest_out",to:"d1",toPort:"context_in",role:"context"},
{id:"c11",from:"d1",fromPort:"data_out",to:"oe2",toPort:"candidate_in",role:"data"},
{id:"c12",from:"oe2",fromPort:"repair_instruction_out",to:"rev1",toPort:"instruction_in",role:"data"},
{id:"c13",from:"oe2",fromPort:"research_need_out",to:"r1",toPort:"research_goal_in",role:"data"},
{id:"c14",from:"oe2",fromPort:"feedback_bundle_out",to:"forum1",toPort:"evaluation_result_in",role:"data"},
{id:"c15",from:"rev1",fromPort:"data_out",to:"rt1",toPort:"data_in",role:"data"},
{id:"c16",from:"rt1",fromPort:"failed_out",to:"rev1",toPort:"instruction_in",role:"data"},
{id:"c17",from:"rt1",fromPort:"approved_out",to:"exp1",toPort:"data_in",role:"data"},
{id:"c18",from:"exp1",fromPort:"comparison_out",to:"j1",toPort:"comparison_in",role:"data"},
{id:"c19",from:"j1",fromPort:"scores_out",to:"m7",toPort:"data_in",role:"data"},
{id:"c20",from:"j1",fromPort:"analysis_out",to:"forum1",toPort:"evaluation_result_in",role:"data"},
];
const runSt = {
idle:{},
running:{env1:"na",m1:"done",sw1:"done",forum1:"active",r1:"done",oe1:"done",d1:"done",oe2:"active",rev1:"wait",rt1:"wait",exp1:"wait",j1:"wait",m7:"wait"},
paused:{env1:"na",m1:"done",sw1:"done",forum1:"done",r1:"done",oe1:"done",d1:"done",oe2:"done",rev1:"active",rt1:"wait",exp1:"wait",j1:"wait",m7:"wait"},
complete:{env1:"na",m1:"done",sw1:"done",forum1:"done",r1:"done",oe1:"done",d1:"done",oe2:"done",rev1:"done",rt1:"done",exp1:"done",j1:"done",m7:"done"},
};
const stCol = {done:c.green,active:c.accent,paused:c.amber,error:c.red,wait:c.textTer,na:"transparent"};
// ── SIZING — tighter ──
function modW(mod){
if(mod.isMult) return 18;
// Two-part measurement: category label is small mono, name is larger sans bold
const catPx = (catLabel[mod.cat].length + 2) * 5.0; // mono 8.5px ≈ 5.0px/char, +2 for ": "
const namePx = mod.name.length * 7.2; // sans 12px bold ≈ 7.2px/char
const pad = 30; // 10px left + 20px right (pin + breathing)
const textW = catPx + namePx + pad;
return Math.max(100, Math.min(235, textW)); // floor 100, cap 235 (~23 char names)
}
function modH(mod){
if(mod.isMult) return 60;
if(mod.cat==="environment"||mod.cat==="system") return 50;
return {trigger:78,step:78,utility:68,output:78,source:68}[mod.cat]||78;
}
function primaryInPos(mod){return{x:mod.x,y:mod.y+modH(mod)/2};}
function primaryOutPos(mod){return{x:mod.x+modW(mod),y:mod.y+modH(mod)/2};}
function bez(x1,y1,x2,y2){const dx=Math.abs(x2-x1);const cp=Math.min(Math.max(70,dx*0.35),dx/2);return`M${x1},${y1} C${x1+cp},${y1} ${x2-cp},${y2} ${x2},${y2}`;}
// ═══════════════════════════════════════════════════════════════
// DOC20 WORKSPACE SHELL PREVIEW
// ═══════════════════════════════════════════════════════════════
function QTaskWorkspaceShell(){
const [browserOpen,setBrowserOpen]=useState(true);
const [chatOpen,setChatOpen]=useState(false);
return (
<div style={{height:"100vh",width:"100vw",display:"flex",flexDirection:"column",background:"#f6f8fb",fontFamily:sans,color:"#1f2937",overflow:"hidden"}}>
<div style={{height:34,minHeight:34,display:"flex",alignItems:"center",justifyContent:"center",borderBottom:"1px solid #d7dce5",background:"#ffffff",fontSize:12,fontWeight:600,color:"#374151",position:"relative"}}>
<div style={{position:"absolute",left:12,display:"flex",gap:8,alignItems:"center",fontSize:11,color:"#6b7280"}}>
<span style={{width:10,height:10,borderRadius:"50%",background:"#ef4444",display:"inline-block"}} />
<span style={{width:10,height:10,borderRadius:"50%",background:"#f59e0b",display:"inline-block"}} />
<span style={{width:10,height:10,borderRadius:"50%",background:"#22c55e",display:"inline-block"}} />
</div>
Q Dashboard · Task tab preview
<button onClick={()=>setChatOpen(!chatOpen)} style={{position:"absolute",right:10,padding:"4px 10px",border:"1px solid #d7dce5",background:chatOpen?"#eef2ff":"#fff",borderRadius:6,fontSize:11,color:"#4b5563",cursor:"pointer"}}>{chatOpen?"Hide main chat":"Show main chat"}</button>
</div>
<div style={{flex:1,minHeight:0,display:"flex",overflow:"hidden"}}>
<div style={{width:38,minWidth:38,background:"#0f172a",display:"flex",flexDirection:"column",alignItems:"center",paddingTop:7,gap:9,color:"#cbd5e1"}}>
<div style={{width:25,height:25,borderRadius:6,background:"#2563eb",display:"flex",alignItems:"center",justifyContent:"center",fontWeight:800,fontSize:13}}>Q</div>
{[["browser","▣"],["chat","◌"],["tasks","◆"],["notes","▤"],["settings","⚙"]].map(([k,ic])=>
<div key={k} onClick={()=>k==="browser"&&setBrowserOpen(!browserOpen)} title={k} style={{width:26,height:26,borderRadius:6,display:"flex",alignItems:"center",justifyContent:"center",background:k==="tasks"?"#1e293b":"transparent",border:k==="tasks"?"1px solid #334155":"1px solid transparent",cursor:"pointer",fontSize:14}}>{ic}</div>
)}
</div>
{browserOpen&&<TaskBrowserColumn />}
<div style={{flex:1,minWidth:0,display:"flex",flexDirection:"column",overflow:"hidden",background:"#fff"}}>
<div style={{height:36,minHeight:36,borderBottom:"1px solid #d7dce5",background:"#f9fafb",display:"flex",alignItems:"end",gap:2,padding:"0 8px",overflow:"hidden"}}>
{[
{name:"Paramount Research",color:"#22c55e"},
{name:"Sanli Expert Report.pdf",color:"#64748b"},
{name:"Task · Complaint Evaluation",color:"#f59e0b",active:true},
{name:"Paramount damages",color:"#1d4ed8"},
].map(t=><div key={t.name} style={{height:28,padding:"0 12px",border:`1px solid ${t.active?"#f59e0b":"#d7dce5"}`,borderBottom:t.active?"1px solid #0f1117":"1px solid #d7dce5",borderTop:`2px solid ${t.color}`,borderRadius:"7px 7px 0 0",background:t.active?"#0f1117":"#fff",color:t.active?"#e5e7eb":"#4b5563",display:"flex",alignItems:"center",gap:8,fontSize:11,maxWidth:210,whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"}}>
<span>{t.name}</span><span style={{opacity:.55}}>×</span>
</div>)}
<div style={{padding:"0 8px 8px",fontSize:16,color:"#9ca3af"}}>+</div>
</div>
<div style={{flex:1,minHeight:0,overflow:"hidden"}}>
<TaskCanvasInner />
</div>
</div>
{chatOpen&&<MainChatPreview />}
</div>
</div>
);
}
function TaskBrowserColumn(){
const rows=[
["Today — May 4, 2026","NOTE","2h"],
["Draft Complaint — Marex","TASK","running"],
["Complaint Evaluation","SAVED TASK","18 runs"],
["Motion Red-Team Segment","SEGMENT","preset"],
["Complaint Red-Team Module","PRESET","11 uses"],
["Marex Filing Monitor","SCHEDULED","5:00 PM"],
["Sanli Expert Report.pdf","DOC","10m"],
["Paramount damages","CHAT","1h"],
];
return <div style={{width:246,minWidth:246,borderRight:"1px solid #d7dce5",background:"#f8fafc",display:"flex",flexDirection:"column",overflow:"hidden"}}>
<div style={{height:34,display:"flex",alignItems:"center",gap:14,padding:"0 12px",borderBottom:"1px solid #e5e7eb",fontSize:11,color:"#6b7280"}}>
<span>Nav</span><span style={{fontWeight:700,color:"#1d4ed8",borderBottom:"2px solid #1d4ed8",height:34,display:"flex",alignItems:"center"}}>Browser</span><span>Notes</span><span>Web</span>
</div>
<div style={{padding:9,borderBottom:"1px solid #e5e7eb"}}>
<input placeholder="Search tasks, runs, files…" style={{width:"100%",boxSizing:"border-box",height:26,border:"1px solid #d7dce5",borderRadius:5,padding:"0 8px",fontSize:11,outline:"none"}} />
<div style={{display:"flex",gap:5,flexWrap:"wrap",marginTop:8}}>
{['Document','Chat','Task','Artifact','Preset','Segment'].map((x,i)=><span key={x} style={{fontSize:9,padding:"3px 6px",border:`1px solid ${i===2?"#f59e0b":"#d7dce5"}`,borderRadius:4,background:i===2?"#fffbeb":"#fff",color:i===2?"#b45309":"#64748b"}}>{x}</span>)}
</div>
<div style={{fontSize:9,color:"#94a3b8",marginTop:8}}>Task filter active · drag items onto graph, module, or segment.</div>
</div>
<div style={{flex:1,overflowY:"auto"}}>
{rows.map(([name,type,time],i)=><div key={name} draggable style={{padding:"8px 10px",borderBottom:"1px solid #e5e7eb",display:"grid",gridTemplateColumns:"1fr auto",gap:8,cursor:"grab",background:i===1?"#fffbeb":"transparent"}}>
<div style={{fontSize:11,color:"#111827",overflow:"hidden",whiteSpace:"nowrap",textOverflow:"ellipsis"}}>{name}</div>
<div style={{fontSize:8,fontWeight:700,color:type==="TASK"?"#f59e0b":type==="SEGMENT"?"#8b5cf6":type==="PRESET"?"#7c3aed":"#64748b"}}>{type}</div>
<div style={{fontSize:9,color:"#94a3b8"}}>{time}</div><div />
</div>)}
</div>
<div style={{padding:9,borderTop:"1px solid #e5e7eb",display:"flex",gap:6}}>
<button style={{flex:1,padding:"6px 0",border:"1px solid #f59e0b",background:"#fffbeb",borderRadius:5,color:"#b45309",fontSize:10,fontWeight:700}}>+ New Task</button>
<button style={{padding:"6px 8px",border:"1px solid #d7dce5",background:"#fff",borderRadius:5,color:"#64748b",fontSize:10}}>Dupe</button>
</div>
</div>;
}
function MainChatPreview(){
return <div style={{width:330,minWidth:330,borderLeft:"1px solid #d7dce5",background:"#fff",display:"flex",flexDirection:"column"}}>
<div style={{height:34,borderBottom:"1px solid #e5e7eb",display:"flex",alignItems:"center",padding:"0 12px",fontSize:12,fontWeight:700,color:"#1f2937"}}>Main chat</div>
<div style={{flex:1,padding:14,fontSize:12,color:"#4b5563",lineHeight:1.5}}>Main Elnor chat is closed by default on the task tab. This preview shows how it can still reopen without replacing the Task Agent side panel.</div>
<div style={{padding:10,borderTop:"1px solid #e5e7eb"}}><input placeholder="Ask Task Agent…" style={{width:"100%",boxSizing:"border-box",height:34,border:"1px solid #d7dce5",borderRadius:8,padding:"0 10px"}} /></div>
</div>;
}
// ═══════════════════════════════════════════════════════════════
// MAIN COMPONENT
// ═══════════════════════════════════════════════════════════════
function TaskCanvasInner() {
const [modules, setModules] = useState(initModules);
const [cables, setCables] = useState(initCables);
const [selected, setSelected] = useState(null);
const [detailId, setDetailId] = useState(null);
const [hovCable, setHovCable] = useState(null);
const [exec, setExec] = useState("idle");
const [zoom, setZoom] = useState(65);
const [pan, setPan] = useState({x:0,y:0});
const [palette, setPalette] = useState(null);
const [connecting, setConnecting] = useState(null);
const [mouseXY, setMouseXY] = useState({x:0,y:0});
const [popup, setPopup] = useState(null);
const [editingName, setEditingName] = useState(null);
const [taskName, setTaskName] = useState("Experiment: Compare Drafting Approaches");
const [editingTaskName, setEditingTaskName] = useState(false);
const [alsoSendTo, setAlsoSendTo] = useState(null);
const [confirmDelete, setConfirmDelete] = useState(null);
const [reviewMode, setReviewMode] = useState(false);
const [rightPanel, setRightPanel] = useState(null); // null, "log", "artifacts", "inspector", "agent", "tasks"
const [taskSettings, setTaskSettings] = useState(false);
const [expandedOverlay, setExpandedOverlay] = useState(null); // null | "experiment" | "judge"
const canvasRef = useRef(null);
const dragRef = useRef(null);
const panRef = useRef(null);
const es = runSt[exec]||{};
const detailMod = detailId ? modules.find(m=>m.id===detailId) : null;
const scale = zoom/100;
const svgW=2400, svgH=1400;
// ── COORDINATE FIX ──
const toSvg = useCallback((clientX,clientY)=>{
const el=canvasRef.current;
if(!el) return {x:0,y:0};
const r=el.getBoundingClientRect();
const s=zoom/100;
return {x:(clientX-r.left+el.scrollLeft)/s-pan.x, y:(clientY-r.top+el.scrollTop)/s-pan.y};
},[zoom,pan]);
// ── RECURSIVE SELECTION ──
const selCables = new Set();
const selMods = new Set();
if(selected && modules.some(m=>m.id===selected)){
const visited=new Set();
const queue=[selected];
while(queue.length){
const mid=queue.shift();
if(visited.has(mid)) continue;
visited.add(mid);
selMods.add(mid);
cables.forEach(cb=>{
if(cb.from===mid||cb.to===mid){
selCables.add(cb.id);
const next=cb.from===mid?cb.to:cb.from;
const nextMod=modules.find(m=>m.id===next);
if(nextMod && nextMod.isMult) queue.push(next);
else if(nextMod) selMods.add(next);
}
});
}
}
// ── MOUSE/DRAG/PAN ──
const onMouseMove = useCallback((e)=>{
setMouseXY(toSvg(e.clientX,e.clientY));
if(dragRef.current){
const s=zoom/100;
const dx=(e.clientX-dragRef.current.sx)/s;
const dy=(e.clientY-dragRef.current.sy)/s;
setModules(prev=>prev.map(m=>m.id===dragRef.current.id?{...m,x:dragRef.current.ox+dx,y:dragRef.current.oy+dy}:m));
}
if(panRef.current){
const s=zoom/100;
setPan({x:panRef.current.opx+(e.clientX-panRef.current.sx)/s, y:panRef.current.opy+(e.clientY-panRef.current.sy)/s});
}
},[zoom,toSvg]);
const onMouseUp = useCallback(()=>{dragRef.current=null;panRef.current=null;},[]);
// ── CONNECTION ──
const startConnect = useCallback((e,mod)=>{
e.stopPropagation();
let oc=outputChoices[mod.type];
// Add named file outputs dynamically
if(mod.namedOutputs&&mod.namedOutputs.length>0){
const namedPorts=mod.namedOutputs.map(name=>({id:`named_${name.toLowerCase().replace(/\s+/g,"_")}`,label:`📄 ${name}`,desc:`Files matching "${name}"`,color:c.green}));
oc=oc?[...oc.slice(0,-2),...namedPorts,...oc.slice(-2)]:[{id:"data_out",label:"Data Out",color:c.accent},...namedPorts,{id:"signal_out",label:"⚡ Signal",color:c.amber}];
}
if(oc){
const pos=toSvg(e.clientX,e.clientY);
setPopup({type:"output_select",x:pos.x,y:pos.y,mod,choices:oc,then:(portId)=>{
setConnecting({modId:mod.id,portId,role:portId.includes("signal")?"signal":portId.includes("error")?"error":"data"});
setPopup(null);
}});
} else {
setConnecting({modId:mod.id,portId:"data_out",role:"data"});
}
},[toSvg]);
const finishConnect = useCallback((e,targetMod)=>{
e.stopPropagation();
if(!connecting||!targetMod||targetMod.id===connecting.modId) return;
if(targetMod.cat==="environment"||targetMod.cat==="system") return;
// For agent-capable modules: ALWAYS show role popup first, then handle mult
if(needsInputPopup(targetMod.type)&&!targetMod.isMult){
const pos=toSvg(e.clientX,e.clientY);
setPopup({type:"input_select",x:pos.x,y:pos.y,mod:targetMod,choices:getInputChoices(targetMod.type),then:(portId)=>{
// Check if this specific port already has a cable → mult needed
const existingOnPort=cables.filter(cb=>cb.to===targetMod.id&&cb.toPort===portId);
if(existingOnPort.length>0){
const multId="mult"+Date.now();
const tp=primaryInPos(targetMod);
const newMult={id:multId,cat:"utility",type:"utility.junction",name:"⫴",x:tp.x-55,y:tp.y-30+existingOnPort.length*20,isMult:true,downstreamPort:portId};
setModules(prev=>[...prev,newMult]);
setCables(prev=>{
const rewired=prev.map(cb=>(cb.to===targetMod.id&&cb.toPort===portId)?{...cb,to:multId,toPort:"data_in"}:cb);
return [...rewired,
{id:"c"+Date.now()+"a",from:connecting.modId,fromPort:connecting.portId,to:multId,toPort:"data_in",role:connecting.role},
{id:"c"+Date.now()+"b",from:multId,fromPort:"data_out",to:targetMod.id,toPort:portId,role:"data"},
];
});
} else {
addCable(connecting.modId,connecting.portId,targetMod.id,portId,connecting.role);
}
setPopup(null);setConnecting(null);
}});
return;
}
// Non-agent modules: check for mult on data_in
const existingIn=cables.filter(cb=>cb.to===targetMod.id);
if(existingIn.length>0&&!targetMod.isMult){
const multId="mult"+Date.now();
const tp=primaryInPos(targetMod);
const newMult={id:multId,cat:"utility",type:"utility.junction",name:"⫴",x:tp.x-55,y:tp.y-30,isMult:true};
setModules(prev=>[...prev,newMult]);
setCables(prev=>{
const rewired=prev.map(cb=>cb.to===targetMod.id?{...cb,to:multId}:cb);
return [...rewired,
{id:"c"+Date.now()+"a",from:connecting.modId,fromPort:connecting.portId,to:multId,toPort:"data_in",role:connecting.role},
{id:"c"+Date.now()+"b",from:multId,fromPort:"data_out",to:targetMod.id,toPort:"data_in",role:"data"},
];
});
setConnecting(null);
return;
}
addCable(connecting.modId,connecting.portId,targetMod.id,"data_in",connecting.role);
setConnecting(null);
},[connecting,cables,toSvg]);
const addCable = useCallback((from,fromPort,to,toPort,role)=>{
setCables(prev=>[...prev,{id:`c${Date.now()}`,from,fromPort,to,toPort,role:role||"data"}]);
},[]);
const startSignalConnect = useCallback((e,mod,type)=>{
e.stopPropagation();
setConnecting({modId:mod.id,portId:type==="signal"?"signal_out":"error_out",role:type});
},[]);
// ── MODULE ACTIONS ──
const addModule = useCallback((cat,type,name)=>{
const id=`m${Date.now()}`;
setModules(prev=>[...prev,{id,cat,type,name,x:600-pan.x,y:350-pan.y}]);
setPalette(null);setDetailId(id);setSelected(id);setRightPanel(null);
},[pan]);
const duplicateMod = useCallback((mod)=>{
const id=`m${Date.now()}`;
setModules(prev=>[...prev,{...mod,id,name:mod.name+" (copy)",x:mod.x+40,y:mod.y+40}]);
setDetailId(id);
},[]);
const deleteMod = useCallback((modId)=>{
setCables(prev=>prev.filter(cb=>cb.from!==modId&&cb.to!==modId));
setModules(prev=>prev.filter(m=>m.id!==modId));
if(detailId===modId) setDetailId(null);
if(selected===modId) setSelected(null);
setConfirmDelete(null);
},[detailId,selected]);
const changeModType = useCallback((modId,newType)=>{
const newCat=newType.split(".")[0];
const newName=typeNames[newType]||newType.split(".").pop();
setModules(prev=>prev.map(m=>m.id===modId?{...m,type:newType,cat:newCat,name:newName}:m));
},[]);
const updateModName = useCallback((modId,name)=>{
setModules(prev=>prev.map(m=>m.id===modId?{...m,name}:m));
},[]);
const alsoSendToCreate = useCallback((detMod,outputType)=>{
if(!detMod) return;
const id=`m${Date.now()}`;
const tname=typeNames[outputType]||outputType.split(".").pop();
const newMod={id,cat:"output",type:outputType,name:tname,x:detMod.x,y:detMod.y+110};
setModules(prev=>[...prev,newMod]);
const inputCable=cables.find(cb=>cb.to===detMod.id&&(cb.toPort==="data_in"||!cb.toPort));
if(inputCable){
setCables(prev=>[...prev,{id:"c"+Date.now(),from:inputCable.from,fromPort:inputCable.fromPort,to:id,toPort:"data_in",role:"data"}]);
}
setAlsoSendTo(null);setDetailId(id);
},[cables]);
const renameMod = useCallback((id,name)=>{setModules(prev=>prev.map(m=>m.id===id?{...m,name}:m));setEditingName(null);},[]);
const startDrag = useCallback((e,mod)=>{if(e.button!==0||connecting||e.altKey)return;e.stopPropagation();dragRef.current={id:mod.id,sx:e.clientX,sy:e.clientY,ox:mod.x,oy:mod.y};},[connecting]);
const onCanvasClick = useCallback((e)=>{
if(popup){setPopup(null);return;}
if(connecting){setConnecting(null);return;}
setSelected(null);setPalette(null);setEditingName(null);setAlsoSendTo(null);
},[connecting,popup]);
const onCanvasDown = useCallback((e)=>{
if(e.button===1||(e.button===0&&e.altKey)){e.preventDefault();panRef.current={sx:e.clientX,sy:e.clientY,opx:pan.x,opy:pan.y};}
},[pan]);
// ═══ RENDER ═══
return (
<div style={{display:"flex",flexDirection:"column",height:"100%",background:c.bg,color:c.text,fontFamily:mono,overflow:"hidden",userSelect:"none"}}>
{/* ═══ HEADER ═══ */}
<div style={{height:44,minHeight:44,borderBottom:`1px solid ${c.border}`,display:"flex",alignItems:"center",justifyContent:"space-between",padding:"0 12px",flexShrink:0,background:c.bg,zIndex:10}}>
<div style={{display:"flex",alignItems:"center",gap:5,minWidth:520}}>
<span style={{color:c.textSec,cursor:"pointer",fontSize:18,fontWeight:600}}>←</span>
<span style={{fontSize:9,color:exec==="idle"?c.textSec:exec==="running"?c.accent:exec==="paused"?c.amber:c.green,padding:"3px 8px",borderRadius:4,border:`1px solid ${exec==="idle"?c.border:exec==="running"?c.accent+"40":exec==="paused"?c.amber+"40":c.green+"40"}`,background:exec==="idle"?"transparent":exec==="running"?c.accent+"10":exec==="paused"?c.amber+"10":c.green+"10",display:"flex",alignItems:"center",gap:4,height:22,boxSizing:"border-box"}}>
{exec!=="idle"&&<span style={{width:5,height:5,borderRadius:"50%",background:"currentColor",...(exec==="running"?{animation:"pulse 1.5s infinite"}:{})}} />}
{exec==="idle"?"14 runs · $31.20":exec==="running"?"Run 15 · $2.47 · 3m 22s":exec==="paused"?"Paused · $2.47":"✓ Complete · $2.47"}
</span>
<span onClick={()=>{setRightPanel(rightPanel==="tasks"?null:"tasks");setDetailId(null);}} style={{fontSize:10,color:rightPanel==="tasks"?c.accent:c.textSec,cursor:"pointer",padding:"3px 8px",borderRadius:4,border:`1px solid ${rightPanel==="tasks"?c.accent+"40":c.border}`,background:rightPanel==="tasks"?c.accent+"10":"transparent",height:22,boxSizing:"border-box",display:"flex",alignItems:"center"}}>☰ Task List</span>
<span onClick={()=>{setRightPanel(rightPanel==="log"?null:"log");setDetailId(null);}} style={{fontSize:10,color:rightPanel==="log"?c.accent:c.textSec,cursor:"pointer",padding:"3px 8px",borderRadius:4,border:`1px solid ${rightPanel==="log"?c.accent+"40":c.border}`,background:rightPanel==="log"?c.accent+"10":"transparent",height:22,boxSizing:"border-box",display:"flex",alignItems:"center"}}>📋 Runs</span>
<span onClick={()=>{setRightPanel(rightPanel==="artifacts"?null:"artifacts");setDetailId(null);}} style={{fontSize:10,color:rightPanel==="artifacts"?c.accent:c.textSec,cursor:"pointer",padding:"3px 8px",borderRadius:4,border:`1px solid ${rightPanel==="artifacts"?c.accent+"40":c.border}`,background:rightPanel==="artifacts"?c.accent+"10":"transparent",height:22,boxSizing:"border-box",display:"flex",alignItems:"center"}}>📂 Artifacts</span>
<span onClick={()=>{setRightPanel(rightPanel==="inspector"?null:"inspector");setDetailId(null);}} style={{fontSize:10,color:rightPanel==="inspector"?c.accent:c.textSec,cursor:"pointer",padding:"3px 8px",borderRadius:4,border:`1px solid ${rightPanel==="inspector"?c.accent+"40":c.border}`,background:rightPanel==="inspector"?c.accent+"10":"transparent",height:22,boxSizing:"border-box",display:"flex",alignItems:"center"}}>⇄ Inspector</span>
<span onClick={()=>{setRightPanel(rightPanel==="source"?null:"source");setDetailId(null);}} style={{fontSize:10,color:rightPanel==="source"?c.green:c.textSec,cursor:"pointer",padding:"3px 8px",borderRadius:4,border:`1px solid ${rightPanel==="source"?c.green+"40":c.border}`,background:rightPanel==="source"?c.green+"10":"transparent",height:22,boxSizing:"border-box",display:"flex",alignItems:"center"}}>◇ Sources</span>
<span onClick={()=>{setRightPanel(rightPanel==="forum"?null:"forum");setDetailId(null);}} style={{fontSize:10,color:rightPanel==="forum"?c.cyan:c.textSec,cursor:"pointer",padding:"3px 8px",borderRadius:4,border:`1px solid ${rightPanel==="forum"?c.cyan+"40":c.border}`,background:rightPanel==="forum"?c.cyan+"10":"transparent",height:22,boxSizing:"border-box",display:"flex",alignItems:"center"}}>☷ Board</span>
<span onClick={()=>{setRightPanel(rightPanel==="agent"?null:"agent");setDetailId(null);}} style={{fontSize:10,color:rightPanel==="agent"?c.purple:c.textSec,cursor:"pointer",padding:"3px 8px",borderRadius:4,border:`1px solid ${rightPanel==="agent"?c.purple+"50":c.border}`,background:rightPanel==="agent"?c.purple+"10":"transparent",height:22,boxSizing:"border-box",display:"flex",alignItems:"center"}}>✦ Task Agent</span>
</div>
<div style={{flex:1,display:"flex",justifyContent:"center"}}>
{editingTaskName?(
<input autoFocus value={taskName} onChange={e=>setTaskName(e.target.value)}
onBlur={()=>setEditingTaskName(false)} onKeyDown={e=>{if(e.key==="Enter")setEditingTaskName(false);}}
style={{fontSize:14,fontWeight:600,fontFamily:sans,background:c.bgCard,border:`1px solid ${c.accent}`,borderRadius:4,color:c.text,padding:"2px 12px",textAlign:"center",outline:"none",minWidth:200}} />
):(
<span onDoubleClick={()=>setEditingTaskName(true)} style={{fontSize:14,fontWeight:600,fontFamily:sans,cursor:"text",padding:"2px 12px",borderRadius:4,border:"1px solid transparent"}}
onMouseEnter={e=>e.currentTarget.style.borderColor=c.border} onMouseLeave={e=>e.currentTarget.style.borderColor="transparent"}>
{taskName}
</span>
)}
</div>
<div style={{display:"flex",alignItems:"center",gap:4,justifyContent:"flex-end",minWidth:320}}>
{connecting&&<span style={{fontSize:9,color:c.amber,padding:"3px 8px",background:`${c.amber}15`,borderRadius:4,border:`1px solid ${c.amber}40`,height:22,boxSizing:"border-box",display:"flex",alignItems:"center"}}>● Click input · ESC cancel</span>}
<div style={{position:"relative"}}>
<Btn onClick={()=>setTaskSettings(!taskSettings)}>⚙ Settings</Btn>
{taskSettings&&(
<div style={{position:"absolute",top:36,right:0,width:340,background:c.bgPopup,border:`1px solid ${c.border}`,borderRadius:8,padding:14,zIndex:40,boxShadow:"0 8px 24px #0008",maxHeight:"80vh",overflowY:"auto"}}>
<div style={{fontSize:11,fontWeight:600,color:c.text,marginBottom:10,fontFamily:sans}}>Task Settings</div>
<DL>Default Agent</DL>
<DS opts={["Named agent","Spawn new"]} v="Named agent" />
<DS opts={["Elnor","Nova","Prism","Atlas","Sage"]} v="Elnor" />
<div style={{display:"grid",gridTemplateColumns:"1fr 1fr",gap:8,marginTop:6}}>
<div><DL>Model</DL><DS opts={["(agent default)","claude-opus-4-6","claude-sonnet-4-6","gemini-2.5-pro"]} v="(agent default)" /></div>
<div><DL>Think Level</DL><DS opts={["(agent default)","off","minimal","low","medium","high","xhigh"]} v="(agent default)" /></div>
</div>
<div style={{fontSize:9,color:c.textTer,marginTop:4,lineHeight:1.4}}>All modules set to "Task Default" use this agent. Override per-module in each module's config.</div>
<div style={{borderTop:`1px solid ${c.border}`,marginTop:10,paddingTop:10}} />
<DL>Concurrency Policy</DL>
<DS opts={["Queue (run one at a time)","Parallel (allow multiple instances)","Skip if already running","Cancel previous, start new"]} v="Queue (run one at a time)" />
<DL mt>Per-Run Cost Ceiling ($)</DL>
<DI value="" placeholder="no limit" />
<DL mt>Daily Cost Ceiling ($)</DL>
<DI value="" placeholder="no limit" />
<DL mt>Total Lifetime Cost Ceiling ($)</DL>
<DI value="" placeholder="no limit" />
<DL mt>On Any Ceiling Hit</DL>
<DS opts={["Pause task and notify (default)","Cancel task and notify"]} v="Pause task and notify (default)" />
<DL mt>Restrict Outbound Email</DL>
<DS opts={["Explicitly defined addresses in this task only (safest)","Allowed domains below","Use global security settings"]} v="Explicitly defined addresses in this task only (safest)" />
<DI value="" placeholder="allowed domains if selected above" />
<DL mt>Restrict File Output Paths 📁</DL>
<DI value="" placeholder="blank = use global security settings" />
<div style={{borderTop:`1px solid ${c.border}`,marginTop:10,paddingTop:10}} />
<DL>Error Handling Defaults</DL>
<div style={{display:"grid",gridTemplateColumns:"1fr 1fr",gap:8,marginTop:4}}>
<div><DL>Default Retries</DL><DS opts={["0 (no retry)","1","2","3","5"]} v="0 (no retry)" /></div>
<div><DL>Backoff (min)</DL><DI value="1" placeholder="minutes" /></div>
</div>
<DL mt>Retry On</DL>
<DS opts={["Retryable errors only (timeout, rate limit)","All errors"]} v="Retryable errors only (timeout, rate limit)" />
<DL mt>Default Call Timeout (min)</DL>
<DI value="" placeholder="blank = per-module defaults" />
<div style={{fontSize:8,color:c.textTer,marginTop:2}}>Defaults: Agent Task 20min, Eval/Judgment 12min, Panel/RedTeam off</div>
<DL mt>On Unhandled Error (error_out not wired)</DL>
<DS opts={["Pause task and notify","Fail task"]} v="Pause task and notify" />
<div style={{fontSize:9,color:c.textTer,marginTop:4,lineHeight:1.4}}>Retries must be explicit — use RetryConfig per module or wire error_out to a retry path. Model fallback is never an error.</div>
</div>
)}
</div>
<span style={{fontSize:9,color:c.amber,padding:"3px 8px",borderRadius:4,border:`1px solid ${c.amber}40`,background:`${c.amber}10`,fontWeight:600,cursor:"pointer",height:22,boxSizing:"border-box",display:"flex",alignItems:"center"}} title="2 events queued — click Settings to view">⏳ 2</span>
<span style={{width:1,height:16,background:c.border}} />
<div style={{display:"flex",gap:2}}>
<Btn>Load</Btn>
<Btn>Save</Btn>
<Btn onClick={()=>setTaskName(taskName+" (1)")}>Dupe</Btn>
</div>
<div style={{minWidth:72,display:"flex",justifyContent:"flex-end"}}>
{exec==="idle"&&<Btn primary onClick={()=>setExec("running")}>▶ Run</Btn>}
{exec==="running"&&<span style={{fontSize:9,color:c.accent,display:"flex",alignItems:"center",gap:4,padding:"3px 8px"}}><span style={{width:5,height:5,borderRadius:"50%",background:c.accent,animation:"pulse 1.5s infinite"}} />Running</span>}
{exec==="paused"&&<Btn primary onClick={()=>setExec("running")}>▶ Resume</Btn>}
{exec==="complete"&&<Btn onClick={()=>setExec("idle")}>↺ Reset</Btn>}
</div>
</div>
</div>
{/* ═══ BODY ═══ */}
<div style={{display:"flex",flex:1,overflow:"hidden",position:"relative"}}>
{/* ── PALETTE BAR ── */}
<div style={{width:58,borderRight:`1px solid ${c.border}`,display:"flex",flexDirection:"column",alignItems:"center",padding:"10px 0",gap:2,flexShrink:0}}>
{Object.keys(catColor).map(cat=>(
<button key={cat} onClick={()=>setPalette(palette===cat?null:cat)} style={{
width:48,padding:"5px 0",background:palette===cat?`${catColor[cat]}15`:"transparent",
border:`1px solid ${palette===cat?catColor[cat]+"50":"transparent"}`,
borderRadius:5,cursor:"pointer",display:"flex",flexDirection:"column",alignItems:"center",gap:2
}}>
<span style={{fontSize:15,lineHeight:1}}>{catIcon[cat]}</span>
<span style={{fontSize:10,color:catColor[cat],fontWeight:600,letterSpacing:0.3,opacity:palette===cat?1:0.85}}>
{catLabel[cat]}
</span>
</button>
))}
</div>
{/* ── PALETTE POPOVER ── */}
{palette&&(
<div style={{width:200,borderRight:`1px solid ${c.border}`,background:c.bgPanel,overflowY:"auto",flexShrink:0,padding:"8px 0"}}>
<div style={{padding:"4px 12px 8px",fontSize:12,color:catColor[palette],fontWeight:600}}>
{catIcon[palette]} {catLabel[palette]}
</div>
{(palTypes[palette]||[]).map((item,i)=>(
<div key={i} onClick={()=>addModule(item.t.split(".")[0],item.t,item.n)}
style={{padding:"7px 12px",cursor:"pointer",borderLeft:"3px solid transparent"}}
onMouseEnter={e=>{e.currentTarget.style.background=c.bgHover;e.currentTarget.style.borderLeftColor=catColor[palette];}}
onMouseLeave={e=>{e.currentTarget.style.background="transparent";e.currentTarget.style.borderLeftColor="transparent";}}>
<div style={{fontSize:13,color:c.text,fontFamily:sans,fontWeight:500}}>{item.n}</div>
<div style={{fontSize:11,color:c.textSec,marginTop:1}}>{item.d}</div>
</div>
))}
</div>
)}
{/* ═══ CANVAS ═══ */}
<div ref={canvasRef} style={{flex:1,overflow:"auto",position:"relative"}}
onMouseMove={onMouseMove} onMouseUp={onMouseUp} onMouseDown={onCanvasDown}
onClick={onCanvasClick} onKeyDown={e=>{if(e.key==="Escape"){setConnecting(null);setPopup(null);}}} tabIndex={0}>
<svg width={svgW*scale} height={svgH*scale} style={{minWidth:"100%",minHeight:"100%"}}>
<g transform={`scale(${scale}) translate(${pan.x},${pan.y})`}>
<defs><pattern id="dots6" width="40" height="40" patternUnits="userSpaceOnUse"><circle cx="20" cy="20" r="0.6" fill={c.border}/></pattern></defs>
<rect x={-500} y={-500} width={5000} height={3000} fill="url(#dots6)" />
{/* Grid dots */}
{/* CABLES */}
{cables.map((cb,ci)=>{
const fm=modules.find(m=>m.id===cb.from);const tm=modules.find(m=>m.id===cb.to);
if(!fm||!tm) return null;
const fp=primaryOutPos(fm);const tp=primaryInPos(tm);
const sf=cables.filter(c2=>c2.from===cb.from);const iF=sf.indexOf(cb);const oF=(iF-(sf.length-1)/2)*6;
const st2=cables.filter(c2=>c2.to===cb.to);const iT=st2.indexOf(cb);const oT=(iT-(st2.length-1)/2)*6;
const path=bez(fp.x,fp.y+oF,tp.x,tp.y+oT);
const col=getCableColor(cb,ci);
const isHi=hovCable===cb.id||selCables.has(cb.id);
const isDim=(selected&&!selCables.has(cb.id))||(hovCable&&hovCable!==cb.id);
const fst=es[cb.from];const isLive=fst==="done"||fst==="active";
return(
<g key={cb.id} onMouseEnter={()=>setHovCable(cb.id)} onMouseLeave={()=>setHovCable(null)}>
<path d={path} fill="none" stroke="transparent" strokeWidth={14} style={{cursor:"pointer"}}
onClick={e=>{e.stopPropagation();setCables(prev=>prev.filter(c2=>c2.id!==cb.id));}} />
<path d={path} fill="none" stroke={isHi?"#fff":cb.role==="target"?c.orange:col}
strokeWidth={isHi?2.5:cb.role==="signal"?1.2:cb.role==="target"?2:1.8}
strokeDasharray={cb.role==="signal"?"6,5":cb.role==="target"?"8,4":"none"}
opacity={isDim?0.25:isHi?1:isLive?0.65:0.4}
style={{transition:"opacity 0.15s"}} />
{isLive&&!isHi&&exec!=="paused"&&<circle r={3} fill={col} opacity={0.7}><animateMotion dur="2s" repeatCount="indefinite" path={path}/></circle>}
</g>
);
})}
{/* Wiring preview */}
{connecting && modules.find(m=>m.id===connecting.modId) && (
<path d={bez(primaryOutPos(modules.find(m=>m.id===connecting.modId)).x, primaryOutPos(modules.find(m=>m.id===connecting.modId)).y, mouseXY.x, mouseXY.y)} fill="none" stroke={c.amber} strokeWidth={2} strokeDasharray="8,5" opacity={0.6} />
)}
{/* MODULES */}
{modules.map(mod=>{
const w=modW(mod);const h=modH(mod);const col=catColor[mod.cat]||c.textTer;
const st=es[mod.id];const isSel=selected===mod.id;
const isDim=selected&&!selMods.has(mod.id);
const isEnv=mod.cat==="environment";const isSys=mod.cat==="system";
const isConnTarget=connecting&&mod.id!==connecting.modId&&!isEnv&&!isSys;
if(mod.isMult){
const mh=60;
// Compute pass-through label: what feeds in and what port it goes to
const multIns=cables.filter(cb=>cb.to===mod.id);
const multOut=cables.find(cb=>cb.from===mod.id);
const destMod=multOut?modules.find(m=>m.id===multOut.to):null;
const destPort=multOut?multOut.toPort||"data_in":"?";
const srcNames=multIns.map(cb=>{const s=modules.find(m=>m.id===cb.from);return s?s.name:cb.from;}).join(", ");
return(
<g key={mod.id} style={{opacity:isDim?0.35:1,cursor:"grab"}}
onMouseDown={e=>startDrag(e,mod)} onClick={e=>{e.stopPropagation();setSelected(mod.id);}}
onDoubleClick={e=>{e.stopPropagation();setDetailId(mod.id);}}>
<rect x={mod.x} y={mod.y} width={18} height={mh} rx={4} fill={c.bgCard} stroke={isSel?c.purple:c.border} strokeWidth={isSel?2:1} />
<rect x={mod.x} y={mod.y} width={18} height={3} rx={4} fill={c.purple} opacity={0.8} />
<rect x={mod.x} y={mod.y+2} width={18} height={1} fill={c.purple} opacity={0.8} />
{[0,1,2].map(i=><circle key={i} cx={mod.x+9} cy={mod.y+15+i*14} r={2.5} fill={c.purple} opacity={0.7}/>)}
{/* Pass-through label */}
<text x={mod.x+9} y={mod.y+mh+10} fontSize={7.5} fill={c.textTer} fontFamily={mono} textAnchor="middle">{destPort}</text>
{destMod&&<text x={mod.x+9} y={mod.y+mh+20} fontSize={7} fill={c.purple} fontFamily={mono} textAnchor="middle" opacity={0.7}>→ {destMod.name}</text>}
<g onClick={e=>{if(connecting)finishConnect(e,mod);}} style={{cursor:isConnTarget?"pointer":"default"}}>
<circle cx={mod.x} cy={mod.y+mh/2} r={isConnTarget?12:6} fill={isConnTarget?`${c.amber}20`:"transparent"}
stroke={isConnTarget?c.amber:isSel?c.purple:c.border} strokeWidth={isConnTarget?2:1.5} />
<polygon points={`${mod.x-2},${mod.y+mh/2-2.5} ${mod.x+1.5},${mod.y+mh/2} ${mod.x-2},${mod.y+mh/2+2.5}`}
fill={isConnTarget?c.amber:c.textSec} />
</g>
<g onClick={e=>startConnect(e,mod)} style={{cursor:"pointer"}}>
<circle cx={mod.x+18} cy={mod.y+mh/2} r={6} fill="transparent" stroke={c.purple} strokeWidth={1.5} />
<polygon points={`${mod.x+16},${mod.y+mh/2-2.5} ${mod.x+20},${mod.y+mh/2} ${mod.x+16},${mod.y+mh/2+2.5}`} fill={c.purple} />
</g>
</g>
);
}
return(
<g key={mod.id} style={{opacity:isDim?0.2:1,transition:"opacity 0.15s",cursor:"grab"}}
onMouseDown={e=>startDrag(e,mod)} onClick={e=>{e.stopPropagation();setSelected(mod.id);}}>
{/* Active glow / paused indicator */}
{st==="active"&&exec!=="paused"&&<rect x={mod.x-3} y={mod.y-3} width={w+6} height={h+6} rx={9} fill="none" stroke={c.accent} strokeWidth={1.5} opacity={0.4} style={{animation:"pulse 2s infinite"}} />}
{st==="active"&&exec==="paused"&&<rect x={mod.x-3} y={mod.y-3} width={w+6} height={h+6} rx={9} fill="none" stroke={c.amber} strokeWidth={1.5} opacity={0.5} />}
<rect x={mod.x+2} y={mod.y+2} width={w} height={h} rx={6} fill="#000" opacity={0.2} />
<rect x={mod.x} y={mod.y} width={w} height={h} rx={6}
fill={isSel?c.bgSel:c.bgCard}
stroke={isSel?col:isConnTarget?c.amber:st==="active"?c.accent:isEnv||isSys?c.textTer:c.border}
strokeWidth={isSel?2:isConnTarget?1.5:st==="active"?1.5:1}
strokeDasharray={isEnv||isSys?"5,3":"none"} />
<rect x={mod.x} y={mod.y} width={w} height={4} rx={6} fill={col} opacity={st==="done"?1:st==="active"?0.8:0.35} />
<rect x={mod.x} y={mod.y+2} width={w} height={2} fill={col} opacity={st==="done"?1:st==="active"?0.8:0.35} />
{st&&st!=="na"&&st!=="wait"&&<rect x={mod.x+w-3} y={mod.y} width={3} height={h} rx={1.5} fill={stCol[st]} opacity={0.5}/>}
{editingName===mod.id?(
<foreignObject x={mod.x+10} y={mod.y+8} width={w-30} height={22}>
<input autoFocus defaultValue={mod.name}
onBlur={e=>renameMod(mod.id,e.target.value)} onKeyDown={e=>{if(e.key==="Enter")renameMod(mod.id,e.target.value);}}
style={{width:"100%",background:c.bg,border:`1px solid ${c.accent}`,borderRadius:3,color:c.text,fontFamily:sans,fontSize:12,fontWeight:600,padding:"1px 4px",outline:"none",boxSizing:"border-box"}}
onClick={e=>e.stopPropagation()} />
</foreignObject>
):(
<text x={mod.x+10} y={mod.y+22} fontSize={12} fontFamily={sans} fill={c.text}
onDoubleClick={e=>{e.stopPropagation();setEditingName(mod.id);}} style={{cursor:"text"}}>
<tspan fontFamily={mono} fontSize={8.5} fill={col} opacity={0.9}>{catLabel[mod.cat]}: </tspan>
<tspan fontWeight={600}>{(()=>{const avail=w-22-(catLabel[mod.cat].length+2)*5.0;const maxChars=Math.floor(avail/7.2);return mod.name.length>maxChars?mod.name.slice(0,maxChars)+"…":mod.name;})()}</tspan>
</text>
)}
{!isEnv&&!isSys&&<text x={mod.x+10} y={mod.y+37} fontSize={9} fill={col} opacity={0.7} fontFamily={mono}>
{mod.type.split(".").pop().replace(/_/g," ")}
</text>}
{/* Running / Done / Waiting / Error / Finishing badge — bottom left */}
{st==="active"&&exec!=="paused"&&<text x={mod.x+10} y={mod.y+h-8} fontSize={8} fill={c.accent} fontFamily={mono} style={{animation:"pulse 2s infinite"}}>⟳ running</text>}
{st==="active"&&exec==="paused"&&<text x={mod.x+10} y={mod.y+h-8} fontSize={8} fill={c.amber} fontFamily={mono}>⟳ finishing...</text>}
{st==="done"&&<text x={mod.x+10} y={mod.y+h-8} fontSize={8} fill={c.green} fontFamily={mono}>✓ done{mod.runCount>1?` ${mod.runCount}x`:""}</text>}
{st==="wait"&&exec==="paused"&&<text x={mod.x+10} y={mod.y+h-8} fontSize={8} fill={c.amber} fontFamily={mono}>⏸ paused</text>}
{st==="wait"&&exec!=="paused"&&<text x={mod.x+10} y={mod.y+h-8} fontSize={8} fill={c.textTer} fontFamily={mono}>◦ waiting</text>}
{st==="error"&&<text x={mod.x+10} y={mod.y+h-8} fontSize={8} fill={c.red} fontFamily={mono}>✕ error</text>}
{/* Validation badges — bottom left, visible only when idle (no execution state) */}
{exec==="idle"&&mod.validationWarning&&!mod.validationError&&<text x={mod.x+10} y={mod.y+h-8} fontSize={9} fill={c.amber} fontFamily={mono}>⚠ empty instruction</text>}
{exec==="idle"&&mod.validationError&&<text x={mod.x+10} y={mod.y+h-8} fontSize={9} fill={c.red} fontFamily={mono}>⛔ config error</text>}
{/* Outcome Evaluator / Source Workspace / Forum badges */}
{mod.type==="step.outcome_evaluator"&&exec==="idle"&&<text x={mod.x+10} y={mod.y+h-8} fontSize={8} fill={c.purple} fontFamily={mono}>◆ outcome: {mod.outcomeState||"configured"}</text>}
{mod.type==="step.outcome_evaluator"&&st==="active"&&<text x={mod.x+10} y={mod.y+h-8} fontSize={8} fill={c.purple} fontFamily={mono}>⟳ evaluating + routing feedback</text>}
{mod.type==="step.outcome_evaluator"&&st==="done"&&<text x={mod.x+10} y={mod.y+h-8} fontSize={8} fill={c.green} fontFamily={mono}>✓ feedback bundle emitted</text>}
{mod.type==="system.source_workspace"&&exec==="idle"&&<text x={mod.x+10} y={mod.y+h-8} fontSize={8} fill={c.green} fontFamily={mono}>◇ shared sources · tiered</text>}
{mod.type==="system.source_workspace"&&st&&st!=="na"&&<text x={mod.x+10} y={mod.y+h-8} fontSize={8} fill={st==="active"?c.accent:c.green} fontFamily={mono}>◇ 42 refs · 11 notes · 6 needs</text>}
{mod.type==="system.task_forum"&&exec==="idle"&&<text x={mod.x+10} y={mod.y+h-8} fontSize={8} fill={c.cyan} fontFamily={mono}>☷ board + optional forum</text>}
{mod.type==="system.task_forum"&&st&&st!=="na"&&<text x={mod.x+10} y={mod.y+h-8} fontSize={8} fill={st==="active"?c.accent:c.cyan} fontFamily={mono}>☷ 18 posts · 3 open repairs</text>}
{/* Experiment variant dots */}
{mod.type==="system.experiment"&&mod.expVariants&&exec==="idle"&&(
<g>
{mod.expVariants.map((v,vi)=>(
<circle key={vi} cx={mod.x+14+vi*12} cy={mod.y+h-10} r={4}
fill={[c.accent,c.amber,c.purple,c.green][vi]||c.textTer} opacity={0.8} />
))}
<text x={mod.x+14+mod.expVariants.length*12+4} y={mod.y+h-7} fontSize={8} fill={c.textSec} fontFamily={mono}>{mod.expVariants.length} variants</text>
</g>
)}
{mod.type==="system.experiment"&&st==="active"&&<text x={mod.x+10} y={mod.y+h-8} fontSize={8} fill={c.accent} fontFamily={mono}>⟳ {mod.expVariants?.length||2} variants running</text>}
{mod.type==="system.experiment"&&st==="done"&&<text x={mod.x+10} y={mod.y+h-8} fontSize={8} fill={c.green} fontFamily={mono}>✓ {mod.expVariants?.length||2} variants complete</text>}
{/* Judge score badges */}
{mod.type==="step.judge"&&mod.judgeScores&&exec==="idle"&&(
<g>
<text x={mod.x+10} y={mod.y+h-8} fontSize={8} fill={c.green} fontFamily={mono}>
⚖ A:{mod.judgeScores.a} B:{mod.judgeScores.b}{mod.judgeScores.c?` C:${mod.judgeScores.c}`:""}
</text>
</g>
)}
{mod.type==="step.judge"&&st==="active"&&<text x={mod.x+10} y={mod.y+h-8} fontSize={8} fill={c.accent} fontFamily={mono}>⟳ scoring</text>}
{mod.type==="step.judge"&&st==="done"&&<text x={mod.x+10} y={mod.y+h-8} fontSize={8} fill={c.green} fontFamily={mono}>✓ scored · 0.85 avg</text>}
{/* Session continuity indicator */}
{mod.sessionContinue&&exec==="idle"&&<text x={mod.x+w-28} y={mod.y+13} fontSize={10} fill={c.cyan} opacity={0.8}>⟲</text>}
{/* Pin indicator (Carry Forward) — amber arrow, upper right */}
{mod.pinned&&!isEnv&&!isSys&&<text x={mod.x+w-16} y={mod.y+13} fontSize={14} fontWeight={700} fill={c.amber} opacity={0.95}>›</text>}
{/* INPUT PORT */}
{!isEnv&&!isSys&&(
<g onClick={e=>{if(connecting)finishConnect(e,mod);}} style={{cursor:isConnTarget?"pointer":"default"}}>
<circle cx={mod.x} cy={mod.y+h/2} r={isConnTarget?16:12} fill="transparent" />
<circle cx={mod.x} cy={mod.y+h/2} r={isConnTarget?9:6}
fill={isConnTarget?`${c.amber}30`:st==="done"?`${c.green}30`:"transparent"}
stroke={isConnTarget?c.amber:isSel?col:st==="done"?c.green:c.border}
strokeWidth={isConnTarget?2.5:1.5} />
{isConnTarget&&<circle cx={mod.x} cy={mod.y+h/2} r={14} fill="none" stroke={c.amber} strokeWidth={1} opacity={0.3} style={{animation:"pulse 1s infinite"}}/>}
<polygon points={`${mod.x-2.5},${mod.y+h/2-2.5} ${mod.x+1.5},${mod.y+h/2} ${mod.x-2.5},${mod.y+h/2+2.5}`}
fill={isConnTarget?c.amber:isSel?col:c.textTer} />
</g>
)}
{/* OUTPUT PORT */}
{!isEnv&&!isSys&&(
<g onClick={e=>startConnect(e,mod)} style={{cursor:"pointer"}}>
<circle cx={mod.x+w} cy={mod.y+h/2} r={12} fill="transparent" />
<circle cx={mod.x+w} cy={mod.y+h/2} r={6}
fill={connecting?.modId===mod.id?`${col}40`:st==="done"?`${c.green}30`:"transparent"}
stroke={connecting?.modId===mod.id?col:isSel?col:st==="done"?c.green:c.border}
strokeWidth={connecting?.modId===mod.id?2.5:1.5} />
<polygon points={`${mod.x+w-2},${mod.y+h/2-2.5} ${mod.x+w+2.5},${mod.y+h/2} ${mod.x+w-2},${mod.y+h/2+2.5}`}
fill={isSel?col:c.textTer} />
</g>
)}
{/* Signal/error ports are now included in the output selection popup — no separate dots needed */}
<rect x={mod.x} y={mod.y} width={w} height={h} rx={6} fill="transparent"
onDoubleClick={e=>{e.stopPropagation();setDetailId(mod.id);}} />
</g>
);
})}
{/* POPUP */}
{popup&&popup.choices&&(
<foreignObject x={popup.x-10} y={Math.max(10,popup.y-10)} width={250} height={600}>
<div style={{background:c.bgPopup,border:`1px solid ${c.border}`,borderRadius:8,padding:8,boxShadow:"0 8px 24px #0008",maxHeight:580,overflowY:"auto"}}>
<div style={{fontSize:10,color:c.textTer,marginBottom:6,fontWeight:600}}>
{popup.type==="output_select"?"Select output:":"Assign input role:"}
</div>
{popup.choices.map(ch=>(
<div key={ch.id} onClick={()=>popup.then(ch.id)}
style={{padding:"7px 10px",borderRadius:5,cursor:"pointer",marginBottom:3,fontSize:12,color:ch.color||c.text,border:`1px solid ${c.border}`}}
onMouseEnter={e=>e.currentTarget.style.background=c.bgHover}
onMouseLeave={e=>e.currentTarget.style.background="transparent"}>
<div style={{fontWeight:600}}>{ch.label}</div>
{ch.desc&&<div style={{fontSize:10,color:c.textSec,marginTop:2}}>{ch.desc}</div>}
</div>
))}
</div>
</foreignObject>
)}
</g>
</svg>
</div>
{/* ═══ CONNECTIONS INFO (V4 style, bottom-left) ═══ */}
{selected && !popup && !detailId && <ConnInfo sel={selected} modules={modules} cables={cables} />}
{/* ═══ DETAIL PANEL ═══ */}
{detailId&&detailMod&&(
<div style={{width:360,borderLeft:`1px solid ${c.border}`,background:c.bgPanel,overflowY:"auto",flexShrink:0,display:"flex",flexDirection:"column"}}>
{/* Header with Done */}
<div style={{padding:"12px 16px",borderBottom:`1px solid ${c.border}`,display:"flex",justifyContent:"space-between",alignItems:"center",flexShrink:0}}>
<div>
<div style={{fontSize:8.5,color:catColor[detailMod.cat]||c.textTer,fontWeight:600,letterSpacing:0.4}}>{catLabel[detailMod.cat]}</div>
<div style={{fontSize:9,color:c.textTer,marginTop:1,fontFamily:mono}}>{detailMod.type}</div>
</div>
<button onClick={()=>setDetailId(null)} style={{padding:"4px 14px",background:`${c.accent}15`,border:`1px solid ${c.accent}40`,borderRadius:4,color:c.accent,fontSize:10,fontWeight:600,cursor:"pointer",fontFamily:mono}}>
✓ Done
</button>
</div>
<div style={{flex:1,overflowY:"auto"}}>
{/* Review mode toggle for Human Review modules */}
{detailMod.type==="step.user_review_gate"&&(
<div style={{display:"flex",borderBottom:`1px solid ${c.border}`}}>
<button onClick={()=>setReviewMode(false)} style={{flex:1,padding:"8px 0",background:!reviewMode?`${c.accent}10`:"transparent",border:"none",borderBottom:!reviewMode?`2px solid ${c.accent}`:`2px solid transparent`,color:!reviewMode?c.accent:c.textTer,fontSize:10,fontWeight:600,cursor:"pointer",fontFamily:mono}}>Configure</button>
<button onClick={()=>setReviewMode(true)} style={{flex:1,padding:"8px 0",background:reviewMode?`${c.amber}10`:"transparent",border:"none",borderBottom:reviewMode?`2px solid ${c.amber}`:`2px solid transparent`,color:reviewMode?c.amber:c.textTer,fontSize:10,fontWeight:600,cursor:"pointer",fontFamily:mono}}>Review Card</button>
</div>
)}
{/* ═══ REVIEW CARD (shown when review mode active) ═══ */}
{detailMod.type==="step.user_review_gate"&&reviewMode?(
<div style={{padding:0}}>
{/* Status bar */}
<div style={{padding:"10px 16px",background:`${c.amber}08`,borderBottom:`1px solid ${c.amber}30`,display:"flex",alignItems:"center",gap:8}}>
<span style={{width:8,height:8,borderRadius:"50%",background:c.amber,animation:"pulse 1.5s infinite"}} />
<span style={{fontSize:11,color:c.amber,fontWeight:600}}>Awaiting Your Review</span>
</div>
{/* Review instructions */}
<div style={{padding:"12px 16px",borderBottom:`1px solid ${c.border}`}}>
<div style={{fontSize:9,fontWeight:600,color:c.textTer,textTransform:"uppercase",letterSpacing:0.4,marginBottom:6}}>REVIEW INSTRUCTIONS</div>
<div style={{fontSize:11,color:c.text,lineHeight:1.6,padding:"8px 10px",background:c.bg,borderRadius:4,border:`1px solid ${c.border}`}}>
Check this filing analysis for accuracy. Verify all CCP deadlines are correct. Confirm the SoL calculations reference the right code sections.
</div>
</div>
{/* Output to review */}
<div style={{padding:"12px 16px",borderBottom:`1px solid ${c.border}`}}>
<div style={{fontSize:9,fontWeight:600,color:c.textTer,textTransform:"uppercase",letterSpacing:0.4,marginBottom:6}}>OUTPUT TO REVIEW</div>
<div style={{fontSize:10,color:c.textSec,lineHeight:1.6,padding:"10px",background:c.bg,borderRadius:4,border:`1px solid ${c.border}`,maxHeight:200,overflowY:"auto",fontFamily:mono}}>
<div style={{color:c.text,fontWeight:600,marginBottom:6}}>Filing Analysis — Henderson v. City of LA</div>
<div style={{marginBottom:4}}>1. Motion to Compel (CCP §2025.450)</div>
<div style={{marginBottom:4,paddingLeft:12,color:c.textSec}}>Response deadline: 15 days from service</div>
<div style={{marginBottom:4,paddingLeft:12,color:c.textSec}}>Calculated due: April 12, 2026</div>
<div style={{marginBottom:4}}>2. Demurrer (CCP §430.40)</div>
<div style={{marginBottom:4,paddingLeft:12,color:c.textSec}}>Response deadline: 30 days from service</div>
<div style={{marginBottom:4,paddingLeft:12,color:c.textSec}}>Calculated due: April 27, 2026</div>
<div style={{marginTop:8,fontSize:9,color:c.textTer}}>… (scrollable — full output shown in review page)</div>
</div>
<button style={{marginTop:6,padding:"5px 10px",background:"transparent",border:`1px solid ${c.border}`,borderRadius:4,color:c.textSec,fontSize:9,cursor:"pointer",fontFamily:mono}}>↗ Open full output in Document Viewer</button>
</div>
{/* Task context */}
<div style={{padding:"10px 16px",borderBottom:`1px solid ${c.border}`}}>
<div style={{fontSize:9,fontWeight:600,color:c.textTer,textTransform:"uppercase",letterSpacing:0.4,marginBottom:4}}>TASK CONTEXT</div>
<div style={{fontSize:10,color:c.textSec,display:"grid",gridTemplateColumns:"70px 1fr",gap:"3px 8px"}}>
<span style={{color:c.textTer}}>Task:</span><span>Process Opposing Filing</span>
<span style={{color:c.textTer}}>Step:</span><span>Analyze Filing → Human Review</span>
<span style={{color:c.textTer}}>Run:</span><span style={{fontFamily:mono,fontSize:9}}>#run-2026-03-21-001</span>
<span style={{color:c.textTer}}>Revision:</span><span>1 of 3</span>
</div>
</div>
{/* Action buttons */}
<div style={{padding:"12px 16px",borderBottom:`1px solid ${c.border}`}}>
<button style={{width:"100%",padding:"10px 0",background:`${c.green}15`,border:`1px solid ${c.green}50`,borderRadius:6,color:c.green,fontSize:12,fontWeight:700,cursor:"pointer",fontFamily:sans,marginBottom:8}}>
✓ Approve
</button>
<div style={{display:"flex",gap:8}}>
<button style={{flex:1,padding:"8px 0",background:`${c.red}10`,border:`1px solid ${c.red}30`,borderRadius:5,color:c.red,fontSize:11,fontWeight:600,cursor:"pointer",fontFamily:sans}}>
✕ Reject
</button>
<button style={{flex:1,padding:"8px 0",background:`${c.amber}10`,border:`1px solid ${c.amber}30`,borderRadius:5,color:c.amber,fontSize:11,fontWeight:600,cursor:"pointer",fontFamily:sans}}>
↻ Revise
</button>
</div>
</div>
{/* Revision instructions (shown when Revise is clicked, always visible in mockup) */}
<div style={{padding:"12px 16px",borderBottom:`1px solid ${c.border}`}}>
<div style={{fontSize:9,fontWeight:600,color:c.amber,textTransform:"uppercase",letterSpacing:0.4,marginBottom:6}}>REVISION INSTRUCTIONS</div>
<textarea placeholder="What needs to change? These instructions go to the agent that produced this output."
style={{width:"100%",height:80,background:c.bg,border:`1px solid ${c.amber}40`,borderRadius:4,color:c.text,fontFamily:mono,fontSize:10,padding:8,resize:"vertical",outline:"none",boxSizing:"border-box",lineHeight:1.5}} />
<div style={{marginTop:6,padding:"8px",border:`1px dashed ${c.amber}30`,borderRadius:4,fontSize:9,color:c.amber,textAlign:"center",cursor:"pointer"}}>
📁 Attach edited or redlined files — drag or click to browse
</div>
<button style={{width:"100%",marginTop:6,padding:"8px 0",background:`${c.amber}15`,border:`1px solid ${c.amber}40`,borderRadius:5,color:c.amber,fontSize:11,fontWeight:600,cursor:"pointer",fontFamily:mono}}>
Send Revision + Attachments
</button>
<div style={{fontSize:9,color:c.textTer,marginTop:6,lineHeight:1.5}}>
Text + files route through revision_out to the receiving step. You can also reply to the notification email.
</div>
</div>
{/* Rejection reason (shown when Reject is clicked, always visible in mockup) */}
<div style={{padding:"12px 16px"}}>
<div style={{fontSize:9,fontWeight:600,color:c.red,textTransform:"uppercase",letterSpacing:0.4,marginBottom:6}}>REJECTION REASON</div>
<textarea placeholder="Why is this being rejected?"
style={{width:"100%",height:50,background:c.bg,border:`1px solid ${c.red}30`,borderRadius:4,color:c.text,fontFamily:mono,fontSize:10,padding:8,resize:"vertical",outline:"none",boxSizing:"border-box",lineHeight:1.5}} />
<button style={{width:"100%",marginTop:6,padding:"8px 0",background:`${c.red}10`,border:`1px solid ${c.red}30`,borderRadius:5,color:c.red,fontSize:11,fontWeight:600,cursor:"pointer",fontFamily:mono}}>
Confirm Rejection
</button>
</div>
</div>
):(
<React.Fragment>
{/* ═══ NORMAL CONFIG (non-review or configure tab) ═══ */}
{/* Expand button for complex modules */}
{(detailMod.type==="system.experiment"||detailMod.type==="step.judge")&&(
<div style={{padding:"8px 16px",borderBottom:`1px solid ${c.border}`,background:`${c.accent}08`}}>
<button onClick={()=>setExpandedOverlay(detailMod.type==="system.experiment"?"experiment":"judge")}
style={{width:"100%",padding:"10px 0",background:`${c.accent}15`,border:`1px solid ${c.accent}40`,borderRadius:6,color:c.accent,fontSize:12,fontWeight:700,cursor:"pointer",fontFamily:sans,display:"flex",alignItems:"center",justifyContent:"center",gap:6}}>
↗ Open Expanded View
</button>
<div style={{fontSize:9,color:c.textTer,marginTop:4,textAlign:"center"}}>Side-by-side variant editing and scoring results</div>
</div>
)}
<ModuleRunQuickAccess
mod={detailMod}
execState={exec}
onOpenOutput={()=>{setRightPanel("artifacts");setDetailId(null);setSelected(detailMod.id);}}
onOpenInspector={()=>{setRightPanel("inspector");setDetailId(null);setSelected(detailMod.id);}}
onAskTaskAgent={()=>{setRightPanel("agent");setDetailId(null);setSelected(detailMod.id);}}
/>
{/* Display Name — live-syncs to canvas */}
<div style={{padding:"10px 16px",borderBottom:`1px solid ${c.border}`}}>
<DL>Display Name</DL>
<input value={detailMod.name} onChange={e=>updateModName(detailMod.id,e.target.value)}
style={{width:"100%",padding:"6px 8px",background:c.bg,border:`1px solid ${c.border}`,borderRadius:4,color:c.text,fontFamily:mono,fontSize:11,outline:"none",boxSizing:"border-box"}} />
</div>
{/* Type switcher */}
{!detailMod.isMult&&typesByCat[detailMod.cat]&&typesByCat[detailMod.cat].length>1&&(
<div style={{padding:"10px 16px",borderBottom:`1px solid ${c.border}`}}>
<DL>Module Type</DL>
<select value={detailMod.type} onChange={e=>changeModType(detailMod.id,e.target.value)}
style={{width:"100%",padding:"6px 8px",background:c.bg,border:`1px solid ${c.border}`,borderRadius:4,color:c.text,fontFamily:mono,fontSize:11,outline:"none",boxSizing:"border-box"}}>
{typesByCat[detailMod.cat].map(t=><option key={t} value={t}>{typeNames[t]||t}</option>)}
</select>
</div>
)}
{/* Config */}
<div style={{padding:"10px 16px",borderBottom:`1px solid ${c.border}`}}>
<RenderFields fields={MC[detailMod.type]||[{t:"n",v:"No configuration for this module type."}]} />
{detailMod.cat==="trigger"&&detailMod.type!=="trigger.manual"&&<RenderFields fields={universalTrigger} />}
</div>
{/* ═══ PIN (CARRY FORWARD) TOGGLE — for non-environment/system modules ═══ */}
{detailMod.cat!=="environment"&&detailMod.cat!=="system"&&(
<div style={{padding:"8px 16px",borderBottom:`1px solid ${c.border}`,display:"flex",justifyContent:"space-between",alignItems:"center"}}>
<div>
<span style={{fontSize:10,fontWeight:600,color:c.text}}>📌 Carry Forward</span>
<div style={{fontSize:8,color:c.textTer,marginTop:1}}>Full fidelity in all downstream chain history</div>
</div>
<span onClick={()=>{const m=modules.map(mod=>mod.id===detailMod.id?{...mod,pinned:!mod.pinned}:mod);setModules(m);}}
style={{width:34,height:18,borderRadius:9,background:detailMod.pinned?c.amber:`${c.border}`,cursor:"pointer",display:"flex",alignItems:"center",padding:"0 2px",transition:"background 0.15s"}}>
<span style={{width:14,height:14,borderRadius:7,background:c.bgCard,boxShadow:"0 1px 3px #0003",transform:detailMod.pinned?"translateX(16px)":"translateX(0)",transition:"transform 0.15s"}} />
</span>
</div>
)}
{/* ═══ CONTEXT CONTROLS — for agent-capable modules ═══ */}
{["step.agent_task","step.agent_review_gate","step.panel","step.red_team"].includes(detailMod.type)&&(
<div style={{padding:"10px 16px",borderBottom:`1px solid ${c.border}`}}>
<DL>Context Controls</DL>
<DC label="Exclude chain history (prior step outputs)" checked={false} />
<div style={{marginTop:4}}><DC label="Exclude global instructions (from Environment)" checked={false} /></div>
<div style={{marginTop:6}}>
<DL mt>Chain History Override</DL>
<DS opts={["Environment default","Last N steps","None"]} v="Environment default" />
</div>
<div style={{marginTop:6}}>
<DL mt>Max Context Tokens</DL>
<DI value="" placeholder="blank = no cap" />
</div>
</div>
)}
{/* ═══ INCOMING DATA TABLE ═══ */}
{["step.agent_task","step.agent_review_gate","step.panel","step.red_team"].includes(detailMod.type)&&(
<IncomingDataTable modId={detailMod.id} modules={modules} cables={cables} />
)}
{/* ═══ OUTGOING DATA ═══ */}
{detailMod.cat!=="environment"&&detailMod.cat!=="system"&&(
<OutgoingDataSection modId={detailMod.id} modules={modules} cables={cables} />
)}
{/* Connections */}
<DetailConns modId={detailMod.id} cables={cables} modules={modules} />
{/* Actions */}
<div style={{padding:"10px 16px"}}>
<div style={{display:"flex",gap:6}}>
<button style={{flex:1,padding:"8px 0",background:`${c.accent}15`,border:`1px solid ${c.accent}40`,borderRadius:5,color:c.accent,fontSize:10,fontWeight:600,cursor:"pointer",fontFamily:mono}}>⚡ Test</button>
<button style={{flex:1,padding:"8px 0",background:`${c.amber}10`,border:`1px solid ${c.amber}30`,borderRadius:5,color:c.amber,fontSize:10,fontWeight:600,cursor:"pointer",fontFamily:mono,opacity:0.5}}>↻ Retry</button>
</div>
{detailMod.cat==="output"&&(
<div style={{position:"relative",marginTop:8}}>
<button onClick={()=>setAlsoSendTo(alsoSendTo?null:detailMod.id)}
style={{width:"100%",padding:"7px 0",background:"transparent",border:`1px solid ${c.border}`,borderRadius:5,color:c.textSec,fontSize:10,cursor:"pointer",fontFamily:mono}}>
+ Also send to…
</button>
{alsoSendTo===detailMod.id&&(
<div style={{position:"absolute",bottom:36,left:0,right:0,background:c.bgPopup,border:`1px solid ${c.border}`,borderRadius:6,padding:6,zIndex:30,boxShadow:"0 4px 16px #0005"}}>
{palTypes.output.map(ot=>(
<div key={ot.t} onClick={()=>alsoSendToCreate(detailMod,ot.t)}
style={{padding:"5px 8px",fontSize:10,color:c.text,cursor:"pointer",borderRadius:3}}
onMouseEnter={e=>e.currentTarget.style.background=c.bgHover}
onMouseLeave={e=>e.currentTarget.style.background="transparent"}>
{ot.n}
</div>
))}
</div>
)}
</div>
)}
<div style={{display:"flex",gap:6,marginTop:8}}>
<button onClick={()=>duplicateMod(detailMod)}
style={{flex:1,padding:"7px 0",background:"transparent",border:`1px solid ${c.border}`,borderRadius:5,color:c.textSec,fontSize:10,cursor:"pointer",fontFamily:mono}}>
⧉ Duplicate
</button>
{confirmDelete===detailMod.id?(
<div style={{flex:1,display:"flex",gap:4}}>
<button onClick={()=>deleteMod(detailMod.id)} style={{flex:1,padding:"7px 0",background:`${c.red}15`,border:`1px solid ${c.red}40`,borderRadius:5,color:c.red,fontSize:10,cursor:"pointer",fontFamily:mono}}>Confirm</button>
<button onClick={()=>setConfirmDelete(null)} style={{flex:1,padding:"7px 0",background:"transparent",border:`1px solid ${c.border}`,borderRadius:5,color:c.textSec,fontSize:10,cursor:"pointer",fontFamily:mono}}>Cancel</button>
</div>
):(
<button onClick={()=>setConfirmDelete(detailMod.id)}
style={{flex:1,padding:"7px 0",background:"transparent",border:`1px solid ${c.red}20`,borderRadius:5,color:c.red,fontSize:10,cursor:"pointer",fontFamily:mono,opacity:0.7}}>
✕ Delete
</button>
)}
</div>
<button style={{width:"100%",marginTop:6,padding:"7px 0",background:`${c.accent}10`,border:`1px solid ${c.accent}30`,borderRadius:5,color:c.accent,fontSize:10,cursor:"pointer",fontFamily:mono}}>
☆ Save as Preset
</button>
<div style={{display:"flex",justifyContent:"space-between",marginTop:10,fontSize:9,color:c.textTer}}>
<span style={{fontFamily:mono}}>{detailMod.id}</span>
<span style={{cursor:"pointer"}}>📋 Copy ID</span>
</div>
</div>
</React.Fragment>
)}
</div>
</div>
)}
{/* ═══ LOG PANEL (right side) ═══ */}
{!detailId&&rightPanel==="log"&&(
<div style={{width:360,borderLeft:`1px solid ${c.border}`,background:c.bgPanel,overflowY:"auto",flexShrink:0,display:"flex",flexDirection:"column"}}>
<div style={{padding:"12px 16px",borderBottom:`1px solid ${c.border}`,display:"flex",justifyContent:"space-between",alignItems:"center",flexShrink:0}}>
<span style={{fontSize:12,fontWeight:600,fontFamily:sans,color:c.text}}>Run Log</span>
<button onClick={()=>setRightPanel(null)} style={{padding:"4px 14px",background:`${c.accent}15`,border:`1px solid ${c.accent}40`,borderRadius:4,color:c.accent,fontSize:10,fontWeight:600,cursor:"pointer",fontFamily:mono}}>✕ Close</button>
</div>
<div style={{flex:1,overflowY:"auto",padding:0}}>
{[
{id:"run-015",status:"complete",time:"Mar 25, 2:14pm",dur:"4m 12s",cost:"$2.47",modules:9},
{id:"run-014",status:"complete",time:"Mar 24, 9:30am",dur:"3m 48s",cost:"$2.31",modules:9},
{id:"run-013",status:"error",time:"Mar 23, 4:15pm",dur:"1m 02s",cost:"$0.85",modules:4,err:"Timeout on Analyze Filing"},
{id:"run-012",status:"complete",time:"Mar 22, 11:00am",dur:"5m 33s",cost:"$3.10",modules:9},
{id:"run-011",status:"complete",time:"Mar 21, 2:45pm",dur:"3m 55s",cost:"$2.28",modules:9},
].map((run,i)=>(
<div key={i} style={{padding:"10px 16px",borderBottom:`1px solid ${c.border}`,cursor:"pointer"}}
onMouseEnter={e=>e.currentTarget.style.background=c.bgHover} onMouseLeave={e=>e.currentTarget.style.background="transparent"}>
<div style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:4}}>
<span style={{fontSize:10,fontWeight:600,color:c.text,fontFamily:mono}}>{run.id}</span>
<span style={{fontSize:8,padding:"2px 6px",borderRadius:3,
background:run.status==="complete"?`${c.green}15`:run.status==="error"?`${c.red}15`:`${c.amber}15`,
color:run.status==="complete"?c.green:run.status==="error"?c.red:c.amber,
border:`1px solid ${run.status==="complete"?c.green+"30":run.status==="error"?c.red+"30":c.amber+"30"}`
}}>{run.status}</span>
</div>
<div style={{display:"grid",gridTemplateColumns:"1fr 1fr",gap:"2px 8px",fontSize:9,color:c.textSec}}>
<span>{run.time}</span><span style={{textAlign:"right"}}>{run.dur}</span>
<span>{run.modules} modules</span><span style={{textAlign:"right",color:c.amber}}>{run.cost}</span>
</div>
{run.err&&<div style={{fontSize:8,color:c.red,marginTop:4,padding:"3px 6px",background:`${c.red}08`,borderRadius:3,border:`1px solid ${c.red}15`}}>{run.err}</div>}
<div style={{fontSize:8,color:c.accent,marginTop:4}}>Open in Run Inspector →</div>
</div>
))}
</div>
</div>
)}
{/* ═══ ARTIFACTS PANEL (right side) ═══ */}
{!detailId&&rightPanel==="artifacts"&&(
<div style={{width:360,borderLeft:`1px solid ${c.border}`,background:c.bgPanel,overflowY:"auto",flexShrink:0,display:"flex",flexDirection:"column"}}>
<div style={{padding:"12px 16px",borderBottom:`1px solid ${c.border}`,display:"flex",justifyContent:"space-between",alignItems:"center",flexShrink:0}}>
<span style={{fontSize:12,fontWeight:600,fontFamily:sans,color:c.text}}>Artifacts</span>
<button onClick={()=>setRightPanel(null)} style={{padding:"4px 14px",background:`${c.accent}15`,border:`1px solid ${c.accent}40`,borderRadius:4,color:c.accent,fontSize:10,fontWeight:600,cursor:"pointer",fontFamily:mono}}>✕ Close</button>
</div>
<div style={{padding:"8px 16px",borderBottom:`1px solid ${c.border}`,display:"flex",gap:4}}>
<span style={{fontSize:9,padding:"3px 8px",background:`${c.accent}15`,border:`1px solid ${c.accent}30`,borderRadius:3,color:c.accent,cursor:"pointer"}}>All</span>
<span style={{fontSize:9,padding:"3px 8px",border:`1px solid ${c.border}`,borderRadius:3,color:c.textSec,cursor:"pointer"}}>Documents</span>
<span style={{fontSize:9,padding:"3px 8px",border:`1px solid ${c.border}`,borderRadius:3,color:c.textSec,cursor:"pointer"}}>Emails</span>
</div>
<div style={{flex:1,overflowY:"auto",padding:0}}>
{[
{name:"Henderson_Filing_Analysis_2026-03-25.docx",type:"docx",size:"24 KB",module:"Save to Case",run:"run-015",time:"Mar 25, 2:18pm"},
{name:"Henderson_Filing_Analysis_2026-03-24.docx",type:"docx",size:"22 KB",module:"Save to Case",run:"run-014",time:"Mar 24, 9:34am"},
{name:"Deadline_Summary_2026-03-24.pdf",type:"pdf",size:"18 KB",module:"Analyze Filing",run:"run-014",time:"Mar 24, 9:33am"},
{name:"Calendar_Entry_Motion_to_Compel.ics",type:"ics",size:"2 KB",module:"Add to Calendar",run:"run-014",time:"Mar 24, 9:34am"},
{name:"Henderson_Filing_Analysis_2026-03-22.docx",type:"docx",size:"21 KB",module:"Save to Case",run:"run-012",time:"Mar 22, 11:05am"},
{name:"email_to_will_2026-03-22.eml",type:"eml",size:"45 KB",module:"Email to Will",run:"run-012",time:"Mar 22, 11:05am"},
].map((f,i)=>{
const icon = f.type==="docx"?"📄":f.type==="pdf"?"📕":f.type==="eml"?"✉":f.type==="ics"?"📅":"📎";
return (
<div key={i} style={{padding:"8px 16px",borderBottom:`1px solid ${c.border}`,cursor:"pointer"}}
onMouseEnter={e=>e.currentTarget.style.background=c.bgHover} onMouseLeave={e=>e.currentTarget.style.background="transparent"}>
<div style={{display:"flex",alignItems:"center",gap:6,marginBottom:3}}>
<span style={{fontSize:12}}>{icon}</span>
<span style={{fontSize:10,fontWeight:600,color:c.text,flex:1,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"}}>{f.name}</span>
<span style={{fontSize:8,color:c.textTer}}>{f.size}</span>
</div>
<div style={{display:"flex",justifyContent:"space-between",fontSize:8,color:c.textTer,paddingLeft:22}}>
<span>from <span style={{color:c.textSec}}>{f.module}</span></span>
<span>{f.run} · {f.time}</span>
</div>
</div>
);
})}
</div>
</div>
)}
{/* ═══ TASKS COMMAND CENTER PREVIEW ═══ */}
{!detailId&&rightPanel==="tasks"&&(
<TasksPreviewPanel
onClose={()=>setRightPanel(null)}
onOpenInspector={()=>setRightPanel("inspector")}
onOpenGraph={()=>setRightPanel(null)}
onAskTaskAgent={()=>setRightPanel("agent")}
/>
)}
{/* ═══ COMPACT RUN INSPECTOR PREVIEW ═══ */}
{!detailId&&rightPanel==="inspector"&&(
<RunInspectorPreviewPanel
selectedModuleId={selected}
modules={modules}
onClose={()=>setRightPanel(null)}
onBackToGraph={()=>setRightPanel(null)}
onOpenArtifacts={()=>setRightPanel("artifacts")}
onAskTaskAgent={()=>setRightPanel("agent")}
/>
)}
{/* ═══ SOURCE WORKSPACE PREVIEW ═══ */}
{!detailId&&rightPanel==="source"&&(<SourceWorkspacePreviewPanel onClose={()=>setRightPanel(null)} onOpenForum={()=>setRightPanel("forum")} />)}
{/* ═══ TASK RUN BOARD / FORUM PREVIEW ═══ */}
{!detailId&&rightPanel==="forum"&&(<TaskForumPreviewPanel onClose={()=>setRightPanel(null)} onOpenSource={()=>setRightPanel("source")} onAskTaskAgent={()=>setRightPanel("agent")} />)}
{/* ═══ TASK AGENT SIDE PANEL PREVIEW ═══ */}
{!detailId&&rightPanel==="agent"&&(
<TaskAgentSidePanel
selectedModuleId={selected}
modules={modules}
onClose={()=>setRightPanel(null)}
onOpenInspector={()=>setRightPanel("inspector")}
/>
)}
</div>
{/* ═══ TOOLBAR ═══ */}
<div style={{
position:"fixed",bottom:12,left:"50%",transform:"translateX(-50%)",zIndex:30,
display:"flex",alignItems:"center",gap:5,padding:"7px 14px",
background:`${c.bgCard}ee`,border:`1px solid ${c.border}`,borderRadius:8,
backdropFilter:"blur(10px)",boxShadow:"0 4px 16px #0005",minWidth:380,justifyContent:"center",
}}>
<TB onClick={()=>setZoom(z=>Math.max(30,z-10))}>−</TB>
<span style={{fontSize:10,color:c.textSec,minWidth:36,textAlign:"center"}}>{zoom}%</span>
<TB onClick={()=>setZoom(z=>Math.min(150,z+10))}>+</TB>
<BDiv />
<TB onClick={()=>{setZoom(65);setPan({x:0,y:0});}}>Fit</TB>
<BDiv />
<TB accent>Validate</TB>
<BDiv />
{exec==="idle"&&<TB accent onClick={()=>setExec("running")}>▶ Run</TB>}
{exec==="running"&&<><TB onClick={()=>setExec("paused")}>⏸ Pause</TB><TB danger onClick={()=>setExec("idle")}>⏹ Stop</TB></>}
{exec==="paused"&&<><TB accent onClick={()=>setExec("running")}>▶ Resume</TB><TB danger onClick={()=>setExec("idle")}>⏹ Stop</TB></>}
{exec==="complete"&&<TB onClick={()=>setExec("idle")}>↺ Reset</TB>}
</div>
{/* ═══ EXPANDED DETAIL OVERLAY ═══ */}
{expandedOverlay&&(
<div onClick={()=>setExpandedOverlay(null)} style={{position:"fixed",top:0,left:0,right:0,bottom:0,background:"rgba(0,0,0,0.65)",zIndex:100,display:"flex",alignItems:"center",justifyContent:"center"}}>
<div onClick={e=>e.stopPropagation()} style={{width:"85%",maxWidth:1300,height:"75%",maxHeight:800,background:c.bgPanel,border:`1px solid ${c.border}`,borderRadius:12,display:"flex",flexDirection:"column",boxShadow:"0 20px 60px #000a",overflow:"hidden"}}>
{/* Overlay header */}
<div style={{height:48,minHeight:48,borderBottom:`1px solid ${c.border}`,display:"flex",alignItems:"center",justifyContent:"space-between",padding:"0 20px",background:c.bg}}>
<div style={{display:"flex",alignItems:"center",gap:10}}>
<span style={{fontSize:16}}>{expandedOverlay==="experiment"?"⚗":"⚖"}</span>
<span style={{fontSize:14,fontWeight:700,fontFamily:sans,color:c.text}}>
{expandedOverlay==="experiment"?"Experiment: Compare Drafting Approaches":"Judge: Output Quality Assessment"}
</span>
</div>
<div style={{display:"flex",alignItems:"center",gap:8}}>
{expandedOverlay==="experiment"&&<Btn primary>▶ Run All Variants</Btn>}
{expandedOverlay==="judge"&&<Btn accent>Re-Score</Btn>}
<span onClick={()=>setExpandedOverlay(null)} style={{fontSize:18,color:c.textSec,cursor:"pointer",padding:"0 4px"}}>✕</span>
</div>
</div>
{/* Overlay body */}
<div style={{flex:1,display:"flex",overflow:"hidden"}}>
{/* ═══ EXPERIMENT EXPANDED VIEW ═══ */}
{expandedOverlay==="experiment"&&(
<React.Fragment>
{/* Left: shared config */}
<div style={{width:220,minWidth:220,borderRight:`1px solid ${c.border}`,padding:16,overflowY:"auto"}}>
<DL>Target Module</DL>
<div style={{fontSize:11,color:c.accent,padding:"6px 8px",background:`${c.accent}10`,border:`1px solid ${c.accent}30`,borderRadius:4,marginBottom:10}}>◆ Draft Analysis</div>
<DL mt>Run Mode</DL>
<DS opts={["Parallel","Sequential"]} v="Parallel" />
<DL mt>Scoring Preset</DL>
<DS opts={["(none — manual judge)","Quality Review","Code Review","+ New preset..."]} v="(none — manual judge)" />
<div style={{borderTop:`1px solid ${c.border}`,marginTop:14,paddingTop:12}}>
<DL>Last Run</DL>
<div style={{fontSize:10,color:c.textSec,lineHeight:1.6}}>
<div>3 variants × 1 input</div>
<div>Cost: $0.12 · Duration: 8s</div>
<div style={{color:c.green,marginTop:4}}>✓ All variants complete</div>
</div>
</div>
</div>
{/* Right: variant columns */}
<div style={{flex:1,display:"flex",overflowX:"auto",overflowY:"auto"}}>
{/* Variant A */}
<div style={{flex:1,minWidth:260,borderRight:`1px solid ${c.border}`,padding:16}}>
<div style={{display:"flex",alignItems:"center",gap:8,marginBottom:12}}>
<span style={{width:10,height:10,borderRadius:"50%",background:c.accent}} />
<span style={{fontSize:13,fontWeight:700,fontFamily:sans,color:c.text}}>Variant A (Baseline)</span>
</div>
<DL>Agent</DL>
<DS opts={["Elnor","Nova","Atlas","Sage","+ Configure..."]} v="Elnor" />
<DL mt>Model</DL>
<DS opts={["(agent default)","claude-opus-4-6","claude-sonnet-4-6","gemini-2.5-pro"]} v="(agent default)" />
<DL mt>Think Level</DL>
<DS opts={["(agent default)","off","minimal","low","medium","high","xhigh"]} v="high" />
<DL mt>Instruction</DL>
<DT value="Draft a comprehensive analysis covering all key aspects of the subject matter. Structure the analysis with clear sections. Cite specific evidence for every factual claim." h={120} />
<div style={{borderTop:`1px solid ${c.border}`,marginTop:14,paddingTop:10}}>
<div style={{fontSize:9,fontWeight:600,color:c.green,marginBottom:4}}>LAST RUN RESULT</div>
<div style={{fontSize:10,color:c.textSec,lineHeight:1.5}}>
<div>Cost: $0.04 · 2,847 tokens · 3.2s</div>
<div style={{marginTop:6,padding:8,background:c.bg,borderRadius:4,border:`1px solid ${c.border}`,maxHeight:100,overflow:"auto",fontSize:10,color:c.text}}>
The analysis identifies three primary areas of concern. First, the methodology employed lacks sufficient controls...
</div>
</div>
</div>
</div>
{/* Variant B */}
<div style={{flex:1,minWidth:260,borderRight:`1px solid ${c.border}`,padding:16}}>
<div style={{display:"flex",alignItems:"center",gap:8,marginBottom:12}}>
<span style={{width:10,height:10,borderRadius:"50%",background:c.amber}} />
<span style={{fontSize:13,fontWeight:700,fontFamily:sans,color:c.text}}>Variant B — Nova</span>
</div>
<DL>Agent</DL>
<DS opts={["Elnor","Nova","Atlas","Sage","+ Configure..."]} v="Nova" />
<DL mt>Model</DL>
<div style={{display:"flex",alignItems:"center",gap:6}}>
<DC label="Same as A" checked={false} />
</div>
<DS opts={["(agent default)","claude-opus-4-6","claude-sonnet-4-6","gemini-2.5-pro"]} v="gemini-2.5-pro" />
<DL mt>Think Level</DL>
<DS opts={["(agent default)","off","minimal","low","medium","high","xhigh"]} v="medium" />
<DL mt>Instruction</DL>
<div style={{marginBottom:4}}><DC label="Same as A" checked={true} /></div>
<DT value="Draft a comprehensive analysis covering all key aspects of the subject matter. Structure the analysis with clear sections. Cite specific evidence for every factual claim." h={120} />
<div style={{borderTop:`1px solid ${c.border}`,marginTop:14,paddingTop:10}}>
<div style={{fontSize:9,fontWeight:600,color:c.amber,marginBottom:4}}>LAST RUN RESULT</div>
<div style={{fontSize:10,color:c.textSec,lineHeight:1.5}}>
<div>Cost: $0.02 · 2,103 tokens · 1.8s</div>
<div style={{marginTop:6,padding:8,background:c.bg,borderRadius:4,border:`1px solid ${c.border}`,maxHeight:100,overflow:"auto",fontSize:10,color:c.text}}>
This analysis examines the subject through multiple lenses. The primary finding indicates significant gaps in the current approach...
</div>
</div>
</div>
</div>
{/* Variant C */}
<div style={{flex:1,minWidth:260,padding:16}}>
<div style={{display:"flex",alignItems:"center",gap:8,marginBottom:12}}>
<span style={{width:10,height:10,borderRadius:"50%",background:c.purple}} />
<span style={{fontSize:13,fontWeight:700,fontFamily:sans,color:c.text}}>Variant C — Atlas</span>
</div>
<DL>Agent</DL>
<DS opts={["Elnor","Nova","Atlas","Sage","+ Configure..."]} v="Atlas" />
<DL mt>Model</DL>
<div style={{marginBottom:4}}><DC label="Same as A" checked={false} /></div>
<DS opts={["(agent default)","claude-opus-4-6","claude-sonnet-4-6","gemini-2.5-pro"]} v="claude-sonnet-4-6" />
<DL mt>Think Level</DL>
<DS opts={["(agent default)","off","minimal","low","medium","high","xhigh"]} v="(agent default)" />
<DL mt>Instruction</DL>
<div style={{marginBottom:4}}><DC label="Same as A" checked={true} /></div>
<DT value="Draft a comprehensive analysis covering all key aspects of the subject matter. Structure the analysis with clear sections. Cite specific evidence for every factual claim." h={120} />
<div style={{borderTop:`1px solid ${c.border}`,marginTop:14,paddingTop:10}}>
<div style={{fontSize:9,fontWeight:600,color:c.purple,marginBottom:4}}>LAST RUN RESULT</div>
<div style={{fontSize:10,color:c.textSec,lineHeight:1.5}}>
<div>Cost: $0.03 · 2,521 tokens · 2.1s</div>
<div style={{marginTop:6,padding:8,background:c.bg,borderRadius:4,border:`1px solid ${c.border}`,maxHeight:100,overflow:"auto",fontSize:10,color:c.text}}>
Based on a thorough examination, several critical factors emerge. The evidence supports a structured intervention approach...
</div>
</div>
</div>
</div>
</div>
</React.Fragment>
)}
{/* ═══ JUDGE EXPANDED VIEW ═══ */}
{expandedOverlay==="judge"&&(
<React.Fragment>
{/* Left: scoring config + DSPy */}
<div style={{width:250,minWidth:250,borderRight:`1px solid ${c.border}`,padding:16,overflowY:"auto"}}>
<DL>Scoring Dimensions</DL>
{[{name:"Accuracy",method:"Claim Verification",clr:c.accent},
{name:"Completeness",method:"Checklist",clr:c.green},
{name:"Overall Quality",method:"Subjective",clr:c.purple}].map((dim,i)=>(
<div key={i} style={{padding:"8px 10px",background:`${dim.clr}08`,border:`1px solid ${dim.clr}25`,borderRadius:6,marginTop:i?8:4}}>
<div style={{fontSize:11,fontWeight:600,color:dim.clr}}>{dim.name}</div>
<div style={{fontSize:9,color:c.textSec,marginTop:2}}>{dim.method}</div>
</div>
))}
<div style={{marginTop:8}}><Btn>+ Add Dimension</Btn></div>
<div style={{borderTop:`1px solid ${c.border}`,marginTop:14,paddingTop:12}}>
<DL>Judge Agent</DL>
<DS opts={["Elnor","Nova","Atlas"]} v="Elnor" />
<DL mt>Model</DL>
<DS opts={["claude-opus-4-6","claude-sonnet-4-6"]} v="claude-opus-4-6" />
</div>
<div style={{borderTop:`1px solid ${c.border}`,marginTop:14,paddingTop:12}}>
<div style={{fontSize:10,fontWeight:700,color:c.purple,marginBottom:8}}>DSPy Optimization</div>
<DC label="Enable optimization" checked={true} />
<DL mt>Optimizer</DL>
<DS opts={["GEPA (recommended)","MIPROv2 (lighter)"]} v="GEPA (recommended)" />
<DL mt>Reflection Agent</DL>
<DS opts={["claude-opus-4-6","claude-sonnet-4-6"]} v="claude-opus-4-6" />
<DL mt>Intensity</DL>
<DS opts={["Light (~$2)","Medium (~$5)","Heavy (~$15)"]} v="Medium (~$5)" />
<DL mt>Safety Floors</DL>
<div style={{fontSize:10,color:c.textSec,lineHeight:1.5}}>
<div>Accuracy ≥ 0.90</div>
<div>Completeness ≥ 0.70</div>
</div>
<div style={{marginTop:10}}>
<button style={{width:"100%",padding:"10px 0",background:`${c.purple}15`,border:`1px solid ${c.purple}40`,borderRadius:6,color:c.purple,fontSize:12,fontWeight:700,cursor:"pointer",fontFamily:sans}}>
⚡ Suggest Improvements
</button>
</div>
</div>
<div style={{borderTop:`1px solid ${c.border}`,marginTop:14,paddingTop:12}}>
<DL>Preset</DL>
<DS opts={["(custom)","Save current...","Manage..."]} v="(custom)" />
</div>
</div>
{/* Right: scoring results */}
<div style={{flex:1,padding:20,overflowY:"auto"}}>
{/* Score bars */}
<div style={{marginBottom:20}}>
<div style={{fontSize:12,fontWeight:700,fontFamily:sans,color:c.text,marginBottom:12}}>Scores</div>
<div style={{display:"grid",gridTemplateColumns:"120px 1fr 1fr 1fr",gap:"6px 12px",alignItems:"center"}}>
<div style={{fontSize:9,fontWeight:600,color:c.textTer}}>DIMENSION</div>
<div style={{fontSize:9,fontWeight:600,color:c.accent,textAlign:"center"}}>● Variant A</div>
<div style={{fontSize:9,fontWeight:600,color:c.amber,textAlign:"center"}}>● Variant B</div>
<div style={{fontSize:9,fontWeight:600,color:c.purple,textAlign:"center"}}>● Variant C</div>
<div style={{fontSize:11,color:c.text}}>Accuracy</div>
{[{s:0.92,clr:c.accent},{s:0.78,clr:c.amber},{s:0.85,clr:c.purple}].map((v,i)=>(
<div key={i} style={{display:"flex",alignItems:"center",gap:6}}>
<div style={{flex:1,height:8,background:c.bg,borderRadius:4,overflow:"hidden"}}>
<div style={{width:`${v.s*100}%`,height:"100%",background:v.clr,borderRadius:4}} />
</div>
<span style={{fontSize:10,color:v.clr,fontFamily:mono,minWidth:30}}>{v.s.toFixed(2)}</span>
</div>
))}
<div style={{fontSize:11,color:c.text}}>Completeness</div>
{[{s:0.75,clr:c.accent},{s:0.90,clr:c.amber},{s:0.82,clr:c.purple}].map((v,i)=>(
<div key={i} style={{display:"flex",alignItems:"center",gap:6}}>
<div style={{flex:1,height:8,background:c.bg,borderRadius:4,overflow:"hidden"}}>
<div style={{width:`${v.s*100}%`,height:"100%",background:v.clr,borderRadius:4}} />
</div>
<span style={{fontSize:10,color:v.clr,fontFamily:mono,minWidth:30}}>{v.s.toFixed(2)}</span>
</div>
))}
<div style={{fontSize:11,color:c.text}}>Overall Quality</div>
{[{s:0.84,clr:c.accent},{s:0.76,clr:c.amber},{s:0.80,clr:c.purple}].map((v,i)=>(
<div key={i} style={{display:"flex",alignItems:"center",gap:6}}>
<div style={{flex:1,height:8,background:c.bg,borderRadius:4,overflow:"hidden"}}>
<div style={{width:`${v.s*100}%`,height:"100%",background:v.clr,borderRadius:4}} />
</div>
<span style={{fontSize:10,color:v.clr,fontFamily:mono,minWidth:30}}>{v.s.toFixed(2)}</span>
</div>
))}
<div style={{fontSize:11,fontWeight:600,color:c.text,borderTop:`1px solid ${c.border}`,paddingTop:6}}>Aggregate</div>
{[{s:0.84,clr:c.accent},{s:0.81,clr:c.amber},{s:0.82,clr:c.purple}].map((v,i)=>(
<div key={i} style={{display:"flex",alignItems:"center",gap:6,borderTop:`1px solid ${c.border}`,paddingTop:6}}>
<div style={{flex:1,height:10,background:c.bg,borderRadius:4,overflow:"hidden"}}>
<div style={{width:`${v.s*100}%`,height:"100%",background:v.clr,borderRadius:4}} />
</div>
<span style={{fontSize:11,fontWeight:700,color:v.clr,fontFamily:mono,minWidth:30}}>{v.s.toFixed(2)}</span>
</div>
))}
</div>
</div>
{/* Audit trail */}
<div style={{marginBottom:20,padding:14,background:c.bg,borderRadius:8,border:`1px solid ${c.border}`}}>
<div style={{fontSize:11,fontWeight:700,fontFamily:sans,color:c.text,marginBottom:8}}>Audit Trail — Accuracy (Variant A)</div>
<div style={{fontSize:10,color:c.textSec,marginBottom:8}}>20 claims extracted · 18 supported · 1 contradicted · 1 not addressed</div>
<div style={{padding:"8px 10px",background:`${c.red}08`,border:`1px solid ${c.red}25`,borderRadius:4,marginBottom:6}}>
<div style={{fontSize:10,color:c.red,fontWeight:600}}>✕ CONTRADICTED — Claim #14</div>
<div style={{fontSize:10,color:c.text,marginTop:4}}>"The review period began in Q3 2025"</div>
<div style={{fontSize:9,color:c.textSec,marginTop:4}}>Evidence (Red Team A): Source document states "Q2 2025" (paragraph 3, line 4)</div>
</div>
<div style={{padding:"8px 10px",background:`${c.amber}08`,border:`1px solid ${c.amber}25`,borderRadius:4}}>
<div style={{fontSize:10,color:c.amber,fontWeight:600}}>? NOT ADDRESSED — Claim #17</div>
<div style={{fontSize:10,color:c.text,marginTop:4}}>"Industry benchmarks show a 15% improvement"</div>
<div style={{fontSize:9,color:c.textSec,marginTop:4}}>No supporting evidence found in any evidence source</div>
</div>
</div>
{/* Recommendation */}
<div style={{marginBottom:20,padding:14,background:`${c.accent}08`,borderRadius:8,border:`1px solid ${c.accent}25`}}>
<div style={{fontSize:11,fontWeight:700,fontFamily:sans,color:c.accent,marginBottom:6}}>Recommendation</div>
<div style={{fontSize:11,color:c.text,lineHeight:1.6}}>
<strong>Variant A (Elnor)</strong> is strongest overall. It leads on accuracy (0.92 vs 0.78/0.85) due to more precise evidence citations. Variant B scores highest on completeness (0.90) — it covers more checklist items but with less factual precision. Variant C is a balanced middle ground.
</div>
<div style={{fontSize:10,color:c.textSec,marginTop:8,lineHeight:1.5}}>
<strong>Synthesis opportunity:</strong> Combining A's citation precision with B's broader coverage could improve both dimensions. The DSPy optimizer can attempt this automatically.
</div>
</div>
{/* DSPy candidate */}
<div style={{padding:14,background:`${c.purple}08`,borderRadius:8,border:`1px solid ${c.purple}25`}}>
<div style={{fontSize:11,fontWeight:700,fontFamily:sans,color:c.purple,marginBottom:6}}>DSPy Candidate — Optimized Instruction</div>
<div style={{padding:10,background:c.bg,borderRadius:6,border:`1px solid ${c.border}`,fontSize:10,color:c.text,fontFamily:mono,lineHeight:1.6,marginBottom:10}}>
Draft a comprehensive analysis covering all key aspects of the subject matter. Structure the analysis with clear sections.
<span style={{background:`${c.green}20`,color:c.green}}> IMPORTANT: For every factual claim, cite the specific source document, section, and paragraph. Verify all date references and numerical data against primary sources before including them. Address each of the following areas: scope definition, methodology, key findings, recommendations, and limitations.</span>
</div>
<div style={{display:"grid",gridTemplateColumns:"1fr 1fr 1fr",gap:8,marginBottom:10}}>
<div style={{padding:8,background:c.bg,borderRadius:4,textAlign:"center"}}>
<div style={{fontSize:9,color:c.textTer}}>BASELINE</div>
<div style={{fontSize:16,fontWeight:700,color:c.textSec,fontFamily:mono}}>0.84</div>
</div>
<div style={{padding:8,background:c.bg,borderRadius:4,textAlign:"center"}}>
<div style={{fontSize:9,color:c.textTer}}>CANDIDATE</div>
<div style={{fontSize:16,fontWeight:700,color:c.green,fontFamily:mono}}>0.91</div>
</div>
<div style={{padding:8,background:c.bg,borderRadius:4,textAlign:"center"}}>
<div style={{fontSize:9,color:c.textTer}}>IMPROVEMENT</div>
<div style={{fontSize:16,fontWeight:700,color:c.green,fontFamily:mono}}>+8.3%</div>
</div>
</div>
<div style={{fontSize:10,color:c.textSec,lineHeight:1.5,marginBottom:10}}>
<div>Accuracy: 0.92 → 0.95 ✓ (above floor 0.90)</div>
<div>Completeness: 0.75 → 0.88 ✓ (above floor 0.70)</div>
<div>Overall: 4.2 → 4.0 (slight decrease, within tolerance)</div>
</div>
<div style={{fontSize:10,color:c.purple,lineHeight:1.5,marginBottom:12}}>
<strong>GEPA reasoning:</strong> Added explicit citation requirement and checklist enumeration. The citation instruction addresses the contradicted claims found in Variant A. The checklist enumeration addresses the completeness gap across all variants.
</div>
<div style={{display:"flex",gap:8}}>
<button style={{flex:1,padding:"10px 0",background:`${c.green}15`,border:`1px solid ${c.green}40`,borderRadius:6,color:c.green,fontSize:12,fontWeight:700,cursor:"pointer",fontFamily:sans}}>✓ Promote</button>
<button style={{flex:1,padding:"10px 0",background:`${c.red}10`,border:`1px solid ${c.red}30`,borderRadius:6,color:c.red,fontSize:11,fontWeight:600,cursor:"pointer",fontFamily:sans}}>✕ Reject</button>
<button style={{flex:1,padding:"10px 0",background:`${c.purple}10`,border:`1px solid ${c.purple}30`,borderRadius:6,color:c.purple,fontSize:11,fontWeight:600,cursor:"pointer",fontFamily:sans}}>↻ Run Again</button>
</div>
</div>
</div>
</React.Fragment>
)}
</div>
</div>
</div>
)}
<style>{`@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.4}}`}</style>
</div>
);
}
// ═══ ALT B PREVIEW COMPONENTS — RUN INSPECTOR / TASK AGENT / TASKS PAGE ═══
const previewStepRows = [
{n:1,name:"Upload MTD + Record",type:"trigger.manual",status:"done",dur:"12s",cost:"$0.00",at:"May 9 · 9:01 AM",act:"#1",out:"MTD + complaint + SEC filings",deliver:"Source Workspace"},
{n:2,name:"Deep Source Research",type:"step.source_research",status:"done",dur:"18m 32s",cost:"$9.80",at:"May 9 · 9:04 AM",act:"#1-2",out:"42 source refs · 11 notes · 6 open needs",deliver:"Source Workspace + Board"},
{n:3,name:"Research Sufficiency",type:"step.outcome_evaluator",status:"done",dur:"3m 05s",cost:"$1.74",at:"May 9 · 9:24 AM",act:"#1",out:"satisfied after 1 repair loop",deliver:"2 repair instructions consumed"},
{n:4,name:"Draft Opposition",type:"step.agent_task",status:"done",dur:"12m 10s",cost:"$5.60",at:"May 9 · 9:29 AM",act:"#1",out:"Opposition draft v1.md",deliver:"kept with run"},
{n:5,name:"Source Accuracy Gate",type:"step.outcome_evaluator",status:"active",dur:"4m 22s",cost:"$2.04",at:"May 9 · 9:43 AM",act:"#1",out:"needs source verification",deliver:"repair → Revision, research_need → Source Research"},
{n:6,name:"Revision Pass",type:"step.agent_task",status:"pending",dur:"—",cost:"est. $4–7",at:"after feedback",act:"—",out:"expected: revised draft",deliver:"receives repair bundle + board digest"},
{n:7,name:"Red Team / Strategy Review",type:"step.agent_review_gate",status:"pending",dur:"—",cost:"est. $3–5",at:"after revision",act:"—",out:"red-team findings",deliver:"Board + Revision loop"},
{n:8,name:"Final Outcome Judge",type:"step.judge",status:"pending",dur:"—",cost:"est. $2–4",at:"final gate",act:"—",out:"final score + blocker check",deliver:"Save Brief Package if satisfied"},
];
function statusColor(status){
return status==="done"?c.green:status==="active"?c.accent:status==="error"?c.red:status==="pending"?c.textTer:c.amber;
}
function PanelHeader({title,sub,onClose,backLabel,onBack}){
return <div style={{padding:"12px 14px",borderBottom:`1px solid ${c.border}`,display:"flex",alignItems:"center",justifyContent:"space-between",gap:8,flexShrink:0}}>
<div>
<div style={{fontSize:12,fontWeight:700,fontFamily:sans,color:c.text}}>{title}</div>
{sub&&<div style={{fontSize:9,color:c.textTer,marginTop:2}}>{sub}</div>}
</div>
<div style={{display:"flex",gap:6,alignItems:"center"}}>
{onBack&&<button onClick={onBack} style={{padding:"5px 9px",background:`${c.accent}10`,border:`1px solid ${c.accent}30`,borderRadius:4,color:c.accent,fontSize:10,cursor:"pointer",fontFamily:mono}}>{backLabel||"← Graph"}</button>}
<button onClick={onClose} style={{padding:"5px 9px",background:"transparent",border:`1px solid ${c.border}`,borderRadius:4,color:c.textSec,fontSize:10,cursor:"pointer",fontFamily:mono}}>✕</button>
</div>
</div>;
}
function ModuleRunQuickAccess({mod,execState,onOpenOutput,onOpenInspector,onAskTaskAgent}){
if(!mod || mod.cat==="environment" || mod.cat==="system") return null;
const isOutput = mod.cat==="output";
const isJudge = mod.type==="step.judge";
const sampleOut = isOutput ? "Delivery receipt" : isJudge ? "Judge score bundle" : mod.type==="system.experiment" ? "Variant comparison" : mod.type==="trigger.email" ? "New filing alert.eml" : "Analysis.md";
const status = execState==="running" ? "running" : execState==="complete" ? "completed" : "last completed";
return <div style={{padding:"10px 16px",borderBottom:`1px solid ${c.border}`,background:`${c.green}06`}}>
<div style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:8}}>
<div>
<div style={{fontSize:9,fontWeight:700,color:c.green,textTransform:"uppercase",letterSpacing:0.4}}>Run Data</div>
<div style={{fontSize:9,color:c.textTer,marginTop:2}}>Selected-run output and detailed run access for this module.</div>
</div>
<span style={{fontSize:8,color:c.green,border:`1px solid ${c.green}30`,background:`${c.green}10`,borderRadius:4,padding:"3px 6px"}}>{status}</span>
</div>
<div style={{display:"grid",gridTemplateColumns:"58px 1fr",gap:"3px 8px",fontSize:9,color:c.textSec,marginBottom:8}}>
<span style={{color:c.textTer}}>Output</span><span style={{color:c.text}}>{sampleOut}</span>
<span style={{color:c.textTer}}>Time</span><span>6m 14s · $2.18</span>
<span style={{color:c.textTer}}>Run</span><span style={{fontFamily:mono}}>run-015 · selected activation</span>
</div>
<div style={{display:"grid",gridTemplateColumns:"1fr 1fr",gap:6}}>
<button onClick={onOpenOutput} style={{padding:"7px 0",background:`${c.green}12`,border:`1px solid ${c.green}35`,borderRadius:5,color:c.green,fontSize:10,fontWeight:700,cursor:"pointer",fontFamily:mono}}>↗ Open output in Q tab</button>
<button onClick={onOpenInspector} style={{padding:"7px 0",background:`${c.accent}12`,border:`1px solid ${c.accent}35`,borderRadius:5,color:c.accent,fontSize:10,fontWeight:700,cursor:"pointer",fontFamily:mono}}>⇄ Run inspector</button>
</div>
<div onClick={onAskTaskAgent} style={{marginTop:7,fontSize:9,color:c.purple,cursor:"pointer",fontFamily:mono}}>✦ Ask Task Agent with this module selected</div>
</div>;
}
function RunInspectorPreviewPanel({selectedModuleId,modules,onClose,onBackToGraph,onOpenArtifacts,onAskTaskAgent}){
const selectedMod = modules.find(m=>m.id===selectedModuleId);
const [section,setSection] = React.useState("steps");
const [expanded,setExpanded] = React.useState(selectedMod?.name || "Damages Memo");
return <div style={{width:470,borderLeft:`1px solid ${c.border}`,background:c.bgPanel,overflow:"hidden",flexShrink:0,display:"flex",flexDirection:"column"}}>
<PanelHeader title="Run Inspector" sub="run-015 · detailed view linked to graph" onClose={onClose} onBack={onBackToGraph} backLabel="Focus graph" />
<div style={{padding:"12px 14px",borderBottom:`1px solid ${c.border}`,background:c.bgCard}}>
<div style={{display:"flex",justifyContent:"space-between",alignItems:"start",gap:10}}>
<div>
<div style={{fontSize:13,fontWeight:700,color:c.text,fontFamily:sans}}>MTD Opposition — Outcome-Gated Run</div>
<div style={{fontSize:10,color:c.textSec,marginTop:3}}>Running · Source Accuracy Gate active · 44m elapsed · $19.23 so far</div>
<div style={{fontSize:10,color:c.textSec,marginTop:3}}>Next: repair feedback routes to Revision Pass; open source needs route to Source Research.</div>
</div>
<div style={{display:"flex",gap:5,flexDirection:"column",alignItems:"flex-end"}}>
<button onClick={onAskTaskAgent} style={{padding:"5px 9px",background:`${c.purple}12`,border:`1px solid ${c.purple}35`,borderRadius:4,color:c.purple,fontSize:10,cursor:"pointer",fontFamily:mono}}>✦ Task Agent</button>
<button style={{padding:"5px 9px",background:"transparent",border:`1px solid ${c.border}`,borderRadius:4,color:c.textSec,fontSize:10,cursor:"pointer",fontFamily:mono}}>Export audit</button>
</div>
</div>
<div style={{marginTop:9,padding:"8px 10px",border:`1px solid ${c.green}25`,borderRadius:6,background:`${c.green}08`,fontSize:10,color:c.textSec,lineHeight:1.5}}>
<strong style={{color:c.green}}>Deliverables preview:</strong> final opposition package saves to <span style={{fontFamily:mono}}>/Marex/Opposition/</span>. External delivery remains blocked until human approval.
</div>
</div>
<div style={{display:"flex",gap:6,padding:"8px 14px",borderBottom:`1px solid ${c.border}`}}>
{[['steps','Run Flow & Steps'],['artifacts','Artifacts & Deliveries'],['audit','Context & Audit']].map(([id,label])=>(
<button key={id} onClick={()=>setSection(id)} style={{padding:"6px 8px",background:section===id?`${c.accent}15`:"transparent",border:`1px solid ${section===id?c.accent+"40":c.border}`,borderRadius:5,color:section===id?c.accent:c.textSec,fontSize:10,cursor:"pointer",fontFamily:mono}}>{label}</button>
))}
</div>
<div style={{flex:1,overflowY:"auto",padding:14}}>
{section==="steps"&&<React.Fragment>
<div style={{fontSize:10,color:c.textTer,marginBottom:8}}>Execution order is activation-based, not a simple linear timeline. Loops, branches and repeated activations appear as separate rows; filter/search/sort by module, activation, status, date, cost or output.</div>
{previewStepRows.map(row=>(
<div key={row.n} style={{border:`1px solid ${expanded===row.name?c.accent+"40":c.border}`,background:expanded===row.name?`${c.accent}08`:c.bgCard,borderRadius:7,marginBottom:7,overflow:"hidden"}}>
<div onClick={()=>setExpanded(expanded===row.name?null:row.name)} style={{display:"grid",gridTemplateColumns:"24px 1fr 92px 58px 56px",gap:6,alignItems:"center",padding:"8px 10px",cursor:"pointer"}}>
<div style={{fontSize:11,color:statusColor(row.status),fontWeight:700}}>{row.status==="done"?'✓':row.status==="active"?'▶':'○'}</div>
<div>
<div style={{fontSize:11,fontWeight:700,color:c.text,fontFamily:sans}}>{row.n}. {row.name}</div>
<div style={{fontSize:8.5,color:c.textTer,fontFamily:mono}}>{row.type} · activation {row.act}</div>
</div>
<div style={{fontSize:8.5,color:c.textTer,textAlign:"right"}}>{row.at}</div>
<div style={{fontSize:9,color:c.textSec,textAlign:"right"}}>{row.dur}</div>
<div style={{fontSize:9,color:c.amber,textAlign:"right"}}>{row.cost}</div>
</div>
{expanded===row.name&&<div style={{padding:"0 10px 10px",fontSize:9,color:c.textSec,lineHeight:1.55,borderTop:`1px solid ${c.border}`}}>
<div style={{display:"grid",gridTemplateColumns:"72px 1fr",gap:"3px 8px",marginTop:8}}>
<span style={{color:c.textTer}}>Output</span><span style={{color:c.text}}>{row.out}</span>
<span style={{color:c.textTer}}>Delivery</span><span>{row.deliver}</span>
<span style={{color:c.textTer}}>Model</span><span>GPT-5.5 Pro · Think: High</span>
<span style={{color:c.textTer}}>Context</span><span>Task-scoped packet · 5 injected · 2 excluded</span>
</div>
<div style={{display:"flex",gap:6,marginTop:8}}>
<button onClick={onOpenArtifacts} style={{padding:"5px 8px",background:`${c.green}10`,border:`1px solid ${c.green}30`,borderRadius:4,color:c.green,fontSize:9,cursor:"pointer",fontFamily:mono}}>Open output</button>
<button onClick={()=>setSection("audit")} style={{padding:"5px 8px",background:`${c.accent}10`,border:`1px solid ${c.accent}30`,borderRadius:4,color:c.accent,fontSize:9,cursor:"pointer",fontFamily:mono}}>Prompt/context</button>
</div>
</div>}
</div>
))}
</React.Fragment>}
{section==="artifacts"&&<ArtifactDeliveryPreview />}
{section==="audit"&&<ContextAuditPreview selectedMod={selectedMod} />}
</div>
</div>;
}
function ArtifactDeliveryPreview(){
const groups=[
{h:"Final outputs",items:[{n:"Revised Complaint Draft.docx",m:"Final Output",d:"pending email to will@will.com · save /Marex/Drafts",tag:"final"},{n:"Final Summary.md",m:"Final Output",d:"posts back to Q chat",tag:"final"}]},
{h:"Intermediate outputs",items:[{n:"Damages Memo.md",m:"Damages Memo",d:"used by Complaint Drafter + Red-Team; candidate to make findable",tag:"memo"},{n:"Red-Team Report.md",m:"Red-Team Review",d:"findable with summary after run",tag:"eval"}]},
{h:"Inputs / consumed docs",items:[{n:"Complaint.pdf",m:"Source Intake",d:"DOC25 processed · pages used by 5 modules",tag:"doc"},{n:"Exhibit A.pdf",m:"DOC25 Intake",d:"DOC25 processed · used by damages analysis",tag:"doc"}]},
{h:"Receipts",items:[{n:"Email delivery receipt",m:"Email Output",d:"will@will.com · May 4, 2026 11:31 AM",tag:"receipt"},{n:"File save receipt",m:"File Output",d:"/Marex/Drafts/Revised Complaint Draft.docx",tag:"receipt"}]},
];
return <div>
{groups.map(g=><div key={g.h} style={{marginBottom:14}}>
<div style={{fontSize:9,fontWeight:700,color:c.textTer,textTransform:"uppercase",letterSpacing:0.4,marginBottom:6}}>{g.h}</div>
{g.items.map(it=><div key={it.n} style={{padding:"9px 10px",border:`1px solid ${c.border}`,borderRadius:6,background:c.bgCard,marginBottom:6}}>
<div style={{display:"flex",justifyContent:"space-between",gap:8}}>
<div style={{fontSize:11,fontWeight:700,color:c.text,fontFamily:sans}}>{it.n}</div>
<span style={{fontSize:8,color:c.accent,border:`1px solid ${c.accent}30`,borderRadius:3,padding:"2px 5px"}}>{it.tag}</span>
</div>
<div style={{fontSize:9,color:c.textTer,marginTop:3}}>Produced by: {it.m}</div>
<div style={{fontSize:9,color:c.textSec,marginTop:3,lineHeight:1.45}}>{it.d}</div>
<div style={{display:"flex",gap:6,marginTop:7}}>
<button style={{padding:"4px 7px",background:`${c.green}10`,border:`1px solid ${c.green}25`,borderRadius:4,color:c.green,fontSize:9,cursor:"pointer",fontFamily:mono}}>Open</button>
<button style={{padding:"4px 7px",background:"transparent",border:`1px solid ${c.border}`,borderRadius:4,color:c.textSec,fontSize:9,cursor:"pointer",fontFamily:mono}}>Copy link</button>
<button style={{padding:"4px 7px",background:"transparent",border:`1px solid ${c.border}`,borderRadius:4,color:c.textSec,fontSize:9,cursor:"pointer",fontFamily:mono}}>Save/reuse</button>
</div>
</div>)}
</div>)}
</div>;
}
function ContextAuditPreview({selectedMod}){
const target = selectedMod?.name || "Damages Memo";
const injected=[
["Memory","Will prefers issue matrix + revision plan for complaint reviews."],
["Entity","Marex matter · Complaint work product · Defendants"],
["Procedure","Draft → Red Team → Revise pattern"],
["Tool","DOC25 retrieve_document_pages · Citation checker"],
["Document","Complaint.pdf pages 1–12 · Exhibit A.pdf"],
];
const excluded=[
["DOC73 spec review chat","unrelated task-run scope"],
["CSA research library","wrong library"],
];
return <div>
<div style={{fontSize:11,fontWeight:700,color:c.text,fontFamily:sans,marginBottom:6}}>Prompt & Context Inspector — {target}</div>
<div style={{fontSize:9,color:c.textTer,lineHeight:1.45,marginBottom:10}}>Shows what DOC24 injected above the module prompt and what it excluded. Feedback feeds DOC8/BDSM utility learning.</div>
<div style={{padding:9,border:`1px solid ${c.border}`,borderRadius:6,background:c.bgCard,marginBottom:10}}>
<div style={{fontSize:9,fontWeight:700,color:c.textTer,textTransform:"uppercase",letterSpacing:0.4,marginBottom:6}}>Prompt layers</div>
{['System / protected runtime','Task blueprint goals','Module instruction','DOC24 task-module packet','data_in + context_in','chain history projection'].map((l,i)=><div key={l} style={{display:"flex",gap:8,alignItems:"center",fontSize:9,color:c.textSec,padding:"3px 0"}}><span style={{fontFamily:mono,color:c.textTer,width:18}}>{i+1}</span>{l}</div>)}
<button style={{marginTop:6,padding:"5px 8px",background:"transparent",border:`1px solid ${c.border}`,borderRadius:4,color:c.textSec,fontSize:9,cursor:"pointer",fontFamily:mono}}>View effective prompt snapshot</button>
</div>
<div style={{fontSize:9,fontWeight:700,color:c.green,textTransform:"uppercase",letterSpacing:0.4,marginBottom:6}}>Injected</div>
{injected.map(([kind,text])=><div key={kind} style={{padding:"8px 9px",border:`1px solid ${c.green}20`,borderRadius:5,background:`${c.green}06`,marginBottom:6}}>
<div style={{fontSize:8,color:c.green,fontWeight:700}}>{kind}</div>
<div style={{fontSize:9,color:c.textSec,marginTop:3,lineHeight:1.45}}>{text}</div>
<div style={{display:"flex",gap:5,marginTop:6}}><button style={{fontSize:9,background:"transparent",border:`1px solid ${c.border}`,borderRadius:3,color:c.textSec}}>👍</button><button style={{fontSize:9,background:"transparent",border:`1px solid ${c.border}`,borderRadius:3,color:c.textSec}}>👎</button><button style={{fontSize:9,background:"transparent",border:`1px solid ${c.border}`,borderRadius:3,color:c.textSec}}>Comment</button></div>
</div>)}
<div style={{fontSize:9,fontWeight:700,color:c.amber,textTransform:"uppercase",letterSpacing:0.4,margin:"10px 0 6px"}}>Excluded</div>
{excluded.map(([what,why])=><div key={what} style={{padding:"8px 9px",border:`1px solid ${c.amber}20`,borderRadius:5,background:`${c.amber}06`,marginBottom:6}}>
<div style={{fontSize:9,color:c.text}}>{what}</div>
<div style={{fontSize:8.5,color:c.textTer,marginTop:2}}>Reason: {why}</div>
<div style={{display:"flex",gap:5,marginTop:6}}><button style={{fontSize:9,background:"transparent",border:`1px solid ${c.border}`,borderRadius:3,color:c.textSec}}>Correct</button><button style={{fontSize:9,background:"transparent",border:`1px solid ${c.border}`,borderRadius:3,color:c.textSec}}>Should include</button></div>
</div>)}
</div>;
}
function SourceWorkspacePreviewPanel({onClose,onOpenForum}){
const tiers=[
["Receipt","Checked Local Rule 7.1 page limit · no source card created","lookup receipt"],
["Source ref","Omnicare, 575 U.S. 175 · verified source exists","reference"],
["Source note","Second Circuit scienter case supports narrowed proposition only","short note"],
["Source card","Key SEC filing: 10-K risk disclosures and corrective disclosure timeline","full card"],
];
const needs=["Verify whether Safe Harbor argument requires separate heading","Find stronger authority for post-class-period disclosure theory","Check local rule for table of authorities threshold"];
return <div style={{width:430,borderLeft:`1px solid ${c.border}`,background:c.bgPanel,overflow:"hidden",flexShrink:0,display:"flex",flexDirection:"column"}}>
<PanelHeader title="Source Workspace" sub="task-scoped shared source/research substrate" onClose={onClose} />
<div style={{padding:12,borderBottom:`1px solid ${c.border}`,background:c.bgCard}}>
<div style={{fontSize:12,fontWeight:700,color:c.text,fontFamily:sans}}>MTD Opposition Source Workspace</div>
<div style={{fontSize:9,color:c.textSec,marginTop:4,lineHeight:1.45}}>Visible to configured future modules. Not auto-injected wholesale; modules receive listings, summaries, retrieval tools, or scoped packets.</div>
<div style={{display:"grid",gridTemplateColumns:"1fr 1fr 1fr",gap:6,marginTop:9}}>{[["42","source refs"],["11","source notes/cards"],["6","open needs"]].map(([n,l])=><div key={l} style={{padding:8,border:`1px solid ${c.border}`,borderRadius:6,background:c.bg}}><div style={{fontSize:15,fontWeight:800,color:c.green}}>{n}</div><div style={{fontSize:8,color:c.textTer}}>{l}</div></div>)}</div>
</div>
<div style={{flex:1,overflowY:"auto",padding:12}}>
<div style={{fontSize:9,fontWeight:700,color:c.textTer,textTransform:"uppercase",letterSpacing:0.4,marginBottom:6}}>Documentation tiers</div>
{tiers.map(([k,t,tag])=><div key={t} style={{padding:9,border:`1px solid ${c.border}`,borderRadius:6,background:c.bgCard,marginBottom:7}}>
<div style={{display:"flex",justifyContent:"space-between",gap:8}}><div style={{fontSize:10,fontWeight:700,color:c.text,fontFamily:sans}}>{k}</div><span style={{fontSize:8,color:c.green,border:`1px solid ${c.green}30`,borderRadius:3,padding:"2px 5px"}}>{tag}</span></div>
<div style={{fontSize:9,color:c.textSec,lineHeight:1.45,marginTop:4}}>{t}</div>
</div>)}
<div style={{fontSize:9,fontWeight:700,color:c.amber,textTransform:"uppercase",letterSpacing:0.4,margin:"12px 0 6px"}}>Open research/source needs</div>
{needs.map((n,i)=><div key={n} style={{padding:9,border:`1px solid ${c.amber}25`,borderRadius:6,background:`${c.amber}07`,marginBottom:7}}>
<div style={{fontSize:9.5,color:c.text,lineHeight:1.45}}>{n}</div>
<div style={{fontSize:8,color:c.textTer,marginTop:4}}>priority: {i===0?"blocking":"medium"} · route: Source Research module</div>
</div>)}
<button onClick={onOpenForum} style={{marginTop:4,padding:"7px 9px",background:`${c.cyan}10`,border:`1px solid ${c.cyan}35`,borderRadius:5,color:c.cyan,fontSize:10,cursor:"pointer",fontFamily:mono}}>Open related board posts</button>
</div>
</div>;
}
function TaskForumPreviewPanel({onClose,onOpenSource,onAskTaskAgent}){
const posts=[
{t:"9:04",a:"Source Research",kind:"source_added",body:"Added 31 source refs and 8 short notes. Three open source needs remain.",col:c.green},
{t:"9:24",a:"Research Sufficiency Evaluator",kind:"evaluation_finding",body:"Research sufficiency satisfied after adding separate Safe Harbor source check. Finding lifecycle: accepted by gate, not gospel; re-check if source workspace changes.",col:c.purple},
{t:"9:43",a:"Source Accuracy Gate",kind:"repair_instruction",body:"Draft v1 cites Omnicare too broadly. Repair: narrow proposition or find stronger source. ResearchNeed opened; Revision Pass notified.",col:c.red},
{t:"9:44",a:"User",kind:"user_guidance",body:"Keep Safe Harbor concise. Make it a fallback point, not the centerpiece.",col:c.amber},
{t:"9:45",a:"System",kind:"routing",body:"repair_instruction_out → Revision Pass.instruction_in; research_need_out → Deep Source Research; feedback_bundle_out → Task Forum.",col:c.cyan},
];
return <div style={{width:470,borderLeft:`1px solid ${c.border}`,background:c.bgPanel,overflow:"hidden",flexShrink:0,display:"flex",flexDirection:"column"}}>
<PanelHeader title="Task Run Board / Forum" sub="forum-style audit stream; active forum only if module is configured" onClose={onClose} />
<div style={{padding:12,borderBottom:`1px solid ${c.border}`,background:c.bgCard}}>
<div style={{fontSize:11,fontWeight:700,color:c.text,fontFamily:sans}}>Graph remains primary</div>
<div style={{fontSize:9,color:c.textSec,lineHeight:1.45,marginTop:4}}>This board shows what happened and coordinates configured modules. It is optional and module-shaped; it does not replace ports, cables, gates, loops, or explicit wiring.</div>
<div style={{display:"flex",gap:6,marginTop:9,flexWrap:"wrap"}}>{["All","Research","Evaluations","Repairs","User","Routing"].map((x,i)=><span key={x} style={{fontSize:9,padding:"4px 8px",border:`1px solid ${i===0?c.cyan+"45":c.border}`,background:i===0?`${c.cyan}10`:"transparent",borderRadius:4,color:i===0?c.cyan:c.textSec}}>{x}</span>)}</div>
</div>
<div style={{flex:1,overflowY:"auto",padding:12}}>
{posts.map((p,i)=><div key={i} style={{display:"grid",gridTemplateColumns:"42px 1fr",gap:9,marginBottom:10}}>
<div style={{fontSize:9,color:c.textTer,fontFamily:mono,paddingTop:3}}>{p.t}</div>
<div style={{padding:10,border:`1px solid ${p.col}30`,borderRadius:8,background:`${p.col}07`}}>
<div style={{display:"flex",justifyContent:"space-between",gap:8,alignItems:"center"}}><div style={{fontSize:10,fontWeight:700,color:c.text,fontFamily:sans}}>{p.a}</div><span style={{fontSize:8,color:p.col,border:`1px solid ${p.col}35`,borderRadius:3,padding:"2px 5px"}}>{p.kind}</span></div>
<div style={{fontSize:9.5,color:c.textSec,lineHeight:1.55,marginTop:5}}>{p.body}</div>
<div style={{display:"flex",gap:6,marginTop:8}}><button style={{padding:"4px 7px",background:"transparent",border:`1px solid ${c.border}`,borderRadius:4,color:c.textSec,fontSize:8.5,fontFamily:mono}}>Open refs</button><button style={{padding:"4px 7px",background:"transparent",border:`1px solid ${c.border}`,borderRadius:4,color:c.textSec,fontSize:8.5,fontFamily:mono}}>Mark stale</button></div>
</div>
</div>)}
<div style={{padding:10,border:`1px dashed ${c.border}`,borderRadius:8,background:c.bgCard,fontSize:9,color:c.textSec,lineHeight:1.5}}>Module participation is configured on the <span style={{color:c.cyan}}>Task Forum</span> module: who can read, post, ask questions, receive mentions, and consume board digests.</div>
</div>
<div style={{padding:12,borderTop:`1px solid ${c.border}`,display:"flex",gap:6}}>
<button onClick={onOpenSource} style={{padding:"7px 9px",background:`${c.green}10`,border:`1px solid ${c.green}35`,borderRadius:5,color:c.green,fontSize:10,cursor:"pointer",fontFamily:mono}}>Open Source Workspace</button>
<button onClick={onAskTaskAgent} style={{padding:"7px 9px",background:`${c.purple}10`,border:`1px solid ${c.purple}35`,borderRadius:5,color:c.purple,fontSize:10,cursor:"pointer",fontFamily:mono}}>Ask Task Agent about process</button>
</div>
</div>;
}
function TaskAgentSidePanel({selectedModuleId,modules,onClose,onOpenInspector}){
const selectedMod = modules.find(m=>m.id===selectedModuleId);
return <div style={{width:390,borderLeft:`1px solid ${c.border}`,background:c.bgPanel,overflow:"hidden",flexShrink:0,display:"flex",flexDirection:"column"}}>
<PanelHeader title="Task Agent" sub={selectedMod?`Context: ${selectedMod.name}`:"Context-aware task chat panel"} onClose={onClose} />
<div style={{padding:12,borderBottom:`1px solid ${c.border}`,background:c.bgCard}}>
<div style={{display:"grid",gridTemplateColumns:"1fr 1fr",gap:6}}>
{['Deep task review','Run validation / test','Improve selected prompt','Generate assessment'].map((q,i)=><button key={q} onClick={i===0?onOpenInspector:undefined} style={{padding:"7px 8px",background:`${c.purple}10`,border:`1px solid ${c.purple}25`,borderRadius:5,color:c.purple,fontSize:9.5,cursor:"pointer",fontFamily:mono,textAlign:"left"}}>{q}</button>)}
</div>
</div>
<div style={{flex:1,overflowY:"auto",padding:14,display:"flex",flexDirection:"column",gap:10}}>
<div style={{alignSelf:"flex-start",maxWidth:"86%",padding:"9px 10px",background:c.bgCard,border:`1px solid ${c.border}`,borderRadius:10,fontSize:11,color:c.textSec,lineHeight:1.55}}>
The run is currently in <strong style={{color:c.text}}>Source Accuracy Gate</strong>. Evaluator findings are being routed as repair instructions, research needs, and board posts. I can assess process gaps, not make the substantive legal call by default.
</div>
<div style={{alignSelf:"flex-start",maxWidth:"86%",padding:"9px 10px",background:c.bgCard,border:`1px solid ${c.border}`,borderRadius:10,fontSize:11,color:c.textSec,lineHeight:1.55}}>
I see a process question: if Source Accuracy fails repeatedly, consider adding a verification segment before drafting or wiring research_need_out more tightly to Source Research. This would be a visible graph patch, not hidden orchestration.
</div>
</div>
<div style={{padding:12,borderTop:`1px solid ${c.border}`}}>
<div style={{display:"flex",gap:6,alignItems:"center"}}><button title="attach" style={{width:32,height:32,border:`1px solid ${c.border}`,background:"transparent",borderRadius:7,color:c.textSec,cursor:"pointer"}}>+</button><input placeholder="Ask Task Agent…" style={{flex:1,boxSizing:"border-box",background:c.bg,border:`1px solid ${c.border}`,borderRadius:8,color:c.text,padding:"10px 11px",fontSize:11,fontFamily:sans,outline:"none"}} /><button title="send" style={{width:32,height:32,border:`1px solid ${c.purple}40`,background:`${c.purple}12`,borderRadius:7,color:c.purple,cursor:"pointer"}}>↑</button></div>
</div>
</div>;
}
function TasksPreviewPanel({onClose,onOpenInspector,onOpenGraph,onAskTaskAgent}){
const rows=[
{tab:"Active",name:"Draft Complaint — Marex",meta:"Running · 5/8 steps · 18m · $7.84",stats:"est. 9m left",color:c.accent},
{tab:"Scheduled",name:"Marex Filing Monitor",meta:"Next run today 5:00 PM · email/PACER trigger",stats:"last run $1.12",color:c.amber},
{tab:"Saved",name:"Complaint Evaluation",meta:"Reusable saved task · 18 runs",stats:"avg 42m · avg $14.80 · 89% success",color:c.green},
{tab:"Segment",name:"Motion Red-Team Segment",meta:"Reusable module group · used in 6 tasks",stats:"avg 11m · avg $4.20",color:c.cyan},
{tab:"Preset",name:"Complaint Red-Team Module",meta:"Module preset · used in 11 tasks",stats:"avg module $3.20",color:c.purple},
];
return <div style={{width:430,borderLeft:`1px solid ${c.border}`,background:c.bgPanel,overflow:"hidden",flexShrink:0,display:"flex",flexDirection:"column"}}>
<PanelHeader title="Tasks" sub="Active, scheduled, saved, reusable, segments and presets" onClose={onClose} />
<div style={{padding:12,borderBottom:`1px solid ${c.border}`}}>
<input placeholder="Search tasks, runs, artifacts, matters…" style={{width:"100%",boxSizing:"border-box",background:c.bg,border:`1px solid ${c.border}`,borderRadius:6,color:c.text,padding:"8px 10px",fontSize:11,fontFamily:sans,outline:"none"}} />
<div style={{display:"flex",gap:5,marginTop:8,flexWrap:"wrap"}}>{['Active','Scheduled','Saved','History','Segments','Presets'].map((t,i)=><span key={t} style={{fontSize:9,padding:"4px 8px",border:`1px solid ${i===0?c.accent+"40":c.border}`,background:i===0?`${c.accent}10`:"transparent",borderRadius:4,color:i===0?c.accent:c.textSec}}>{t}</span>)}</div>
</div>
<div style={{flex:1,overflowY:"auto",padding:12}}>
{rows.map(r=><div key={r.name} style={{padding:10,border:`1px solid ${c.border}`,borderRadius:7,background:c.bgCard,marginBottom:8}}>
<div style={{display:"flex",justifyContent:"space-between",alignItems:"center"}}>
<div style={{fontSize:11,fontWeight:700,color:c.text,fontFamily:sans}}>{r.name}</div>
<span style={{fontSize:8,color:r.color,border:`1px solid ${r.color}35`,background:`${r.color}10`,borderRadius:3,padding:"2px 5px"}}>{r.tab}</span>
</div>
<div style={{fontSize:9,color:c.textSec,marginTop:4}}>{r.meta}</div>
<div style={{fontSize:9,color:c.textTer,marginTop:3}}>{r.stats}</div>
<div style={{display:"flex",gap:6,marginTop:8}}>
<button onClick={onOpenGraph} style={{padding:"5px 8px",background:"transparent",border:`1px solid ${c.border}`,borderRadius:4,color:c.textSec,fontSize:9,cursor:"pointer",fontFamily:mono}}>Open graph</button>
<button onClick={onOpenInspector} style={{padding:"5px 8px",background:`${c.accent}10`,border:`1px solid ${c.accent}30`,borderRadius:4,color:c.accent,fontSize:9,cursor:"pointer",fontFamily:mono}}>Inspector</button>
<button onClick={onAskTaskAgent} style={{padding:"5px 8px",background:`${c.purple}10`,border:`1px solid ${c.purple}30`,borderRadius:4,color:c.purple,fontSize:9,cursor:"pointer",fontFamily:mono}}>Task Agent</button>
</div>
</div>)}
</div>
</div>;
}
// ═══ OUTGOING DATA SECTION (R2.9) ═══
function OutgoingDataSection({modId, modules, cables}) {
const [open, setOpen] = React.useState(false);
const mod = modules.find(m=>m.id===modId);
if (!mod) return null;
const outs = cables.filter(cb => cb.from === modId);
const destinations = outs.map(cb => {
const dest = modules.find(m=>m.id===cb.to);
return { name: dest ? dest.name : cb.to, port: cb.fromPort || "data_out" };
});
return (
<div style={{padding:"0 16px 0",borderBottom:`1px solid ${c.border}`}}>
<div onClick={()=>setOpen(!open)} style={{padding:"8px 0",cursor:"pointer",display:"flex",justifyContent:"space-between",alignItems:"center"}}>
<span style={{fontSize:9,fontWeight:600,color:c.textTer,textTransform:"uppercase",letterSpacing:0.4}}>Outgoing Data</span>
<span style={{fontSize:8,color:c.textTer}}>{open?"▼":"▶"}</span>
</div>
{open&&(
<div style={{paddingBottom:10}}>
{destinations.length === 0 ? (
<div style={{fontSize:9,color:c.textTer,fontStyle:"italic"}}>No outgoing connections — terminal module.</div>
) : (
<React.Fragment>
<div style={{display:"grid",gridTemplateColumns:"1fr 80px 50px",gap:"0 4px",marginBottom:4}}>
<span style={{fontSize:7.5,color:c.textTer,fontWeight:700,letterSpacing:0.3}}>DESTINATION</span>
<span style={{fontSize:7.5,color:c.textTer,fontWeight:700,letterSpacing:0.3}}>SENT FROM</span>
<span style={{fontSize:7.5,color:c.textTer,fontWeight:700,letterSpacing:0.3}}>PIN</span>
</div>
{destinations.map((d,i)=>(
<div key={i} style={{display:"grid",gridTemplateColumns:"1fr 80px 50px",gap:"1px 4px",alignItems:"center",padding:"2px 0",borderTop:i>0?`1px solid ${c.border}22`:"none"}}>
<span style={{fontSize:9,color:c.text,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"}}>{d.name}</span>
<span style={{fontSize:8,color:c.green,fontFamily:mono}}>{d.port}</span>
<span style={{fontSize:8,color:c.textTer}}>—</span>
</div>
))}
{/* Chain propagation row */}
<div style={{display:"grid",gridTemplateColumns:"1fr 80px 50px",gap:"1px 4px",alignItems:"center",padding:"3px 0",borderTop:`1px solid ${c.border}22`,marginTop:2}}>
<span style={{fontSize:9,color:c.textTer,fontStyle:"italic"}}>(downstream chain)</span>
<span style={{fontSize:8,color:c.textTer,fontFamily:mono}}>(added)</span>
<span style={{fontSize:8,color:mod.pinned?c.amber:c.textTer,fontWeight:mod.pinned?700:400}}>{mod.pinned?"📌 On":"Off"}</span>
</div>
{!mod.pinned&&<div style={{fontSize:8,color:c.textTer,marginTop:2}}>Subject to progressive decay downstream.</div>}
{mod.pinned&&<div style={{fontSize:8,color:c.amber,marginTop:2}}>Full fidelity in all downstream chain history.</div>}
</React.Fragment>
)}
</div>
)}
</div>
);
}
// ═══ INCOMING DATA TABLE (R2.9 — context flow visualization) ═══
function IncomingDataTable({modId, modules, cables}) {
const [open, setOpen] = React.useState(false);
const mod = modules.find(m=>m.id===modId);
if (!mod) return null;
// Build incoming data rows from cables
const rows = [];
const ins = cables.filter(cb => cb.to === modId);
ins.forEach(cb => {
const src = modules.find(m=>m.id===cb.from);
if (!src) return;
const port = cb.toPort || "data_in";
const receivedAt = port.includes("context") ? "context_in" : port.includes("instruct") ? "instruction_in" : "data_in";
rows.push({ name: src.name, receivedAt, format: "Full", pinned: false });
});
// Add simulated chain history items for demo
const upstreamIds = new Set();
function walkUp(mid, depth) {
if (depth > 6 || upstreamIds.has(mid)) return;
upstreamIds.add(mid);
cables.filter(cb=>cb.to===mid && (cb.toPort||"data_in")==="data_in").forEach(cb => {
const src = modules.find(m=>m.id===cb.from);
if (src && src.id !== modId && !ins.find(i=>i.from===src.id)) {
const d = depth;
const fmt = src.pinned ? "📌 Full" : d <= 2 ? "Full" : d <= 5 ? "Summarized" : "Listed";
rows.push({ name: src.name, receivedAt: "chain", format: fmt, pinned: !!src.pinned });
}
walkUp(cb.from, depth + 1);
});
}
ins.filter(cb=>(cb.toPort||"data_in")==="data_in").forEach(cb => walkUp(cb.from, 1));
const fmtColor = (f) => f.includes("📌") ? c.amber : f === "Full" ? c.green : f === "Summarized" ? c.cyan : c.textTer;
return (
<div style={{padding:"0 16px 0",borderBottom:`1px solid ${c.border}`}}>
<div onClick={()=>setOpen(!open)} style={{padding:"8px 0",cursor:"pointer",display:"flex",justifyContent:"space-between",alignItems:"center"}}>
<span style={{fontSize:9,fontWeight:600,color:c.textTer,textTransform:"uppercase",letterSpacing:0.4}}>Incoming Data</span>
<span style={{fontSize:8,color:c.textTer}}>{open?"▼":"▶"}</span>
</div>
{open&&(
<div style={{paddingBottom:10}}>
{rows.length === 0 ? (
<div style={{fontSize:9,color:c.textTer,fontStyle:"italic",padding:"4px 0"}}>No incoming data — first step in branch.</div>
) : (
<React.Fragment>
<div style={{display:"grid",gridTemplateColumns:"1fr 70px 70px",gap:"0 4px",marginBottom:4}}>
<span style={{fontSize:7.5,color:c.textTer,fontWeight:700,letterSpacing:0.3}}>MODULE</span>
<span style={{fontSize:7.5,color:c.textTer,fontWeight:700,letterSpacing:0.3}}>RECEIVED AT</span>
<span style={{fontSize:7.5,color:c.textTer,fontWeight:700,letterSpacing:0.3}}>FORMAT</span>
</div>
{rows.map((r,i)=>(
<div key={i} style={{display:"grid",gridTemplateColumns:"1fr 70px 70px",gap:"1px 4px",alignItems:"center",padding:"2px 0",borderTop:i>0?`1px solid ${c.border}22`:"none"}}>
<span style={{fontSize:9,color:c.text,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"}}>{r.name}</span>
<span style={{fontSize:8,color:r.receivedAt==="chain"?c.purple:c.cyan,fontFamily:mono}}>{r.receivedAt}</span>
<span style={{fontSize:8,color:fmtColor(r.format),fontWeight:r.pinned?700:400,fontFamily:mono}}>{r.format}</span>
</div>
))}
<div style={{fontSize:8,color:c.textTer,marginTop:6,fontStyle:"italic"}}>
Pre-run: structure only · Post-run: hover for token counts
</div>
<div style={{fontSize:8,color:c.accent,cursor:"pointer",marginTop:3}}>Full data flow → Run Inspector</div>
</React.Fragment>
)}
</div>
)}
</div>
);
}
// ═══ CONNECTIONS INFO (V4 style — compact, bottom-left) ═══
function ConnInfo({sel, modules, cables}) {
const selMod = modules.find(m => m.id === sel);
if (!selMod) return null;
const ins = cables.filter(cb => cb.to === sel);
const outs = cables.filter(cb => cb.from === sel);
return (
<div style={{
position:"fixed", bottom:60, left:74, zIndex:20,
background:`${c.bgCard}f0`, border:`1px solid ${c.border}`, borderRadius:8,
padding:"10px 12px", minWidth:240, maxWidth:320, backdropFilter:"blur(8px)",
boxShadow:"0 4px 16px #0006",
}}>
<div style={{fontSize:12,fontWeight:600,color:c.text,fontFamily:sans,marginBottom:1}}>{selMod.name}</div>
<div style={{fontSize:9,color:c.textTer,marginBottom:8,fontFamily:mono}}>{selMod.type}</div>
{ins.length > 0 && (
<div style={{marginBottom:6}}>
<div style={{display:"grid",gridTemplateColumns:"10px 1fr 65px",gap:"0 6px",alignItems:"center",marginBottom:3}}>
<span /><span style={{fontSize:8,color:c.cyan,fontWeight:700,letterSpacing:0.5}}>INPUTS</span>
<span style={{fontSize:8,color:c.textTer,fontWeight:700,letterSpacing:0.5}}>DATA TYPE</span>
</div>
{ins.map((cb, i) => {
const fm = modules.find(m => m.id === cb.from);
const portLabel = cb.toPort === "data_in" ? "process" : (cb.toPort || "").includes("context") ? "context" : (cb.toPort || "").includes("instruct") ? "instruction" : cb.toPort || "data_in";
return (
<div key={i} style={{display:"grid",gridTemplateColumns:"10px 1fr 65px",gap:"2px 6px",alignItems:"center"}}>
<span style={{width:6,height:6,borderRadius:"50%",background:getCableColor(cb,cables.indexOf(cb))}} />
<span style={{fontSize:10,color:c.textSec}}>← {fm ? fm.name : cb.from}</span>
<span style={{fontSize:9,color:c.cyan}}>{portLabel}</span>
</div>
);
})}
</div>
)}
{outs.length > 0 && (
<div>
<div style={{display:"grid",gridTemplateColumns:"10px 1fr 65px",gap:"0 6px",alignItems:"center",marginBottom:3}}>
<span /><span style={{fontSize:8,color:c.green,fontWeight:700,letterSpacing:0.5}}>OUTPUTS</span>
<span style={{fontSize:8,color:c.textTer,fontWeight:700,letterSpacing:0.5}}>DATA TYPE</span>
</div>
{outs.map((cb, i) => {
const tm = modules.find(m => m.id === cb.to);
return (
<div key={i} style={{display:"grid",gridTemplateColumns:"10px 1fr 65px",gap:"2px 6px",alignItems:"center"}}>
<span style={{width:6,height:6,borderRadius:"50%",background:getCableColor(cb,cables.indexOf(cb))}} />
<span style={{fontSize:10,color:c.textSec}}>→ {tm ? tm.name : cb.to}</span>
<span style={{fontSize:9,color:c.green}}>{cb.role}</span>
</div>
);
})}
</div>
)}
{ins.length === 0 && outs.length === 0 && (
<div style={{fontSize:10,color:c.textTer}}>No connections</div>
)}
</div>
);
}
// ═══ DETAIL PANEL CONNECTIONS (safe, no IIFE) ═══
function DetailConns({modId, cables, modules}) {
const mod = modules.find(m=>m.id===modId);
const ins = cables.filter(cb => cb.to === modId);
const outs = cables.filter(cb => cb.from === modId);
const [showIO, setShowIO] = React.useState(false);
// Group inputs by port
const portGroups = {};
ins.forEach(cb => {
const port = cb.toPort || "data_in";
if (!portGroups[port]) portGroups[port] = [];
portGroups[port].push(cb);
});
// Default participation per port type
const defaultParticipation = (port) => {
if (["stop_in","release_in","switch_a","switch_b","switch_c","switch_d"].includes(port)) return "Anytime";
return "Required";
};
const [participation, setParticipation] = React.useState({});
React.useEffect(()=>{
const init = {};
Object.keys(portGroups).forEach(p => { init[p] = participation[p] || defaultParticipation(p); });
setParticipation(init);
},[ins.length]);
// For mult junctions, show what downstream port they feed
const multLabel = mod && mod.isMult && mod.downstreamPort ? ` → ${mod.downstreamPort}` : "";
return (
<div style={{padding:"10px 16px",borderBottom:`1px solid ${c.border}`}}>
<DL>Connections & Input Handling{multLabel && <span style={{color:c.cyan,fontWeight:400}}>{multLabel}</span>}</DL>
{Object.keys(portGroups).length > 0 && <div style={{marginBottom:6}}>
<div style={{fontSize:8,color:c.cyan,fontWeight:700,marginBottom:3}}>INPUTS</div>
{Object.entries(portGroups).map(([port, cbs]) => (
<div key={port} style={{marginBottom:6,padding:"4px 6px",background:`${c.bgCard}`,borderRadius:4,border:`1px solid ${c.border}`}}>
<div style={{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:2}}>
<span style={{color:c.cyan,fontFamily:mono,fontSize:9}}>{port}</span>
<div style={{display:"flex",gap:2}}>
{["Required","Anytime"].map(mode=>(
<span key={mode} onClick={()=>setParticipation(prev=>({...prev,[port]:mode}))}
style={{fontSize:8,padding:"1px 6px",borderRadius:3,cursor:"pointer",fontWeight:600,
background:participation[port]===mode?(mode==="Required"?`${c.accent}20`:`${c.amber}20`):"transparent",
color:participation[port]===mode?(mode==="Required"?c.accent:c.amber):c.textTer,
border:`1px solid ${participation[port]===mode?(mode==="Required"?`${c.accent}40`:`${c.amber}40`):c.border}`
}}>{mode}</span>
))}
</div>
</div>
{cbs.map((cb,i)=>{
const fm=modules.find(m=>m.id===cb.from);
// If source is a mult junction, trace through to show original sources
if(fm && fm.isMult){
const junctionIns=cables.filter(jcb=>jcb.to===fm.id);
return <div key={i}>
<div style={{fontSize:9,color:c.purple,padding:"1px 0"}}>← ⫴ junction ({junctionIns.length} inputs → {port})</div>
{junctionIns.map((jcb,j)=>{
const origMod=modules.find(m=>m.id===jcb.from);
return <div key={j} style={{fontSize:9,color:c.textSec,padding:"1px 0 1px 12px"}}>
← <span style={{color:c.text}}>{origMod?origMod.name:"?"}</span>
<span style={{color:c.textTer,fontFamily:mono,fontSize:8,marginLeft:4}}>{jcb.fromPort||"data_out"}</span>
</div>;
})}
</div>;
}
return <div key={i} style={{fontSize:10,color:c.textSec,padding:"1px 0"}}>
<span>← </span><span style={{color:c.text}}>{fm?fm.name:"?"}</span>
<span style={{color:c.textTer,fontFamily:mono,fontSize:8,marginLeft:4}}>{cb.fromPort||"data_out"}</span>
</div>;
})}
</div>
))}
</div>}
{outs.length > 0 && <div>
<div style={{fontSize:8,color:c.green,fontWeight:700,marginBottom:3}}>OUTPUTS</div>
{outs.map((cb, i) => {
const tm = modules.find(m => m.id === cb.to);
return <div key={i} style={{fontSize:10,color:c.textSec,padding:"2px 0",display:"flex",justifyContent:"space-between"}}>
<span style={{color:c.green}}>{tm ? tm.name : "?"}</span>
<span style={{color:c.textTer,fontFamily:mono,fontSize:9}}>{cb.fromPort || "data_out"}</span>
</div>;
})}
</div>}
{/* Last Run I/O */}
<div style={{marginTop:8,borderTop:`1px solid ${c.border}`,paddingTop:6}}>
<div onClick={()=>setShowIO(!showIO)} style={{fontSize:9,color:c.textTer,cursor:"pointer",display:"flex",justifyContent:"space-between",alignItems:"center"}}>
<span style={{fontWeight:600}}>Last Run I/O</span>
<span style={{fontSize:8}}>{showIO?"▼":"▶"}</span>
</div>
{showIO&&<div style={{marginTop:4}}>
<div style={{fontSize:9,color:c.textSec,padding:"3px 6px",background:`${c.bgCard}`,borderRadius:3,border:`1px solid ${c.border}`,marginBottom:4}}>
<div style={{color:c.cyan,fontSize:8,fontWeight:600,marginBottom:2}}>INPUT</div>
<div style={{fontFamily:mono,fontSize:8,color:c.textTer,lineHeight:1.4}}>{"(no runs yet — run the task to see I/O)"}</div>
</div>
<div style={{fontSize:9,color:c.textSec,padding:"3px 6px",background:`${c.bgCard}`,borderRadius:3,border:`1px solid ${c.border}`}}>
<div style={{color:c.green,fontSize:8,fontWeight:600,marginBottom:2}}>OUTPUT</div>
<div style={{fontFamily:mono,fontSize:8,color:c.textTer,lineHeight:1.4}}>{"(no runs yet)"}</div>
</div>
<div style={{fontSize:8,color:c.accent,cursor:"pointer",marginTop:3}}>Full details in Run Inspector →</div>
</div>}
</div>
</div>
);
}
// ═══ FIELD RENDERER ═══
function RenderFields({fields}){
if(!fields) return null;
return fields.map((f,i)=>{
const mt=i>0;
if(f.t==="n") return <div key={i} style={{fontSize:10,color:c.textSec,padding:"4px 0",lineHeight:1.5,fontStyle:"italic"}}>{f.v}</div>;
if(f.t==="hd") return <div key={i} style={{fontSize:10,fontWeight:700,color:c.text,marginTop:12,marginBottom:2,paddingTop:8,borderTop:`1px solid ${c.border}`}}>{f.l}</div>;
if(f.t==="row") return(
<div key={i} style={{display:"grid",gridTemplateColumns:`repeat(${f.items.length},1fr)`,gap:8,marginTop:mt?6:0}}>
{f.items.map((it,j)=><div key={j}><DL>{it.l}</DL>{it.t==="i"?<DI value={it.v||""} placeholder={it.p}/>:<DS opts={it.o} v={it.v}/>}</div>)}
</div>
);
if(f.t==="i") return <div key={i}><DL mt={mt}>{f.l}</DL><DI value={f.v||""} placeholder={f.p}/></div>;
if(f.t==="s") return <div key={i}><DL mt={mt}>{f.l}</DL><DS opts={f.o} v={f.v}/></div>;
if(f.t==="ta") return <div key={i}><DL mt={mt}>{f.l}</DL><DT value={f.v||""} h={f.h||60}/></div>;
if(f.t==="c") return <div key={i} style={{marginTop:mt?5:0}}><DC label={f.l} checked={f.ck}/></div>;
if(f.t==="btn") return <div key={i} style={{marginTop:8}}><button style={{width:"100%",padding:"9px 0",background:`${c.accent}20`,border:`1px solid ${c.accent}50`,borderRadius:5,color:c.accent,fontSize:11,fontWeight:600,cursor:"pointer",fontFamily:mono}}>{f.l}</button></div>;
if(f.t==="tags") return(
<div key={i}><DL mt={mt}>{f.l}</DL>
<div style={{display:"flex",gap:4,flexWrap:"wrap"}}>
{(f.v||[]).map(tag=><span key={tag} style={{fontSize:9,padding:"2px 7px",background:`${c.accent}15`,border:`1px solid ${c.accent}30`,borderRadius:3,color:c.accent}}>{tag}</span>)}
<span style={{fontSize:9,padding:"3px 8px",border:`1px solid ${c.accent}40`,borderRadius:3,color:c.accent,cursor:"pointer",background:`${c.accent}10`}}>+ add</span>
</div>
</div>
);
return null;
});
}
// ═══ HELPERS ═══
function DL({children,mt}){return <div style={{fontSize:9,fontWeight:600,color:c.textTer,textTransform:"uppercase",letterSpacing:0.4,marginBottom:3,marginTop:mt?10:0}}>{children}</div>}
function DI({value,placeholder}){return <input defaultValue={value} placeholder={placeholder} style={{width:"100%",padding:"6px 8px",background:c.bg,border:`1px solid ${c.border}`,borderRadius:4,color:c.text,fontFamily:mono,fontSize:11,outline:"none",boxSizing:"border-box"}} />}
function DS({opts,v}){return <select defaultValue={v} style={{width:"100%",padding:"6px 8px",background:c.bg,border:`1px solid ${c.border}`,borderRadius:4,color:c.text,fontFamily:mono,fontSize:11,outline:"none",boxSizing:"border-box"}}>{opts.map(o=><option key={o}>{o}</option>)}</select>}
function DT({value,h}){return <textarea defaultValue={value} style={{width:"100%",height:h||80,background:c.bg,border:`1px solid ${c.border}`,borderRadius:4,color:c.text,fontFamily:mono,fontSize:10,padding:8,resize:"vertical",outline:"none",boxSizing:"border-box",lineHeight:1.5}} />}
function DC({label,checked}){return <label style={{display:"flex",alignItems:"center",gap:7,fontSize:11,color:c.textSec,cursor:"pointer"}}><span style={{width:14,height:14,borderRadius:3,border:`1.5px solid ${checked?c.accent:c.border}`,background:checked?c.accent:"transparent",display:"inline-flex",alignItems:"center",justifyContent:"center",fontSize:9,color:c.bg,flexShrink:0}}>{checked&&"✓"}</span>{label}</label>}
function Btn({children,active,primary,danger,onClick}){return <button onClick={onClick} style={{padding:"5px 12px",background:primary?c.accent:danger?`${c.red}20`:active?`${c.accent}15`:"transparent",border:`1px solid ${primary?c.accent:danger?c.red+"50":active?c.accent+"40":c.border}`,borderRadius:5,color:primary?c.bg:danger?c.red:active?c.accent:c.textSec,fontSize:11,fontWeight:primary||active?600:400,cursor:"pointer",fontFamily:mono}}>{children}</button>}
function TB({children,onClick,accent,danger}){return <button onClick={onClick} style={{padding:"4px 9px",background:accent?`${c.accent}20`:danger?`${c.red}15`:"transparent",border:`1px solid ${accent?c.accent+"40":danger?c.red+"30":"transparent"}`,borderRadius:3,color:accent?c.accent:danger?c.red:c.textSec,fontSize:10,cursor:"pointer",fontFamily:mono,fontWeight:accent||danger?600:400}}>{children}</button>}
function Sep(){return <div style={{width:1,height:22,background:c.border,margin:"0 2px"}} />}
function BDiv(){return <div style={{width:1,height:16,background:c.border}} />}
export default function CanvasV11AltD(){ return <QTaskWorkspaceShell />; }