import { app } from "../../scripts/app.js";
const NAME="continuity-director.dashboard", VER="0.8.20", KEY="continuity-director.language";
const groups={
locks:[["CDProjectLock","projectLock","pi-folder"],["CDCharacterLock","characterLock","pi-user"],["CDSceneLock","sceneLock","pi-map-marker"],["CDShotLock","shotLock","pi-video"],["CDManifestBuilder","manifest","pi-box"]],
directing:[["CDBatchDirector","batchDirector","pi-sitemap"],["CDReferenceHandoff","referenceHandoff","pi-link"]],
quality:[["CDQualityGate","qualityGate","pi-shield"],["CDTakeRanker","takeRanker","pi-sort-amount-down"],["CDContinuityReport","continuityReport","pi-search"]],
runtime:[["CDExecutionPlan","executionPlan","pi-bolt"]],
collaboration:[["CDAuditEvent","auditEvent","pi-history"],["CDThreeWayMerge","threeWayMerge","pi-code-branch"]],
export:[["CDExportPackage","exportPackage","pi-download"]],
reliability:[["CDVerifyPackage","verifyPackage","pi-check-circle"],["CDMigratePayload","migratePayload","pi-sync"],["CDRetryPolicy","retryPolicy","pi-refresh"],["CDQueueCheckpoint","queueCheckpoint","pi-save"],["CDIdempotencyKey","idempotencyKey","pi-key"],["CDEnvironmentLock","environmentLock","pi-lock"]]
};
const en={title:"Continuity Director",subtitle:"Production continuity control",overview:"Overview",nodes:"Nodes",guide:"Guide",language:"Language",cdNodes:"CD nodes",connections:"Connections",projectState:"Project state",ready:"Ready",empty:"Not configured",quickStart:"Quick start",quickStartHint:"Add the core production chain to the current workflow.",addStarter:"Add starter chain",fitNodes:"Focus CD nodes",nodeLibrary:"Node library",pipeline:"Recommended pipeline",health:"Workflow health",noNodes:"No Continuity Director nodes in this workflow.",active:"Active",locks:"Continuity locks",directing:"Directing",quality:"Quality",runtime:"Runtime",collaboration:"Collaboration",export:"Export",reliability:"Reliability",step1:"Lock project, characters, scenes, and shots.",step2:"Build a manifest and expand storyboard takes.",step3:"Rank takes and block failed quality gates.",step4:"Create execution waves, audit decisions, and export.",docs:"Open documentation",added:"Node added",starterAdded:"Starter chain added",projectLock:"Project Lock",characterLock:"Character Lock",sceneLock:"Scene Lock",shotLock:"Shot Lock",manifest:"Manifest Builder",batchDirector:"Batch Director",referenceHandoff:"Reference Handoff",qualityGate:"Quality Gate",takeRanker:"Take Ranker",continuityReport:"Continuity Report",executionPlan:"Execution Plan",auditEvent:"Audit Event",threeWayMerge:"Three-Way Merge",exportPackage:"Export Package",verifyPackage:"Verify Package",migratePayload:"Migrate Payload",retryPolicy:"Retry Policy",queueCheckpoint:"Queue Checkpoint",idempotencyKey:"Idempotency Key",environmentLock:"Environment Lock"};
const zh={title:"连续性导演",subtitle:"AI 视频生产连续性控制台",overview:"总览",nodes:"节点",guide:"流程指南",language:"语言",cdNodes:"插件节点",connections:"连接数",projectState:"项目状态",ready:"可用",empty:"未配置",quickStart:"快速开始",quickStartHint:"向当前工作流添加一套核心生产链。",addStarter:"添加入门链",fitNodes:"聚焦插件节点",nodeLibrary:"节点库",pipeline:"推荐流程",health:"工作流状态",noNodes:"当前工作流还没有连续性导演节点。",active:"已启用",locks:"连续性锁",directing:"导演编排",quality:"质量控制",runtime:"运行计划",collaboration:"协作审计",export:"导出",reliability:"可靠性",step1:"锁定项目、角色、场景和镜头。",step2:"生成清单,并将分镜扩展为多个 Take。",step3:"对 Take 排名,并阻止未通过质量门的结果。",step4:"生成执行波次、记录审计事件并导出。",docs:"打开文档",added:"节点已添加",starterAdded:"入门链已添加",projectLock:"项目锁",characterLock:"角色锁",sceneLock:"场景锁",shotLock:"镜头锁",manifest:"清单构建器",batchDirector:"批量导演",referenceHandoff:"参考帧承接",qualityGate:"质量门",takeRanker:"Take 排名",continuityReport:"连续性报告",executionPlan:"执行计划",auditEvent:"审计事件",threeWayMerge:"三方合并",exportPackage:"导出生产包",verifyPackage:"校验生产包",migratePayload:"迁移数据结构",retryPolicy:"重试策略",queueCheckpoint:"队列断点",idempotencyKey:"幂等键",environmentLock:"环境锁"};
const lang=()=>["en","zh-CN","bilingual"].includes(localStorage.getItem(KEY))?localStorage.getItem(KEY):"en";
const t=(k,l=lang())=>l==="bilingual"?`${en[k]||k} / ${zh[k]||en[k]||k}`:l==="zh-CN"?(zh[k]||en[k]||k):(en[k]||k);
const el=(tag,c,text)=>{const n=document.createElement(tag);if(c)n.className=c;if(text!==undefined)n.textContent=text;return n};
const ic=n=>el("i",`pi ${n||"pi-circle"}`);
function css(){if(document.querySelector("link[data-cd-style]"))return;const l=document.createElement("link");l.rel="stylesheet";l.href=new URL("./continuity_director.css",import.meta.url).href;l.dataset.cdStyle="1";document.head.append(l)}
function isCD(n){return String(n?.comfyClass||n?.type||"").startsWith("CD")}
function stats(){const nodes=(app.graph?._nodes||[]).filter(isCD),ids=new Set(nodes.map(n=>n.id));let links=0;for(const n of nodes)for(const o of n.outputs||[])for(const id of o.links||[]){const x=app.graph?.links?.[id];if(x&&ids.has(x.target_id))links++}return{nodes,links}}
function toast(a,b=""){app.extensionManager?.toast?.add?.({severity:"success",summary:a,detail:b,life:2000})}
function center(){const c=app.canvas;if(Array.isArray(c?.graph_mouse))return [...c.graph_mouse];const s=c?.ds?.scale||1,o=c?.ds?.offset||[0,0];return[(c?.canvas?.width||1200)/2/s-o[0],(c?.canvas?.height||800)/2/s-o[1]]}
function add(type,pos){const n=window.LiteGraph?.createNode?.(type);if(!n||!app.graph)return null;n.pos=pos||center();app.graph.add(n);app.canvas?.setDirty?.(true,true);toast(t("added"),n.title||type);return n}
function starter(){const b=center(),spec=[["CDProjectLock",0,0],["CDCharacterLock",0,250],["CDSceneLock",0,500],["CDShotLock",360,250],["CDManifestBuilder",720,120],["CDBatchDirector",1080,120],["CDExecutionPlan",1440,120],["CDExportPackage",1800,120]],m=new Map;for(const[x,a,c]of spec)m.set(x,add(x,[b[0]+a,b[1]+c]));const c=(a,ao,z,zi)=>m.get(a)?.connect?.(ao,m.get(z),zi);c("CDProjectLock",0,"CDShotLock",0);c("CDSceneLock",0,"CDShotLock",1);c("CDCharacterLock",0,"CDShotLock",2);c("CDProjectLock",0,"CDManifestBuilder",0);c("CDCharacterLock",0,"CDManifestBuilder",1);c("CDSceneLock",0,"CDManifestBuilder",2);c("CDShotLock",0,"CDManifestBuilder",3);c("CDManifestBuilder",0,"CDBatchDirector",0);c("CDBatchDirector",0,"CDExecutionPlan",0);c("CDManifestBuilder",0,"CDExportPackage",0);c("CDBatchDirector",0,"CDExportPackage",1);c("CDExecutionPlan",0,"CDExportPackage",2);toast(t("starterAdded"))}
function focus(){const n=stats().nodes;if(!n.length)return;if(n.length===1)return app.canvas?.centerOnNode?.(n[0]);app.canvas?.selectNodes?.(n);app.canvas?.fitViewToSelection?.();app.canvas?.setDirty?.(true,true)}
function button(label,kind,fn,icon){const b=el("button",`cd-button ${kind}`);b.append(ic(icon),document.createTextNode(label));b.onclick=fn;return b}
function header(host,l){const h=el("header","cd-header"),brand=el("div","cd-brand"),mark=el("div","cd-mark");mark.innerHTML='';const copy=el("div","cd-brand-copy");copy.append(el("strong","",t("title",l)),el("span","",t("subtitle",l)));brand.append(mark,copy);const s=document.createElement("select");s.className="cd-language";for(const[v,x]of[["en","English"],["zh-CN","中文"],["bilingual","EN / 中文"]]){const o=document.createElement("option");o.value=v;o.textContent=x;o.selected=l===v;s.append(o)}s.onchange=()=>{localStorage.setItem(KEY,s.value);render(host)};h.append(brand,s);return h}
function metric(icon,label,value,state=""){const x=el("div",`cd-metric ${state}`),v=el("div","cd-metric-icon"),c=el("div","cd-metric-copy");v.append(ic(icon));c.append(el("b","",String(value)),el("span","",label));x.append(v,c);return x}
function overview(l){const s=stats(),v=el("section","cd-view"),ms=el("div","cd-metrics");ms.append(metric("pi-box",t("cdNodes",l),s.nodes.length),metric("pi-share-alt",t("connections",l),s.links),metric(s.nodes.length?"pi-check-circle":"pi-minus-circle",t("projectState",l),t(s.nodes.length?"ready":"empty",l),s.nodes.length?"ok":"idle"));const q=el("div","cd-card"),qcopy=el("div");qcopy.append(el("h3","",t("quickStart",l)),el("p","",t("quickStartHint",l)));const acts=el("div","cd-actions");acts.append(button(t("addStarter",l),"primary",starter,"pi-plus"),button(t("fitNodes",l),"secondary",focus,"pi-expand"));q.append(qcopy,acts);const health=el("div","cd-card");health.append(el("h3","",t("health",l)));const row=el("div","cd-health"),dot=el("span",`cd-dot ${s.nodes.length?"ok":"idle"}`);row.append(dot,el("span","",s.nodes.length?t("active",l):t("noNodes",l)));health.append(row);v.append(ms,q,health);return v}
function library(l){const v=el("section","cd-view"),head=el("div","cd-section-head");head.append(el("h3","",t("nodeLibrary",l)),el("span","cd-chip",`v${VER}`));v.append(head);for(const[k,list]of Object.entries(groups)){const g=el("div","cd-group"),gh=el("div","cd-group-head");gh.append(el("span","",t(k,l)),el("span","cd-chip",String(list.length)));g.append(gh);for(const[type,label,icon]of list){const b=el("button","cd-node-button"),i=el("span","cd-node-icon"),c=el("span","cd-node-copy");i.append(ic(icon));c.append(el("b","",t(label,l)),el("small","",type));b.append(i,c,ic("pi-plus"));b.onclick=()=>add(type);g.append(b)}v.append(g)}return v}
function guide(l){const v=el("section","cd-view"),line=el("div","cd-timeline");v.append(el("h3","",t("pipeline",l)));["step1","step2","step3","step4"].forEach((k,i)=>{const r=el("div","cd-step");r.append(el("span","cd-step-no",String(i+1)),el("p","",t(k,l)));line.append(r)});v.append(line,button(t("docs",l),"secondary",()=>window.open("https://github.com/xinjian0101/continuity-director","_blank","noopener,noreferrer"),"pi-external-link"));return v}
function render(host){if(!host)return;const l=lang(),root=el("div","cd-dashboard"),tabs=el("nav","cd-tabs"),main=el("main","cd-content"),views={overview:overview(l),nodes:library(l),guide:guide(l)};host.replaceChildren();host.classList.add("cd-host");root.append(header(host,l));let active=host.dataset.cdTab||"overview";const show=id=>{active=id;host.dataset.cdTab=id;tabs.querySelectorAll("button").forEach(b=>b.classList.toggle("active",b.dataset.tab===id));main.replaceChildren(views[id])};for(const[id,icon]of[["overview","pi-chart-bar"],["nodes","pi-th-large"],["guide","pi-compass"]]){const b=el("button","cd-tab");b.dataset.tab=id;b.append(ic(icon),document.createTextNode(t(id,l)));b.onclick=()=>show(id);tabs.append(b)}const foot=el("footer","cd-footer",`Continuity Director v${VER} · 20 nodes`);root.append(tabs,main,foot);host.append(root);show(active)}
function fallback(){if(document.querySelector(".cd-launcher"))return;const b=el("button","cd-launcher","CD"),p=el("aside","cd-floating");p.hidden=true;b.onclick=()=>{p.hidden=!p.hidden;if(!p.hidden)render(p)};document.body.append(b,p)}
function style(n){if(!isCD(n))return;const type=n.comfyClass||n.type||"",p=type.includes("Quality")||type.includes("Report")||type.includes("Ranker")?["#0f766e","#102f2f"]:type.includes("Audit")||type.includes("Merge")?["#7c3aed","#281b3d"]:type.includes("Verify")||type.includes("Migrate")||type.includes("Retry")||type.includes("Checkpoint")||type.includes("Idempotency")||type.includes("Environment")?["#0369a1","#102a43"]:type.includes("Export")?["#c2410c","#3b2015"]:["#1d4ed8","#172554"];n.color=p[0];n.bgcolor=p[1];n.shape=window.LiteGraph?.ROUND_SHAPE??n.shape;n.size=[Math.max(n.size?.[0]||260,280),n.size?.[1]||180]}
app.registerExtension({name:NAME,aboutPageBadges:[{label:`Continuity Director v${VER}`,url:"https://github.com/xinjian0101/continuity-director",icon:"pi pi-video"}],commands:[{id:"continuity-director.add-starter",label:"Continuity Director: Add starter chain",icon:"pi pi-plus",function:starter},{id:"continuity-director.focus",label:"Continuity Director: Focus nodes",icon:"pi pi-expand",function:focus}],menuCommands:[{path:["Continuity Director"],commands:["continuity-director.add-starter","continuity-director.focus"]}],keybindings:[{combo:{key:"D",ctrl:true,shift:true},commandId:"continuity-director.add-starter"}],async setup(){css();try{if(app.extensionManager?.registerSidebarTab)app.extensionManager.registerSidebarTab({id:"continuity-director",icon:"pi pi-video",title:"Continuity Director",tooltip:"Continuity Director",type:"custom",render});else fallback()}catch(e){console.warn("[Continuity Director] sidebar fallback",e);fallback()}},nodeCreated:style,loadedGraphNode:style});