viteboard 0.1.8 → 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.
- package/bin/content-api-plugin.mjs +143 -1
- package/bin/pdf-export.mjs +337 -0
- package/content/machine/viteboard.json +1 -1
- package/dist/assets/index-CD-CmqNM.js +97 -0
- package/dist/assets/index-CD3PKrv7.css +1 -0
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/vite.config.ts +178 -1
- package/dist/assets/index-2x-lv2vK.css +0 -1
- package/dist/assets/index-XoZPFUvZ.js +0 -95
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
var Cs=Object.defineProperty;var ks=(i,t,e)=>t in i?Cs(i,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):i[t]=e;var u=(i,t,e)=>ks(i,typeof t!="symbol"?t+"":t,e);(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const s of document.querySelectorAll('link[rel="modulepreload"]'))n(s);new MutationObserver(s=>{for(const o of s)if(o.type==="childList")for(const a of o.addedNodes)a.tagName==="LINK"&&a.rel==="modulepreload"&&n(a)}).observe(document,{childList:!0,subtree:!0});function e(s){const o={};return s.integrity&&(o.integrity=s.integrity),s.referrerPolicy&&(o.referrerPolicy=s.referrerPolicy),s.crossOrigin==="use-credentials"?o.credentials="include":s.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function n(s){if(s.ep)return;s.ep=!0;const o=e(s);fetch(s.href,o)}})();const $="/__api";class se extends Error{constructor(e,n){super(e);u(this,"currentRevision");this.name="RevisionConflictError",this.currentRevision=n}}async function P(i){var t;if(!i.ok){let e="";try{e=((t=await i.json())==null?void 0:t.error)??""}catch{}throw new Error(`${i.status} ${i.statusText}${e?` — ${e}`:""}`)}return i.json()}function D(i={}){return i}const k={async getWorkspace(){return P(await fetch(`${$}/workspace`,D()))},async removeProject(i){await P(await fetch(`${$}/workspace/project?id=${encodeURIComponent(i)}`,D({method:"DELETE"})))},async pickAndAddProject(){return P(await fetch(`${$}/workspace/pick-project`,D({method:"POST"})))},async addProject(i,t){return P(await fetch(`${$}/workspace/project`,D({method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({rootPath:i,label:t})})))},async ensureProjectDocsRoot(i){return(await P(await fetch(`${$}/workspace/project/docs-root`,D({method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectId:i})})))).root},async getTree(i){return(await P(await fetch(`${$}/tree?rootId=${encodeURIComponent(i)}`,D()))).tree},async getProjectTree(i){return(await P(await fetch(`${$}/project-tree?projectId=${encodeURIComponent(i)}`,D()))).tree},async createDocsFolder(i,t){await P(await fetch(`${$}/workspace/project/create-docs-folder`,D({method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectId:i,parentPath:t})})))},async readFile(i,t){return(await P(await fetch(`${$}/file?rootId=${encodeURIComponent(i)}&path=${encodeURIComponent(t)}`,D()))).content},async readFileWithRevision(i,t){return P(await fetch(`${$}/file?rootId=${encodeURIComponent(i)}&path=${encodeURIComponent(t)}`,D()))},async writeFile(i,t,e,n){const s=await fetch(`${$}/file`,D({method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({rootId:i,path:t,content:e,expectedRevision:n})}));if(s.status===409){const a=await s.json();throw new se(a.error??"File changed on disk",a.currentRevision??null)}return(await P(s)).revision},async createDirectory(i,t){await P(await fetch(`${$}/directory`,D({method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({rootId:i,path:t})})))},async getBoard(i,t){const e=await fetch(`${$}/board?rootId=${encodeURIComponent(i)}&id=${encodeURIComponent(t)}`,D());return e.status===404?null:(await P(e)).doc},async getBoardWithRevision(i,t){const e=await fetch(`${$}/board?rootId=${encodeURIComponent(i)}&id=${encodeURIComponent(t)}`,D());return e.status===404?null:P(e)},async saveBoard(i,t,e,n){const s=await fetch(`${$}/board`,D({method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({rootId:i,id:t,doc:e,expectedRevision:n})}));if(s.status===409){const a=await s.json();throw new se(a.error??"Board changed on disk",a.currentRevision??null)}return(await P(s)).revision},async deleteFile(i,t){await P(await fetch(`${$}/file?rootId=${encodeURIComponent(i)}&path=${encodeURIComponent(t)}`,D({method:"DELETE"})))},async renameFile(i,t,e,n=!1){await P(await fetch(`${$}/file`,D({method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({rootId:i,oldPath:t,newPath:e,ignoreMissing:n})})))},async deleteBoard(i,t){await P(await fetch(`${$}/board?rootId=${encodeURIComponent(i)}&id=${encodeURIComponent(t)}`,D({method:"DELETE"})))},assetUrl(i,t){return`${$}/asset?rootId=${encodeURIComponent(i)}&path=${encodeURIComponent(t)}`},async uploadAsset(i,t,e,n){return(await P(await fetch(`${$}/asset`,D({method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({rootId:i,name:t,base64:e,mimeType:n})})))).path},async cleanupManagedAssets(i,t=[]){return(await P(await fetch(`${$}/asset/cleanup`,D({method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({rootId:i,preservePaths:t})})))).removed}};function Es(i,t){const e=i.find(o=>o.type==="dir"&&o.name===t);if(!e||e.type!=="dir")return[];const n=[],s=o=>{for(const a of o)a.type==="file"?n.push(a):s(a.children)};return s(e.children),n}function Ue(i){for(const t of i){if(t.type==="file"&&t.name.endsWith(".md"))return t.path;if(t.type==="dir"){const e=Ue(t.children);if(e)return e}}return null}function wt(i){const t=[];for(const e of i.replace(/\\/g,"/").split("/")){const n=e.trim();if(!(!n||n===".")){if(n===".."){t.length>0&&t.pop();continue}t.push(n)}}return t.join("/")}function qn(i){const t=wt(i),e=t.lastIndexOf("/");return e===-1?"":t.slice(0,e)}function Ss(i,t){if(/^(https?:|mailto:|tel:|#|data:)/.test(t))return t;if(t.startsWith("/"))return wt(t);const e=qn(i);return wt(e?`${e}/${t}`:t)}function Ke(i,t){const e=qn(i),n=e?e.split("/").filter(Boolean):[],s=wt(t).split("/").filter(Boolean);let o=0;for(;o<n.length&&o<s.length&&n[o]===s[o];)o++;const a=n.slice(o).map(()=>".."),r=s.slice(o);return[...a,...r].join("/")||s[s.length-1]||""}const Ts=["sticky","task","rectangle","ellipse","text","line","arrow","connector","image","screenshot","frame","templateGroup"];function ce(i){const t=new Set;for(const e of i.elements){const n=e.data.assetId;typeof n=="string"&&t.add(n)}return{...i.schemaUrl?{$schema:i.schemaUrl}:{},version:1,id:i.boardId,title:i.title,createdAt:i.createdAt,updatedAt:Date.now(),viewport:{...i.viewport},theme:i.theme,elements:i.elements,assetIds:[...t]}}function Y(i){return typeof i=="number"&&Number.isFinite(i)}function Yn(i){if(typeof i!="object"||i===null)return null;const t=i;return typeof t.id!="string"||typeof t.type!="string"||!Ts.includes(t.type)||!Y(t.x)||!Y(t.y)||!Y(t.width)||!Y(t.height)?null:{id:t.id,type:t.type,x:t.x,y:t.y,width:t.width,height:t.height,rotation:Y(t.rotation)?t.rotation:0,zIndex:Y(t.zIndex)?t.zIndex:0,locked:t.locked===!0,hidden:t.hidden===!0,createdAt:Y(t.createdAt)?t.createdAt:Date.now(),updatedAt:Y(t.updatedAt)?t.updatedAt:Date.now(),style:typeof t.style=="object"&&t.style!==null?t.style:{},data:typeof t.data=="object"&&t.data!==null?t.data:{}}}function we(i){if(typeof i!="object"||i===null)return null;const t=i;if(t.version!==1||typeof t.id!="string"||!Array.isArray(t.elements))return null;const e=[];for(const s of t.elements){const o=Yn(s);o&&e.push(o)}const n=t.viewport??{};return{...typeof t.$schema=="string"?{$schema:t.$schema}:{},version:1,id:t.id,title:typeof t.title=="string"?t.title.slice(0,200):"Imported Board",createdAt:Y(t.createdAt)?t.createdAt:Date.now(),updatedAt:Y(t.updatedAt)?t.updatedAt:Date.now(),viewport:{offsetX:Y(n.offsetX)?n.offsetX:0,offsetY:Y(n.offsetY)?n.offsetY:0,scale:Y(n.scale)&&n.scale>.01&&n.scale<=40?n.scale:1},theme:t.theme==="dark"?"dark":"light",elements:e,assetIds:Array.isArray(t.assetIds)?t.assetIds.filter(s=>typeof s=="string"):[]}}const Bs="/board-v1.schema.json",Ms=".board.json";function G(i){return i.replace(/\.board\.json$/,"")}function Is(i){return i.trim().replace(/^\/+/,"").replace(/\.board\.json$/,"")}function Rs(i,t){return re(i,t)[0]??""}function re(i,t){const e=i.trim(),n=Is(i);if(!n)return[];if(e.startsWith("./")||e.startsWith("../")){const r=t.lastIndexOf("/"),c=r===-1?"":t.slice(0,r);return[wt(c?`${c}/${n}`:n)]}if(e.startsWith("/")){const r=t.lastIndexOf("/"),c=r===-1?"":t.slice(0,r),l=wt(c?`${c}/${n}`:n),d=[n];if(l&&l!==n&&d.push(l),c){const h=c.includes("/")?c.slice(0,c.lastIndexOf("/")):"",p=wt(h?`${h}/${n}`:n);p&&!d.includes(p)&&d.push(p)}return d}const s=t.lastIndexOf("/"),o=s===-1?"":t.slice(0,s),a=wt(o?`${o}/${n}`:n);return a&&a!==n?[a,n]:[n]}function nn(i){return i.toLowerCase().replace(/[^\w\s-]/g,"").trim().replace(/\s+/g,"-").slice(0,48)||"board"}function Ls(i){return new Promise((t,e)=>{const n=new FileReader;n.onload=()=>{const s=n.result;t(s.slice(s.indexOf(",")+1))},n.onerror=()=>e(n.error),n.readAsDataURL(i)})}const J={async listIds(i){const t=await k.getTree(i);return $s(t).map(e=>G(e.path))},async list(i){const t=await this.listIds(i),e=[];for(const n of t){const s=await this.load(i,n);s&&e.push({id:n,title:s.title,elementCount:s.elements.length,updatedAt:s.updatedAt,filePath:`${n}.board.json`})}return e.sort((n,s)=>s.updatedAt-n.updatedAt),e},async load(i,t){const e=await k.getBoard(i,t);if(e)return we(e)??void 0},async save(i,t){await k.saveBoard(i,t.id,t)},async delete(i,t){await k.deleteBoard(i,t)},async move(i,t,e){const n=await this.load(i,t);if(!n)throw new Error("Board not found");const s=t.split("/").pop()??t,o=e.replace(/^\/+|\/+$/g,""),a=o?`${o}/${s}`:s;if(a===t)return t;if(new Set(await this.listIds(i)).has(a))throw new Error("A board already exists there");const c=Date.now();return await k.saveBoard(i,a,{...n,id:a,updatedAt:c}),await k.deleteBoard(i,t),a},async rename(i,t,e){const n=await this.load(i,t);if(!n)throw new Error("Board not found");const s=t.includes("/")?t.slice(0,t.lastIndexOf("/")):"",o=nn(e),a=s?`${s}/${o}`:o,r=Date.now();if(a===t)return await k.saveBoard(i,t,{...n,title:e.trim()||n.title,updatedAt:r}),t;if(new Set(await this.listIds(i)).has(a))throw new Error("A board already exists with that name");return await k.saveBoard(i,a,{...n,id:a,title:e.trim()||n.title,updatedAt:r}),await k.deleteBoard(i,t),a},async create(i,t,e,n=""){const s=new Set(await this.listIds(i)),o=nn(t);let r=`${n?`${n.replace(/^\/+|\/+$/g,"")}/`:""}${o}`;s.has(r)&&(r=`${r}-${Date.now().toString(36)}`);const c=Date.now(),l={$schema:Bs,version:1,id:r,title:t.trim()||"Untitled Board",createdAt:c,updatedAt:c,viewport:{offsetX:0,offsetY:0,scale:1},theme:e,elements:[],assetIds:[]};return await k.saveBoard(i,r,l),r},async uploadImage(i,t,e){const n=await Ls(t);return k.uploadAsset(i,e??"image.png",n,t.type)},async cleanupUnusedAssets(i,t=[]){return k.cleanupManagedAssets(i,t)}};function $s(i){const t=[],e=n=>{for(const s of n)s.type==="file"?s.name.endsWith(Ms)&&t.push(s):e(s.children)};return e(i),t}const Ps=new Set(["capabilities","assets"]);class Ds{constructor(t,e){u(this,"element");u(this,"header");u(this,"nav");u(this,"footer");u(this,"links",new Map);u(this,"collapsibles",new Map);u(this,"collapsedState",new Map);u(this,"callbacks");u(this,"contextMenu");u(this,"draggedItem");u(this,"dropExpandTimer");u(this,"dropExpandTarget");u(this,"lastWorkspace",{projects:[]});u(this,"lastTreesByRoot",new Map);u(this,"lastTreesByProject",new Map);u(this,"rawModeProjects",new Set);u(this,"lastActiveHash","");u(this,"forceNextActiveSync",!0);this.callbacks=e,this.element=document.createElement("aside"),this.element.className="sidebar",this.header=document.createElement("div"),this.header.className="sidebar-header";const n=document.createElement("a");n.className="sidebar-brand",n.href="#/",n.textContent="ViteBoard",this.header.appendChild(n),this.nav=document.createElement("nav"),this.nav.className="sidebar-nav",this.footer=document.createElement("div"),this.footer.className="sidebar-footer";const s=document.createElement("button");s.className="sidebar-add-repo",s.type="button",s.textContent="Add Repo",s.title="Add a local project root",s.addEventListener("click",()=>this.callbacks.onAddRepo()),this.footer.appendChild(s),this.element.append(this.header,this.nav,this.footer),t.appendChild(this.element),document.addEventListener("pointerdown",o=>{this.contextMenu&&(this.contextMenu.contains(o.target)||this.closeContextMenu())},!0)}refresh(t,e,n){this.lastWorkspace=t,this.lastTreesByRoot=e,n&&(this.lastTreesByProject=n),this.render()}render(){const t=this.lastWorkspace,e=this.lastTreesByRoot;if(this.lastTreesByProject,this.nav.replaceChildren(),this.links.clear(),this.collapsibles.clear(),t.projects.length===0){const s=document.createElement("div");s.className="nav-group",s.appendChild(this.groupLabel("Workspace"));const o=document.createElement("div");o.className="nav-label nav-empty-copy",o.textContent="Add a project root to discover docs folders.",s.appendChild(o),this.nav.appendChild(s)}else for(const s of t.projects)this.nav.appendChild(this.projectSection(s,e));const n=document.createElement("div");n.className="nav-group",n.appendChild(this.groupLabel("System")),n.appendChild(this.link("#/tokens","Tokens","tokens-root")),this.nav.appendChild(n),this.setActive(location.hash,{forceSyncAncestors:this.forceNextActiveSync}),this.forceNextActiveSync=!1}setActive(t,e={}){var s,o;for(const a of new Set(this.links.values()))a.classList.remove("active");const n=this.links.get(t);if(n)n.classList.add("active");else if(t.startsWith("#/root/")){const a=t.match(/^#\/root\/([^/]+)\/(boards|capabilities|docs)(?:\/(.+))?$/);if(a){const[,r,c]=a,l=decodeURIComponent(r);c==="boards"?(s=this.links.get(Hs(l)))==null||s.classList.add("active"):c==="capabilities"&&((o=this.links.get(Fs(l)))==null||o.classList.add("active"))}}(e.forceSyncAncestors||t!==this.lastActiveHash)&&this.syncCollapsed(t),this.lastActiveHash=t}toggleRawMode(t){this.rawModeProjects.has(t)?this.rawModeProjects.delete(t):this.rawModeProjects.add(t),this.forceNextActiveSync=!0,this.render()}syncCollapsed(t){const e=this.ancestorKeys(t);for(const n of e)this.applyCollapsed(n,!1)}ancestorKeys(t){const e=new Set,n=t.match(/^#\/project\/([^/]+)(?:\/boards)?$/);if(n)return e.add(decodeURIComponent(n[1])),e;const s=t.match(/^#\/root\/([^/]+)\/(docs|boards|capabilities)(?:\/(.+))?$/);if(!s)return e;const o=decodeURIComponent(s[1]),a=s[2],r=s[3]?decodeURIComponent(s[3]):"";let c,l;for(const p of this.lastWorkspace.projects){const m=p.docsRoots.find(g=>g.id===o);if(m){c=p.id,l=m.displayPath;break}}if(!c||!l)return e;e.add(c),e.add(o);const d=l.split("/").filter(Boolean).slice(0,-1);let h="";for(const p of d){h=h?`${h}/${p}`:p;const m=`folder:${c}:${h}`;this.collapsibles.has(m)&&e.add(m)}if((a==="docs"||a==="capabilities")&&r){const m=(a==="capabilities"?`capabilities/${r}`:r).split("/");for(let g=1;g<m.length;g++){const y=m.slice(0,g).join("/"),b=`doc:${o}:${y}`;this.collapsibles.has(b)&&e.add(b)}}return e}projectSection(t,e){const{id:n,label:s,docsRoots:o}=t,a=this.rawModeProjects.has(n),r=document.createElement("div");r.className="nav-repo-body";const c=document.createElement("div");c.className="nav-group nav-repo-group";const l=document.createElement("div");l.className="nav-repo-header-row";const d=document.createElement("button");d.className="nav-repo-header",d.type="button";const h=document.createElement("span");h.className="nav-repo-chevron",h.textContent="›";const p=document.createElement("span");p.className="nav-repo-label-text",p.textContent=s;const m=!a&&o.find(v=>xt(v.displayPath)==="docs"),g=document.createElement("span");g.className="nav-tree-badge",g.textContent="DOCS",g.hidden=!m;const y=document.createElement("button");y.className="nav-repo-more",y.type="button",y.title="Project options",y.innerHTML='<svg viewBox="0 0 16 16" width="14" height="14" fill="currentColor"><circle cx="8" cy="3" r="1.3"/><circle cx="8" cy="8" r="1.3"/><circle cx="8" cy="13" r="1.3"/></svg>',y.addEventListener("click",v=>{v.stopPropagation();const S=y.getBoundingClientRect();this.showContextMenu(S.right+4,S.top,s,[{icon:a?Et:At,iconClass:a?"nav-ctx-doc":"nav-ctx-dir",label:a?"View: Docs":"View: Raw",action:()=>this.toggleRawMode(n)},{icon:$e,iconClass:"nav-ctx-edit",label:"Wiki",action:()=>{location.hash=`#/project/${encodeURIComponent(n)}`}},{kind:"divider"},{icon:Et,iconClass:"nav-ctx-doc",label:"New Doc",action:()=>this.callbacks.onCreateProjectDoc(n)},{icon:Dt,iconClass:"nav-ctx-board",label:"New Board",action:()=>this.callbacks.onCreateProjectBoard(n)},{icon:At,iconClass:"nav-ctx-dir",label:"New Directory",action:()=>this.callbacks.onCreateProjectDirectory(n)},{kind:"divider"},{icon:Vt,iconClass:"nav-ctx-delete",label:"Remove from workspace",action:()=>this.callbacks.onRemoveProject(n,s),danger:!0}])}),d.append(h,p,g),l.append(d,y),this.registerCollapsible(n,r,c,d,!0),d.addEventListener("click",()=>this.toggleCollapsible(n)),l.addEventListener("contextmenu",v=>{v.preventDefault(),v.stopPropagation(),y.click()});const b=Os(t,this.lastTreesByProject.get(n)??[],e),x=a?b:Xe(ve(b));if(x.length===0){const v=document.createElement("div");v.className="nav-label nav-empty-copy",v.textContent="No folders found.",r.appendChild(v)}else r.appendChild(this.renderCanonicalNodes(t,x,0,a));return c.append(l,r),c}renderCanonicalNodes(t,e,n,s){const o=document.createElement("div");for(const a of Us(e)){if(a.kind==="doc"||a.kind==="board"||a.kind==="capability"||a.kind==="file"){o.appendChild(this.renderCanonicalFile(a,n));continue}const r=document.createElement("div");r.className="nav-tree-group",r.style.setProperty("--tree-depth",String(n));const c=document.createElement("div");c.className="nav-tree-children";const l=document.createElement("button");l.className="nav-tree-row nav-tree-folder"+(a.kind==="docsRoot"?" nav-tree-docs-root":"")+(a.docsContainerRoot?" nav-tree-docs-container":"")+(a.root?"":" nav-tree-scaffold"),l.type="button",l.style.setProperty("--tree-depth",String(n));const d=document.createElement("span");d.className="nav-repo-chevron",d.textContent="›";const h=_s(a);h&&l.classList.add("nav-tree-capability-folder");const p=document.createElement("span");if(p.textContent=a.name,l.appendChild(d),h&&l.insertAdjacentHTML("beforeend",Re),l.appendChild(p),a.kind==="docsRoot"||a.docsContainerRoot){const g=document.createElement("span");g.className="nav-tree-badge",g.textContent="DOCS",l.appendChild(g)}const m=a.kind==="docsRoot"||a.docsContainerRoot?(a.docsContainerRoot??a.root).id:a.root?`doc:${a.root.id}:${a.contentPath??""}`:`folder:${t.id}:${a.projectPath}`;if(this.registerCollapsible(m,c,r,l,!0),l.addEventListener("click",()=>this.toggleCollapsible(m)),a.kind==="docsRoot"||a.docsContainerRoot){const g=a.docsContainerRoot??a.root;l.addEventListener("contextmenu",y=>{y.preventDefault(),y.stopPropagation(),this.openDirContextMenu(y.clientX,y.clientY,g,"")}),this.enableDirectoryDropTarget(r,g.id,"")}else if(a.root){const g=a.root,y=a.contentPath??"";this.enableDragSource(l,{kind:"dir",rootId:g.id,path:y}),this.enableDirectoryDropTarget(r,g.id,y),r.addEventListener("contextmenu",b=>{b.preventDefault(),b.stopPropagation(),this.openDirContextMenu(b.clientX,b.clientY,g,y)})}else l.addEventListener("contextmenu",g=>{g.preventDefault(),g.stopPropagation(),this.showContextMenu(g.clientX,g.clientY,a.name,[{icon:At,iconClass:"nav-ctx-dir",label:"Create docs folder here",action:()=>this.callbacks.onCreateDocsFolder(t.id,a.projectPath)}])});c.appendChild(this.renderCanonicalNodes(t,a.children,n+1,s)),r.append(l,c),o.appendChild(r)}return o}renderCanonicalFile(t,e){if(t.kind==="file"){if(t.root&&t.contentPath&&ie(t.contentPath)){const l=document.createElement("button");return l.className="nav-link nav-tree-row nav-tree-file nav-tree-capability-file",l.type="button",l.style.setProperty("--tree-depth",String(e)),l.insertAdjacentHTML("afterbegin",Le),l.appendChild(document.createTextNode(t.name)),l.addEventListener("click",d=>{d.preventDefault(),this.callbacks.onOpenTextFile(t.root.id,t.contentPath)}),l.addEventListener("contextmenu",d=>{d.preventDefault(),d.stopPropagation(),this.openTextFileContextMenu(d.clientX,d.clientY,t.root,t.contentPath)}),l}const c=document.createElement("div");return c.className="nav-tree-row nav-tree-file",c.style.setProperty("--tree-depth",String(e)),c.textContent=t.name,c}const n=t.root,s=t.contentPath,o=t.kind==="doc"?sn(n.id,s):t.kind==="board"?on(n.id,G(s)):js(n.id,s),a=t.kind==="doc"&&t.name==="index.md"?"Overview":Nt(t.kind==="board"?G(t.name):t.name),r=this.link(o,a,`${t.kind}:${n.id}:${s}`);return r.classList.add("nav-tree-row",`nav-tree-${t.kind}`),r.style.setProperty("--tree-depth",String(e)),r.insertAdjacentHTML("afterbegin",t.kind==="board"?Dt:t.kind==="capability"?Re:Et),this.enableDragSource(r,t.kind==="board"?{kind:"board",rootId:n.id,boardId:G(s)}:t.kind==="capability"?{kind:"capability",rootId:n.id,path:s}:{kind:"doc",rootId:n.id,path:s}),r.addEventListener("contextmenu",c=>{c.preventDefault(),c.stopPropagation(),t.kind==="board"?this.openBoardContextMenu(c.clientX,c.clientY,n,G(s)):t.kind==="doc"&&this.openDocContextMenu(c.clientX,c.clientY,n,s)}),r}renderBrowseNode(t,e,n){if(e.type==="file"){const p=document.createElement("div");return p.className="nav-browse-file",p.textContent=e.name,p}if(e.name==="docs"){const p=t.docsRoots.find(m=>m.displayPath===e.path||m.displayPath.endsWith("/"+e.path));if(p){const m=n.get(p.id)??[];return this.docsRootSection(p,m)}}const o=Gn(e),a=`browse:${t.id}:${e.path}`,r=document.createElement("div");r.className=o?"nav-subgroup nav-collapsible-dir":"nav-subgroup nav-browse-dir";const c=document.createElement("div");c.className="nav-dir-body";const l=document.createElement("button");l.className=o?"nav-dir-toggle nav-dir-project":"nav-browse-dir-toggle",l.type="button";const d=document.createElement("span");d.className="nav-repo-chevron",d.textContent="›";const h=document.createElement("span");if(h.textContent=e.name,l.append(d,h),l.addEventListener("click",()=>this.toggleCollapsible(a)),o){const p=t.docsRoots.find(m=>m.displayPath.startsWith(e.path+"/")||m.displayPath===e.path);p&&l.addEventListener("contextmenu",m=>{m.preventDefault(),m.stopPropagation(),this.openDirContextMenu(m.clientX,m.clientY,p,"")})}else l.addEventListener("contextmenu",p=>{p.preventDefault(),p.stopPropagation(),this.showContextMenu(p.clientX,p.clientY,e.name,[{icon:At,iconClass:"nav-ctx-dir",label:"Create /docs/",action:()=>this.callbacks.onCreateDocsFolder(t.id,e.path)}])});for(const p of e.children)p.type==="dir"&&c.appendChild(this.renderBrowseNode(t,p,n));return this.registerCollapsible(a,c,r,l,!0),r.append(l,c),r}renderPathNodes(t,e,n,s=""){const o=document.createElement("div");for(const a of t)if(a.kind==="root")o.appendChild(this.docsRootSection(a.root,e.get(a.root.id)??[]));else{const r=s?`${s}/${a.segment}`:a.segment,c=`pathseg:${n}:${r}`,l=this.renderPathNodes(a.children,e,n,r);l.classList.add("nav-dir-body");const d=document.createElement("div");d.className="nav-subgroup nav-collapsible-dir";const h=Zn(a),{row:p,toggle:m}=this.createDirectoryRow(a.segment,`#/project/${encodeURIComponent(n)}`,`${a.segment} wiki`,()=>this.toggleCollapsible(c),!0);h&&d.addEventListener("contextmenu",g=>{g.preventDefault(),g.stopPropagation(),this.openDirContextMenu(g.clientX,g.clientY,h,"")}),this.registerCollapsible(c,l,d,m,!0),d.append(p,l),o.appendChild(d)}return o}docsRootSection(t,e){const n=document.createElement("div");n.className="nav-subgroup nav-docs-root";const s=t.displayPath.split("/").pop()??t.displayPath;if(s.toLowerCase()!=="docs"){const a=document.createElement("div");a.className="nav-root-path-label",a.textContent=s,n.appendChild(a),a.addEventListener("contextmenu",r=>{r.preventDefault(),r.stopPropagation(),this.openDirContextMenu(r.clientX,r.clientY,t,"")})}return n.addEventListener("contextmenu",a=>{var r;(r=a.target)!=null&&r.closest(".nav-link, .nav-dir-toggle")||(a.preventDefault(),a.stopPropagation(),this.openDirContextMenu(a.clientX,a.clientY,t,""))}),this.enableDirectoryDropTarget(n,t.id,""),n.appendChild(this.docsNodes(t,As(e),"",e)),n}createDirectoryRow(t,e,n,s,o=!1){const a=document.createElement("div");a.className="nav-dir-header-row";const r=document.createElement("button");r.className=o?"nav-dir-toggle nav-dir-project":"nav-dir-toggle nav-dir-docs",r.type="button";const c=document.createElement("span");c.className="nav-repo-chevron",c.innerHTML='<svg viewBox="0 0 16 16" width="10" height="10" fill="currentColor"><path d="M6 4l4 4-4 4"/></svg>';const l=document.createElement("span");l.textContent=t,r.append(c,l),r.addEventListener("click",s);const d=document.createElement("a");return d.className="nav-repo-wiki-link nav-dir-wiki-link",d.href=e,d.title=n,d.innerHTML='<svg viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="2" width="10" height="10" rx="2"/><path d="M5 5h4M5 7h4M5 9h2"/></svg>',d.addEventListener("click",h=>h.stopPropagation()),this.links.set(e,d),a.append(r,d),{row:a,toggle:r}}docsNodes(t,e,n,s){const o=document.createElement("div"),a=Kn(e),r=Ns(s,n),c=[...a.map(l=>({kind:l.type==="file"&&ie(l.path)?"file":"doc",node:l})),...r.map(l=>({kind:"board",name:Nt(G(l.path).split("/").pop()??G(l.path)),boardId:G(l.path)}))].sort((l,d)=>{const h=l.kind==="doc"&&l.node.type==="file"&&l.node.name==="index.md",p=d.kind==="doc"&&d.node.type==="file"&&d.node.name==="index.md";if(h!==p)return h?-1:1;const m=(l.kind==="doc"||l.kind==="file")&&l.node.type==="file"&&_e(l.node.name),g=(d.kind==="doc"||d.kind==="file")&&d.node.type==="file"&&_e(d.node.name);if(m!==g)return m?-1:1;const y=(l.kind==="doc"||l.kind==="file")&&l.node.type==="dir",b=(d.kind==="doc"||d.kind==="file")&&d.node.type==="dir";if(y!==b)return y?-1:1;const x=l.kind==="board"?l.name:l.node.name,v=d.kind==="board"?d.name:d.node.name;return x.localeCompare(v)});for(const l of c)if(l.kind==="board"){const d=document.createElement("a");d.className="nav-link nav-link-board",d.href=on(t.id,l.boardId),d.insertAdjacentHTML("afterbegin",Dt),d.appendChild(document.createTextNode(l.name));const h=`board:${t.id}:${l.boardId}`;this.links.set(h,d),this.links.set(d.href,d),this.enableDragSource(d,{kind:"board",rootId:t.id,boardId:l.boardId}),d.addEventListener("contextmenu",p=>{p.preventDefault(),p.stopPropagation(),this.openBoardContextMenu(p.clientX,p.clientY,t,l.boardId)}),o.appendChild(d)}else{const{node:d}=l;if(d.type==="file"){if(l.kind==="file"){const m=document.createElement("button");m.className="nav-link nav-tree-file nav-tree-capability-file",m.type="button",m.insertAdjacentHTML("afterbegin",Le),m.appendChild(document.createTextNode(d.name)),m.addEventListener("click",()=>this.callbacks.onOpenTextFile(t.id,d.path)),m.addEventListener("contextmenu",g=>{g.preventDefault(),g.stopPropagation(),this.openTextFileContextMenu(g.clientX,g.clientY,t,d.path)}),o.appendChild(m);continue}if(!d.name.endsWith(".md"))continue;const h=d.name==="index.md"?"Overview":Nt(d.name),p=this.link(sn(t.id,d.path),h,`doc:${t.id}:${d.path}`);p.insertAdjacentHTML("afterbegin",Et),this.enableDragSource(p,{kind:"doc",rootId:t.id,path:d.path}),p.addEventListener("contextmenu",m=>{m.preventDefault(),m.stopPropagation(),this.openDocContextMenu(m.clientX,m.clientY,t,d.path)}),o.appendChild(p)}else{const h=`doc:${t.id}:${d.path}`,p=this.docsNodes(t,d.children,d.path,s);p.classList.add("nav-dir-body");const m=document.createElement("div");m.className="nav-subgroup nav-collapsible-dir";const{row:g,toggle:y}=this.createDirectoryRow(Nt(d.name),`#/root/${encodeURIComponent(t.id)}/directory/${encodeURIComponent(d.path)}`,`${d.name} wiki`,b=>{b.button===0&&this.toggleCollapsible(h)},!1);this.enableDragSource(y,{kind:"dir",rootId:t.id,path:d.path}),m.addEventListener("contextmenu",b=>{b.preventDefault(),b.stopPropagation(),this.openDirContextMenu(b.clientX,b.clientY,t,d.path)}),this.enableDirectoryDropTarget(m,t.id,d.path),this.registerCollapsible(h,p,m,y,!0),m.append(g,p),o.appendChild(m)}}return o}enableDragSource(t,e){t.draggable=!0,t.addEventListener("dragstart",n=>{var s,o;this.closeContextMenu(),this.draggedItem=e,t.classList.add("nav-dragging"),(s=n.dataTransfer)==null||s.setData("application/x-docs-nav-item",JSON.stringify(e)),(o=n.dataTransfer)==null||o.setData("text/plain",Xs(e)),n.dataTransfer&&(n.dataTransfer.effectAllowed="move")}),t.addEventListener("dragend",()=>{t.classList.remove("nav-dragging"),this.draggedItem=void 0,this.clearDropTargets()})}enableDirectoryDropTarget(t,e,n){t.addEventListener("dragover",s=>{const o=this.currentDragItem(s);!o||!this.canDropItem(o,e,n)||(s.preventDefault(),s.stopPropagation(),s.dataTransfer&&(s.dataTransfer.dropEffect="move"),t.classList.add("nav-drop-target"),this.scheduleDropExpand(t))}),t.addEventListener("dragleave",s=>{t.contains(s.relatedTarget)||(t.classList.remove("nav-drop-target"),this.clearDropExpand(t))}),t.addEventListener("drop",s=>{const o=this.currentDragItem(s);!o||!this.canDropItem(o,e,n)||(s.preventDefault(),s.stopPropagation(),this.clearDropExpand(t),this.clearDropTargets(),this.moveDraggedItem(o,n))})}scheduleDropExpand(t){this.dropExpandTarget===t||!t.dataset.collapsibleKey||(this.clearDropExpand(),this.dropExpandTarget=t,this.dropExpandTimer=window.setTimeout(()=>{const e=t.dataset.collapsibleKey;e&&this.applyCollapsed(e,!1),t.classList.add("nav-drop-auto-expanded"),this.dropExpandTimer=void 0},650))}clearDropExpand(t){var e;t&&this.dropExpandTarget!==t||(this.dropExpandTimer!==void 0&&window.clearTimeout(this.dropExpandTimer),this.dropExpandTimer=void 0,(e=this.dropExpandTarget)==null||e.classList.remove("nav-drop-auto-expanded"),this.dropExpandTarget=void 0)}currentDragItem(t){var n;if(this.draggedItem)return this.draggedItem;const e=(n=t.dataTransfer)==null?void 0:n.getData("application/x-docs-nav-item");if(e)try{const s=JSON.parse(e);if(s&&(s.kind==="doc"||s.kind==="capability"||s.kind==="board"||s.kind==="dir")&&typeof s.rootId=="string")return s}catch{}}canDropItem(t,e,n){if(t.rootId!==e)return!1;const s=xt(n);if(t.kind==="doc")return Ct(t.path)!==s;if(t.kind==="capability")return s==="capabilities"||s.startsWith("capabilities/")&&Ct(t.path)!==s;if(t.kind==="board")return Ct(t.boardId)!==s;const o=xt(t.path);return!o||o===s||Ct(o)===s?!1:!s.startsWith(`${o}/`)}moveDraggedItem(t,e){const n=xt(e);t.kind==="doc"||t.kind==="capability"?this.callbacks.onMoveDoc(t.rootId,t.path,n):t.kind==="board"?this.callbacks.onMoveBoard(t.rootId,t.boardId,n):this.callbacks.onMoveDirectory(t.rootId,t.path,n)}clearDropTargets(){this.clearDropExpand(),this.element.querySelectorAll(".nav-drop-target").forEach(t=>{t.classList.remove("nav-drop-target")})}startInlineRename(t,e,n){const s=document.createElement("input");s.className="nav-inline-input",s.value=e.replace(/\.md$/,"");const o=t.parentElement;if(!o)return;t.style.display="none",o.insertBefore(s,t.nextSibling),s.focus(),s.select();let a=!1;const r=c=>{if(!a&&(a=!0,s.remove(),t.style.display="",c)){const l=s.value.trim();l&&l!==e.replace(/\.md$/,"")&&n(l)}};s.addEventListener("keydown",c=>{c.stopPropagation(),c.key==="Enter"&&(c.preventDefault(),r(!0)),c.key==="Escape"&&(c.preventDefault(),r(!1))}),s.addEventListener("blur",()=>r(!0))}startInlineCreate(t,e,n){const s=document.createElement("input");s.className="nav-inline-input",s.placeholder=e,t.insertBefore(s,t.firstChild),s.focus();let o=!1;const a=r=>{if(!o&&(o=!0,s.remove(),r)){const c=s.value.trim();c&&n(c)}};s.addEventListener("keydown",r=>{r.stopPropagation(),r.key==="Enter"&&(r.preventDefault(),a(!0)),r.key==="Escape"&&(r.preventDefault(),a(!1))}),s.addEventListener("blur",()=>a(!0))}registerCollapsible(t,e,n,s,o){this.collapsibles.set(t,{body:e,group:n,btn:s}),n.dataset.collapsibleKey=t,this.applyCollapsed(t,this.collapsedState.get(t)??o)}applyCollapsed(t,e){const n=this.collapsibles.get(t);n&&(this.collapsedState.set(t,e),n.body.hidden=e,n.group.classList.toggle("collapsed",e),n.btn.setAttribute("aria-expanded",String(!e)))}toggleCollapsible(t){const e=this.collapsibles.get(t);e&&this.applyCollapsed(t,!e.body.hidden)}openDirContextMenu(t,e,n,s){const o=s?Nt(s.split("/").pop()??s):n.displayPath.split("/").pop()??n.displayPath,a=[{icon:At,iconClass:"nav-ctx-dir",label:"New Directory",action:()=>this.callbacks.onCreateDirectory(n.id,s)},{icon:Dt,iconClass:"nav-ctx-board",label:"New Board",action:()=>this.callbacks.onCreateBoard(n.id,s)},{icon:Et,iconClass:"nav-ctx-doc",label:"New Doc",action:()=>this.callbacks.onCreateDoc(n.id,s)},{icon:Re,iconClass:"nav-ctx-cap",label:"New Capability",action:()=>this.callbacks.onCreateCapability(n.id,s)}];s&&a.push({icon:le,iconClass:"nav-ctx-rename",label:"Rename",action:()=>this.callbacks.onRenameDirectory(n.id,s)},{icon:Vt,iconClass:"nav-ctx-delete",label:"Delete",action:()=>this.callbacks.onDeleteDirectory(n.id,s)}),this.showContextMenu(t,e,o,a)}openEmptyProjectContextMenu(t,e,n,s){this.showContextMenu(t,e,s,[{icon:At,iconClass:"nav-ctx-dir",label:"New Directory",action:()=>this.callbacks.onCreateProjectDirectory(n)},{icon:Dt,iconClass:"nav-ctx-board",label:"New Board",action:()=>this.callbacks.onCreateProjectBoard(n)},{icon:Et,iconClass:"nav-ctx-doc",label:"New Doc",action:()=>this.callbacks.onCreateProjectDoc(n)}])}openDocContextMenu(t,e,n,s){const o=s.split("/").pop()??s;this.showContextMenu(t,e,o,[{icon:Et,iconClass:"nav-ctx-doc",label:"Open",action:()=>this.callbacks.onOpenDoc(n.id,s)},{icon:$e,iconClass:"nav-ctx-edit",label:"Edit",action:()=>this.callbacks.onEditDoc(n.id,s)},{kind:"divider"},{icon:le,iconClass:"nav-ctx-rename",label:"Rename",action:()=>this.callbacks.onRenameDoc(n.id,s)},{icon:Vt,iconClass:"nav-ctx-delete",label:"Delete",action:()=>this.callbacks.onDeleteDoc(n.id,s),danger:!0}])}openTextFileContextMenu(t,e,n,s){const o=s.split("/").pop()??s;this.showContextMenu(t,e,o,[{icon:Le,iconClass:"nav-ctx-doc",label:"Open",action:()=>this.callbacks.onOpenTextFile(n.id,s)},{icon:$e,iconClass:"nav-ctx-edit",label:"Edit",action:()=>this.callbacks.onOpenTextFile(n.id,s)},{kind:"divider"},{icon:le,iconClass:"nav-ctx-rename",label:"Rename",action:()=>this.callbacks.onRenameDoc(n.id,s)},{icon:Vt,iconClass:"nav-ctx-delete",label:"Delete",action:()=>this.callbacks.onDeleteDoc(n.id,s),danger:!0}])}openBoardContextMenu(t,e,n,s){const o=Nt(s.split("/").pop()??s);this.showContextMenu(t,e,o,[{icon:Dt,iconClass:"nav-ctx-board",label:"Open",action:()=>this.callbacks.onOpenBoard(n.id,s)},{kind:"divider"},{icon:le,iconClass:"nav-ctx-rename",label:"Rename",action:()=>this.callbacks.onRenameBoard(n.id,s)},{icon:Vt,iconClass:"nav-ctx-delete",label:"Delete",action:()=>this.callbacks.onDeleteBoard(n.id,s),danger:!0}])}showContextMenu(t,e,n,s){this.closeContextMenu();const o=document.createElement("div");o.className="nav-context-menu",o.addEventListener("pointerdown",c=>c.stopPropagation());const a=document.createElement("div");a.className="nav-context-label",a.textContent=n,o.appendChild(a);const r=document.createElement("div");r.className="nav-context-divider",o.appendChild(r);for(const c of s){if(c.kind==="divider"){const p=document.createElement("div");p.className="nav-context-divider",o.appendChild(p);continue}const l=document.createElement("button");l.className="nav-context-item"+(c.danger?" nav-context-item-danger":""),l.type="button";const d=document.createElement("span");d.className=`nav-ctx-icon ${c.iconClass}`,d.innerHTML=c.icon;const h=document.createElement("span");h.textContent=c.label,l.append(d,h),l.addEventListener("click",()=>{this.closeContextMenu(),c.action()}),o.appendChild(l)}o.style.position="fixed",o.style.left=`${t}px`,o.style.top=`${e}px`,document.body.appendChild(o),this.contextMenu=o,requestAnimationFrame(()=>{if(!this.contextMenu)return;const c=o.getBoundingClientRect();c.right>window.innerWidth&&(o.style.left=`${Math.max(0,t-c.width)}px`),c.bottom>window.innerHeight&&(o.style.top=`${Math.max(0,e-c.height)}px`)})}closeContextMenu(){var t;(t=this.contextMenu)==null||t.remove(),this.contextMenu=void 0}groupLabel(t){const e=document.createElement("div");return e.className="nav-label",e.textContent=t,e}link(t,e,n){const s=document.createElement("a");return s.className="nav-link",s.href=t,s.textContent=e,this.links.set(n,s),this.links.set(t,s),s}}const Dt='<svg class="nav-node-icon nav-node-icon-board" viewBox="0 0 16 16" aria-hidden="true"><rect x="1" y="1" width="6" height="6" rx="1"/><rect x="9" y="1" width="6" height="6" rx="1"/><rect x="1" y="9" width="6" height="6" rx="1"/><rect x="9" y="9" width="6" height="6" rx="1"/></svg>',Re='<svg class="nav-node-icon nav-node-icon-capability" viewBox="0 0 16 16" aria-hidden="true"><path d="M8 1l2 4.5 4.5 0.7-3.3 3.1 0.8 4.7-4-2.1-4 2.1 0.8-4.7L1.5 6.2l4.5-0.7z"/></svg>',Et='<svg class="nav-node-icon nav-node-icon-doc" viewBox="0 0 16 16" aria-hidden="true"><path d="M3 1.5h6.5l3 3V14.5H3z"/><path d="M9.5 1.5V4.5h3" stroke="none"/><line x1="5" y1="7" x2="11" y2="7" stroke="var(--bg-canvas)" stroke-width="0.8"/><line x1="5" y1="9.5" x2="11" y2="9.5" stroke="var(--bg-canvas)" stroke-width="0.8"/><line x1="5" y1="12" x2="9" y2="12" stroke="var(--bg-canvas)" stroke-width="0.8"/></svg>',Le='<svg class="nav-node-icon nav-node-icon-doc" viewBox="0 0 16 16" aria-hidden="true"><path d="M3 1.5h6.5l3 3V14.5H3z"/><path d="M9.5 1.5V4.5h3" stroke="none"/><line x1="5" y1="7" x2="11" y2="7" stroke="var(--bg-canvas)" stroke-width="0.8"/><line x1="5" y1="9.5" x2="11" y2="9.5" stroke="var(--bg-canvas)" stroke-width="0.8"/></svg>',At='<svg class="nav-ctx-svg" viewBox="0 0 16 16" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.45" stroke-linecap="round" stroke-linejoin="round"><path d="M2 4.5h4l1.4 1.6H14v6.4a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1z"/><path d="M2 4.5v-1a1 1 0 0 1 1-1h3l1.4 2"/></svg>',$e='<svg class="nav-ctx-svg" viewBox="0 0 16 16" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M11 2l3 3-8 8H3v-3z"/></svg>',le='<svg class="nav-ctx-svg" viewBox="0 0 16 16" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12h12M8 3v9M5 6l3-3 3 3"/></svg>',Vt='<svg class="nav-ctx-svg" viewBox="0 0 16 16" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M2 4h12M5 4V2h6v2M6 7v5M10 7v5M3 4l1 9a1 1 0 001 1h6a1 1 0 001-1l1-9"/></svg>';function Kn(i){return i.filter(t=>t.type==="file"?t.name.endsWith(".md")||ie(t.path):!Ps.has(t.name)).map(t=>t.type==="file"?t:{...t,children:Kn(t.children)})}function As(i){const t=i.find(e=>e.type==="dir"&&e.name.toLowerCase()==="docs");return!t||t.type!=="dir"?i:i.filter(e=>e!==t).concat(t.children)}function Ns(i,t){const e=t?`${t}/`:"";return zs(i).filter(n=>e?n.path.startsWith(e)?!n.path.slice(e.length).includes("/"):!1:!n.path.includes("/"))}function zs(i){const t=[],e=n=>{for(const s of n)s.type==="file"?s.name.endsWith(".board.json")&&t.push(s):e(s.children)};return e(i),t}function sn(i,t){return`#/root/${encodeURIComponent(i)}/docs/${encodeURIComponent(t)}`}function Hs(i){return`#/root/${encodeURIComponent(i)}/boards`}function on(i,t){return`#/root/${encodeURIComponent(i)}/boards/${encodeURIComponent(t)}`}function Fs(i){return`#/root/${encodeURIComponent(i)}/capabilities`}function js(i,t){return`#/root/${encodeURIComponent(i)}/capabilities/${encodeURIComponent(Ws(t))}`}function Ws(i){return i.replace(/^capabilities\//,"")}function Os(i,t,e){const n=new Map,s=[],o=c=>{const l=xt(c),d=n.get(l);if(d)return d;const h=Ct(l),p={id:`folder:${i.id}:${l}`,kind:"folder",name:l.split("/").pop()??i.label,projectPath:l,children:[]};return n.set(l,p),l&&h?o(h).children.push(p):s.push(p),p},a=c=>{for(const l of c)if(l.type==="dir")o(l.path),a(l.children);else{const d={id:`file:${i.id}:${l.path}`,kind:"file",name:l.name,projectPath:l.path,children:[]};n.set(l.path,d);const h=Ct(l.path);h?o(h).children.push(d):s.push(d)}};a(t);for(const c of i.docsRoots){const l=xt(c.displayPath),d=o(l);d.kind="docsRoot",d.id=`root:${c.id}`,d.root=c,d.contentPath="";const h=p=>{for(const m of p){const g=[l,m.path].filter(Boolean).join("/");if(m.type==="dir"){const E=o(g);E.kind!=="docsRoot"&&(E.root=c,E.contentPath=m.path),h(m.children);continue}const y=m.name.endsWith(".board.json"),b=Vn(m.path),x=ie(m.path);if(!m.name.endsWith(".md")&&!y&&!x)continue;const v=Ct(g),S=n.get(g),w=S??{id:"",kind:"file",name:m.name,projectPath:g,children:[]};w.id=`${y?"board":b?"capability":x?"file":"doc"}:${c.id}:${m.path}`,w.kind=y?"board":b?"capability":x?"file":"doc",w.root=c,w.contentPath=m.path,S||(n.set(g,w),v?o(v).children.push(w):s.push(w))}};h(e.get(c.id)??[])}const r=(c,l)=>{const d=c.kind==="docsRoot"?c.root:c.root??l;if(d&&(c.kind==="folder"||c.kind==="file")&&!c.contentPath){c.root=d;const h=xt(d.displayPath);c.contentPath=c.projectPath.slice(h.length).replace(/^\//,"")}for(const h of c.children)r(h,d)};for(const c of s)r(c);return s}function Us(i){return[...i].sort((t,e)=>{const n=s=>s.kind==="doc"&&s.name==="index.md"?0:(s.kind==="doc"||s.kind==="file")&&_e(s.name)?1:s.kind==="doc"?2:s.kind==="capability"?3:s.kind==="board"?4:s.kind==="folder"&&s.root&&!s.docsContainerRoot?5:s.kind==="folder"&&s.docsContainerRoot?6:s.kind==="folder"&&!s.root?7:s.kind==="docsRoot"?8:9;return n(t)-n(e)||t.name.localeCompare(e.name,void 0,{sensitivity:"accent"})})}function _e(i){return/^readme\.(md|txt)$/i.test(i)}function ve(i,t,e=!1){const n=[];for(const s of i){if(s.kind==="docsRoot"){n.push({...s,children:ve(s.children,!1,!0)});continue}if(e){if(s.kind==="file"){ie(s.contentPath??"")&&n.push(s);continue}if(s.kind==="folder"){const o=ve(s.children,!1,!0);(s.name!=="assets"||o.length>0)&&n.push({...s,children:o})}else n.push(s);continue}if(s.kind==="folder"){const o=ve(s.children);o.length>0&&n.push({...s,children:o})}}return n}function Xe(i){var e;const t=[];for(const n of i){if(n.kind==="docsRoot"){t.push(...Xe(n.children));continue}if(n.kind!=="folder"){t.push(n);continue}const s=(e=n.children.find(o=>o.kind==="docsRoot"))==null?void 0:e.root;t.push({...n,docsContainerRoot:s,children:Xe(n.children)})}return t}function Nt(i){const t=i.replace(/\.md$/,"").replace(/[-_]/g," ");return t.charAt(0).toUpperCase()+t.slice(1)}function Vn(i){if(!i.startsWith("capabilities/")||!i.endsWith(".md"))return!1;const t=i.slice(13);return t.includes("/")?t.endsWith("/index.md"):!0}function ie(i){return!i.startsWith("capabilities/")||i.endsWith(".board.json")?!1:!Vn(i)}function _s(i){var t;return i.kind!=="folder"||!((t=i.contentPath)!=null&&t.startsWith("capabilities/"))?null:i.children.find(e=>e.kind==="capability"&&Ct(e.contentPath??"")===i.contentPath)??null}function Gn(i){if(i.type==="file")return!1;for(const t of i.children)if(t.type==="dir"&&t.name==="docs"||t.type==="dir"&&Gn(t))return!0;return!1}function xt(i){return i.replace(/^\/+|\/+$/g,"")}function Ct(i){const t=xt(i),e=t.lastIndexOf("/");return e===-1?"":t.slice(0,e)}function Xs(i){return i.kind==="doc"||i.kind==="capability"?i.path:i.kind==="board"?i.boardId:i.path}function Zn(i){if(i.kind==="root")return i.root;for(const t of i.children){const e=Zn(t);if(e)return e}return null}function ne(i,t={}){const{frontmatter:e,body:n}=qs(i);try{const s=Jn(n.split(`
|
|
2
|
-
`),t);return{frontmatter:e,html:s}}catch(s){return console.error("Markdown render failed",s),{frontmatter:e,html:`<div class="md-error"><strong>Could not render this document.</strong><pre>${Ut(String(s))}</pre></div>`}}}function qs(i){const t={};if(!i.startsWith("---"))return{frontmatter:t,body:i};const e=i.indexOf(`
|
|
3
|
-
---`,3);if(e===-1)return{frontmatter:t,body:i};const n=i.slice(3,e).trim();for(const o of n.split(`
|
|
4
|
-
`)){const a=o.indexOf(":");a!==-1&&(t[o.slice(0,a).trim()]=o.slice(a+1).trim())}const s=i.slice(e+4).replace(/^\n+/,"");return{frontmatter:t,body:s}}function Jn(i,t){const e=[];let n=0,s=0;const o=i.length*4+1e3;for(;n<i.length;){if(++s>o)throw new Error(`renderBlocks exceeded ${o} iterations at line ${n} — aborting to avoid an infinite loop`);const a=i[n];if(a.trim()===""){n++;continue}const r=a.match(/^(`{3,})\s*(\w+)?\s*$/);if(r){const d=r[1].length,h=(r[2]??"").toLowerCase(),p=new RegExp("^`{"+d+",}\\s*$"),m=[];for(n++;n<i.length&&!p.test(i[n]);)m.push(i[n]),n++;n++,h==="board"?e.push(Vs(ts(m.join(`
|
|
5
|
-
`)))):e.push(`<pre class="code-block"><code${h?` class="language-${h}"`:""}>${Ut(m.join(`
|
|
6
|
-
`))}</code></pre>`);continue}const c=a.match(/^(#{1,6})\s+(.*)$/);if(c){const d=c[1].length,h=Lt(c[2].trim(),t),p=Js(c[2]);e.push(`<h${d} id="${p}">${h}</h${d}>`),n++;continue}if(/^(-{3,}|\*{3,}|_{3,})\s*$/.test(a)){e.push("<hr />"),n++;continue}if(/^>\s?/.test(a)){const d=[];for(;n<i.length&&/^>\s?/.test(i[n]);)d.push(i[n].replace(/^>\s?/,"")),n++;e.push(`<blockquote>${Jn(d,t)}</blockquote>`);continue}if(a.includes("|")&&n+1<i.length&&Ys(i[n+1])){const d=[];for(;n<i.length&&i[n].includes("|")&&i[n].trim()!=="";)d.push(i[n]),n++;e.push(Ks(d,t));continue}if(Wt(a)){const d=[];for(;n<i.length&&(Wt(i[n])||/^\s+\S/.test(i[n])||i[n].trim()==="");){if(i[n].trim()===""){const h=i[n+1];if(h===void 0||!Wt(h)&&!/^\s+\S/.test(h))break}d.push(i[n]),n++}e.push(Qn(d,t));continue}const l=[i[n]];for(n++;n<i.length&&i[n].trim()!==""&&!/^`{3,}/.test(i[n])&&!/^#{1,6}\s/.test(i[n])&&!/^>\s?/.test(i[n])&&!Wt(i[n])&&!/^(-{3,}|\*{3,}|_{3,})\s*$/.test(i[n]);)l.push(i[n]),n++;e.push(`<p>${Lt(l.join(" ").trim(),t)}</p>`)}return e.join(`
|
|
7
|
-
`)}function Wt(i){return/^\s*([-*+]|\d+\.)\s+/.test(i)}function Qn(i,t){const e=i.filter(l=>Wt(l)).map(l=>l.match(/^\s*/)[0].length);if(e.length===0)return Lt(i.join(" ").trim(),t);const n=Math.min(...e),s=[];let o=!1,a=null;for(const l of i){const d=l.match(/^\s*/)[0].length,h=l.match(/^\s*([-*+]|\d+\.)\s+(.*)$/);if(h&&d===n){o=/\d+\./.test(h[1]);let p=h[2],m=null;const g=p.match(/^\[([ xX])\]\s+(.*)$/);g&&(m=g[1].toLowerCase()==="x",p=g[2]),a={ordered:o,content:[p],checked:m},s.push(a)}else a&&a.content.push(l.slice(n))}const r=o?"ol":"ul",c=s.map(l=>{const d=l.content[0],h=l.content.slice(1);let p=Lt(d,t);const m=h.filter(g=>g.trim()!=="");return m.some(g=>Wt(g))?p+=Qn(h,t):m.length&&(p+=" "+Lt(m.join(" ").trim(),t)),l.checked!==null?`<li class="task">${`<input type="checkbox" disabled${l.checked?" checked":""} /> `}${p}</li>`:`<li>${p}</li>`}).join("");return`<${r}>${c}</${r}>`}function Ys(i){return/^\s*\|?\s*:?-{1,}:?\s*(\|\s*:?-{1,}:?\s*)*\|?\s*$/.test(i)}function Pe(i){return i.trim().replace(/^\|/,"").replace(/\|$/,"").split("|").map(t=>t.trim())}function Ks(i,t){const e=Pe(i[0]),n=Pe(i[1]).map(r=>{const c=r.startsWith(":"),l=r.endsWith(":");return c&&l?"center":l?"right":c?"left":""}),s=i.slice(2).map(Pe),o=e.map((r,c)=>`<th${n[c]?` style="text-align:${n[c]}"`:""}>${Lt(r,t)}</th>`).join(""),a=s.map(r=>`<tr>${r.map((c,l)=>`<td${n[l]?` style="text-align:${n[l]}"`:""}>${Lt(c,t)}</td>`).join("")}</tr>`).join("");return`<div class="markdown-table-scroll"><table><thead><tr>${o}</tr></thead><tbody>${a}</tbody></table></div>`}function ts(i){const t={};for(const n of i.split(`
|
|
8
|
-
`)){const s=n.replace(/#.*$/,"").trim();if(!s)continue;const o=s.indexOf(":");o!==-1&&(t[s.slice(0,o).trim()]=s.slice(o+1).trim())}let e=null;if(t.region){const n=t.region.split(",").map(s=>parseFloat(s.trim()));n.length===4&&n.every(s=>Number.isFinite(s))&&(e=[n[0],n[1],n[2],n[3]])}return{src:t.src??"",region:e,height:t.height?Math.max(120,parseInt(t.height,10)||360):360,title:t.title??null}}function Vs(i){const t=i.region?i.region.join(","):"";return`<figure class="board-embed" data-src="${Mt(i.src)}" data-region="${t}" data-height="${i.height}" data-title="${Mt(i.title??"")}"></figure>`}function Lt(i,t){let e=i;const n=[];e=e.replace(/\\([\\`*_{}\[\]()#+\-.!|])/g,(a,r)=>(n.push(r),`${n.length-1}`));const s=[];e=e.replace(/`([^`]+)`/g,(a,r)=>(s.push(`<code>${Ut(de(r,n))}</code>`),`\0${s.length-1}\0`));const o=[];return e=e.replace(/!\[([^\]]*)\]\(([^)\s]+)\)/g,(a,r,c)=>{const l=de(c,n),d=Ve(l,t),h=Gs(l,t),p=de(r,n),m=d===l?"":` data-md-src="${Mt(l)}"`;return o.push(`<img src="${Mt(h)}" alt="${Mt(p)}" loading="lazy"${m} />`),`${o.length-1}`}),e=e.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g,(a,r,c)=>{const l=de(c,n),d=Zs(l,t),p=/^(https?:|mailto:|tel:)/.test(l)?' target="_blank" rel="noopener noreferrer"':"",m=d===l?"":` data-md-href="${Mt(l)}"`;return o.push(`<a href="${Mt(d)}"${p}${m}>${an(r,n,s)}</a>`),`${o.length-1}`}),e=an(e,n,s),e=e.replace(/\u0002(\d+)\u0002/g,(a,r)=>o[Number(r)]??""),e}function an(i,t,e){let n=Ut(i);return n=n.replace(/\*\*([\s\S]+?)\*\*/g,"<strong>$1</strong>"),n=n.replace(/__([\s\S]+?)__/g,"<strong>$1</strong>"),n=n.replace(/(^|[^*])\*([^*\s][^*]*)\*/g,"$1<em>$2</em>"),n=n.replace(/(^|[^_])_([^_\s][^_]*)_/g,"$1<em>$2</em>"),n=n.replace(/\u0000(\d+)\u0000/g,(s,o)=>e[Number(o)]??""),n=n.replace(/\u0001(\d+)\u0001/g,(s,o)=>Ut(t[Number(o)]??"")),n}function de(i,t){return i.replace(/\u0001(\d+)\u0001/g,(e,n)=>t[Number(n)]??"")}function Gs(i,t){if(/^(https?:|data:)/.test(i)||!t.rootId||!t.docPath)return i;const e=Ve(i,t);return e?k.assetUrl(t.rootId,e):i}function Zs(i,t){if(/^(https?:|mailto:|tel:|#|data:)/.test(i)||!t.rootId||!t.docPath)return i;const e=Ve(i,t);return e?e.endsWith(".md")?`#/root/${encodeURIComponent(t.rootId)}/docs/${encodeURIComponent(e)}`:k.assetUrl(t.rootId,e):i}function Ve(i,t){return t.docPath?Ss(t.docPath,i):i}function Ut(i){return i.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}function Mt(i){return Ut(i).replace(/"/g,""")}function Js(i){return i.toLowerCase().replace(/[^\w\s-]/g,"").trim().replace(/\s+/g,"-")}class es{constructor(t,e,n){u(this,"cache",new Map);u(this,"loading",new Set);u(this,"missing",new Set);u(this,"assetStore");u(this,"onLoaded");u(this,"resolveSrcUrl");this.assetStore=t,this.onLoaded=e,this.resolveSrcUrl=n}get(t){const e=this.cache.get(t);return e||(!this.loading.has(t)&&!this.missing.has(t)&&this.load(t),null)}isMissing(t){return this.missing.has(t)}srcToUrl(t){return this.resolveSrcUrl?this.resolveSrcUrl(t):/^(https?:)?\/\//.test(t)||t.startsWith("/")||t.startsWith("data:")?t:`/__api/asset?path=${encodeURIComponent(t)}`}keyForSrc(t){return this.srcToUrl(t)}getBySrc(t){const e=this.srcToUrl(t),n=this.cache.get(e);return n||(!this.loading.has(e)&&!this.missing.has(e)&&this.loadUrl(e),null)}isMissingSrc(t){return this.missing.has(this.srcToUrl(t))}async loadUrl(t){this.loading.add(t);try{const e=await fetch(t);if(!e.ok){this.missing.add(t);return}const n=await e.blob(),s=await createImageBitmap(n);this.cache.set(t,s),this.onLoaded()}catch{this.missing.add(t)}finally{this.loading.delete(t)}}set(t,e){this.cache.set(t,e),this.missing.delete(t)}setBySrc(t,e){this.set(this.srcToUrl(t),e)}async load(t){this.loading.add(t);try{const e=await this.assetStore.get(t);if(!e){this.missing.add(t);return}const n=await createImageBitmap(e.blob);this.cache.set(t,n),this.onLoaded()}catch{this.missing.add(t)}finally{this.loading.delete(t)}}evict(t){const e=this.cache.get(t);e&&e.close(),this.cache.delete(t),this.missing.delete(t)}clear(){for(const t of this.cache.values())t.close();this.cache.clear(),this.missing.clear()}get size(){return this.cache.size}}const Qs="viteboard",ti=1;class ei{constructor(){u(this,"dbPromise",null)}open(){return this.dbPromise?this.dbPromise:(this.dbPromise=new Promise((t,e)=>{const n=indexedDB.open(Qs,ti);n.onupgradeneeded=()=>{const s=n.result;s.objectStoreNames.contains("boards")||s.createObjectStore("boards",{keyPath:"id"}),s.objectStoreNames.contains("assets")||s.createObjectStore("assets",{keyPath:"id"})},n.onsuccess=()=>t(n.result),n.onerror=()=>e(n.error??new Error("IndexedDB open failed")),n.onblocked=()=>e(new Error("IndexedDB blocked"))}),this.dbPromise)}async put(t,e){const n=await this.open();return new Promise((s,o)=>{const a=n.transaction(t,"readwrite");a.objectStore(t).put(e),a.oncomplete=()=>s(),a.onerror=()=>o(a.error??new Error("IndexedDB write failed"))})}async get(t,e){const n=await this.open();return new Promise((s,o)=>{const r=n.transaction(t,"readonly").objectStore(t).get(e);r.onsuccess=()=>s(r.result),r.onerror=()=>o(r.error??new Error("IndexedDB read failed"))})}async delete(t,e){const n=await this.open();return new Promise((s,o)=>{const a=n.transaction(t,"readwrite");a.objectStore(t).delete(e),a.oncomplete=()=>s(),a.onerror=()=>o(a.error??new Error("IndexedDB delete failed"))})}async getAllKeys(t){const e=await this.open();return new Promise((n,s)=>{const a=e.transaction(t,"readonly").objectStore(t).getAllKeys();a.onsuccess=()=>n(a.result),a.onerror=()=>s(a.error??new Error("IndexedDB keys failed"))})}async getAll(t){const e=await this.open();return new Promise((n,s)=>{const a=e.transaction(t,"readonly").objectStore(t).getAll();a.onsuccess=()=>n(a.result),a.onerror=()=>s(a.error??new Error("IndexedDB read failed"))})}async clearStore(t){const e=await this.open();return new Promise((n,s)=>{const o=e.transaction(t,"readwrite");o.objectStore(t).clear(),o.oncomplete=()=>n(),o.onerror=()=>s(o.error??new Error("IndexedDB clear failed"))})}}const K=new ei;class ni{async get(t){try{return await K.get("assets",t)}catch{return}}async createFromBlob(t,e){const n=await si(t),s=await this.get(n);if(s)return s;const o=await createImageBitmap(t),a=Date.now(),r={id:n,type:"image",mimeType:t.type||"image/png",blob:t,width:o.width,height:o.height,sizeBytes:t.size,createdAt:a,updatedAt:a,name:e};return o.close(),await K.put("assets",r),r}async put(t){await K.put("assets",t)}async delete(t){await K.delete("assets",t)}async getAllKeys(){try{return await K.getAllKeys("assets")}catch{return[]}}async getAll(){try{return await K.getAll("assets")}catch{return[]}}async totalSizeBytes(){return(await this.getAll()).reduce((e,n)=>e+n.sizeBytes,0)}async clearUnused(t){const e=await this.getAllKeys();let n=0;for(const s of e)t.has(s)||(await K.delete("assets",s),n++);return n}async clearAll(){await K.clearStore("assets")}}const it=new ni;async function si(i){const t=await crypto.subtle.digest("SHA-256",await i.arrayBuffer());return Array.from(new Uint8Array(t),e=>e.toString(16).padStart(2,"0")).join("")}const Ot=new Map,ii=2e3;function X(i){for(const t of Ot.keys())t.startsWith(i+"|")&&Ot.delete(t)}function Te(i){if(i.data.shapeKind==="chevron"){if(typeof i.style.fontSize=="number"&&Number.isFinite(i.style.fontSize))return i.style.fontSize;const t=Math.abs(i.height)*.34,e=Math.abs(i.width)*.18;return Math.round(Math.max(13,Math.min(48,t,e)))}return i.style.fontSize??14}function oi(i,t,e,n){const s=Ot.get(t);if(s)return s;const o=[];for(const a of e.split(`
|
|
9
|
-
`)){if(a===""){o.push("");continue}const r=a.split(" ");let c="";for(const l of r){const d=c===""?l:c+" "+l;i.measureText(d).width>n&&c!==""?(o.push(c),c=l):c=d}o.push(c)}return Ot.size>ii&&Ot.clear(),Ot.set(t,o),o}function _t(i,t,e,n,s,o){const a=Math.min(o,n/2,s/2);i.beginPath(),i.moveTo(t+a,e),i.arcTo(t+n,e,t+n,e+s,a),i.arcTo(t+n,e+s,t,e+s,a),i.arcTo(t,e+s,t,e,a),i.arcTo(t,e,t+n,e,a),i.closePath()}function xe(i,t){const{ctx:e,lod:n}=t;if(e.save(),e.globalAlpha=i.style.opacity??1,n===0){ai(i,t),e.restore();return}switch(i.type){case"sticky":ri(i,t);break;case"task":ci(i,t);break;case"rectangle":case"frame":rn(i,t);break;case"ellipse":fi(i,t);break;case"text":gi(i,t);break;case"line":case"arrow":case"connector":vi(i,t);break;case"image":case"screenshot":wi(i,t);break;default:rn(i,t)}e.restore()}function ai(i,t){const{ctx:e}=t;if(i.type==="line"||i.type==="arrow"||i.type==="connector"){e.strokeStyle=i.style.stroke??(t.theme==="dark"?"#888":"#999"),e.lineWidth=Math.max(1/t.scale,i.style.strokeWidth??2),e.beginPath(),e.moveTo(i.x,i.y),e.lineTo(i.x+i.width,i.y+i.height),e.stroke();return}e.fillStyle=i.style.fill??(t.theme==="dark"?"#3a3d42":"#d8d8d4"),e.fillRect(i.x,i.y,i.width,i.height)}function Be(i,t){i.style.shadow&&t.lod===2&&(t.ctx.shadowColor=t.theme==="dark"?"rgba(0,0,0,0.5)":"rgba(0,0,0,0.14)",t.ctx.shadowBlur=8*t.scale,t.ctx.shadowOffsetY=2*t.scale)}function Me(i){i.ctx.shadowColor="transparent",i.ctx.shadowBlur=0,i.ctx.shadowOffsetY=0}function ri(i,t){const{ctx:e,lod:n}=t,s=i.style.fill??"#fef08a";if(Be(i,t),_t(e,i.x,i.y,i.width,i.height,i.style.borderRadius??12),e.fillStyle=s,e.fill(),Me(t),n===2){const o=i.data.text??"";o&&Ge(i,t,o,14)}}function ci(i,t){const{ctx:e,lod:n}=t,s=i.data.checked===!0,o=t.theme==="dark",a=i.style.borderRadius??14;if(Be(i,t),_t(e,i.x,i.y,i.width,i.height,a),e.fillStyle=i.style.fill??(o?"#17191b":"#ffffff"),e.fill(),Me(t),e.strokeStyle=i.style.stroke??(o?"rgba(255,255,255,0.12)":"rgba(0,0,0,0.09)"),e.lineWidth=i.style.strokeWidth??1,e.stroke(),n<2)return;const r=Math.min(22,Math.max(16,i.height*.38)),c=i.x+16,l=i.y+i.height/2-r/2;_t(e,c,l,r,r,6),e.fillStyle=s?"#0078d4":o?"#111314":"#f3f2f1",e.fill(),e.strokeStyle=s?"#0078d4":o?"rgba(255,255,255,0.18)":"rgba(0,0,0,0.13)",e.lineWidth=1.4,e.stroke(),s&&(e.strokeStyle="#ffffff",e.lineWidth=2.1,e.lineCap="round",e.lineJoin="round",e.beginPath(),e.moveTo(c+r*.26,l+r*.52),e.lineTo(c+r*.43,l+r*.68),e.lineTo(c+r*.75,l+r*.32),e.stroke());const d=i.data.text??"";if(!d)return;const h=50,p=16,m=i.style.fontSize??15,g=i.style.fontFamily??'-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif';if(e.font=`${i.style.fontWeight??"650"} ${m}px ${g}`,e.fillStyle=s?o?"#a7aaa8":"#777777":i.style.textColor??(o?"#f6f6f3":"#252829"),e.textAlign="left",e.textBaseline="middle",e.save(),e.beginPath(),e.rect(i.x+h,i.y,Math.max(10,i.width-h-p),i.height),e.clip(),e.fillText(d,i.x+h,i.y+i.height/2),s){const y=e.measureText(d),b=i.y+i.height/2+1;e.strokeStyle=o?"rgba(167,170,168,0.58)":"rgba(119,119,119,0.48)",e.lineWidth=1.3,e.beginPath(),e.moveTo(i.x+h,b),e.lineTo(i.x+h+Math.min(y.width,i.width-h-p),b),e.stroke()}e.restore()}function rn(i,t){const{ctx:e,lod:n}=t,s=i.data.shapeKind??"rectangle",o=i.style.borderRadius??0;if(Be(i,t),s==="diamond"?li(e,i):s==="triangle"?di(e,i):s==="chevron"?hi(e,i):s==="parallelogram"?pi(e,i):s==="hexagon"?ui(e,i):s==="cylinder"?mi(e,i):s==="pill"?_t(e,i.x,i.y,i.width,i.height,Math.min(Math.abs(i.height)/2,Math.abs(i.width)/2)):o>0?_t(e,i.x,i.y,i.width,i.height,o):(e.beginPath(),e.rect(i.x,i.y,i.width,i.height)),i.style.fill&&i.style.fill!=="transparent"&&(e.fillStyle=i.style.fill,e.fill()),Me(t),i.style.stroke&&(i.style.strokeWidth??0)>0&&(e.strokeStyle=i.style.stroke,e.lineWidth=i.style.strokeWidth??1,e.stroke(),s==="cylinder")){const a=Math.abs(i.width)/2,r=Math.min(Math.abs(i.height)*.18,18);e.beginPath(),e.ellipse(i.x+i.width/2,i.y+r,a,r,0,0,Math.PI*2),e.stroke()}if(n===2){const a=i.data.text??"";if(a){const r=s==="chevron"?Math.min(Math.abs(i.width)*.18,Math.abs(i.height)*.55)+8:10;Ge(i,t,a,r)}}}function li(i,t){const e=t.x,n=t.y,s=t.width,o=t.height;i.beginPath(),i.moveTo(e+s/2,n),i.lineTo(e+s,n+o/2),i.lineTo(e+s/2,n+o),i.lineTo(e,n+o/2),i.closePath()}function di(i,t){const e=t.x,n=t.y,s=t.width,o=t.height;i.beginPath(),i.moveTo(e+s/2,n),i.lineTo(e+s,n+o),i.lineTo(e,n+o),i.closePath()}function hi(i,t){const e=t.x,n=t.y,s=t.width,o=t.height,a=Math.min(Math.abs(s)*.22,Math.abs(o)*.62);i.beginPath(),i.moveTo(e,n),i.lineTo(e+s-a,n),i.lineTo(e+s,n+o/2),i.lineTo(e+s-a,n+o),i.lineTo(e,n+o),i.lineTo(e+a,n+o/2),i.closePath()}function pi(i,t){const e=Math.min(Math.abs(t.width)*.2,Math.abs(t.height)*.55);i.beginPath(),i.moveTo(t.x+e,t.y),i.lineTo(t.x+t.width,t.y),i.lineTo(t.x+t.width-e,t.y+t.height),i.lineTo(t.x,t.y+t.height),i.closePath()}function ui(i,t){const e=Math.min(Math.abs(t.width)*.22,Math.abs(t.height)*.5);i.beginPath(),i.moveTo(t.x+e,t.y),i.lineTo(t.x+t.width-e,t.y),i.lineTo(t.x+t.width,t.y+t.height/2),i.lineTo(t.x+t.width-e,t.y+t.height),i.lineTo(t.x+e,t.y+t.height),i.lineTo(t.x,t.y+t.height/2),i.closePath()}function mi(i,t){const e=Math.abs(t.width)/2,n=Math.min(Math.abs(t.height)*.18,18),s=t.x+t.width/2;i.beginPath(),i.ellipse(s,t.y+n,e,n,0,Math.PI,Math.PI*2),i.lineTo(t.x+t.width,t.y+t.height-n),i.ellipse(s,t.y+t.height-n,e,n,0,0,Math.PI),i.lineTo(t.x,t.y+n),i.closePath()}function fi(i,t){const{ctx:e,lod:n}=t;if(Be(i,t),e.beginPath(),e.ellipse(i.x+i.width/2,i.y+i.height/2,Math.abs(i.width/2),Math.abs(i.height/2),0,0,Math.PI*2),i.style.fill&&i.style.fill!=="transparent"&&(e.fillStyle=i.style.fill,e.fill()),Me(t),i.style.stroke&&(i.style.strokeWidth??0)>0&&(e.strokeStyle=i.style.stroke,e.lineWidth=i.style.strokeWidth??1,e.stroke()),n===2){const s=i.data.text??"";s&&Ge(i,t,s,10)}}function gi(i,t){if(t.lod<2){t.ctx.fillStyle=t.theme==="dark"?"#55585e":"#c5c5c0",t.ctx.fillRect(i.x,i.y,i.width,Math.min(i.height,6/t.scale));return}const e=i.data.text??"";yi(i,t,e)}function yi(i,t,e){const{ctx:n}=t,s=Te(i),o=i.style.fontFamily??'-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',a=i.style.textColor??(t.theme==="dark"?"#f2f2f2":"#1f2933"),r=Math.max(10,i.width),c=ns(e),l=s*1.35;let d=i.y+l/2;n.save(),n.beginPath(),n.rect(i.x,i.y,i.width,i.height),n.clip(),n.textBaseline="middle",n.textAlign="left";for(const h of c){if(d>i.y+i.height)break;let p=h.text,m=i.x,g=s,y=a;if(h.kind==="heading"?g=h.level===1?s*1.35:h.level===2?s*1.18:s*1.08:h.kind==="quote"?(m+=14,y=t.theme==="dark"?"#cbd5e1":"#475569",n.fillStyle=t.theme==="dark"?"#334155":"#cbd5e1",n.fillRect(i.x+2,d-l/2+2,3,Math.max(l-4,10))):h.kind==="list"&&(m+=16,n.fillStyle=y,n.beginPath(),n.arc(i.x+6,d,2.2,0,Math.PI*2),n.fill()),h.kind==="code"){const v=p.split(/\r?\n/),S=g*1.45,w=v.length*S+12;n.fillStyle=t.theme==="dark"?"#111827":"#eef2f7",n.fillRect(i.x,d-l/2,Math.max(80,i.width),w);for(const E of v){if(d>i.y+i.height)break;const B=cn(n,[{text:E,code:!0}],r-12,g,o);for(const T of B){let R=i.x+8;for(const A of T)qe(n,A,g,o),n.fillStyle=y,n.fillText(A.text,R,d+6),R+=n.measureText(A.text).width;d+=S}}d+=8;continue}const b=bi(p),x=cn(n,b,r-(m-i.x),g,o);for(const v of x){if(d>i.y+i.height)break;let S=m;for(const w of v){if(qe(n,w,g,o),w.code){const E=n.measureText(w.text).width;n.fillStyle=t.theme==="dark"?"#1f2937":"#eef2f7",n.fillRect(S-2,d-g*.48,E+4,g*.96),n.fillStyle=y}else n.fillStyle=y;n.fillText(w.text,S,d),S+=n.measureText(w.text).width}d+=l}}n.restore()}function ns(i){const t=i.split(/\r?\n/),e=[];for(let n=0;n<t.length;n++){const s=t[n],o=s.trimStart();if(!o){e.push({kind:"paragraph",text:""});continue}if(/^```/.test(o)){const l=[];for(n++;n<t.length&&!/^```/.test(t[n].trimStart());)l.push(t[n]),n++;e.push({kind:"code",text:l.join(`
|
|
10
|
-
`)});continue}const a=o.match(/^(#{1,3})\s+(.*)$/);if(a){e.push({kind:"heading",level:a[1].length,text:a[2]});continue}const r=o.match(/^>\s?(.*)$/);if(r){e.push({kind:"quote",text:r[1]});continue}const c=o.match(/^([-*+]|\d+\.)\s+(.*)$/);if(c){e.push({kind:"list",text:c[2]});continue}e.push({kind:"paragraph",text:s})}return e}function It(i){if(i.type!=="text")return;const t=String(i.data.text??""),e=ns(t),n=Te(i);let s=0;for(const o of e){if(o.kind==="code"){s+=Math.max(1,o.text.split(/\r?\n/).length)+1;continue}const a=Math.max(1,Math.floor(Math.max(120,i.width)/Math.max(6,n*.6))),r=o.text.replace(/[*_`#>-]/g,"");s+=Math.max(1,Math.ceil(Math.max(1,r.length)/a))}i.height=Math.max(n*1.45,s*n*1.35+8)}function bi(i){const t=[];let e=0;for(;e<i.length;){if(i.startsWith("**",e)){const s=i.indexOf("**",e+2);if(s!==-1){t.push({text:i.slice(e+2,s),bold:!0}),e=s+2;continue}}if(i.startsWith("*",e)){const s=i.indexOf("*",e+1);if(s!==-1){t.push({text:i.slice(e+1,s),italic:!0}),e=s+1;continue}}if(i.startsWith("`",e)){const s=i.indexOf("`",e+1);if(s!==-1){t.push({text:i.slice(e+1,s),code:!0}),e=s+1;continue}}let n=i.length;for(const s of["**","*","`"]){const o=i.indexOf(s,e+1);o!==-1&&o<n&&(n=o)}t.push({text:i.slice(e,n)}),e=n}return t.filter(n=>n.text.length>0)}function cn(i,t,e,n,s){const o=[[]];let a=0;for(const r of t){const c=r.text.split(/(\s+)/);for(const l of c){if(!l)continue;const d={...r,text:l};qe(i,d,n,s);const h=i.measureText(l).width;a>0&&a+h>e&&l.trim()&&(o.push([]),a=0),o[o.length-1].push(d),a+=h}}return o}function qe(i,t,e,n){const s=t.bold?"700":"400",o=t.italic?"italic ":"",a=t.code?'"SF Mono", ui-monospace, Menlo, monospace':n;i.font=`${o}${s} ${e}px ${a}`}function Ge(i,t,e,n,s="center",o="middle"){const{ctx:a}=t,r=i.data.shapeKind==="chevron",c=Te(i),l=i.style.fontFamily??'-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',d=r?"750":i.style.fontWeight??"400";a.font=`${d} ${c}px ${l}`,a.fillStyle=i.style.textColor??(t.theme==="dark"&&i.type==="text"?"#f2f2f2":"#1f2933");const h=Math.max(10,i.width-n*2),p=`${i.id}|${i.updatedAt}|${c}|${h.toFixed(0)}`,m=oi(a,p,e,h),g=c*1.35,y=m.length*g;let b;o==="middle"?b=i.y+i.height/2-y/2+g/2:b=i.y+n+g/2,a.textBaseline="middle",a.textAlign=s;const x=s==="center"?i.x+i.width/2:i.x+n;a.save(),a.beginPath(),a.rect(i.x,i.y,i.width,i.height),a.clip();for(let v=0;v<m.length;v++)a.fillText(m[v],x,b+v*g);a.restore()}function vi(i,t){const{ctx:e,lod:n}=t,s=i.x,o=i.y,a=i.x+i.width,r=i.y+i.height,c=i.style.stroke??(t.theme==="dark"?"#d1d5db":"#374151"),l=i.style.strokeWidth??2;if(e.strokeStyle=c,e.fillStyle=c,e.lineWidth=l,e.setLineDash(i.style.strokeDash??[]),e.beginPath(),e.moveTo(s,o),e.lineTo(a,r),e.stroke(),e.setLineDash([]),n>=1){const d=Math.atan2(r-o,a-s),h=Math.max(11,l*5.5);(i.style.arrowEnd??i.type==="arrow")&&ln(e,a,r,d,h),i.style.arrowStart&&ln(e,s,o,d+Math.PI,h);const p=typeof i.data.text=="string"?i.data.text.trim():"";if(p)if(i.data.labelPosition==="sides"){const[m,g]=p.split("|").map(y=>y.trim());m&&De(e,m,s,o,a,r,t,.24),g&&De(e,g,s,o,a,r,t,.76)}else De(e,p,s,o,a,r,t)}}function De(i,t,e,n,s,o,a,r=.5){i.font='600 13px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif';const h=i.measureText(t).width+7*2,p=21,m=e+(s-e)*r-h/2,g=n+(o-n)*r-p/2;i.fillStyle=a.theme==="dark"?"#202328":"#ffffff",_t(i,m,g,h,p,4),i.fill(),i.strokeStyle=a.theme==="dark"?"#4a4d52":"#d8dade",i.lineWidth=1,i.stroke(),i.fillStyle=a.theme==="dark"?"#f2f2f2":"#1f2933",i.textAlign="center",i.textBaseline="middle",i.fillText(t,m+h/2,g+p/2)}function ln(i,t,e,n,s){i.beginPath(),i.moveTo(t,e),i.lineTo(t-s*Math.cos(n-Math.PI/6),e-s*Math.sin(n-Math.PI/6)),i.lineTo(t-s*Math.cos(n+Math.PI/6),e-s*Math.sin(n+Math.PI/6)),i.closePath(),i.fill()}function wi(i,t){const{ctx:e,imageCache:n}=t,s=i.data.src,o=i.data.assetId,a=s?n.getBySrc(s):o?n.get(o):null;if(a){if(t.lod===1&&t.scale<.15){e.fillStyle=t.theme==="dark"?"#3a3d42":"#d8d8d4",e.fillRect(i.x,i.y,i.width,i.height);return}e.drawImage(a,i.x,i.y,i.width,i.height)}else if(e.fillStyle=t.theme==="dark"?"#2a2d31":"#ececea",e.fillRect(i.x,i.y,i.width,i.height),e.strokeStyle=t.theme==="dark"?"#4a4d52":"#c9c9c5",e.lineWidth=1/t.scale,e.strokeRect(i.x,i.y,i.width,i.height),t.lod===2){const c=(s?n.isMissingSrc(s):o?n.isMissing(o):!1)?"Missing image":"Loading…";e.font="12px -apple-system, sans-serif",e.fillStyle=t.theme==="dark"?"#9ca3af":"#6b7280",e.textAlign="center",e.textBaseline="middle",e.fillText(c,i.x+i.width/2,i.y+i.height/2)}}class xi{constructor(t,e){u(this,"element");u(this,"canvas");u(this,"ctx");u(this,"imageCache");u(this,"doc",null);u(this,"region",[0,0,100,100]);u(this,"opts");u(this,"openLink");u(this,"ro");this.opts=e,this.element=t,t.classList.add("board-embed"),t.textContent="";const n=document.createElement("div");n.className="board-embed-frame",n.style.height=e.height+"px",this.canvas=document.createElement("canvas"),this.ctx=this.canvas.getContext("2d"),n.appendChild(this.canvas),t.appendChild(n);const s=document.createElement("a");s.className="board-embed-open";const o=e.region?`?region=${encodeURIComponent(e.region.join(","))}`:"";if(s.href=`#/root/${encodeURIComponent(e.rootId)}/boards/${encodeURIComponent(e.src)}${o}`,s.textContent="Open board →",this.openLink=s,n.appendChild(s),e.title){const a=document.createElement("figcaption");a.textContent=e.title,t.appendChild(a)}this.imageCache=new es(it,()=>this.render(),a=>k.assetUrl(this.opts.rootId,a)),this.ro=new ResizeObserver(()=>this.render()),this.ro.observe(n),this.load()}async load(){var t;try{const e=(t=this.opts.srcCandidates)!=null&&t.length?this.opts.srcCandidates:[this.opts.src];let n=null,s=this.opts.src;for(const a of e){const r=await k.getBoard(this.opts.rootId,a);if(n=r?we(r):null,n){s=a;break}}if(!n)return this.renderError(`Board "${this.opts.src}" not found`);this.opts.src=s;const o=this.opts.region?`?region=${encodeURIComponent(this.opts.region.join(","))}`:"";this.openLink.href=`#/root/${encodeURIComponent(this.opts.rootId)}/boards/${encodeURIComponent(s)}${o}`,this.doc=n,this.region=this.opts.region??Ci(n.elements),this.render()}catch{this.renderError("Failed to load board")}}renderError(t){var n;(n=this.element.querySelector(".board-embed-frame"))==null||n.classList.add("error");const e=this.element.querySelector(".board-embed-frame");if(e){e.innerHTML="";const s=document.createElement("span");s.className="board-embed-msg",s.textContent=t,e.appendChild(s)}}render(){if(!this.doc)return;const t=this.canvas.parentElement,e=Math.max(1,t.clientWidth),n=Math.max(1,t.clientHeight),s=Math.max(1,window.devicePixelRatio||1);this.canvas.width=Math.round(e*s),this.canvas.height=Math.round(n*s),this.canvas.style.width=e+"px",this.canvas.style.height=n+"px";const o=this.ctx;o.setTransform(s,0,0,s,0,0),o.clearRect(0,0,e,n),o.fillStyle=this.opts.theme==="dark"?"#111315":"#f3f2f1",o.fillRect(0,0,e,n);const[a,r,c,l]=this.region;if(c<=0||l<=0)return;const d=Math.min(e/c,n/l),h=(e-c*d)/2-a*d,p=(n-l*d)/2-r*d;o.save(),o.translate(h,p),o.scale(d,d);const m={ctx:o,scale:d,lod:2,theme:this.opts.theme,imageCache:this.imageCache},g=Math.max(c,l)*.1;for(const y of[...this.doc.elements].sort((b,x)=>b.zIndex-x.zIndex))y.hidden||y.x+y.width<a-g||y.x>a+c+g||y.y+y.height<r-g||y.y>r+l+g||xe(y,m);o.restore()}setTheme(t){this.opts.theme!==t&&(this.opts.theme=t,this.render())}destroy(){this.ro.disconnect(),this.imageCache.clear()}}function Ci(i){if(i.length===0)return[0,0,800,600];let t=1/0,e=1/0,n=-1/0,s=-1/0;for(const a of i)t=Math.min(t,a.x,a.x+a.width),e=Math.min(e,a.y,a.y+a.height),n=Math.max(n,a.x,a.x+a.width),s=Math.max(s,a.y,a.y+a.height);const o=40;return[t-o,e-o,n-t+o*2,s-e+o*2]}function ee(){return document.documentElement.getAttribute("data-theme")==="dark"?"dark":"light"}function dn(i,t,e){const{html:n,frontmatter:s}=ne(t,e),o=document.createElement("article");o.className="markdown-body",o.innerHTML=n,i.replaceChildren(o),o.querySelectorAll("pre.code-block").forEach(r=>{var h;const c=r.querySelector("code"),l=(h=c==null?void 0:c.textContent)==null?void 0:h.trimEnd();if(!l)return;const d=document.createElement("button");d.className="code-copy-btn",d.type="button",d.textContent="Copy",d.addEventListener("click",async()=>{try{await navigator.clipboard.writeText(l),d.textContent="Copied"}catch{d.textContent="Failed"}window.setTimeout(()=>{d.textContent="Copy"},1200)}),r.appendChild(d)});const a=[];return o.querySelectorAll("figure.board-embed").forEach(r=>{const c=r.dataset.src??"";if(!c)return;const l=re(c,e.docPath),d=l[0]??"",h=r.dataset.region??"",p=h?h.split(",").map(Number):[],m=p.length===4&&p.every(b=>Number.isFinite(b))?[p[0],p[1],p[2],p[3]]:null,g=parseInt(r.dataset.height??"360",10)||360,y=r.dataset.title||null;try{a.push(new xi(r,{rootId:e.rootId,src:d,srcCandidates:l,region:m,height:g,title:y,theme:ee()}))}catch(b){console.error("Board embed failed to mount",c,b),r.textContent=`Could not load board "${c}".`}}),{frontmatter:s,destroy:()=>{for(const r of a)r.destroy()},setTheme:r=>{for(const c of a)c.setTheme(r)}}}const ki=.05,Ei=40,ss=1;function L(i,t,e){return{x:(i-e.offsetX)/e.scale,y:(t-e.offsetY)/e.scale}}function Rt(i,t,e){return{x:i*e.scale+e.offsetX,y:t*e.scale+e.offsetY}}function is(i){return Math.min(Ei,Math.max(ki,i))}function oe(i,t,e,n){const s=is(n),o={x:(t-i.offsetX)/i.scale,y:(e-i.offsetY)/i.scale};i.scale=s,i.offsetX=t-o.x*i.scale,i.offsetY=e-o.y*i.scale}function Si(i,t,e){const n=L(0,0,i),s=L(t,e,i);return{left:n.x,top:n.y,right:s.x,bottom:s.y}}class Ti{constructor(){u(this,"schemaUrl");u(this,"boardId","default-board");u(this,"title","Untitled Board");u(this,"createdAt",Date.now());u(this,"viewport",{offsetX:0,offsetY:0,scale:ss});u(this,"theme","light");u(this,"elements",[]);u(this,"elementsById",new Map);u(this,"selectedIds",new Set);u(this,"hoveredId",null);u(this,"sortedCache",null);u(this,"changeListeners",[]);u(this,"renderListeners",[])}onChange(t){this.changeListeners.push(t)}onRenderNeeded(t){this.renderListeners.push(t)}emitChange(){this.sortedCache=null;for(const t of this.changeListeners)t();this.requestRender()}requestRender(){for(const t of this.renderListeners)t()}addElement(t){this.elements.push(t),this.elementsById.set(t.id,t),this.sortedCache=null}removeElement(t){const e=this.elementsById.get(t);if(!e)return;this.elementsById.delete(t);const n=this.elements.indexOf(e);return n>=0&&this.elements.splice(n,1),this.selectedIds.delete(t),this.sortedCache=null,e}getSorted(){return this.sortedCache||(this.sortedCache=[...this.elements].sort((t,e)=>t.zIndex-e.zIndex)),this.sortedCache}invalidateSort(){this.sortedCache=null}maxZIndex(){let t=0;for(const e of this.elements)e.zIndex>t&&(t=e.zIndex);return t}minZIndex(){let t=0;for(const e of this.elements)e.zIndex<t&&(t=e.zIndex);return t}getSelected(){const t=[];for(const e of this.selectedIds){const n=this.elementsById.get(e);n&&t.push(n)}return t}clearSelection(){this.selectedIds.clear()}getVisibleElements(t){const e=[];for(const n of this.getSorted())n.hidden||Bi(n,t)&&e.push(n);return e}}function Bi(i,t){const e=Math.min(i.x,i.x+i.width),n=Math.max(i.x,i.x+i.width),s=Math.min(i.y,i.y+i.height),o=Math.max(i.y,i.y+i.height);return!(n<t.left||e>t.right||o<t.top||s>t.bottom)}let Ae=0;function ae(){return Ae=(Ae+1)%65535,Date.now().toString(36)+"-"+Ae.toString(36)+"-"+Math.random().toString(36).slice(2,8)}function H(i,t={}){const e=Date.now();return{id:ae(),type:i,x:0,y:0,width:100,height:100,rotation:0,zIndex:0,locked:!1,hidden:!1,createdAt:e,updatedAt:e,style:{},data:{},...t}}const hn=200;class Mi{constructor(){u(this,"undoStack",[]);u(this,"redoStack",[])}execute(t,e,n){const s={id:ae(),type:t,timestamp:Date.now(),do:e,undo:n};s.do(),this.undoStack.push(s),this.undoStack.length>hn&&this.undoStack.shift(),this.redoStack.length=0}push(t,e,n){const s={id:ae(),type:t,timestamp:Date.now(),do:e,undo:n};this.undoStack.push(s),this.undoStack.length>hn&&this.undoStack.shift(),this.redoStack.length=0}undo(){const t=this.undoStack.pop();return t?(t.undo(),this.redoStack.push(t),!0):!1}redo(){const t=this.redoStack.pop();return t?(t.do(),this.undoStack.push(t),!0):!1}clear(){this.undoStack.length=0,this.redoStack.length=0}}class Ii{constructor(t,e){u(this,"state");u(this,"commands");this.state=t,this.commands=e}deleteSelection(){const t=this.state.getSelected().filter(s=>!s.locked);if(t.length===0)return;const e=this.state,n=t.map(s=>s);this.commands.execute("delete",()=>{for(const s of n)e.removeElement(s.id);e.emitChange()},()=>{for(const s of n)e.elementsById.has(s.id)||e.addElement(s);e.emitChange()})}duplicateSelection(t=24){const e=this.state.getSelected();if(e.length===0)return[];const n=e.map(s=>this.cloneElement(s,t));this.insertElements(n,"duplicate"),this.state.clearSelection();for(const s of n)this.state.selectedIds.add(s.id);return this.state.requestRender(),n}cloneElement(t,e){const n=Date.now();return{...t,id:ae(),x:t.x+e,y:t.y+e,zIndex:this.state.maxZIndex()+1,createdAt:n,updatedAt:n,style:{...t.style},data:{...t.data}}}insertElements(t,e){const n=this.state;this.commands.execute(e,()=>{for(const s of t)n.elementsById.has(s.id)||n.addElement(s);n.emitChange()},()=>{for(const s of t)n.removeElement(s.id);n.emitChange()})}nudgeSelection(t,e){const n=this.state.getSelected().filter(c=>!c.locked);if(n.length===0)return;const s=this.state,o=n.map(c=>({id:c.id,x:c.x,y:c.y,updatedAt:c.updatedAt})),a=n.map(c=>({id:c.id,x:c.x+t,y:c.y+e,updatedAt:Date.now()})),r=c=>{for(const l of c){const d=s.elementsById.get(l.id);d&&(d.x=l.x,d.y=l.y,d.updatedAt=l.updatedAt)}s.emitChange()};this.commands.execute("nudge",()=>r(a),()=>r(o))}applyZChange(t){const e=this.state.getSelected();if(e.length===0)return;const n=this.state,s=e.map(r=>({id:r.id,z:r.zIndex}));for(const r of e)t(r);n.invalidateSort();const o=e.map(r=>({id:r.id,z:r.zIndex})),a=r=>{for(const c of r){const l=n.elementsById.get(c.id);l&&(l.zIndex=c.z)}n.invalidateSort(),n.emitChange()};this.commands.push("layer",()=>a(o),()=>a(s)),n.emitChange()}bringForward(){this.applyZChange(t=>t.zIndex+=1)}sendBackward(){this.applyZChange(t=>t.zIndex-=1)}bringToFront(){let t=this.state.maxZIndex();this.applyZChange(e=>e.zIndex=++t)}sendToBack(){let t=this.state.minZIndex();this.applyZChange(e=>e.zIndex=--t)}}const Ri=["image/png","image/jpeg","image/webp","image/gif"],Li=5*1024*1024;class $i{constructor(t,e,n,s,o){u(this,"state");u(this,"commands");u(this,"imageCache");u(this,"notify");u(this,"uploadImage");u(this,"uploadedImageByHash",new Map);this.state=t,this.commands=e,this.imageCache=n,this.notify=s,this.uploadImage=o}async insertBlob(t,e,n,s){if(!Ri.includes(t.type)){this.notify("Unsupported image type");return}t.size>Li&&this.notify("Large image added — consider optimizing it for git");let o=0,a=0,r=null;try{r=await createImageBitmap(t),o=r.width,a=r.height}catch{this.notify("Could not decode image");return}const c={originalWidth:o,originalHeight:a,alt:s};if(this.uploadImage)try{const b=await this.uploadOrReuseImage(t,s);c.src=b,r&&this.imageCache.setBySrc(b,r)}catch(b){this.notify("Failed to save image to disk"),console.error("Image upload failed",b);return}else try{const b=await it.createFromBlob(t,s);c.assetId=b.id,r&&this.imageCache.set(b.id,r)}catch(b){this.notify("Failed to store image (storage quota?)"),console.error("Asset store failed",b);return}const l=L(e,n,this.state.viewport),d=480/Math.min(1,this.state.viewport.scale);let h=o,p=a;const m=Math.max(h,p);if(m>d){const b=d/m;h*=b,p*=b}const g=H("image",{x:l.x-h/2,y:l.y-p/2,width:h,height:p,zIndex:this.state.maxZIndex()+1,data:c}),y=this.state;this.commands.execute("insert-image",()=>{y.elementsById.has(g.id)||y.addElement(g),y.emitChange()},()=>{y.removeElement(g.id),y.emitChange()}),y.clearSelection(),y.selectedIds.add(g.id),y.requestRender()}async insertFiles(t,e,n){let s=0;for(const o of Array.from(t))o.type.startsWith("image/")&&(await this.insertBlob(o,e+s,n+s,o.name),s+=24)}async uploadOrReuseImage(t,e){if(!this.uploadImage)throw new Error("Missing uploadImage");const n=await Pi(t),s=this.uploadedImageByHash.get(n);if(s)return s;const o=await this.uploadImage(t,e);return this.uploadedImageByHash.set(n,o),o}}async function Pi(i){const t=await crypto.subtle.digest("SHA-256",await i.arrayBuffer());return Array.from(new Uint8Array(t),e=>e.toString(16).padStart(2,"0")).join("")}const pn="application/x-viteboard-elements";class Di{constructor(t,e,n,s){u(this,"state");u(this,"actions");u(this,"imageInsert");u(this,"internalClipboard",[]);u(this,"internalClipboardToken","");u(this,"lastPointer",{x:window.innerWidth/2,y:window.innerHeight/2});this.state=t,this.actions=e,this.imageInsert=n,window.addEventListener("pointermove",o=>{this.lastPointer={x:o.clientX,y:o.clientY}},{signal:s}),window.addEventListener("paste",o=>{this.isEditingText(o.target)||this.handlePaste(o)},{signal:s}),window.addEventListener("copy",o=>{this.isEditingText(o.target)||this.copySelection(o.clipboardData)&&o.preventDefault()},{signal:s}),window.addEventListener("cut",o=>{this.isEditingText(o.target)||this.copySelection(o.clipboardData)&&(this.actions.deleteSelection(),o.preventDefault())},{signal:s})}isEditingText(t){const e=t;return!!e&&(e.tagName==="TEXTAREA"||e.tagName==="INPUT"||e.isContentEditable)}copySelection(t){const e=this.state.getSelected();return e.length===0?!1:(this.internalClipboard=e.map(n=>({...n,style:{...n.style},data:{...n.data}})),this.internalClipboardToken=Date.now().toString(36)+"-"+Math.random().toString(36).slice(2,8),t&&this.writeClipboardPayload(t,e),!0)}async handlePaste(t){const e=t.clipboardData,n=e==null?void 0:e.items,s=e==null?void 0:e.getData("text/plain").trim();if(e&&this.isInternalClipboardPaste(e)){t.preventDefault(),this.pasteInternal();return}if(n){for(const o of n)if(o.type.startsWith("image/")){const a=o.getAsFile();if(a&&!s){t.preventDefault(),await this.imageInsert.insertBlob(a,this.lastPointer.x,this.lastPointer.y,"Pasted screenshot");return}}}if(!e&&this.internalClipboard.length>0){t.preventDefault(),this.pasteInternal();return}s&&(t.preventDefault(),this.pasteText(s))}writeClipboardPayload(t,e){try{t.clearData(),t.setData(pn,this.internalClipboardToken),t.setData("text/plain",this.selectionText(e))}catch{}}isInternalClipboardPaste(t){const e=t.getData(pn).trim();return!!e&&e===this.internalClipboardToken&&this.internalClipboard.length>0}selectionText(t){return t.map(n=>typeof n.data.text=="string"?n.data.text.trim():"").filter(Boolean).join(`
|
|
11
|
-
|
|
12
|
-
`)}pasteInternal(){const t=L(this.lastPointer.x,this.lastPointer.y,this.state.viewport);let e=1/0,n=1/0,s=-1/0,o=-1/0;for(const d of this.internalClipboard)e=Math.min(e,Math.min(d.x,d.x+d.width)),n=Math.min(n,Math.min(d.y,d.y+d.height)),s=Math.max(s,Math.max(d.x,d.x+d.width)),o=Math.max(o,Math.max(d.y,d.y+d.height));const a=(e+s)/2,r=(n+o)/2;let c=this.state.maxZIndex();const l=[];for(const d of this.internalClipboard){const h=Yn(d);if(!h)continue;const p=this.actions.cloneElement(h,0);p.x=h.x+(t.x-a),p.y=h.y+(t.y-r),p.zIndex=++c,l.push(p)}if(l.length!==0){this.actions.insertElements(l,"paste"),this.state.clearSelection();for(const d of l)this.state.selectedIds.add(d.id);this.state.requestRender()}}pasteText(t){const e=L(this.lastPointer.x,this.lastPointer.y,this.state.viewport),n=H("text",{x:e.x,y:e.y,width:Math.min(420,Math.max(160,t.length*8)),height:28,zIndex:this.state.maxZIndex()+1,style:{fontSize:16,textColor:this.state.theme==="dark"?"#f2f2f2":"#1f2933"},data:{text:t.slice(0,5e3)}});It(n),this.actions.insertElements([n],"paste-text"),this.state.clearSelection(),this.state.selectedIds.add(n.id),this.state.requestRender()}}function Ai(i,t){const{state:e,commands:n,actions:s,toolController:o}=i;window.addEventListener("keydown",a=>{const r=a.target;if(r&&(r.tagName==="TEXTAREA"||r.tagName==="INPUT"||r.isContentEditable))return;const c=a.metaKey||a.ctrlKey,l=a.shiftKey?10:1;if(!c)switch(a.key){case"ArrowLeft":a.preventDefault(),s.nudgeSelection(-l,0);return;case"ArrowRight":a.preventDefault(),s.nudgeSelection(l,0);return;case"ArrowUp":a.preventDefault(),s.nudgeSelection(0,-l);return;case"ArrowDown":a.preventDefault(),s.nudgeSelection(0,l);return}if(c){switch(a.key.toLowerCase()){case"d":a.preventDefault(),s.duplicateSelection();return;case"z":a.preventDefault(),a.shiftKey?n.redo():n.undo();return;case"]":a.preventDefault(),a.shiftKey?s.bringToFront():s.bringForward();return;case"[":a.preventDefault(),a.shiftKey?s.sendToBack():s.sendBackward();return;case"=":case"+":a.preventDefault(),oe(e.viewport,window.innerWidth/2,window.innerHeight/2,e.viewport.scale*1.2),e.requestRender();return;case"-":a.preventDefault(),oe(e.viewport,window.innerWidth/2,window.innerHeight/2,e.viewport.scale/1.2),e.requestRender();return;case"0":a.preventDefault(),i.resetView();return;case"a":a.preventDefault(),e.clearSelection();for(const d of e.elements)!d.locked&&!d.hidden&&e.selectedIds.add(d.id);e.requestRender();return}return}switch(a.key){case"Delete":case"Backspace":a.preventDefault(),s.deleteSelection();return;case"Escape":o.cancel(),o.setTool("select");return}switch(a.key.toLowerCase()){case"v":o.setTool("select");return;case"h":o.setTool("hand");return;case"s":o.setTool("sticky");return;case"r":o.setTool("rectangle");return;case"o":o.setTool("ellipse");return;case"t":o.setTool(a.shiftKey?"task":"text");return;case"a":o.setTool("arrow");return;case"i":o.setTool("image");return}},{signal:t})}function Ni(i){if(i.elements.length===0)return null;let t=1/0,e=1/0,n=-1/0,s=-1/0;for(const a of i.elements)a.hidden||(t=Math.min(t,Math.min(a.x,a.x+a.width)),e=Math.min(e,Math.min(a.y,a.y+a.height)),n=Math.max(n,Math.max(a.x,a.x+a.width)),s=Math.max(s,Math.max(a.y,a.y+a.height)));if(!Number.isFinite(t))return null;const o=48;return{x:t-o,y:e-o,w:n-t+o*2,h:s-e+o*2}}function os(i,t,e,n){let o=Math.round(e.w*n),a=Math.round(e.h*n);if(o>8192||a>8192){const h=8192/Math.max(o,a);n*=h,o=Math.round(e.w*n),a=Math.round(e.h*n)}const r=document.createElement("canvas");r.width=Math.max(1,o),r.height=Math.max(1,a);const c=r.getContext("2d"),l=i.theme==="dark"?"#111315":"#f3f2f1";c.fillStyle=l,c.fillRect(0,0,r.width,r.height),c.scale(n,n),c.translate(-e.x,-e.y);const d=i.getSorted();for(const h of d)h.hidden||Math.max(h.x,h.x+h.width)<e.x||Math.min(h.x,h.x+h.width)>e.x+e.w||Math.max(h.y,h.y+h.height)<e.y||Math.min(h.y,h.y+h.height)>e.y+e.h||xe(h,{ctx:c,scale:n,lod:2,theme:i.theme,imageCache:t});return r}function as(i){return new Promise((t,e)=>{i.toBlob(n=>{n?t(n):e(new Error("PNG encode failed"))},"image/png")})}async function zi(i,t,e,n){const s=i.viewport,o={x:-s.offsetX/s.scale,y:-s.offsetY/s.scale,w:e/s.scale,h:n/s.scale},a=os(i,t,o,s.scale);return as(a)}async function Hi(i,t){const e=Ni(i);if(!e)return null;const n=os(i,t,e,1);return as(n)}function un(i,t){const e=URL.createObjectURL(i),n=document.createElement("a");n.href=e,n.download=t,n.click(),URL.revokeObjectURL(e)}class Ze{constructor(t,e){u(this,"overlay");u(this,"rectEl");u(this,"abort",new AbortController);u(this,"start",null);u(this,"onComplete");u(this,"done",!1);this.onComplete=e,this.overlay=document.createElement("div"),this.overlay.className="embed-select-overlay";const n=document.createElement("div");n.className="embed-select-hint",n.textContent="Drag to select the area to embed · Esc to cancel",this.overlay.appendChild(n),this.rectEl=document.createElement("div"),this.rectEl.className="embed-select-rect",this.rectEl.style.display="none",this.overlay.appendChild(this.rectEl),t.appendChild(this.overlay);const s=this.abort.signal;this.overlay.addEventListener("pointerdown",o=>this.onDown(o),{signal:s}),this.overlay.addEventListener("pointermove",o=>this.onMove(o),{signal:s}),this.overlay.addEventListener("pointerup",o=>this.onUp(o),{signal:s}),window.addEventListener("keydown",o=>{o.key==="Escape"&&(o.preventDefault(),this.finish(null))},{signal:s})}static begin(t,e){new Ze(t,e)}onDown(t){t.preventDefault(),this.start={x:t.clientX,y:t.clientY},this.rectEl.style.display="block",this.draw(t.clientX,t.clientY),this.overlay.setPointerCapture(t.pointerId)}onMove(t){this.start&&this.draw(t.clientX,t.clientY)}onUp(t){if(!this.start)return;const e=this.rectFrom(t.clientX,t.clientY);if(e.w<8||e.h<8){this.finish(null);return}this.finish(e)}rectFrom(t,e){const n=this.start;return{x:Math.min(n.x,t),y:Math.min(n.y,e),w:Math.abs(t-n.x),h:Math.abs(e-n.y)}}draw(t,e){const n=this.rectFrom(t,e);this.rectEl.style.left=`${n.x}px`,this.rectEl.style.top=`${n.y}px`,this.rectEl.style.width=`${n.w}px`,this.rectEl.style.height=`${n.h}px`}finish(t){this.done||(this.done=!0,this.abort.abort(),this.overlay.remove(),this.onComplete(t))}}class Fi{constructor(t,e=3,n){u(this,"container");u(this,"layers",[]);u(this,"contexts",[]);u(this,"width",0);u(this,"height",0);u(this,"dpr",1);u(this,"resizeListeners",[]);this.container=document.createElement("div"),this.container.className="canvas-stack",t.appendChild(this.container);for(let s=0;s<e;s++){const o=document.createElement("canvas"),a=o.getContext("2d",{alpha:s!==0});this.container.appendChild(o),this.layers.push(o),this.contexts.push(a)}this.resize(),window.addEventListener("resize",()=>{this.resize();for(const s of this.resizeListeners)s()},{signal:n})}get interactionLayer(){return this.layers[this.layers.length-1]}onResize(t){this.resizeListeners.push(t)}resize(){this.dpr=Math.max(1,window.devicePixelRatio||1),this.width=window.innerWidth,this.height=window.innerHeight;for(const t of this.layers)t.width=Math.round(this.width*this.dpr),t.height=Math.round(this.height*this.dpr),t.style.width=this.width+"px",t.style.height=this.height+"px"}beginLayer(t){const e=this.contexts[t];return e.setTransform(this.dpr,0,0,this.dpr,0,0),e.clearRect(0,0,this.width,this.height),e}}class ji{constructor(t,e,n){u(this,"surface");u(this,"state");u(this,"imageCache");u(this,"overlay",{marquee:null,preview:null});u(this,"lastRenderMs",0);u(this,"visibleCount",0);u(this,"cullingEnabled",!0);u(this,"lodEnabled",!0);this.surface=t,this.state=e,this.imageCache=n}getLod(){if(!this.lodEnabled)return 2;const t=this.state.viewport.scale;return t<.1?0:t<.25?1:2}render(){const t=performance.now();this.renderGrid(),this.renderObjects(),this.renderOverlay(),this.lastRenderMs=performance.now()-t}renderGrid(){const{surface:t,state:e}=this,n=t.beginLayer(0),s=e.viewport,o=getComputedStyle(document.documentElement).getPropertyValue("--bg-canvas").trim(),a=getComputedStyle(document.documentElement).getPropertyValue("--grid-dot").trim();n.fillStyle=o||"#f3f2f1",n.fillRect(0,0,t.width,t.height);let c=32*s.scale;if(!Number.isFinite(c)||c<=0)return;for(;c<16;)c*=2;for(;c>64;)c/=2;if(c<4)return;const l=(s.offsetX%c+c)%c,d=(s.offsetY%c+c)%c,h=Math.min(1.5,Math.max(.75,s.scale*.9));n.fillStyle=a||"rgba(0,0,0,0.14)",n.beginPath();for(let p=l;p<t.width;p+=c)for(let m=d;m<t.height;m+=c)n.moveTo(p+h,m),n.arc(p,m,h,0,Math.PI*2);n.fill()}renderObjects(){const{surface:t,state:e}=this,n=t.beginLayer(1),s=e.viewport;n.translate(s.offsetX,s.offsetY),n.scale(s.scale,s.scale);const o=Si(s,t.width,t.height),a=this.cullingEnabled?e.getVisibleElements(o):e.getSorted().filter(c=>!c.hidden);this.visibleCount=a.length;const r={ctx:n,scale:s.scale,lod:this.getLod(),theme:e.theme,imageCache:this.imageCache};for(const c of a)xe(c,r)}renderOverlay(){const{surface:t,state:e}=this,n=t.beginLayer(2),s=e.viewport,o=getComputedStyle(document.documentElement).getPropertyValue("--selection").trim()||"#6e6e6e";if(e.hoveredId&&!e.selectedIds.has(e.hoveredId)){const r=e.elementsById.get(e.hoveredId);if(r){const c=this.elementScreenBBox(r);n.strokeStyle=o,n.globalAlpha=.45,n.lineWidth=1.5,n.strokeRect(c.x,c.y,c.w,c.h),n.globalAlpha=1}}const a=e.getSelected();if(a.length>0){n.strokeStyle=o,n.lineWidth=1.5;let r=1/0,c=1/0,l=-1/0,d=-1/0;for(const p of a){const m=this.elementScreenBBox(p);n.strokeRect(m.x,m.y,m.w,m.h),r=Math.min(r,m.x),c=Math.min(c,m.y),l=Math.max(l,m.x+m.w),d=Math.max(d,m.y+m.h)}a.length>1&&(n.setLineDash([4,4]),n.strokeRect(r-4,c-4,l-r+8,d-c+8),n.setLineDash([]));const h=a.length===1?this.elementScreenBBox(a[0]):{x:r,y:c,w:l-r,h:d-c};this.drawHandles(n,h,o)}if(this.overlay.marquee){const r=this.overlay.marquee;n.fillStyle=o+"22",n.strokeStyle=o,n.lineWidth=1,n.fillRect(r.x,r.y,r.w,r.h),n.strokeRect(r.x,r.y,r.w,r.h)}this.overlay.preview&&(n.save(),n.translate(s.offsetX,s.offsetY),n.scale(s.scale,s.scale),n.globalAlpha=.8,xe(this.overlay.preview,{ctx:n,scale:s.scale,lod:2,theme:e.theme,imageCache:this.imageCache}),n.restore())}elementScreenBBox(t){const e=this.state.viewport,n=Math.min(t.x,t.x+t.width),s=Math.min(t.y,t.y+t.height),o=Math.max(t.x,t.x+t.width),a=Math.max(t.y,t.y+t.height),r=Rt(n,s,e),c=Rt(o,a,e);return{x:r.x,y:r.y,w:c.x-r.x,h:c.y-r.y}}drawHandles(t,e,n){const a=rs(e);t.fillStyle=getComputedStyle(document.documentElement).getPropertyValue("--bg-panel").trim()||"#fff",t.strokeStyle=n,t.lineWidth=1.5;for(const r of a)t.beginPath(),t.rect(r.x-4,r.y-4,8,8),t.fill(),t.stroke()}}function rs(i){const{x:t,y:e,w:n,h:s}=i;return[{id:"nw",x:t,y:e},{id:"n",x:t+n/2,y:e},{id:"ne",x:t+n,y:e},{id:"e",x:t+n,y:e+s/2},{id:"se",x:t+n,y:e+s},{id:"s",x:t+n/2,y:e+s},{id:"sw",x:t,y:e+s},{id:"w",x:t,y:e+s/2}]}class Wi{constructor(t){u(this,"rafId",null);u(this,"renderFn");this.renderFn=t}requestRender(){this.rafId===null&&(this.rafId=requestAnimationFrame(()=>{this.rafId=null,this.renderFn()}))}cancel(){this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null)}}function Oi(i,t,e,n,s,o){const a=s-e,r=o-n,c=a*a+r*r;if(c===0)return Math.hypot(i-e,t-n);let l=((i-e)*a+(t-n)*r)/c;return l=Math.max(0,Math.min(1,l)),Math.hypot(i-(e+l*a),t-(n+l*r))}class Ui{constructor(t){u(this,"state");u(this,"lastHitTestMs",0);this.state=t}hitTest(t,e,n){const s=performance.now(),o=this.state.getSorted(),a=6/n;let r=null;for(let c=o.length-1;c>=0;c--){const l=o[c];if(!(l.hidden||l.locked)&&this.hitsElement(l,t,e,a)){r=l;break}}return this.lastHitTestMs=performance.now()-s,r}hitsElement(t,e,n,s){if(t.type==="line"||t.type==="arrow"||t.type==="connector")return Oi(e,n,t.x,t.y,t.x+t.width,t.y+t.height)<=Math.max(s,(t.style.strokeWidth??2)/2+s);const o=Math.min(t.x,t.x+t.width),a=Math.max(t.x,t.x+t.width),r=Math.min(t.y,t.y+t.height),c=Math.max(t.y,t.y+t.height);if(e<o||e>a||n<r||n>c)return!1;if(t.type==="ellipse"){const l=(o+a)/2,d=(r+c)/2,h=(a-o)/2,p=(c-r)/2;if(h===0||p===0)return!1;const m=(e-l)/h,g=(n-d)/p;return m*m+g*g<=1}return!0}elementsInRect(t,e,n,s){const o=Math.min(t,t+n),a=Math.max(t,t+n),r=Math.min(e,e+s),c=Math.max(e,e+s),l=[];for(const d of this.state.elements){if(d.hidden||d.locked)continue;const h=Math.min(d.x,d.x+d.width),p=Math.max(d.x,d.x+d.width),m=Math.min(d.y,d.y+d.height),g=Math.max(d.y,d.y+d.height);h>=o&&p<=a&&m>=r&&g<=c&&l.push(d)}return l}}class _i{constructor(){u(this,"fps",0);u(this,"frameCount",0);u(this,"lastSample",performance.now());u(this,"running",!1)}start(){if(this.running)return;this.running=!0;const t=()=>{if(!this.running)return;this.frameCount++;const e=performance.now(),n=e-this.lastSample;n>=1e3&&(this.fps=Math.round(this.frameCount*1e3/n),this.frameCount=0,this.lastSample=e),requestAnimationFrame(t)};requestAnimationFrame(t)}stop(){this.running=!1}}const Xi=16,qi=720,mn=120,Yi=.0032;class Ki{constructor(t,e,n,s){u(this,"tools",new Map);u(this,"active");u(this,"ctx");u(this,"target");u(this,"spaceDown",!1);u(this,"tempPanning",!1);u(this,"panLast",{x:0,y:0});u(this,"toolChangeListeners",[]);u(this,"signal");this.target=t,this.ctx=e,this.signal=s;for(const o of n)this.tools.set(o.name,o);this.active=this.tools.get("select"),this.bindEvents()}get activeName(){return this.active.name}onToolChange(t){this.toolChangeListeners.push(t)}setTool(t){var n,s,o,a,r;if(this.active.name===t){(s=(n=this.active).onActivate)==null||s.call(n,this.ctx);return}(a=(o=this.active).onDeactivate)==null||a.call(o,this.ctx);const e=this.tools.get(t);if(e){this.active=e,this.ctx.setCursor(e.cursor),(r=e.onActivate)==null||r.call(e,this.ctx);for(const c of this.toolChangeListeners)c(t)}}cancel(){var t,e;(e=(t=this.active).onCancel)==null||e.call(t,this.ctx)}bindEvents(){const t=this.target;t.addEventListener("pointerdown",e=>{if(e.button===1||e.button===2||this.spaceDown){e.preventDefault(),this.tempPanning=!0,this.panLast={x:e.clientX,y:e.clientY},this.ctx.setCursor("grabbing"),t.setPointerCapture(e.pointerId);return}e.button===0&&(t.setPointerCapture(e.pointerId),this.active.onPointerDown(e,this.ctx))}),t.addEventListener("pointermove",e=>{if(this.tempPanning){const n=this.ctx.state.viewport;n.offsetX+=e.clientX-this.panLast.x,n.offsetY+=e.clientY-this.panLast.y,this.panLast={x:e.clientX,y:e.clientY},this.ctx.state.requestRender();return}this.active.onPointerMove(e,this.ctx)}),t.addEventListener("pointerup",e=>{if(this.tempPanning){this.tempPanning=!1,this.ctx.setCursor(this.spaceDown?"grab":this.active.cursor);return}this.active.onPointerUp(e,this.ctx)}),t.addEventListener("pointercancel",()=>{this.tempPanning&&(this.tempPanning=!1,this.ctx.setCursor(this.spaceDown?"grab":this.active.cursor))}),t.addEventListener("dblclick",e=>{var n,s;(s=(n=this.active).onDoubleClick)==null||s.call(n,e,this.ctx)}),t.addEventListener("wheel",e=>{e.preventDefault();const n=this.ctx.state.viewport,s=fn(e.deltaX,e.deltaMode,this.ctx.state.viewport.scale),o=fn(e.deltaY,e.deltaMode,this.ctx.state.viewport.scale);if(e.ctrlKey||e.metaKey){const a=Math.exp(-Vi(o)*Yi);oe(n,e.clientX,e.clientY,n.scale*a)}else e.shiftKey&&Math.abs(s)<Math.abs(o)?n.offsetX-=o:(n.offsetX-=s,n.offsetY-=o);this.ctx.state.requestRender()},{passive:!1}),window.addEventListener("keydown",e=>{e.code==="Space"&&!this.isEditingText(e)&&(this.spaceDown||(this.spaceDown=!0,this.tempPanning||this.ctx.setCursor("grab")),e.preventDefault())},{signal:this.signal}),window.addEventListener("keyup",e=>{e.code==="Space"&&(this.spaceDown=!1,this.tempPanning||this.ctx.setCursor(this.active.cursor))},{signal:this.signal}),t.addEventListener("contextmenu",e=>e.preventDefault())}isEditingText(t){const e=t.target;return!!e&&(e.tagName==="TEXTAREA"||e.tagName==="INPUT"||e.isContentEditable)}}function fn(i,t,e){const s=i*(t===1?Xi:t===2?qi:1),o=e<.5?.8:e>2?1.08:1;return s*o}function Vi(i){return Math.max(-mn,Math.min(mn,i))}const he=7,Gi={nw:"nwse-resize",n:"ns-resize",ne:"nesw-resize",e:"ew-resize",se:"nwse-resize",s:"ns-resize",sw:"nesw-resize",w:"ew-resize"};class Zi{constructor(){u(this,"name","select");u(this,"cursor","default");u(this,"mode","none");u(this,"startScreen",{x:0,y:0});u(this,"startWorld",{x:0,y:0});u(this,"snapshots",[]);u(this,"activeHandle",null);u(this,"endpointIsStart",!1);u(this,"moved",!1);u(this,"pointerDownHitId",null)}onPointerDown(t,e){const{state:n,hitTester:s,renderer:o}=e;this.startScreen={x:t.clientX,y:t.clientY},this.startWorld=L(t.clientX,t.clientY,n.viewport),this.moved=!1;const a=this.hitHandle(t,e);if(a){this.mode="resize",this.activeHandle=a,this.takeSnapshots(n.getSelected());return}const r=n.getSelected();if(r.length===1&&this.isLinear(r[0])){const l=this.hitEndpoint(t,r[0],e);if(l!==null){this.mode="endpoint",this.endpointIsStart=l,this.takeSnapshots(r);return}}const c=s.hitTest(this.startWorld.x,this.startWorld.y,n.viewport.scale);if(c){this.pointerDownHitId=c.id,t.shiftKey?n.selectedIds.has(c.id)?n.selectedIds.delete(c.id):n.selectedIds.add(c.id):n.selectedIds.has(c.id)||(n.clearSelection(),n.selectedIds.add(c.id)),this.mode="move",this.takeSnapshots(n.getSelected()),n.requestRender();return}t.shiftKey||n.clearSelection(),this.mode="marquee",o.overlay.marquee={x:t.clientX,y:t.clientY,w:0,h:0},n.requestRender()}onPointerMove(t,e){const{state:n,renderer:s,hitTester:o}=e;if(this.mode==="none"){const d=this.hitHandle(t,e);if(d){e.setCursor(Gi[d]);return}const h=L(t.clientX,t.clientY,n.viewport),p=o.hitTest(h.x,h.y,n.viewport.scale),m=p?p.id:null;e.setCursor(p?"move":"default"),m!==n.hoveredId&&(n.hoveredId=m,n.requestRender());return}const a=t.clientX-this.startScreen.x,r=t.clientY-this.startScreen.y;Math.abs(a)+Math.abs(r)>2&&(this.moved=!0);const c=a/n.viewport.scale,l=r/n.viewport.scale;if(this.mode==="move"){for(const d of this.snapshots){const h=n.elementsById.get(d.id);!h||h.locked||(h.x=d.x+c,h.y=d.y+l)}n.requestRender()}else if(this.mode==="marquee")s.overlay.marquee={x:this.startScreen.x,y:this.startScreen.y,w:a,h:r},n.requestRender();else if(this.mode==="resize"&&this.activeHandle)this.applyResize(c,l,t.shiftKey,e),n.requestRender();else if(this.mode==="endpoint"){const d=this.snapshots[0],h=n.elementsById.get(d.id);h&&(this.endpointIsStart?(h.x=d.x+c,h.y=d.y+l,h.width=d.width-c,h.height=d.height-l):(h.width=d.width+c,h.height=d.height+l)),n.requestRender()}}onPointerUp(t,e){const{state:n,renderer:s,hitTester:o,commands:a}=e;if(this.mode==="marquee"){const r=s.overlay.marquee;if(s.overlay.marquee=null,r&&(Math.abs(r.w)>3||Math.abs(r.h)>3)){const c=L(r.x,r.y,n.viewport),l=L(r.x+r.w,r.y+r.h,n.viewport),d=o.elementsInRect(c.x,c.y,l.x-c.x,l.y-c.y);for(const h of d)n.selectedIds.add(h.id)}n.requestRender()}else if((this.mode==="move"||this.mode==="resize"||this.mode==="endpoint")&&this.moved){const r=this.snapshots,c=r.map(d=>{const h=n.elementsById.get(d.id);return{id:d.id,x:h.x,y:h.y,width:h.width,height:h.height}}),l=d=>{for(const h of d){const p=n.elementsById.get(h.id);p&&(p.x=h.x,p.y=h.y,p.width=h.width,p.height=h.height,p.updatedAt=Date.now(),X(p.id))}n.emitChange()};for(const d of c){const h=n.elementsById.get(d.id);h&&(h.updatedAt=Date.now(),X(h.id))}a.push(this.mode,()=>l(c),()=>l(r)),n.emitChange()}else this.mode==="move"&&!this.moved&&this.toggleTaskIfCheckboxClick(t,e);this.mode="none",this.activeHandle=null,this.snapshots=[],this.pointerDownHitId=null}onDoubleClick(t,e){const{state:n,hitTester:s}=e,o=L(t.clientX,t.clientY,n.viewport),a=s.hitTest(o.x,o.y,n.viewport.scale);a&&(a.type==="sticky"||a.type==="task"||a.type==="text"||a.type==="rectangle"||a.type==="ellipse"||a.type==="frame"||a.type==="arrow"||a.type==="line"||a.type==="connector")&&e.openTextEditor(a)}onCancel(t){if(this.mode!=="none"&&this.snapshots.length>0)for(const e of this.snapshots){const n=t.state.elementsById.get(e.id);n&&(n.x=e.x,n.y=e.y,n.width=e.width,n.height=e.height)}t.renderer.overlay.marquee=null,this.mode="none",t.state.clearSelection(),t.state.requestRender()}isLinear(t){return t.type==="line"||t.type==="arrow"||t.type==="connector"}takeSnapshots(t){this.snapshots=t.map(e=>({id:e.id,x:e.x,y:e.y,width:e.width,height:e.height}))}toggleTaskIfCheckboxClick(t,e){const{state:n,hitTester:s,commands:o}=e,a=L(t.clientX,t.clientY,n.viewport),r=s.hitTest(a.x,a.y,n.viewport.scale);if(!r||r.id!==this.pointerDownHitId||r.type!=="task"||!this.hitTaskCheckbox(a.x,a.y,r))return;const c=r.data.checked===!0,l=!c,d=r.id,h=p=>{const m=n.elementsById.get(d);m&&(m.data.checked=p,m.updatedAt=Date.now(),n.emitChange())};o.execute("toggle-task",()=>h(l),()=>h(c))}hitTaskCheckbox(t,e,n){const s=Math.min(22,Math.max(16,n.height*.38)),o=n.x+16,a=n.y+n.height/2-s/2,r=8;return t>=o-r&&t<=o+s+r&&e>=a-r&&e<=a+s+r}hitHandle(t,e){const{state:n,renderer:s}=e,o=n.getSelected();if(o.length===0||o.length===1&&this.isLinear(o[0]))return null;let a;if(o.length===1)a=s.elementScreenBBox(o[0]);else{let r=1/0,c=1/0,l=-1/0,d=-1/0;for(const h of o){const p=s.elementScreenBBox(h);r=Math.min(r,p.x),c=Math.min(c,p.y),l=Math.max(l,p.x+p.w),d=Math.max(d,p.y+p.h)}a={x:r,y:c,w:l-r,h:d-c}}for(const r of rs(a))if(Math.abs(t.clientX-r.x)<=he&&Math.abs(t.clientY-r.y)<=he)return r.id;return null}hitEndpoint(t,e,n){const s=n.state.viewport,o=e.x*s.scale+s.offsetX,a=e.y*s.scale+s.offsetY,r=(e.x+e.width)*s.scale+s.offsetX,c=(e.y+e.height)*s.scale+s.offsetY;return Math.hypot(t.clientX-o,t.clientY-a)<=he+2?!0:Math.hypot(t.clientX-r,t.clientY-c)<=he+2?!1:null}applyResize(t,e,n,s){const{state:o}=s,a=this.activeHandle;let r=1/0,c=1/0,l=-1/0,d=-1/0;for(const w of this.snapshots)r=Math.min(r,Math.min(w.x,w.x+w.width)),c=Math.min(c,Math.min(w.y,w.y+w.height)),l=Math.max(l,Math.max(w.x,w.x+w.width)),d=Math.max(d,Math.max(w.y,w.y+w.height));const h=l-r,p=d-c;if(h===0||p===0)return;let m=r,g=c,y=h,b=p;if(a.includes("e")&&(y=h+t),a.includes("w")&&(m=r+t,y=h-t),a.includes("s")&&(b=p+e),a.includes("n")&&(g=c+e,b=p-e),n&&(a==="nw"||a==="ne"||a==="se"||a==="sw")){const w=h/p;if(Math.abs(y/h)>Math.abs(b/p)){const E=y/w;a.includes("n")&&(g=g+(b-E)),b=E}else{const E=b*w;a.includes("w")&&(m=m+(y-E)),y=E}}const x=4;if(Math.abs(y)<x||Math.abs(b)<x)return;const v=y/h,S=b/p;for(const w of this.snapshots){const E=o.elementsById.get(w.id);!E||E.locked||(E.x=m+(w.x-r)*v,E.y=g+(w.y-c)*S,E.width=w.width*v,E.height=w.height*S,E.updatedAt=Date.now(),X(E.id))}}}class Ji{constructor(){u(this,"name","hand");u(this,"cursor","grab");u(this,"panning",!1);u(this,"last",{x:0,y:0})}onPointerDown(t,e){this.panning=!0,this.last={x:t.clientX,y:t.clientY},e.setCursor("grabbing")}onPointerMove(t,e){if(!this.panning)return;const n=e.state.viewport;n.offsetX+=t.clientX-this.last.x,n.offsetY+=t.clientY-this.last.y,this.last={x:t.clientX,y:t.clientY},e.state.requestRender()}onPointerUp(t,e){this.panning=!1,e.setCursor("grab")}onCancel(t){this.panning=!1,t.setCursor("grab")}}const Ce=["#fef08a","#fdba74","#fca5a5","#f9a8d4","#d8b4fe","#a5b4fc","#93c5fd","#86efac"];let cs=Ce[0];function Qi(){return cs}function to(i){cs=i}class eo{constructor(){u(this,"name","sticky");u(this,"cursor","crosshair")}onPointerDown(t,e){const{state:n,commands:s}=e,o=L(t.clientX,t.clientY,n.viewport),a=H("sticky",{x:o.x-110,y:o.y-80,width:220,height:160,zIndex:n.maxZIndex()+1,style:{fill:Qi(),borderRadius:12,shadow:!0,fontSize:15,textColor:"#1f2933"},data:{text:""}});s.execute("create-sticky",()=>{n.elementsById.has(a.id)||n.addElement(a),n.emitChange()},()=>{n.removeElement(a.id),n.emitChange()}),n.clearSelection(),n.selectedIds.add(a.id),e.setTool("select"),e.openTextEditor(a)}onPointerMove(t,e){}onPointerUp(t,e){}}class no{constructor(){u(this,"name","task");u(this,"cursor","crosshair")}onPointerDown(t,e){const{state:n,commands:s}=e,o=L(t.clientX,t.clientY,n.viewport),a=n.theme==="dark",r=H("task",{x:o.x-140,y:o.y-28,width:280,height:56,zIndex:n.maxZIndex()+1,style:{fill:a?"#17191b":"#ffffff",stroke:a?"rgba(255,255,255,0.12)":"rgba(0,0,0,0.09)",strokeWidth:1,borderRadius:14,shadow:!0,fontSize:15,fontWeight:"650",textColor:a?"#f6f6f3":"#252829"},data:{text:"Task title",checked:!1}});s.execute("create-task",()=>{n.elementsById.has(r.id)||n.addElement(r),n.emitChange()},()=>{n.removeElement(r.id),n.emitChange()}),n.clearSelection(),n.selectedIds.add(r.id),e.setTool("select"),e.openTextEditor(r)}onPointerMove(t,e){}onPointerUp(t,e){}}class ct{constructor(t){u(this,"name");u(this,"cursor","crosshair");u(this,"shapeType");u(this,"shapeKind");u(this,"drawing",!1);u(this,"startWorld",{x:0,y:0});u(this,"preview",null);this.name=t,this.shapeKind=t,this.shapeType=t==="ellipse"?"ellipse":"rectangle"}onPointerDown(t,e){const{state:n}=e;this.drawing=!0,this.startWorld=L(t.clientX,t.clientY,n.viewport);const s=n.theme==="dark",o=!["rectangle","ellipse","pill","cylinder"].includes(this.shapeKind);this.preview=H(this.shapeType,{x:this.startWorld.x,y:this.startWorld.y,width:0,height:0,zIndex:n.maxZIndex()+1,style:{fill:o?"#0078d4":s?"#2a2d33":"#ffffff",stroke:o?"#495057":s?"#9ca3af":"#374151",strokeWidth:o?0:1.5,borderRadius:this.shapeKind==="pill"?999:this.shapeKind==="rectangle"?8:0,fontSize:14,textColor:o?"#ffffff":s?"#f2f2f2":"#1f2933"},data:{text:"",shapeKind:this.shapeKind}}),e.renderer.overlay.preview=this.preview}onPointerMove(t,e){if(!this.drawing||!this.preview)return;const n=L(t.clientX,t.clientY,e.state.viewport);let s=n.x-this.startWorld.x,o=n.y-this.startWorld.y;if(t.shiftKey){const a=Math.max(Math.abs(s),Math.abs(o));s=Math.sign(s||1)*a,o=Math.sign(o||1)*a}this.preview.x=Math.min(this.startWorld.x,this.startWorld.x+s),this.preview.y=Math.min(this.startWorld.y,this.startWorld.y+o),this.preview.width=Math.abs(s),this.preview.height=Math.abs(o),e.state.requestRender()}onPointerUp(t,e){const{state:n,commands:s}=e;if(!this.drawing||!this.preview)return;this.drawing=!1,e.renderer.overlay.preview=null;const o=this.preview;this.preview=null,(o.width<4||o.height<4)&&(o.width=this.shapeKind==="chevron"?210:160,o.height=this.shapeKind==="chevron"?76:100,o.x-=o.width/2,o.y-=o.height/2),s.execute("create-shape",()=>{n.elementsById.has(o.id)||n.addElement(o),n.emitChange()},()=>{n.removeElement(o.id),n.emitChange()}),n.clearSelection(),n.selectedIds.add(o.id),e.setTool("select")}onCancel(t){this.drawing=!1,this.preview=null,t.renderer.overlay.preview=null,t.state.requestRender()}}class so{constructor(){u(this,"name","text");u(this,"cursor","text")}onPointerDown(t,e){const{state:n,commands:s}=e,o=L(t.clientX,t.clientY,n.viewport),a=H("text",{x:o.x,y:o.y-12,width:240,height:28,zIndex:n.maxZIndex()+1,style:{fontSize:16,textColor:n.theme==="dark"?"#f2f2f2":"#1f2933"},data:{text:""}});It(a),s.execute("create-text",()=>{n.elementsById.has(a.id)||n.addElement(a),n.emitChange()},()=>{n.removeElement(a.id),n.emitChange()}),n.clearSelection(),n.selectedIds.add(a.id),e.setTool("select"),e.openTextEditor(a)}onPointerMove(t,e){}onPointerUp(t,e){}}class Gt{constructor(t="arrow"){u(this,"name");u(this,"cursor","crosshair");u(this,"drawing",!1);u(this,"startWorld",{x:0,y:0});u(this,"preview",null);this.name=t}onPointerDown(t,e){const{state:n}=e;this.drawing=!0,this.startWorld=L(t.clientX,t.clientY,n.viewport),this.preview=H("arrow",{x:this.startWorld.x,y:this.startWorld.y,width:0,height:0,zIndex:n.maxZIndex()+1,style:{stroke:n.theme==="dark"?"#d1d5db":"#374151",strokeWidth:2,arrowEnd:!0,arrowStart:this.name==="twoWayArrow",strokeDash:this.name==="dashedArrow"||t.altKey?[8,6]:void 0},data:{text:"",labelPosition:this.name==="sidedLabelArrow"?"sides":"center"}}),e.renderer.overlay.preview=this.preview}onPointerMove(t,e){if(!this.drawing||!this.preview)return;const n=L(t.clientX,t.clientY,e.state.viewport);let s=n.x-this.startWorld.x,o=n.y-this.startWorld.y;if(t.shiftKey){const a=Math.atan2(o,s),r=Math.round(a/(Math.PI/4))*(Math.PI/4),c=Math.hypot(s,o);s=Math.cos(r)*c,o=Math.sin(r)*c}this.preview.width=s,this.preview.height=o,e.state.requestRender()}onPointerUp(t,e){const{state:n,commands:s}=e;if(!this.drawing||!this.preview)return;this.drawing=!1,e.renderer.overlay.preview=null;const o=this.preview;if(this.preview=null,Math.hypot(o.width,o.height)<8){n.requestRender();return}s.execute("create-arrow",()=>{n.elementsById.has(o.id)||n.addElement(o),n.emitChange()},()=>{n.removeElement(o.id),n.emitChange()}),n.clearSelection(),n.selectedIds.add(o.id),e.setTool("select"),(this.name==="labeledArrow"||this.name==="sidedLabelArrow")&&e.openTextEditor(o)}onCancel(t){this.drawing=!1,this.preview=null,t.renderer.overlay.preview=null,t.state.requestRender()}}class io{constructor(t){u(this,"name","image");u(this,"cursor","default");u(this,"onInsert");this.onInsert=t}onActivate(t){const e=document.createElement("input");e.type="file",e.accept="image/png,image/jpeg,image/webp,image/gif",e.multiple=!0,e.onchange=()=>{e.files&&e.files.length>0&&this.onInsert(e.files,window.innerWidth/2,window.innerHeight/2),t.setTool("select")},e.oncancel=()=>t.setTool("select"),e.click()}onPointerDown(t,e){}onPointerMove(t,e){}onPointerUp(t,e){}}const pe="psc:";class oo{constructor(){u(this,"lastSaveMs",0)}async saveBoard(t){const e=performance.now();try{await K.put("boards",t),this.lastSaveMs=performance.now()-e,this.setPref("lastBoardId",t.id),this.setPref("recovery",JSON.stringify({id:t.id,title:t.title,updatedAt:t.updatedAt,elementCount:t.elements.length}))}catch(n){throw console.error("Board save failed",n),n}}async loadBoard(t){try{return await K.get("boards",t)}catch(e){console.error("Board load failed",e);return}}async deleteBoard(t){await K.delete("boards",t)}async clearAllBoards(){await K.clearStore("boards")}async getAllBoards(){try{return await K.getAll("boards")}catch{return[]}}setPref(t,e){try{localStorage.setItem(pe+t,e)}catch{}}getPref(t){try{return localStorage.getItem(pe+t)}catch{return null}}removePref(t){try{localStorage.removeItem(pe+t)}catch{}}clearPrefs(){try{const t=[];for(let e=0;e<localStorage.length;e++){const n=localStorage.key(e);n&&n.startsWith(pe)&&t.push(n)}for(const e of t)localStorage.removeItem(e)}catch{}}async estimateStorage(){try{if(navigator.storage&&navigator.storage.estimate){const t=await navigator.storage.estimate();return{usage:t.usage??0,quota:t.quota??0}}}catch{}return{usage:0,quota:0}}}const tt=new oo;function ao(i){return new Promise((t,e)=>{const n=new FileReader;n.onload=()=>{const s=n.result;t(s.slice(s.indexOf(",")+1))},n.onerror=()=>e(n.error),n.readAsDataURL(i)})}function ro(i,t){const e=atob(i),n=new Uint8Array(e.length);for(let s=0;s<e.length;s++)n[s]=e.charCodeAt(s);return new Blob([n],{type:t})}function gn(i,t){const e=new Blob([JSON.stringify(t)],{type:"application/json"}),n=URL.createObjectURL(e),s=document.createElement("a");s.href=n,s.download=i,s.click(),URL.revokeObjectURL(n)}function yn(i){return i.replace(/[^a-z0-9-_ ]/gi,"").trim().slice(0,60)||"board"}class co{exportBoardJson(t){gn(`${yn(t.title)}.json`,t)}async exportFullBundle(t){const e=[];for(const s of t.assetIds){const o=await it.get(s);o&&e.push({id:o.id,mimeType:o.mimeType,width:o.width,height:o.height,name:o.name,base64:await ao(o.blob)})}const n={bundleVersion:1,board:t,assets:e};gn(`${yn(t.title)}-bundle.json`,n)}async importFromFile(t){let e;try{e=JSON.parse(await t.text())}catch{return null}if(typeof e!="object"||e===null)return null;const n=e;if(n.bundleVersion===1&&Array.isArray(n.assets)){const s=we(n.board);if(!s)return null;const o=new Map;for(const a of n.assets){const r=await this.importAsset(a);r&&o.set(r.originalId,r.asset.id)}for(const a of s.elements){const r=a.data.assetId;typeof r=="string"&&o.has(r)&&(a.data.assetId=o.get(r))}return s.assetIds=[...o.values()],s}return we(e)}async importAsset(t){if(typeof t!="object"||t===null)return null;const e=t;if(typeof e.id!="string"||typeof e.base64!="string"||typeof e.mimeType!="string"||!e.mimeType.startsWith("image/"))return null;try{const n=ro(e.base64,e.mimeType),s=Date.now(),o={id:ae(),type:"image",mimeType:e.mimeType,blob:n,width:typeof e.width=="number"?e.width:0,height:typeof e.height=="number"?e.height:0,sizeBytes:n.size,createdAt:s,updatedAt:s,name:typeof e.name=="string"?e.name.slice(0,100):void 0};return await it.put(o),{originalId:e.id,asset:o}}catch{return null}}}const Ne=new co,V={select:'<svg viewBox="0 0 24 24"><path d="M4 3l7 18 2.5-7.5L21 11z"/></svg>',hand:'<svg viewBox="0 0 24 24"><path d="M18 11V6.5a1.5 1.5 0 0 0-3 0V11m0-1V4.5a1.5 1.5 0 0 0-3 0V10m0 .5v-5a1.5 1.5 0 0 0-3 0V12m9-1v1.5a1.5 1.5 0 0 1 3 0V14c0 4-2.5 7-6.5 7S8 18.5 6 15l-1.8-3.2a1.4 1.4 0 0 1 2.3-1.5L8 12V7"/></svg>',sticky:'<svg viewBox="0 0 24 24"><path d="M5 4h14a1 1 0 0 1 1 1v9l-6 6H5a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1z"/><path d="M14 20v-5a1 1 0 0 1 1-1h5"/></svg>',task:'<svg viewBox="0 0 24 24"><rect x="4" y="5" width="16" height="14" rx="3"/><path d="M8 12l2.3 2.3L16 8.8"/></svg>',rectangle:'<svg viewBox="0 0 24 24"><rect x="4" y="5" width="16" height="14" rx="2"/></svg>',ellipse:'<svg viewBox="0 0 24 24"><ellipse cx="12" cy="12" rx="8.5" ry="7"/></svg>',diamond:'<svg viewBox="0 0 24 24"><path d="M12 3l8 9-8 9-8-9z"/></svg>',triangle:'<svg viewBox="0 0 24 24"><path d="M12 4l9 16H3z"/></svg>',pill:'<svg viewBox="0 0 24 24"><rect x="3.5" y="7" width="17" height="10" rx="5"/></svg>',chevron:'<svg viewBox="0 0 24 24"><path d="M4 6h12l5 6-5 6H4l5-6z"/></svg>',parallelogram:'<svg viewBox="0 0 24 24"><path d="M7 5h13l-3 14H4z"/></svg>',hexagon:'<svg viewBox="0 0 24 24"><path d="M8 4h8l5 8-5 8H8l-5-8z"/></svg>',cylinder:'<svg viewBox="0 0 24 24"><ellipse cx="12" cy="6" rx="7" ry="3"/><path d="M5 6v11c0 1.7 3.1 3 7 3s7-1.3 7-3V6"/></svg>',arrowDashed:'<svg viewBox="0 0 24 24"><path stroke-dasharray="4 3" d="M4 12h14m0 0l-5-5m5 5l-5 5"/></svg>',arrowBoth:'<svg viewBox="0 0 24 24"><path d="M4 12h16m-16 0l5-5m-5 5l5 5m11-5l-5-5m5 5l-5 5"/></svg>',arrowLabel:'<svg viewBox="0 0 24 24"><path d="M3 17L17 7m0 0h-6m6 0v6"/><rect x="7" y="9" width="10" height="6" rx="1"/></svg>',text:'<svg viewBox="0 0 24 24"><path d="M5 6V4h14v2M12 4v16m-3 0h6"/></svg>',arrow:'<svg viewBox="0 0 24 24"><path d="M5 19L19 5m0 0h-7m7 0v7"/></svg>',image:'<svg viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="16" rx="2"/><circle cx="9" cy="10" r="1.6"/><path d="M21 16l-5-5-9 9"/></svg>',templates:'<svg viewBox="0 0 24 24"><rect x="3" y="3" width="8" height="8" rx="1.5"/><rect x="13" y="3" width="8" height="8" rx="1.5"/><rect x="3" y="13" width="8" height="8" rx="1.5"/><rect x="13" y="13" width="8" height="8" rx="1.5"/></svg>',shapes:'<svg viewBox="0 0 24 24"><rect x="4" y="5" width="7" height="7" rx="1.5"/><circle cx="16.5" cy="8.5" r="3.5"/><path d="M7.5 14l3.8 6H3.7z"/><path d="M15 14h4l2 3-2 3h-4l2-3z"/></svg>',sun:'<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="4"/><path d="M12 2v2m0 16v2M4.9 4.9l1.4 1.4m11.4 11.4l1.4 1.4M2 12h2m16 0h2M4.9 19.1l1.4-1.4m11.4-11.4l1.4-1.4"/></svg>',moon:'<svg viewBox="0 0 24 24"><path d="M21 12.8A9 9 0 1 1 11.2 3 7 7 0 0 0 21 12.8z"/></svg>',export:'<svg viewBox="0 0 24 24"><path d="M12 15V3m0 0L7 8m5-5l5 5M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2"/></svg>',import:'<svg viewBox="0 0 24 24"><path d="M12 3v12m0 0l-5-5m5 5l5-5M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2"/></svg>',gauge:'<svg viewBox="0 0 24 24"><path d="M12 14l4-4M3.3 14a9 9 0 1 1 17.4 0"/></svg>',storage:'<svg viewBox="0 0 24 24"><ellipse cx="12" cy="5.5" rx="8" ry="2.5"/><path d="M4 5.5V12c0 1.4 3.6 2.5 8 2.5s8-1.1 8-2.5V5.5M4 12v6.5C4 19.9 7.6 21 12 21s8-1.1 8-2.5V12"/></svg>',flask:'<svg viewBox="0 0 24 24"><path d="M9 3h6M10 3v6L4.5 18a2 2 0 0 0 1.8 3h11.4a2 2 0 0 0 1.8-3L14 9V3"/><path d="M7 15h10"/></svg>',reset:'<svg viewBox="0 0 24 24"><path d="M3 12a9 9 0 1 0 2.6-6.3M3 4v5h5"/></svg>',camera:'<svg viewBox="0 0 24 24"><path d="M3 8a2 2 0 0 1 2-2h2l2-2h6l2 2h2a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><circle cx="12" cy="13" r="3.5"/></svg>',trash:'<svg viewBox="0 0 24 24"><path d="M4 7h16M10 11v6m4-6v6M6 7l1 13a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1l1-13M9 7V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v3"/></svg>',embed:'<svg viewBox="0 0 24 24"><rect x="3" y="5" width="18" height="14" rx="2" stroke-dasharray="3 3"/><path d="M9 10l-2 2 2 2m6-4l2 2-2 2"/></svg>',arrowLeft:'<svg viewBox="0 0 24 24"><path d="M19 12H5m0 0l6-6m-6 6l6 6"/></svg>',search:'<svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="M20 20l-4-4"/></svg>',panelLeft:'<svg viewBox="0 0 24 24"><rect x="4" y="4" width="16" height="16" rx="2"/><path d="M10 4v16"/></svg>',edit:'<svg viewBox="0 0 24 24"><path d="M12 20h9"/><path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4z"/></svg>',copy:'<svg viewBox="0 0 24 24"><rect x="8" y="8" width="11" height="11" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v1"/></svg>',folder:'<svg viewBox="0 0 24 24"><path d="M4 7a2 2 0 0 1 2-2h4l2 2h6a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2z"/></svg>',folderOpen:'<svg viewBox="0 0 24 24"><path d="M4 7a2 2 0 0 1 2-2h4l2 2h6a2 2 0 0 1 2 2v1M4 8l1.5 9c.1.6.6 1 1.2 1h10.6c.6 0 1.1-.4 1.2-1L20 8z"/></svg>',file:'<svg viewBox="0 0 24 24"><path d="M13 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V10z"/><path d="M13 3v7h7"/></svg>',fileText:'<svg viewBox="0 0 24 24"><path d="M13 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V10z"/><path d="M13 3v7h7M10 13h4m-4 4h4"/></svg>',board:'<svg viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="12" y="3" width="9" height="7" rx="1"/><rect x="3" y="12" width="9" height="9" rx="1"/><rect x="14" y="12" width="7" height="9" rx="1"/></svg>',doc:'<svg viewBox="0 0 24 24"><path d="M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/><path d="M14 3v6h6M10 13h4m-4 4h4m-4-8h1"/></svg>',skill:'<svg viewBox="0 0 24 24"><path d="M12 2l3.1 6.3 6.9 1-5 4.9 1.2 6.8-6.2-3.3-6.2 3.3 1.2-6.8-5-4.9 6.9-1z"/></svg>',plus:'<svg viewBox="0 0 24 24"><path d="M12 5v14m-7-7h14"/></svg>',moreVertical:'<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg>',chevronRight:'<svg viewBox="0 0 24 24"><path d="M9 18l6-6-6-6"/></svg>',chevronDown:'<svg viewBox="0 0 24 24"><path d="M6 9l6 6 6-6"/></svg>',wiki:'<svg viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6M8 13h2m-2 4h6m-6-8h1"/></svg>',list:'<svg viewBox="0 0 24 24"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><circle cx="4" cy="6" r="1"/><circle cx="4" cy="12" r="1"/><circle cx="4" cy="18" r="1"/></svg>'},lo=[{tool:"select",icon:"select",label:"Select",key:"V"},{tool:"hand",icon:"hand",label:"Hand",key:"H"},{tool:"sticky",icon:"sticky",label:"Sticky note",key:"S"},{tool:"task",icon:"task",label:"Task",key:""},{tool:"text",icon:"text",label:"Text",key:"T"},{tool:"image",icon:"image",label:"Image",key:"I"}],ls=[{tool:"rectangle",icon:"rectangle",label:"Rectangle",key:"R"},{tool:"ellipse",icon:"ellipse",label:"Ellipse",key:"O"},{tool:"diamond",icon:"diamond",label:"Diamond",key:""},{tool:"triangle",icon:"triangle",label:"Triangle",key:""},{tool:"pill",icon:"pill",label:"Pill",key:""},{tool:"chevron",icon:"chevron",label:"Chevron",key:""},{tool:"parallelogram",icon:"parallelogram",label:"Parallelogram",key:""},{tool:"hexagon",icon:"hexagon",label:"Hexagon",key:""},{tool:"cylinder",icon:"cylinder",label:"Cylinder",key:""}],ho=[{tool:"arrow",icon:"arrow",label:"Arrow (hold Shift to snap)",key:"A"},{tool:"dashedArrow",icon:"arrowDashed",label:"Dashed arrow",key:""},{tool:"twoWayArrow",icon:"arrowBoth",label:"Two-way arrow",key:""},{tool:"labeledArrow",icon:"arrowLabel",label:"Centered label arrow",key:""},{tool:"sidedLabelArrow",icon:"arrowLabel",label:"Left and right labels",key:""}],po=new Set(ls.map(i=>i.tool));class uo{constructor(t,e){u(this,"element");u(this,"toolButtons",new Map);u(this,"shapeButtons",new Map);u(this,"shapeBtn");u(this,"shapeGroup");u(this,"arrowGroup");u(this,"themeBtn");this.element=document.createElement("div"),this.element.className="toolbar";for(const a of lo){const r=this.makeButton(a.icon,a.label,a.key);r.addEventListener("click",()=>e.setTool(a.tool)),this.toolButtons.set(a.tool,r),this.element.appendChild(r),a.tool==="sticky"&&this.element.appendChild(this.makeShapePicker(e)),a.tool==="text"&&this.element.appendChild(this.makeArrowPicker(e))}this.element.appendChild(this.divider());const n=this.makeButton("templates","Templates","");n.addEventListener("click",e.openTemplates),this.element.appendChild(n),this.element.appendChild(this.divider()),this.themeBtn=this.makeButton("moon","Toggle theme",""),this.themeBtn.addEventListener("click",e.toggleTheme),this.element.appendChild(this.themeBtn);const s=this.makeButton("export","Export / Import","");s.addEventListener("click",e.openExportMenu),this.element.appendChild(s);const o=this.makeButton("flask","Stress test","");if(o.addEventListener("click",e.toggleStressPanel),this.element.appendChild(o),e.copyEmbed){this.element.appendChild(this.divider());const a=this.makeButton("embed","Copy embed of a region","");a.addEventListener("click",e.copyEmbed),this.element.appendChild(a)}t.appendChild(this.element)}makeButton(t,e,n){const s=document.createElement("button");s.className="tool-btn",s.setAttribute("aria-label",e),s.innerHTML=V[t]??"";const o=document.createElement("span");if(o.className="tooltip",o.textContent=e,n){const a=document.createElement("kbd");a.textContent=n,o.appendChild(a)}return s.appendChild(o),s}makeShapePicker(t){this.shapeGroup=document.createElement("div"),this.shapeGroup.className="shape-tool-group",this.shapeBtn=this.makeButton("shapes","Shapes",""),this.shapeBtn.classList.add("shape-tool-trigger"),this.shapeBtn.addEventListener("click",n=>{n.stopPropagation(),this.shapeGroup.classList.toggle("open")});const e=document.createElement("div");e.className="shape-tool-popover";for(const n of ls){const s=this.makeButton(n.icon,n.label,n.key);s.classList.add("shape-option-btn"),s.addEventListener("click",o=>{o.stopPropagation(),t.setTool(n.tool),this.shapeGroup.classList.remove("open")}),this.shapeButtons.set(n.tool,s),e.appendChild(s)}return this.shapeGroup.append(this.shapeBtn,e),this.shapeGroup}makeArrowPicker(t){this.arrowGroup=document.createElement("div"),this.arrowGroup.className="shape-tool-group";const e=this.makeButton("arrow","Arrow types","");e.classList.add("shape-tool-trigger"),e.addEventListener("click",s=>{s.stopPropagation(),this.arrowGroup.classList.toggle("open")});const n=document.createElement("div");n.className="shape-tool-popover";for(const s of ho){const o=this.makeButton(s.icon,s.label,s.key);o.classList.add("shape-option-btn"),o.addEventListener("click",a=>{a.stopPropagation(),t.setTool(s.tool),this.arrowGroup.classList.remove("open")}),this.toolButtons.set(s.tool,o),n.appendChild(o)}return this.arrowGroup.append(e,n),this.arrowGroup}divider(){const t=document.createElement("div");return t.className="toolbar-divider",t}setActiveTool(t){var e;for(const[n,s]of this.toolButtons)s.classList.toggle("active",n===t);for(const[n,s]of this.shapeButtons)s.classList.toggle("active",n===t);(e=this.shapeBtn)==null||e.classList.toggle("active",po.has(t))}setTheme(t){const e=this.themeBtn.querySelector(".tooltip");this.themeBtn.innerHTML=V[t==="dark"?"sun":"moon"],e&&this.themeBtn.appendChild(e)}}class mo{constructor(t,e,n){u(this,"zoomLabel");u(this,"titleInput");u(this,"titleMeasure");u(this,"statusEl");u(this,"statusTextEl");u(this,"statusActionBtn");const s=document.createElement("div");if(s.className="top-left",e.onExit){const m=document.createElement("button");m.className="control-btn back-btn",m.innerHTML=`${V.arrowLeft}<span>ViteBoard</span>`,m.title="Save and back to ViteBoard",m.setAttribute("aria-label","Back to ViteBoard"),m.addEventListener("click",e.onExit),s.appendChild(m)}this.titleInput=document.createElement("input"),this.titleInput.className="board-title",this.titleInput.value=n,this.titleInput.maxLength=120,this.titleInput.setAttribute("aria-label","Board title"),this.titleInput.addEventListener("change",()=>e.onTitleChange(this.titleInput.value.trim()||"Untitled Board")),this.titleInput.addEventListener("input",()=>this.resizeTitleInput()),this.titleInput.addEventListener("keydown",m=>{m.stopPropagation(),m.key==="Enter"&&this.titleInput.blur()}),this.titleMeasure=document.createElement("span"),this.titleMeasure.className="board-title-measure",s.appendChild(this.titleInput),s.appendChild(this.titleMeasure),t.appendChild(s),this.resizeTitleInput();const o=document.createElement("div");o.className="top-right";const a=document.createElement("div");a.className="control-group";const r=this.btn("−","Zoom out (Ctrl/Cmd + wheel or Ctrl/Cmd + -)");r.addEventListener("click",e.zoomOut),a.appendChild(r),this.zoomLabel=this.btn("100%","Reset zoom to 100% (Ctrl/Cmd + 0)"),this.zoomLabel.classList.add("zoom-label"),this.zoomLabel.addEventListener("click",e.resetView),a.appendChild(this.zoomLabel);const c=this.btn("+","Zoom in (Ctrl/Cmd + wheel or Ctrl/Cmd + +)");c.addEventListener("click",e.zoomIn),a.appendChild(c),o.appendChild(a);const l=document.createElement("div");l.className="control-group";const d=this.iconBtn("reset","Reset view");d.addEventListener("click",e.resetView),l.appendChild(d);const h=this.iconBtn("storage","Storage manager");h.addEventListener("click",e.openStorageManager),l.appendChild(h);const p=this.iconBtn("gauge","Performance overlay");if(p.addEventListener("click",e.togglePerfOverlay),l.appendChild(p),e.deleteBoard){const m=this.iconBtn("trash","Delete board");m.classList.add("danger-control"),m.addEventListener("click",e.deleteBoard),l.appendChild(m)}o.appendChild(l),this.statusEl=document.createElement("div"),this.statusEl.className="board-live-status",this.statusEl.hidden=!0,this.statusTextEl=document.createElement("span"),this.statusActionBtn=document.createElement("button"),this.statusActionBtn.className="status-action-btn",this.statusActionBtn.hidden=!0,this.statusEl.append(this.statusTextEl,this.statusActionBtn),o.appendChild(this.statusEl),t.appendChild(o)}btn(t,e){const n=document.createElement("button");return n.className="control-btn",n.textContent=t,n.title=e,n.setAttribute("aria-label",e),n}iconBtn(t,e){const n=document.createElement("button");return n.className="control-btn",n.innerHTML=V[t]??"",n.title=e,n.setAttribute("aria-label",e),n}setZoom(t){this.zoomLabel.textContent=Math.round(t*100)+"%"}setTitle(t){this.titleInput.value=t,this.resizeTitleInput()}setStatus(t,e,n){this.statusEl.hidden=!t,this.statusTextEl.textContent=t,this.statusEl.dataset.state=e,this.statusActionBtn.hidden=!n,this.statusActionBtn.textContent=(n==null?void 0:n.label)??"",this.statusActionBtn.onclick=(n==null?void 0:n.onClick)??null}resizeTitleInput(){if(!this.titleMeasure||!this.titleInput)return;this.titleMeasure.textContent=this.titleInput.value||"Untitled Board";const t=Math.ceil(this.titleMeasure.getBoundingClientRect().width+22),e=Math.max(180,window.innerWidth-460);this.titleInput.style.width=Math.min(Math.max(90,t),e)+"px"}}class fo{constructor(t,e){u(this,"state");u(this,"commands");u(this,"textareas",[]);u(this,"editingId",null);this.state=t,this.commands=e}get isEditing(){return this.editingId!==null}open(t){if(this.close(),this.editingId=t.id,t.data.labelPosition==="sides"){this.openSideEditors(t);return}const e=document.createElement("textarea");e.className="text-editor",e.value=t.data.text??"",t.data.labelPosition==="sides"&&(e.placeholder="left label | right label"),this.position(e,t),document.body.appendChild(e),this.textareas=[e],e.focus(),e.select();const n=t.data.text??"";t.data.text="",t.updatedAt=Date.now(),X(t.id),this.state.requestRender();const s=()=>{const o=e.value;if(t.data.text=o,It(t),t.updatedAt=Date.now(),X(t.id),o!==n){const a=t.id,r=this.state;this.commands.push("edit-text",()=>{const c=r.elementsById.get(a);c&&(c.data.text=o,It(c),c.updatedAt=Date.now(),X(a),r.emitChange())},()=>{const c=r.elementsById.get(a);c&&(c.data.text=n,It(c),c.updatedAt=Date.now(),X(a),r.emitChange())})}this.state.emitChange(),this.close()};e.addEventListener("blur",s),e.addEventListener("keydown",o=>{o.stopPropagation(),o.key==="Escape"?(t.data.text=n,X(t.id),this.close(),this.state.requestRender()):o.key==="Enter"&&(o.metaKey||o.ctrlKey)&&e.blur()})}openSideEditors(t){const e=String(t.data.text??""),[n="",s=""]=e.split("|",2).map(d=>d.trim()),o=this.makeSideEditor("Left label",n),a=this.makeSideEditor("Right label",s);this.positionSideEditors(o,a,t),document.body.append(o,a),this.textareas=[o,a],o.focus(),o.select(),t.data.text="",t.updatedAt=Date.now(),X(t.id),this.state.requestRender();let r=!1;const c=()=>{if(r)return;r=!0;const d=`${o.value.trim()} | ${a.value.trim()}`;t.data.text=d,t.updatedAt=Date.now(),X(t.id),d!==e&&this.pushTextCommand(t.id,e,d),this.state.emitChange(),this.close()},l=()=>{r||(r=!0,t.data.text=e,X(t.id),this.close(),this.state.requestRender())};for(const d of[o,a])d.addEventListener("keydown",h=>{h.stopPropagation(),h.key==="Escape"?l():h.key==="Enter"&&(h.metaKey||h.ctrlKey)&&c()}),d.addEventListener("blur",()=>{window.setTimeout(()=>{this.textareas.includes(document.activeElement)||c()},0)})}makeSideEditor(t,e){const n=document.createElement("textarea");return n.className="text-editor text-editor-side",n.placeholder=t,n.value=e,n.style.fontFamily='-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',n.style.fontWeight="600",n.style.textAlign="center",n.style.lineHeight="1.35",n.style.color=this.state.theme==="dark"?"#f2f2f2":"#1f2933",n}positionSideEditors(t,e,n){const s=this.state.viewport,o=Rt(n.x,n.y,s),a=Rt(n.x+n.width,n.y+n.height,s),r=13*s.scale,c=Math.max(82,Math.min(150,Math.hypot(a.x-o.x,a.y-o.y)*.34)),l=Math.max(24,r*1.35+4);for(const[d,h]of[[t,.24],[e,.76]])d.style.left=o.x+(a.x-o.x)*h-c/2+"px",d.style.top=o.y+(a.y-o.y)*h-l/2+"px",d.style.width=c+"px",d.style.height=l+"px",d.style.fontSize=r+"px"}pushTextCommand(t,e,n){const s=this.state;this.commands.push("edit-text",()=>{const o=s.elementsById.get(t);o&&(o.data.text=n,It(o),o.updatedAt=Date.now(),X(t),s.emitChange())},()=>{const o=s.elementsById.get(t);o&&(o.data.text=e,It(o),o.updatedAt=Date.now(),X(t),s.emitChange())})}position(t,e){const n=this.state.viewport;if(e.type==="arrow"||e.type==="line"||e.type==="connector"){const b=Rt(e.x,e.y,n),x=Rt(e.x+e.width,e.y+e.height,n),v=13*n.scale,S=Math.max(100,Math.min(240,Math.hypot(x.x-b.x,x.y-b.y)-16)),w=Math.max(24,v*1.35+4);t.style.left=(b.x+x.x)/2-S/2+"px",t.style.top=(b.y+x.y)/2-w/2+"px",t.style.width=S+"px",t.style.height=w+"px",t.style.fontSize=v+"px",t.style.lineHeight="1.35",t.style.fontFamily='-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',t.style.fontWeight="600",t.style.color=this.state.theme==="dark"?"#f2f2f2":"#1f2933",t.style.textAlign="center";return}const s=Rt(e.x,e.y,n),o=e.data.shapeKind==="chevron",a=Te(e)*n.scale,r=o?Math.min(Math.abs(e.width)*.18,Math.abs(e.height)*.55)+8:10,c=(e.type==="sticky"?14:e.type==="task"?50:e.type==="text"?0:r)*n.scale,l=e.type!=="text"&&e.type!=="task",d=o?e.height*n.scale:e.height*n.scale-(e.type==="sticky"?28:l?r*2:0)*n.scale,h=Math.max(1,String(e.data.text??"").split(/\r?\n/).length),p=Math.max(24,a*(o?1.18:1.35)*h+4),m=l?Math.min(Math.max(24,d),p):Math.max(24,d),g=l?Math.max(0,(e.height*n.scale-m)/2):e.type==="sticky"?14*n.scale:0,y=(e.type==="task"?16:e.type==="sticky"?14:e.type==="text"?0:r)*n.scale;t.style.left=s.x+c+"px",t.style.top=s.y+g+"px",t.style.width=Math.max(40,e.width*n.scale-c-y)+"px",t.style.height=m+"px",t.style.fontSize=a+"px",t.style.lineHeight=o?"1.18":"1.35",t.style.fontFamily=e.style.fontFamily??'-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',t.style.fontWeight=o?"750":e.style.fontWeight??"400",t.style.color=e.style.textColor??(this.state.theme==="dark"?"#f2f2f2":"#1f2933"),t.style.textAlign=e.type==="text"||e.type==="task"?"left":"center"}close(){if(this.textareas.length){const t=this.textareas;this.textareas=[],this.editingId=null;for(const e of t)e.remove()}}}function _(i,t){const e=document.createElement("div");e.className="modal-backdrop";const n=document.createElement("div");n.className="modal",n.setAttribute("role","dialog"),n.setAttribute("aria-label",i);const s=document.createElement("button");s.className="modal-close",s.textContent="✕",s.setAttribute("aria-label","Close");const o=document.createElement("h2");o.textContent=i;const a=document.createElement("div"),r=()=>e.remove();s.addEventListener("click",r),e.addEventListener("click",c=>{c.target===e&&r()}),e.addEventListener("keydown",c=>{c.stopPropagation(),c.key==="Escape"&&r()}),n.appendChild(s),n.appendChild(o),n.appendChild(a),e.appendChild(n),document.body.appendChild(e),t(a,r),s.focus()}let lt=null,ze=null;function C(i){lt||(lt=document.createElement("div"),lt.className="toast",document.body.appendChild(lt)),lt.textContent=i,lt.classList.add("visible"),ze!==null&&clearTimeout(ze),ze=window.setTimeout(()=>{lt==null||lt.classList.remove("visible")},2600)}function gt(i){return i==="dark"?{card:"#1e2126",cardStroke:"#3a3f46",heading:"#f2f2f2",text:"#c8cdd4",accent:"#4dabf7",sticky:"#fef08a",zone:"#22262c"}:{card:"#ffffff",cardStroke:"#d8dade",heading:"#1f2933",text:"#4b5563",accent:"#6e6e6e",sticky:"#fef08a",zone:"#f0f0ed"}}let Yt=0;function yt(i){Yt=i}function N(i,t,e,n,s,o=""){return H("rectangle",{x:i,y:t,width:e,height:n,zIndex:++Yt,style:{borderRadius:10,...s},data:{text:o}})}function I(i,t,e,n,s,o,a="600"){return H("text",{x:i,y:t,width:e,height:s*1.6,zIndex:++Yt,style:{fontSize:s,textColor:o,fontWeight:a},data:{text:n}})}function W(i,t,e,n,s=180,o=120){return H("sticky",{x:i,y:t,width:s,height:o,zIndex:++Yt,style:{fill:n,borderRadius:12,shadow:!0,fontSize:13,textColor:"#1f2933"},data:{text:e}})}function z(i,t,e,n,s,o={}){return H("arrow",{x:i,y:t,width:e-i,height:n-t,zIndex:++Yt,style:{stroke:s,strokeWidth:2,arrowEnd:!0,arrowStart:o.arrowStart,strokeDash:o.dashed?[8,6]:void 0},data:{text:o.label??"",labelPosition:o.sides?"sides":"center"}})}function go(i,t,e,n,s,o=""){return H("ellipse",{x:i,y:t,width:e,height:n,zIndex:++Yt,style:s,data:{text:o}})}function yo(i,t,e,n){const s=gt(e);yt(n);const o=[];return o.push(N(i,t,760,560,{fill:s.card,stroke:s.cardStroke,strokeWidth:1.5,borderRadius:16,shadow:!0})),o.push(I(i+28,t+22,400,"User Persona",22,s.heading,"700")),o.push(I(i+28,t+60,300,"Name",13,s.accent)),o.push(W(i+28,t+86,"Jordan Lee",s.sticky,200,64)),o.push(I(i+260,t+60,300,"Role",13,s.accent)),o.push(W(i+260,t+86,"Senior Product Manager","#a5b4fc",220,64)),o.push(I(i+510,t+60,300,"Buying trigger",13,s.accent)),o.push(W(i+510,t+86,"Team scaling pains","#fdba74",220,64)),[["Goals","Ship the right features faster","#86efac"],["Pain points","Scattered docs, unclear priorities","#fca5a5"],["Jobs to be done","Align team on what to build next","#93c5fd"],["Current workaround","Spreadsheets + slide decks","#d8b4fe"],["Success criteria","Stakeholders aligned in 1 meeting","#f9a8d4"],["Key quote",'"I need clarity, not more tools."',"#fef08a"]].forEach(([r,c,l],d)=>{const h=d%3,p=Math.floor(d/3),m=i+28+h*242,g=t+188+p*178;o.push(I(m,g,230,r,13,s.accent)),o.push(W(m,g+26,c,l,220,124))}),o}const He=["Awareness","Discovery","Evaluation","Onboarding","Activation","Retention"],bo=["#93c5fd","#a5b4fc","#d8b4fe","#f9a8d4","#fdba74","#86efac"];function vo(i,t,e,n){const s=gt(e);yt(n);const o=[],a=230,r=He.length*a+56;return o.push(N(i,t,r,640,{fill:s.card,stroke:s.cardStroke,strokeWidth:1.5,borderRadius:16,shadow:!0})),o.push(I(i+28,t+22,500,"User Journey Map",22,s.heading,"700")),He.forEach((c,l)=>{const d=i+28+l*a,h=t+70;o.push(N(d,h,a-16,48,{fill:bo[l],borderRadius:10},c)),l<He.length-1&&o.push(z(d+a-14,h+24,d+a-2,h+24,s.heading)),o.push(N(d,h+64,a-16,150,{fill:s.zone,borderRadius:10},"")),o.push(I(d+10,h+70,a-36,"Actions",12,s.accent)),o.push(W(d+14,h+96,"","#fef08a",a-44,100)),o.push(N(d,h+226,a-16,150,{fill:s.zone,borderRadius:10},"")),o.push(I(d+10,h+232,a-36,"Pain points",12,s.accent)),o.push(W(d+14,h+258,"","#fca5a5",a-44,100)),o.push(N(d,h+388,a-16,150,{fill:s.zone,borderRadius:10},"")),o.push(I(d+10,h+394,a-36,"Opportunities",12,s.accent)),o.push(W(d+14,h+420,"","#86efac",a-44,100))}),o}function wo(i,t,e,n){const s=gt(e);yt(n);const o=[],a=680;o.push(N(i,t,a+120,a+130,{fill:s.card,stroke:s.cardStroke,strokeWidth:1.5,borderRadius:16,shadow:!0})),o.push(I(i+28,t+22,500,"Feature Prioritization Matrix",22,s.heading,"700"));const r=i+80,c=t+80,l=a/2;return o.push(N(r,c,l,l,{fill:"#86efac55",borderRadius:8})),o.push(N(r+l,c,l,l,{fill:"#93c5fd55",borderRadius:8})),o.push(N(r,c+l,l,l,{fill:"#fef08a55",borderRadius:8})),o.push(N(r+l,c+l,l,l,{fill:"#fca5a555",borderRadius:8})),o.push(I(r+16,c+12,300,"Quick wins",16,s.heading,"700")),o.push(I(r+l+16,c+12,300,"Strategic bets",16,s.heading,"700")),o.push(I(r+16,c+l+12,300,"Fill-ins",16,s.heading,"700")),o.push(I(r+l+16,c+l+12,300,"Avoid",16,s.heading,"700")),o.push(z(r-24,c+a,r-24,c,s.heading)),o.push(I(r-64,c+l-12,60,"Impact",14,s.text,"600")),o.push(z(r,c+a+24,r+a,c+a+24,s.heading)),o.push(I(r+l-30,c+a+34,80,"Effort",14,s.text,"600")),o.push(W(r+40,c+60,"Feature idea","#fef08a",150,90)),o.push(W(r+l+40,c+60,"Big bet","#a5b4fc",150,90)),o}const bn=[["Problem","#fca5a5"],["User","#93c5fd"],["Context","#d8b4fe"],["Current workflow","#fdba74"],["Proposed solution","#86efac"],["Edge cases","#f9a8d4"],["Acceptance criteria","#a5b4fc"],["Dependencies","#fef08a"],["Risks","#fca5a5"]];function xo(i,t,e,n){const s=gt(e);yt(n);const o=[],a=3,r=280,c=210,l=a*r+56,h=Math.ceil(bn.length/a)*c+100;return o.push(N(i,t,l,h,{fill:s.card,stroke:s.cardStroke,strokeWidth:1.5,borderRadius:16,shadow:!0})),o.push(I(i+28,t+22,500,"Product Requirements Flow",22,s.heading,"700")),bn.forEach(([p,m],g)=>{const y=g%a,b=Math.floor(g/a),x=i+28+y*r,v=t+72+b*c;o.push(I(x,v,r-30,`${g+1}. ${p}`,14,s.accent)),o.push(W(x,v+30,"",m,r-28,c-60))}),o}function Co(i,t,e,n){const s=gt(e);yt(n);const o=[];o.push(I(i,t-40,400,"System Diagram",22,s.heading,"700"));const a=(r,c,l,d,h,p)=>N(r,c,l,d,{fill:h,stroke:s.cardStroke,strokeWidth:1.5,borderRadius:12,shadow:!0,fontSize:15,fontWeight:"600",textColor:"#1f2933"},p);return o.push(go(i,t+130,110,110,{fill:"#f9a8d4",stroke:s.cardStroke,strokeWidth:1.5,fontSize:14,fontWeight:"600",textColor:"#1f2933"},"User")),o.push(a(i+200,t+140,180,90,"#93c5fd","Frontend")),o.push(a(i+470,t+140,180,90,"#a5b4fc","API / Server")),o.push(a(i+740,t+60,180,90,"#86efac","Database")),o.push(a(i+740,t+200,180,90,"#fdba74","External Service")),o.push(a(i+470,t+330,180,90,"#fef08a","Notification Service")),o.push(z(i+115,t+185,i+195,t+185,s.heading,{label:"uses"})),o.push(z(i+385,t+185,i+465,t+185,s.heading,{label:"request | response",sides:!0,arrowStart:!0})),o.push(z(i+655,t+165,i+735,t+110,s.heading,{label:"read"})),o.push(z(i+655,t+205,i+735,t+240,s.heading,{label:"webhook",dashed:!0})),o.push(z(i+560,t+235,i+560,t+325,s.heading,{label:"event",dashed:!0})),o.push(z(i+470,t+375,i+80,t+245,s.heading,{label:"notify",dashed:!0})),o}function ko(i,t,e,n){const s=gt(e);yt(n);const o=[];o.push(I(i+280,t-40,500,"Opportunity Solution Tree",22,s.heading,"700")),o.push(N(i+300,t,280,80,{fill:"#a5b4fc",borderRadius:12,shadow:!0,fontSize:15,fontWeight:"600",textColor:"#1f2933"},"Desired outcome"));const a=t+160,r=[i,i+300,i+600];for(const d of r)o.push(W(d,a,"Opportunity","#93c5fd",220,100)),o.push(z(i+440,t+85,d+110,a-5,s.heading));const c=a+180;for(const d of r)o.push(W(d,c,"Solution","#86efac",220,100)),o.push(z(d+110,a+105,d+110,c-5,s.heading));const l=c+180;for(const d of r)o.push(W(d,l,"Experiment","#fdba74",220,90)),o.push(z(d+110,c+105,d+110,l-5,s.heading));return o.push(I(i,l+120,400,"Signals / metrics",14,s.accent)),o.push(W(i,l+150,"What signal tells us this is working?","#fef08a",380,100)),o}function Eo(i,t,e,n){const s=gt(e);yt(n);const o=[];o.push(N(i,t,1060,620,{fill:s.card,stroke:s.cardStroke,strokeWidth:1.5,borderRadius:16,shadow:!0})),o.push(I(i+28,t+22,500,"Screenshot Review",22,s.heading,"700")),o.push(I(i+28,t+64,300,"Screenshot",13,s.accent));const a=H("frame",{x:i+28,y:t+92,width:480,height:360,zIndex:n+100,style:{fill:s.zone,stroke:s.cardStroke,strokeWidth:1.5,borderRadius:12,fontSize:13,textColor:s.text},data:{text:"Paste a screenshot here (Cmd/Ctrl+V)"}});return o.push(a),[["Observations","#93c5fd"],["Problems","#fca5a5"],["UX notes","#d8b4fe"],["Requirements","#86efac"]].forEach(([c,l],d)=>{const h=d%2,p=Math.floor(d/2),m=i+540+h*250,g=t+64+p*200;o.push(I(m,g,230,c,13,s.accent)),o.push(W(m,g+28,"",l,230,140))}),o.push(I(i+28,t+470,300,"Follow-up tasks",13,s.accent)),o.push(W(i+28,t+498,"","#fef08a",480,96)),o.push(I(i+540,t+470,300,"Decision",13,s.accent)),o.push(W(i+540,t+498,"","#fdba74",480,96)),o}function So(i,t,e,n){const s=gt(e);yt(n);const o=[];o.push(I(i,t,880,"Workflow map",26,s.heading,"700"));const a=[["Request","#dbeafe"],["Review","#fef3c7"],["Deliver","#dcfce7"]];for(let r=0;r<a.length;r++){const c=i+r*280;o.push(N(c,t+130,180,96,{fill:a[r][1],stroke:s.cardStroke,strokeWidth:1.5,borderRadius:12,fontSize:16,fontWeight:"650",textColor:"#1f2933",shadow:!0},a[r][0]))}return o.push(z(i+180,t+178,i+280,t+178,s.accent,{label:"approve"})),o.push(z(i+460,t+178,i+560,t+178,s.accent,{label:"ship"})),o.push(z(i+650,t+226,i+370,t+310,"#9ca3af",{label:"feedback",dashed:!0})),o.push(N(i+280,t+300,180,72,{fill:s.zone,stroke:s.cardStroke,strokeWidth:1.5,borderRadius:10,fontSize:14,textColor:s.text},"Learn and iterate")),o}function To(i,t,e,n){const s=gt(e);yt(n);const o=[];return o.push(I(i,t,800,"Handoff map",26,s.heading,"700")),[["Explore","#dbeafe"],["Decide","#fef3c7"],["Act","#dcfce7"]].forEach(([r,c],l)=>{o.push(N(i+l*270,t+150,170,92,{fill:c,stroke:s.cardStroke,strokeWidth:1.5,borderRadius:12,shadow:!0,fontSize:16,fontWeight:"650",textColor:"#1f2933"},r))}),o.push(z(i+170,t+196,i+270,t+196,s.accent,{label:"proposal"})),o.push(z(i+440,t+196,i+540,t+196,s.accent,{label:"owner | outcome",sides:!0,arrowStart:!0})),o.push(z(i+625,t+245,i+355,t+330,"#9ca3af",{label:"feedback",dashed:!0})),o.push(N(i+265,t+320,180,70,{fill:s.zone,stroke:s.cardStroke,strokeWidth:1.5,borderRadius:10,fontSize:14,textColor:s.text},"Review signal")),o}const Bo=[{id:"persona",name:"User Persona Card",description:"Name, role, goals, pain points, JTBD, success criteria.",build:yo},{id:"journey",name:"User Journey Map",description:"Awareness → Retention stages with sticky note areas.",build:vo},{id:"matrix",name:"Prioritization Matrix",description:"Impact vs effort: quick wins, strategic bets, fill-ins, avoid.",build:wo},{id:"requirements",name:"Requirements Flow",description:"Problem, user, context, solution, edge cases, risks.",build:xo},{id:"system",name:"System Diagram Starter",description:"User, frontend, API, database, external services + arrows.",build:Co},{id:"ost",name:"Opportunity Solution Tree",description:"Outcome, opportunities, solutions, experiments, signals.",build:ko},{id:"screenshot-review",name:"Screenshot Review Board",description:"Screenshot zone with observations, problems, UX notes.",build:Eo},{id:"workflow",name:"Workflow Map",description:"Labeled handoffs, a dashed feedback loop, and editable steps.",build:So},{id:"handoff",name:"Handoff Map",description:"A shared decision flow with ownership, outcomes, and feedback.",build:To}];function Mo(i,t){_("Insert a template",(e,n)=>{const s=document.createElement("div");s.className="template-grid";for(const o of Bo){const a=document.createElement("button");a.className="template-card";const r=document.createElement("span");r.className="t-name",r.textContent=o.name;const c=document.createElement("span");c.className="t-desc",c.textContent=o.description,a.appendChild(r),a.appendChild(c),a.addEventListener("click",()=>{Io(i,t,o.build),n()}),s.appendChild(a)}e.appendChild(s)})}function Io(i,t,e){const n=L(window.innerWidth/2,window.innerHeight/2,i.viewport),s=e(n.x-380,n.y-280,i.theme,i.maxZIndex());t.execute("insert-template",()=>{for(const o of s)i.elementsById.has(o.id)||i.addElement(o);i.emitChange()},()=>{for(const o of s)i.removeElement(o.id);i.emitChange()}),i.clearSelection();for(const o of s)i.selectedIds.add(o.id);i.requestRender()}function ue(i){return i<1024?i+" B":i<1024*1024?(i/1024).toFixed(1)+" KB":(i/(1024*1024)).toFixed(1)+" MB"}function Ro(i,t,e,n){_(n?"Storage":"Local storage",async(s,o)=>{const a=document.createElement("div");s.appendChild(a);const r=(g,y)=>{const b=document.createElement("div");b.className="storage-row";const x=document.createElement("span");x.textContent=g;const v=document.createElement("span");v.textContent=y,b.appendChild(x),b.appendChild(v),a.appendChild(b)};r("Elements on board",String(i.elements.length));const c=JSON.stringify(i.elements);if(r("Board data size",ue(c.length)),n)r("Workspace assets","Stored on disk under docs/assets");else{const g=await it.getAllKeys();r("Image assets",String(g.length));const y=await it.totalSizeBytes();r("Image asset size",ue(y))}const l=await tt.estimateStorage();!n&&l.quota>0&&r("Browser storage",`${ue(l.usage)} of ${ue(l.quota)}`);const d=document.createElement("div");d.className="modal-actions";const h=document.createElement("button");h.className="btn",h.textContent=n?"Clean unused managed assets":"Clear unused assets",h.addEventListener("click",async()=>{if(n){const x=i.elements.map(v=>typeof v.data.src=="string"?v.data.src:"").filter(Boolean);try{const v=await n(x);C(`Removed ${v} unused managed asset${v===1?"":"s"}`),o()}catch{C("Could not clean workspace assets")}return}const g=new Set,y=await tt.getAllBoards();for(const x of y)for(const v of x.elements){const S=v.data.assetId;typeof S=="string"&&g.add(S)}const b=await it.clearUnused(g);C(`Removed ${b} unused asset${b===1?"":"s"}`),o()}),d.appendChild(h);const p=document.createElement("button");p.className="btn danger",p.textContent="Clear board",p.addEventListener("click",()=>{confirm("Remove all elements from this board?")&&(t(),o())}),d.appendChild(p);const m=document.createElement("button");m.className="btn danger",m.textContent=n?"Reset local-only data":"Reset all local data",m.addEventListener("click",()=>{confirm("Delete ALL local boards, assets and preferences? This cannot be undone.")&&(e(),o())}),d.appendChild(m),s.appendChild(d)})}class Lo{constructor(t,e,n,s,o,a){u(this,"element");u(this,"visible",!1);u(this,"state");u(this,"renderer");u(this,"hitTester");u(this,"monitor");u(this,"imageCache");u(this,"timer",null);u(this,"storageEstimate","–");this.state=e,this.renderer=n,this.hitTester=s,this.monitor=o,this.imageCache=a,this.element=document.createElement("div"),this.element.className="perf-overlay",t.appendChild(this.element)}toggle(){this.visible=!this.visible,this.element.classList.toggle("visible",this.visible),this.visible?(this.monitor.start(),this.update(),this.timer=window.setInterval(()=>this.update(),500),this.refreshStorage()):this.timer!==null&&(clearInterval(this.timer),this.timer=null)}async refreshStorage(){const t=await it.totalSizeBytes();this.storageEstimate=(t/(1024*1024)).toFixed(1)+"MB"}update(){const t=this.state.viewport,e=[`FPS: ${this.monitor.fps}`,`Total elements: ${this.state.elements.length.toLocaleString()}`,`Visible elements: ${this.renderer.visibleCount.toLocaleString()}`,`Render time: ${this.renderer.lastRenderMs.toFixed(1)}ms`,`Hit-test time: ${this.hitTester.lastHitTestMs.toFixed(2)}ms`,`Save time: ${tt.lastSaveMs.toFixed(0)}ms`,`Image cache: ${this.imageCache.size}`,`Estimated storage: ${this.storageEstimate}`,`Zoom: ${Math.round(t.scale*100)}%`,`Viewport: x: ${Math.round(-t.offsetX/t.scale)}, y: ${Math.round(-t.offsetY/t.scale)}`,`Culling: ${this.renderer.cullingEnabled?"on":"off"} LOD: ${this.renderer.lodEnabled?"on":"off"}`];this.element.textContent=e.join(`
|
|
13
|
-
`)}}const me=["#93c5fd","#a5b4fc","#86efac","#fdba74","#fca5a5","#d8b4fe","#f9a8d4","#fef08a","#ffffff","#e5e7eb"],vn=["Onboarding","Checkout","Retention","Activation","Churn risk","API gateway","User insight","Edge case","Quick win","Tech debt","Experiment","Metric","Persona","Journey","Feature flag"];function Q(i,t){return i+Math.random()*(t-i)}function dt(i){return i[Math.floor(Math.random()*i.length)]}const ht="stress";class $o{constructor(t){u(this,"state");u(this,"lastGenerateMs",0);this.state=t}generate(t){const e=performance.now(),n=Math.sqrt(t)*280,s=["sticky","rectangle","ellipse","text","arrow"];let o=this.state.maxZIndex();const a=[];for(let r=0;r<t;r++){const c=dt(s),l=Q(-n/2,n/2),d=Q(-n/2,n/2);let h;c==="sticky"?h=H("sticky",{x:l,y:d,width:220,height:160,zIndex:++o,style:{fill:dt(Ce),borderRadius:12,shadow:!0,fontSize:14,textColor:"#1f2933"},data:{text:dt(vn),[ht]:!0}}):c==="rectangle"||c==="ellipse"?h=H(c,{x:l,y:d,width:Q(80,300),height:Q(60,200),zIndex:++o,style:{fill:dt(me),stroke:"#37415188",strokeWidth:1.5,borderRadius:c==="rectangle"?8:0},data:{[ht]:!0}}):c==="text"?h=H("text",{x:l,y:d,width:200,height:28,zIndex:++o,style:{fontSize:16,textColor:this.state.theme==="dark"?"#f2f2f2":"#1f2933"},data:{text:dt(vn),[ht]:!0}}):h=H("arrow",{x:l,y:d,width:Q(-250,250),height:Q(-250,250),zIndex:++o,style:{stroke:dt(me),strokeWidth:2,arrowEnd:!0},data:{[ht]:!0}}),a.push(h)}for(const r of a)this.state.addElement(r);this.lastGenerateMs=performance.now()-e,this.state.emitChange()}generateImagePlaceholders(t){const e=Math.sqrt(t)*420;let n=this.state.maxZIndex();for(let s=0;s<t;s++){const o=H("image",{x:Q(-e/2,e/2),y:Q(-e/2,e/2),width:320,height:220,zIndex:++n,data:{assetId:"stress-placeholder-"+s,[ht]:!0}});this.state.addElement(o)}this.state.emitChange()}generateMixedPmBoard(){this.generate(300)}randomizePositions(){const t=Math.sqrt(this.state.elements.length)*280;for(const e of this.state.elements)e.data[ht]&&(e.x=Q(-t/2,t/2),e.y=Q(-t/2,t/2));this.state.emitChange()}randomizeColors(){for(const t of this.state.elements)t.data[ht]&&(t.type==="sticky"?t.style.fill=dt(Ce):t.type==="rectangle"||t.type==="ellipse"?t.style.fill=dt(me):t.type==="arrow"&&(t.style.stroke=dt(me)));this.state.emitChange()}randomizeZIndex(){for(const t of this.state.elements)t.data[ht]&&(t.zIndex=Math.floor(Q(0,this.state.elements.length)));this.state.invalidateSort(),this.state.emitChange()}clearGenerated(){const t=this.state.elements.filter(e=>e.data[ht]);for(const e of t)this.state.removeElement(e.id);this.state.emitChange()}}class Po{constructor(t,e,n){u(this,"element");u(this,"visible",!1);this.element=document.createElement("div"),this.element.className="stress-panel";const s=(a,r,c=!1)=>{const l=document.createElement("button");l.className="btn"+(c?" danger":""),l.textContent=a,l.addEventListener("click",r),this.element.appendChild(l)},o=a=>()=>{e.generate(a),C(`Generated ${a.toLocaleString()} elements in ${e.lastGenerateMs.toFixed(0)}ms`)};s("+100",o(100)),s("+1,000",o(1e3)),s("+5,000",o(5e3)),s("+10,000",o(1e4)),s("+20,000",o(2e4)),s("+100 images",()=>{e.generateImagePlaceholders(100),C("Generated 100 image placeholders")}),s("Mixed PM board",()=>{e.generateMixedPmBoard(),C("Generated mixed PM board")}),s("Shuffle positions",()=>e.randomizePositions()),s("Shuffle colors",()=>e.randomizeColors()),s("Shuffle z-index",()=>e.randomizeZIndex()),s("Toggle culling",()=>{n.cullingEnabled=!n.cullingEnabled,C(`Viewport culling ${n.cullingEnabled?"enabled":"disabled"}`),n.state.requestRender()}),s("Toggle LOD",()=>{n.lodEnabled=!n.lodEnabled,C(`Simplified rendering ${n.lodEnabled?"enabled":"disabled"}`),n.state.requestRender()}),s("Clear generated",()=>{e.clearGenerated(),C("Cleared generated elements")},!0),t.appendChild(this.element)}toggle(){this.visible=!this.visible,this.element.classList.toggle("visible",this.visible)}}const Do=300,Ao=1e3,wn=[{label:"Small",shortLabel:"S",fontSize:13,fontWeight:"400"},{label:"Body",shortLabel:"Body",fontSize:16,fontWeight:"400"},{label:"H3",shortLabel:"H3",fontSize:18,fontWeight:"650"},{label:"H2",shortLabel:"H2",fontSize:24,fontWeight:"700"},{label:"H1",shortLabel:"H1",fontSize:32,fontWeight:"750"}],No=new Set(["text"]),zo=new Set(["rectangle","ellipse","frame"]),Ho=["#0078d4","#4dabf7","#6e6e6e","#ffffff","#0f0f0f","#fef08a","#93c5fd","#fca5a5"],Fo=[{label:"Black text",value:"#1f2933"},{label:"White text",value:"#ffffff"}];class jo{constructor(t,e={}){u(this,"state",new Ti);u(this,"commands",new Mi);u(this,"surface");u(this,"renderer");u(this,"scheduler");u(this,"hitTester");u(this,"imageCache");u(this,"monitor",new _i);u(this,"actions");u(this,"imageInsert");u(this,"toolController");u(this,"textEditor");u(this,"toolbar");u(this,"topControls");u(this,"perfOverlay");u(this,"stressPanel");u(this,"boardSaveTimer",null);u(this,"viewportSaveTimer",null);u(this,"saveInFlight",null);u(this,"saveQueued",!1);u(this,"suppressPersistence",!1);u(this,"options");u(this,"listenerAbort",new AbortController);u(this,"root");u(this,"revision",null);u(this,"stale",!1);u(this,"bottomToolCluster",null);u(this,"stickyColorPopover",null);u(this,"textSizePopover",null);u(this,"shapeStylePopover",null);u(this,"layerMenu",null);this.root=t,this.options=e,e.boardId&&(this.state.boardId=e.boardId);const n=this.listenerAbort.signal;this.surface=new Fi(t,3,n),this.imageCache=new es(it,()=>this.state.requestRender(),e.resolveAssetUrl),this.renderer=new ji(this.surface,this.state,this.imageCache),this.scheduler=new Wi(()=>this.renderer.render()),this.hitTester=new Ui(this.state),this.actions=new Ii(this.state,this.commands),this.textEditor=new fo(this.state,this.commands),this.imageInsert=new $i(this.state,this.commands,this.imageCache,C,e.uploadImage),this.state.onRenderNeeded(()=>{var s;this.scheduler.requestRender(),(s=this.topControls)==null||s.setZoom(this.state.viewport.scale),this.suppressPersistence||this.scheduleViewportSave(),this.updateContextualToolPanels()}),this.state.onChange(()=>{this.suppressPersistence||this.scheduleBoardSave()}),this.surface.onResize(()=>this.state.requestRender()),this.setupTools(t,n),this.setupUi(t),this.setupSelectionOverlays(t,n),this.setupDragDrop(t),new Di(this.state,this.actions,this.imageInsert,n),Ai({state:this.state,commands:this.commands,actions:this.actions,toolController:this.toolController,resetView:()=>this.resetView()},n),this.loadInitialState()}destroy(){this.boardSaveTimer!==null&&clearTimeout(this.boardSaveTimer),this.viewportSaveTimer!==null&&clearTimeout(this.viewportSaveTimer),this.listenerAbort.abort(),this.imageCache.clear()}setupTools(t,e){const n={state:this.state,renderer:this.renderer,surface:this.surface,hitTester:this.hitTester,commands:this.commands,setTool:s=>this.toolController.setTool(s),setCursor:s=>{this.surface.container.style.cursor=s},openTextEditor:s=>this.textEditor.open(s)};this.toolController=new Ki(this.surface.container,n,[new Zi,new Ji,new eo,new no,new ct("rectangle"),new ct("ellipse"),new ct("diamond"),new ct("triangle"),new ct("pill"),new ct("chevron"),new ct("parallelogram"),new ct("hexagon"),new ct("cylinder"),new so,new Gt,new Gt("dashedArrow"),new Gt("twoWayArrow"),new Gt("labeledArrow"),new Gt("sidedLabelArrow"),new io((s,o,a)=>void this.imageInsert.insertFiles(s,o,a))],e),this.toolController.onToolChange(s=>{var o;(o=this.toolbar)==null||o.setActiveTool(s)})}setupUi(t){this.toolbar=new uo(t,{setTool:n=>this.toolController.setTool(n),openTemplates:()=>Mo(this.state,this.commands),toggleTheme:()=>this.toggleTheme(),openExportMenu:()=>this.openExportMenu(),toggleStressPanel:()=>this.stressPanel.toggle(),copyEmbed:this.options.onExit?()=>this.startEmbedSelection():void 0}),this.toolbar.setActiveTool("select"),this.topControls=new mo(t,{onExit:this.options.onExit?()=>this.exitToDocs():void 0,resetView:()=>this.resetView(),zoomIn:()=>{oe(this.state.viewport,window.innerWidth/2,window.innerHeight/2,this.state.viewport.scale*1.2),this.state.requestRender()},zoomOut:()=>{oe(this.state.viewport,window.innerWidth/2,window.innerHeight/2,this.state.viewport.scale/1.2),this.state.requestRender()},openStorageManager:()=>Ro(this.state,()=>this.clearBoard(),()=>void this.resetAllLocalData(),this.options.cleanupUnusedAssets),togglePerfOverlay:()=>this.perfOverlay.toggle(),deleteBoard:this.options.deleteBoard?()=>this.confirmDeleteBoard():void 0,onTitleChange:n=>{this.state.title=n,this.state.emitChange()}},this.state.title),this.perfOverlay=new Lo(t,this.state,this.renderer,this.hitTester,this.monitor,this.imageCache);const e=new $o(this.state);this.stressPanel=new Po(t,e,this.renderer)}setupSelectionOverlays(t,e){this.bottomToolCluster=document.createElement("div"),this.bottomToolCluster.className="bottom-tool-cluster",t.appendChild(this.bottomToolCluster),this.bottomToolCluster.appendChild(this.toolbar.element),this.stickyColorPopover=document.createElement("div"),this.stickyColorPopover.className="context-tool-popover sticky-color-popover",this.stickyColorPopover.setAttribute("aria-hidden","true");for(const s of Ce){const o=document.createElement("button");o.type="button",o.className="sticky-color-btn",o.title="Set sticky color",o.setAttribute("aria-label",`Set sticky color ${s}`),o.style.setProperty("--swatch",s),o.addEventListener("click",()=>this.setSelectedStickyColor(s)),this.stickyColorPopover.appendChild(o)}this.bottomToolCluster.appendChild(this.stickyColorPopover),this.textSizePopover=document.createElement("div"),this.textSizePopover.className="context-tool-popover text-size-popover",this.textSizePopover.setAttribute("aria-hidden","true");for(const s of wn){const o=document.createElement("button");o.type="button",o.className="text-size-btn",o.textContent=s.shortLabel,o.setAttribute("aria-label",`Set text size ${s.label}`),o.addEventListener("click",()=>this.setSelectedTextSize(s)),this.textSizePopover.appendChild(o)}this.bottomToolCluster.appendChild(this.textSizePopover),this.shapeStylePopover=document.createElement("div"),this.shapeStylePopover.className="context-tool-popover shape-style-popover",this.shapeStylePopover.setAttribute("aria-hidden","true");for(const s of Ho){const o=document.createElement("button");o.type="button",o.className="shape-fill-btn",o.title="Set shape color",o.setAttribute("aria-label",`Set shape color ${s}`),o.style.setProperty("--swatch",s),o.addEventListener("click",()=>this.setSelectedShapeFill(s)),this.shapeStylePopover.appendChild(o)}const n=document.createElement("div");n.className="context-tool-divider",this.shapeStylePopover.appendChild(n);for(const s of Fo){const o=document.createElement("button");o.type="button",o.className="shape-text-color-btn",o.textContent=s.value==="#ffffff"?"W":"B",o.setAttribute("aria-label",s.label),o.addEventListener("click",()=>this.setSelectedShapeTextColor(s.value)),this.shapeStylePopover.appendChild(o)}this.bottomToolCluster.appendChild(this.shapeStylePopover),this.surface.interactionLayer.addEventListener("contextmenu",s=>this.openLayerMenu(s),{signal:e,capture:!0}),document.addEventListener("pointerdown",s=>{this.layerMenu&&!this.layerMenu.contains(s.target)&&this.closeLayerMenu()},{signal:e}),document.addEventListener("keydown",s=>{s.key==="Escape"&&this.closeLayerMenu()},{signal:e})}updateContextualToolPanels(){this.updateStickyColorPopover(),this.updateTextSizePopover(),this.updateShapeStylePopover()}setContextPanelVisible(t,e){t&&(t.classList.toggle("is-visible",e),t.setAttribute("aria-hidden",String(!e)))}updateStickyColorPopover(){var o;const t=this.stickyColorPopover;if(!t)return;const e=this.state.getSelected().filter(a=>a.type==="sticky"),n=e.length>0&&!this.textEditor.isEditing;if(this.setContextPanelVisible(t,n),!n)return;const s=(o=e[0])==null?void 0:o.style.fill;for(const a of t.children){const r=a,c=r.style.getPropertyValue("--swatch")===s;r.classList.toggle("active",c),r.setAttribute("aria-pressed",String(c))}}setSelectedStickyColor(t){to(t);const e=this.state.getSelected().filter(r=>r.type==="sticky"&&!r.locked);if(e.length===0)return;const n=this.state,s=e.map(r=>({id:r.id,fill:r.style.fill,textColor:r.style.textColor,updatedAt:r.updatedAt})),o=e.map(r=>({id:r.id,fill:t,textColor:"#1f2933",updatedAt:Date.now()})),a=r=>{for(const c of r){const l=n.elementsById.get(c.id);l&&(l.style.fill=c.fill,l.style.textColor=c.textColor,l.updatedAt=c.updatedAt)}n.emitChange()};this.commands.execute("sticky-color",()=>a(o),()=>a(s))}updateTextSizePopover(){var o,a;const t=this.textSizePopover;if(!t)return;const e=this.selectedTextTargets(),n=e.length>0&&!this.textEditor.isEditing;if(this.setContextPanelVisible(t,n),!n)return;const s=((o=e[0])==null?void 0:o.style.fontSize)??(((a=e[0])==null?void 0:a.type)==="sticky"?15:16);for(const r of t.children){const c=r,l=wn.find(h=>h.shortLabel===c.textContent),d=(l==null?void 0:l.fontSize)===s;c.classList.toggle("active",d),c.setAttribute("aria-pressed",String(d))}}selectedTextTargets(){return this.state.getSelected().filter(t=>No.has(t.type)&&!t.locked)}setSelectedTextSize(t){const e=this.selectedTextTargets();if(e.length===0)return;const n=this.state,s=e.map(r=>({id:r.id,fontSize:r.style.fontSize,fontWeight:r.style.fontWeight,height:r.height,updatedAt:r.updatedAt})),o=e.map(r=>({id:r.id,fontSize:t.fontSize,fontWeight:t.fontWeight,height:r.type==="text"?Math.max(r.height,Math.ceil(t.fontSize*1.45)):r.height,updatedAt:Date.now()})),a=r=>{for(const c of r){const l=n.elementsById.get(c.id);l&&(l.style.fontSize=c.fontSize,l.style.fontWeight=c.fontWeight,l.height=c.height,l.updatedAt=c.updatedAt,X(l.id))}n.emitChange()};this.commands.execute("text-size",()=>a(o),()=>a(s))}updateShapeStylePopover(){var a,r;const t=this.shapeStylePopover;if(!t)return;const e=this.selectedShapeTargets(),n=e.length>0&&!this.textEditor.isEditing;if(this.setContextPanelVisible(t,n),!n)return;const s=(a=e[0])==null?void 0:a.style.fill,o=((r=e[0])==null?void 0:r.style.textColor)??"#1f2933";for(const c of t.children){const l=c;if(l.classList.contains("shape-fill-btn")){const d=l.style.getPropertyValue("--swatch")===s;l.classList.toggle("active",d),l.setAttribute("aria-pressed",String(d))}if(l.classList.contains("shape-text-color-btn")){const h=l.textContent==="W"?o==="#ffffff":o!=="#ffffff";l.classList.toggle("active",h),l.setAttribute("aria-pressed",String(h))}}}selectedShapeTargets(){return this.state.getSelected().filter(t=>zo.has(t.type)&&!t.locked)}setSelectedShapeFill(t){const e=this.selectedShapeTargets();if(e.length===0)return;const n=this.state,s=e.map(r=>({id:r.id,fill:r.style.fill,updatedAt:r.updatedAt})),o=e.map(r=>({id:r.id,fill:t,updatedAt:Date.now()})),a=r=>{for(const c of r){const l=n.elementsById.get(c.id);l&&(l.style.fill=c.fill,l.updatedAt=c.updatedAt)}n.emitChange()};this.commands.execute("shape-fill",()=>a(o),()=>a(s))}setSelectedShapeTextColor(t){const e=this.selectedShapeTargets();if(e.length===0)return;const n=this.state,s=e.map(r=>({id:r.id,textColor:r.style.textColor,updatedAt:r.updatedAt})),o=e.map(r=>({id:r.id,textColor:t,updatedAt:Date.now()})),a=r=>{for(const c of r){const l=n.elementsById.get(c.id);l&&(l.style.textColor=c.textColor,l.updatedAt=c.updatedAt,X(l.id))}n.emitChange()};this.commands.execute("shape-text-color",()=>a(o),()=>a(s))}openLayerMenu(t){if(t.preventDefault(),t.stopPropagation(),this.textEditor.isEditing)return;const e=L(t.clientX,t.clientY,this.state.viewport),n=this.hitTester.hitTest(e.x,e.y,this.state.viewport.scale);n&&!this.state.selectedIds.has(n.id)&&(this.state.clearSelection(),this.state.selectedIds.add(n.id),this.state.requestRender());const s=this.state.getSelected();if(s.length===0){this.closeLayerMenu();return}this.closeLayerMenu();const o=document.createElement("div");o.className="layer-menu",o.setAttribute("role","menu");const a=document.createElement("div");a.className="layer-menu-label",a.textContent=s.length===1?"Layer":`${s.length} layers`,o.appendChild(a);const r=(l,d)=>{const h=document.createElement("button");h.type="button",h.setAttribute("role","menuitem"),h.textContent=l,h.addEventListener("click",()=>{d(),this.closeLayerMenu()}),o.appendChild(h)},c=()=>{const l=document.createElement("div");l.className="layer-menu-divider",o.appendChild(l)};r("Bring forward",()=>this.actions.bringForward()),r("Send backward",()=>this.actions.sendBackward()),c(),r("Bring to front",()=>this.actions.bringToFront()),r("Send to back",()=>this.actions.sendToBack()),this.root.appendChild(o),this.layerMenu=o,this.positionLayerMenu(o,t.clientX,t.clientY)}positionLayerMenu(t,e,n){t.style.left="0px",t.style.top="0px",requestAnimationFrame(()=>{const o=t.getBoundingClientRect(),a=Math.min(Math.max(12,e),window.innerWidth-o.width-12),r=Math.min(Math.max(12,n),window.innerHeight-o.height-12);t.style.left=`${a}px`,t.style.top=`${r}px`})}closeLayerMenu(){var t;(t=this.layerMenu)==null||t.remove(),this.layerMenu=null}exitToDocs(){this.boardSaveTimer!==null&&(clearTimeout(this.boardSaveTimer),this.boardSaveTimer=null),this.saveBoard().then(t=>{var e,n;t&&((n=(e=this.options).onExit)==null||n.call(e))})}startEmbedSelection(){Ze.begin(this.root,t=>{t&&this.copyEmbedSnippet(t)})}async copyEmbedSnippet(t){const e=this.state.viewport,n=L(t.x,t.y,e),s=L(t.x+t.w,t.y+t.h,e),o=Math.round(n.x),a=Math.round(n.y),r=Math.round(s.x-n.x),c=Math.round(s.y-n.y),l=`\`\`\`board
|
|
14
|
-
src: /${this.state.boardId}
|
|
15
|
-
region: ${o}, ${a}, ${r}, ${c}
|
|
16
|
-
height: 360
|
|
17
|
-
title: ${this.state.title}
|
|
18
|
-
\`\`\``;try{await navigator.clipboard.writeText(l),C("Embed snippet copied — paste it into any doc")}catch{C("Could not access clipboard")}}setupDragDrop(t){t.addEventListener("dragover",e=>{e.preventDefault(),e.dataTransfer&&(e.dataTransfer.dropEffect="copy")}),t.addEventListener("drop",e=>{var n;e.preventDefault(),(n=e.dataTransfer)!=null&&n.files&&e.dataTransfer.files.length>0&&this.imageInsert.insertFiles(e.dataTransfer.files,e.clientX,e.clientY)})}async loadInitialState(){this.suppressPersistence=!0;const t=tt.getPref("theme");if(t==="dark"||t==="light"?this.applyTheme(t):window.matchMedia("(prefers-color-scheme: dark)").matches&&this.applyTheme("dark"),this.options.loadBoard){try{const n=await this.options.loadBoard(this.options.boardId??this.state.boardId);n&&(this.revision=n.revision,this.stale=!1,this.applyDocument(n.doc),this.fitInitialRegion())}catch(n){console.error("Board file load failed",n),C("Could not load board file")}finally{this.suppressPersistence=!1}this.state.requestRender();return}const e=tt.getPref("lastBoardId")??"default-board";try{const n=await tt.loadBoard(e);n&&this.applyDocument(n)}catch(n){console.error("Board recovery failed",n),tt.getPref("recovery")&&C("Could not load saved board — starting fresh")}this.suppressPersistence=!1,this.state.requestRender()}applyDocument(t){this.state.schemaUrl=t.$schema,this.state.boardId=t.id,this.state.title=t.title,this.state.createdAt=t.createdAt,this.state.viewport={...t.viewport},this.state.elements=[],this.state.elementsById.clear();for(const e of t.elements)this.state.addElement(e);this.applyTheme(t.theme),this.topControls.setTitle(t.title),this.topControls.setZoom(t.viewport.scale),this.state.invalidateSort()}fitInitialRegion(){const t=this.options.initialRegion;if(!t)return;const[e,n,s,o]=t;if(s<=0||o<=0)return;const a=Math.min(120,Math.max(48,this.surface.width*.08)),r=88,c=124,l=Math.max(240,this.surface.width-a*2),d=Math.max(180,this.surface.height-r-c),h=Math.min(l/s,d/o),p=Math.max(.7,Math.min(1.1,this.state.viewport.scale*1.15)),m=is(Math.min(h*.58,p));this.state.viewport={scale:m,offsetX:a+(l-s*m)/2-e*m,offsetY:r+(d-o*m)/2-n*m},this.topControls.setZoom(m)}scheduleBoardSave(){this.boardSaveTimer!==null&&clearTimeout(this.boardSaveTimer),this.boardSaveTimer=window.setTimeout(()=>{this.boardSaveTimer=null,this.saveBoard()},Do)}scheduleViewportSave(){this.viewportSaveTimer===null&&(this.viewportSaveTimer=window.setTimeout(()=>{this.viewportSaveTimer=null,this.saveBoard()},Ao))}async saveBoard(){if(this.saveInFlight)return this.saveQueued=!0,this.saveInFlight;const t=async()=>{if(this.stale)return C("Board changed elsewhere — reload before saving"),this.setBoardStatus("Changed elsewhere","stale"),!1;try{return this.options.saveBoard?(this.revision=await this.options.saveBoard(ce(this.state)),this.stale=!1,this.setBoardStatus("Saved","saved")):await tt.saveBoard(ce(this.state)),!0}catch(n){return n instanceof se?(this.revision=n.currentRevision,this.stale=!0,this.setBoardStatus("Changed elsewhere","stale"),C("Board changed elsewhere — reload before saving"),!1):(C(this.options.saveBoard?"Save to file failed":"Save failed — browser storage may be full"),this.options.saveBoard&&this.setBoardStatus("Save failed","error"),!1)}};this.saveInFlight=t();const e=await this.saveInFlight;return this.saveInFlight=null,this.saveQueued&&!this.stale?(this.saveQueued=!1,this.saveBoard()):(this.saveQueued=!1,e)}markStale(){this.stale=!0,this.setBoardStatus("Changed elsewhere","stale")}async reloadLatest(){if(this.options.loadBoard&&window.confirm("Discard local edits and reload the latest board version?"))try{this.suppressPersistence=!0;const t=await this.options.loadBoard(this.options.boardId??this.state.boardId);if(!t){C("Could not reload board file"),this.suppressPersistence=!1;return}this.revision=t.revision,this.stale=!1,this.applyDocument(t.doc),this.state.requestRender(),this.setBoardStatus("Reloaded latest","saved")}catch{this.setBoardStatus("Reload failed","error"),C("Could not reload board file")}finally{this.suppressPersistence=!1}}setBoardStatus(t,e){var n;(n=this.topControls)==null||n.setStatus(t,e,e==="stale"&&this.options.loadBoard?{label:"Reload latest",onClick:()=>void this.reloadLatest()}:void 0)}applyTheme(t){var e;this.state.theme=t,document.documentElement.setAttribute("data-theme",t),tt.setPref("theme",t),(e=this.toolbar)==null||e.setTheme(t),this.state.requestRender()}toggleTheme(){this.applyTheme(this.state.theme==="dark"?"light":"dark"),this.scheduleBoardSave()}resetView(){this.state.viewport.offsetX=0,this.state.viewport.offsetY=0,this.state.viewport.scale=ss,this.state.requestRender()}clearBoard(){const t=this.state,e=[...t.elements];this.commands.execute("clear-board",()=>{for(const n of e)t.removeElement(n.id);t.emitChange()},()=>{for(const n of e)t.elementsById.has(n.id)||t.addElement(n);t.emitChange()}),C("Board cleared")}async resetAllLocalData(){await tt.clearAllBoards(),await it.clearAll(),tt.clearPrefs(),this.imageCache.clear(),this.commands.clear(),this.state.elements=[],this.state.elementsById.clear(),this.state.clearSelection(),this.state.invalidateSort(),this.resetView(),C("All local data cleared")}confirmDeleteBoard(){_("Delete board?",(t,e)=>{const n=document.createElement("p");n.className="modal-copy",n.textContent=`This will permanently delete "${this.state.title}" from the active docs root. This cannot be undone.`;const s=document.createElement("div");s.className="modal-actions";const o=document.createElement("button");o.className="btn",o.textContent="Cancel",o.addEventListener("click",e);const a=document.createElement("button");a.className="btn danger",a.textContent="Delete board",a.addEventListener("click",async()=>{var r,c,l;a.disabled=!0,this.boardSaveTimer!==null&&(clearTimeout(this.boardSaveTimer),this.boardSaveTimer=null),this.viewportSaveTimer!==null&&(clearTimeout(this.viewportSaveTimer),this.viewportSaveTimer=null);try{await((c=(r=this.options).deleteBoard)==null?void 0:c.call(r,this.state.boardId)),e(),C("Board deleted"),(l=this.options.onDeleted??this.options.onExit)==null||l()}catch{a.disabled=!1,C("Could not delete board")}}),s.append(o,a),t.append(n,s)})}openExportMenu(){_("Export & import",(t,e)=>{const n=document.createElement("div");n.className="modal-actions",n.style.flexDirection="column",n.style.alignItems="stretch";const s=(o,a,r=!1)=>{const c=document.createElement("button");c.className="btn"+(r?" primary":""),c.textContent=o,c.addEventListener("click",()=>{a(),e()}),n.appendChild(c)};s("Export current view as PNG",async()=>{const o=await zi(this.state,this.imageCache,this.surface.width,this.surface.height);un(o,this.state.title+"-view.png")},!0),s("Export full board as PNG",async()=>{const o=await Hi(this.state,this.imageCache);o?un(o,this.state.title+"-board.png"):C("Board is empty")}),s("Export board JSON (no images)",()=>{Ne.exportBoardJson(ce(this.state))}),s("Export full bundle (JSON + images)",async()=>{await Ne.exportFullBundle(ce(this.state))}),s("Import board from file…",()=>this.pickImportFile()),t.appendChild(n)})}pickImportFile(){const t=document.createElement("input");t.type="file",t.accept="application/json,.json",t.onchange=async()=>{var s;const e=(s=t.files)==null?void 0:s[0];if(!e)return;const n=await Ne.importFromFile(e);if(!n){C("Import failed — invalid board file");return}this.state.boardId=n.id,this.state.title=n.title,this.state.createdAt=n.createdAt,this.state.viewport={...n.viewport},this.state.elements=[],this.state.elementsById.clear(),this.state.clearSelection();for(const o of n.elements)this.state.addElement(o);this.applyTheme(n.theme),this.topControls.setTitle(n.title),this.commands.clear(),this.state.emitChange(),C("Board imported")},t.click()}}const xn="/__api/asset?path=",ds=new Set(["P","DIV","UL","OL","H1","H2","H3","H4","H5","H6","BLOCKQUOTE","PRE","TABLE","FIGURE","HR"]);function Wo(i){const t=[];for(const e of Array.from(i.childNodes))Je(t,e);return t.map(e=>e.replace(/[ \t]+$/gm,"")).join(`
|
|
19
|
-
|
|
20
|
-
`).replace(/\n{3,}/g,`
|
|
21
|
-
|
|
22
|
-
`).trim()+`
|
|
23
|
-
`}function Je(i,t){const e=Oo(t);e!==null&&e.trim()!==""&&i.push(e)}function Oo(i){if(i.nodeType===Node.TEXT_NODE){const n=(i.textContent??"").trim();return n||null}if(i.nodeType!==Node.ELEMENT_NODE)return null;const t=i,e=t.tagName;switch(e){case"H1":case"H2":case"H3":case"H4":case"H5":case"H6":return"#".repeat(Number(e[1]))+" "+ft(t).trim();case"HR":return"---";case"P":return t.classList.contains("embed-caret-spacer")&&!_o(t)?null:kn(t)?En(t):Ye(ft(t).trim());case"BLOCKQUOTE":return Cn(t).split(`
|
|
24
|
-
`).map(s=>s?`> ${s}`:">").join(`
|
|
25
|
-
`);case"UL":return ke(t,!1,0);case"OL":return ke(t,!0,0);case"PRE":{const n=t.querySelector("code"),s=Ko(n??t),o=(n??t).textContent??"";return"```"+s+`
|
|
26
|
-
`+o.replace(/\n$/,"")+"\n```"}case"TABLE":return Xo(t);case"FIGURE":return t.classList.contains("board-embed")?qo(t):Cn(t)||null;case"DIV":return kn(t)?En(t):Ye(ft(t).trim());case"BR":return null;default:return ft(t).trim()||null}}function Cn(i){const t=[];for(const e of Array.from(i.childNodes))Je(t,e);return t.join(`
|
|
27
|
-
|
|
28
|
-
`)}function kn(i){for(const t of Array.from(i.children))if(ds.has(t.tagName))return!0;return!1}function En(i){const t=[];let e="";const n=()=>{const s=Ye(e.trim());s&&t.push(s),e=""};for(const s of Array.from(i.childNodes)){if(Uo(s)){n(),Je(t,s);continue}e+=Qe(s)}return n(),t.join(`
|
|
29
|
-
|
|
30
|
-
`)||null}function Uo(i){return i.nodeType===Node.ELEMENT_NODE&&ds.has(i.tagName)}function _o(i){for(const t of Array.from(i.childNodes)){if(t.nodeType===Node.TEXT_NODE&&(t.textContent??"").trim())return!0;if(t.nodeType!==Node.ELEMENT_NODE)continue;const e=t;if(e.tagName!=="BR"&&((e.textContent??"").trim()||["IMG","FIGURE","HR"].includes(e.tagName)))return!0}return!1}function ke(i,t,e){const n=[],s=" ".repeat(e);let o=0;for(const a of Array.from(i.children)){if(a.tagName!=="LI")continue;o++;const r=t?`${o}. `:"- ";let c="";const l=[];let d=null;for(const h of Array.from(a.childNodes)){if(h.nodeType===Node.ELEMENT_NODE){const p=h;if(p.tagName==="UL"){l.push(ke(p,!1,e+1));continue}if(p.tagName==="OL"){l.push(ke(p,!0,e+1));continue}if(p.tagName==="INPUT"){d=p.checked?"[x] ":"[ ] ";continue}}c+=Qe(h)}n.push(s+r+(d??"")+c.trim());for(const h of l)h&&n.push(h)}return n.join(`
|
|
31
|
-
`)}function Xo(i){const t=Array.from(i.querySelectorAll("tr"));if(t.length===0)return"";const e=o=>Array.from(o.querySelectorAll("th,td")).map(a=>ft(a).trim()),n=e(t[0]),s=[];s.push("| "+n.join(" | ")+" |"),s.push("| "+n.map(()=>"---").join(" | ")+" |");for(let o=1;o<t.length;o++)s.push("| "+e(t[o]).join(" | ")+" |");return s.join(`
|
|
32
|
-
`)}function qo(i){const t=["```board",`src: ${i.dataset.src??""}`];return i.dataset.region&&t.push(`region: ${i.dataset.region.split(",").map(e=>e.trim()).join(", ")}`),i.dataset.height&&t.push(`height: ${i.dataset.height}`),i.dataset.title&&t.push(`title: ${i.dataset.title}`),t.push("```"),t.join(`
|
|
33
|
-
`)}function ft(i){let t="";for(const e of Array.from(i.childNodes))t+=Qe(e);return t}function Qe(i){if(i.nodeType===Node.TEXT_NODE)return Tn(i.textContent??"");if(i.nodeType!==Node.ELEMENT_NODE)return"";const t=i;switch(t.tagName){case"STRONG":case"B":return Sn("**",ft(t));case"EM":case"I":return Sn("*",ft(t));case"CODE":return Vo(t.textContent??"");case"A":{const e=(t.getAttribute("data-md-href")??t.getAttribute("href")??"").trim(),n=ft(t).trim()||Tn(e);return e?`[${Bn(n)}](${Mn(e)})`:n}case"IMG":return`)})`;case"BR":return`
|
|
34
|
-
`;default:return ft(t)}}function Yo(i){const t=i.getAttribute("data-md-src");if(t)return t;const e=i.getAttribute("src")??"";return e.startsWith(xn)?decodeURIComponent(e.slice(xn.length)):e}function Ko(i){const e=(i.getAttribute("class")??"").match(/language-(\w+)/);return e?e[1]:""}function Sn(i,t){const e=t.trim();return e?`${i}${e}${i}`:""}function Vo(i){if(!i.includes("`"))return"`"+i+"`";const t=Math.max(...(i.match(/`+/g)??[""]).map(n=>n.length)),e="`".repeat(t+1);return`${e} ${i} ${e}`}function Tn(i){return i.replace(/\\/g,"\\\\").replace(/\[/g,"\\[").replace(/\]/g,"\\]").replace(/\*/g,"\\*").replace(/_/g,"\\_").replace(/`/g,"\\`")}function Ye(i){return i?/^#{1,6}\s/.test(i)||/^>\s?/.test(i)||/^([-+*]|\d+\.)\s+/.test(i)||/^`{3,}/.test(i)||/^(-{3,}|\*{3,}|_{3,})$/.test(i)?"\\"+i:i:null}function Bn(i){return i.replace(/\\/g,"\\\\").replace(/\]/g,"\\]")}function Mn(i){return i.trim().replace(/\s/g,"%20").replace(/\)/g,"%29").replace(/</g,"%3C").replace(/>/g,"%3E")}class tn{constructor(t){u(this,"opts");u(this,"overlay");u(this,"scrollEl");u(this,"surface");u(this,"statusEl");u(this,"statusTextEl");u(this,"reloadBtn");u(this,"fileInput");u(this,"toolbarButtons",[]);u(this,"abort",new AbortController);u(this,"frontmatter","");u(this,"revision",null);u(this,"stale",!1);u(this,"saveTimer",null);u(this,"closed",!1);u(this,"history",[]);u(this,"historyIndex",-1);u(this,"applyingHistory",!1);this.opts=t,this.build(),this.load()}static open(t){return new tn(t)}build(){this.overlay=pt("doc-editor");const t=pt("doc-editor-bar"),e=pt("doc-editor-bar-left"),n=Fe("doc-editor-done","Done");n.addEventListener("click",()=>void this.close());const s=pt("doc-editor-path");s.textContent=this.opts.path,e.append(n,s),this.statusEl=pt("doc-editor-status"),this.statusTextEl=document.createElement("span"),this.reloadBtn=Fe("status-action-btn","Reload latest"),this.reloadBtn.hidden=!0,this.reloadBtn.addEventListener("click",()=>void this.reloadLatest()),this.statusEl.append(this.statusTextEl,this.reloadBtn),this.setStatus("idle"),t.append(e,this.statusEl);const o=this.buildToolbar();this.scrollEl=pt("doc-editor-scroll"),this.surface=pt("markdown-body doc-editor-surface"),this.surface.contentEditable="true",this.surface.spellcheck=!0,this.scrollEl.appendChild(this.surface),this.fileInput=document.createElement("input"),this.fileInput.type="file",this.fileInput.accept="image/*",this.fileInput.style.display="none",this.fileInput.addEventListener("change",()=>{var c;const r=(c=this.fileInput.files)==null?void 0:c[0];r&&this.insertImageFile(r),this.fileInput.value=""}),this.overlay.append(t,o,this.scrollEl,this.fileInput),document.body.appendChild(this.overlay);const a=this.abort.signal;this.surface.addEventListener("input",()=>this.handleInput(),{signal:a}),this.surface.addEventListener("keyup",()=>this.updateToolbarState(),{signal:a}),this.surface.addEventListener("mouseup",()=>this.updateToolbarState(),{signal:a}),this.surface.addEventListener("click",r=>this.handleSurfaceClick(r),{signal:a}),document.addEventListener("selectionchange",()=>this.updateToolbarState(),{signal:a}),this.surface.addEventListener("paste",r=>this.onPaste(r),{signal:a}),this.surface.addEventListener("dragover",r=>r.preventDefault(),{signal:a}),this.surface.addEventListener("drop",r=>this.onDrop(r),{signal:a}),window.addEventListener("keydown",r=>{if(r.key==="Escape"&&document.activeElement!==this.surface&&this.close(),(r.metaKey||r.ctrlKey)&&r.key.toLowerCase()==="s"&&(r.preventDefault(),this.save()),(r.metaKey||r.ctrlKey)&&r.key.toLowerCase()==="z"){r.preventDefault(),r.shiftKey?this.redoHistory():this.undoHistory();return}if(r.ctrlKey&&!r.metaKey&&r.key.toLowerCase()==="y"){r.preventDefault(),this.redoHistory();return}this.handleEmbedDeleteKey(r)},{signal:a})}buildToolbar(){const t=pt("doc-editor-toolbar"),e=s=>{const o=pt("doc-editor-tb-group");return o.append(...s),o},n=(s,o,a,r=!1)=>{const c=Fe("doc-editor-tb-btn",r?"":s);return r&&(c.innerHTML=V[s]??"",c.classList.add("icon")),c.title=o,c.addEventListener("mousedown",l=>{l.preventDefault(),this.surface.focus(),a(),this.updateToolbarState(),this.scheduleSave()}),this.toolbarButtons.push(c),c};return t.append(e([n("¶","Paragraph",()=>this.formatBlock("P")),ut(n("H1","Heading 1",()=>this.formatBlock("H1")),"block","H1"),ut(n("H2","Heading 2",()=>this.formatBlock("H2")),"block","H2"),ut(n("H3","Heading 3",()=>this.formatBlock("H3")),"block","H3")]),e([ut(n("B","Bold (Cmd+B)",()=>document.execCommand("bold")),"command","bold"),ut(n("I","Italic (Cmd+I)",()=>document.execCommand("italic")),"command","italic"),ut(n("</>","Inline code",()=>this.toggleInlineCode()),"inline","CODE")]),e([ut(n("❝","Quote",()=>this.formatBlock("BLOCKQUOTE")),"block","BLOCKQUOTE"),ut(n("•","Bulleted list",()=>document.execCommand("insertUnorderedList")),"command","insertUnorderedList"),ut(n("1.","Numbered list",()=>document.execCommand("insertOrderedList")),"command","insertOrderedList"),n("{ }","Code block",()=>this.insertCodeBlock())]),e([n("↗","Link",()=>this.insertLink()),n("image","Image",()=>this.fileInput.click(),!0),n("―","Divider",()=>document.execCommand("insertHorizontalRule"))])),t}async load(t=this.opts.initialScrollTop??0,e=this.opts.initialScrollProgress){try{const n=await k.readFileWithRevision(this.opts.rootId,this.opts.path);this.revision=n.revision,this.stale=!1;const s=n.content,{fm:o,body:a}=Zo(s);this.frontmatter=o;const{html:r}=ne(a,{rootId:this.opts.rootId,docPath:this.opts.path});this.surface.innerHTML=r||"<p></p>",this.decorateEmbeds(),document.execCommand("defaultParagraphSeparator",!1,"p"),this.recordHistorySnapshot(!0),this.updateToolbarState(),this.setStatus("idle"),requestAnimationFrame(()=>{if(!this.closed){if(typeof e=="number"){Go(this.scrollEl,e);return}this.scrollEl.scrollTop=t}})}catch{this.surface.innerHTML="<p></p>",this.setStatus("error","Could not load file");return}}decorateEmbeds(){this.normalizeEmbedSpacing(),this.surface.querySelectorAll("figure.board-embed").forEach(t=>{t.contentEditable="false",t.tabIndex=0,t.setAttribute("role","button"),t.setAttribute("aria-label",`Board embed ${t.dataset.src??""}. Press Delete to remove.`),t.classList.add("embed-chip");const e=t.dataset.region?" · region":"";t.innerHTML=`<span class="embed-chip-icon">▦</span> Board embed: <strong>${t.dataset.src??""}</strong>${e}<span class="embed-chip-remove" aria-hidden="true">Delete</span>`,this.ensureEmbedCaretSpace(t)})}ensureEmbedCaretSpace(t){Tt(t.previousElementSibling)||t.before(Zt()),Tt(t.nextElementSibling)||t.after(Zt())}normalizeEmbedSpacing(){const t=Array.from(this.surface.querySelectorAll("figure.board-embed"));for(const n of t)In(n.previousElementSibling),In(n.nextElementSibling);let e=!1;for(const n of Array.from(this.surface.children)){if(!Tt(n)){e=!1;continue}if(hs(n)){n.classList.remove("embed-caret-spacer"),e=!1;continue}if(e){n.remove();continue}e=!0}}handleSurfaceClick(t){const e=t.target,n=e==null?void 0:e.closest("figure.board-embed.embed-chip");!n||!this.surface.contains(n)||(t.preventDefault(),this.selectEmbed(n))}selectEmbed(t){this.surface.querySelectorAll(".embed-chip.selected").forEach(s=>s.classList.remove("selected")),t.classList.add("selected");const e=document.createRange();e.selectNode(t);const n=window.getSelection();n==null||n.removeAllRanges(),n==null||n.addRange(e),t.focus()}clearEmbedSelection(){this.surface.querySelectorAll(".embed-chip.selected").forEach(t=>t.classList.remove("selected"))}handleEmbedDeleteKey(t){if(t.key!=="Backspace"&&t.key!=="Delete")return!1;const e=document.activeElement,s=(e==null?void 0:e.closest("figure.board-embed.embed-chip.selected"))??this.surface.querySelector("figure.board-embed.embed-chip.selected")??this.embedAdjacentToCaret(t.key);if(!s||!this.surface.contains(s))return!1;t.preventDefault();const o=t.key==="Backspace"?s.previousElementSibling:s.nextElementSibling;return s.remove(),this.clearEmbedSelection(),this.placeCaretInBlock(Tt(o)?o:this.surface),this.recordHistorySnapshot(),this.scheduleSave(),!0}embedAdjacentToCaret(t){const e=window.getSelection();if(!e||e.rangeCount===0||!e.isCollapsed)return null;const n=e.anchorNode,s=Jo(n,"P");if(!s||!this.surface.contains(s)||!Tt(s))return null;const o=t==="Backspace"?s.previousElementSibling:s.nextElementSibling;return o instanceof HTMLElement&&o.matches("figure.board-embed.embed-chip")?o:null}handleInput(){this.applyingHistory||(this.clearEmbedSelection(),this.recordHistorySnapshot(),this.scheduleSave())}placeCaretInBlock(t){const e=document.createRange();e.selectNodeContents(t),e.collapse(!1);const n=window.getSelection();n==null||n.removeAllRanges(),n==null||n.addRange(e),this.surface.focus()}scheduleSave(){this.normalizeEmbedSpacing(),this.saveTimer!==null&&clearTimeout(this.saveTimer),this.setStatus("saving"),this.saveTimer=window.setTimeout(()=>{this.saveTimer=null,this.save()},700)}async save(){if(this.saveTimer!==null&&(clearTimeout(this.saveTimer),this.saveTimer=null),this.stale)return this.setStatus("stale","Changed elsewhere — reload before saving"),!1;this.normalizeEmbedSpacing();const t=Wo(this.surface),e=this.frontmatter?`${this.frontmatter}
|
|
35
|
-
|
|
36
|
-
${t}`:t;try{return this.setStatus("saving"),this.revision=await k.writeFile(this.opts.rootId,this.opts.path,e,this.revision??void 0),this.stale=!1,this.setStatus("saved"),!0}catch(n){return n instanceof se?(this.revision=n.currentRevision,this.stale=!0,this.setStatus("stale","Changed elsewhere — reload before saving"),!1):(this.setStatus("error","Save failed"),!1)}}async close(){if(this.closed||!await this.save())return;this.closed=!0;const e=this.scrollEl.scrollTop,n=Rn(this.scrollEl);this.abort.abort(),this.overlay.remove(),this.opts.onClose(e,n)}markStale(){this.stale=!0,this.setStatus("stale","Changed elsewhere — reload before saving")}async reloadLatest(){window.confirm("Discard local edits and reload the latest version?")&&(this.saveTimer!==null&&(clearTimeout(this.saveTimer),this.saveTimer=null),await this.load(this.scrollEl.scrollTop,Rn(this.scrollEl)))}formatBlock(t){document.execCommand("formatBlock",!1,`<${t}>`)}insertLink(){const t=window.getSelection(),e=t&&!t.isCollapsed?t.toString().trim():"",n=window.prompt("Link URL","https://");if(!n)return;const s=n.trim();s&&(e?document.execCommand("createLink",!1,s):document.execCommand("insertHTML",!1,`<a href="${$n(s)}">${sa(s)}</a>`))}toggleInlineCode(){const t=window.getSelection();if(!t||t.rangeCount===0||t.isCollapsed)return;const e=t.getRangeAt(0),n=je(e.commonAncestorContainer,"CODE");if(n){const o=n.parentNode;for(;n.firstChild;)o.insertBefore(n.firstChild,n);o.removeChild(n);return}const s=document.createElement("code");s.appendChild(e.extractContents()),e.insertNode(s),e.setStartAfter(s),e.collapse(!0),t.removeAllRanges(),t.addRange(e)}insertCodeBlock(){document.execCommand("insertHTML",!1,'<pre class="code-block"><code>code</code></pre><p><br></p>')}updateToolbarState(){const t=window.getSelection(),e=(t==null?void 0:t.anchorNode)??null,n=!!e&&this.surface.contains(e);for(const s of this.toolbarButtons){const o=s.dataset.stateType,a=s.dataset.stateValue;let r=!1;if(n&&o==="command"&&a)try{r=document.queryCommandState(a)}catch{r=!1}else(n&&o==="block"&&a||n&&o==="inline"&&a)&&(r=!!je(e,a));s.classList.toggle("active",r),s.setAttribute("aria-pressed",r?"true":"false")}}onPaste(t){var s,o;const e=oa(((s=t.clipboardData)==null?void 0:s.getData("text/plain"))??"");if(e){t.preventDefault(),this.insertSpecialBlock(e);return}const n=(o=t.clipboardData)==null?void 0:o.items;if(n){for(const a of n)if(a.type.startsWith("image/")){const r=a.getAsFile();if(r){t.preventDefault(),this.insertImageFile(r);return}}}}insertSpecialBlock(t){this.surface.focus();const e=ra(t);this.insertBlockNode(e),this.decorateEmbeds(),this.selectEmbed(e),this.recordHistorySnapshot(),this.updateToolbarState(),this.scheduleSave()}insertBlockNode(t){const e=window.getSelection(),n=e&&e.rangeCount>0?e.getRangeAt(0):null;if(!n||!this.surface.contains(n.commonAncestorContainer)){this.surface.append(t,Zt()),this.placeCaretInBlock(this.surface.lastElementChild??this.surface);return}const s=Qo(this.surface,n.commonAncestorContainer);if(s&&ta(s)&&!Tt(s)){this.replaceBlockWithInsertedNode(s,n,t);return}n.deleteContents();const o=Zt();n.insertNode(o),n.insertNode(t),this.placeCaretInBlock(o)}replaceBlockWithInsertedNode(t,e,n){const s=document.createRange();s.selectNodeContents(t),s.setEnd(e.startContainer,e.startOffset);const o=document.createRange();o.selectNodeContents(t),o.setStart(e.endContainer,e.endOffset);const a=Ln(t,s.cloneContents()),r=Ln(t,o.cloneContents()),c=t.parentNode;if(!c)return;a&&c.insertBefore(a,t),c.insertBefore(n,t),r&&c.insertBefore(r,t),c.removeChild(t);const l=r??Zt();r||c.insertBefore(l,n.nextSibling),this.placeCaretInBlock(l)}onDrop(t){var n,s;const e=(s=(n=t.dataTransfer)==null?void 0:n.files)==null?void 0:s[0];e&&e.type.startsWith("image/")&&(t.preventDefault(),this.insertImageFile(e))}async insertImageFile(t){this.setStatus("saving","Uploading image…");try{const e=await na(t),n=await k.uploadAsset(this.opts.rootId,t.name,e),s=Ke(this.opts.path,n),o=k.assetUrl(this.opts.rootId,n),a=t.name.replace(/\.[^.]+$/,"");this.surface.focus(),document.execCommand("insertHTML",!1,`<img src="${o}" data-md-src="${s}" alt="${$n(a)}" /><p><br></p>`),this.recordHistorySnapshot(),await this.save()}catch{this.setStatus("error","Image upload failed")}}recordHistorySnapshot(t=!1){if(this.applyingHistory)return;this.normalizeEmbedSpacing();const e=this.surface.innerHTML;if(t){this.history=[e],this.historyIndex=0;return}this.history[this.historyIndex]!==e&&(this.history=this.history.slice(0,this.historyIndex+1),this.history.push(e),this.history.length>100&&this.history.shift(),this.historyIndex=this.history.length-1)}undoHistory(){this.historyIndex<=0||this.applyHistorySnapshot(this.historyIndex-1)}redoHistory(){this.historyIndex>=this.history.length-1||this.applyHistorySnapshot(this.historyIndex+1)}applyHistorySnapshot(t){const e=this.history[t];e!==void 0&&(this.applyingHistory=!0,this.historyIndex=t,this.surface.innerHTML=e,this.decorateEmbeds(),this.clearEmbedSelection(),this.placeCaretInBlock(this.surface.lastElementChild??this.surface),this.applyingHistory=!1,this.updateToolbarState(),this.scheduleSave())}setStatus(t,e){const n={idle:"",saving:e??"Saving…",saved:"Saved",stale:e??"Changed elsewhere",error:e??"Couldn't save"};this.statusTextEl.textContent=n[t],this.statusEl.dataset.state=t,this.reloadBtn.hidden=t!=="stale"}}function pt(i){const t=document.createElement("div");return t.className=i,t}function Fe(i,t){const e=document.createElement("button");return e.className=i,e.textContent=t,e}function ut(i,t,e){return i.dataset.stateType=t,i.dataset.stateValue=e,i.setAttribute("aria-pressed","false"),i}function Zt(){const i=document.createElement("p");return i.className="embed-caret-spacer",i.appendChild(document.createElement("br")),i}function Tt(i){return!!i&&i.tagName==="P"&&i.classList.contains("embed-caret-spacer")}function In(i){Tt(i)&&hs(i)&&i.classList.remove("embed-caret-spacer")}function hs(i){for(const t of Array.from(i.childNodes)){if(t.nodeType===Node.TEXT_NODE&&(t.textContent??"").trim())return!0;if(t.nodeType!==Node.ELEMENT_NODE)continue;const e=t;if(e.tagName!=="BR"&&((e.textContent??"").trim()||["IMG","FIGURE","HR"].includes(e.tagName)))return!0}return!1}function Rn(i){const t=Math.max(1,i.scrollHeight-i.clientHeight);return i.scrollTop/t}function Go(i,t){const e=Math.max(0,i.scrollHeight-i.clientHeight);i.scrollTop=Math.max(0,Math.min(1,t))*e}function Zo(i){if(!i.startsWith("---"))return{fm:"",body:i};const t=i.indexOf(`
|
|
37
|
-
---`,3);if(t===-1)return{fm:"",body:i};const e=i.slice(0,t+4),n=i.slice(t+4).replace(/^\n+/,"");return{fm:e,body:n}}function je(i,t){let e=i;for(;e;){if(e.nodeType===Node.ELEMENT_NODE&&e.tagName===t)return e;e=e.parentNode}return null}function Jo(i,t){let e=i;for(;e;){if(e.nodeType===Node.ELEMENT_NODE&&e.matches(t))return e;e=e.parentNode}return null}function Qo(i,t){let e=t;for(;e&&e.parentNode!==i;)e=e.parentNode;return e instanceof HTMLElement?e:null}function ta(i){return i.tagName==="P"||i.tagName==="DIV"}function Ln(i,t){if(!ea(t))return null;const e=document.createElement(i.tagName);return i.tagName==="P"&&i.classList.contains("embed-caret-spacer")&&(e.className="embed-caret-spacer"),e.appendChild(t),e}function ea(i){for(const t of Array.from(i.childNodes)){if(t.nodeType===Node.TEXT_NODE&&(t.textContent??"").trim())return!0;if(t.nodeType!==Node.ELEMENT_NODE)continue;const e=t;if(e.tagName!=="BR"&&!(e.tagName==="P"&&e.classList.contains("embed-caret-spacer"))&&((e.textContent??"").trim()||["IMG","FIGURE","HR"].includes(e.tagName)))return!0}return!1}function na(i){return new Promise((t,e)=>{const n=new FileReader;n.onload=()=>{const s=n.result;t(s.slice(s.indexOf(",")+1))},n.onerror=()=>e(n.error),n.readAsDataURL(i)})}function $n(i){return i.replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<")}function sa(i){return i.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}const ia={board:i=>{const t=ts(i);return t.src?{kind:"board",src:t.src,region:t.region,height:t.height,title:t.title}:null}};function oa(i){const t=aa(i);if(!t)return null;const e=ia[t.language];return e?e(t.body):null}function aa(i){const e=i.trim().match(/^(`{3,})\s*([^\s`]+)?\s*\n([\s\S]*?)\n\1\s*$/);return e?{language:(e[2]??"").toLowerCase(),body:e[3]}:null}function ra(i){switch(i.kind){case"board":return ca(i)}}function ca(i){const t=document.createElement("figure");return t.className="board-embed",t.dataset.src=i.src,t.dataset.height=String(i.height),i.region&&(t.dataset.region=i.region.join(",")),i.title&&(t.dataset.title=i.title),t}class en{constructor(t){u(this,"opts");u(this,"root");u(this,"statusEl");u(this,"statusTextEl");u(this,"reloadBtn");u(this,"textarea");u(this,"abort",new AbortController);u(this,"revision",null);u(this,"stale",!1);u(this,"saveTimer",null);u(this,"closed",!1);this.opts=t,this.build(),this.load()}static open(t){return new en(t)}build(){this.root=zt("doc-editor text-file-editor");const t=zt("doc-editor-bar"),e=zt("doc-editor-bar-left"),n=Pn("doc-editor-done","Done");n.addEventListener("click",()=>void this.close());const s=zt("doc-editor-path");s.textContent=this.opts.path,e.append(n,s),this.statusEl=zt("doc-editor-status"),this.statusTextEl=document.createElement("span"),this.reloadBtn=Pn("status-action-btn","Reload latest"),this.reloadBtn.hidden=!0,this.reloadBtn.addEventListener("click",()=>void this.reloadLatest()),this.statusEl.append(this.statusTextEl,this.reloadBtn),this.setStatus("idle"),t.append(e,this.statusEl);const o=zt("doc-editor-scroll text-file-editor-scroll");this.textarea=document.createElement("textarea"),this.textarea.className="text-file-editor-textarea",this.textarea.spellcheck=!1,o.appendChild(this.textarea),this.root.append(t,o),this.opts.host.appendChild(this.root);const a=this.abort.signal;this.textarea.addEventListener("input",()=>this.scheduleSave(),{signal:a}),window.addEventListener("keydown",r=>{if((r.metaKey||r.ctrlKey)&&r.key.toLowerCase()==="s"){r.preventDefault(),this.save();return}r.key==="Escape"&&document.activeElement!==this.textarea&&this.close()},{signal:a})}async load(){try{const t=await k.readFileWithRevision(this.opts.rootId,this.opts.path);this.revision=t.revision,this.stale=!1,this.textarea.value=t.content,this.setStatus("idle"),requestAnimationFrame(()=>this.textarea.focus())}catch{this.textarea.value="",this.setStatus("error","Could not load file")}}scheduleSave(){this.saveTimer!==null&&clearTimeout(this.saveTimer),this.setStatus("saving"),this.saveTimer=window.setTimeout(()=>{this.saveTimer=null,this.save()},700)}async save(){if(this.saveTimer!==null&&(clearTimeout(this.saveTimer),this.saveTimer=null),this.stale)return this.setStatus("stale","Changed elsewhere — reload before saving"),!1;try{return this.setStatus("saving"),this.revision=await k.writeFile(this.opts.rootId,this.opts.path,this.textarea.value,this.revision??void 0),this.stale=!1,this.setStatus("saved"),!0}catch(t){return t instanceof se?(this.revision=t.currentRevision,this.stale=!0,this.setStatus("stale","Changed elsewhere — reload before saving"),!1):(this.setStatus("error","Save failed"),!1)}}async close(){return this.closed?!0:await this.save()?(this.closed=!0,this.abort.abort(),this.root.remove(),this.opts.onClose(),!0):!1}destroy(){this.saveTimer!==null&&clearTimeout(this.saveTimer),this.saveTimer=null,this.closed=!0,this.abort.abort(),this.root.remove()}async reloadLatest(){window.confirm("Discard local edits and reload the latest version?")&&(this.saveTimer!==null&&(clearTimeout(this.saveTimer),this.saveTimer=null),await this.load())}setStatus(t,e){const n={idle:e??"Ready",saving:e??"Saving…",saved:e??"Saved",error:e??"Save failed",stale:e??"Changed elsewhere"};this.statusEl.dataset.state=t,this.statusTextEl.textContent=n[t],this.reloadBtn.hidden=t!=="stale"}}function zt(i){const t=document.createElement("div");return t.className=i,t}function Pn(i,t){const e=document.createElement("button");return e.className=i,e.type="button",e.textContent=t,e}const Dn="docs:theme",An="docs:sidebar-collapsed",Nn="docs:sidebar-width",la=220,da=420,We=[{group:"Brand Colors",name:"100",variable:"--brand-100",value:"#FFFFFF",description:"lightest brand tone"},{group:"Brand Colors",name:"200",variable:"--brand-200",value:"#4DABF7",description:"light azure accent"},{group:"Brand Colors",name:"300",variable:"--brand-300",value:"#0078D4",description:"primary azure blue"},{group:"Brand Colors",name:"400",variable:"--brand-400",value:"#495057",description:"deep slate gray"},{group:"Brand Colors",name:"500",variable:"--brand-500",value:"#0078D4",description:"primary azure blue"},{group:"Brand Colors",name:"600",variable:"--brand-600",value:"#005A9E",description:"darker azure blue"},{group:"Neutral Colors",name:"100",variable:"--neutral-100",value:"#F3F2F1",description:"page background"},{group:"Neutral Colors",name:"200",variable:"--neutral-200",value:"#EAEAEA",description:"subtle surface"},{group:"Neutral Colors",name:"300",variable:"--neutral-300",value:"#D1D1D1",description:"soft border"},{group:"Neutral Colors",name:"400",variable:"--neutral-400",value:"#C8C8C8",description:"medium gray"},{group:"Neutral Colors",name:"500",variable:"--neutral-500",value:"#6E6E6E",description:"neutral mid-tone"},{group:"Neutral Colors",name:"600",variable:"--neutral-600",value:"#7A7A7A",description:"secondary text"},{group:"Neutral Colors",name:"700",variable:"--neutral-700",value:"#495057",description:"deep slate gray"},{group:"Neutral Colors",name:"800",variable:"--neutral-800",value:"#4A4A4A",description:"strong text"},{group:"Neutral Colors",name:"900",variable:"--neutral-900",value:"#0F0F0F",description:"primary text"},{group:"Neutral Colors",name:"000",variable:"--neutral-000",value:"#FFFFFF",description:"white"},{group:"Semantic Colors",name:"Success Main",variable:"--success-main",value:"#28A745",description:"positive actions"},{group:"Semantic Colors",name:"Success Background",variable:"--success-bg",value:"#E6F4EA",description:"positive surface"},{group:"Semantic Colors",name:"Success Text",variable:"--success-text",value:"#1E7E34",description:"positive text"},{group:"Semantic Colors",name:"Warning Main",variable:"--warning-main",value:"#DC2626",description:"warning actions"},{group:"Semantic Colors",name:"Warning Background",variable:"--warning-bg",value:"#FEF2F2",description:"warning surface"},{group:"Semantic Colors",name:"Warning Text",variable:"--warning-text",value:"#FFFFFF",description:"warning text"},{group:"Semantic Colors",name:"Error Main",variable:"--error-main",value:"#CD0000",description:"destructive actions"},{group:"Semantic Colors",name:"Error Background",variable:"--error-bg",value:"#FFE5E5",description:"destructive surface"},{group:"Semantic Colors",name:"Error Text",variable:"--error-text",value:"#B91C1C",description:"destructive text"},{group:"Semantic Colors",name:"Info Main",variable:"--info-main",value:"#1080A6",description:"informational actions"},{group:"Semantic Colors",name:"Info Background",variable:"--info-bg",value:"#E0F7FA",description:"informational surface"},{group:"Semantic Colors",name:"Info Text",variable:"--info-text",value:"#0369A1",description:"informational text"}];class ha{constructor(t){u(this,"root");u(this,"layout");u(this,"main");u(this,"topbar");u(this,"contentScroll");u(this,"content");u(this,"sidebar");u(this,"searchBtn");u(this,"cartBtn");u(this,"editBtn");u(this,"themeBtn");u(this,"palette");u(this,"paletteInput");u(this,"paletteResults");u(this,"cartPopover");u(this,"searchIndex",[]);u(this,"searchIndexing",!1);u(this,"searchIndexRun",0);u(this,"capabilityCart",new Map);u(this,"selectedTokens",new Set);u(this,"currentEditableFile");u(this,"workspace",{projects:[]});u(this,"treesByRoot",new Map);u(this,"treesByProject",new Map);u(this,"cleanup");u(this,"updateRenderedTheme");u(this,"editor");u(this,"docEditor");u(this,"textFileEditor");u(this,"editorHost");u(this,"editingId");u(this,"lastNonBoardHash","#/");u(this,"lastNonBoardScrollTop",0);u(this,"pendingScrollRestore");u(this,"sidebarResizeHandle");this.root=t,this.applyInitialTheme(),this.layout=f("div","shell");const e={onAddRepo:()=>void this.addRepoDocs(),onRemoveProject:(n,s)=>void this.promptRemoveProject(n,s),onCreateDirectory:(n,s)=>this.promptNewDirectory(n,s),onCreateDoc:(n,s)=>this.promptNewDoc(n,s),onCreateBoard:(n,s)=>this.promptNewBoard(n,s),onCreateCapability:(n,s)=>this.promptNewCapability(n,s),onCreateDocsFolder:(n,s)=>void this.createDocsFolder(n,s),onCreateProjectDirectory:n=>void this.promptNewProjectDirectory(n),onCreateProjectDoc:n=>void this.promptNewProjectDoc(n),onCreateProjectBoard:n=>void this.promptNewProjectBoard(n),onRenameDirectory:(n,s)=>void this.promptRenameDirectory(n,s),onDeleteDirectory:(n,s)=>void this.promptDeleteDirectory(n,s),onOpenDoc:(n,s)=>{location.hash=O(n,s)},onEditDoc:(n,s)=>this.openDocEditor({rootId:n,path:s}),onOpenTextFile:(n,s)=>{this.openTextFileEditor({rootId:n,path:s})},onRenameDoc:(n,s)=>void this.promptRenameDoc(n,s),onDeleteDoc:(n,s)=>void this.promptDeleteDoc(n,s),onOpenBoard:(n,s)=>{location.hash=Z(n,s)},onRenameBoard:(n,s)=>void this.promptRenameBoard(n,s),onDeleteBoard:(n,s)=>void this.promptDeleteBoard(n,s),onMoveDoc:(n,s,o)=>void this.moveDoc(n,s,o),onMoveBoard:(n,s,o)=>void this.moveBoard(n,s,o),onMoveDirectory:(n,s,o)=>void this.moveDirectory(n,s,o)};this.sidebar=new Ds(this.layout,e),this.sidebarResizeHandle=f("button","sidebar-resize-handle"),this.sidebarResizeHandle.type="button",this.sidebarResizeHandle.innerHTML=V.arrowBoth,this.sidebarResizeHandle.setAttribute("aria-label","Resize sidebar"),this.sidebarResizeHandle.title="Resize sidebar",this.main=f("div","shell-main"),this.topbar=this.createTopbar(),this.contentScroll=f("div","content-scroll"),this.content=f("div","content"),this.contentScroll.appendChild(this.content),this.main.append(this.topbar,this.contentScroll),this.layout.appendChild(this.sidebarResizeHandle),this.layout.appendChild(this.main),this.root.appendChild(this.layout),window.addEventListener("hashchange",()=>this.route()),window.addEventListener("keydown",n=>this.handleGlobalKeydown(n)),localStorage.getItem(An)==="true"&&this.setSidebarCollapsed(!0),this.applySidebarWidth(this.readSidebarWidth()),this.setupSidebarResize(),this.refreshContentIndex().then(()=>this.route())}route(t={}){const e=t.preserveScroll?this.contentScroll.scrollTop:this.pendingScrollRestore;this.pendingScrollRestore=void 0;const n=location.hash||"#/";this.sidebar.setActive(n);const s=n.replace(/^#/,"");this.setEditableFile(void 0);const o=s.match(/^\/root\/([^/]+)\/boards\/([^?]+)(?:\?(.*))?$/);if(o){this.lastNonBoardScrollTop=this.contentScroll.scrollTop,this.updateTopbarCart(!0),this.closeCartPopover(),this.openEditor(decodeURIComponent(o[1]),decodeURIComponent(o[2]),$a(o[3]));return}if(this.lastNonBoardHash=n,this.exitEditor(),this.layout.style.display="",this.updateTopbarCart(/\/root\/[^/]+\/boards/.test(s)),/\/root\/[^/]+\/boards/.test(s)&&this.closeCartPopover(),s==="/"||s==="")return void this.renderWorkspaceHome(e);const a=s.match(/^\/root\/([^/]+)\//);if(a){const g=decodeURIComponent(a[1]);if(!this.docsRootById(g))return void this.renderMessage("Project not found","This link points to a docs root that isn't in your workspace. Add the project and try again.")}const r=s.match(/^\/root\/([^/]+)\/docs\/(.+)$/);if(r)return void this.renderDoc(decodeURIComponent(r[1]),decodeURIComponent(r[2]),e);const c=s.match(/^\/root\/([^/]+)\/boards$/);if(c)return void this.renderBoardList(decodeURIComponent(c[1]),e);const l=s.match(/^\/project\/([^/]+)\/boards$/);if(l)return void this.renderProjectBoardList(decodeURIComponent(l[1]),e);const d=s.match(/^\/root\/([^/]+)\/capabilities$/);if(d)return void this.renderCapabilitiesIndex(decodeURIComponent(d[1]),e);const h=s.match(/^\/root\/([^/]+)\/capabilities\/(.+)$/);if(h)return void this.renderCapabilityDoc(decodeURIComponent(h[1]),decodeURIComponent(h[2]),e);const p=s.match(/^\/project\/([^/]+)$/);if(p)return void this.renderProjectWiki(decodeURIComponent(p[1]),e);const m=s.match(/^\/root\/([^/]+)\/directory\/(.+)$/);if(m){const g=decodeURIComponent(m[1]),y=decodeURIComponent(m[2]),b=this.docsRootById(g);if(b)return void this.renderProjectWiki(b.projectId,e,g,y)}if(s==="/tokens")return void this.renderTokenLibrary(e);this.renderMessage("Not found",`No route for ${s}`)}async renderWorkspaceHome(t){this.disposeContent(),this.content.classList.add("content-wide");const e=f("div","page home-page"),n=f("div","home-layout"),s=f("div","home-guide"),o=f("h1","home-hero-title");o.textContent="ViteBoard";const a=f("p","home-hero-sub");a.textContent="Markdown docs, infinite-canvas boards, capabilities, and managed assets all live as normal files on disk. The app should feel trustworthy under real mutation: create content quickly, recover safely from concurrent edits, and keep human and machine workflows pointed at the same file truth.",s.append(o,a);const r=f("div","home-agent-callout"),c=f("p","home-agent-copy");c.textContent="Use the running ViteBoard session as shared context for a human and a coding agent: same origin, same docs roots, same boards, same file truth.";const l=f("button","btn home-agent-btn");l.type="button",l.textContent="Copy agent prompt",l.addEventListener("click",()=>void this.copySetupText(`Connect to ViteBoard at ${window.location.origin} and follow its machine entry at ${window.location.origin}/llms.txt. Work in <project path>, import the project if needed, and create or update docs and boards using normal filesystem edits.`,"Agent prompt copied")),r.append(c,l),s.appendChild(r);const d=[{label:"File truth",kind:"doc",body:"The source of truth is always the filesystem. Docs, boards, capabilities, and assets stay git-native, diffable, and directly editable without inventing a separate database-shaped content layer.",items:["Markdown docs, board JSON, and asset files all live under docs roots","Create, rename, move, and delete content from the sidebar","Search indexes the current workspace rather than a parallel content store"]},{label:"Docs + boards",kind:"board",body:"Boards are not separate from the documentation system. They are sibling files in the same docs roots, embeddable inside markdown, and meant to sharpen the same body of work rather than fragment it.",items:["Open a board from the sidebar, work full-screen, then drop back into docs","Embed a board region into markdown with fenced board blocks",'Use "Copy embed" in the board editor to capture the current region',"Keep visual planning and written explanation in the same repo structure"]},{label:"Collaboration safety",kind:"capability",body:"The product now assumes live mutation is normal. Locks, stale-state detection, reload actions, and visible status all exist to make concurrent work practical instead of surprising.",items:["Docs and boards surface explicit reload actions when stale","Revision-aware saves block silent overwrites","Human and machine actors can cooperate through the same running workspace"]},{label:"Machine workflow",kind:"",body:"ViteBoard is also a machine-readable workspace surface. The app can expose docs roots, boards, schema references, and live coordination routes while the files remain the only persistent truth.",items:["The local HTTP surface advertises machine docs and workspace discovery","Capabilities and selected docs can be assembled into prompts from the UI","The practical goal is faster real work, not another disconnected abstraction layer"]}];for(const v of d){const S=f("div","home-feature"),w=f("div","home-feature-header"),E=f("span",`home-feature-badge home-feature-badge-${v.kind||"neutral"}`);E.textContent=v.label,w.appendChild(E),S.appendChild(w);const M=f("p","home-feature-body");if(M.textContent=v.body,S.appendChild(M),v.items){const B=document.createElement("ul");B.className="home-feature-list";for(const T of v.items){const R=document.createElement("li");R.textContent=T,B.appendChild(R)}S.appendChild(B)}s.appendChild(S)}const h=f("div","home-shortcut-title");h.textContent="Current operating model",s.appendChild(h);const p=[["1. Add repo","Bring a local project into the workspace"],["2. Create docs/ if needed","Start the repo as a writable docs root"],["3. Right-click in the tree","Create docs, boards, directories, and capabilities in place"],["4. Open a board","Think visually, then embed the useful region back into docs"],["5. Cmd K / Ctrl K","Search across the current workspace"],["6. Stale state appears","Use Reload latest instead of guessing which copy is current"],["7. Copy agent prompt","Hand the same workspace context to an AI assistant"],["8. Git stays normal","The files on disk remain the real source of truth"]],m=f("div","home-shortcut-table");for(const[v,S]of p){const w=f("div","home-shortcut-row"),E=f("kbd","home-shortcut-key");E.textContent=v;const M=f("span","home-shortcut-desc");M.textContent=S,w.append(E,M),m.appendChild(w)}s.appendChild(m),n.appendChild(s);const g=f("div","home-panel"),y=f("div","home-panel-head"),b=f("div","home-panel-title");b.textContent="Active workspace";const x=f("button","btn primary home-panel-add");if(x.textContent="+ Add repo",x.addEventListener("click",()=>void this.addRepoDocs()),y.append(b,x),g.appendChild(y),this.workspace.projects.length===0){const v=f("div","home-panel-empty");v.textContent="No projects yet. Add a project root, create docs/ if needed, and start writing against the real filesystem.",g.appendChild(v)}else for(const v of this.workspace.projects){const S=f("div","home-panel-project"),w=f("a","home-panel-project-head");w.href=Bt(v.id);const E=f("span","home-panel-project-name");E.textContent=v.label;const M=v.docsRoots.reduce((q,et)=>{const F=this.treesByRoot.get(et.id)??[];return q+Jt(F).length},0),B=v.docsRoots.reduce((q,et)=>{const F=this.treesByRoot.get(et.id)??[];return q+vt(F).length},0),T=f("span","home-panel-project-meta"),R=[`${v.docsRoots.length} root${v.docsRoots.length===1?"":"s"}`];M&&R.push(`${M} doc${M===1?"":"s"}`),B&&R.push(`${B} board${B===1?"":"s"}`),T.textContent=R.join(" · "),w.append(E,T),S.appendChild(w);const A=6,Kt=v.docsRoots,Ie=Kt.slice(0,A),Pt=f("div","home-panel-roots");for(const q of Ie){const et=this.treesByRoot.get(q.id)??[],F=Ue(Xt(et)),kt=F?O(q.id,F):jt(q.id),nt=f("a","home-panel-root");nt.href=kt;const j=f("span","home-panel-root-path");j.textContent=q.displayPath;const ot=Jt(et).length,U=vt(et).length;if(ot||U){const at=f("span","home-panel-root-meta"),rt=[];ot&&rt.push(String(ot)),U&&rt.push(`${U}▦`),at.textContent=rt.join(" · "),nt.append(j,at)}else nt.appendChild(j);Pt.appendChild(nt)}if(Kt.length>A){const q=f("a","home-panel-more");q.href=Bt(v.id),q.textContent=`+ ${Kt.length-A} more roots`,Pt.appendChild(q)}S.appendChild(Pt),g.appendChild(S)}n.appendChild(g),e.appendChild(n),this.content.replaceChildren(e),this.contentScroll.scrollTop=t??0}async copySetupText(t,e){try{await navigator.clipboard.writeText(t),C(e)}catch{C("Could not access clipboard")}}async renderProjectWiki(t,e,n,s){this.disposeContent(),this.content.classList.add("content-wide");const o=this.workspace.projects.find(p=>p.id===t);if(!o){this.renderMessage("Project not found","This project is no longer registered.");return}const a=f("div","page wiki-page"),r=!!n,c=!!s;let l=!1;if(c&&n){const p=this.docsRootById(n);p&&(l=p.displayPath===s||p.displayPath.startsWith(s+"/"))}const d=f("div","wiki-head");if(c&&n){const p=[{label:"Home",href:"#/"}];p.push({label:o.label,href:Bt(o.id)});const m=s.split("/").filter(Boolean);for(let y=0;y<m.length;y++){const b=m.slice(0,y+1).join("/"),x=y===m.length-1;p.push({label:m[y],href:x?void 0:Se(n,b)})}d.appendChild(bt(p));const g=f("h1","wiki-title");g.textContent=m[m.length-1]||o.label,d.appendChild(g)}else if(r&&n){const p=this.docsRootById(n),m=[{label:"Home",href:"#/"}];if(m.push({label:o.label,href:Bt(o.id)}),p){const y=p.displayPath.split("/").filter(Boolean);for(let b=0;b<y.length;b++){const x=y.slice(0,b+1).join("/"),v=b===y.length-1;m.push({label:y[b],href:v?void 0:Se(n,x)})}}d.appendChild(bt(m));const g=f("h1","wiki-title");g.textContent=(p==null?void 0:p.displayPath.split("/").pop())||o.label,d.appendChild(g)}else{d.appendChild(bt([{label:"Home",href:"#/"},{label:o.label}]));const p=f("h1","wiki-title");p.textContent=o.label,d.appendChild(p)}if(!r&&!c){const p=o.docsRoots.reduce((b,x)=>{const v=this.treesByRoot.get(x.id)??[];return b+Jt(v).length},0),m=o.docsRoots.reduce((b,x)=>{const v=this.treesByRoot.get(x.id)??[];return b+vt(v).length},0),g=o.docsRoots.reduce((b,x)=>b+$t(this.treesByRoot.get(x.id)??[]).length,0),y=f("div","wiki-stats");for(const[b,x]of[[String(o.docsRoots.length),"docs roots"],[String(p),"documents"],[String(m),"boards"],[String(g),"capabilities"]]){const v=f("div","wiki-stat"),S=f("div","wiki-stat-val");S.textContent=b;const w=f("div","wiki-stat-label");w.textContent=x,v.append(S,w),y.appendChild(v)}d.appendChild(y)}a.appendChild(d);const h=n?o.docsRoots.filter(p=>p.id===n):o.docsRoots;for(const p of h){const m=this.treesByRoot.get(p.id)??[];let g=m;if(s&&!l){const w=(M,B)=>{for(const T of M){if(T.type==="dir"&&T.path===B)return T;if(T.type==="dir"){const R=w(T.children,B);if(R)return R}}return null},E=w(m,s);g=(E==null?void 0:E.type)==="dir"?E.children:[]}const y=Jt(g),b=vt(g),x=$t(g),v=f("div","wiki-root-section");if(!s||l){const w=f("div","wiki-root-head"),E=f("div","wiki-root-path");E.textContent=p.displayPath,w.appendChild(E),v.appendChild(w)}if(!y.length&&!b.length&&!x.length){const w=f("div","wiki-root-empty");w.textContent="No docs, boards, or capabilities yet. Right-click in the sidebar to add content.",v.appendChild(w),a.appendChild(v);continue}const S=f("div","wiki-root-cols");if(y.length>0){const w=f("div","wiki-col"),E=f("div","wiki-col-head");E.textContent=`Docs (${y.length})`,w.appendChild(E);const M=f("div","wiki-col-list");for(const B of y){const T=f("a","wiki-col-item wiki-col-item-doc");T.href=O(p.id,B.path),T.textContent=B.name==="index.md"?"Overview":B.name.replace(/\.md$/,"").replace(/[-_]/g," ");const R=f("span","wiki-col-item-path"),A=B.path.includes("/")?B.path.slice(0,B.path.lastIndexOf("/")):"";R.textContent=A,A&&T.appendChild(R),M.appendChild(T)}w.appendChild(M),S.appendChild(w)}if(b.length>0){const w=f("div","wiki-col"),E=f("div","wiki-col-head");E.textContent=`Boards (${b.length})`,w.appendChild(E);const M=f("div","wiki-col-list");for(const B of b){const T=G(B.path),R=f("a","wiki-col-item wiki-col-item-board");R.href=Z(p.id,T),R.textContent=T.split("/").pop().replace(/[-_]/g," ");const A=f("span","wiki-col-item-path");A.textContent=T.includes("/")?T.slice(0,T.lastIndexOf("/")):p.displayPath,R.appendChild(A),M.appendChild(R)}w.appendChild(M),S.appendChild(w)}if(x.length>0){const w=f("div","wiki-col"),E=f("div","wiki-col-head");E.textContent=`Capabilities (${x.length})`,w.appendChild(E);const M=f("div","wiki-col-list");for(const B of x){const T=f("a","wiki-col-item wiki-col-item-cap");T.href=Ft(p.id,B.entryPath),T.textContent=Ee(B.entryPath),M.appendChild(T)}w.appendChild(M),S.appendChild(w)}v.appendChild(S),a.appendChild(v)}this.content.replaceChildren(a),this.contentScroll.scrollTop=e??0}async renderDoc(t,e,n){if(this.disposeContent(),!this.docsRootById(t)){this.renderMessage("Docs root unavailable","This docs root is no longer registered. Re-add the project root if needed.");return}try{const s=await k.readFile(t,e);this.setEditableFile({rootId:t,path:e});const o=f("div","doc-page"),a=this.docsRootById(t),r=fe(this.workspace,t,{filePath:e});o.appendChild(bt(r));const c=f("div","");o.appendChild(c),this.content.replaceChildren(o);const{destroy:l,setTheme:d}=dn(c,s,{rootId:t,docPath:e});this.cleanup=l,this.updateRenderedTheme=d;const h=await k.getTree(t),p=ge(t,h),m=ye(p,O(t,e));m&&o.appendChild(m),this.contentScroll.scrollTop=n??0}catch{this.renderMessage("Document not found",e)}}openDocEditor(t){const e=this.contentScroll.scrollTop;this.topbar.classList.add("editing-doc"),this.closeCartPopover(),this.docEditor=tn.open({rootId:t.rootId,path:t.path,initialScrollTop:e,onClose:n=>{this.docEditor=void 0,this.topbar.classList.remove("editing-doc"),this.pendingScrollRestore=n,this.route()}})}async openTextFileEditor(t){this.textFileEditor&&!await this.textFileEditor.close()||(this.closeCartPopover(),this.textFileEditor=en.open({rootId:t.rootId,path:t.path,host:document.body,onClose:()=>{this.textFileEditor=void 0}}))}async renderCapabilityDoc(t,e,n){if(this.disposeContent(),!this.docsRootById(t)){this.renderMessage("Docs root unavailable","This docs root is no longer registered. Re-add the project root if needed.");return}const s=this.treesByRoot.get(t)??await k.getTree(t),o=pa(s,e)??e,a=Hn(s,e)??Hn(s,o);let r;try{r=await k.readFile(t,o),this.setEditableFile(void 0)}catch{this.renderMessage("Capability not found",e);return}const c=f("div","page"),l=this.docsRootById(t);l&&this.workspace.projects.find(F=>F.id===l.projectId);const d=Ee(o),h=fe(this.workspace,t,{filePath:o,currentLabel:d});c.appendChild(bt(h));const p=f("div","page-bar"),m=f("div","doc-actions capability-doc-actions"),{frontmatter:g}=ne(r),y=f("button","btn");y.classList.add("capability-detail-btn");const b=zn(t,a??ua(o),r,g);this.updateCartButton(y,b.path),y.addEventListener("click",()=>{this.toggleCapabilityCart(b),this.updateCartButton(y,b.path)});const x=f("button","btn primary");x.classList.add("capability-detail-btn"),x.textContent="Copy capability context",x.addEventListener("click",async()=>{await this.copyCapabilityContext(b,s)});const v=f("button","btn");v.classList.add("capability-detail-btn"),v.textContent="Open entry doc",v.addEventListener("click",()=>{location.hash=O(t,o)}),m.append(y,x,v),p.append(m),c.appendChild(p);const S=f("div","card-tags capability-meta-tags"),w=f("span","tag");if(w.textContent=b.displayPath,S.appendChild(w),b.kind){const F=f("span","tag");F.textContent=b.kind,S.appendChild(F)}c.appendChild(S);const E=fa(b.branchPath),M=E?await this.readOptionalFile(t,E):null;if(E&&M){const F=f("section","token-section capability-machine-section"),kt=f("div","page-bar"),nt=f("h2","");nt.textContent="Machine entrypoint";const j=f("div","doc-actions capability-doc-actions"),ot=f("button","btn primary");ot.textContent="Copy llms.txt",ot.addEventListener("click",async()=>{try{await navigator.clipboard.writeText(M),C("Capability machine entrypoint copied")}catch{C("Could not access clipboard")}}),j.appendChild(ot),kt.append(nt,j);const U=f("p","page-lead capability-entry-lead");U.textContent="This is the capability-local machine entrypoint. It should be the first file an LLM reads to understand what this capability branch is for, what files matter, and how to use it.";const at=document.createElement("pre");at.className="code-block";const rt=document.createElement("code");rt.textContent=M,at.appendChild(rt),F.append(kt,U,at),c.appendChild(F)}const B=f("div",""),T=f("section","token-section capability-entry-section"),R=f("h2","");R.textContent="Entry document";const A=f("p","page-lead capability-entry-lead");A.textContent="This markdown entry explains the capability branch. The capability itself may also include nested files, scripts, helpers, and other reusable execution material.",T.append(R,A,B),c.appendChild(T),this.content.replaceChildren(c);const{destroy:Kt,setTheme:Ie}=dn(B,r,{rootId:t,docPath:o});this.cleanup=Kt,this.updateRenderedTheme=Ie;const Pt=Fn(s,b.branchPath);if(Pt.length>1){const F=f("section","token-section capability-files-section"),kt=f("h2","");kt.textContent="Files in this capability";const nt=f("div","card-grid capability-files-grid");for(const j of Pt){const ot=j.path.endsWith(".md")&&j.path===b.entryPath,U=document.createElement(ot?"a":"button");U.className="card",U instanceof HTMLAnchorElement&&(U.href=O(t,j.path)),U instanceof HTMLButtonElement&&(U.type="button",U.addEventListener("click",()=>{this.openTextFileEditor({rootId:t,path:j.path})}));const at=f("div","card-title");at.textContent=j.path===b.entryPath?`${be(j.path)} (entry)`:j.path===E?`${be(j.path)} (machine entrypoint)`:be(j.path);const rt=f("div","card-desc");rt.textContent=qt(j.path),U.append(at,rt),nt.appendChild(U)}F.append(kt,nt),c.appendChild(F)}const q=ge(t,s),et=ye(q,Ft(t,b.branchPath));et&&c.appendChild(et),this.contentScroll.scrollTop=n??0}async renderCapabilitiesIndex(t,e){if(this.disposeContent(),!this.docsRootById(t)){this.renderMessage("Docs root unavailable","This docs root is no longer registered. Re-add the project root if needed.");return}this.content.classList.add("content-wide");const n=f("div","page capability-library"),s=fe(this.workspace,t,{currentLabel:"Capabilities"});n.appendChild(bt(s));const o=f("h1","");o.textContent="Capability library";const a=f("p","page-lead");a.textContent="Browse open-ended capability branches. Inspect their files, understand their entrypoints, and reuse them as working context, automations, or tool-bearing capability units.",n.append(o,a);const r=f("div","capability-layout"),c=f("div","card-grid capability-grid"),l=this.renderCapabilityCart();r.append(c,l),n.appendChild(r),this.content.replaceChildren(n);try{const d=this.treesByRoot.get(t)??await k.getTree(t),h=$t(d);for(const g of h){const y=await k.readFile(t,g.entryPath),{frontmatter:b}=ne(y),x=zn(t,g,y,b),v=document.createElement("article");v.className="card";const S=f("div","card-title"),w=document.createElement("a");w.href=Ft(t,g.branchPath),w.textContent=x.title,S.appendChild(w);const E=f("div","card-desc");E.textContent=x.summary,v.append(S,E);const M=f("div","card-tags"),B=f("span","tag");if(B.textContent=x.displayPath,M.appendChild(B),x.kind){const T=f("span","tag");T.textContent=x.kind,M.appendChild(T)}if(v.appendChild(M),x.tags.length){const T=f("div","card-tags");for(const R of x.tags){const A=f("span","tag");A.textContent=R,T.appendChild(A)}v.appendChild(T)}c.appendChild(v)}h.length===0&&c.appendChild(Ht("No capabilities yet."));const p=ge(t,d),m=ye(p,ps(t));m&&n.appendChild(m)}catch{c.appendChild(Ht("Could not load capabilities."))}this.contentScroll.scrollTop=e??0}renderTokenLibrary(t){this.disposeContent(),this.content.classList.add("content-wide");const e=f("div","page token-library"),n=f("div","page-bar"),s=f("h1","");s.textContent="Tokens";const o=f("div","token-actions"),a=f("button","btn");a.textContent="Select all",a.addEventListener("click",()=>{this.selectedTokens=new Set(We.map(d=>d.variable)),this.renderTokenLibrary(this.contentScroll.scrollTop)});const r=f("button","btn");r.textContent="Clear",r.addEventListener("click",()=>{this.selectedTokens.clear(),this.renderTokenLibrary(this.contentScroll.scrollTop)});const c=f("button","btn primary");c.textContent=`Copy selected (${this.selectedTokens.size})`,c.disabled=this.selectedTokens.size===0,c.addEventListener("click",()=>void this.copySelectedTokens()),o.append(a,r,c),n.append(s,o);const l=f("p","page-lead");l.textContent="Brand, neutral, and semantic CSS variables used throughout the app. Select tokens and copy them as a ready-to-paste :root block.",e.append(n,l);for(const d of["Brand Colors","Neutral Colors","Semantic Colors"]){const h=f("section","token-section"),p=f("h2","");p.textContent=d;const m=f("div","token-grid");for(const g of We.filter(y=>y.group===d)){const y=f("div","token-card");this.selectedTokens.has(g.variable)&&y.classList.add("selected");const b=f("label","token-select-zone"),x=document.createElement("input");x.type="checkbox",x.checked=this.selectedTokens.has(g.variable),x.addEventListener("change",()=>{x.checked?this.selectedTokens.add(g.variable):this.selectedTokens.delete(g.variable),this.renderTokenLibrary(this.contentScroll.scrollTop)});const v=f("span","token-swatch");v.style.background=g.value;const S=f("span","token-copy"),w=f("strong","");w.textContent=g.name;const E=f("code","");E.textContent=g.value;const M=f("small","");M.textContent=g.description,S.append(w,E,M),b.append(x,v,S);const B=f("button","token-copy-btn");B.type="button",B.innerHTML=V.copy,B.title=`Copy ${g.variable}`,B.setAttribute("aria-label",`Copy ${g.variable}`),B.addEventListener("click",T=>{T.stopPropagation(),this.copyToken(g)}),y.append(b,B),m.appendChild(y)}h.append(p,m),e.appendChild(h)}this.content.replaceChildren(e),this.contentScroll.scrollTop=t??0}async copySelectedTokens(){const t=We.filter(n=>this.selectedTokens.has(n.variable));if(t.length===0)return;const e=[":root {",...t.map(n=>` ${n.variable}: ${n.value};`),"}"].join(`
|
|
38
|
-
`);try{await navigator.clipboard.writeText(e),C(`${t.length} token${t.length===1?"":"s"} copied`)}catch{C("Could not access clipboard")}}async copyToken(t){try{await navigator.clipboard.writeText(`${t.variable}: ${t.value};`),C(`${t.variable} copied`)}catch{C("Could not access clipboard")}}renderCapabilityCart(t){const e=t??(()=>{const h=va(location.hash);h&&this.renderCapabilitiesIndex(h,this.contentScroll.scrollTop)}),n=f("aside","capability-cart"),s=f("div","capability-cart-head"),o=f("h2","");o.textContent="Capability toolbelt";const a=f("span","capability-cart-count");a.textContent=String(this.capabilityCart.size),s.append(o,a);const r=f("div","capability-cart-body");if(this.capabilityCart.size===0){const h=f("div","capability-cart-empty");h.textContent="Equip capabilities to build an active toolbelt for the current task.",r.appendChild(h)}else for(const h of this.capabilityCart.values()){const p=f("div","capability-cart-item"),m=f("div",""),g=f("div","capability-cart-item-title");g.textContent=h.title;const y=f("div","capability-cart-item-meta");y.textContent=h.displayPath,m.append(g,y);const b=f("button","capability-cart-remove");b.type="button",b.textContent="Remove",b.addEventListener("click",()=>{this.capabilityCart.delete(h.path),this.updateTopbarCart(),e()}),p.append(m,b),r.appendChild(p)}const c=f("div","capability-cart-actions"),l=f("button","btn primary");l.textContent="Copy toolbelt context",l.disabled=this.capabilityCart.size===0,l.addEventListener("click",()=>void this.copyCapabilityCartPrompt());const d=f("button","btn");return d.textContent="Clear",d.disabled=this.capabilityCart.size===0,d.addEventListener("click",()=>{this.capabilityCart.clear(),this.updateTopbarCart(),e()}),c.append(l,d),n.append(s,r,c),n}toggleCapabilityCart(t){this.capabilityCart.has(t.path)?this.capabilityCart.delete(t.path):this.capabilityCart.set(t.path,t),this.updateTopbarCart()}updateCartButton(t,e){const n=this.capabilityCart.has(e);t.textContent=n?"Unequip":"Equip",t.classList.toggle("primary",!n),t.classList.toggle("is-selected",n)}async copyCapabilityCartPrompt(){if(this.capabilityCart.size===0)return;const e=`Use this capability toolbelt as implementation guidance for the next coding task. These are integrated capability branches that may contain context, workflows, scripts, helpers, or other reusable execution material.
|
|
39
|
-
|
|
40
|
-
`+(await Promise.all([...this.capabilityCart.values()].map(async(n,s)=>{const o=this.treesByRoot.get(n.rootId)??await k.getTree(n.rootId);return`## ${s+1}. ${n.title}
|
|
41
|
-
|
|
42
|
-
${await this.capabilityBranchContext(n,o)}`}))).join(`
|
|
43
|
-
|
|
44
|
-
---
|
|
45
|
-
|
|
46
|
-
`);try{await navigator.clipboard.writeText(e),C(`${this.capabilityCart.size} capabilit${this.capabilityCart.size===1?"y":"ies"} copied`)}catch{C("Could not access clipboard")}}async copyCapabilityContext(t,e){try{const n=e??this.treesByRoot.get(t.rootId)??await k.getTree(t.rootId);await navigator.clipboard.writeText(await this.capabilityBranchContext(t,n)),C("Capability context copied")}catch{C("Could not access clipboard")}}async capabilityBranchContext(t,e){const n=Fn(e,t.branchPath).filter(o=>ga(o.path)),s=await Promise.all(n.map(async o=>{const a=await k.readFile(t.rootId,o.path);return`### ${be(o.path)}
|
|
47
|
-
|
|
48
|
-
${a.trim()}`}));return[`Capability: ${t.title}`,`Branch: ${t.displayPath}`,...t.kind?[`Kind: ${t.kind}`]:[],"",...s].join(`
|
|
49
|
-
`)}async readOptionalFile(t,e){try{return await k.readFile(t,e)}catch{return null}}async renderBoardList(t,e){if(this.disposeContent(),!this.docsRootById(t)){this.renderMessage("Docs root unavailable","This docs root is no longer registered. Re-add the project root if needed.");return}this.content.classList.add("content-wide");const n=f("div","page board-library"),s=fe(this.workspace,t,{currentLabel:"Boards"});n.appendChild(bt(s));const o=f("div","page-bar"),a=f("h1","");a.textContent="Boards";const r=f("button","btn primary");r.textContent="+ New board",r.addEventListener("click",()=>this.promptNewBoard(t)),o.append(a,r),n.appendChild(o);const c=f("p","page-lead");c.textContent="Each board is a git-tracked JSON file. Open one to edit it, then use “Copy embed” to drop a framed preview into any doc.",n.appendChild(c);const l=f("div","card-grid board-grid");n.appendChild(l),this.content.replaceChildren(n);try{const d=await J.list(t);for(const g of d){const y=document.createElement("a");y.className="card",y.href=Z(t,g.id);const b=f("div","card-title");b.textContent=g.title;const x=f("div","card-desc");x.textContent=`${g.elementCount} element${g.elementCount===1?"":"s"} · ${g.filePath}`,y.append(b,x),l.appendChild(y)}d.length===0&&l.appendChild(Ht("No boards yet — create one."));const h=await k.getTree(t),p=ge(t,h),m=ye(p,jt(t));m&&n.appendChild(m)}catch{l.appendChild(Ht("Could not load boards."))}this.contentScroll.scrollTop=e??0}async renderProjectBoardList(t,e){this.disposeContent(),this.content.classList.add("content-wide");const n=this.workspace.projects.find(d=>d.id===t);if(!n){this.renderMessage("Project unavailable","This project is no longer registered. Re-add it if needed.");return}const s=f("div","page boards-page"),o=[{label:"Home",href:"#/"},{label:n.label,href:Bt(t)},{label:"Boards"}];s.appendChild(bt(o));const a=f("div","page-bar"),r=f("h1","");r.textContent=`${n.label} boards`,a.appendChild(r),s.appendChild(a);const c=f("p","page-lead");c.textContent="Boards across every mounted docs root in this project. Each card shows the docs root and board file path.",s.appendChild(c);const l=f("div","card-grid board-grid");s.appendChild(l),this.content.replaceChildren(s);try{const d=await this.listProjectBoards(n);for(const h of d){const p=document.createElement("a");p.className="card",p.href=Z(h.root.id,h.board.id);const m=f("div","card-title");m.textContent=h.board.title;const g=f("div","card-desc");g.textContent=`${h.board.elementCount} element${h.board.elementCount===1?"":"s"} · ${h.root.displayPath} · ${h.board.filePath}`,p.append(m,g),l.appendChild(p)}d.length===0&&l.appendChild(Ht("No boards yet — create one."))}catch{l.appendChild(Ht("Could not load boards."))}this.contentScroll.scrollTop=e??0}async listProjectBoards(t){const e=[];for(const n of t.docsRoots){const s=await J.list(n.id);for(const o of s)e.push({root:n,board:o})}return e.sort((n,s)=>s.board.updatedAt!==n.board.updatedAt?s.board.updatedAt-n.board.updatedAt:n.root.displayPath!==s.root.displayPath?n.root.displayPath.localeCompare(s.root.displayPath):n.board.filePath.localeCompare(s.board.filePath)),e}promptNewBoard(t,e=""){const n=e?`doc:${t}:${e}`:t,s=this.sidebar.collapsibles.get(n),o=(s==null?void 0:s.body)??this.sidebar.element.querySelector(".nav-repo-body");o?this.sidebar.startInlineCreate(o,"board-name",async a=>{const r=St(a,"board");if(!r.ok){C(r.message);return}try{const c=await J.create(t,r.raw,ee(),e);await this.refreshContentIndex(),location.hash=Z(t,c)}catch{C("Could not create board")}}):_("New board",(a,r)=>{const c=document.createElement("input");c.className="board-title",c.placeholder="Board title",c.style.width="100%",c.style.marginBottom="12px";const l=f("div","modal-actions"),d=f("button","btn primary");d.textContent="Create";const h=async()=>{const p=St(c.value,"board");if(!p.ok){C(p.message),c.focus();return}try{const m=await J.create(t,p.raw,ee(),e);r(),await this.refreshContentIndex(),location.hash=Z(t,m)}catch{C("Could not create board file")}};d.addEventListener("click",()=>void h()),c.addEventListener("keydown",p=>{p.stopPropagation(),p.key==="Enter"&&h()}),l.appendChild(d),a.append(c,l),c.focus()})}promptNewDirectory(t,e){const n=e?`doc:${t}:${e}`:t,s=this.sidebar.collapsibles.get(n),o=(s==null?void 0:s.body)??this.sidebar.element.querySelector(".nav-repo-body");o?this.sidebar.startInlineCreate(o,"directory-name",async a=>{const r=St(a,"directory");if(!r.ok){C(r.message);return}const c=e?`${e}/${r.slug}`:r.slug;try{await k.createDirectory(t,c),await this.refreshContentIndex()}catch{C("Could not create directory")}}):_("New directory",(a,r)=>{const c=document.createElement("input");c.className="board-title",c.placeholder="Directory name",c.style.width="100%",c.style.marginBottom="12px";const l=f("div","modal-actions"),d=f("button","btn primary");d.textContent="Create";const h=async()=>{const p=St(c.value,"directory");if(!p.ok){C(p.message),c.focus();return}const m=e?`${e}/${p.slug}`:p.slug;try{await k.createDirectory(t,m),r(),await this.refreshContentIndex()}catch{C("Could not create directory")}};d.addEventListener("click",()=>void h()),c.addEventListener("keydown",p=>{p.stopPropagation(),p.key==="Enter"&&h()}),l.appendChild(d),a.append(c,l),c.focus()})}async ensureProjectDocsRoot(t){var n;const e=(n=this.workspace.projects.find(s=>s.id===t))==null?void 0:n.docsRoots[0];if(e)return e;try{const s=await k.ensureProjectDocsRoot(t);return await this.refreshContentIndex(),this.docsRootById(s.id)??s}catch{C("Could not create docs folder");return}}async createDocsFolder(t,e){try{await k.createDocsFolder(t,e),await this.refreshContentIndex(),C(`Created docs/ in ${e||"project root"}`)}catch{C("Could not create docs folder")}}async promptNewProjectDirectory(t){const e=await this.ensureProjectDocsRoot(t);e&&this.promptNewDirectory(e.id,"")}async promptNewProjectDoc(t){const e=await this.ensureProjectDocsRoot(t);e&&this.promptNewDoc(e.id,"")}async promptNewProjectBoard(t){const e=await this.ensureProjectDocsRoot(t);e&&this.promptNewBoard(e.id,"")}promptNewDoc(t,e){const n=e?`doc:${t}:${e}`:t,s=this.sidebar.collapsibles.get(n),o=(s==null?void 0:s.body)??this.sidebar.element.querySelector(".nav-repo-body");o?this.sidebar.startInlineCreate(o,"document-name.md",async a=>{const r=St(a.replace(/\.md$/i,""),"doc");if(!r.ok){C(r.message);return}const c=e?`${e}/${r.slug}.md`:`${r.slug}.md`;try{await k.writeFile(t,c,`# ${st(r.slug)}
|
|
50
|
-
`),await this.refreshContentIndex(),location.hash=O(t,c)}catch{C("Could not create doc")}}):_("New doc",(a,r)=>{const c=document.createElement("input");c.className="board-title",c.placeholder="Document name (e.g. overview)",c.style.width="100%",c.style.marginBottom="12px";const l=f("div","modal-actions"),d=f("button","btn primary");d.textContent="Create";const h=async()=>{const p=St(c.value.replace(/\.md$/i,""),"doc");if(!p.ok){C(p.message),c.focus();return}const m=e?`${e}/${p.slug}.md`:`${p.slug}.md`;try{await k.writeFile(t,m,`# ${st(p.slug)}
|
|
51
|
-
`),r(),await this.refreshContentIndex(),location.hash=O(t,m)}catch{C("Could not create doc file")}};d.addEventListener("click",()=>void h()),c.addEventListener("keydown",p=>{p.stopPropagation(),p.key==="Enter"&&h()}),l.appendChild(d),a.append(c,l),c.focus()})}promptNewCapability(t,e){_("New capability",(n,s)=>{const o=document.createElement("input");o.className="board-title",o.placeholder="Capability name (e.g. auth-flow)",o.style.width="100%",o.style.marginBottom="12px";const a=f("div","modal-actions"),r=f("button","btn primary");r.textContent="Create";const c=async()=>{const l=(o.value.trim()||"untitled").replace(/\.md$/,"").replace(/\s+/g,"-").toLowerCase(),d=e.replace(/^capabilities\/?/,""),h=d?`capabilities/${d}/${l}`:`capabilities/${l}`,p=`${h}/index.md`,m=`${h}/llms.txt`,g=st(l),y=`---
|
|
52
|
-
title: ${g}
|
|
53
|
-
summary:
|
|
54
|
-
tags:
|
|
55
|
-
kind:
|
|
56
|
-
---
|
|
57
|
-
|
|
58
|
-
# ${g}
|
|
59
|
-
|
|
60
|
-
## Purpose
|
|
61
|
-
|
|
62
|
-
## When to use
|
|
63
|
-
|
|
64
|
-
## Important files
|
|
65
|
-
|
|
66
|
-
## Outputs
|
|
67
|
-
`,b=`# ${g}
|
|
68
|
-
|
|
69
|
-
Read index.md first for the human overview of this capability branch.
|
|
70
|
-
|
|
71
|
-
## Purpose
|
|
72
|
-
|
|
73
|
-
Describe what this capability helps an LLM do.
|
|
74
|
-
|
|
75
|
-
## Entry points
|
|
76
|
-
|
|
77
|
-
- Human entry: index.md
|
|
78
|
-
- Machine entry: llms.txt
|
|
79
|
-
|
|
80
|
-
## Important files
|
|
81
|
-
|
|
82
|
-
List the files or subdirectories an LLM should inspect before acting.
|
|
83
|
-
|
|
84
|
-
## Working rules
|
|
85
|
-
|
|
86
|
-
Record the constraints, tools, scripts, or workflows that matter for this capability.
|
|
87
|
-
`;try{await k.createDirectory(t,h),await k.writeFile(t,p,y),await k.writeFile(t,m,b),s(),await this.refreshContentIndex(),location.hash=Ft(t,p)}catch{C("Could not create capability")}};r.addEventListener("click",()=>void c()),o.addEventListener("keydown",l=>{l.stopPropagation(),l.key==="Enter"&&c()}),a.appendChild(r),n.append(o,a),o.focus()})}promptRenameDoc(t,e){const n=e.includes("/")?e.slice(0,e.lastIndexOf("/")+1):"",s=e.replace(/\.md$/,"").split("/").pop()??"",o=`doc:${t}:${e}`,a=this.sidebar.links.get(o);a?this.sidebar.startInlineRename(a,s,async r=>{const c=r.replace(/\.md$/,"").replace(/\s+/g,"-").toLowerCase(),l=`${n}${c}.md`;if(l!==e)try{await k.renameFile(t,e,l),await this.refreshContentIndex(),location.hash=O(t,l)}catch{C("Could not rename doc")}}):_("Rename doc",(r,c)=>{const l=document.createElement("input");l.className="board-title",l.value=s,l.style.width="100%",l.style.marginBottom="12px",l.select();const d=f("div","modal-actions"),h=f("button","btn primary");h.textContent="Rename";const p=async()=>{const m=(l.value.trim()||s).replace(/\.md$/,"").replace(/\s+/g,"-").toLowerCase(),g=`${n}${m}.md`;if(g===e){c();return}try{await k.renameFile(t,e,g),c(),await this.refreshContentIndex(),location.hash=O(t,g)}catch{C("Could not rename doc")}};h.addEventListener("click",()=>void p()),l.addEventListener("keydown",m=>{m.stopPropagation(),m.key==="Enter"&&p()}),d.appendChild(h),r.append(l,d),l.focus()})}async moveDoc(t,e,n){const s=Xn(n,te(e));if(s!==e)try{const o=new Set(vt(await k.getTree(t)).map(c=>G(c.path))),a=await k.readFile(t,e),r=Un(a,e,s,o);await k.renameFile(t,e,s),r!==a&&await k.writeFile(t,s,r),await this.refreshContentIndex(),location.hash===O(t,e)&&(location.hash=O(t,s)),C(`Moved ${te(e)}`)}catch{C("Could not move doc")}}async moveBoard(t,e,n){try{const s=await J.move(t,e,n),o=await this.updateBoardEmbedReferences(t,e,s);await this.refreshContentIndex(),location.hash===Z(t,e)&&(location.hash=Z(t,s)),C(o>0?`Moved ${te(e)} and updated ${o} embed${o===1?"":"s"}`:`Moved ${te(e)}`)}catch(s){C(s instanceof Error?s.message:"Could not move board")}}async updateBoardEmbedReferences(t,e,n){if(e===n)return 0;const s=await k.getTree(t),o=bs(s);let a=0;for(const r of o){const c=await k.readFile(t,r.path),l=Sa(c,r.path,e,n);l!==c&&(await k.writeFile(t,r.path,l),a++)}return a}async moveDirectory(t,e,n){const s=te(e),o=Xn(n,s);if(o!==e)try{const a=await k.getTree(t),r=new Set(vt(a).map(p=>G(p.path))),c=ka(a,e),l=Ea(a,e),d=await Promise.all(l.map(async p=>[p.path,await k.readFile(t,p.path)]));await k.renameFile(t,e,o);let h=0;for(const[p,m]of d){const g=`${o}${p.slice(e.length)}`,y=Un(m,p,g,r);y!==m&&(await k.writeFile(t,g,y),h++)}for(const p of c){const m=`${o}${p.slice(e.length)}`;await this.rewriteMovedBoardDocumentId(t,p,m),h+=await this.updateBoardEmbedReferences(t,p,m)}await this.refreshContentIndex(),this.rewriteRouteAfterDirectoryMove(t,e,o),C(h>0?`Moved ${s} and updated ${h} embed${h===1?"":"s"}`:`Moved ${s}`)}catch{C("Could not move directory")}}promptRenameDirectory(t,e){const n=e.includes("/")?e.slice(0,e.lastIndexOf("/")+1):"",s=e.split("/").pop()??"";_("Rename directory",(o,a)=>{const r=document.createElement("input");r.className="board-title",r.value=s,r.style.width="100%",r.style.marginBottom="12px",r.select();const c=f("div","modal-actions"),l=f("button","btn primary");l.textContent="Rename";const d=async()=>{const h=(r.value.trim()||s).replace(/\s+/g,"-").toLowerCase(),p=`${n}${h}`;if(p===e){a();return}try{await k.renameFile(t,e,p),a(),await this.refreshContentIndex(),C(`Renamed to ${h}`)}catch{C("Could not rename directory")}};l.addEventListener("click",()=>void d()),r.addEventListener("keydown",h=>{h.stopPropagation(),h.key==="Enter"&&d()}),c.appendChild(l),o.append(r,c),r.focus()})}async promptDeleteDirectory(t,e){const n=e.split("/").pop()??e;if(confirm(`Delete "${n}" and all its contents? This cannot be undone.`))try{await k.deleteFile(t,e),await this.refreshContentIndex(),C(`Deleted ${n}`)}catch{C("Could not delete directory")}}async rewriteMovedBoardDocumentId(t,e,n){const s=await k.getBoard(t,n);s&&s.id!==n&&await k.saveBoard(t,n,{...s,id:n,updatedAt:Date.now()})}rewriteRouteAfterDirectoryMove(t,e,n){const s=encodeURIComponent(t),o=`#/root/${s}/docs/`,a=`#/root/${s}/boards/`,r=location.hash;if(r.startsWith(o)){const c=decodeURIComponent(r.slice(o.length));(c===e||c.startsWith(`${e}/`))&&(location.hash=O(t,`${n}${c.slice(e.length)}`))}else if(r.startsWith(a)){const c=decodeURIComponent(r.slice(a.length));(c===e||c.startsWith(`${e}/`))&&(location.hash=Z(t,`${n}${c.slice(e.length)}`))}}async promptDeleteDoc(t,e){const n=e.split("/").pop()??e;_("Delete doc",(s,o)=>{const a=f("p","modal-copy");a.textContent=`Delete "${n}"? This cannot be undone.`;const r=f("div","modal-actions"),c=f("button","btn danger");c.textContent="Delete",c.addEventListener("click",async()=>{try{await k.deleteFile(t,e),o(),await this.refreshContentIndex(),location.hash="#/"}catch{C("Could not delete doc")}});const l=f("button","btn");l.textContent="Cancel",l.addEventListener("click",o),r.append(c,l),s.append(a,r)})}async promptRenameBoard(t,e){const n=await J.load(t,e);if(!n){C("Board not found");return}_("Rename board",(s,o)=>{const a=document.createElement("input");a.className="board-title",a.value=n.title,a.style.width="100%",a.style.marginBottom="12px",a.select();const r=f("div","modal-actions"),c=f("button","btn primary");c.textContent="Rename";const l=async()=>{const d=St(a.value,"board");if(!d.ok){C(d.message),a.focus();return}try{const h=await J.rename(t,e,d.raw),p=await this.updateBoardEmbedReferences(t,e,h);o(),await this.refreshContentIndex(),location.hash===Z(t,e)&&(location.hash=Z(t,h)),p>0&&C(`Renamed board and updated ${p} embed${p===1?"":"s"}`)}catch(h){C(h instanceof Error?h.message:"Could not rename board")}};c.addEventListener("click",()=>void l()),a.addEventListener("keydown",d=>{d.stopPropagation(),d.key==="Enter"&&l()}),r.appendChild(c),s.append(a,r),a.focus()})}promptDeleteBoard(t,e){_("Delete board",(n,s)=>{const o=f("p","modal-copy");o.textContent=`Delete "${e}.board.json"? This cannot be undone.`;const a=f("div","modal-actions"),r=f("button","btn danger");r.textContent="Delete",r.addEventListener("click",async()=>{try{await J.delete(t,e),s(),await this.refreshContentIndex(),location.hash=jt(t)}catch{C("Could not delete board")}});const c=f("button","btn");c.textContent="Cancel",c.addEventListener("click",s),a.append(r,c),n.append(o,a)})}async openEditor(t,e,n){if(!this.docsRootById(t)){this.renderMessage("Docs root unavailable","This docs root is no longer registered. Re-add the project root if needed.");return}this.editor&&this.editingId===`${t}:${e}`||(this.exitEditor(),this.disposeContent(),this.layout.style.display="none",this.editorHost=f("div","board-editor-host"),this.root.appendChild(this.editorHost),this.editingId=`${t}:${e}`,this.editor=new jo(this.editorHost,{boardId:e,loadBoard:async s=>{const o=await k.getBoardWithRevision(t,s);return o||void 0},saveBoard:(s,o)=>k.saveBoard(t,s.id,s,o),deleteBoard:s=>J.delete(t,s),uploadImage:(s,o)=>J.uploadImage(t,s,o),cleanupUnusedAssets:s=>J.cleanupUnusedAssets(t,s),resolveAssetUrl:s=>k.assetUrl(t,s),initialRegion:n,onExit:()=>{this.pendingScrollRestore=this.lastNonBoardScrollTop,location.hash=this.lastNonBoardHash},onDeleted:()=>{location.hash=jt(t)}}))}exitEditor(){this.editor&&(this.editor.destroy(),this.editor=void 0),this.editorHost&&(this.editorHost.remove(),this.editorHost=void 0),this.editingId=void 0,this.applyInitialTheme()}applyInitialTheme(){const t=localStorage.getItem(Dn),e=t==="dark"||t==="light"?t:window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light";document.documentElement.setAttribute("data-theme",e)}toggleTheme(){var e;const t=ee()==="dark"?"light":"dark";document.documentElement.setAttribute("data-theme",t),localStorage.setItem(Dn,t),this.updateThemeButton(),(e=this.updateRenderedTheme)==null||e.call(this,t)}createTopbar(){const t=f("div","shell-topbar"),e=f("button","topbar-icon-btn");return e.innerHTML=V.panelLeft,e.title="Toggle navigation",e.setAttribute("aria-label","Toggle navigation"),e.addEventListener("click",()=>this.setSidebarCollapsed(!this.layout.classList.contains("sidebar-collapsed"))),this.searchBtn=f("button","topbar-search"),this.searchBtn.type="button",this.searchBtn.innerHTML=`${V.search}<span>Search docs</span><kbd>${this.shortcutLabel()}</kbd>`,this.searchBtn.addEventListener("click",()=>this.openCommandPalette()),this.cartBtn=f("button","topbar-cart"),this.cartBtn.type="button",this.cartBtn.hidden=!0,this.editBtn=f("button","btn topbar-edit"),this.editBtn.type="button",this.editBtn.innerHTML=`${V.edit}<span>Edit</span>`,this.editBtn.title="Edit this document",this.editBtn.addEventListener("click",()=>{this.currentEditableFile&&this.openDocEditor(this.currentEditableFile)}),this.themeBtn=f("button","topbar-icon-btn topbar-theme"),this.themeBtn.type="button",this.themeBtn.title="Toggle theme",this.themeBtn.setAttribute("aria-label","Toggle theme"),this.themeBtn.addEventListener("click",()=>this.toggleTheme()),this.updateThemeButton(),t.append(e,this.searchBtn,this.editBtn,this.themeBtn),t}setSidebarCollapsed(t){this.layout.classList.toggle("sidebar-collapsed",t),localStorage.setItem(An,String(t))}setupSidebarResize(){this.sidebarResizeHandle&&this.sidebarResizeHandle.addEventListener("pointerdown",t=>{var r;if(this.layout.classList.contains("sidebar-collapsed"))return;t.preventDefault();const e=t.clientX,n=this.readSidebarWidth(),s=t.pointerId;(r=this.sidebarResizeHandle)==null||r.setPointerCapture(s),document.body.classList.add("sidebar-resizing");const o=c=>{const l=Oe(n+c.clientX-e);this.applySidebarWidth(l)},a=()=>{var c;(c=this.sidebarResizeHandle)==null||c.releasePointerCapture(s),document.body.classList.remove("sidebar-resizing"),window.removeEventListener("pointermove",o),window.removeEventListener("pointerup",a)};window.addEventListener("pointermove",o),window.addEventListener("pointerup",a)})}readSidebarWidth(){const t=Number(localStorage.getItem(Nn)??270);return Oe(t)}applySidebarWidth(t){const e=Oe(t);this.layout.style.setProperty("--sidebar-width",`${e}px`),localStorage.setItem(Nn,String(e))}setEditableFile(t){this.currentEditableFile=t,this.editBtn&&(this.editBtn.hidden=!t,this.editBtn.disabled=!t)}promptRemoveProject(t,e){_("Remove from workspace",(n,s)=>{const o=f("p","modal-copy");o.textContent=`Remove "${e}" from the workspace? The files on disk are not deleted.`;const a=f("div","modal-actions"),r=f("button","btn danger");r.textContent="Remove",r.addEventListener("click",async()=>{try{await k.removeProject(t),s(),await this.refreshContentIndex(),location.hash="#/"}catch{C("Could not remove project")}});const c=f("button","btn");c.textContent="Cancel",c.addEventListener("click",s),a.append(r,c),n.append(o,a)})}async addRepoDocs(){var t;try{const e=new Set(this.allDocsRoots().map(h=>h.id)),n=new Set(this.workspace.projects.map(h=>h.id));let s={cancelled:!1};try{s=await k.pickAndAddProject()}catch{const p=window.prompt("Project root path","");if(!p||!p.trim())return;const m=await k.addProject(p.trim());this.workspace=m,s={cancelled:!1,addedProjectId:(t=m.projects.find(g=>!n.has(g.id)))==null?void 0:t.id}}if(s.cancelled)return;await this.refreshContentIndex();let o=s.addedProjectId?this.workspace.projects.find(h=>h.id===s.addedProjectId):void 0,a=o==null?void 0:o.docsRoots[0];if(o&&o.docsRoots.length===0){const h=await this.promptCreateProjectDocsRoot(o.id,o.label);if(!h){location.hash=Bt(o.id),C(`Added ${o.label}. Create docs/ later or switch to Raw view from the project menu.`);return}o=this.workspace.projects.find(p=>p.id===(o==null?void 0:o.id)),a=h}const r=this.allDocsRoots(),c=a??r.find(h=>!e.has(h.id));if(!c){C("No docs folders found under that project root.");return}const l=this.treesByRoot.get(c.id)??[],d=Ue(Xt(l));d?location.hash=O(c.id,d):location.hash=jt(c.id),C(`Added ${c.projectLabel}.`)}catch(e){C(e instanceof Error?e.message:"Could not add project root.")}}promptCreateProjectDocsRoot(t,e){return new Promise(n=>{_("Create docs folder",(s,o)=>{let a=!1;const r=m=>{a||(a=!0,o(),n(m))},c=document.createElement("p");c.textContent=`${e} does not have a docs/ folder yet. Create one at the project root now?`;const l=document.createElement("p");l.textContent="You can keep the repo in the workspace and switch to Raw view later if you prefer.";const d=f("div","modal-actions"),h=f("button","btn primary");h.textContent="Create docs/",h.addEventListener("click",()=>{h.disabled=!0,p.disabled=!0,this.ensureProjectDocsRoot(t).then(m=>{if(!m){h.disabled=!1,p.disabled=!1;return}r(m)})});const p=f("button","btn");p.textContent="Maybe later",p.addEventListener("click",()=>r(void 0)),d.append(h,p),s.append(c,l,d)})})}updateTopbarCart(t=!1){this.cartBtn&&(this.cartBtn.hidden=!0)}toggleCartPopover(){this.closeCartPopover()}closeCartPopover(){var t;this.content.classList.remove("cart-popover-open"),(t=this.cartPopover)==null||t.classList.remove("visible")}updateThemeButton(){if(!this.themeBtn)return;const t=ee()==="dark";this.themeBtn.innerHTML=t?V.sun:V.moon,this.themeBtn.title=t?"Switch to light mode":"Switch to dark mode",this.themeBtn.setAttribute("aria-label",this.themeBtn.title)}handleGlobalKeydown(t){if((t.metaKey||t.ctrlKey)&&t.key.toLowerCase()==="k"){const e=t.target;if(e!=null&&e.closest(".doc-editor"))return;t.preventDefault(),this.openCommandPalette();return}if(t.key==="F2"){const e=location.hash,n=t.target;if(n!=null&&n.closest(".doc-editor, input, textarea, [contenteditable]"))return;t.preventDefault();const s=e.match(/^#\/root\/([^/]+)\/docs\/(.+)$/);if(s){const r=decodeURIComponent(s[1]),c=decodeURIComponent(s[2]);this.promptRenameDoc(r,c);return}const o=e.match(/^#\/root\/([^/]+)\/boards\/(.+)$/);if(o){const r=decodeURIComponent(o[1]),c=decodeURIComponent(o[2]);this.promptRenameBoard(r,c);return}const a=e.match(/^#\/root\/([^/]+)\/directory\/(.+)$/);if(a){const r=decodeURIComponent(a[1]),c=decodeURIComponent(a[2]);this.promptRenameDirectory(r,c);return}}}allDocsRoots(){return this.workspace.projects.flatMap(t=>t.docsRoots)}docsRootById(t){return this.allDocsRoots().find(e=>e.id===t)}async refreshContentIndex(){try{this.workspace=await k.getWorkspace()}catch{this.workspace={projects:[]},this.treesByRoot=new Map,this.treesByProject=new Map,this.sidebar.refresh(this.workspace,this.treesByRoot),this.searchIndex=[],this.searchIndexing=!1;return}const t=this.allDocsRoots(),e=await Promise.all(t.map(async s=>{try{return[s.id,await k.getTree(s.id)]}catch{return[s.id,[]]}}));this.treesByRoot=new Map(e);const n=await Promise.all(this.workspace.projects.map(async s=>{try{return[s.id,await k.getProjectTree(s.id)]}catch{return[s.id,[]]}}));this.treesByProject=new Map(n),this.sidebar.refresh(this.workspace,this.treesByRoot,this.treesByProject),this.refreshSearchIndexInBackground(t,this.treesByRoot)}async refreshSearchIndexInBackground(t,e){var o,a;const n=++this.searchIndexRun;this.searchIndexing=!0;const s=[];for(const r of t){const c=e.get(r.id)??[],l=[...Jt(c),...Es(c,"capabilities")].filter(d=>d.type==="file"&&d.name.endsWith(".md"));for(const d of l){const h="path"in d?d.path:"";let p="";try{p=await k.readFile(r.id,h)}catch{}const{frontmatter:m}=ne(p),g=ys(p),y=m.title??fs(p)??st(d.name),b=m.summary??gs(g),x=h.startsWith("capabilities/")?"Capability":"Doc",v=h.startsWith("capabilities/")?Ft(r.id,h):O(r.id,h);s.push({title:y,path:`${r.projectLabel}/${r.displayPath}/${h}`,href:v,section:x,summary:b,body:g.map(S=>S.text).join(" "),chunks:g,rootId:r.id})}}n===this.searchIndexRun&&(this.searchIndex=s.filter(r=>r.body||r.title),this.searchIndexing=!1,this.paletteResults&&((o=this.palette)!=null&&o.classList.contains("visible"))&&this.renderSearchResults(((a=this.paletteInput)==null?void 0:a.value)??""))}openCommandPalette(){var t;this.palette||this.createCommandPalette(),(t=this.palette)==null||t.classList.add("visible"),document.body.style.overflow="hidden",this.paletteInput.value="",this.renderSearchResults(""),requestAnimationFrame(()=>{var e;return(e=this.paletteInput)==null?void 0:e.focus()})}closeCommandPalette(){var t;(t=this.palette)==null||t.classList.remove("visible"),document.body.style.overflow=""}createCommandPalette(){this.palette=f("div","command-palette");const t=f("div","command-panel"),e=f("div","command-search");e.innerHTML=V.search,this.paletteInput=document.createElement("input"),this.paletteInput.type="search",this.paletteInput.placeholder="Search docs",this.paletteInput.setAttribute("aria-label","Search docs"),e.appendChild(this.paletteInput),this.paletteResults=f("div","command-results"),t.append(e,this.paletteResults),this.palette.appendChild(t),document.body.appendChild(this.palette),this.palette.addEventListener("mousedown",n=>{n.target===this.palette&&this.closeCommandPalette()}),this.paletteInput.addEventListener("input",()=>this.renderSearchResults(this.paletteInput.value)),this.paletteInput.addEventListener("keydown",n=>this.handlePaletteKeydown(n))}handlePaletteKeydown(t){var o,a,r,c,l;if(t.key==="Escape"){t.preventDefault(),this.closeCommandPalette();return}const e=[...((o=this.paletteResults)==null?void 0:o.querySelectorAll(".command-result"))??[]],n=(a=this.paletteResults)==null?void 0:a.querySelector(".command-result.active"),s=n?e.indexOf(n):-1;if(t.key==="ArrowDown"||t.key==="ArrowUp"){t.preventDefault();const d=t.key==="ArrowDown"?Math.min(s+1,e.length-1):Math.max(s-1,0);e.forEach(h=>h.classList.remove("active")),(r=e[d])==null||r.classList.add("active"),(c=e[d])==null||c.scrollIntoView({block:"nearest"})}t.key==="Enter"&&(t.preventDefault(),(l=n??e[0])==null||l.click())}renderSearchResults(t){if(!this.paletteResults)return;const e=wa(t),n=this.rankSearch(e).slice(0,8);if(this.paletteResults.replaceChildren(),n.length===0){const s=f("div","command-empty");s.textContent=this.searchIndex.length?"No matching docs.":this.searchIndexing?"Indexing docs...":"No docs indexed yet.",this.paletteResults.appendChild(s);return}for(const[s,o]of n.entries()){const{entry:a}=o,r=document.createElement("a");r.className="command-result",s===0&&r.classList.add("active"),r.href=a.href,r.addEventListener("click",()=>this.closeCommandPalette());const c=f("div","command-result-title");On(c,a.title,e);const l=f("div","command-result-meta");l.textContent=[a.section,o.heading,a.path].filter(Boolean).join(" · ");const d=f("div","command-result-summary");On(d,o.preview,e),r.append(c,l),d.textContent&&r.appendChild(d),this.paletteResults.appendChild(r)}}rankSearch(t){if(t.length===0)return this.searchIndex.map(s=>({entry:s,score:0,heading:"",preview:s.summary||s.body.slice(0,220)}));const e=t.map(mt).filter(Boolean),n=mt(t.join(" "));return this.searchIndex.map(s=>{const o=mt(s.title),a=mt(s.path),r=mt(s.summary);let c=Qt(o,e,n,10);c+=Qt(a,e,n,4),c+=Qt(r,e,n,3);let l,d=0;for(const p of s.chunks){const m=Qt(mt(p.heading),e,n,5),g=Qt(p.normalized,e,n,p.kind==="heading"?8:2),y=m+g;y>d&&(d=y,l=p)}const h=c+d;return{entry:s,score:h,heading:l!=null&&l.heading&&l.heading!==s.title?l.heading:"",preview:l?Ma(l.text,e):s.summary}}).filter(s=>s.score>0).sort((s,o)=>o.score-s.score||s.entry.title.localeCompare(o.entry.title))}shortcutLabel(){return/Mac|iPhone|iPad/.test(navigator.platform)?"⌘K":"Ctrl K"}disposeContent(){this.cleanup&&(this.cleanup(),this.cleanup=void 0),this.updateRenderedTheme=void 0,this.content.classList.remove("content-wide"),this.content.replaceChildren()}renderMessage(t,e){this.disposeContent();const n=f("div","page"),s=f("h1","");s.textContent=t;const o=f("p","page-lead");o.textContent=e,n.append(s,o),this.content.replaceChildren(n)}}function fe(i,t,e={}){const n=[{label:"Home",href:"#/"}];let s;for(const c of i.projects){const l=c.docsRoots.find(d=>d.id===t);if(l){n.push({label:c.label,href:Bt(c.id)}),s=l;break}}if(s){const c=s.displayPath.split("/").filter(Boolean);for(let l=0;l<c.length;l++){const d=c.slice(0,l+1).join("/");n.push({label:c[l],href:Se(t,d)})}}const a=(e.filePath??e.dirPath??"").split("/").filter(Boolean),r=e.filePath?a.slice(0,-1):a;for(let c=0;c<r.length;c++){const l=r.slice(0,c+1).join("/");n.push({label:st(r[c]),href:Se(t,l)})}if(e.currentLabel)n.push({label:e.currentLabel});else if(e.filePath){const c=a[a.length-1];n.push({label:c==="index.md"?"Overview":st(c)})}return n}function bt(i){const t=f("nav","breadcrumb");t.setAttribute("aria-label","Breadcrumb");for(let e=0;e<i.length;e++){const n=i[e],s=e===i.length-1;if(!s&&n.href){const o=document.createElement("a");o.className="breadcrumb-link",o.href=n.href,o.textContent=n.label,t.appendChild(o)}else{const o=f("span","breadcrumb-current");o.textContent=n.label,t.appendChild(o)}if(!s){const o=f("span","breadcrumb-sep");o.textContent="/",o.setAttribute("aria-hidden","true"),t.appendChild(o)}}return t}function f(i,t){const e=document.createElement(i);return t&&(e.className=t),e}function Ht(i){const t=f("div","empty-note");return t.textContent=i,t}function zn(i,t,e,n){var s;return{path:`${i}:${t.branchPath}`,rootId:i,branchPath:t.branchPath,entryPath:t.entryPath,displayPath:t.routePath,title:n.title??fs(e)??Ee(t.entryPath),summary:n.summary??gs(ys(e)),kind:((s=n.kind)==null?void 0:s.trim())||void 0,tags:n.tags?n.tags.split(",").map(o=>o.trim()).filter(Boolean):[],md:e}}function ge(i,t){const e=[];function n(a){const c=[...Xt(a)].sort((l,d)=>{const h=l.type==="file"&&l.name==="index.md"?-1:0,p=d.type==="file"&&d.name==="index.md"?-1:0;return h!==p?h-p:l.type!==d.type?l.type==="dir"?-1:1:l.name.localeCompare(d.name)});for(const l of c)if(l.type==="file"){const d=l.name==="index.md"?"Overview":st(l.name);e.push({label:d,href:O(i,l.path)})}else n(l.children)}n(t);const s=vt(t).sort((a,r)=>a.path.localeCompare(r.path));if(s.length>0){e.push({label:"All Boards",href:jt(i)});for(const a of s){const r=G(a.path);e.push({label:st(r.split("/").pop()??r),href:Z(i,r)})}}const o=$t(t);if(o.length>0){e.push({label:"All Capabilities",href:ps(i)});for(const a of o)e.push({label:Ee(a.entryPath),href:Ft(i,a.entryPath)})}return e}function ye(i,t){const e=i.findIndex(a=>a.href===t);if(e===-1)return null;const n=e>0?i[e-1]:null,s=e<i.length-1?i[e+1]:null;if(!n&&!s)return null;const o=f("nav","page-nav");if(n){const a=f("a","page-nav-prev");a.href=n.href,a.innerHTML=`<span class="page-nav-arrow">←</span><span class="page-nav-label">${n.label}</span>`,o.appendChild(a)}else o.appendChild(f("span","page-nav-spacer"));if(s){const a=f("a","page-nav-next");a.href=s.href,a.innerHTML=`<span class="page-nav-label">${s.label}</span><span class="page-nav-arrow">→</span>`,o.appendChild(a)}return o}function ps(i){return`#/root/${encodeURIComponent(i)}/capabilities`}function Xt(i){return i.filter(t=>t.type==="file"?t.name.endsWith(".md"):t.name==="capabilities"||t.name==="assets"?!1:Xt(t.children).length>0).map(t=>t.type==="file"?t:{...t,children:Xt(t.children)})}function Jt(i){const t=[],e=n=>{for(const s of Xt(n))s.type==="file"?t.push(s):e(s.children)};return e(i),t}function O(i,t){return`#/root/${encodeURIComponent(i)}/docs/${encodeURIComponent(t)}`}function Ft(i,t){return`#/root/${encodeURIComponent(i)}/capabilities/${encodeURIComponent(qt(t))}`}function $t(i){const t=i.find(s=>s.type==="dir"&&s.name==="capabilities");if(!t||t.type!=="dir")return[];const e=[],n=s=>{for(const o of s){if(o.type==="file"){o.path.startsWith("capabilities/")&&o.name.endsWith(".md")&&!o.path.slice(13).includes("/")&&e.push({branchPath:o.path,entryPath:o.path,routePath:qt(o.path)});continue}const a=ba(o);a&&e.push({branchPath:o.path,entryPath:a.path,routePath:qt(o.path)}),n(o.children)}};return n(t.children),e.sort((s,o)=>s.branchPath.localeCompare(o.branchPath))}function pa(i,t){const e=t.replace(/^\/+|\/+$/g,""),n=e.startsWith("capabilities/")?e:`capabilities/${e}`;if(n.endsWith(".md"))return n;const s=$t(i).find(o=>o.branchPath===n||o.routePath===e);return(s==null?void 0:s.entryPath)??null}function Hn(i,t){const e=t.replace(/^\/+|\/+$/g,""),n=e.startsWith("capabilities/")?e:`capabilities/${e}`,s=$t(i).find(o=>o.branchPath===n||o.entryPath===n||o.routePath===e);return s||($t(i).filter(o=>n.startsWith(`${o.branchPath}/`)).sort((o,a)=>a.branchPath.length-o.branchPath.length)[0]??null)}function Ee(i){if(i.endsWith("/index.md")){const t=i.slice(0,-9);return st(t.split("/").pop()??t)}return st(i.split("/").pop()??i)}function qt(i){const t=i.replace(/^capabilities\//,"");return t.endsWith("/index.md")?t.slice(0,-9):t}function ua(i){return{branchPath:ma(i),entryPath:i,routePath:qt(i)}}function ma(i){return i.endsWith("/index.md")?i.slice(0,-9):i}function Fn(i,t){const e=us(i,t);return e?e.type==="file"?[e]:ms(e.children).sort(ya):[]}function be(i){return qt(i)||"index.md"}function fa(i){return i.endsWith(".md")?null:`${i}/llms.txt`}function ga(i){return/\.(md|txt|json|ya?ml|js|ts|mjs|cjs|sh|css|html)$/i.test(i)}function us(i,t){for(const e of i){if(e.path===t)return e;if(e.type==="dir"){const n=us(e.children,t);if(n)return n}}return null}function ms(i){const t=[];for(const e of i)e.type==="file"?t.push(e):t.push(...ms(e.children));return t}function ya(i,t){const e=jn(i.path),n=jn(t.path);if(e!==n)return e.localeCompare(n);const s=Wn(i.name),o=Wn(t.name);return s!==o?s?-1:1:i.path.localeCompare(t.path)}function jn(i){const t=i.lastIndexOf("/");return t===-1?"":i.slice(0,t)}function Wn(i){return/^readme\.(md|txt)$/i.test(i)}function ba(i){const t=i.children.find(e=>e.type==="file"&&e.name==="index.md");return t||(i.children.find(e=>e.type==="file"&&e.name.endsWith(".md"))??null)}function jt(i){return`#/root/${encodeURIComponent(i)}/boards`}function Z(i,t){return`#/root/${encodeURIComponent(i)}/boards/${encodeURIComponent(t)}`}function Bt(i){return`#/project/${encodeURIComponent(i)}`}function Se(i,t){return`#/root/${encodeURIComponent(i)}/directory/${encodeURIComponent(t)}`}function va(i){const t=i.match(/^#\/root\/([^/]+)/);return t?decodeURIComponent(t[1]):null}function fs(i){var t,e;return((e=(t=i.match(/^#\s+(.+)$/m))==null?void 0:t[1])==null?void 0:e.trim())??null}function wa(i){return[...new Set(i.trim().split(/\s+/).map(mt).filter(Boolean))]}function On(i,t,e){const n=e.filter(Boolean).sort((a,r)=>r.length-a.length);if(n.length===0){i.textContent=t;return}const s=new RegExp(`(${n.map(xa).join("|")})`,"ig");let o=0;for(const a of t.matchAll(s)){const r=a.index??0;r>o&&i.appendChild(document.createTextNode(t.slice(o,r)));const c=document.createElement("mark");c.textContent=a[0],i.appendChild(c),o=r+a[0].length}o<t.length&&i.appendChild(document.createTextNode(t.slice(o)))}function xa(i){return i.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function gs(i){var t;return((t=i.find(e=>e.kind==="text"&&e.text.length>0))==null?void 0:t.text.slice(0,180))??""}function ys(i){const e=i.replace(/^---[\s\S]*?---\s*/,"").split(`
|
|
88
|
-
`),n=[];let s="",o=[];const a=(c,l="text",d=s)=>{const h=_n(c);h&&n.push({heading:d,text:h,normalized:mt(h),kind:l})},r=()=>{o.length!==0&&(a(o.join(" "),"text"),o=[])};for(let c=0;c<e.length;c++){const l=e[c],d=l.match(/^(`{3,})\s*([\w-]+)?\s*$/);if(d){r();const m=d[1].length,g=(d[2]??"").toLowerCase(),y=new RegExp("^`{"+m+",}\\s*$"),b=[];for(c++;c<e.length&&!y.test(e[c]);)b.push(e[c]),c++;a(g==="board"?Ca(b.join(`
|
|
89
|
-
`)):b.join(`
|
|
90
|
-
`),g==="board"?"board":"code");continue}const h=l.match(/^(#{1,6})\s+(.+)$/);if(h){r(),s=_n(h[2]),a(s,"heading",s);continue}if(l.trim()===""||/^(-{3,}|\*{3,}|_{3,})\s*$/.test(l)){r();continue}if(Ia(l))continue;const p=l.replace(/^>\s?/,"").replace(/^\s*([-*+]|\d+\.)\s+/,"").replace(/^\[([ xX])\]\s+/,"").replace(/\|/g," ");o.push(p)}return r(),n}function Ca(i){return i.split(`
|
|
91
|
-
`).map(t=>t.replace(/#.*$/,"").trim()).filter(Boolean).map(t=>t.replace(/^(\w+)\s*:\s*/,"$1 ")).join(" ")}function bs(i){const t=[],e=n=>{for(const s of n)s.type==="file"?s.name.endsWith(".md")&&t.push(s):e(s.children)};return e(i),t}function vt(i){const t=[],e=n=>{for(const s of n)s.type==="file"?s.name.endsWith(".board.json")&&t.push(s):e(s.children)};return e(i),t}function ka(i,t){const e=t.replace(/^\/+|\/+$/g,"");return vt(i).map(n=>G(n.path)).filter(n=>n===e||n.startsWith(`${e}/`))}function Ea(i,t){const e=t.replace(/^\/+|\/+$/g,"");return bs(i).filter(n=>n.path.startsWith(`${e}/`))}function Un(i,t,e,n){if(t===e)return i;const s=i.split(`
|
|
92
|
-
`);let o=!1,a,r=!1;const c=s.map(l=>{if(!o){const m=l.match(/^(`{3,})\s*board\s*$/i);return m&&(o=!0,a=new RegExp("^`{"+m[1].length+",}\\s*$")),l}if(a!=null&&a.test(l))return o=!1,a=void 0,l;const d=l.match(/^(\s*src\s*:\s*)(.*)$/i);if(!d)return l;const h=vs(d[2]);if(!h.value)return l;const p=Ba(h.value,t,e,n);return p===h.value?l:(r=!0,`${d[1]}${p}${h.suffix}`)});return r?c.join(`
|
|
93
|
-
`):i}function Sa(i,t,e,n){const s=i.split(`
|
|
94
|
-
`);let o=!1,a,r=!1;const c=s.map(l=>{if(!o){const m=l.match(/^(`{3,})\s*board\s*$/i);return m&&(o=!0,a=new RegExp("^`{"+m[1].length+",}\\s*$")),l}if(a!=null&&a.test(l))return o=!1,a=void 0,l;const d=l.match(/^(\s*src\s*:\s*)(.*)$/i);if(!d)return l;const h=vs(d[2]);return!h.value||!re(h.value,t).includes(e)?l:(r=!0,`${d[1]}${Ta(h.value,t,e,n)}${h.suffix}`)});return r?c.join(`
|
|
95
|
-
`):i}function vs(i){var o;const t=i.indexOf("#"),e=t===-1?i:i.slice(0,t),n=t===-1?"":i.slice(t),s=((o=e.match(/\s*$/))==null?void 0:o[0])??"";return{value:e.trim(),suffix:`${s}${n}`}}function Ta(i,t,e,n){const s=i.trim(),o=s.startsWith("/"),a=s.startsWith("./")||s.startsWith("../"),r=s.endsWith(".board.json"),c=re(s,t),l=!a&&c[0]!==e&&c.includes(e);if(o){const h=`/${n}`;return r&&!h.endsWith(".board.json")?`${h}.board.json`:h}let d=a||l?ws(Ke(t,n)):n;return r&&!d.endsWith(".board.json")&&(d=`${d}.board.json`),d}function Ba(i,t,e,n){const s=i.trim(),o=s.startsWith("./")||s.startsWith("../"),a=re(s,t),r=a[0]??"",c=a.find(p=>p!==r&&n.has(p));if(!o&&(!c||n.has(r)))return i;const l=s.endsWith(".board.json"),d=o?Rs(s,t):c;if(!d)return i;let h=ws(Ke(e,d));return l&&!h.endsWith(".board.json")&&(h=`${h}.board.json`),h}function ws(i){return!i||i.startsWith("./")||i.startsWith("../")?i:`./${i}`}function _n(i){return i.replace(/!\[([^\]]*)\]\(([^)]+)\)/g,"$1 $2").replace(/\[([^\]]+)\]\(([^)]+)\)/g,"$1 $2").replace(/`([^`]+)`/g,"$1").replace(/~~([\s\S]*?)~~/g,"$1").replace(/\*\*\*([\s\S]*?)\*\*\*/g,"$1").replace(/___([\s\S]*?)___/g,"$1").replace(/\*\*([\s\S]*?)\*\*/g,"$1").replace(/__([\s\S]*?)__/g,"$1").replace(/(^|[\s([{])\*([^*\s][\s\S]*?[^*\s])\*(?=$|[\s)\]},.!?:;])/g,"$1$2").replace(/(^|[\s([{])_([^_\s][\s\S]*?[^_\s])_(?=$|[\s)\]},.!?:;])/g,"$1$2").replace(/\\([\\`*_[\]{}()#+\-.!|>])/g,"$1").replace(/<[^>]+>/g," ").replace(/ /g," ").replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/(^|[\s([{])[*_]{1,3}(?=\S)/g,"$1").replace(new RegExp("(?<=\\S)[*_]{1,3}(?=$|[\\s)\\]},.!?:;])","g"),"").replace(/\s+/g," ").trim()}function mt(i){return i.normalize("NFKD").replace(/[\u0300-\u036f]/g,"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim()}function Qt(i,t,e,n){if(!i)return 0;let s=e&&i.includes(e)?n*4:0;for(const o of t)i===o&&(s+=n*5),i.startsWith(o)&&(s+=n*3),i.includes(o)&&(s+=n);return s}function Ma(i,t){if(t.length===0)return i.slice(0,220);const e=mt(i),n=t.map(l=>e.indexOf(l)).filter(l=>l>=0).sort((l,d)=>l-d)[0];if(n===void 0)return i.slice(0,220);const s=e.length===0?0:n/e.length,o=Math.round(i.length*s),a=Math.max(0,o-90),r=Math.min(i.length,o+170),c=i.slice(a,r).trim();return`${a>0?"... ":""}${c}${r<i.length?" ...":""}`}function Ia(i){return/^\s*\|?\s*:?-{1,}:?\s*(\|\s*:?-{1,}:?\s*)*\|?\s*$/.test(i)}function st(i){const t=i.replace(/\.md$/,"").replace(/[-_]/g," ");return t.charAt(0).toUpperCase()+t.slice(1)}function Ra(i){return i.trim().replace(/[\\/]+/g,"-").replace(/\s+/g,"-").replace(/[^a-z0-9-_]/gi,"-").replace(/-+/g,"-").replace(/^-|-$/g,"").toLowerCase()}const La=new Set([".","..","assets","boards","capabilities","docs"]);function St(i,t){const e=t==="doc"?"doc":t,n=i.trim();if(!n)return{ok:!1,message:`Enter a ${e} name.`};if(n.length>80)return{ok:!1,message:"Name must be 80 characters or fewer."};if(/[\\/]/.test(n))return{ok:!1,message:"Name cannot contain slashes."};if(/^\.+$/.test(n))return{ok:!1,message:"Name cannot contain only dots."};if(n.startsWith("."))return{ok:!1,message:"Name cannot start with a dot."};if(/[<>:"|?*\x00-\x1F]/.test(n))return{ok:!1,message:"Name contains characters that are not allowed in files."};const s=Ra(n);return s?s.length>64?{ok:!1,message:"Name is too long after formatting."}:La.has(s)?{ok:!1,message:`"${s}" is reserved by the workspace.`}:/[a-z0-9]/.test(s)?{ok:!0,raw:n,slug:s}:{ok:!1,message:"Name must include letters or numbers."}:{ok:!1,message:"Name must include letters or numbers."}}function te(i){const t=i.replace(/^\/+|\/+$/g,"");return t.split("/").pop()??t}function Xn(i,t){const e=i.replace(/^\/+|\/+$/g,""),n=t.replace(/^\/+|\/+$/g,"");return e?`${e}/${n}`:n}function $a(i){if(!i)return;const t=new URLSearchParams(i).get("region");if(!t)return;const e=t.split(",").map(n=>Number(n.trim()));if(!(e.length!==4||e.some(n=>!Number.isFinite(n))||e[2]<=0||e[3]<=0))return[e[0],e[1],e[2],e[3]]}function Oe(i){return Math.max(la,Math.min(da,i))}const xs=document.getElementById("app");if(!xs)throw new Error("Missing #app root element");new ha(xs);
|