flowsvg 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/CHANGELOG.md +54 -1
  2. package/PATTERNS.md +71 -4
  3. package/README.md +148 -438
  4. package/README.zh-CN.md +148 -438
  5. package/dist/adapters/react.d.ts +55 -0
  6. package/dist/adapters/react.js +138 -0
  7. package/dist/adapters/vue.d.ts +158 -0
  8. package/dist/adapters/vue.js +177 -0
  9. package/dist/adapters/web-component.d.ts +111 -0
  10. package/dist/adapters/web-component.js +425 -0
  11. package/dist/{player-controls.d.ts → browser/controls.d.ts} +7 -3
  12. package/dist/{player-controls.js → browser/controls.js} +23 -31
  13. package/dist/browser/export-controls.d.ts +32 -0
  14. package/dist/browser/export-controls.js +151 -0
  15. package/dist/browser/player.d.ts +77 -0
  16. package/dist/browser/player.js +412 -0
  17. package/dist/browser/select-control.d.ts +33 -0
  18. package/dist/browser/select-control.js +171 -0
  19. package/dist/browser.d.ts +5 -69
  20. package/dist/browser.js +3 -353
  21. package/dist/core/color.d.ts +3 -0
  22. package/dist/core/color.js +18 -0
  23. package/dist/core/diagram.d.ts +40 -0
  24. package/dist/core/diagram.js +481 -0
  25. package/dist/core/edge-geometry.d.ts +45 -0
  26. package/dist/core/edge-geometry.js +609 -0
  27. package/dist/{core.d.ts → core/model.d.ts} +37 -61
  28. package/dist/core/model.js +1 -0
  29. package/dist/core/node-content-measurement.d.ts +34 -0
  30. package/dist/core/node-content-measurement.js +66 -0
  31. package/dist/{node-content-paint.d.ts → core/node-content-paint.d.ts} +2 -2
  32. package/dist/{node-content-paint.js → core/node-content-paint.js} +11 -20
  33. package/dist/{node-content.d.ts → core/node-content.d.ts} +18 -1
  34. package/dist/{node-content.js → core/node-content.js} +30 -6
  35. package/dist/core/svg-number.d.ts +5 -0
  36. package/dist/core/svg-number.js +7 -0
  37. package/dist/{theme.d.ts → core/theme.d.ts} +6 -0
  38. package/dist/core/theme.js +17 -0
  39. package/dist/{timeline.d.ts → core/timeline.d.ts} +11 -5
  40. package/dist/{timeline.js → core/timeline.js} +30 -22
  41. package/dist/core/validation.d.ts +11 -0
  42. package/dist/core/validation.js +399 -0
  43. package/dist/export/browser.d.ts +9 -0
  44. package/dist/export/browser.js +172 -0
  45. package/dist/export/captions.d.ts +18 -0
  46. package/dist/export/captions.js +44 -0
  47. package/dist/export/plan.d.ts +25 -0
  48. package/dist/export/plan.js +94 -0
  49. package/dist/export/types.d.ts +64 -0
  50. package/dist/export/types.js +9 -0
  51. package/dist/export/worker-source.d.ts +1 -0
  52. package/dist/export/worker-source.js +1 -0
  53. package/dist/export.d.ts +3 -0
  54. package/dist/export.js +2 -0
  55. package/dist/index.d.ts +7 -1
  56. package/dist/index.js +3 -1
  57. package/dist/node/cli.d.ts +2 -0
  58. package/dist/node/cli.js +72 -0
  59. package/dist/node/gif.d.ts +16 -0
  60. package/dist/node/gif.js +184 -0
  61. package/dist/node/html.d.ts +19 -0
  62. package/dist/node/html.js +61 -0
  63. package/dist/node/output.d.ts +2 -0
  64. package/dist/node/output.js +22 -0
  65. package/dist/node.d.ts +6 -0
  66. package/dist/node.js +3 -0
  67. package/dist/portable/LICENSE.esbuild.txt +21 -0
  68. package/dist/portable/LICENSE.flowsvg.txt +21 -0
  69. package/dist/portable/LICENSE.gifenc.txt +21 -0
  70. package/dist/portable/export.js +5 -0
  71. package/dist/portable/manifest.json +36 -0
  72. package/dist/portable/player.js +2 -0
  73. package/dist/portable/styles.css +227 -0
  74. package/dist/react.d.ts +3 -48
  75. package/dist/react.js +2 -126
  76. package/dist/styles.css +194 -14
  77. package/dist/vue.d.ts +3 -132
  78. package/dist/vue.js +2 -161
  79. package/dist/web-component.d.ts +3 -103
  80. package/dist/web-component.js +2 -396
  81. package/docs/images/entity-graph.png +0 -0
  82. package/docs/images/facts-timeline.png +0 -0
  83. package/docs/images/facts-timeline.zh-CN.png +0 -0
  84. package/docs/images/flowsvg-wordmark.gif +0 -0
  85. package/docs/images/node-content.png +0 -0
  86. package/docs/images/numbered-list.png +0 -0
  87. package/docs/images/numbered-list.zh-CN.png +0 -0
  88. package/docs/images/quick-start.png +0 -0
  89. package/docs/images/record-tags.png +0 -0
  90. package/package.json +38 -6
  91. package/dist/core.js +0 -1149
  92. package/dist/theme.js +0 -6
  93. /package/dist/{player-accessibility.d.ts → browser/accessibility.d.ts} +0 -0
  94. /package/dist/{player-accessibility.js → browser/accessibility.js} +0 -0
  95. /package/dist/{player-lifecycle.d.ts → browser/lifecycle.d.ts} +0 -0
  96. /package/dist/{player-lifecycle.js → browser/lifecycle.js} +0 -0
  97. /package/dist/{svg-dom.d.ts → browser/svg-dom.d.ts} +0 -0
  98. /package/dist/{svg-dom.js → browser/svg-dom.js} +0 -0
@@ -0,0 +1,2 @@
1
+ (()=>{var Fe=Object.defineProperty;var je=(e,t)=>()=>(e&&(t=e(e=0)),t);var Be=(e,t)=>{for(var n in t)Fe(e,n,{get:t[n],enumerable:!0})};var Ht={};Be(Ht,{exportGif:()=>We});async function We(...e){let t=globalThis.FlowSVGExport;if(!t)throw new Error("GIF export is not embedded. Generate this HTML with gifExport: true.");return t.exportGif(...e)}var Ft=je(()=>{});var it=class{constructor(t,n){this.owner=t;this.captureInput=n;this.element=t.createElement("span"),this.element.className="flowsvg-export-controls",this.startButton=t.createElement("button"),this.startButton.type="button",this.startButton.textContent="Export GIF",this.startButton.setAttribute("aria-label","Export GIF"),this.cancelButton=t.createElement("button"),this.cancelButton.type="button",this.cancelButton.textContent="Cancel export",this.cancelButton.hidden=!0,this.progress=t.createElement("progress"),this.progress.setAttribute("aria-label","GIF export progress"),this.progress.hidden=!0,this.status=t.createElement("span"),this.status.setAttribute("role","status"),this.status.setAttribute("aria-live","polite"),this.element.append(this.startButton,this.cancelButton,this.progress,this.status),this.startButton.addEventListener("click",this.start),this.cancelButton.addEventListener("click",this.cancel)}element;startButton;cancelButton;status;progress;task;destroyed=!1;downloadUrls=new Map;destroy(){this.destroyed=!0,this.task?.abort(),this.startButton.removeEventListener("click",this.start),this.cancelButton.removeEventListener("click",this.cancel);for(let[t,n]of this.downloadUrls)clearTimeout(n),URL.revokeObjectURL(t);this.downloadUrls.clear()}start=()=>{if(this.destroyed||this.task)return;let t=new AbortController;this.task=t;let n=this.captureInput();this.startButton.disabled=!0,this.cancelButton.hidden=!1,this.progress.hidden=!1,this.progress.removeAttribute("value"),this.status.textContent="Preparing GIF\u2026",this.run(t,n)};cancel=()=>{this.task?.abort()};async run(t,n){try{let{exportGif:i}=await Ve(t.signal);if(!this.isActive(t))return;let o=await i(n.document,{theme:n.theme,signal:t.signal,onProgress:r=>{this.isActive(t)&&this.renderProgress(r)}});if(!this.isActive(t))return;this.download(o,n.document.title),this.status.textContent="GIF export complete."}catch(i){if(this.destroyed||this.task!==t)return;this.status.textContent=t.signal.aborted?"GIF export cancelled.":`GIF export failed: ${i instanceof Error?i.message:String(i)}`}finally{this.task===t&&(this.task=void 0,this.destroyed||(t.signal.aborted&&(this.status.textContent="GIF export cancelled."),this.startButton.disabled=!1,this.cancelButton.hidden=!0,this.progress.hidden=!0))}}isActive(t){return!this.destroyed&&this.task===t&&!t.signal.aborted}renderProgress(t){this.progress.max=Math.max(1,t.total),this.progress.value=t.completed;let n={preparing:"Preparing",sampling:"Sampling",encoding:"Encoding",complete:"Completing"};this.status.textContent=`${n[t.phase]} GIF: ${t.completed}/${t.total}`}download(t,n){let i=URL.createObjectURL(new Blob([new Uint8Array(t)],{type:"image/gif"})),o=this.owner.createElement("a");o.href=i,o.download=ze(n);try{this.owner.body.append(o),o.click()}finally{o.remove();let r=setTimeout(()=>{URL.revokeObjectURL(i),this.downloadUrls.delete(i)},0);this.downloadUrls.set(i,r)}}};function ze(e){let t=(e??"").replace(/[<>:"/\\|?*\u0000-\u001f\u007f]/g,"_").replace(/^[.\s]+|[.\s]+$/g,"").slice(0,120);return`${(/^(con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(t)?`_${t}`:t)||"FlowSVG"}.gif`}async function Ve(e){e.throwIfAborted();let t=()=>{},n=new Promise((i,o)=>{let r=()=>o(e.reason);e.addEventListener("abort",r,{once:!0}),t=()=>e.removeEventListener("abort",r)});try{return await Promise.race([Promise.resolve().then(()=>(Ft(),Ht)),n])}finally{t()}}var jt="#000000",Bt="#ffffff";function ot(e,t,n){return t===0?e:{...e,...e.graphs?{graphs:e.graphs.map(i=>({...i,nodes:i.nodes.map(o=>({...o,fill:bt(o.fill,n)})),edges:i.edges.map(o=>({...o,color:bt(o.color,n)}))}))}:{},lines:e.lines.map(i=>{let r=(i.role==="tag"?e.tags.find(l=>l.blockIndex===i.blockIndex&&l.itemIndex===i.itemIndex&&l.tagIndex===i.tagIndex):void 0)?.fill??n,s=l=>bt(l,r),a=i.spans.map(l=>({...l,color:l.explicitColor?l.color:s(l.color)}));return{...i,spans:a,color:a[0]?.color??s(i.color)}})}}function bt(e,t){return xt(e,t)>=4.5?e:xt(jt,t)>=xt(Bt,t)?jt:Bt}function xt(e,t){let n=Wt(e),i=Wt(t);return(Math.max(n,i)+.05)/(Math.min(n,i)+.05)}function Wt(e){let t=e.slice(1),n=t.length<=4?[...t.slice(0,3)].map(o=>o+o).join(""):t,i=[0,2,4].map(o=>Number.parseInt(n.slice(o,o+2),16)/255).map(o=>o<=.04045?o/12.92:((o+.055)/1.055)**2.4);return i[0]*.2126+i[1]*.7152+i[2]*.0722}function rt(e,t){let n=e.ownerDocument.createElement("template");n.innerHTML=t;let i=n.content.firstElementChild;if(!i)throw new Error("Core SVG output is empty");let o=e.firstElementChild;if(!o||o.tagName!==i.tagName){e.replaceChildren(i);return}zt(o,i)}function zt(e,t){for(let n of Array.from(e.attributes))t.hasAttribute(n.name)||e.removeAttribute(n.name);for(let n of Array.from(t.attributes))e.getAttribute(n.name)!==n.value&&e.setAttribute(n.name,n.value);if(t.children.length===0){e.textContent!==t.textContent&&(e.textContent=t.textContent);return}Ue(e,t)}function Ue(e,t){let n=Array.from(e.children),i=new Map;for(let s of n){let a=vt(s);a!==void 0&&i.set(a,s)}let o=new Set,r=e.firstElementChild;for(let s of Array.from(t.children)){let a=vt(s),l=a===void 0?n.find(c=>!o.has(c)&&vt(c)===void 0&&c.tagName===s.tagName):i.get(a),d=l?.tagName===s.tagName?l:s;o.add(d),d!==r&&e.insertBefore(d,r),d===l&&zt(d,s),r=d.nextElementSibling}for(let s of n)o.has(s)||s.remove()}function vt(e){let t=e.getAttribute("data-id");if(t!==null)return`object:${t}`;let n=e.getAttribute("id");return n===null?void 0:`id:${n}`}function Vt(e,t,n){let i=e.ownerDocument,o=e.style.display,r=e.style.flexDirection;e.style.display="flex",e.style.flexDirection="column",t.style.flex="1 1 auto",t.style.minHeight="0",t.style.minWidth="0",t.style.overflow="hidden";let s=i.createElement("style");s.textContent=".flowsvg-canvas > svg { display: block; width: 100%; height: 100%; }",e.append(s);let a=()=>{i.visibilityState==="hidden"&&n()};return i.addEventListener("visibilitychange",a),()=>{i.removeEventListener("visibilitychange",a),s.remove(),e.style.display=o,e.style.flexDirection=r}}var Ke="(prefers-reduced-motion: reduce)";function Ut(e,t,n){if(t!==void 0)return{value:t,cleanup:()=>{}};let i=e.defaultView?.matchMedia(Ke),o=r=>n(r.matches);return i?.addEventListener("change",o),{value:i?.matches??!1,cleanup:()=>i?.removeEventListener("change",o)}}function Kt(e,t){e.tabIndex=0,e.setAttribute("aria-label","Diagram playback"),e.setAttribute("aria-keyshortcuts","Space ArrowLeft ArrowRight");let n=i=>{if(i.defaultPrevented||i.repeat||i.altKey||i.ctrlKey||i.metaKey||i.shiftKey)return;let o=i.target;o instanceof HTMLElement&&(o.isContentEditable||o.closest("button,input,select,textarea,a[href]"))||(i.key===" "?(i.preventDefault(),t.toggle()):(i.key==="ArrowLeft"||i.key==="ArrowRight")&&(i.preventDefault(),t.step(i.key==="ArrowLeft"?-1:1)))};return e.addEventListener("keydown",n),()=>e.removeEventListener("keydown",n)}var Ye=0,U=class{constructor(t,n,i){this.onChange=i;this.element=t.createElement("div"),this.element.className="flowsvg-select",this.trigger=t.createElement("button"),this.trigger.type="button",this.trigger.className="flowsvg-select-trigger",this.trigger.setAttribute("role","combobox"),this.trigger.setAttribute("aria-label",n),this.trigger.setAttribute("aria-haspopup","listbox"),this.trigger.setAttribute("aria-expanded","false"),this.list=t.createElement("div"),this.list.id=`flowsvg-choices-${++Ye}`,this.list.className="flowsvg-select-options",this.list.setAttribute("role","listbox"),this.list.setAttribute("aria-label",n),this.list.hidden=!0,this.trigger.setAttribute("aria-controls",this.list.id),this.trigger.addEventListener("click",this.toggle),this.trigger.addEventListener("keydown",this.onKey),this.element.addEventListener("focusout",this.onBlur),this.element.append(this.trigger,this.list)}element;trigger;list;choices=[];activeIndex=0;destroyed=!1;render(t,n,i=!1){if(this.destroyed)return;(t.length!==this.choices.length||t.some((r,s)=>r.value!==this.choices[s]?.value||r.label!==this.choices[s]?.label))&&(this.close(),this.choices=t,this.list.replaceChildren(...t.map((r,s)=>this.createOption(r,s)))),this.trigger.value=n,this.trigger.dataset.value=n,this.trigger.textContent=t.find(r=>r.value===n)?.label??"\u2014",this.trigger.disabled=i||t.length===0;for(let r of Array.from(this.list.children))r.setAttribute("aria-selected",String(r.getAttribute("data-value")===n));this.trigger.disabled&&this.close()}destroy(){this.close(),this.destroyed=!0,this.trigger.removeEventListener("click",this.toggle),this.trigger.removeEventListener("keydown",this.onKey),this.element.removeEventListener("focusout",this.onBlur),this.element.remove()}createOption(t,n){let i=this.element.ownerDocument.createElement("div");return i.id=`${this.list.id}-${n}`,i.setAttribute("role","option"),i.dataset.value=t.value,i.textContent=t.label,i.addEventListener("pointerdown",o=>o.preventDefault()),i.addEventListener("click",()=>this.commit(n)),i}toggle=()=>{this.list.hidden?this.open():this.close()};open(){this.destroyed||this.trigger.disabled||!this.list.hidden||(this.list.hidden=!1,this.trigger.setAttribute("aria-expanded","true"),this.highlight(Math.max(0,this.choices.findIndex(t=>t.value===this.trigger.value))),this.element.ownerDocument.addEventListener("pointerdown",this.onOutside))}close(){this.list.hidden=!0,this.trigger.setAttribute("aria-expanded","false"),this.trigger.removeAttribute("aria-activedescendant"),this.element.ownerDocument.removeEventListener("pointerdown",this.onOutside)}highlight(t){this.activeIndex=Math.max(0,Math.min(this.choices.length-1,t)),Array.from(this.list.children).forEach((i,o)=>{i.toggleAttribute("data-active",o===this.activeIndex)});let n=this.list.children[this.activeIndex];n&&(this.trigger.setAttribute("aria-activedescendant",n.id),n.scrollIntoView({block:"nearest"}))}commit(t){if(this.destroyed||this.trigger.disabled||this.list.hidden)return;let n=this.choices[t];this.close(),n&&n.value!==this.trigger.value&&this.onChange(n.value)}onOutside=t=>{t.composedPath().includes(this.element)||this.close()};onBlur=t=>{let n=t.relatedTarget;(!(n instanceof this.element.ownerDocument.defaultView.Node)||!this.element.contains(n))&&this.close()};onKey=t=>{if(!(t.altKey||t.ctrlKey||t.metaKey||this.trigger.disabled)){if(t.key==="Tab"){this.close();return}if(t.key==="Escape"){this.list.hidden||(t.preventDefault(),t.stopPropagation(),this.close());return}if(["Enter"," "].includes(t.key)){t.preventDefault(),this.list.hidden?this.open():this.commit(this.activeIndex);return}if(["ArrowDown","ArrowUp","Home","End"].includes(t.key)){t.preventDefault();let n=!this.list.hidden;this.open(),t.key==="Home"?this.highlight(0):t.key==="End"?this.highlight(this.choices.length-1):n&&this.highlight(this.activeIndex+(t.key==="ArrowDown"?1:-1));return}if(t.key.length===1&&t.key.trim()){let n=this.list.hidden?-1:this.activeIndex;this.open();let o=this.choices.map((r,s)=>(n+s+1)%this.choices.length).find(r=>this.choices[r].label.toLocaleLowerCase().startsWith(t.key.toLocaleLowerCase()));o!==void 0&&(t.preventDefault(),this.highlight(o))}}}};var Xe=[.5,1,1.5,2],at=class{element;play;loop;previous;next;progress;scene;speed;sceneChoices;constructor(t,n){this.element=t.createElement("div"),this.element.className="flowsvg-controls",this.play=st(t,"Play",n.toggle),this.play.className="flowsvg-play-toggle",this.loop=st(t,"Loop",n.toggleLoop),this.loop.className="flowsvg-loop-toggle",this.previous=st(t,"Previous step",()=>n.step(-1)),this.next=st(t,"Next step",()=>n.step(1)),this.progress=t.createElement("input"),this.progress.type="range",this.progress.min="0",this.progress.step="any",this.progress.setAttribute("aria-label","Progress"),this.progress.addEventListener("input",()=>n.seek(Number(this.progress.value))),this.scene=new U(t,"Scene",i=>{let o=this.sceneChoices?.find(r=>r.id===i);o&&n.seek(o.startMs)}),this.speed=new U(t,"Speed",i=>n.setSpeed(Number(i))),this.element.append(this.previous,this.play,this.next,this.progress,this.scene.element,this.speed.element,this.loop)}render(t){let n=t.totalDuration===0;this.play.textContent=t.playing?"Pause":"Play",this.play.setAttribute("aria-label",this.play.textContent),this.play.disabled=n||!t.playing&&!t.loop&&t.timeMs>=t.totalDuration,this.loop.setAttribute("aria-pressed",String(t.loop)),this.loop.disabled=n,this.previous.disabled=n||t.timeMs<=0,this.next.disabled=n||t.timeMs>=t.totalDuration,this.progress.disabled=n,this.progress.max=String(t.totalDuration),this.progress.value=String(t.timeMs),this.sceneChoices=t.scenes,this.scene.render(t.scenes.map(o=>({value:o.id,label:o.label})),t.sceneId??"",n);let i=[...new Set([...Xe,t.speed])].sort((o,r)=>o-r);this.speed.render(i.map(o=>({value:String(o),label:`${o}\xD7`})),String(t.speed),n)}destroy(){this.scene.destroy(),this.speed.destroy()}};function st(e,t,n){let i=e.createElement("button");return i.type="button",i.textContent=t,i.setAttribute("aria-label",t),i.addEventListener("click",n),i}var Ze={background:"#ffffff",fill:"#ffffff",stroke:"#cfd8e3",text:"#1d2939",mutedText:"#5a6473",tagText:"#1d4fa8",tagFill:"#eaf1ff",accentText:"#1d4fa8",edge:"#8892a4",highlight:"#2a63c8",highlightFill:"#eaf1ff",highlightText:"#ffffff"},qe={background:"#101828",fill:"#1d2939",stroke:"#475467",text:"#f2f4f7",mutedText:"#b8c2d1",tagText:"#caddff",tagFill:"#253b5c",accentText:"#9bbfff",edge:"#98a2b3",highlight:"#8ab4f8",highlightFill:"#233b60",highlightText:"#101828"},lt=1.2,Yt=8,Xt='system-ui, -apple-system, "PingFang SC", "Microsoft YaHei", "Noto Sans CJK SC", sans-serif';function ht(e){return e==="dark"?qe:Ze}var Je=new Intl.Segmenter("en",{granularity:"grapheme"});function wt(e){return e==="rect"||e==="roundedRect"||e==="cylinder"}function Qe(e,t,n){let i=n==="roundedRect"?4:0,o=n==="cylinder"?Math.min(14,e.height/4)*2:0,r=t+i,s=r+o;return{x:e.x+r,y:e.y+s,width:Math.max(0,e.width-r*2),height:Math.max(0,e.height-s*2)}}function qt(e,t,n,i){return tn(e,t,n,i).layout}function tn(e,t,n,i){let o=Qe(t,e.style?.padding??16,i),r=o.y,s=!1,a=!1,l=[],d=[],c=[],h=e.blocks.flatMap((m,u)=>{let g=m.type==="graph"?en(m,o,r,n,u,d):m.type==="list"?on(m,o,r,n,u):G(m.text,m.type==="title"?"title":m.role??"body",m.style,o,r,n,u),f=g.widthOverflow||g.lines.some(y=>nn(y,o));return f&&c.push({blockIndex:u,dimension:"width"}),g.heightOverflow&&c.push({blockIndex:u,dimension:"height"}),s||=f,a||=g.heightOverflow??!1,l.push(...g.tags),r=g.bottom+(u<e.blocks.length-1?e.style?.blockGap??14:0),g.lines});return{contentHeight:r-o.y,overflows:c,layout:{safeArea:o,lines:h,tags:l,...d.length?{graphs:d}:{},widthOverflow:s,heightOverflow:a||r>o.y+o.height+1e-6}}}function en(e,t,n,i,o,r){let s=new Map(e.nodes.map(c=>[c.id,{x:t.x+c.x,y:n+c.y}])),a=e.nodes.map(c=>({...s.get(c.id),radius:4,fill:c.highlighted?i.accentText:i.mutedText,highlighted:c.highlighted??!1})),l=e.edges.map(c=>{let h=s.get(c.from),m=s.get(c.to),u=[c.from,c.to].every(g=>e.nodes.find(f=>f.id===g)?.highlighted);return{x1:h.x,y1:h.y,x2:m.x,y2:m.y,color:u?i.accentText:i.mutedText,highlighted:u}}),d=e.nodes.map(c=>{let h=Math.max(0,Math.min(c.x,t.width-c.x)),m={...t,x:t.x+c.x-h,width:h*2};return G(c.label,"body",{fontSize:12,fontWeight:c.highlighted?700:400,align:"center"},m,n+c.y-4-7-12,i,o)});return r.push({blockIndex:o,nodes:a,edges:l}),{lines:d.flatMap(c=>c.lines),tags:[],bottom:n+e.height,heightOverflow:d.some(c=>c.lines.some(h=>h.y-h.fontSize<n-1e-6)||c.bottom>n+e.height)||e.nodes.some(c=>c.y-4<0||c.y+4>e.height),widthOverflow:d.some(c=>c.widthOverflow)||e.nodes.some(c=>c.x<4||c.x+4>t.width)}}function nn(e,t){return e.width>t.width||e.spans.some(n=>n.x<t.x-1e-6||n.x+n.width>t.x+t.width+1e-6)}function G(e,t,n,i,o,r,s,a,l=0){let d=n?.fontSize??(t==="title"?20:t==="auxiliary"||t==="status"?12:t==="tag"?11:14),c=n?.fontWeight??(t==="title"?700:400),h=n?.color??(t==="auxiliary"?r.mutedText:t==="tag"?r.tagText:t==="status"?r.accentText:r.text),m=n?.align??"left",u=i.x+(m==="right"?i.width:m==="center"?i.width/2:0),g=ne(e,i.width,{fontSize:d,fontWeight:c,color:h,explicitColor:n?.color!==void 0}),f=o,y=g.map((b,x)=>{let E=b.length>0?b.reduce((S,C)=>Math.max(S,C.style.fontSize),0):d,v=Math.max(E,x===0?l:0),w=b.reduce((S,C)=>S+C.width,0),P=f+v,N=u-(m==="right"?w:m==="center"?w/2:0),k=un(b,N,P);return f+=Math.max(20,v*1.35),{text:b.map(S=>S.text).join(""),spans:k,x:u,y:P,width:w,fontSize:v,fontWeight:c,color:h,align:m,role:t,blockIndex:s,...a===void 0?{}:{itemIndex:a}}});return{lines:y,tags:[],bottom:f,widthOverflow:y.some(b=>b.width>i.width)}}function on(e,t,n,i,o){let r=n,s=[],a=[],l=!1,d=hn(e),c=d>0?d+8:0,h={...t,x:t.x+c,width:Math.max(0,t.width-c)};return e.items.forEach((m,u)=>{if(!ee(m))return;s.length>0&&(r+=e.style?.itemGap??12);let g=s.length,f=d===0?0:m.body===void 0?m.auxiliaryStyle?.fontSize??12:m.style?.fontSize??e.style?.fontSize??14,y=rn(m,h,r,i,o,u,e.style?.tagGap??6,f);if(s.push(...y.lines),a.push(...y.tags),l||=y.widthOverflow,r=y.bottom,m.body!==void 0){let b=G(m.body,"body",Zt(e.style,m.style),h,r,i,o,u,f);s.push(...b.lines),l||=b.widthOverflow,r=b.bottom}if(m.auxiliary!==void 0){let b=G(m.auxiliary,"auxiliary",m.auxiliaryStyle,h,r,i,o,u,m.body===void 0?f:0);s.push(...b.lines),l||=b.widthOverflow,r=b.bottom}if(d>0){let b=s[g],x=m.body===void 0?m.auxiliaryStyle:Zt(e.style,m.style),E=G(e.marker==="bullet"?"\u2022":`${u+1}.`,"marker",{...x,fontSize:x?.fontSize??(m.body===void 0?12:14),align:"left"},{...t,width:d},n,i,o,u).lines[0];s.splice(g,0,{...E,y:b.y,spans:E.spans.map(v=>({...v,y:b.y}))})}}),{lines:s,tags:a,bottom:r,widthOverflow:l}}function rn(e,t,n,i,o,r,s,a){let l=sn(e.tags??[],t,n,i,o,r,s,a);if(e.status===void 0)return l;let d=te(e.status,e.statusStyle,12),c=l.tags.at(-1),h=c?c.x+c.width-t.x:0,m=l.tags.every(x=>x.y===n)&&l.lines.length===l.tags.length,u=!ln(e.status).match(/[\r\n]/),g=m&&u&&h+(c?s:0)+d<=t.width,f=g?n:l.bottom,y=G(e.status,"status",{...e.statusStyle,align:"right"},t,f,i,o,r,a),b=l;if(g&&l.lines.length>0){let x=Math.max(...l.lines.map(E=>E.y),y.lines[0].y);b=Jt(l,x-l.lines[0].y),y=Qt(y,x-y.lines[0].y)}return{lines:[...b.lines,...y.lines],tags:b.tags,bottom:Math.max(b.bottom,y.bottom),widthOverflow:b.widthOverflow||y.widthOverflow}}function sn(e,t,n,i,o,r,s,a){let l=[],d=t.x,c={lines:[],tags:[],bottom:n,widthOverflow:!1},h=()=>{if(l.length===0)return;let m=c.tags.length>0?c.bottom+s:n,u=l.map(({tag:y,tagIndex:b,width:x,x:E})=>an(y,{...t,x:E,width:x},m,i,o,r,b,a)),g=Math.max(...u.map(y=>y.lines[0].y)),f=u.map(y=>Jt(y,g-y.lines[0].y));c={lines:[...c.lines,...f.flatMap(y=>y.lines)],tags:[...c.tags,...f.flatMap(y=>y.tags)],bottom:Math.max(...f.map(y=>y.bottom)),widthOverflow:c.widthOverflow||f.some(y=>y.widthOverflow)},l=[],d=t.x};return e.forEach((m,u)=>{let g=Math.min(t.width,te(m.text,m.style,11)+12);l.length>0&&d+g>t.x+t.width&&h(),l.push({tag:m,tagIndex:u,width:g,x:d}),d+=g+s}),h(),c}function an(e,t,n,i,o,r,s,a){let l={...t,x:t.x+6,width:Math.max(0,t.width-12)},d=G(e.text,"tag",e.style,l,n+2,i,o,r,a);return{...d,widthOverflow:d.widthOverflow||t.width<12,lines:d.lines.map(c=>({...c,tagIndex:s})),bottom:d.bottom+2,tags:[{x:t.x,y:n,width:t.width,height:d.bottom-n+2,fill:e.style?.fill??i.tagFill,explicitFill:e.style?.fill!==void 0,...e.style?.shape===void 0?{}:{shape:e.style.shape},blockIndex:o,itemIndex:r,tagIndex:s}]}}function Jt(e,t){return{...Qt(e,t),tags:e.tags.map(n=>({...n,height:n.height+t}))}}function Qt(e,t){return{...e,bottom:e.bottom+t,lines:e.lines.map(n=>({...n,y:n.y+t,spans:n.spans.map(i=>({...i,y:i.y+t}))}))}}function ln(e){return typeof e=="string"?e:e.map(t=>t.text).join("")}function te(e,t,n){let i=ne(e,Number.MAX_VALUE,{fontSize:t?.fontSize??n,fontWeight:t?.fontWeight??400,color:t?.color??"#000000",explicitColor:t?.color!==void 0});return Math.max(0,...i.map(o=>o.reduce((r,s)=>r+s.width,0)))}function ee(e){return e.body!==void 0||e.auxiliary!==void 0||e.status!==void 0||(e.tags?.length??0)>0}function hn(e){return e.marker===void 0||e.marker==="none"?0:e.items.reduce((t,n,i)=>{if(!ee(n))return t;let o=n.body===void 0?n.auxiliaryStyle?.fontSize??12:n.style?.fontSize??e.style?.fontSize??14;return Math.max(t,ie(e.marker==="bullet"?"\u2022":`${i+1}.`,o))},0)}function Zt(e,t){return{fontSize:t?.fontSize??e?.fontSize,fontWeight:t?.fontWeight??e?.fontWeight,color:t?.color??e?.color,align:t?.align??e?.align}}function ne(e,t,n){let o=cn(typeof e=="string"?[{text:e}]:e,n),r=[[]];return o.forEach(s=>{/^\r\n$|^[\r\n]$/.test(s.text)?r.push([]):r.at(-1).push(s)}),r.flatMap(s=>dn(s,t))}function cn(e,t){let n=e.map(r=>r.text).join(""),i=0,o=e[0]?.text.length??0;return Array.from(Je.segment(n),({segment:r,index:s})=>{for(;s>=o&&i<e.length-1;)i+=1,o+=e[i].text.length;let a=e[i]?.style,l={...t,...a,explicitColor:a?.color!==void 0||t.explicitColor};return{text:r,sourceIndex:i,style:l,width:ie(r,l.fontSize)}})}function dn(e,t){let n=e,i=[];for(;n.length>0;){let o=0,r=0;for(;o<n.length&&r+n[o].width<=t;)r+=n[o].width,o+=1;if(o>=n.length){i.push(n);break}let s=n.slice(0,o+1).map(l=>l.text).lastIndexOf(" "),a=s>0?s:Math.max(1,o);for(i.push(n.slice(0,a)),n=n.slice(a);n[0]?.text===" ";)n=n.slice(1)}return i.length>0?i:[[]]}function un(e,t,n){let i=[],o=t,r=-1;for(let s of e){let a=i.at(-1);a&&r===s.sourceIndex?i[i.length-1]={...a,text:a.text+s.text,width:a.width+s.width}:i.push({text:s.text,x:o,y:n,width:s.width,...s.style}),o+=s.width,r=s.sourceIndex}return i}function ie(e,t){return e==="\u2022"?t*.6:/\p{Extended_Pictographic}|\p{Regional_Indicator}|\u20e3/u.test(e)?t:Array.from(e).filter(n=>!/\p{Mark}|\p{Default_Ignorable_Code_Point}/u.test(n)).reduce((n,i)=>n+t*(i.codePointAt(0)>255||"MWmw@%".includes(i)?1:.6),0)}function Et(e,t,n){if(!I(e)){n.push({path:t,message:"must be an object"});return}if(D(e,["blocks","style"],t,n),!Array.isArray(e.blocks)){n.push({path:`${t}.blocks`,message:"must be an array"});return}pn(e.style,`${t}.style`,n);for(let i=0;i<e.blocks.length;i+=1)i in e.blocks||n.push({path:`${t}.blocks[${i}]`,message:"must be a value"});e.blocks.forEach((i,o)=>{let r=`${t}.blocks[${o}]`;if(!I(i)){n.push({path:r,message:"must be an object"});return}if(i.type==="list"){fn(i,r,n);return}if(i.type==="graph"){mn(i,r,n);return}D(i,i.type==="paragraph"?["type","text","role","style"]:["type","text","style"],r,n),i.type!=="title"&&i.type!=="paragraph"&&n.push({path:`${r}.type`,message:"must be title, paragraph, list or graph"}),K(i.text,`${r}.text`,n),O(i.style,`${r}.style`,n),i.role!==void 0&&i.role!=="body"&&i.role!=="auxiliary"&&n.push({path:`${r}.role`,message:"must be body or auxiliary"})})}function mn(e,t,n){D(e,["type","height","nodes","edges"],t,n),j(e.height,`${t}.height`,n,!0);let i=new Set;if(!Array.isArray(e.nodes))n.push({path:`${t}.nodes`,message:"must be an array"});else for(let o=0;o<e.nodes.length;o+=1){let r=e.nodes[o],s=`${t}.nodes[${o}]`;if(!I(r)){n.push({path:s,message:"must be an object"});continue}D(r,["id","label","x","y","highlighted"],s,n),typeof r.id!="string"||!r.id.trim()||i.has(r.id)?n.push({path:`${s}.id`,message:"must be a unique nonempty local ID"}):i.add(r.id),typeof r.label!="string"&&n.push({path:`${s}.label`,message:"must be a string"});for(let a of["x","y"])j(r[a],`${s}.${a}`,n);r.highlighted!==void 0&&typeof r.highlighted!="boolean"&&n.push({path:`${s}.highlighted`,message:"must be a boolean"})}if(!Array.isArray(e.edges)){n.push({path:`${t}.edges`,message:"must be an array"});return}for(let o=0;o<e.edges.length;o+=1){let r=e.edges[o],s=`${t}.edges[${o}]`;if(!I(r)){n.push({path:s,message:"must be an object"});continue}D(r,["from","to"],s,n);for(let a of["from","to"])(typeof r[a]!="string"||!i.has(r[a]))&&n.push({path:`${s}.${a}`,message:"must reference a node in this graph block"})}}function fn(e,t,n){if(D(e,["type","marker","items","style"],t,n),e.marker!==void 0&&e.marker!=="none"&&e.marker!=="bullet"&&e.marker!=="number"&&n.push({path:`${t}.marker`,message:"must be none, bullet or number"}),I(e.style)){let{itemGap:i,tagGap:o,...r}=e.style;O(r,`${t}.style`,n),i!==void 0&&j(i,`${t}.style.itemGap`,n),o!==void 0&&j(o,`${t}.style.tagGap`,n)}else O(e.style,`${t}.style`,n);if(!Array.isArray(e.items)){n.push({path:`${t}.items`,message:"must be an array"});return}for(let i=0;i<e.items.length;i+=1){let o=e.items[i],r=`${t}.items[${i}]`;if(!I(o)){n.push({path:r,message:"must be an object"});continue}D(o,["body","auxiliary","style","auxiliaryStyle","tags","status","statusStyle"],r,n),o.body!==void 0&&K(o.body,`${r}.body`,n),o.auxiliary!==void 0&&K(o.auxiliary,`${r}.auxiliary`,n),gn(o.tags,`${r}.tags`,n),o.status!==void 0&&K(o.status,`${r}.status`,n),O(o.statusStyle,`${r}.statusStyle`,n,!0),O(o.style,`${r}.style`,n),O(o.auxiliaryStyle,`${r}.auxiliaryStyle`,n)}}function gn(e,t,n){if(e!==void 0){if(!Array.isArray(e)){n.push({path:t,message:"must be an array"});return}for(let i=0;i<e.length;i+=1){let o=e[i],r=`${t}[${i}]`;if(!I(o)){n.push({path:r,message:"must be an object"});continue}if(D(o,["text","style"],r,n),K(o.text,`${r}.text`,n),!I(o.style)){O(o.style,`${r}.style`,n,!0);continue}let{fill:s,shape:a,...l}=o.style;O(l,`${r}.style`,n,!0),s!==void 0&&oe(s,`${r}.style.fill`,n),a!==void 0&&a!=="rounded"&&a!=="pill"&&n.push({path:`${r}.style.shape`,message:"must be rounded or pill"})}}}function K(e,t,n){if(typeof e!="string"){if(!Array.isArray(e)){n.push({path:t,message:"must be a string or an array of text spans"});return}for(let i=0;i<e.length;i+=1){let o=e[i],r=`${t}[${i}]`;if(!I(o)){n.push({path:r,message:"must be an object"});continue}D(o,["text","style"],r,n),typeof o.text!="string"&&n.push({path:`${r}.text`,message:"must be a string"}),O(o.style,`${r}.style`,n,!0)}}}function O(e,t,n,i=!1){if(e!==void 0){if(!I(e)){n.push({path:t,message:"must be an object"});return}D(e,i?["fontSize","fontWeight","color"]:["fontSize","fontWeight","color","align"],t,n),e.fontSize!==void 0&&j(e.fontSize,`${t}.fontSize`,n,!0),e.fontWeight!==void 0&&(typeof e.fontWeight!="number"||!Number.isInteger(e.fontWeight)||e.fontWeight<100||e.fontWeight>900)&&n.push({path:`${t}.fontWeight`,message:"must be an integer from 100 through 900"}),e.align!==void 0&&e.align!=="left"&&e.align!=="center"&&e.align!=="right"&&n.push({path:`${t}.align`,message:"must be left, center or right"}),e.color!==void 0&&oe(e.color,`${t}.color`,n)}}function oe(e,t,n){(typeof e!="string"||!/^#(?:[\da-f]{3}|[\da-f]{4}|[\da-f]{6}|[\da-f]{8})$/i.test(e))&&n.push({path:t,message:"must be a hexadecimal color"})}function pn(e,t,n){if(e!==void 0){if(!I(e)){n.push({path:t,message:"must be an object"});return}D(e,["padding","blockGap"],t,n);for(let i of["padding","blockGap"])e[i]!==void 0&&j(e[i],`${t}.${i}`,n)}}function j(e,t,n,i=!1){typeof e!="number"||!Number.isFinite(e)?n.push({path:t,message:"must be a finite number"}):(i?e<=0:e<0)&&n.push({path:t,message:i?"must be greater than zero":"must be zero or greater"})}function I(e){if(typeof e!="object"||e===null||Array.isArray(e))return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}function D(e,t,n,i){for(let o of Object.keys(e))t.includes(o)||i.push({path:`${n}.${o}`,message:"unknown field"})}function H(e,t,n){if(n===0)return e;if(n===1)return t;let i=l=>{let d=l.slice(1),c=d.length<=4?[...d].map(h=>h+h).join(""):d;return[0,2,4,6].map(h=>h<c.length?parseInt(c.slice(h,h+2),16):255)},o=i(e),r=i(t),s=o.map((l,d)=>Math.round(l+(r[d]-l)*n));return`#${s.map(l=>l.toString(16).padStart(2,"0")).slice(0,s[3]===255?3:4).join("")}`}var R=class extends Error{issues;constructor(t){super(`Invalid diagram document (${t.length} issue${t.length===1?"":"s"})`),this.name="DiagramValidationError",this.issues=t}};function ae(e){let t=[];if(!$(e))return[{path:"$",message:"document must be an object"}];L(e,["version","title","viewport","theme","nodes","edges","scenes"],"$",t),e.version!==1&&t.push({path:"version",message:"must be 1"}),e.title!==void 0&&typeof e.title!="string"&&t.push({path:"title",message:"must be a string"}),e.theme!==void 0&&e.theme!=="light"&&e.theme!=="dark"&&t.push({path:"theme",message:"must be light or dark"});let n=e.viewport;$(n)?(L(n,["width","height","fitHeight"],"viewport",t),Y(n.width,"viewport.width",t),Y(n.height,"viewport.height",t),n.fitHeight!==void 0&&($(n.fitHeight)?(L(n.fitHeight,["padding"],"viewport.fitHeight",t),q(n.fitHeight.padding,"viewport.fitHeight.padding",t)):t.push({path:"viewport.fitHeight",message:"must be an object"}))):t.push({path:"viewport",message:"must be an object"}),Array.isArray(e.nodes)||t.push({path:"nodes",message:"must be an array"}),Array.isArray(e.edges)||t.push({path:"edges",message:"must be an array"}),Array.isArray(e.scenes)||t.push({path:"scenes",message:"must be an array"});let i=Array.isArray(e.nodes)?e.nodes:[],o=Array.isArray(e.edges)?e.edges:[],r=Array.isArray(e.scenes)?e.scenes:[];X(i,"nodes",t),X(o,"edges",t),X(r,"scenes",t);let s=new Set;i.forEach((h,m)=>{let u=`nodes[${m}]`;if(!$(h)){t.push({path:u,message:"must be an object"});return}L(h,["id","kind","x","y","width","height","label","content","parentId","visible","style"],u,t),A(h.id)?s.has(h.id)?t.push({path:`${u}.id`,message:`duplicate id "${h.id}"`}):s.add(h.id):t.push({path:`${u}.id`,message:"must be a non-empty string"}),yn(h.kind)||t.push({path:`${u}.kind`,message:"must be a supported node kind"}),Z(h.x,`${u}.x`,t),Z(h.y,`${u}.y`,t),Y(h.width,`${u}.width`,t),Y(h.height,`${u}.height`,t),h.label!==void 0&&typeof h.label!="string"&&t.push({path:`${u}.label`,message:"must be a string"}),h.content!==void 0&&(Et(h.content,`${u}.content`,t),h.label!==void 0&&t.push({path:`${u}.content`,message:"must not be declared together with label"}),wt(h.kind)||t.push({path:`${u}.content`,message:"requires a rect, roundedRect or cylinder node"})),h.parentId!==void 0&&!A(h.parentId)&&t.push({path:`${u}.parentId`,message:"must be a non-empty string"}),h.visible!==void 0&&typeof h.visible!="boolean"&&t.push({path:`${u}.visible`,message:"must be a boolean"}),bn(h.style,`${u}.style`,t)});let a=new Map;i.forEach(h=>{$(h)&&A(h.id)&&a.set(h.id,h)}),i.forEach((h,m)=>{if($(h)&&h.parentId!==void 0&&A(h.parentId)){let u=a.get(h.parentId);u?u.kind!=="group"&&t.push({path:`nodes[${m}].parentId`,message:"must reference a group node"}):t.push({path:`nodes[${m}].parentId`,message:"must reference an existing node"})}}),vn(i,a,t);let l=new Map;o.forEach((h,m)=>{let u=`edges[${m}]`;if(!$(h)){t.push({path:u,message:"must be an object"});return}L(h,["id","from","to","label","route","fromPort","toPort","visible","style"],u,t),A(h.id)?s.has(h.id)?t.push({path:`${u}.id`,message:`duplicate id "${h.id}"`}):(s.add(h.id),l.set(h.id,h)):t.push({path:`${u}.id`,message:"must be a non-empty string"}),se(h.from,`${u}.from`,a,t),se(h.to,`${u}.to`,a,t),h.label!==void 0&&typeof h.label!="string"&&t.push({path:`${u}.label`,message:"must be a string"}),h.route!==void 0&&h.route!=="straight"&&h.route!=="orthogonal"&&h.route!=="curved"&&t.push({path:`${u}.route`,message:"must be straight, orthogonal or curved"}),h.fromPort!==void 0&&!re(h.fromPort)&&t.push({path:`${u}.fromPort`,message:"must be top, right, bottom or left"}),h.toPort!==void 0&&!re(h.toPort)&&t.push({path:`${u}.toPort`,message:"must be top, right, bottom or left"}),h.visible!==void 0&&typeof h.visible!="boolean"&&t.push({path:`${u}.visible`,message:"must be a boolean"}),xn(h.style,`${u}.style`,t)});let d=new Set([...a.keys(),...l.keys()]),c=new Set;return r.forEach((h,m)=>{let u=`scenes[${m}]`;if(!$(h)){t.push({path:u,message:"must be an object"});return}L(h,["id","label","beats"],u,t),A(h.id)?c.has(h.id)?t.push({path:`${u}.id`,message:`duplicate id "${h.id}"`}):c.add(h.id):t.push({path:`${u}.id`,message:"must be a non-empty string"}),A(h.label)||t.push({path:`${u}.label`,message:"must be a non-empty string"}),Array.isArray(h.beats)?(X(h.beats,`${u}.beats`,t),h.beats.length===0&&t.push({path:`${u}.beats`,message:"must contain at least one beat"}),h.beats.forEach((g,f)=>wn(g,`${u}.beats[${f}]`,d,a,l,t))):t.push({path:`${u}.beats`,message:"must be an array"})}),t}function $(e){if(typeof e!="object"||e===null||Array.isArray(e))return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}function A(e){return typeof e=="string"&&e.length>0}function yn(e){return e==="rect"||e==="roundedRect"||e==="ellipse"||e==="cylinder"||e==="group"}function re(e){return e==="top"||e==="right"||e==="bottom"||e==="left"}function Z(e,t,n){(typeof e!="number"||!Number.isFinite(e))&&n.push({path:t,message:"must be a finite number"})}function Y(e,t,n){Z(e,t,n),typeof e=="number"&&Number.isFinite(e)&&e<=0&&n.push({path:t,message:"must be greater than zero"})}function q(e,t,n){Z(e,t,n),typeof e=="number"&&Number.isFinite(e)&&e<0&&n.push({path:t,message:"must be zero or greater"})}function L(e,t,n,i){let o=new Set(t);for(let r of Object.keys(e))o.has(r)||i.push({path:n==="$"?r:`${n}.${r}`,message:"unknown field"})}function X(e,t,n){for(let i=0;i<e.length;i+=1)i in e||n.push({path:`${t}[${i}]`,message:"must be a value"})}function Pt(e,t,n){(typeof e!="string"||!/^#(?:[\da-f]{3}|[\da-f]{4}|[\da-f]{6}|[\da-f]{8})$/i.test(e))&&n.push({path:t,message:"must be a hexadecimal color"})}function bn(e,t,n){if(e!==void 0){if(!$(e)){n.push({path:t,message:"must be an object"});return}L(e,["fill","stroke","strokeWidth","textColor","dashed"],t,n);for(let i of["fill","stroke","textColor"])e[i]!==void 0&&Pt(e[i],`${t}.${i}`,n);e.strokeWidth!==void 0&&q(e.strokeWidth,`${t}.strokeWidth`,n),e.dashed!==void 0&&typeof e.dashed!="boolean"&&n.push({path:`${t}.dashed`,message:"must be a boolean"})}}function xn(e,t,n){if(e!==void 0){if(!$(e)){n.push({path:t,message:"must be an object"});return}L(e,["stroke","strokeWidth","dashed"],t,n),e.stroke!==void 0&&Pt(e.stroke,`${t}.stroke`,n),e.strokeWidth!==void 0&&q(e.strokeWidth,`${t}.strokeWidth`,n),e.dashed!==void 0&&typeof e.dashed!="boolean"&&n.push({path:`${t}.dashed`,message:"must be a boolean"})}}function se(e,t,n,i){if(!A(e)){i.push({path:t,message:"must reference an existing node"});return}n.get(e)||i.push({path:t,message:"must reference an existing node"})}function vn(e,t,n){let i=new Set;for(let[o,r]of t){let s=new Set,a=o;for(;a;){if(s.has(a)){let d=e.findIndex(h=>$(h)&&h.id===o),c=d>=0?`nodes[${d}].parentId`:"nodes.parentId";i.has(c)||(n.push({path:c,message:"group parent references form a cycle"}),i.add(c));break}s.add(a);let l=t.get(a)?.parentId;a=A(l)?l:void 0}}}function wn(e,t,n,i,o,r){if(!$(e)){r.push({path:t,message:"must be an object"});return}if(L(e,["durationMs","transitionMs","caption","actions"],t,r),Y(e.durationMs,`${t}.durationMs`,r),e.transitionMs!==void 0&&(q(e.transitionMs,`${t}.transitionMs`,r),typeof e.durationMs=="number"&&Number.isFinite(e.durationMs)&&typeof e.transitionMs=="number"&&Number.isFinite(e.transitionMs)&&e.transitionMs>e.durationMs&&r.push({path:`${t}.transitionMs`,message:"must not exceed durationMs"})),e.caption!==void 0&&typeof e.caption!="string"&&r.push({path:`${t}.caption`,message:"must be a string"}),!Array.isArray(e.actions)){r.push({path:`${t}.actions`,message:"must be an array"});return}X(e.actions,`${t}.actions`,r);let s=new Set;e.actions.forEach((a,l)=>En(a,`${t}.actions[${l}]`,n,i,o,s,r))}function En(e,t,n,i,o,r,s){if(!$(e)){s.push({path:t,message:"must be an object"});return}if(typeof e.type!="string"){s.push({path:`${t}.type`,message:"must be a supported action"});return}let a=e.type,l={show:["type","target"],hide:["type","target"],highlight:["type","target"],unhighlight:["type","target"],updateNode:["type","target","value"],updateEdge:["type","target","value"],focus:["type","target"],flow:["type","target","direction","text","highlightParticipants"]},d=Object.hasOwn(l,a)?l[a]:void 0;if(!d){s.push({path:`${t}.type`,message:"unknown action"});return}if(L(e,d,t,s),["show","hide","highlight","unhighlight"].includes(a)){if(!A(e.target)){s.push({path:`${t}.target`,message:"must reference an existing object"});return}n.has(e.target)||s.push({path:`${t}.target`,message:"must reference an existing object"}),ct(`${a==="show"||a==="hide"?"visibility":"highlight"}:${e.target}`,t,r,s);return}if(a==="focus"){e.target!==null&&(!A(e.target)||!i.has(e.target))&&s.push({path:`${t}.target`,message:"must reference a node or group"}),(e.target===null||A(e.target)&&i.has(e.target))&&ct("focus",t,r,s);return}if(a==="flow"){A(e.target)?o.has(e.target)||s.push({path:`${t}.target`,message:"must reference an existing edge"}):s.push({path:`${t}.target`,message:"must reference an existing edge"}),e.direction!=="forward"&&e.direction!=="reverse"&&s.push({path:`${t}.direction`,message:"must be forward or reverse"}),e.text!==void 0&&typeof e.text!="string"&&s.push({path:`${t}.text`,message:"must be a string"}),e.highlightParticipants!==void 0&&typeof e.highlightParticipants!="boolean"&&s.push({path:`${t}.highlightParticipants`,message:"must be a boolean"}),A(e.target)&&o.has(e.target)&&ct(`flow:${e.target}`,`${t}.target`,r,s);return}A(e.target)?(a==="updateNode"?!i.has(e.target):!o.has(e.target))&&s.push({path:`${t}.target`,message:`must reference an existing ${a==="updateNode"?"node":"edge"}`}):s.push({path:`${t}.target`,message:`must reference a ${a==="updateNode"?"node":"edge"}`}),Pn(e.value,`${t}.value`,a==="updateNode",r,e.target,s);let c=typeof e.target=="string"?i.get(e.target):void 0;a==="updateNode"&&c&&$(e.value)&&e.value.content!==void 0&&e.value.content!==null&&!wt(c.kind)&&s.push({path:`${t}.value.content`,message:"requires a rect, roundedRect or cylinder node"})}function Pn(e,t,n,i,o,r){if(!$(e)){r.push({path:t,message:"must be an object"});return}let s=n?["x","y","width","height","label","content","fill","stroke","opacity"]:["label","stroke","strokeWidth","opacity"];L(e,s,t,r),n&&Object.hasOwn(e,"label")&&Object.hasOwn(e,"content")&&r.push({path:`${t}.content`,message:"must not be declared together with label"});for(let a of Object.keys(e)){if(!s.includes(a))continue;let l=n&&(a==="label"||a==="content")?"text-mode":a;ct(`${n?"node":"edge"}:${String(o)}:${l}`,`${t}.${a}`,i,r);let d=e[a];["x","y","width","height","opacity"].includes(a)&&Z(d,`${t}.${a}`,r),a==="strokeWidth"&&q(d,`${t}.${a}`,r),["width","height"].includes(a)&&typeof d=="number"&&d<=0&&r.push({path:`${t}.${a}`,message:"must be greater than zero"}),a==="opacity"&&typeof d=="number"&&(d<0||d>1)&&r.push({path:`${t}.${a}`,message:"must be between zero and one"}),["fill","stroke"].includes(a)&&Pt(d,`${t}.${a}`,r),a==="label"&&typeof d!="string"&&r.push({path:`${t}.${a}`,message:"must be a string"}),a==="content"&&d!==null&&Et(d,`${t}.${a}`,r)}}function ct(e,t,n,i){n.has(e)?i.push({path:t,message:"duplicate beat assignment"}):n.add(e)}var Mn=250,le=200,$n=100,$t=200,An=le+$n+$t,Sn=["x","y","width","height","opacity","strokeWidth"],Cn=2,J=class{totalDuration;schedule;beats;initial;title;constructor(t,n=new Map){this.initial=new Map([...t.nodes.map(l=>[l.id,{visible:l.visible!==!1,highlighted:!1,kind:"node",values:{x:l.x,y:l.y,width:l.width,height:l.height,label:l.label,content:l.content,fill:l.style?.fill,stroke:l.style?.stroke,opacity:1}}]),...t.edges.map(l=>[l.id,{visible:l.visible!==!1,highlighted:!1,kind:"edge",values:{label:l.label,stroke:l.style?.stroke,strokeWidth:l.style?.strokeWidth??Cn,opacity:1}}])]),this.title=t.title;let i=[],o=this.initial,r=null,s=0,a=t.title;for(let[l,d]of t.scenes.entries())d.beats.forEach((c,h)=>{let m=new Map(o),u=r,g=[];for(let v of c.actions){if(v.type==="flow"){g.push({target:v.target,direction:v.direction,highlightParticipants:v.highlightParticipants??!0,...v.text!==void 0?{text:v.text}:{}});continue}if(v.type==="focus"){r=v.target;continue}let w=m.get(v.target);if(w)if(v.type==="updateNode"||v.type==="updateEdge"){let P=v.type!=="updateNode"?{}:Object.hasOwn(v.value,"content")?{label:void 0}:Object.hasOwn(v.value,"label")?{content:void 0}:{};m.set(v.target,{...w,values:{...w.values,...P,...v.value}})}else m.set(v.target,v.type==="show"||v.type==="hide"?{...w,visible:v.type==="show"}:{...w,highlighted:v.type==="highlight"})}a=c.caption??a;let f=`${d.id}:${h}`,y=n.get(f)??c.durationMs,b=n.has(f)?y+An:c.durationMs,x=s+b,E=Math.min(b,c.transitionMs??Math.min(Mn,c.durationMs));if(!Number.isFinite(y)||!Number.isFinite(x))throw new R([{path:`scenes[${l}].beats[${h}].durationMs`,message:"effective beat timing must be finite"}]);i.push({startMs:s,endMs:x,movementMs:y,transitionMs:E,sceneId:d.id,beatId:f,beatIndex:h,caption:a,focusBefore:u,focusAfter:r,before:o,after:m,flows:g}),o=m,s=x});this.beats=i,this.schedule=Object.freeze(i.map(({sceneId:l,beatId:d,beatIndex:c,startMs:h,endMs:m,caption:u})=>Object.freeze({sceneId:l,beatId:d,beatIndex:c,startMs:h,endMs:m,...u!==void 0?{caption:u}:{}}))),this.totalDuration=s}evaluateAt(t,n,i){let o=Number.isFinite(t)?Math.max(0,Math.min(t,this.totalDuration)):0,r=this.findBeat(o),s=!r||n.reducedMotion||r.transitionMs===0?1:Mt(Math.min(1,(o-r.startMs)/r.transitionMs)),a=r?.before??this.initial,l=r?.after??this.initial,d=new Map([...l].map(([c,h])=>{let m=a.get(c)??h,u=Tn(m,h,s,i),g=Number(m.visible)+(Number(h.visible)-Number(m.visible))*s;return[c,{values:u,visible:h.visible||m.visible&&s<1,opacity:Math.min(u.opacity??1,g),highlighted:h.highlighted,highlight:Number(m.highlighted)+(Number(h.highlighted)-Number(m.highlighted))*s}]}));return{timeMs:o,sceneId:r?.sceneId??null,beatId:r?.beatId??null,beatProgress:r?(o-r.startMs)/(r.endMs-r.startMs):0,flowProgress:r?Math.min(1,(o-r.startMs)/r.movementMs):0,flowArrivalProgress:!r||n.reducedMotion||r.flows.length!==1?1:Mt(Math.max(0,Math.min(1,(o-r.startMs-r.movementMs)/le))),flowDepartureProgress:!r||n.reducedMotion||r.flows.length!==1?0:Mt(Math.max(0,Math.min(1,(o-r.endMs+$t)/$t))),transitionProgress:s,focusBefore:r?.focusBefore??null,focusAfter:r?.focusAfter??null,caption:r?.caption??this.title,objects:d,flows:r&&o<r.endMs?r.flows:[]}}findBeat(t){let n=0,i=this.beats.length-1;for(;n<i;){let o=Math.floor((n+i)/2);t<this.beats[o].endMs?i=o:n=o+1}return this.beats[n]}};function Mt(e){return e<.5?4*e**3:1-(-2*e+2)**3/2}function Tn(e,t,n,i){if(n===0)return{...e.values,label:t.values.label,content:t.values.content};if(n===1)return{...t.values};let o=Object.fromEntries(Sn.flatMap(s=>{let a=e.values[s],l=t.values[s];return a===void 0||l===void 0?[]:[[s,a*(1-n)+l*n]]})),r=t.kind==="node"?i.stroke:i.edge;return{...t.values,...o,fill:H(e.values.fill??i.fill,t.values.fill??i.fill,n),stroke:H(e.values.stroke??r,t.values.stroke??r,n)}}function p(e){return Number.isInteger(e)?String(e):String(Number(e.toFixed(3)))}function Dn(e,t){let n={x:e.x+e.width/2,y:e.y+e.height/2},i={x:t.x+t.width/2,y:t.y+t.height/2},o=fe(e,i),r=fe(t,n);return[o,r]}var T=8,In=24,ge=24,St=12,he=16,Ln=.002,Nn=16,ce=1e3,kn=14,V=7;function gt(){return z([])}function z(e,t,n,i){let o=pt(e),r=t??Kn(o),s=o.slice(1).map((h,m)=>({start:o[m],end:h,length:Math.hypot(h.x-o[m].x,h.y-o[m].y)})),a=s.reduce((h,m)=>h+m.length,0),l=o.map(h=>h.x),d=o.map(h=>h.y),c=l.length===0?{left:0,top:0,right:0,bottom:0}:{left:Math.min(...l),top:Math.min(...d),right:Math.max(...l),bottom:Math.max(...d)};return{path:r,points:o,length:a||1,terminalDirection:On(o,n),paintedBottom:i??c.bottom,bounds:c,pointAt(h){if(o.length===0)return{x:0,y:0};if(o.length===1||a===0)return o[0];let m=Math.min(1,Math.max(0,h))*a,u=0;for(let g of s){if(m<=u+g.length){let f=g.length===0?0:(m-u)/g.length;return{x:g.start.x+(g.end.x-g.start.x)*f,y:g.start.y+(g.end.y-g.start.y)*f}}u+=g.length}return o.at(-1)}}}function On(e,t){let n=e.at(-1),i=t??e.at(-2);if(!n||!i)return{x:1,y:0};let o=n.x-i.x,r=n.y-i.y,s=Math.hypot(o,r);return s>0?{x:o/s,y:r/s}:{x:1,y:0}}function _n(e,t,n){let i=pt(e);if(i.length<3)return z(i);let o=[i[0]],r=[`M ${p(i[0].x)} ${p(i[0].y)}`],s=Math.max(ge,n*V+T);for(let l=1;l<i.length-1;l+=1){let d=i[l-1],c=i[l],h=i[l+1],m=Math.hypot(c.x-d.x,c.y-d.y),u=Math.hypot(h.x-c.x,h.y-c.y),f=(c.x-d.x)*(h.x-c.x)+(c.y-d.y)*(h.y-c.y)===0?Math.max(0,Math.min(St,m/2,u/2,l===i.length-2?u-Math.min(s,u):St)):0,y=Rn(c,d,h,f,t,n/2),b=ut(c,d,y),x=ut(c,h,y);if(r.push(`L ${p(b.x)} ${p(b.y)}`),o.push(b),y!==0){r.push(`Q ${p(c.x)} ${p(c.y)} ${p(x.x)} ${p(x.y)}`);for(let E=1;E<=he;E+=1){let v=E/he,w=1-v;o.push({x:w**2*b.x+2*w*v*c.x+v**2*x.x,y:w**2*b.y+2*w*v*c.y+v**2*x.y})}}}let a=i.at(-1);return r.push(`L ${p(a.x)} ${p(a.y)}`),o.push(a),z(o,r.join(" "))}function ut(e,t,n){let i=Math.hypot(t.x-e.x,t.y-e.y);return{x:e.x+(t.x-e.x)/i*n,y:e.y+(t.y-e.y)/i*n}}function Rn(e,t,n,i,o,r){let s=d=>{let c=ut(e,t,d),h=ut(e,n,d),m=r+Ln;return!o.some(u=>Math.min(c.x,e.x,h.x)<u.right+m&&Math.max(c.x,e.x,h.x)>u.left-m&&Math.min(c.y,e.y,h.y)<u.bottom+m&&Math.max(c.y,e.y,h.y)>u.top-m)};if(i===0||!s(0))return 0;if(s(i))return i;let a=0,l=i;for(let d=0;d<Nn;d+=1){let c=(a+l)/2;s(c)?a=c:l=c}return Math.floor(a*ce)/ce}function Gn(e,t,n,i){let o=[];for(let a=0;a<=32;a+=1){let l=a/32,d=1-l;o.push({x:d**3*e.x+3*d**2*l*t.x+3*d*l**2*n.x+l**3*i.x,y:d**3*e.y+3*d**2*l*t.y+3*d*l**2*n.y+l**3*i.y})}let s=`M ${p(e.x)} ${p(e.y)} C ${p(t.x)} ${p(t.y)} ${p(n.x)} ${p(n.y)} ${p(i.x)} ${p(i.y)}`;return z(o,s,n,Ct(e,t,n,i))}function Ct(e,t,n,i){let o=Math.max(Math.abs(e.y),Math.abs(t.y),Math.abs(n.y),Math.abs(i.y),1),r=e.y/o,s=t.y/o,a=n.y/o,l=i.y/o,d=-r+3*s-3*a+l,c=2*(r-2*s+a),h=s-r,m=c*c-4*d*h,u=d===0?c===0?[]:[-h/c]:m<0?[]:[(-c-Math.sqrt(m))/(2*d),(-c+Math.sqrt(m))/(2*d)],g=Math.max(e.y,i.y);for(let f of u){if(f<=0||f>=1)continue;let y=1-f,b=y**3*e.y+3*y**2*f*t.y+3*y*f**2*n.y+f**3*i.y;g=Math.max(g,b)}return g}function Hn(e,t,n,i,o,r,s){let a=[],l=(c,h,m,u)=>{for(let g=0;g<=16;g+=1){if(a.length>0&&g===0)continue;let f=g/16,y=1-f;a.push({x:y**3*c.x+3*y**2*f*h.x+3*y*f**2*m.x+f**3*u.x,y:y**3*c.y+3*y**2*f*h.y+3*y*f**2*m.y+f**3*u.y})}};l(e,t,i,n),l(n,o,r,s);let d=`M ${p(e.x)} ${p(e.y)} C ${p(t.x)} ${p(t.y)} ${p(i.x)} ${p(i.y)} ${p(n.x)} ${p(n.y)} C ${p(o.x)} ${p(o.y)} ${p(r.x)} ${p(r.y)} ${p(s.x)} ${p(s.y)}`;return z(a,d,r,Math.max(Ct(e,t,i,n),Ct(n,o,r,s)))}function pe(e,t,n){for(let i=1;i<t.points.length;i+=1)if(Fn(e,t.points[i-1],t.points[i])<=n)return!0;return!1}function Fn(e,t,n){let i=n.x-t.x,o=n.y-t.y;if(i===0&&o===0)return Math.hypot(e.x-t.x,e.y-t.y);let r=Math.max(0,Math.min(1,((e.x-t.x)*i+(e.y-t.y)*o)/(i*i+o*o)));return Math.hypot(e.x-(t.x+r*i),e.y-(t.y+r*o))}function ye(e,t,n,i,o){if(e.route==="curved"){let s=tt(t,n),a=e.fromPort??s.from,l=e.toPort??s.to,d=B(t,a),c=B(n,l,o.targetOffset);return zn(d,c,a,l,o.reverseOffset)}if(e.route!=="orthogonal"){let s=e.fromPort||e.toPort?[B(t,e.fromPort??tt(t,n).from),B(n,e.toPort??tt(t,n).to)]:Dn(t,n);return z(s)}let r=i.filter(s=>s.visible&&s.kind!=="group"&&s.id!==t.id&&s.id!==n.id).map(W);return _n(jn(e,t,n,r,o.targetOffset,i),[...r,W(t),W(n)],e.style?.strokeWidth??2)}function be(e,t){let n=new Map;for(let o of e){if(o.route!=="orthogonal"&&o.route!=="curved")continue;let r=t.get(o.from),s=t.get(o.to);if(!r||!s)continue;let a=o.toPort??tt(r,s).to,l=JSON.stringify([s.id,a]),d=n.get(l),c=a==="top"||a==="bottom"?r.x+r.width/2:r.y+r.height/2;n.set(l,{node:s,side:a,inputs:[...d?.inputs??[],{id:o.id,position:c}]})}let i=new Map;for(let{node:o,side:r,inputs:s}of n.values()){let a=r==="top"||r==="bottom"?o.width:o.height,l=Math.min(In,a/(2*s.length));[...s].sort((d,c)=>d.position-c.position).forEach((d,c)=>i.set(d.id,(c-(s.length-1)/2)*l))}return i}function W(e){return{left:e.x,top:e.y,right:e.x+e.width,bottom:e.y+e.height}}function At(e,t,n=T){return{left:e.left-(t==="left"?n:T),top:e.top-(t==="top"?n:T),right:e.right+(t==="right"?n:T),bottom:e.bottom+(t==="bottom"?n:T)}}function jn(e,t,n,i,o,r){let s=tt(t,n),a=e.fromPort??s.from,l=e.toPort??s.to,d=B(t,a),c=B(n,l,o),h=Math.max(ge,(e.style?.strokeWidth??2)*V+T),m=W(t),u=W(n),g=de(dt(d,a,m),a,T,[...i,u]),f=de(dt(c,l,u),l,h+St,[...i,m]),y=At(m,a,g),b=At(u,l,f),x=dt(d,a,y),E=dt(c,l,b),v=[...i.map(k=>At(k)),y,b],w=Bn(x,E,a,l,[m,u,...Wn(t,n,r)],h),N=w&&w.every((k,S)=>S===0||!ft(w[S-1],k,v))?w:Vn(x,E,v);return[d,...N,c]}function Bn(e,t,n,i,o,r){let s=n==="top"||n==="bottom";if(s!==(i==="top"||i==="bottom"))return;let a=s?"y":"x",l=mt(n),d=s?l.y:l.x,c;if(n===i){let h=s?d<0?"top":"bottom":d<0?"left":"right",m=o.map(g=>g[h]),u=(d<0?Math.min(...m):Math.max(...m))+d*r;c=d<0?Math.min(u,e[a],t[a]):Math.max(u,e[a],t[a])}else{if((t[a]-e[a])*d<0)return;let h=o[0],m=o[1],u=s?d<0?h.top:h.bottom:d<0?h.left:h.right,g=s?d<0?m.bottom:m.top:d<0?m.right:m.left,f=(u+g)/2;c=Math.max(Math.min(e[a],t[a]),Math.min(Math.max(e[a],t[a]),f))}return pt([e,{...e,[a]:c},{...t,[a]:c},t])}function Wn(e,t,n){let i=new Map(n.map(d=>[d.id,d])),o=d=>{let c=[],h=d.parentId?i.get(d.parentId):void 0;for(;h;)c.push(h),h=h.parentId?i.get(h.parentId):void 0;return c},r=o(e),s=o(t),a=new Set([e.id,...r.map(({id:d})=>d)]),l=new Set([t.id,...s.map(({id:d})=>d)]);return[...r.filter(({id:d})=>!l.has(d)),...s.filter(({id:d})=>!a.has(d))].map(W)}function de(e,t,n,i){let o=t==="top"||t==="bottom",r=mt(t);return i.reduce((s,a)=>{let l=o?e.x:e.y,d=o?a.left:a.top,c=o?a.right:a.bottom;if(l<d-T||l>c+T)return s;let h=o?(t==="top"?a.bottom-e.y:a.top-e.y)*r.y:(t==="left"?a.right-e.x:a.left-e.x)*r.x;return h<0?s:Math.min(s,Math.max(h-T,h/2))},n)}function dt(e,t,n){return t==="top"?{x:e.x,y:n.top}:t==="bottom"?{x:e.x,y:n.bottom}:t==="left"?{x:n.left,y:e.y}:{x:n.right,y:e.y}}function tt(e,t){let n={x:e.x+e.width/2,y:e.y+e.height/2},i={x:t.x+t.width/2,y:t.y+t.height/2};if(Math.abs(i.x-n.x)>=Math.abs(i.y-n.y)){let s=i.x>=n.x;return{from:s?"right":"left",to:s?"left":"right"}}let r=i.y>=n.y;return{from:r?"bottom":"top",to:r?"top":"bottom"}}function B(e,t,n=0){let i={x:e.x+e.width/2,y:e.y+e.height/2},o=t==="top"||t==="bottom",r=(o?e.width:e.height)/2,s=Math.sqrt(Math.max(0,1-(n/r)**2)),a=e.kind==="ellipse"?(o?e.height:e.width)/2*(1-s):e.kind==="cylinder"&&o?Math.min(kn,e.height/4)*(1-s):0;return t==="top"?{x:i.x+n,y:e.y+a}:t==="right"?{x:e.x+e.width-a,y:i.y+n}:t==="bottom"?{x:i.x+n,y:e.y+e.height-a}:{x:e.x+a,y:i.y+n}}function zn(e,t,n,i,o){let r=Math.hypot(t.x-e.x,t.y-e.y),s=Math.max(24,r*.32),a=mt(n),l=mt(i),d={x:-(t.y-e.y),y:t.x-e.x},c=Math.hypot(d.x,d.y)||1,h=o*Math.min(72,Math.max(28,r*.16));if(o!==0){let g={x:(e.x+t.x)/2+d.x/c*h,y:(e.y+t.y)/2+d.y/c*h},f={x:t.x-e.x,y:t.y-e.y},y=Math.hypot(f.x,f.y)||1,b={x:f.x/y,y:f.y/y};return Hn(e,{x:e.x+a.x*s,y:e.y+a.y*s},g,{x:g.x-b.x*s*.5,y:g.y-b.y*s*.5},{x:g.x+b.x*s*.5,y:g.y+b.y*s*.5},{x:t.x+l.x*s,y:t.y+l.y*s},t)}let m={x:e.x+a.x*s+d.x/c*h,y:e.y+a.y*s+d.y/c*h},u={x:t.x+l.x*s+d.x/c*h,y:t.y+l.y*s+d.y/c*h};return Gn(e,m,u,t)}function mt(e){return e==="top"?{x:0,y:-1}:e==="right"?{x:1,y:0}:e==="bottom"?{x:0,y:1}:{x:-1,y:0}}function Vn(e,t,n){let i=me([e.x,t.x,...n.flatMap(g=>[g.left,g.right])]),o=me([e.y,t.y,...n.flatMap(g=>[g.top,g.bottom])]),r=new Map(i.map((g,f)=>[g,f])),s=new Map(o.map((g,f)=>[g,f])),a=Q(e),l=Q(t),d=[e],c=new Map([[a,void 0]]),h=Math.abs(t.x-e.x)>=Math.abs(t.y-e.y),m=h?[[1,0],[0,-1],[0,1],[-1,0]]:[[0,1],[1,0],[-1,0],[0,-1]];for(;d.length>0;){let g=d.shift();if(Q(g)===l)break;let f=r.get(g.x),y=s.get(g.y);if(!(f===void 0||y===void 0))for(let[b,x]of m){let E=i[f+b],v=o[y+x];if(E===void 0||v===void 0)continue;let w={x:E,y:v},P=Q(w);c.has(P)||xe(w,n)||ft(g,w,n)||(c.set(P,g),d.push(w))}}let u=[];if(c.has(l)){let g=t;for(;g;)u.push(g),g=c.get(Q(g));u.reverse()}else{let g=h?{x:t.x,y:e.y}:{x:e.x,y:t.y};u.push(e,g,t)}return Un(u,n)}function Un(e,t){let n=[...e],i=T*2;for(let o=1;o+2<n.length;o+=1){let r=n[o-1],s=n[o],a=n[o+1],l=n[o+2];if(Math.abs(a.x-s.x)+Math.abs(a.y-s.y)>=i)continue;let c=r.x===s.x?{x:s.x,y:l.y}:{x:l.x,y:s.y};xe(c,t)||ft(r,c,t)||ft(c,l,t)||(n.splice(o,2,c),o=Math.max(0,o-2))}return n}function Kn(e){return pt(e).map((t,n)=>`${n===0?"M":"L"} ${p(t.x)} ${p(t.y)}`).join(" ")}function pt(e){let t=[];for(let i of e){let o=t.at(-1);(!o||o.x!==i.x||o.y!==i.y)&&t.push(i)}let n=[];for(let i of t){let o=n.at(-1),r=n.at(-2),s=o&&r&&(o.x-r.x)*(i.x-o.x)+(o.y-r.y)*(i.y-o.y)>=0;o&&r&&s&&(r.x===o.x&&o.x===i.x||r.y===o.y&&o.y===i.y)?n[n.length-1]=i:n.push(i)}return n}function ft(e,t,n){for(let i of n)if(e.x===t.x){if(e.x>i.left&&e.x<i.right&&ue(e.y,t.y,i.top,i.bottom))return!0}else if(e.y===t.y&&e.y>i.top&&e.y<i.bottom&&ue(e.x,t.x,i.left,i.right))return!0;return!1}function xe(e,t){return t.some(n=>e.x>n.left&&e.x<n.right&&e.y>n.top&&e.y<n.bottom)}function ue(e,t,n,i){return Math.max(Math.min(e,t),n)<Math.min(Math.max(e,t),i)}function Q(e){return`${e.x},${e.y}`}function me(e){return[...new Set(e)].sort((t,n)=>t-n)}function fe(e,t){let n={x:e.x+e.width/2,y:e.y+e.height/2},i=t.x-n.x,o=t.y-n.y;if(i===0&&o===0)return n;let r=i/(e.width/2),s=o/(e.height/2),a=1/(e.kind==="ellipse"?Math.hypot(r,s):Math.max(Math.abs(r),Math.abs(s)));return{x:n.x+i*a,y:n.y+o*a}}function ve(e){let t=e.points;if(t.length<2)return{x:0,y:-12};let n={start:t[0],end:t[1],length:0};for(let o=1;o<t.length;o+=1){let r=t[o-1],s=t[o],a=Math.hypot(s.x-r.x,s.y-r.y);a>n.length&&(n={start:r,end:s,length:a})}let i=n.start.y===n.end.y;return{x:(n.start.x+n.end.x)/2+(i?0:20),y:(n.start.y+n.end.y)/2+(i?-12:0)}}function Lt(e){let t=ae(e);if(t.length>0)throw new R(t);return new It(mi(e))}var It=class{totalDuration;beats;timeline;document;nodesById;constructor(t){this.document=Ne(t),this.nodesById=new Map(t.nodes.map(r=>[r.id,r]));let n=new J(t),i=new Map,o=0;for(let[r,s]of t.scenes.entries())s.beats.forEach((a,l)=>{let d=a.actions.filter(c=>c.type==="flow");if(d.length===1){let c=this.evaluateFrame(o,{reducedMotion:!0},n).edgeGeometries.get(d[0].target);if(!Number.isFinite(c.length)||c.points.some(h=>!Number.isFinite(h.x)||!Number.isFinite(h.y)))throw new R([{path:`scenes[${r}].beats[${l}].durationMs`,message:"effective beat timing must be finite"}]);i.set(`${s.id}:${l}`,Math.max(qn,c.length/Zn*Jn))}o+=a.durationMs});this.timeline=new J(t,i),this.totalDuration=this.timeline.totalDuration,this.beats=this.timeline.schedule}evaluateAt(t,n={}){return this.evaluateFrame(t,n).frame}evaluateFrame(t,n={},i=this.timeline){let o=Te(this.document,n),r=i.evaluateAt(t,n,o),s=new Map(this.document.nodes.map(f=>{let y=r.objects.get(f.id).values;return[f.id,{...f,x:y.x??f.x,y:y.y??f.y,width:y.width??f.width,height:y.height??f.height,label:y.label,content:y.content??void 0}]})),a=Qn(r.flows,this.document.edges,r.flowArrivalProgress,r.flowDepartureProgress),l=[],d=[...s.values()].map((f,y)=>{let b=this.resolvePosition(f.id,s),x=f.label?we(f.label,Math.max(1,f.width-Tt*2),_):[],E=f.kind==="group"?b.y+24:b.y+f.height/2,v=Ee(x,b.x+f.width/2,E,_);f.label&&x.length*_.lineHeight>Math.max(0,f.height-Tt*2)&&l.push({path:`nodes[${y}].label`,objectId:f.id,code:"text-overflow",message:`label for node "${f.id}" exceeds its height`});let w=this.resolveVisibility(f.id,r.objects),P=r.objects.get(f.id),N=Math.max(P.highlight,a.get(f.id)??0),k={strokeWidth:lt,textColor:o.text,...f.style,fill:H(P.values.fill??o.fill,o.highlightFill,N),stroke:H(P.values.stroke??o.stroke,o.highlight,N)},S=f.content?ot(qt(f.content,{...b,width:f.width,height:f.height},o,f.kind),N,k.fill):void 0;if(S)for(let C of["width","height"])S[C==="width"?"widthOverflow":"heightOverflow"]&&l.push({path:`nodes[${y}].content`,objectId:f.id,code:"text-overflow",message:`content for node "${f.id}" exceeds its ${C}`});return{...f,contentLayout:S,...w,highlighted:P.highlighted||a.has(f.id),style:k,x:b.x,y:b.y,labelLines:x,labelBoxes:v}}),c=new Map(d.map(f=>[f.id,f])),h=be(this.document.edges,c),m=new Map,u=this.document.edges.map(f=>{let y=r.objects.get(f.id),b=r.flows.find(F=>F.target===f.id),x={...f,label:y.values.label},E=c.get(x.from),v=c.get(x.to),w=y.visible&&!!(E?.visible&&v?.visible),P=Math.min(y.opacity,E?.opacity??0,v?.opacity??0),N={...x.style,strokeWidth:y.values.strokeWidth??x.style?.strokeWidth??2,dashed:x.style?.dashed??!1,stroke:H(y.values.stroke??o.edge,o.highlight,Math.max(y.highlight,+(b!==void 0)*(1-r.flowDepartureProgress)))},S=x.route==="curved"&&this.document.edges.some(F=>F.id!==x.id&&F.route==="curved"&&F.from===x.to&&F.to===x.from)?1:0,C=E&&v?ye({...x,style:N},E,v,d,{reverseOffset:S,targetOffset:h.get(x.id)??0}):gt();m.set(x.id,C);let _t=x.label?we(x.label,Math.max(1,C.length-Tt*2),yt):[],Rt=ve(C),Gt=b&&w&&P>0&&!n.reducedMotion?{direction:b.direction,progress:r.flowProgress,point:C.pointAt(b.direction==="forward"?r.flowProgress:1-r.flowProgress),...b.text!==void 0?{text:b.text}:{}}:void 0;return{...x,visible:w,opacity:P,highlighted:y.highlighted||b!==void 0,style:N,path:C.path,labelLines:_t,labelBoxes:Ee(_t,Rt.x,Rt.y,yt),...Gt?{flow:Gt}:{}}}),g=si(this.document.viewport,r.focusBefore,r.focusAfter,r.transitionProgress,d,u,m);return{frame:{timeMs:r.timeMs,sceneId:r.sceneId,beatId:r.beatId,beatProgress:r.beatProgress,nodes:d,edges:u,camera:g,warnings:l,...r.caption!==void 0?{caption:r.caption}:{}},edgeGeometries:m}}renderSvg(t=0,n={}){let i=this.evaluateAt(t,n),o=Te(this.document,n);return ti(this.document,i,o)}hitTest(t,n,i={}){if(!Number.isFinite(n.x)||!Number.isFinite(n.y))return null;let o=this.evaluateFrame(t,i),r=o.frame,s=[...r.nodes].reverse(),a=h=>h.visible&&h.opacity>0&&ui(n,h),l=s.find(h=>h.kind!=="group"&&a(h));if(l)return{id:l.id,type:"node"};let d=[...r.edges].reverse().find(h=>h.visible&&h.opacity>0&&pe(n,o.edgeGeometries.get(h.id)??gt(),(h.style?.strokeWidth??2)+ri));if(d)return{id:d.id,type:"edge"};let c=s.find(h=>h.kind==="group"&&a(h));return c?{id:c.id,type:"node"}:null}resolvePosition(t,n){let i=n.get(t);if(!i)return{x:0,y:0};let o=i.parentId?this.resolvePosition(i.parentId,n):{x:0,y:0};return{x:i.x+o.x,y:i.y+o.y}}resolveVisibility(t,n){let i=this.nodesById.get(t),o=n.get(t);if(!i?.parentId)return{visible:o.visible,opacity:o.opacity};let r=this.resolveVisibility(i.parentId,n);return{visible:o.visible&&r.visible,opacity:Math.min(o.opacity,r.opacity)}}},Tt=12,Yn=3,_={fontSize:16,lineHeight:20,charWidth:8.5},yt={fontSize:14,lineHeight:18,charWidth:8.4},De=.25,Dt={fontSize:12,lineHeight:16,charWidth:7.2},et=24,Xn=10,Ie=5,Zn=180,qn=400,Jn=1e3;function we(e,t,n=_){let i=Math.max(1,Math.floor(t/n.charWidth)),o=[];for(let r of e.split(`
2
+ `)){if(r.length===0){o.push("");continue}let s=Array.from(r);for(;s.length>i;){let l=s.slice(0,i+1).lastIndexOf(" "),d=l>0?l:i;o.push(s.slice(0,d).join("")),s=Array.from(s.slice(d).join("").replace(/^\s+/,""))}o.push(s.join(""))}return o}function Qn(e,t,n,i){let o=new Map,r=1-i;for(let s of e){if(!s.highlightParticipants)continue;let a=t.find(c=>c.id===s.target);if(!a)continue;let l=s.direction==="forward"?a.from:a.to,d=s.direction==="forward"?a.to:a.from;o.set(l,r),o.set(d,Math.max(o.get(d)??0,n*r))}return o}function Ee(e,t,n,i){let o=n-(e.length-1)*i.lineHeight/2+i.fontSize*.35;return e.map((r,s)=>({text:r,x:t,y:o+s*i.lineHeight}))}function ti(e,t,n){let i=nt(e.title??"Diagram"),o=t.edges.filter(u=>u.opacity>0).map(u=>{let g=u.style?.stroke??n.edge,f=u.style?.strokeWidth??2,y=u.style?.dashed?' stroke-dasharray="6 4"':"";return`<path d="${M(u.path)}" fill="none" stroke="${M(g)}" color="${M(g)}" stroke-width="${f}"${y} marker-end="url(#diagram-arrow)" data-id="${M(u.id)}" opacity="${u.opacity}"/>`}).join(""),r=t.nodes.filter(u=>u.opacity>0&&u.kind==="group").map(u=>Pe(u,n)).join(""),s=t.nodes.filter(u=>u.opacity>0&&u.kind!=="group").map(u=>Pe(u,n)).join(""),a=t.edges.filter(u=>u.opacity>0).map(u=>Me(u.labelLines,u.labelBoxes,u.style?.stroke??n.text,yt,u.opacity,n.background)).join("")+t.nodes.filter(u=>u.opacity>0).map(u=>Me(u.labelLines,u.labelBoxes,u.style?.textColor??n.text,_,u.opacity)).join(""),l=t.nodes.filter(u=>u.opacity>0&&u.contentLayout).map((u,g)=>ei(u,g)).join(""),d=t.edges.filter(u=>u.opacity>0&&u.flow).map(u=>ni(u.flow,u.id,n,u.opacity)).join(""),c=`<defs><marker id="diagram-arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="${V}" markerHeight="${V}" orient="auto-start-reverse"><path d="M 0 0 L 10 5 L 0 10 z" fill="context-stroke"/></marker></defs>`,h=M(e.title??"Diagram");return`<svg xmlns="http://www.w3.org/2000/svg" viewBox="${`${p(t.camera.x)} ${p(t.camera.y)} ${p(t.camera.width)} ${p(t.camera.height)}`}" role="img" aria-label="${h}" font-family="${M(Xt)}"><title>${i}</title>${c}<rect x="${p(t.camera.x)}" y="${p(t.camera.y)}" width="${p(t.camera.width)}" height="${p(t.camera.height)}" fill="${n.background}"/>${r}${o}${s}${a}${l}${d}</svg>`}function ei(e,t){let n=e.contentLayout,i=n.safeArea,o=[i.x,i.y,i.width,i.height].map(c=>String(c).replaceAll("-","n").replaceAll(".","p").replaceAll("+","a")).join("-"),r=`diagram-content-clip-${t}-${o}`,s=`<defs><clipPath id="${r}" clipPathUnits="userSpaceOnUse"><rect x="${p(i.x)}" y="${p(i.y)}" width="${p(i.width)}" height="${p(i.height)}"/></clipPath></defs>`,a=n.tags.map(c=>`<rect x="${p(c.x)}" y="${p(c.y)}" width="${p(c.width)}" height="${p(c.height)}" rx="${p(c.shape==="pill"?c.height/2:Yn)}" fill="${M(c.fill)}"/>`).join(""),l=n.lines.map(c=>`<text x="${p(c.x)}" y="${p(c.y)}" fill="${M(c.color)}" font-size="${c.fontSize}" font-weight="${c.fontWeight}" text-anchor="${c.align==="left"?"start":c.align==="right"?"end":"middle"}">${c.spans.map(h=>`<tspan x="${p(h.x)}" y="${p(h.y)}" fill="${M(h.color)}" font-size="${h.fontSize}" font-weight="${h.fontWeight}" text-anchor="start">${nt(h.text)}</tspan>`).join("")}</text>`).join(""),d=(n.graphs??[]).map(c=>`<g data-content-graph="${c.blockIndex}" aria-hidden="true">${c.edges.map(h=>`<line x1="${p(h.x1)}" y1="${p(h.y1)}" x2="${p(h.x2)}" y2="${p(h.y2)}" stroke="${h.color}"/>`).join("")}${c.nodes.map(h=>`<circle cx="${p(h.x)}" cy="${p(h.y)}" r="${h.radius}" fill="${h.fill}"/>`).join("")}</g>`).join("");return`${s}<g id="diagram-content-${t}" data-content-id="${M(e.id)}" clip-path="url(#${r})" opacity="${e.opacity}">${a}${d}${l}</g>`}function ni(e,t,n,i){let o=`fill="${n.highlight}" data-flow-id="${M(t)}" opacity="${i}" pointer-events="none" aria-hidden="true"`;if(e.text===void 0)return`<circle cx="${p(e.point.x)}" cy="${p(e.point.y)}" r="${Ie}" ${o}/>`;let r=Math.max(et,Le(e.text,Dt)+Xn*2),s=p(e.point.x-r/2),a=p(e.point.y-et/2),l=`<text x="${p(e.point.x)}" y="${p(e.point.y+Dt.fontSize*.35)}" fill="${n.highlightText}" font-size="${Dt.fontSize}" text-anchor="middle">${nt(e.text)}</text>`;return`<g ${o}><rect x="${s}" y="${a}" width="${p(r)}" height="${et}" rx="${et/2}"/>${l}</g>`}function Le(e,t){return Array.from(e).reduce((n,i)=>{let o=i.codePointAt(0)>255||"MWmw@%".includes(i);return n+(o?t.fontSize:t.charWidth)},0)}function Pe(e,t){let n=e.style?.fill??t.fill,i=e.style?.stroke??t.stroke,o=e.style?.strokeWidth??lt,r=e.kind==="group"&&e.style?.dashed?' stroke-dasharray="10 7"':"",s=`fill="${M(n)}" stroke="${M(i)}" stroke-width="${o}"${r} data-id="${M(e.id)}" opacity="${e.opacity}"`;if(e.kind==="ellipse")return`<ellipse cx="${p(e.x+e.width/2)}" cy="${p(e.y+e.height/2)}" rx="${p(e.width/2)}" ry="${p(e.height/2)}" ${s}/>`;if(e.kind==="cylinder"){let l=Math.min(14,e.height/4),d=e.y+e.height-l,c=`M ${p(e.x)} ${p(e.y+l)} L ${p(e.x+e.width)} ${p(e.y+l)} L ${p(e.x+e.width)} ${p(d)} A ${p(e.width/2)} ${p(l)} 0 0 1 ${p(e.x)} ${p(d)} Z`,h=`<ellipse cx="${p(e.x+e.width/2)}" cy="${p(e.y+l)}" rx="${p(e.width/2)}" ry="${p(l)}"/>`;return`<g ${s}><path d="${c}"/>${h}</g>`}let a=e.kind==="roundedRect"?` rx="${Math.min(Yt,e.width/4,e.height/4)}"`:"";return`<rect x="${p(e.x)}" y="${p(e.y)}" width="${p(e.width)}" height="${p(e.height)}"${a} ${s}/>`}function Me(e,t,n,i,o=1,r){if(e.length===0)return"";let s=t[0]?.x??0,a=e.map((d,c)=>`<tspan x="${p(s)}" y="${p(t[c]?.y??0)}">${nt(d)}</tspan>`).join(""),l=r?` stroke="${M(r)}" stroke-width="6" stroke-linejoin="round" paint-order="stroke"`:"";return`<text fill="${M(n)}"${l} font-size="${i.fontSize}" text-anchor="middle" opacity="${o}">${a}</text>`}var $e=24,ii=1,oi=3,ri=6;function si(e,t,n,i,o,r,s){let a={x:0,y:0,width:e.width,height:ai(e,o,r,s)},l=Se(e,t,o,r,s)??a,d=Se(e,n,o,r,s)??a;return i<=0?l:i>=1?d:{x:l.x+(d.x-l.x)*i,y:l.y+(d.y-l.y)*i,width:l.width+(d.width-l.width)*i,height:l.height+(d.height-l.height)*i}}function ai(e,t,n,i){if(!e.fitHeight)return e.height;let o=[];for(let a of t)if(!(a.opacity<=0)){o.push(a.y+a.height+(a.style?.strokeWidth??lt)/2);for(let l of Ae(a.labelBoxes,_))o.push(l)}for(let a of n){if(a.opacity<=0)continue;let l=i.get(a.id);if(l){let d=a.style?.strokeWidth??2;o.push(l.paintedBottom+d/2,li(l,d))}for(let d of Ae(a.labelBoxes,yt,oi))o.push(d);a.flow&&o.push(a.flow.point.y+(a.flow.text===void 0?Ie:et/2))}if(o.length===0)return e.height;let r=o.reduce((a,l)=>Math.max(a,l),0),s=Math.max(ii,Math.ceil(r+e.fitHeight.padding));if(!Number.isFinite(s))throw new R([{path:"viewport.fitHeight",message:"current painted geometry and padding must produce a finite height"}]);return s}function li(e,t){let n=e.points.at(-1);if(!n)return 0;let i=e.terminalDirection,o=V*t,r=Math.max(.1*i.y,-.9*i.y+.5*Math.abs(i.x));return n.y+o*r}function Ae(e,t,n=0){return e.filter(i=>i.text.length>0).map(i=>i.y+t.fontSize*De+n)}function Se(e,t,n,i,o){if(!t||!n.find(x=>x.id===t))return;let s=new Set(n.filter(x=>x.id===t||hi(x,t,n)).map(x=>x.id)),a=n.filter(x=>s.has(x.id)).map(ci);for(let x of i)x.visible&&x.opacity>0&&(s.has(x.from)||s.has(x.to))&&a.push((o.get(x.id)??gt()).bounds);let l=di(a);if(!l)return;let d=e.width/e.height,c=l.right-l.left+$e*2,h=l.bottom-l.top+$e*2,m=Math.max(c,h*d),u=Math.max(h,c/d);m>e.width&&(m=e.width,u=m/d),u>e.height&&(u=e.height,m=u*d);let g=(l.left+l.right)/2,f=(l.top+l.bottom)/2,y=Ce(g-m/2,0,e.width-m),b=Ce(f-u/2,0,e.height-u);return{x:y,y:b,width:m,height:u}}function hi(e,t,n){let i=new Map(n.map(r=>[r.id,r])),o=e.parentId;for(;o;){if(o===t)return!0;o=i.get(o)?.parentId}return!1}function ci(e){return{left:e.x,top:e.y,right:e.x+e.width,bottom:e.y+e.height}}function di(e){if(e.length!==0)return{left:Math.min(...e.map(t=>t.left)),top:Math.min(...e.map(t=>t.top)),right:Math.max(...e.map(t=>t.right)),bottom:Math.max(...e.map(t=>t.bottom))}}function Ce(e,t,n){return Math.min(n,Math.max(t,e))}function ui(e,t){if(t.labelBoxes.some(i=>{if(i.text.length===0)return!1;let o=Le(i.text,_)/2;return e.x>=i.x-o&&e.x<=i.x+o&&e.y>=i.y-_.fontSize&&e.y<=i.y+_.fontSize*De}))return!0;if(t.kind==="ellipse"){let i=(e.x-(t.x+t.width/2))/(t.width/2),o=(e.y-(t.y+t.height/2))/(t.height/2);return i*i+o*o<=1}return e.x>=t.x&&e.x<=t.x+t.width&&e.y>=t.y&&e.y<=t.y+t.height}function nt(e){return e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&apos;")}function M(e){return nt(e)}function mi(e){return JSON.parse(JSON.stringify(e))}function Ne(e){if(typeof e=="object"&&e!==null){Object.freeze(e);for(let t of Object.values(e))Ne(t)}return e}function Te(e,t){return ht(t.theme??e.theme)}function kt(e,t,n={}){let i=Lt(t);return new Nt(e,i,t,n)}var Nt=class{element;diagram;sourceDocument;gifControls;options;timeMs=0;speed=1;playing=!1;loop;playbackIntent=0;destroyed=!1;frameHandle;lastTimestamp;frame;canvas;caption;controls;scenes;beatStarts;detachLifecycle;stopKeyboard;stopMotion;stopPointer;themeOverride;pointerPosition;hoveredHit=null;constructor(t,n,i,o){this.element=t,this.loop=o.loop??!1,this.diagram=n,this.sourceDocument=_e(i),this.beatStarts=n.beats.map(a=>a.startMs),this.scenes=ke(i,n);let r=t.ownerDocument,s=Ut(r,o.reducedMotion,a=>{this.destroyed||(this.options={...this.options,reducedMotion:a},this.updateFrame())});this.stopMotion=s.cleanup,this.themeOverride=o.theme,this.options={...o,theme:o.theme??i.theme??"light",reducedMotion:s.value},this.frame=n.evaluateAt(0,this.options),this.canvas=r.createElement("div"),this.canvas.className="flowsvg-canvas",this.stopKeyboard=Kt(this.canvas,{toggle:this.togglePlayback,step:a=>this.step(a)}),this.caption=r.createElement("p"),this.caption.className="flowsvg-caption",this.caption.setAttribute("aria-live","polite"),this.caption.setAttribute("aria-atomic","true"),o.controls!==!1&&(this.controls=new at(r,{toggle:this.togglePlayback,step:a=>this.step(a),seek:a=>this.seek(a),setSpeed:a=>this.setSpeed(a),toggleLoop:()=>this.setLoop(!this.loop)})),o.gifExport&&this.controls&&(this.gifControls=new it(r,()=>({document:structuredClone(this.sourceDocument),theme:this.options.theme??"light"})),this.controls.element.append(this.gifControls.element)),t.replaceChildren(this.canvas,this.caption,...this.controls?[this.controls.element]:[]),this.render(),this.stopPointer=pi(this.canvas,{move:a=>this.handlePointer("hover",a),leave:()=>this.clearPointer(),click:a=>this.handlePointer("click",a)}),this.detachLifecycle=Vt(t,this.canvas,()=>this.pause())}play(){if(!(this.destroyed||this.playing||this.isPageHidden()||this.diagram.totalDuration<=0)){if(this.timeMs>=this.diagram.totalDuration){if(!this.loop)return;let t=this.playbackIntent;if(this.timeMs=0,this.updateFrame(),this.destroyed||this.playing||t!==this.playbackIntent||this.isPageHidden())return}this.playing=!0,this.lastTimestamp=void 0,this.renderControls(),this.emit("playstatechange"),this.scheduleFrame()}}pause(){this.destroyed||(this.playbackIntent+=1,this.playing&&(this.playing=!1,this.frameHandle!==void 0&&cancelAnimationFrame(this.frameHandle),this.frameHandle=void 0,this.renderControls(),this.emit("playstatechange")))}seek(t){this.destroyed||(this.playbackIntent+=1,this.timeMs=gi(t,0,this.diagram.totalDuration),this.lastTimestamp=void 0,this.updateFrame(),this.timeMs>=this.diagram.totalDuration&&this.pause())}step(t){if(this.destroyed)return;this.pause();let i=(t>0?this.beatStarts:[...this.beatStarts].reverse()).find(o=>t>0?o>this.timeMs:o<this.timeMs);this.seek(i??(t>0?this.diagram.totalDuration:0))}setSpeed(t){this.destroyed||!Number.isFinite(t)||t<=0||(this.speed=t,this.renderControls())}setLoop(t){this.destroyed||this.loop===t||(this.loop=t,this.renderControls(),this.emit("loopchange"))}load(t){if(!this.destroyed)try{let n=Lt(t);if(this.pause(),this.destroyed)return;this.diagram=n,this.sourceDocument=_e(t),this.options={...this.options,theme:this.themeOverride??t.theme??"light"},this.beatStarts=n.beats.map(i=>i.startMs),this.scenes=ke(t,n),this.timeMs=0,this.pointerPosition=void 0,this.updateFrame()}catch(n){this.emit("error",{...this.state(),error:n instanceof Error?n:new Error(String(n))})}}getState(){return this.state()}destroy(){this.destroyed||(this.destroyed=!0,this.gifControls?.destroy(),this.controls?.destroy(),this.playing=!1,this.frameHandle!==void 0&&cancelAnimationFrame(this.frameHandle),this.frameHandle=void 0,this.lastTimestamp=void 0,this.detachLifecycle(),this.stopKeyboard(),this.stopMotion(),this.stopPointer(),this.pointerPosition=void 0,this.hoveredHit=null,this.element.replaceChildren())}tick=t=>{if(!this.playing||this.destroyed)return;this.frameHandle=void 0;let n=this.lastTimestamp??t;this.lastTimestamp=t;let i=t-n;if(this.timeMs=this.loop?yi(this.timeMs,i,this.speed,this.diagram.totalDuration):this.timeMs+i*this.speed,!this.loop&&this.timeMs>=this.diagram.totalDuration){this.timeMs=this.diagram.totalDuration,this.updateFrame(),this.pause();return}this.updateFrame(),this.scheduleFrame()};scheduleFrame(){this.destroyed||!this.playing||this.frameHandle!==void 0||(this.frameHandle=requestAnimationFrame(this.tick))}updateFrame(){let t=this.frame.beatId,n=this.frame.sceneId;this.frame=this.diagram.evaluateAt(this.timeMs,this.options),this.render(),(t!==this.frame.beatId||n!==this.frame.sceneId)&&this.emit("stepchange"),this.emit("timeupdate")}isPageHidden(){return this.element.ownerDocument.visibilityState==="hidden"}togglePlayback=()=>{this.playing?this.pause():this.play()};render(){rt(this.canvas,this.diagram.renderSvg(this.timeMs,this.options)),this.canvas.style.aspectRatio=`${this.frame.camera.width} / ${this.frame.camera.height}`,this.updateHover(),this.caption.textContent=this.frame.caption??"",this.renderControls()}renderControls(){this.controls?.render({...this.state(),sceneId:this.frame.sceneId,scenes:this.scenes})}state(){return{timeMs:this.timeMs,totalDuration:this.diagram.totalDuration,playing:this.playing,loop:this.loop,speed:this.speed,frame:this.frame}}handlePointer(t,n){if(this.destroyed)return;if(t==="hover"){this.pointerPosition={clientX:n.clientX,clientY:n.clientY},rt(this.canvas,this.diagram.renderSvg(this.timeMs,this.options)),this.updateHover(!0);return}t==="click"&&n.stopPropagation();let i=Oe(this.canvas,this.frame.camera,n),o=this.diagram.hitTest(this.timeMs,i,this.options),r=o?{...o,point:i,timeMs:this.timeMs,sceneId:this.frame.sceneId,beatId:this.frame.beatId}:null;this.emit("click",r),this.emit("objectclick",r)}updateHover(t=!1){let n=this.pointerPosition?Oe(this.canvas,this.frame.camera,this.pointerPosition):void 0,i=n?this.diagram.hitTest(this.timeMs,n,this.options):null;if(i?.type==="node"){let s=Array.from(this.canvas.querySelectorAll("[data-id]")).find(h=>h.getAttribute("data-id")===i.id),a=ht(this.options.theme),l=this.frame.nodes.find(h=>h.id===i.id);this.options.hoverFill&&s?.setAttribute("fill",a.highlightFill),s?.setAttribute("stroke",a.highlight);let d=l?.contentLayout,c=Array.from(this.canvas.querySelectorAll("[data-content-id]")).find(h=>h.getAttribute("data-content-id")===i.id);this.options.hoverFill&&c&&d&&fi(c,ot(d,1,a.highlightFill))}let o=i?.id!==this.hoveredHit?.id||i?.type!==this.hoveredHit?.type;if(this.hoveredHit=i,!t&&!o)return;let r=i&&n?{...i,point:n,timeMs:this.timeMs,sceneId:this.frame.sceneId,beatId:this.frame.beatId}:null;this.emit("hover",r),this.emit("objecthover",r)}clearPointer(){this.destroyed||(this.pointerPosition=void 0,rt(this.canvas,this.diagram.renderSvg(this.timeMs,this.options)),this.updateHover(!0))}emit(t,n){this.destroyed||this.element.dispatchEvent(new CustomEvent(t,{detail:n===void 0?this.state():n}))}};function fi(e,t){let n=e.querySelectorAll("rect");t.tags.forEach((r,s)=>n[s]?.setAttribute("fill",r.fill));let i=e.querySelectorAll("text");t.lines.forEach((r,s)=>{let a=i[s];a?.setAttribute("fill",r.color);let l=a?.querySelectorAll("tspan");r.spans.forEach((d,c)=>l?.[c]?.setAttribute("fill",d.color))});let o=e.querySelectorAll("[data-content-graph]");t.graphs?.forEach((r,s)=>{let a=o[s]?.querySelectorAll("circle"),l=o[s]?.querySelectorAll("line");r.nodes.forEach((d,c)=>a?.[c]?.setAttribute("fill",d.fill)),r.edges.forEach((d,c)=>l?.[c]?.setAttribute("stroke",d.color))})}function gi(e,t,n){return Number.isFinite(e)?Math.min(n,Math.max(t,e)):t}function ke(e,t){let n=new Map(e.scenes.map(i=>[i.id,i.label]));return t.beats.filter(i=>i.beatIndex===0).map(i=>({id:i.sceneId,label:n.get(i.sceneId),startMs:i.startMs}))}function pi(e,t){let n=r=>t.move(r),i=()=>t.leave(),o=r=>t.click(r);return e.addEventListener("pointermove",n),e.addEventListener("pointerleave",i),e.addEventListener("pointercancel",i),e.addEventListener("click",o),()=>{e.removeEventListener("pointermove",n),e.removeEventListener("pointerleave",i),e.removeEventListener("pointercancel",i),e.removeEventListener("click",o)}}function Oe(e,t,n){let i=e.querySelector("svg"),o=i?.getScreenCTM?.();if(o){let l=o.inverse();return{x:n.clientX*l.a+n.clientY*l.c+l.e,y:n.clientX*l.b+n.clientY*l.d+l.f}}let r=i?.getBoundingClientRect()??e.getBoundingClientRect(),s=r.width||1,a=r.height||1;return{x:t.x+(n.clientX-r.left)/s*t.width,y:t.y+(n.clientY-r.top)/a*t.height}}function _e(e){return JSON.parse(JSON.stringify(e))}function yi(e,t,n,i){let o=n,r=t*o,s=0;for(;!Number.isFinite(r);)o/=2,s+=1,r=t*o;let a=r%i;for(let l=0;l<s;l+=1)a=Re(a,a,i);return Re(e%i,a,i)}function Re(e,t,n){let i=n-t;return e>=i?e-i:e+t}var Ge=document.getElementById("flowsvg-data"),He=document.getElementById("flowsvg-player");if(!Ge||!He)throw new Error("FlowSVG offline playback data or host is missing.");var Ot=JSON.parse(Ge.textContent??""),bi=kt(He,Ot.document,Ot.options);Ot.options.autoplay&&bi.play();})();
@@ -0,0 +1,227 @@
1
+ /* CSS variables style playback and custom choices; core themes own diagram colors. */
2
+ .flowsvg-canvas:focus-visible {
3
+ outline: 3px solid #2563eb;
4
+ outline-offset: 3px;
5
+ }
6
+
7
+ .flowsvg-controls {
8
+ display: flex;
9
+ flex-wrap: wrap;
10
+ align-items: center;
11
+ gap: 0.5rem;
12
+ padding: 0.875rem 1rem;
13
+ border-top: 1px solid var(--flowsvg-control-border, #e2e8f0);
14
+ color: var(--flowsvg-control-color, #172033);
15
+ background: var(--flowsvg-control-background, #ffffff);
16
+ font: 500 0.8125rem/1.4 system-ui, sans-serif;
17
+ font-variant-numeric: tabular-nums;
18
+ }
19
+
20
+ .flowsvg-controls :is(button, input) {
21
+ box-sizing: border-box;
22
+ min-height: var(--flowsvg-control-height, 2.75rem);
23
+ max-width: 100%;
24
+ color: var(--flowsvg-control-color, #172033);
25
+ background: var(--flowsvg-control-background, #ffffff);
26
+ border: 1px solid var(--flowsvg-control-border, #d8dee8);
27
+ border-radius: 0.5rem;
28
+ font: inherit;
29
+ accent-color: var(--flowsvg-control-accent, #2563eb);
30
+ transition: background-color 160ms ease, border-color 160ms ease;
31
+ }
32
+
33
+ .flowsvg-controls button {
34
+ padding: var(--flowsvg-control-padding, 0.5rem 0.75rem);
35
+ cursor: pointer;
36
+ }
37
+
38
+ .flowsvg-controls button:enabled:hover,
39
+ .flowsvg-select-trigger:enabled:hover {
40
+ background: color-mix(in srgb, var(--flowsvg-control-color, #172033) 4%, var(--flowsvg-control-background, #ffffff));
41
+ border-color: color-mix(in srgb, var(--flowsvg-control-color, #172033) 25%, var(--flowsvg-control-border, #d8dee8));
42
+ }
43
+
44
+ .flowsvg-controls button:enabled:active,
45
+ .flowsvg-select-trigger:enabled:active {
46
+ background: color-mix(in srgb, var(--flowsvg-control-color, #172033) 8%, var(--flowsvg-control-background, #ffffff));
47
+ }
48
+
49
+ .flowsvg-controls .flowsvg-play-toggle {
50
+ min-width: 4.5rem;
51
+ color: var(--flowsvg-control-accent, #2563eb);
52
+ background: color-mix(in srgb, var(--flowsvg-control-accent, #2563eb) 10%, var(--flowsvg-control-background, #ffffff));
53
+ border-color: color-mix(in srgb, var(--flowsvg-control-accent, #2563eb) 35%, var(--flowsvg-control-border, #d8dee8));
54
+ font-weight: 650;
55
+ }
56
+
57
+ .flowsvg-controls .flowsvg-play-toggle:enabled:hover {
58
+ background: color-mix(in srgb, var(--flowsvg-control-accent, #2563eb) 17%, var(--flowsvg-control-background, #ffffff));
59
+ }
60
+
61
+ .flowsvg-controls .flowsvg-play-toggle:enabled:active {
62
+ background: color-mix(in srgb, var(--flowsvg-control-accent, #2563eb) 23%, var(--flowsvg-control-background, #ffffff));
63
+ }
64
+
65
+ .flowsvg-controls .flowsvg-loop-toggle[aria-pressed="true"] {
66
+ color: var(--flowsvg-control-accent, #2563eb);
67
+ background: color-mix(in srgb, var(--flowsvg-control-accent, #2563eb) 10%, var(--flowsvg-control-background, #ffffff));
68
+ border-color: var(--flowsvg-control-accent, #2563eb);
69
+ }
70
+
71
+ .flowsvg-controls :is(button, input):focus-visible {
72
+ outline: 2px solid var(--flowsvg-control-focus, #2563eb);
73
+ outline-offset: 2px;
74
+ }
75
+
76
+ .flowsvg-controls :disabled {
77
+ opacity: 0.45;
78
+ cursor: not-allowed;
79
+ }
80
+
81
+ .flowsvg-controls input[type="range"] {
82
+ flex: 1 1 10rem;
83
+ min-width: 4rem;
84
+ margin: 0 0.375rem;
85
+ padding: 0;
86
+ border: 0;
87
+ border-radius: 0;
88
+ background: transparent;
89
+ cursor: pointer;
90
+ }
91
+
92
+ .flowsvg-controls input[type="range"]:disabled {
93
+ cursor: not-allowed;
94
+ }
95
+
96
+ .flowsvg-export-controls {
97
+ display: inline-flex;
98
+ flex-wrap: wrap;
99
+ align-items: center;
100
+ gap: 0.5rem;
101
+ margin-inline-start: auto;
102
+ max-width: 100%;
103
+ }
104
+
105
+ .flowsvg-export-controls progress {
106
+ max-width: 7rem;
107
+ height: 0.375rem;
108
+ accent-color: var(--flowsvg-control-accent, #2563eb);
109
+ }
110
+
111
+ .flowsvg-export-controls [role="status"] {
112
+ font-size: 0.75rem;
113
+ }
114
+
115
+ .flowsvg-export-controls [role="status"]:empty {
116
+ display: none;
117
+ }
118
+
119
+ @media (max-width: 640px) {
120
+ .flowsvg-controls { padding: 0.75rem; }
121
+ .flowsvg-controls input[type="range"] { flex-basis: 100%; margin: 0; }
122
+ .flowsvg-export-controls { margin-inline-start: 0; }
123
+ }
124
+
125
+ @media (prefers-reduced-motion: reduce) {
126
+ .flowsvg-controls :is(button, input) { transition: none; }
127
+ }
128
+
129
+ /* Apply to a host or ancestor; sizing affects controls only, never diagram geometry. */
130
+ .flowsvg-compact {
131
+ --flowsvg-control-height: 2rem;
132
+ --flowsvg-control-padding: 0.3125rem 0.625rem;
133
+ }
134
+
135
+ .flowsvg-select {
136
+ position: relative;
137
+ display: inline-flex;
138
+ max-width: 100%;
139
+ color: var(--flowsvg-control-color, #172033);
140
+ font: 500 0.8125rem/1.4 system-ui, sans-serif;
141
+ font-variant-numeric: tabular-nums;
142
+ }
143
+
144
+ .flowsvg-select .flowsvg-select-trigger {
145
+ box-sizing: border-box;
146
+ display: inline-flex;
147
+ align-items: center;
148
+ justify-content: space-between;
149
+ gap: 1rem;
150
+ width: 100%;
151
+ min-height: var(--flowsvg-control-height, 2.75rem);
152
+ padding: var(--flowsvg-control-padding, 0.5rem 0.75rem);
153
+ color: inherit;
154
+ background: var(--flowsvg-control-background, #ffffff);
155
+ border: 1px solid var(--flowsvg-control-border, #d8dee8);
156
+ border-radius: 0.5rem;
157
+ font: inherit;
158
+ text-align: start;
159
+ cursor: pointer;
160
+ }
161
+
162
+ .flowsvg-select-trigger::after {
163
+ content: "";
164
+ width: 0.375rem;
165
+ height: 0.375rem;
166
+ margin: -0.1875rem 0.125rem 0 0;
167
+ border-right: 1.5px solid currentColor;
168
+ border-bottom: 1.5px solid currentColor;
169
+ transform: rotate(45deg);
170
+ }
171
+
172
+ .flowsvg-select-trigger:focus-visible {
173
+ outline: 2px solid var(--flowsvg-control-focus, #2563eb);
174
+ outline-offset: 2px;
175
+ }
176
+
177
+ .flowsvg-select-trigger:disabled { opacity: 0.45; cursor: not-allowed; }
178
+
179
+ .flowsvg-select-options {
180
+ box-sizing: border-box;
181
+ position: absolute;
182
+ z-index: 2;
183
+ inset-block-start: calc(100% + 0.375rem);
184
+ inset-inline-end: 0;
185
+ width: max-content;
186
+ min-width: 100%;
187
+ max-width: min(22rem, calc(100vw - 2.5rem));
188
+ max-height: 14rem;
189
+ overflow-y: auto;
190
+ overscroll-behavior: contain;
191
+ padding: 0.25rem;
192
+ color: var(--flowsvg-control-color, #172033);
193
+ background: var(--flowsvg-control-background, #ffffff);
194
+ border: 1px solid var(--flowsvg-control-border, #d8dee8);
195
+ border-radius: 0.625rem;
196
+ box-shadow: 0 8px 24px color-mix(in srgb, var(--flowsvg-control-color, #172033) 14%, transparent);
197
+ }
198
+
199
+ .flowsvg-controls .flowsvg-select-options {
200
+ inset-block-start: auto;
201
+ inset-block-end: calc(100% + 0.375rem);
202
+ }
203
+
204
+ .flowsvg-select-options [role="option"] {
205
+ position: relative;
206
+ padding: 0.5rem 1.875rem 0.5rem 0.625rem;
207
+ border-radius: 0.375rem;
208
+ overflow-wrap: anywhere;
209
+ cursor: pointer;
210
+ }
211
+
212
+ .flowsvg-select-options [role="option"]:is(:hover, [data-active]) {
213
+ background: color-mix(in srgb, var(--flowsvg-control-accent, #2563eb) 9%, var(--flowsvg-control-background, #ffffff));
214
+ }
215
+
216
+ .flowsvg-select-options [aria-selected="true"] {
217
+ color: var(--flowsvg-control-accent, #2563eb);
218
+ font-weight: 650;
219
+ }
220
+
221
+ .flowsvg-select-options [aria-selected="true"]::after {
222
+ content: "✓";
223
+ position: absolute;
224
+ inset-inline-end: 0.625rem;
225
+ }
226
+
227
+ .flowsvg-compact .flowsvg-play-toggle { min-width: 4rem; }
package/dist/react.d.ts CHANGED
@@ -1,48 +1,3 @@
1
- /**
2
- * React adapter for component lifecycle, props, events and imperative controls.
3
- * Playback/rendering reuse browser; imports do not create DOM, and the player is created only on mount.
4
- */
5
- import { type CSSProperties } from "react";
6
- import { type PlayerErrorDetail, type PlayerState } from "./browser.js";
7
- import type { DiagramDocument, ThemeName } from "./core.js";
8
- /** React document, visual options and callbacks; document reference changes trigger a reload. */
9
- export interface FlowPlayerProps {
10
- document: DiagramDocument;
11
- /** Positive playback multiplier; omit to use the player default. */
12
- speed?: number;
13
- theme?: ThemeName;
14
- controls?: boolean;
15
- /** Defaults to false: hover marks borders only. True also highlights backgrounds, including groups. */
16
- hoverFill?: boolean;
17
- reducedMotion?: boolean;
18
- /** Initial position in milliseconds, applied once; use seek afterward, and document reloads reset to zero. */
19
- initialTimeMs?: number;
20
- className?: string;
21
- style?: CSSProperties;
22
- onStepChange?: (state: PlayerState) => void;
23
- onTimeUpdate?: (state: PlayerState) => void;
24
- onPlayStateChange?: (state: PlayerState) => void;
25
- onError?: (detail: PlayerErrorDetail) => void;
26
- }
27
- /** Imperative controls for a mounted component; calls before mounting or after unmounting throw a not-mounted error. */
28
- export interface FlowPlayerHandle {
29
- /** Resume from the current position when playback can advance. */
30
- play(): void;
31
- /** Pause while retaining the current frame. */
32
- pause(): void;
33
- /** Seek in milliseconds, clamping to the timeline; non-finite values become zero. */
34
- seek(timeMs: number): void;
35
- /** Pause and jump to a beat boundary; backward from inside a beat goes to that beat's start. */
36
- step(direction: 1 | -1): void;
37
- /** Set a finite positive multiplier; ignore invalid values. */
38
- setSpeed(speed: number): void;
39
- /** Reset/pause at zero on success; emit an error and retain playback state if validation fails. */
40
- load(document: DiagramDocument): void;
41
- /** Read the current browser-player snapshot. */
42
- getState(): PlayerState;
43
- }
44
- /**
45
- * SSR renders only a host container; an effect mounts the browser player and unmount cleanup releases it.
46
- * Document reference changes call load; visual/control options rebuild the instance, with initial positioning applied once.
47
- */
48
- export declare const FlowPlayer: import("react").ForwardRefExoticComponent<FlowPlayerProps & import("react").RefAttributes<FlowPlayerHandle>>;
1
+ /** Public React adapter entry. */
2
+ export { FlowPlayer } from "./adapters/react.js";
3
+ export type { FlowPlayerHandle, FlowPlayerProps } from "./adapters/react.js";
package/dist/react.js CHANGED
@@ -1,126 +1,2 @@
1
- /**
2
- * React adapter for component lifecycle, props, events and imperative controls.
3
- * Playback/rendering reuse browser; imports do not create DOM, and the player is created only on mount.
4
- */
5
- import { createElement, forwardRef, useEffect, useImperativeHandle, useRef } from "react";
6
- import { createPlayer } from "./browser.js";
7
- /**
8
- * SSR renders only a host container; an effect mounts the browser player and unmount cleanup releases it.
9
- * Document reference changes call load; visual/control options rebuild the instance, with initial positioning applied once.
10
- */
11
- export const FlowPlayer = forwardRef(function FlowPlayer(props, ref) {
12
- const hostRef = useRef(null);
13
- const playerRef = useRef(null);
14
- const eventCleanupRef = useRef(null);
15
- const callbacksRef = useRef({});
16
- const documentRef = useRef(null);
17
- const optionsRef = useRef(null);
18
- const initialTimeRef = useRef(props.initialTimeMs);
19
- const initialAppliedRef = useRef(false);
20
- // Listeners read current callbacks through a stable ref so callback identity changes do not rebuild the player.
21
- callbacksRef.current = {
22
- onStepChange: props.onStepChange,
23
- onTimeUpdate: props.onTimeUpdate,
24
- onPlayStateChange: props.onPlayStateChange,
25
- onError: props.onError
26
- };
27
- useImperativeHandle(ref, () => ({
28
- play: () => requirePlayer(playerRef).play(),
29
- pause: () => requirePlayer(playerRef).pause(),
30
- seek: (timeMs) => requirePlayer(playerRef).seek(timeMs),
31
- step: (direction) => requirePlayer(playerRef).step(direction),
32
- setSpeed: (speed) => requirePlayer(playerRef).setSpeed(speed),
33
- load: (document) => requirePlayer(playerRef).load(document),
34
- getState: () => requirePlayer(playerRef).getState()
35
- }), []);
36
- useEffect(() => {
37
- const host = hostRef.current;
38
- if (!host)
39
- return;
40
- const nextOptions = {
41
- theme: props.theme,
42
- controls: props.controls,
43
- hoverFill: props.hoverFill,
44
- reducedMotion: props.reducedMotion
45
- };
46
- const existing = playerRef.current;
47
- const optionsChanged = !sameOptions(optionsRef.current, nextOptions);
48
- if (existing && !optionsChanged && documentRef.current !== props.document) {
49
- existing.load(props.document);
50
- documentRef.current = props.document;
51
- }
52
- else if (!existing || optionsChanged) {
53
- // Detach old callbacks before replacement so old lifecycle events cannot reach the new component state.
54
- eventCleanupRef.current?.();
55
- existing?.destroy();
56
- const player = createPlayer(host, props.document, nextOptions);
57
- // A new instance needs current props even when the separate speed effect has no changed dependency.
58
- if (props.speed !== undefined)
59
- player.setSpeed(props.speed);
60
- eventCleanupRef.current = attachPlayerEvents(player, callbacksRef);
61
- playerRef.current = player;
62
- documentRef.current = props.document;
63
- optionsRef.current = nextOptions;
64
- if (!initialAppliedRef.current && initialTimeRef.current !== undefined) {
65
- player.seek(initialTimeRef.current);
66
- }
67
- initialAppliedRef.current = true;
68
- return;
69
- }
70
- optionsRef.current = nextOptions;
71
- }, [props.document, props.theme, props.controls, props.reducedMotion, props.hoverFill]);
72
- useEffect(() => {
73
- const player = playerRef.current;
74
- if (player && props.speed !== undefined)
75
- player.setSpeed(props.speed);
76
- }, [props.speed]);
77
- useEffect(() => () => {
78
- eventCleanupRef.current?.();
79
- eventCleanupRef.current = null;
80
- playerRef.current?.destroy();
81
- playerRef.current = null;
82
- documentRef.current = null;
83
- optionsRef.current = null;
84
- initialAppliedRef.current = false;
85
- }, []);
86
- const className = props.className ? `flowsvg ${props.className}` : "flowsvg";
87
- return createElement("div", { ref: hostRef, className, style: props.style });
88
- });
89
- function requirePlayer(playerRef) {
90
- if (!playerRef.current)
91
- throw new Error("FlowPlayer is not mounted");
92
- return playerRef.current;
93
- }
94
- function sameOptions(previous, next) {
95
- if (!previous)
96
- return false;
97
- return previous.theme === next.theme
98
- && previous.controls === next.controls
99
- && previous.hoverFill === next.hoverFill
100
- && previous.reducedMotion === next.reducedMotion;
101
- }
102
- /** Translate browser event details to component callbacks; share cleanup between replacement and unmount. */
103
- function attachPlayerEvents(player, callbacksRef) {
104
- const onStepChange = (event) => {
105
- callbacksRef.current.onStepChange?.(event.detail);
106
- };
107
- const onTimeUpdate = (event) => {
108
- callbacksRef.current.onTimeUpdate?.(event.detail);
109
- };
110
- const onPlayStateChange = (event) => {
111
- callbacksRef.current.onPlayStateChange?.(event.detail);
112
- };
113
- const onError = (event) => {
114
- callbacksRef.current.onError?.(event.detail);
115
- };
116
- player.element.addEventListener("stepchange", onStepChange);
117
- player.element.addEventListener("timeupdate", onTimeUpdate);
118
- player.element.addEventListener("playstatechange", onPlayStateChange);
119
- player.element.addEventListener("error", onError);
120
- return () => {
121
- player.element.removeEventListener("stepchange", onStepChange);
122
- player.element.removeEventListener("timeupdate", onTimeUpdate);
123
- player.element.removeEventListener("playstatechange", onPlayStateChange);
124
- player.element.removeEventListener("error", onError);
125
- };
126
- }
1
+ /** Public React adapter entry. */
2
+ export { FlowPlayer } from "./adapters/react.js";