viteboard 0.1.0 → 0.1.1
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/README.md +110 -61
- package/content/docs/guide/boards-in-markdown.md +28 -26
- package/content/docs/guide/boards.md +44 -82
- package/content/docs/guide/getting-started.md +36 -48
- package/content/docs/index.md +21 -32
- package/dist/assets/index-C30zRPsW.js +60 -0
- package/dist/assets/index-e5aqtN8W.css +1 -0
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/app/AppController.ts +7 -0
- package/src/elements/renderElement.ts +89 -0
- package/src/elements/types.ts +1 -0
- package/src/shell/Shell.ts +4 -6
- package/src/shell/Sidebar.ts +382 -83
- package/src/styles.css +75 -0
- package/src/templates/handoffTemplate.ts +34 -0
- package/src/templates/helpers.ts +10 -2
- package/src/templates/index.ts +14 -0
- package/src/templates/systemDiagramTemplate.ts +6 -6
- package/src/templates/workflowTemplate.ts +35 -0
- package/src/tools/ArrowTool.ts +11 -1
- package/src/tools/SelectTool.ts +1 -1
- package/src/tools/ShapeTool.ts +2 -2
- package/src/tools/ToolContext.ts +7 -0
- package/src/ui/TextEditorOverlay.ts +136 -11
- package/src/ui/Toolbar.ts +39 -1
- package/src/ui/icons.ts +6 -0
- package/vite.config.ts +16 -10
- package/content/docs/capabilities.md +0 -89
- package/content/docs/docs.md +0 -84
- package/content/docs/guide/test.md +0 -6
- package/content/docs/test.md +0 -219
- package/content/docs/tetst-dir/test-board.board.json +0 -15
- package/content/docs/tetst-dir/test-test.md +0 -1
- package/content/docs/workspace.md +0 -73
- package/dist/assets/index-CEpxLM2o.css +0 -1
- package/dist/assets/index-D4xvJdEQ.js +0 -60
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
var Hn=Object.defineProperty;var Fn=(o,t,e)=>t in o?Hn(o,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):o[t]=e;var u=(o,t,e)=>Fn(o,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 i of s)if(i.type==="childList")for(const a of i.addedNodes)a.tagName==="LINK"&&a.rel==="modulepreload"&&n(a)}).observe(document,{childList:!0,subtree:!0});function e(s){const i={};return s.integrity&&(i.integrity=s.integrity),s.referrerPolicy&&(i.referrerPolicy=s.referrerPolicy),s.crossOrigin==="use-credentials"?i.credentials="include":s.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function n(s){if(s.ep)return;s.ep=!0;const i=e(s);fetch(s.href,i)}})();const P="/__api";async function z(o){var t;if(!o.ok){let e="";try{e=((t=await o.json())==null?void 0:t.error)??""}catch{}throw new Error(`${o.status} ${o.statusText}${e?` — ${e}`:""}`)}return o.json()}const k={async getWorkspace(){return z(await fetch(`${P}/workspace`))},async removeProject(o){await z(await fetch(`${P}/workspace/project?id=${encodeURIComponent(o)}`,{method:"DELETE"}))},async pickAndAddProject(){return z(await fetch(`${P}/workspace/pick-project`,{method:"POST"}))},async ensureProjectDocsRoot(o){return(await z(await fetch(`${P}/workspace/project/docs-root`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectId:o})}))).root},async getTree(o){return(await z(await fetch(`${P}/tree?rootId=${encodeURIComponent(o)}`))).tree},async getProjectTree(o){return(await z(await fetch(`${P}/project-tree?projectId=${encodeURIComponent(o)}`))).tree},async createDocsFolder(o,t){await z(await fetch(`${P}/workspace/project/create-docs-folder`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectId:o,parentPath:t})}))},async readFile(o,t){return(await z(await fetch(`${P}/file?rootId=${encodeURIComponent(o)}&path=${encodeURIComponent(t)}`))).content},async writeFile(o,t,e){await z(await fetch(`${P}/file`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({rootId:o,path:t,content:e})}))},async createDirectory(o,t){await z(await fetch(`${P}/directory`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({rootId:o,path:t})}))},async getBoard(o,t){const e=await fetch(`${P}/board?rootId=${encodeURIComponent(o)}&id=${encodeURIComponent(t)}`);return e.status===404?null:(await z(e)).doc},async saveBoard(o,t,e){await z(await fetch(`${P}/board`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({rootId:o,id:t,doc:e})}))},async deleteFile(o,t){await z(await fetch(`${P}/file?rootId=${encodeURIComponent(o)}&path=${encodeURIComponent(t)}`,{method:"DELETE"}))},async renameFile(o,t,e,n=!1){await z(await fetch(`${P}/file`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({rootId:o,oldPath:t,newPath:e,ignoreMissing:n})}))},async deleteBoard(o,t){await z(await fetch(`${P}/board?rootId=${encodeURIComponent(o)}&id=${encodeURIComponent(t)}`,{method:"DELETE"}))},assetUrl(o,t){return`${P}/asset?rootId=${encodeURIComponent(o)}&path=${encodeURIComponent(t)}`},async uploadAsset(o,t,e){return(await z(await fetch(`${P}/asset`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({rootId:o,name:t,base64:e})}))).path}};function Wn(o,t){const e=o.find(i=>i.type==="dir"&&i.name===t);if(!e||e.type!=="dir")return[];const n=[],s=i=>{for(const a of i)a.type==="file"?n.push(a):s(a.children)};return s(e.children),n}function be(o){for(const t of o){if(t.type==="file"&&t.name.endsWith(".md"))return t.path;if(t.type==="dir"){const e=be(t.children);if(e)return e}}return null}function ct(o){const t=[];for(const e of o.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 hn(o){const t=ct(o),e=t.lastIndexOf("/");return e===-1?"":t.slice(0,e)}function On(o,t){if(/^(https?:|mailto:|tel:|#|data:)/.test(t))return t;if(t.startsWith("/"))return ct(t);const e=hn(o);return ct(e?`${e}/${t}`:t)}function xe(o,t){const e=hn(o),n=e?e.split("/").filter(Boolean):[],s=ct(t).split("/").filter(Boolean);let i=0;for(;i<n.length&&i<s.length&&n[i]===s[i];)i++;const a=n.slice(i).map(()=>".."),r=s.slice(i);return[...a,...r].join("/")||s[s.length-1]||""}const Un=["sticky","task","rectangle","ellipse","text","line","arrow","connector","image","screenshot","frame","templateGroup"];function Wt(o){const t=new Set;for(const e of o.elements){const n=e.data.assetId;typeof n=="string"&&t.add(n)}return{version:1,id:o.boardId,title:o.title,createdAt:o.createdAt,updatedAt:Date.now(),viewport:{...o.viewport},theme:o.theme,elements:o.elements,assetIds:[...t]}}function W(o){return typeof o=="number"&&Number.isFinite(o)}function pn(o){if(typeof o!="object"||o===null)return null;const t=o;return typeof t.id!="string"||typeof t.type!="string"||!Un.includes(t.type)||!W(t.x)||!W(t.y)||!W(t.width)||!W(t.height)?null:{id:t.id,type:t.type,x:t.x,y:t.y,width:t.width,height:t.height,rotation:W(t.rotation)?t.rotation:0,zIndex:W(t.zIndex)?t.zIndex:0,locked:t.locked===!0,hidden:t.hidden===!0,createdAt:W(t.createdAt)?t.createdAt:Date.now(),updatedAt:W(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 Zt(o){if(typeof o!="object"||o===null)return null;const t=o;if(t.version!==1||typeof t.id!="string"||!Array.isArray(t.elements))return null;const e=[];for(const s of t.elements){const i=pn(s);i&&e.push(i)}const n=t.viewport??{};return{version:1,id:t.id,title:typeof t.title=="string"?t.title.slice(0,200):"Imported Board",createdAt:W(t.createdAt)?t.createdAt:Date.now(),updatedAt:W(t.updatedAt)?t.updatedAt:Date.now(),viewport:{offsetX:W(n.offsetX)?n.offsetX:0,offsetY:W(n.offsetY)?n.offsetY:0,scale:W(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 _n=".board.json";function it(o){return o.replace(/\.board\.json$/,"")}function Xn(o){return o.trim().replace(/^\/+/,"").replace(/\.board\.json$/,"")}function qn(o,t){return Ht(o,t)[0]??""}function Ht(o,t){const e=o.trim(),n=Xn(o);if(!n)return[];if(e.startsWith("./")||e.startsWith("../")){const r=t.lastIndexOf("/"),c=r===-1?"":t.slice(0,r);return[ct(c?`${c}/${n}`:n)]}if(e.startsWith("/")){const r=t.lastIndexOf("/"),c=r===-1?"":t.slice(0,r),l=ct(c?`${c}/${n}`:n),d=[n];if(l&&l!==n&&d.push(l),c){const h=c.includes("/")?c.slice(0,c.lastIndexOf("/")):"",p=ct(h?`${h}/${n}`:n);p&&!d.includes(p)&&d.push(p)}return d}const s=t.lastIndexOf("/"),i=s===-1?"":t.slice(0,s),a=ct(i?`${i}/${n}`:n);return a&&a!==n?[a,n]:[n]}function Ie(o){return o.toLowerCase().replace(/[^\w\s-]/g,"").trim().replace(/\s+/g,"-").slice(0,48)||"board"}function Yn(o){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(o)})}const O={async listIds(o){const t=await k.getTree(o);return Kn(t).map(e=>it(e.path))},async list(o){const t=await this.listIds(o),e=[];for(const n of t){const s=await this.load(o,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(o,t){const e=await k.getBoard(o,t);if(e)return Zt(e)??void 0},async save(o,t){await k.saveBoard(o,t.id,t)},async delete(o,t){await k.deleteBoard(o,t)},async move(o,t,e){const n=await this.load(o,t);if(!n)throw new Error("Board not found");const s=t.split("/").pop()??t,i=e.replace(/^\/+|\/+$/g,""),a=i?`${i}/${s}`:s;if(a===t)return t;if(new Set(await this.listIds(o)).has(a))throw new Error("A board already exists there");const c=Date.now();return await k.saveBoard(o,a,{...n,id:a,updatedAt:c}),await k.deleteBoard(o,t),a},async rename(o,t,e){const n=await this.load(o,t);if(!n)throw new Error("Board not found");const s=t.includes("/")?t.slice(0,t.lastIndexOf("/")):"",i=Ie(e),a=s?`${s}/${i}`:i,r=Date.now();if(a===t)return await k.saveBoard(o,t,{...n,title:e.trim()||n.title,updatedAt:r}),t;if(new Set(await this.listIds(o)).has(a))throw new Error("A board already exists with that name");return await k.saveBoard(o,a,{...n,id:a,title:e.trim()||n.title,updatedAt:r}),await k.deleteBoard(o,t),a},async create(o,t,e,n=""){const s=new Set(await this.listIds(o)),i=Ie(t);let r=`${n?`${n.replace(/^\/+|\/+$/g,"")}/`:""}${i}`;s.has(r)&&(r=`${r}-${Date.now().toString(36)}`);const c=Date.now(),l={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(o,r,l),r},async uploadImage(o,t,e){const n=await Yn(t);return k.uploadAsset(o,e??"image.png",n)}};function Kn(o){const t=[],e=n=>{for(const s of n)s.type==="file"?s.name.endsWith(_n)&&t.push(s):e(s.children)};return e(o),t}const Vn=new Set(["capabilities","assets"]);function Gn(o){const t=[];function e(s,i,a){if(i.length===0)return;if(i.length===1){s.push({kind:"root",root:a});return}const[r,...c]=i;let l=s.find(d=>d.kind==="dir"&&d.segment===r);l||(l={kind:"dir",segment:r,children:[]},s.push(l)),e(l.children,c,a)}for(const s of o){const i=s.displayPath.split("/").filter(Boolean);e(t,i,s)}function n(s){return s.map(i=>{if(i.kind==="root")return i;const a=n(i.children);if(a.length===1&&a[0].kind==="dir"){const r=a[0];return{kind:"dir",segment:`${i.segment}/${r.segment}`,children:r.children}}return{...i,children:a}})}return n(t)}class Zn{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,"lastWorkspace",{projects:[]});u(this,"lastTreesByRoot",new Map);u(this,"lastTreesByProject",new Map);u(this,"browseModeProjects",new Set);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",i=>{this.contextMenu&&(this.contextMenu.contains(i.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 i=document.createElement("div");i.className="nav-label nav-empty-copy",i.textContent="Add a project root to discover docs folders.",s.appendChild(i),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)}clearBrowseMode(){this.browseModeProjects.size!==0&&(this.browseModeProjects.clear(),this.render())}toggleBrowseMode(t){this.browseModeProjects.has(t)?this.browseModeProjects.delete(t):this.browseModeProjects.add(t),this.render()}setActive(t){var n,s;for(const i of new Set(this.links.values()))i.classList.remove("active");const e=this.links.get(t);if(e)e.classList.add("active");else if(t.startsWith("#/root/")){const i=t.match(/^#\/root\/([^/]+)\/(boards|capabilities|docs)(?:\/(.+))?$/);if(i){const[,a,r]=i,c=decodeURIComponent(a);r==="boards"?(n=this.links.get(ns(c)))==null||n.classList.add("active"):r==="capabilities"&&((s=this.links.get(Re(c)))==null||s.classList.add("active"))}}this.syncCollapsed(t)}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 i=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===i);if(m){c=p.id,l=m.displayPath;break}}if(!c||!l)return e;e.add(c);const d=l.split("/").filter(Boolean).slice(0,-1);let h="";for(const p of d){h=h?`${h}/${p}`:p;const m=`pathseg:${c}:${h}`;this.collapsibles.has(m)&&e.add(m)}if(a==="docs"&&r){const p=r.split("/");for(let m=1;m<p.length;m++){const g=p.slice(0,m).join("/"),y=`doc:${i}:${g}`;this.collapsibles.has(y)&&e.add(y)}}return e}projectSection(t,e){const{id:n,label:s,docsRoots:i}=t,a=this.browseModeProjects.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("button");l.className="nav-repo-header",l.type="button";const d=document.createElement("span");d.className="nav-repo-chevron",d.textContent="›";const h=document.createElement("span");h.className="nav-repo-label-text",h.textContent=s;const p=document.createElement("button");p.className="nav-repo-more",p.type="button",p.title="Project options",p.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>',p.addEventListener("click",g=>{g.stopPropagation();const y=p.getBoundingClientRect();this.showContextMenu(y.right+4,y.top,s,[{icon:a?yt:It,iconClass:a?"nav-ctx-doc":"nav-ctx-dir",label:a?"View: Docs":"View: Raw",action:()=>this.toggleBrowseMode(n)},{icon:$e,iconClass:"nav-ctx-edit",label:"Wiki",action:()=>{location.hash=`#/project/${encodeURIComponent(n)}`}},{kind:"divider"},{icon:yt,iconClass:"nav-ctx-doc",label:"New Doc",action:()=>this.callbacks.onCreateProjectDoc(n)},{icon:gt,iconClass:"nav-ctx-board",label:"New Board",action:()=>this.callbacks.onCreateProjectBoard(n)},{icon:It,iconClass:"nav-ctx-dir",label:"New Directory",action:()=>this.callbacks.onCreateProjectDirectory(n)},{kind:"divider"},{icon:Ot,iconClass:"nav-ctx-delete",label:"Remove from workspace",action:()=>this.callbacks.onRemoveProject(n,s),danger:!0}])}),l.append(d,h,p),this.registerCollapsible(n,r,c,l,!0),l.addEventListener("click",()=>this.toggleCollapsible(n)),l.addEventListener("contextmenu",g=>{g.preventDefault(),g.stopPropagation(),p.click()});const m=i[0];if(m&&this.enableDirectoryDropTarget(l,m.id,""),a){const g=this.lastTreesByProject.get(n)??[];for(const y of g)y.type==="dir"&&r.appendChild(this.renderBrowseNode(t,y,e))}else if(i.length===0){const g=document.createElement("div");g.className="nav-label nav-empty-copy",g.textContent="No docs folders found.",r.appendChild(g)}else{const g=Gn(i);r.appendChild(this.renderPathNodes(g,e,n))}if(i.length>0){const g=document.createElement("div");g.className="nav-project-boards-row";const y=this.link(ss(n),"All Boards",`boards:${n}`);y.classList.add("nav-link-board"),y.insertAdjacentHTML("afterbegin",gt),g.appendChild(y),r.appendChild(g);const v=i.find(S=>{const C=(e.get(S.id)??[]).find(T=>T.type==="dir"&&T.name==="capabilities");return(C==null?void 0:C.type)==="dir"&&C.children.some(T=>T.type==="file"&&T.name.endsWith(".md"))})??i[0],w=document.createElement("div");w.className="nav-project-boards-row";const x=this.link(Re(v.id),"All Capabilities",`caps:${n}`);x.classList.add("nav-link-capability"),x.insertAdjacentHTML("afterbegin",Le),w.appendChild(x),r.appendChild(w)}return c.append(l,r),c}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 i=mn(e),a=`browse:${t.id}:${e.path}`,r=document.createElement("div");r.className=i?"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=i?"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)),i){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:It,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 i=document.createElement("div");for(const a of t)if(a.kind==="root")i.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=fn(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),i.appendChild(d)}return i}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,Jn(e),"",e)),n}createDirectoryRow(t,e,n,s,i=!1){const a=document.createElement("div");a.className="nav-dir-header-row";const r=document.createElement("button");r.className=i?"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 i=document.createElement("div"),a=un(e),r=Qn(s,n),c=[...a.map(l=>({kind:"doc",node:l})),...r.map(l=>({kind:"board",name:Lt(it(l.path).split("/").pop()??it(l.path)),boardId:it(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.node.type==="dir",g=d.kind==="doc"&&d.node.type==="dir";if(m!==g)return m?-1:1;const y=l.kind==="doc"?l.node.name:l.name,v=d.kind==="doc"?d.node.name:d.name;return y.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=os(t.id,l.boardId),d.insertAdjacentHTML("afterbegin",gt),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)}),i.appendChild(d)}else{const{node:d}=l;if(d.type==="file"){if(!d.name.endsWith(".md"))continue;const h=d.name==="index.md"?"Overview":Lt(d.name),p=this.link(es(t.id,d.path),h,`doc:${t.id}:${d.path}`);p.insertAdjacentHTML("afterbegin",yt),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)}),i.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(Lt(d.name),`#/root/${encodeURIComponent(t.id)}/directory/${encodeURIComponent(d.path)}`,`${d.name} wiki`,v=>{v.button===0&&this.toggleCollapsible(h)},!1);this.enableDragSource(y,{kind:"dir",rootId:t.id,path:d.path}),m.addEventListener("contextmenu",v=>{v.preventDefault(),v.stopPropagation(),this.openDirContextMenu(v.clientX,v.clientY,t,d.path)}),this.enableDirectoryDropTarget(m,t.id,d.path),this.registerCollapsible(h,p,m,y,!0),m.append(g,p),i.appendChild(m)}}return i}enableDragSource(t,e){t.draggable=!0,t.addEventListener("dragstart",n=>{var s,i;this.closeContextMenu(),this.draggedItem=e,t.classList.add("nav-dragging"),(s=n.dataTransfer)==null||s.setData("application/x-docs-nav-item",JSON.stringify(e)),(i=n.dataTransfer)==null||i.setData("text/plain",is(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 i=this.currentDragItem(s);!i||!this.canDropItem(i,e,n)||(s.preventDefault(),s.stopPropagation(),s.dataTransfer&&(s.dataTransfer.dropEffect="move"),t.classList.add("nav-drop-target"))}),t.addEventListener("dragleave",s=>{t.contains(s.relatedTarget)||t.classList.remove("nav-drop-target")}),t.addEventListener("drop",s=>{const i=this.currentDragItem(s);!i||!this.canDropItem(i,e,n)||(s.preventDefault(),s.stopPropagation(),this.clearDropTargets(),this.moveDraggedItem(i,n))})}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==="board"||s.kind==="dir")&&typeof s.rootId=="string")return s}catch{}}canDropItem(t,e,n){if(t.rootId!==e)return!1;const s=Gt(n);if(t.kind==="doc")return he(t.path)!==s;if(t.kind==="board")return he(t.boardId)!==s;const i=Gt(t.path);return!i||i===s||he(i)===s?!1:!s.startsWith(`${i}/`)}moveDraggedItem(t,e){const n=Gt(e);t.kind==="doc"?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.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 i=t.parentElement;if(!i)return;t.style.display="none",i.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 i=!1;const a=r=>{if(!i&&(i=!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,i){this.collapsibles.set(t,{body:e,group:n,btn:s}),this.applyCollapsed(t,this.collapsedState.get(t)??i)}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 i=s?Lt(s.split("/").pop()??s):n.displayPath.split("/").pop()??n.displayPath,a=[{icon:It,iconClass:"nav-ctx-dir",label:"New Directory",action:()=>this.callbacks.onCreateDirectory(n.id,s)},{icon:gt,iconClass:"nav-ctx-board",label:"New Board",action:()=>this.callbacks.onCreateBoard(n.id,s)},{icon:yt,iconClass:"nav-ctx-doc",label:"New Doc",action:()=>this.callbacks.onCreateDoc(n.id,s)},{icon:Le,iconClass:"nav-ctx-cap",label:"New Capability",action:()=>this.callbacks.onCreateCapability(n.id,s)}];s&&a.push({icon:de,iconClass:"nav-ctx-rename",label:"Rename",action:()=>this.callbacks.onRenameDirectory(n.id,s)},{icon:Ot,iconClass:"nav-ctx-delete",label:"Delete",action:()=>this.callbacks.onDeleteDirectory(n.id,s)}),this.showContextMenu(t,e,i,a)}openEmptyProjectContextMenu(t,e,n,s){this.showContextMenu(t,e,s,[{icon:It,iconClass:"nav-ctx-dir",label:"New Directory",action:()=>this.callbacks.onCreateProjectDirectory(n)},{icon:gt,iconClass:"nav-ctx-board",label:"New Board",action:()=>this.callbacks.onCreateProjectBoard(n)},{icon:yt,iconClass:"nav-ctx-doc",label:"New Doc",action:()=>this.callbacks.onCreateProjectDoc(n)}])}openDocContextMenu(t,e,n,s){const i=s.split("/").pop()??s;this.showContextMenu(t,e,i,[{icon:yt,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:de,iconClass:"nav-ctx-rename",label:"Rename",action:()=>this.callbacks.onRenameDoc(n.id,s)},{icon:Ot,iconClass:"nav-ctx-delete",label:"Delete",action:()=>this.callbacks.onDeleteDoc(n.id,s),danger:!0}])}openBoardContextMenu(t,e,n,s){const i=Lt(s.split("/").pop()??s);this.showContextMenu(t,e,i,[{icon:gt,iconClass:"nav-ctx-board",label:"Open",action:()=>this.callbacks.onOpenBoard(n.id,s)},{kind:"divider"},{icon:de,iconClass:"nav-ctx-rename",label:"Rename",action:()=>this.callbacks.onRenameBoard(n.id,s)},{icon:Ot,iconClass:"nav-ctx-delete",label:"Delete",action:()=>this.callbacks.onDeleteBoard(n.id,s),danger:!0}])}showContextMenu(t,e,n,s){this.closeContextMenu();const i=document.createElement("div");i.className="nav-context-menu",i.addEventListener("pointerdown",c=>c.stopPropagation());const a=document.createElement("div");a.className="nav-context-label",a.textContent=n,i.appendChild(a);const r=document.createElement("div");r.className="nav-context-divider",i.appendChild(r);for(const c of s){if(c.kind==="divider"){const p=document.createElement("div");p.className="nav-context-divider",i.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()}),i.appendChild(l)}i.style.position="fixed",i.style.left=`${t}px`,i.style.top=`${e}px`,document.body.appendChild(i),this.contextMenu=i,requestAnimationFrame(()=>{if(!this.contextMenu)return;const c=i.getBoundingClientRect();c.right>window.innerWidth&&(i.style.left=`${Math.max(0,t-c.width)}px`),c.bottom>window.innerHeight&&(i.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 gt='<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>',Le='<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>',yt='<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>',It='<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>',de='<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>',Ot='<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 un(o){return o.filter(t=>t.type==="file"?t.name.endsWith(".md"):!Vn.has(t.name)).map(t=>t.type==="file"?t:{...t,children:un(t.children)})}function Jn(o){const t=o.find(e=>e.type==="dir"&&e.name.toLowerCase()==="docs");return!t||t.type!=="dir"?o:o.filter(e=>e!==t).concat(t.children)}function Qn(o,t){const e=t?`${t}/`:"";return ts(o).filter(n=>e?n.path.startsWith(e)?!n.path.slice(e.length).includes("/"):!1:!n.path.includes("/"))}function ts(o){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(o),t}function es(o,t){return`#/root/${encodeURIComponent(o)}/docs/${encodeURIComponent(t)}`}function ns(o){return`#/root/${encodeURIComponent(o)}/boards`}function ss(o){return`#/project/${encodeURIComponent(o)}/boards`}function os(o,t){return`#/root/${encodeURIComponent(o)}/boards/${encodeURIComponent(t)}`}function Re(o){return`#/root/${encodeURIComponent(o)}/capabilities`}function Lt(o){const t=o.replace(/\.md$/,"").replace(/[-_]/g," ");return t.charAt(0).toUpperCase()+t.slice(1)}function mn(o){if(o.type==="file")return!1;for(const t of o.children)if(t.type==="dir"&&t.name==="docs"||t.type==="dir"&&mn(t))return!0;return!1}function Gt(o){return o.replace(/^\/+|\/+$/g,"")}function he(o){const t=Gt(o),e=t.lastIndexOf("/");return e===-1?"":t.slice(0,e)}function is(o){return o.kind==="doc"?o.path:o.kind==="board"?o.boardId:o.path}function fn(o){if(o.kind==="root")return o.root;for(const t of o.children){const e=fn(t);if(e)return e}return null}function Nt(o,t={}){const{frontmatter:e,body:n}=as(o);try{const s=gn(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>${ne(String(s))}</pre></div>`}}}function as(o){const t={};if(!o.startsWith("---"))return{frontmatter:t,body:o};const e=o.indexOf(`
|
|
3
|
-
---`,3);if(e===-1)return{frontmatter:t,body:o};const n=o.slice(3,e).trim();for(const i of n.split(`
|
|
4
|
-
`)){const a=i.indexOf(":");a!==-1&&(t[i.slice(0,a).trim()]=i.slice(a+1).trim())}const s=o.slice(e+4).replace(/^\n+/,"");return{frontmatter:t,body:s}}function gn(o,t){const e=[];let n=0,s=0;const i=o.length*4+1e3;for(;n<o.length;){if(++s>i)throw new Error(`renderBlocks exceeded ${i} iterations at line ${n} — aborting to avoid an infinite loop`);const a=o[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<o.length&&!p.test(o[n]);)m.push(o[n]),n++;n++,h==="board"?e.push(ls(vn(m.join(`
|
|
5
|
-
`)))):e.push(`<pre class="code-block"><code${h?` class="language-${h}"`:""}>${ne(m.join(`
|
|
6
|
-
`))}</code></pre>`);continue}const c=a.match(/^(#{1,6})\s+(.*)$/);if(c){const d=c[1].length,h=pt(c[2].trim(),t),p=ps(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<o.length&&/^>\s?/.test(o[n]);)d.push(o[n].replace(/^>\s?/,"")),n++;e.push(`<blockquote>${gn(d,t)}</blockquote>`);continue}if(a.includes("|")&&n+1<o.length&&rs(o[n+1])){const d=[];for(;n<o.length&&o[n].includes("|")&&o[n].trim()!=="";)d.push(o[n]),n++;e.push(cs(d,t));continue}if(kt(a)){const d=[];for(;n<o.length&&(kt(o[n])||/^\s+\S/.test(o[n])||o[n].trim()==="");){if(o[n].trim()===""){const h=o[n+1];if(h===void 0||!kt(h)&&!/^\s+\S/.test(h))break}d.push(o[n]),n++}e.push(yn(d,t));continue}const l=[o[n]];for(n++;n<o.length&&o[n].trim()!==""&&!/^`{3,}/.test(o[n])&&!/^#{1,6}\s/.test(o[n])&&!/^>\s?/.test(o[n])&&!kt(o[n])&&!/^(-{3,}|\*{3,}|_{3,})\s*$/.test(o[n]);)l.push(o[n]),n++;e.push(`<p>${pt(l.join(" ").trim(),t)}</p>`)}return e.join(`
|
|
7
|
-
`)}function kt(o){return/^\s*([-*+]|\d+\.)\s+/.test(o)}function yn(o,t){const e=o.filter(l=>kt(l)).map(l=>l.match(/^\s*/)[0].length);if(e.length===0)return pt(o.join(" ").trim(),t);const n=Math.min(...e),s=[];let i=!1,a=null;for(const l of o){const d=l.match(/^\s*/)[0].length,h=l.match(/^\s*([-*+]|\d+\.)\s+(.*)$/);if(h&&d===n){i=/\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:i,content:[p],checked:m},s.push(a)}else a&&a.content.push(l.slice(n))}const r=i?"ol":"ul",c=s.map(l=>{const d=l.content[0],h=l.content.slice(1);let p=pt(d,t);const m=h.filter(g=>g.trim()!=="");return m.some(g=>kt(g))?p+=yn(h,t):m.length&&(p+=" "+pt(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 rs(o){return/^\s*\|?\s*:?-{1,}:?\s*(\|\s*:?-{1,}:?\s*)*\|?\s*$/.test(o)}function pe(o){return o.trim().replace(/^\|/,"").replace(/\|$/,"").split("|").map(t=>t.trim())}function cs(o,t){const e=pe(o[0]),n=pe(o[1]).map(r=>{const c=r.startsWith(":"),l=r.endsWith(":");return c&&l?"center":l?"right":c?"left":""}),s=o.slice(2).map(pe),i=e.map((r,c)=>`<th${n[c]?` style="text-align:${n[c]}"`:""}>${pt(r,t)}</th>`).join(""),a=s.map(r=>`<tr>${r.map((c,l)=>`<td${n[l]?` style="text-align:${n[l]}"`:""}>${pt(c,t)}</td>`).join("")}</tr>`).join("");return`<table><thead><tr>${i}</tr></thead><tbody>${a}</tbody></table>`}function vn(o){const t={};for(const n of o.split(`
|
|
8
|
-
`)){const s=n.replace(/#.*$/,"").trim();if(!s)continue;const i=s.indexOf(":");i!==-1&&(t[s.slice(0,i).trim()]=s.slice(i+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 ls(o){const t=o.region?o.region.join(","):"";return`<figure class="board-embed" data-src="${Et(o.src)}" data-region="${t}" data-height="${o.height}" data-title="${Et(o.title??"")}"></figure>`}function pt(o,t){let e=ne(o);const n=[];e=e.replace(/\\([\\`*_{}\[\]()#+\-.!|])/g,(i,a)=>(n.push(a),`${n.length-1}`));const s=[];return e=e.replace(/`([^`]+)`/g,(i,a)=>(s.push(`<code>${a}</code>`),`\0${s.length-1}\0`)),e=e.replace(/!\[([^\]]*)\]\(([^)\s]+)\)/g,(i,a,r)=>{const c=ke(r,t),l=ds(r,t),d=c===r?"":` data-md-src="${Et(r)}"`;return`<img src="${l}" alt="${Et(a)}" loading="lazy"${d} />`}),e=e.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g,(i,a,r)=>{const c=hs(r,t),d=/^(https?:|mailto:|tel:)/.test(r)?' target="_blank" rel="noopener noreferrer"':"",h=c===r?"":` data-md-href="${Et(r)}"`;return`<a href="${Et(c)}"${d}${h}>${a}</a>`}),e=e.replace(/\*\*([\s\S]+?)\*\*/g,"<strong>$1</strong>"),e=e.replace(/__([\s\S]+?)__/g,"<strong>$1</strong>"),e=e.replace(/(^|[^*])\*([^*\s][^*]*)\*/g,"$1<em>$2</em>"),e=e.replace(/(^|[^_])_([^_\s][^_]*)_/g,"$1<em>$2</em>"),e=e.replace(/\u0000(\d+)\u0000/g,(i,a)=>s[Number(a)]),e=e.replace(/\u0001(\d+)\u0001/g,(i,a)=>n[Number(a)]),e}function ds(o,t){if(/^(https?:|data:)/.test(o)||!t.rootId||!t.docPath)return o;const e=ke(o,t);return e?k.assetUrl(t.rootId,e):o}function hs(o,t){if(/^(https?:|mailto:|tel:|#|data:)/.test(o)||!t.rootId||!t.docPath)return o;const e=ke(o,t);return e?e.endsWith(".md")?`#/root/${encodeURIComponent(t.rootId)}/docs/${encodeURIComponent(e)}`:k.assetUrl(t.rootId,e):o}function ke(o,t){return t.docPath?On(t.docPath,o):o}function ne(o){return o.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}function Et(o){return ne(o).replace(/"/g,""")}function ps(o){return o.toLowerCase().replace(/[^\w\s-]/g,"").trim().replace(/\s+/g,"-")}class bn{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}}let ue=0;function Tt(){return ue=(ue+1)%65535,Date.now().toString(36)+"-"+ue.toString(36)+"-"+Math.random().toString(36).slice(2,8)}function D(o,t={}){const e=Date.now();return{id:Tt(),type:o,x:0,y:0,width:100,height:100,rotation:0,zIndex:0,locked:!1,hidden:!1,createdAt:e,updatedAt:e,style:{},data:{},...t}}const us="viteboard",ms=1;class fs{constructor(){u(this,"dbPromise",null)}open(){return this.dbPromise?this.dbPromise:(this.dbPromise=new Promise((t,e)=>{const n=indexedDB.open(us,ms);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,i)=>{const a=n.transaction(t,"readwrite");a.objectStore(t).put(e),a.oncomplete=()=>s(),a.onerror=()=>i(a.error??new Error("IndexedDB write failed"))})}async get(t,e){const n=await this.open();return new Promise((s,i)=>{const r=n.transaction(t,"readonly").objectStore(t).get(e);r.onsuccess=()=>s(r.result),r.onerror=()=>i(r.error??new Error("IndexedDB read failed"))})}async delete(t,e){const n=await this.open();return new Promise((s,i)=>{const a=n.transaction(t,"readwrite");a.objectStore(t).delete(e),a.oncomplete=()=>s(),a.onerror=()=>i(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 i=e.transaction(t,"readwrite");i.objectStore(t).clear(),i.oncomplete=()=>n(),i.onerror=()=>s(i.error??new Error("IndexedDB clear failed"))})}}const _=new fs;class gs{async get(t){try{return await _.get("assets",t)}catch{return}}async createFromBlob(t,e){const n=await createImageBitmap(t),s=Date.now(),i={id:Tt(),type:"image",mimeType:t.type||"image/png",blob:t,width:n.width,height:n.height,sizeBytes:t.size,createdAt:s,updatedAt:s,name:e};return n.close(),await _.put("assets",i),i}async put(t){await _.put("assets",t)}async delete(t){await _.delete("assets",t)}async getAllKeys(){try{return await _.getAllKeys("assets")}catch{return[]}}async getAll(){try{return await _.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 _.delete("assets",s),n++);return n}async clearAll(){await _.clearStore("assets")}}const Z=new gs,St=new Map,ys=2e3;function G(o){for(const t of St.keys())t.startsWith(o+"|")&&St.delete(t)}function wn(o){if(o.data.shapeKind==="chevron"){const t=Math.abs(o.height)*.34,e=Math.abs(o.width)*.18;return Math.round(Math.max(13,Math.min(48,t,e)))}return o.style.fontSize??14}function vs(o,t,e,n){const s=St.get(t);if(s)return s;const i=[];for(const a of e.split(`
|
|
9
|
-
`)){if(a===""){i.push("");continue}const r=a.split(" ");let c="";for(const l of r){const d=c===""?l:c+" "+l;o.measureText(d).width>n&&c!==""?(i.push(c),c=l):c=d}i.push(c)}return St.size>ys&&St.clear(),St.set(t,i),i}function zt(o,t,e,n,s,i){const a=Math.min(i,n/2,s/2);o.beginPath(),o.moveTo(t+a,e),o.arcTo(t+n,e,t+n,e+s,a),o.arcTo(t+n,e+s,t,e+s,a),o.arcTo(t,e+s,t,e,a),o.arcTo(t,e,t+n,e,a),o.closePath()}function Jt(o,t){const{ctx:e,lod:n}=t;if(e.save(),e.globalAlpha=o.style.opacity??1,n===0){bs(o,t),e.restore();return}switch(o.type){case"sticky":ws(o,t);break;case"task":Cs(o,t);break;case"rectangle":case"frame":De(o,t);break;case"ellipse":Ss(o,t);break;case"text":Ts(o,t);break;case"line":case"arrow":case"connector":Bs(o,t);break;case"image":case"screenshot":Ms(o,t);break;default:De(o,t)}e.restore()}function bs(o,t){const{ctx:e}=t;if(o.type==="line"||o.type==="arrow"||o.type==="connector"){e.strokeStyle=o.style.stroke??(t.theme==="dark"?"#888":"#999"),e.lineWidth=Math.max(1/t.scale,o.style.strokeWidth??2),e.beginPath(),e.moveTo(o.x,o.y),e.lineTo(o.x+o.width,o.y+o.height),e.stroke();return}e.fillStyle=o.style.fill??(t.theme==="dark"?"#3a3d42":"#d8d8d4"),e.fillRect(o.x,o.y,o.width,o.height)}function se(o,t){o.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 oe(o){o.ctx.shadowColor="transparent",o.ctx.shadowBlur=0,o.ctx.shadowOffsetY=0}function ws(o,t){const{ctx:e,lod:n}=t,s=o.style.fill??"#fef08a";if(se(o,t),zt(e,o.x,o.y,o.width,o.height,o.style.borderRadius??12),e.fillStyle=s,e.fill(),oe(t),n===2){const i=o.data.text??"";i&&ie(o,t,i,14)}}function Cs(o,t){const{ctx:e,lod:n}=t,s=o.data.checked===!0,i=t.theme==="dark",a=o.style.borderRadius??14;if(se(o,t),zt(e,o.x,o.y,o.width,o.height,a),e.fillStyle=o.style.fill??(i?"#17191b":"#ffffff"),e.fill(),oe(t),e.strokeStyle=o.style.stroke??(i?"rgba(255,255,255,0.12)":"rgba(0,0,0,0.09)"),e.lineWidth=o.style.strokeWidth??1,e.stroke(),n<2)return;const r=Math.min(22,Math.max(16,o.height*.38)),c=o.x+16,l=o.y+o.height/2-r/2;zt(e,c,l,r,r,6),e.fillStyle=s?"#0078d4":i?"#111314":"#f3f2f1",e.fill(),e.strokeStyle=s?"#0078d4":i?"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=o.data.text??"";if(!d)return;const h=50,p=16,m=o.style.fontSize??15,g=o.style.fontFamily??'-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif';if(e.font=`${o.style.fontWeight??"650"} ${m}px ${g}`,e.fillStyle=s?i?"#a7aaa8":"#777777":o.style.textColor??(i?"#f6f6f3":"#252829"),e.textAlign="left",e.textBaseline="middle",e.save(),e.beginPath(),e.rect(o.x+h,o.y,Math.max(10,o.width-h-p),o.height),e.clip(),e.fillText(d,o.x+h,o.y+o.height/2),s){const y=e.measureText(d),v=o.y+o.height/2+1;e.strokeStyle=i?"rgba(167,170,168,0.58)":"rgba(119,119,119,0.48)",e.lineWidth=1.3,e.beginPath(),e.moveTo(o.x+h,v),e.lineTo(o.x+h+Math.min(y.width,o.width-h-p),v),e.stroke()}e.restore()}function De(o,t){const{ctx:e,lod:n}=t,s=o.data.shapeKind??"rectangle",i=o.style.borderRadius??0;if(se(o,t),s==="diamond"?xs(e,o):s==="triangle"?ks(e,o):s==="chevron"?Es(e,o):s==="pill"?zt(e,o.x,o.y,o.width,o.height,Math.min(Math.abs(o.height)/2,Math.abs(o.width)/2)):i>0?zt(e,o.x,o.y,o.width,o.height,i):(e.beginPath(),e.rect(o.x,o.y,o.width,o.height)),o.style.fill&&o.style.fill!=="transparent"&&(e.fillStyle=o.style.fill,e.fill()),oe(t),o.style.stroke&&(o.style.strokeWidth??0)>0&&(e.strokeStyle=o.style.stroke,e.lineWidth=o.style.strokeWidth??1,e.stroke()),n===2){const a=o.data.text??"";if(a){const r=s==="chevron"?Math.min(Math.abs(o.width)*.18,Math.abs(o.height)*.55)+8:10;ie(o,t,a,r)}}}function xs(o,t){const e=t.x,n=t.y,s=t.width,i=t.height;o.beginPath(),o.moveTo(e+s/2,n),o.lineTo(e+s,n+i/2),o.lineTo(e+s/2,n+i),o.lineTo(e,n+i/2),o.closePath()}function ks(o,t){const e=t.x,n=t.y,s=t.width,i=t.height;o.beginPath(),o.moveTo(e+s/2,n),o.lineTo(e+s,n+i),o.lineTo(e,n+i),o.closePath()}function Es(o,t){const e=t.x,n=t.y,s=t.width,i=t.height,a=Math.min(Math.abs(s)*.22,Math.abs(i)*.62);o.beginPath(),o.moveTo(e,n),o.lineTo(e+s-a,n),o.lineTo(e+s,n+i/2),o.lineTo(e+s-a,n+i),o.lineTo(e,n+i),o.lineTo(e+a,n+i/2),o.closePath()}function Ss(o,t){const{ctx:e,lod:n}=t;if(se(o,t),e.beginPath(),e.ellipse(o.x+o.width/2,o.y+o.height/2,Math.abs(o.width/2),Math.abs(o.height/2),0,0,Math.PI*2),o.style.fill&&o.style.fill!=="transparent"&&(e.fillStyle=o.style.fill,e.fill()),oe(t),o.style.stroke&&(o.style.strokeWidth??0)>0&&(e.strokeStyle=o.style.stroke,e.lineWidth=o.style.strokeWidth??1,e.stroke()),n===2){const s=o.data.text??"";s&&ie(o,t,s,10)}}function Ts(o,t){if(t.lod<2){t.ctx.fillStyle=t.theme==="dark"?"#55585e":"#c5c5c0",t.ctx.fillRect(o.x,o.y,o.width,Math.min(o.height,6/t.scale));return}const e=o.data.text??"";ie(o,t,e,0,"left","top")}function ie(o,t,e,n,s="center",i="middle"){const{ctx:a}=t,r=o.data.shapeKind==="chevron",c=wn(o),l=o.style.fontFamily??'-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',d=r?"750":o.style.fontWeight??"400";a.font=`${d} ${c}px ${l}`,a.fillStyle=o.style.textColor??(t.theme==="dark"&&o.type==="text"?"#f2f2f2":"#1f2933");const h=Math.max(10,o.width-n*2),p=`${o.id}|${o.updatedAt}|${c}|${h.toFixed(0)}`,m=vs(a,p,e,h),g=c*1.35,y=m.length*g;let v;i==="middle"?v=o.y+o.height/2-y/2+g/2:v=o.y+n+g/2,a.textBaseline="middle",a.textAlign=s;const w=s==="center"?o.x+o.width/2:o.x+n;a.save(),a.beginPath(),a.rect(o.x,o.y,o.width,o.height),a.clip();for(let x=0;x<m.length;x++)a.fillText(m[x],w,v+x*g);a.restore()}function Bs(o,t){const{ctx:e,lod:n}=t,s=o.x,i=o.y,a=o.x+o.width,r=o.y+o.height,c=o.style.stroke??(t.theme==="dark"?"#d1d5db":"#374151"),l=o.style.strokeWidth??2;if(e.strokeStyle=c,e.fillStyle=c,e.lineWidth=l,e.beginPath(),e.moveTo(s,i),e.lineTo(a,r),e.stroke(),n>=1){const d=Math.atan2(r-i,a-s),h=Math.max(11,l*5.5);(o.style.arrowEnd??o.type==="arrow")&&Pe(e,a,r,d,h),o.style.arrowStart&&Pe(e,s,i,d+Math.PI,h)}}function Pe(o,t,e,n,s){o.beginPath(),o.moveTo(t,e),o.lineTo(t-s*Math.cos(n-Math.PI/6),e-s*Math.sin(n-Math.PI/6)),o.lineTo(t-s*Math.cos(n+Math.PI/6),e-s*Math.sin(n+Math.PI/6)),o.closePath(),o.fill()}function Ms(o,t){const{ctx:e,imageCache:n}=t,s=o.data.src,i=o.data.assetId,a=s?n.getBySrc(s):i?n.get(i):null;if(a){if(t.lod===1&&t.scale<.15){e.fillStyle=t.theme==="dark"?"#3a3d42":"#d8d8d4",e.fillRect(o.x,o.y,o.width,o.height);return}e.drawImage(a,o.x,o.y,o.width,o.height)}else if(e.fillStyle=t.theme==="dark"?"#2a2d31":"#ececea",e.fillRect(o.x,o.y,o.width,o.height),e.strokeStyle=t.theme==="dark"?"#4a4d52":"#c9c9c5",e.lineWidth=1/t.scale,e.strokeRect(o.x,o.y,o.width,o.height),t.lod===2){const c=(s?n.isMissingSrc(s):i?n.isMissing(i):!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,o.x+o.width/2,o.y+o.height/2)}}class Is{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 i=e.region?`?region=${encodeURIComponent(e.region.join(","))}`:"";if(s.href=`#/root/${encodeURIComponent(e.rootId)}/boards/${encodeURIComponent(e.src)}${i}`,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 bn(Z,()=>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?Zt(r):null,n){s=a;break}}if(!n)return this.renderError(`Board "${this.opts.src}" not found`);this.opts.src=s;const i=this.opts.region?`?region=${encodeURIComponent(this.opts.region.join(","))}`:"";this.openLink.href=`#/root/${encodeURIComponent(this.opts.rootId)}/boards/${encodeURIComponent(s)}${i}`,this.doc=n,this.region=this.opts.region??Ls(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 i=this.ctx;i.setTransform(s,0,0,s,0,0),i.clearRect(0,0,e,n),i.fillStyle=this.opts.theme==="dark"?"#111315":"#f3f2f1",i.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;i.save(),i.translate(h,p),i.scale(d,d);const m={ctx:i,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((v,w)=>v.zIndex-w.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||Jt(y,m);i.restore()}destroy(){this.ro.disconnect(),this.imageCache.clear()}}function Ls(o){if(o.length===0)return[0,0,800,600];let t=1/0,e=1/0,n=-1/0,s=-1/0;for(const a of o)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 i=40;return[t-i,e-i,n-t+i*2,s-e+i*2]}function At(){return document.documentElement.getAttribute("data-theme")==="dark"?"dark":"light"}function Ae(o,t,e){const{html:n,frontmatter:s}=Nt(t,e),i=document.createElement("article");i.className="markdown-body",i.innerHTML=n,o.replaceChildren(i);const a=[];return i.querySelectorAll("figure.board-embed").forEach(r=>{const c=r.dataset.src??"";if(!c)return;const l=Ht(c,e.docPath),d=l[0]??"",h=r.dataset.region??"",p=h?h.split(",").map(Number):[],m=p.length===4&&p.every(v=>Number.isFinite(v))?[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 Is(r,{rootId:e.rootId,src:d,srcCandidates:l,region:m,height:g,title:y,theme:At()}))}catch(v){console.error("Board embed failed to mount",c,v),r.textContent=`Could not load board "${c}".`}}),{frontmatter:s,destroy:()=>{for(const r of a)r.destroy()}}}const $s=.05,Rs=40,Cn=1;function $(o,t,e){return{x:(o-e.offsetX)/e.scale,y:(t-e.offsetY)/e.scale}}function we(o,t,e){return{x:o*e.scale+e.offsetX,y:t*e.scale+e.offsetY}}function xn(o){return Math.min(Rs,Math.max($s,o))}function jt(o,t,e,n){const s=xn(n),i={x:(t-o.offsetX)/o.scale,y:(e-o.offsetY)/o.scale};o.scale=s,o.offsetX=t-i.x*o.scale,o.offsetY=e-i.y*o.scale}function Ds(o,t,e){const n=$(0,0,o),s=$(t,e,o);return{left:n.x,top:n.y,right:s.x,bottom:s.y}}class Ps{constructor(){u(this,"boardId","default-board");u(this,"title","Untitled Board");u(this,"createdAt",Date.now());u(this,"viewport",{offsetX:0,offsetY:0,scale:Cn});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||As(n,t)&&e.push(n);return e}}function As(o,t){const e=Math.min(o.x,o.x+o.width),n=Math.max(o.x,o.x+o.width),s=Math.min(o.y,o.y+o.height),i=Math.max(o.y,o.y+o.height);return!(n<t.left||e>t.right||i<t.top||s>t.bottom)}const Ne=200;class Ns{constructor(){u(this,"undoStack",[]);u(this,"redoStack",[])}execute(t,e,n){const s={id:Tt(),type:t,timestamp:Date.now(),do:e,undo:n};s.do(),this.undoStack.push(s),this.undoStack.length>Ne&&this.undoStack.shift(),this.redoStack.length=0}push(t,e,n){const s={id:Tt(),type:t,timestamp:Date.now(),do:e,undo:n};this.undoStack.push(s),this.undoStack.length>Ne&&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 zs{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:Tt(),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,i=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(i))}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 i=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(i),()=>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 js=["image/png","image/jpeg","image/webp","image/gif"],Hs=5*1024*1024;class Fs{constructor(t,e,n,s,i){u(this,"state");u(this,"commands");u(this,"imageCache");u(this,"notify");u(this,"uploadImage");this.state=t,this.commands=e,this.imageCache=n,this.notify=s,this.uploadImage=i}async insertBlob(t,e,n,s){if(!js.includes(t.type)){this.notify("Unsupported image type");return}t.size>Hs&&this.notify("Large image added — consider optimizing it for git");let i=0,a=0,r=null;try{r=await createImageBitmap(t),i=r.width,a=r.height}catch{this.notify("Could not decode image");return}const c={originalWidth:i,originalHeight:a,alt:s};if(this.uploadImage)try{const v=await this.uploadImage(t,s);c.src=v,r&&this.imageCache.setBySrc(v,r)}catch(v){this.notify("Failed to save image to disk"),console.error("Image upload failed",v);return}else try{const v=await Z.createFromBlob(t,s);c.assetId=v.id,r&&this.imageCache.set(v.id,r)}catch(v){this.notify("Failed to store image (storage quota?)"),console.error("Asset store failed",v);return}const l=$(e,n,this.state.viewport),d=480/Math.min(1,this.state.viewport.scale);let h=i,p=a;const m=Math.max(h,p);if(m>d){const v=d/m;h*=v,p*=v}const g=D("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 i of Array.from(t))i.type.startsWith("image/")&&(await this.insertBlob(i,e+s,n+s,i.name),s+=24)}}class Ws{constructor(t,e,n,s){u(this,"state");u(this,"actions");u(this,"imageInsert");u(this,"internalClipboard",[]);u(this,"lastPointer",{x:window.innerWidth/2,y:window.innerHeight/2});this.state=t,this.actions=e,this.imageInsert=n,window.addEventListener("pointermove",i=>{this.lastPointer={x:i.clientX,y:i.clientY}},{signal:s}),window.addEventListener("paste",i=>{this.isEditingText(i.target)||this.handlePaste(i)},{signal:s}),window.addEventListener("copy",i=>{this.isEditingText(i.target)||this.copySelection()&&i.preventDefault()},{signal:s}),window.addEventListener("cut",i=>{this.isEditingText(i.target)||this.copySelection()&&(this.actions.deleteSelection(),i.preventDefault())},{signal:s})}isEditingText(t){const e=t;return!!e&&(e.tagName==="TEXTAREA"||e.tagName==="INPUT"||e.isContentEditable)}copySelection(){const t=this.state.getSelected();return t.length===0?!1:(this.internalClipboard=t.map(e=>({...e,style:{...e.style},data:{...e.data}})),!0)}async handlePaste(t){var s,i;const e=(s=t.clipboardData)==null?void 0:s.items;if(e){for(const a of e)if(a.type.startsWith("image/")){const r=a.getAsFile();if(r){t.preventDefault(),await this.imageInsert.insertBlob(r,this.lastPointer.x,this.lastPointer.y,"Pasted screenshot");return}}}if(this.internalClipboard.length>0){t.preventDefault(),this.pasteInternal();return}const n=(i=t.clipboardData)==null?void 0:i.getData("text/plain");n&&n.trim()&&(t.preventDefault(),this.pasteText(n.trim()))}pasteInternal(){const t=$(this.lastPointer.x,this.lastPointer.y,this.state.viewport);let e=1/0,n=1/0,s=-1/0,i=-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)),i=Math.max(i,Math.max(d.y,d.y+d.height));const a=(e+s)/2,r=(n+i)/2;let c=this.state.maxZIndex();const l=[];for(const d of this.internalClipboard){const h=pn(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=$(this.lastPointer.x,this.lastPointer.y,this.state.viewport),n=D("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)}});this.actions.insertElements([n],"paste-text"),this.state.clearSelection(),this.state.selectedIds.add(n.id),this.state.requestRender()}}function Os(o,t){const{state:e,commands:n,actions:s,toolController:i}=o;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(),jt(e.viewport,window.innerWidth/2,window.innerHeight/2,e.viewport.scale*1.2),e.requestRender();return;case"-":a.preventDefault(),jt(e.viewport,window.innerWidth/2,window.innerHeight/2,e.viewport.scale/1.2),e.requestRender();return;case"0":a.preventDefault(),o.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":i.cancel(),i.setTool("select");return}switch(a.key.toLowerCase()){case"v":i.setTool("select");return;case"h":i.setTool("hand");return;case"s":i.setTool("sticky");return;case"r":i.setTool("rectangle");return;case"o":i.setTool("ellipse");return;case"t":i.setTool("text");return;case"a":i.setTool("arrow");return;case"i":i.setTool("image");return}},{signal:t})}function Us(o){if(o.elements.length===0)return null;let t=1/0,e=1/0,n=-1/0,s=-1/0;for(const a of o.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 i=48;return{x:t-i,y:e-i,w:n-t+i*2,h:s-e+i*2}}function kn(o,t,e,n){let i=Math.round(e.w*n),a=Math.round(e.h*n);if(i>8192||a>8192){const h=8192/Math.max(i,a);n*=h,i=Math.round(e.w*n),a=Math.round(e.h*n)}const r=document.createElement("canvas");r.width=Math.max(1,i),r.height=Math.max(1,a);const c=r.getContext("2d"),l=o.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=o.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||Jt(h,{ctx:c,scale:n,lod:2,theme:o.theme,imageCache:t});return r}function En(o){return new Promise((t,e)=>{o.toBlob(n=>{n?t(n):e(new Error("PNG encode failed"))},"image/png")})}async function _s(o,t,e,n){const s=o.viewport,i={x:-s.offsetX/s.scale,y:-s.offsetY/s.scale,w:e/s.scale,h:n/s.scale},a=kn(o,t,i,s.scale);return En(a)}async function Xs(o,t){const e=Us(o);if(!e)return null;const n=kn(o,t,e,1);return En(n)}function ze(o,t){const e=URL.createObjectURL(o),n=document.createElement("a");n.href=e,n.download=t,n.click(),URL.revokeObjectURL(e)}class Ee{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",i=>this.onDown(i),{signal:s}),this.overlay.addEventListener("pointermove",i=>this.onMove(i),{signal:s}),this.overlay.addEventListener("pointerup",i=>this.onUp(i),{signal:s}),window.addEventListener("keydown",i=>{i.key==="Escape"&&(i.preventDefault(),this.finish(null))},{signal:s})}static begin(t,e){new Ee(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 qs{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 i=document.createElement("canvas"),a=i.getContext("2d",{alpha:s!==0});this.container.appendChild(i),this.layers.push(i),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 Ys{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,i=getComputedStyle(document.documentElement).getPropertyValue("--bg-canvas").trim(),a=getComputedStyle(document.documentElement).getPropertyValue("--grid-dot").trim();n.fillStyle=i||"#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 i=Ds(s,t.width,t.height),a=this.cullingEnabled?e.getVisibleElements(i):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)Jt(c,r)}renderOverlay(){const{surface:t,state:e}=this,n=t.beginLayer(2),s=e.viewport,i=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=i,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=i,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,i)}if(this.overlay.marquee){const r=this.overlay.marquee;n.fillStyle=i+"22",n.strokeStyle=i,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,Jt(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),i=Math.max(t.x,t.x+t.width),a=Math.max(t.y,t.y+t.height),r=we(n,s,e),c=we(i,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=Sn(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 Sn(o){const{x:t,y:e,w:n,h:s}=o;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 Ks{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 Vs(o,t,e,n,s,i){const a=s-e,r=i-n,c=a*a+r*r;if(c===0)return Math.hypot(o-e,t-n);let l=((o-e)*a+(t-n)*r)/c;return l=Math.max(0,Math.min(1,l)),Math.hypot(o-(e+l*a),t-(n+l*r))}class Gs{constructor(t){u(this,"state");u(this,"lastHitTestMs",0);this.state=t}hitTest(t,e,n){const s=performance.now(),i=this.state.getSorted(),a=6/n;let r=null;for(let c=i.length-1;c>=0;c--){const l=i[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 Vs(e,n,t.x,t.y,t.x+t.width,t.y+t.height)<=Math.max(s,(t.style.strokeWidth??2)/2+s);const i=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<i||e>a||n<r||n>c)return!1;if(t.type==="ellipse"){const l=(i+a)/2,d=(r+c)/2,h=(a-i)/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 i=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>=i&&p<=a&&m>=r&&g<=c&&l.push(d)}return l}}class Zs{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 Js=16,Qs=720,je=120,to=.0032;class eo{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 i of n)this.tools.set(i.name,i);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,i,a,r;if(this.active.name===t){(s=(n=this.active).onActivate)==null||s.call(n,this.ctx);return}(a=(i=this.active).onDeactivate)==null||a.call(i,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=He(e.deltaX,e.deltaMode,this.ctx.state.viewport.scale),i=He(e.deltaY,e.deltaMode,this.ctx.state.viewport.scale);if(e.ctrlKey||e.metaKey){const a=Math.exp(-no(i)*to);jt(n,e.clientX,e.clientY,n.scale*a)}else e.shiftKey&&Math.abs(s)<Math.abs(i)?n.offsetX-=i:(n.offsetX-=s,n.offsetY-=i);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 He(o,t,e){const s=o*(t===1?Js:t===2?Qs:1),i=e<.5?.8:e>2?1.08:1;return s*i}function no(o){return Math.max(-je,Math.min(je,o))}const Ut=7,so={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 oo{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:i}=e;this.startScreen={x:t.clientX,y:t.clientY},this.startWorld=$(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",i.overlay.marquee={x:t.clientX,y:t.clientY,w:0,h:0},n.requestRender()}onPointerMove(t,e){const{state:n,renderer:s,hitTester:i}=e;if(this.mode==="none"){const d=this.hitHandle(t,e);if(d){e.setCursor(so[d]);return}const h=$(t.clientX,t.clientY,n.viewport),p=i.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:i,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=$(r.x,r.y,n.viewport),l=$(r.x+r.w,r.y+r.h,n.viewport),d=i.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(),G(p.id))}n.emitChange()};for(const d of c){const h=n.elementsById.get(d.id);h&&(h.updatedAt=Date.now(),G(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,i=$(t.clientX,t.clientY,n.viewport),a=s.hitTest(i.x,i.y,n.viewport.scale);a&&(a.type==="sticky"||a.type==="task"||a.type==="text"||a.type==="rectangle"||a.type==="ellipse"||a.type==="frame")&&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:i}=e,a=$(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())};i.execute("toggle-task",()=>h(l),()=>h(c))}hitTaskCheckbox(t,e,n){const s=Math.min(22,Math.max(16,n.height*.38)),i=n.x+16,a=n.y+n.height/2-s/2,r=8;return t>=i-r&&t<=i+s+r&&e>=a-r&&e<=a+s+r}hitHandle(t,e){const{state:n,renderer:s}=e,i=n.getSelected();if(i.length===0||i.length===1&&this.isLinear(i[0]))return null;let a;if(i.length===1)a=s.elementScreenBBox(i[0]);else{let r=1/0,c=1/0,l=-1/0,d=-1/0;for(const h of i){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 Sn(a))if(Math.abs(t.clientX-r.x)<=Ut&&Math.abs(t.clientY-r.y)<=Ut)return r.id;return null}hitEndpoint(t,e,n){const s=n.state.viewport,i=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-i,t.clientY-a)<=Ut+2?!0:Math.hypot(t.clientX-r,t.clientY-c)<=Ut+2?!1:null}applyResize(t,e,n,s){const{state:i}=s,a=this.activeHandle;let r=1/0,c=1/0,l=-1/0,d=-1/0;for(const E of this.snapshots)r=Math.min(r,Math.min(E.x,E.x+E.width)),c=Math.min(c,Math.min(E.y,E.y+E.height)),l=Math.max(l,Math.max(E.x,E.x+E.width)),d=Math.max(d,Math.max(E.y,E.y+E.height));const h=l-r,p=d-c;if(h===0||p===0)return;let m=r,g=c,y=h,v=p;if(a.includes("e")&&(y=h+t),a.includes("w")&&(m=r+t,y=h-t),a.includes("s")&&(v=p+e),a.includes("n")&&(g=c+e,v=p-e),n&&(a==="nw"||a==="ne"||a==="se"||a==="sw")){const E=h/p;if(Math.abs(y/h)>Math.abs(v/p)){const C=y/E;a.includes("n")&&(g=g+(v-C)),v=C}else{const C=v*E;a.includes("w")&&(m=m+(y-C)),y=C}}const w=4;if(Math.abs(y)<w||Math.abs(v)<w)return;const x=y/h,S=v/p;for(const E of this.snapshots){const C=i.elementsById.get(E.id);!C||C.locked||(C.x=m+(E.x-r)*x,C.y=g+(E.y-c)*S,C.width=E.width*x,C.height=E.height*S,C.updatedAt=Date.now(),G(C.id))}}}class io{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 Qt=["#fef08a","#fdba74","#fca5a5","#f9a8d4","#d8b4fe","#a5b4fc","#93c5fd","#86efac"];let Tn=Qt[0];function ao(){return Tn}function ro(o){Tn=o}class co{constructor(){u(this,"name","sticky");u(this,"cursor","crosshair")}onPointerDown(t,e){const{state:n,commands:s}=e,i=$(t.clientX,t.clientY,n.viewport),a=D("sticky",{x:i.x-110,y:i.y-80,width:220,height:160,zIndex:n.maxZIndex()+1,style:{fill:ao(),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 lo{constructor(){u(this,"name","task");u(this,"cursor","crosshair")}onPointerDown(t,e){const{state:n,commands:s}=e,i=$(t.clientX,t.clientY,n.viewport),a=n.theme==="dark",r=D("task",{x:i.x-140,y:i.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 vt{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=$(t.clientX,t.clientY,n.viewport);const s=n.theme==="dark",i=!["rectangle","ellipse","pill"].includes(this.shapeKind);this.preview=D(this.shapeType,{x:this.startWorld.x,y:this.startWorld.y,width:0,height:0,zIndex:n.maxZIndex()+1,style:{fill:i?"#0078d4":s?"#2a2d33":"#ffffff",stroke:i?"#495057":s?"#9ca3af":"#374151",strokeWidth:i?0:1.5,borderRadius:this.shapeKind==="pill"?999:this.shapeKind==="rectangle"?8:0,fontSize:14,textColor:i?"#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=$(t.clientX,t.clientY,e.state.viewport);let s=n.x-this.startWorld.x,i=n.y-this.startWorld.y;if(t.shiftKey){const a=Math.max(Math.abs(s),Math.abs(i));s=Math.sign(s||1)*a,i=Math.sign(i||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+i),this.preview.width=Math.abs(s),this.preview.height=Math.abs(i),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 i=this.preview;this.preview=null,(i.width<4||i.height<4)&&(i.width=this.shapeKind==="chevron"?210:160,i.height=this.shapeKind==="chevron"?76:100,i.x-=i.width/2,i.y-=i.height/2),s.execute("create-shape",()=>{n.elementsById.has(i.id)||n.addElement(i),n.emitChange()},()=>{n.removeElement(i.id),n.emitChange()}),n.clearSelection(),n.selectedIds.add(i.id),e.setTool("select")}onCancel(t){this.drawing=!1,this.preview=null,t.renderer.overlay.preview=null,t.state.requestRender()}}class ho{constructor(){u(this,"name","text");u(this,"cursor","text")}onPointerDown(t,e){const{state:n,commands:s}=e,i=$(t.clientX,t.clientY,n.viewport),a=D("text",{x:i.x,y:i.y-12,width:240,height:28,zIndex:n.maxZIndex()+1,style:{fontSize:16,textColor:n.theme==="dark"?"#f2f2f2":"#1f2933"},data:{text:""}});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 po{constructor(){u(this,"name","arrow");u(this,"cursor","crosshair");u(this,"drawing",!1);u(this,"startWorld",{x:0,y:0});u(this,"preview",null)}onPointerDown(t,e){const{state:n}=e;this.drawing=!0,this.startWorld=$(t.clientX,t.clientY,n.viewport),this.preview=D("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}}),e.renderer.overlay.preview=this.preview}onPointerMove(t,e){if(!this.drawing||!this.preview)return;const n=$(t.clientX,t.clientY,e.state.viewport);let s=n.x-this.startWorld.x,i=n.y-this.startWorld.y;if(t.shiftKey){const a=Math.atan2(i,s),r=Math.round(a/(Math.PI/4))*(Math.PI/4),c=Math.hypot(s,i);s=Math.cos(r)*c,i=Math.sin(r)*c}this.preview.width=s,this.preview.height=i,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 i=this.preview;if(this.preview=null,Math.hypot(i.width,i.height)<8){n.requestRender();return}s.execute("create-arrow",()=>{n.elementsById.has(i.id)||n.addElement(i),n.emitChange()},()=>{n.removeElement(i.id),n.emitChange()}),n.clearSelection(),n.selectedIds.add(i.id),e.setTool("select")}onCancel(t){this.drawing=!1,this.preview=null,t.renderer.overlay.preview=null,t.state.requestRender()}}class uo{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 _t="psc:";class mo{constructor(){u(this,"lastSaveMs",0)}async saveBoard(t){const e=performance.now();try{await _.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 _.get("boards",t)}catch(e){console.error("Board load failed",e);return}}async deleteBoard(t){await _.delete("boards",t)}async clearAllBoards(){await _.clearStore("boards")}setPref(t,e){try{localStorage.setItem(_t+t,e)}catch{}}getPref(t){try{return localStorage.getItem(_t+t)}catch{return null}}removePref(t){try{localStorage.removeItem(_t+t)}catch{}}clearPrefs(){try{const t=[];for(let e=0;e<localStorage.length;e++){const n=localStorage.key(e);n&&n.startsWith(_t)&&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 V=new mo;function fo(o){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(o)})}function go(o,t){const e=atob(o),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 Fe(o,t){const e=new Blob([JSON.stringify(t)],{type:"application/json"}),n=URL.createObjectURL(e),s=document.createElement("a");s.href=n,s.download=o,s.click(),URL.revokeObjectURL(n)}function We(o){return o.replace(/[^a-z0-9-_ ]/gi,"").trim().slice(0,60)||"board"}class yo{exportBoardJson(t){Fe(`${We(t.title)}.json`,t)}async exportFullBundle(t){const e=[];for(const s of t.assetIds){const i=await Z.get(s);i&&e.push({id:i.id,mimeType:i.mimeType,width:i.width,height:i.height,name:i.name,base64:await fo(i.blob)})}const n={bundleVersion:1,board:t,assets:e};Fe(`${We(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=Zt(n.board);if(!s)return null;const i=new Map;for(const a of n.assets){const r=await this.importAsset(a);r&&i.set(r.originalId,r.asset.id)}for(const a of s.elements){const r=a.data.assetId;typeof r=="string"&&i.has(r)&&(a.data.assetId=i.get(r))}return s.assetIds=[...i.values()],s}return Zt(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=go(e.base64,e.mimeType),s=Date.now(),i={id:Tt(),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 Z.put(i),{originalId:e.id,asset:i}}catch{return null}}}const me=new yo,X={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>',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>'},vo=[{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:"arrow",icon:"arrow",label:"Arrow",key:"A"},{tool:"image",icon:"image",label:"Image",key:"I"}],Bn=[{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:""}],bo=new Set(Bn.map(o=>o.tool));class wo{constructor(t,e){u(this,"element");u(this,"toolButtons",new Map);u(this,"shapeButtons",new Map);u(this,"shapeBtn");u(this,"shapeGroup");u(this,"themeBtn");this.element=document.createElement("div"),this.element.className="toolbar";for(const a of vo){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))}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 i=this.makeButton("flask","Stress test","");if(i.addEventListener("click",e.toggleStressPanel),this.element.appendChild(i),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=X[t]??"";const i=document.createElement("span");if(i.className="tooltip",i.textContent=e,n){const a=document.createElement("kbd");a.textContent=n,i.appendChild(a)}return s.appendChild(i),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 Bn){const s=this.makeButton(n.icon,n.label,n.key);s.classList.add("shape-option-btn"),s.addEventListener("click",i=>{i.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}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",bo.has(t))}setTheme(t){const e=this.themeBtn.querySelector(".tooltip");this.themeBtn.innerHTML=X[t==="dark"?"sun":"moon"],e&&this.themeBtn.appendChild(e)}}class Co{constructor(t,e,n){u(this,"zoomLabel");u(this,"titleInput");u(this,"titleMeasure");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=`${X.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 i=document.createElement("div");i.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),i.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)}i.appendChild(l),t.appendChild(i)}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=X[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()}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 xo{constructor(t,e){u(this,"state");u(this,"commands");u(this,"textarea",null);u(this,"editingId",null);this.state=t,this.commands=e}get isEditing(){return this.editingId!==null}open(t){this.close(),this.editingId=t.id;const e=document.createElement("textarea");e.className="text-editor",e.value=t.data.text??"",this.position(e,t),document.body.appendChild(e),this.textarea=e,e.focus(),e.select();const n=t.data.text??"";t.data.text="",t.updatedAt=Date.now(),G(t.id),this.state.requestRender();const s=()=>{const i=e.value;if(t.data.text=i,t.updatedAt=Date.now(),G(t.id),i!==n){const a=t.id,r=this.state;this.commands.push("edit-text",()=>{const c=r.elementsById.get(a);c&&(c.data.text=i,c.updatedAt=Date.now(),G(a),r.emitChange())},()=>{const c=r.elementsById.get(a);c&&(c.data.text=n,c.updatedAt=Date.now(),G(a),r.emitChange())})}this.state.emitChange(),this.close()};e.addEventListener("blur",s),e.addEventListener("keydown",i=>{i.stopPropagation(),i.key==="Escape"?(t.data.text=n,G(t.id),this.close(),this.state.requestRender()):i.key==="Enter"&&(i.metaKey||i.ctrlKey)&&e.blur()})}position(t,e){const n=this.state.viewport,s=we(e.x,e.y,n),i=e.data.shapeKind==="chevron",a=wn(e)*n.scale,r=i?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=i?Math.max(28,a*1.28):Math.max(24,e.height*n.scale-(e.type==="sticky"?28:e.type==="task"||e.type==="text"?0:r*2)*n.scale),d=i?Math.max(0,(e.height*n.scale-l)/2):(e.type==="sticky"?14:e.type==="task"||e.type==="text"?0:r)*n.scale,h=(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+d+"px",t.style.width=Math.max(40,e.width*n.scale-c-h)+"px",t.style.height=l+"px",t.style.fontSize=a+"px",t.style.lineHeight=i?"1.18":"1.35",t.style.fontFamily=e.style.fontFamily??'-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',t.style.fontWeight=i?"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.textarea){const t=this.textarea;this.textarea=null,this.editingId=null,t.remove()}}}function F(o,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",o);const s=document.createElement("button");s.className="modal-close",s.textContent="✕",s.setAttribute("aria-label","Close");const i=document.createElement("h2");i.textContent=o;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(i),n.appendChild(a),e.appendChild(n),document.body.appendChild(e),t(a,r),s.focus()}let J=null,fe=null;function b(o){J||(J=document.createElement("div"),J.className="toast",document.body.appendChild(J)),J.textContent=o,J.classList.add("visible"),fe!==null&&clearTimeout(fe),fe=window.setTimeout(()=>{J==null||J.classList.remove("visible")},2600)}function ut(o){return o==="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 Mt=0;function mt(o){Mt=o}function j(o,t,e,n,s,i=""){return D("rectangle",{x:o,y:t,width:e,height:n,zIndex:++Mt,style:{borderRadius:10,...s},data:{text:i}})}function B(o,t,e,n,s,i,a="600"){return D("text",{x:o,y:t,width:e,height:s*1.6,zIndex:++Mt,style:{fontSize:s,textColor:i,fontWeight:a},data:{text:n}})}function A(o,t,e,n,s=180,i=120){return D("sticky",{x:o,y:t,width:s,height:i,zIndex:++Mt,style:{fill:n,borderRadius:12,shadow:!0,fontSize:13,textColor:"#1f2933"},data:{text:e}})}function q(o,t,e,n,s){return D("arrow",{x:o,y:t,width:e-o,height:n-t,zIndex:++Mt,style:{stroke:s,strokeWidth:2,arrowEnd:!0}})}function ko(o,t,e,n,s,i=""){return D("ellipse",{x:o,y:t,width:e,height:n,zIndex:++Mt,style:s,data:{text:i}})}function Eo(o,t,e,n){const s=ut(e);mt(n);const i=[];return i.push(j(o,t,760,560,{fill:s.card,stroke:s.cardStroke,strokeWidth:1.5,borderRadius:16,shadow:!0})),i.push(B(o+28,t+22,400,"User Persona",22,s.heading,"700")),i.push(B(o+28,t+60,300,"Name",13,s.accent)),i.push(A(o+28,t+86,"Jordan Lee",s.sticky,200,64)),i.push(B(o+260,t+60,300,"Role",13,s.accent)),i.push(A(o+260,t+86,"Senior Product Manager","#a5b4fc",220,64)),i.push(B(o+510,t+60,300,"Buying trigger",13,s.accent)),i.push(A(o+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=o+28+h*242,g=t+188+p*178;i.push(B(m,g,230,r,13,s.accent)),i.push(A(m,g+26,c,l,220,124))}),i}const ge=["Awareness","Discovery","Evaluation","Onboarding","Activation","Retention"],So=["#93c5fd","#a5b4fc","#d8b4fe","#f9a8d4","#fdba74","#86efac"];function To(o,t,e,n){const s=ut(e);mt(n);const i=[],a=230,r=ge.length*a+56;return i.push(j(o,t,r,640,{fill:s.card,stroke:s.cardStroke,strokeWidth:1.5,borderRadius:16,shadow:!0})),i.push(B(o+28,t+22,500,"User Journey Map",22,s.heading,"700")),ge.forEach((c,l)=>{const d=o+28+l*a,h=t+70;i.push(j(d,h,a-16,48,{fill:So[l],borderRadius:10},c)),l<ge.length-1&&i.push(q(d+a-14,h+24,d+a-2,h+24,s.heading)),i.push(j(d,h+64,a-16,150,{fill:s.zone,borderRadius:10},"")),i.push(B(d+10,h+70,a-36,"Actions",12,s.accent)),i.push(A(d+14,h+96,"","#fef08a",a-44,100)),i.push(j(d,h+226,a-16,150,{fill:s.zone,borderRadius:10},"")),i.push(B(d+10,h+232,a-36,"Pain points",12,s.accent)),i.push(A(d+14,h+258,"","#fca5a5",a-44,100)),i.push(j(d,h+388,a-16,150,{fill:s.zone,borderRadius:10},"")),i.push(B(d+10,h+394,a-36,"Opportunities",12,s.accent)),i.push(A(d+14,h+420,"","#86efac",a-44,100))}),i}function Bo(o,t,e,n){const s=ut(e);mt(n);const i=[],a=680;i.push(j(o,t,a+120,a+130,{fill:s.card,stroke:s.cardStroke,strokeWidth:1.5,borderRadius:16,shadow:!0})),i.push(B(o+28,t+22,500,"Feature Prioritization Matrix",22,s.heading,"700"));const r=o+80,c=t+80,l=a/2;return i.push(j(r,c,l,l,{fill:"#86efac55",borderRadius:8})),i.push(j(r+l,c,l,l,{fill:"#93c5fd55",borderRadius:8})),i.push(j(r,c+l,l,l,{fill:"#fef08a55",borderRadius:8})),i.push(j(r+l,c+l,l,l,{fill:"#fca5a555",borderRadius:8})),i.push(B(r+16,c+12,300,"Quick wins",16,s.heading,"700")),i.push(B(r+l+16,c+12,300,"Strategic bets",16,s.heading,"700")),i.push(B(r+16,c+l+12,300,"Fill-ins",16,s.heading,"700")),i.push(B(r+l+16,c+l+12,300,"Avoid",16,s.heading,"700")),i.push(q(r-24,c+a,r-24,c,s.heading)),i.push(B(r-64,c+l-12,60,"Impact",14,s.text,"600")),i.push(q(r,c+a+24,r+a,c+a+24,s.heading)),i.push(B(r+l-30,c+a+34,80,"Effort",14,s.text,"600")),i.push(A(r+40,c+60,"Feature idea","#fef08a",150,90)),i.push(A(r+l+40,c+60,"Big bet","#a5b4fc",150,90)),i}const Oe=[["Problem","#fca5a5"],["User","#93c5fd"],["Context","#d8b4fe"],["Current workflow","#fdba74"],["Proposed solution","#86efac"],["Edge cases","#f9a8d4"],["Acceptance criteria","#a5b4fc"],["Dependencies","#fef08a"],["Risks","#fca5a5"]];function Mo(o,t,e,n){const s=ut(e);mt(n);const i=[],a=3,r=280,c=210,l=a*r+56,h=Math.ceil(Oe.length/a)*c+100;return i.push(j(o,t,l,h,{fill:s.card,stroke:s.cardStroke,strokeWidth:1.5,borderRadius:16,shadow:!0})),i.push(B(o+28,t+22,500,"Product Requirements Flow",22,s.heading,"700")),Oe.forEach(([p,m],g)=>{const y=g%a,v=Math.floor(g/a),w=o+28+y*r,x=t+72+v*c;i.push(B(w,x,r-30,`${g+1}. ${p}`,14,s.accent)),i.push(A(w,x+30,"",m,r-28,c-60))}),i}function Io(o,t,e,n){const s=ut(e);mt(n);const i=[];i.push(B(o,t-40,400,"System Diagram",22,s.heading,"700"));const a=(r,c,l,d,h,p)=>j(r,c,l,d,{fill:h,stroke:s.cardStroke,strokeWidth:1.5,borderRadius:12,shadow:!0,fontSize:15,fontWeight:"600",textColor:"#1f2933"},p);return i.push(ko(o,t+130,110,110,{fill:"#f9a8d4",stroke:s.cardStroke,strokeWidth:1.5,fontSize:14,fontWeight:"600",textColor:"#1f2933"},"User")),i.push(a(o+200,t+140,180,90,"#93c5fd","Frontend")),i.push(a(o+470,t+140,180,90,"#a5b4fc","API / Server")),i.push(a(o+740,t+60,180,90,"#86efac","Database")),i.push(a(o+740,t+200,180,90,"#fdba74","External Service")),i.push(a(o+470,t+330,180,90,"#fef08a","Notification Service")),i.push(q(o+115,t+185,o+195,t+185,s.heading)),i.push(q(o+385,t+185,o+465,t+185,s.heading)),i.push(q(o+655,t+165,o+735,t+110,s.heading)),i.push(q(o+655,t+205,o+735,t+240,s.heading)),i.push(q(o+560,t+235,o+560,t+325,s.heading)),i.push(q(o+470,t+375,o+80,t+245,s.heading)),i}function Lo(o,t,e,n){const s=ut(e);mt(n);const i=[];i.push(B(o+280,t-40,500,"Opportunity Solution Tree",22,s.heading,"700")),i.push(j(o+300,t,280,80,{fill:"#a5b4fc",borderRadius:12,shadow:!0,fontSize:15,fontWeight:"600",textColor:"#1f2933"},"Desired outcome"));const a=t+160,r=[o,o+300,o+600];for(const d of r)i.push(A(d,a,"Opportunity","#93c5fd",220,100)),i.push(q(o+440,t+85,d+110,a-5,s.heading));const c=a+180;for(const d of r)i.push(A(d,c,"Solution","#86efac",220,100)),i.push(q(d+110,a+105,d+110,c-5,s.heading));const l=c+180;for(const d of r)i.push(A(d,l,"Experiment","#fdba74",220,90)),i.push(q(d+110,c+105,d+110,l-5,s.heading));return i.push(B(o,l+120,400,"Signals / metrics",14,s.accent)),i.push(A(o,l+150,"What signal tells us this is working?","#fef08a",380,100)),i}function $o(o,t,e,n){const s=ut(e);mt(n);const i=[];i.push(j(o,t,1060,620,{fill:s.card,stroke:s.cardStroke,strokeWidth:1.5,borderRadius:16,shadow:!0})),i.push(B(o+28,t+22,500,"Screenshot Review",22,s.heading,"700")),i.push(B(o+28,t+64,300,"Screenshot",13,s.accent));const a=D("frame",{x:o+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 i.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=o+540+h*250,g=t+64+p*200;i.push(B(m,g,230,c,13,s.accent)),i.push(A(m,g+28,"",l,230,140))}),i.push(B(o+28,t+470,300,"Follow-up tasks",13,s.accent)),i.push(A(o+28,t+498,"","#fef08a",480,96)),i.push(B(o+540,t+470,300,"Decision",13,s.accent)),i.push(A(o+540,t+498,"","#fdba74",480,96)),i}const Ro=[{id:"persona",name:"User Persona Card",description:"Name, role, goals, pain points, JTBD, success criteria.",build:Eo},{id:"journey",name:"User Journey Map",description:"Awareness → Retention stages with sticky note areas.",build:To},{id:"matrix",name:"Prioritization Matrix",description:"Impact vs effort: quick wins, strategic bets, fill-ins, avoid.",build:Bo},{id:"requirements",name:"Requirements Flow",description:"Problem, user, context, solution, edge cases, risks.",build:Mo},{id:"system",name:"System Diagram Starter",description:"User, frontend, API, database, external services + arrows.",build:Io},{id:"ost",name:"Opportunity Solution Tree",description:"Outcome, opportunities, solutions, experiments, signals.",build:Lo},{id:"screenshot-review",name:"Screenshot Review Board",description:"Screenshot zone with observations, problems, UX notes.",build:$o}];function Do(o,t){F("Insert a template",(e,n)=>{const s=document.createElement("div");s.className="template-grid";for(const i of Ro){const a=document.createElement("button");a.className="template-card";const r=document.createElement("span");r.className="t-name",r.textContent=i.name;const c=document.createElement("span");c.className="t-desc",c.textContent=i.description,a.appendChild(r),a.appendChild(c),a.addEventListener("click",()=>{Po(o,t,i.build),n()}),s.appendChild(a)}e.appendChild(s)})}function Po(o,t,e){const n=$(window.innerWidth/2,window.innerHeight/2,o.viewport),s=e(n.x-380,n.y-280,o.theme,o.maxZIndex());t.execute("insert-template",()=>{for(const i of s)o.elementsById.has(i.id)||o.addElement(i);o.emitChange()},()=>{for(const i of s)o.removeElement(i.id);o.emitChange()}),o.clearSelection();for(const i of s)o.selectedIds.add(i.id);o.requestRender()}function Xt(o){return o<1024?o+" B":o<1024*1024?(o/1024).toFixed(1)+" KB":(o/(1024*1024)).toFixed(1)+" MB"}function Ao(o,t,e){F("Local storage",async(n,s)=>{const i=document.createElement("div");n.appendChild(i);const a=(y,v)=>{const w=document.createElement("div");w.className="storage-row";const x=document.createElement("span");x.textContent=y;const S=document.createElement("span");S.textContent=v,w.appendChild(x),w.appendChild(S),i.appendChild(w)};a("Elements on board",String(o.elements.length));const r=JSON.stringify(o.elements);a("Board data size",Xt(r.length));const c=await Z.getAllKeys();a("Image assets",String(c.length));const l=await Z.totalSizeBytes();a("Image asset size",Xt(l));const d=await V.estimateStorage();d.quota>0&&a("Browser storage",`${Xt(d.usage)} of ${Xt(d.quota)}`);const h=document.createElement("div");h.className="modal-actions";const p=document.createElement("button");p.className="btn",p.textContent="Clear unused assets",p.addEventListener("click",async()=>{const y=new Set;for(const w of o.elements){const x=w.data.assetId;typeof x=="string"&&y.add(x)}const v=await Z.clearUnused(y);b(`Removed ${v} unused asset${v===1?"":"s"}`),s()}),h.appendChild(p);const m=document.createElement("button");m.className="btn danger",m.textContent="Clear board",m.addEventListener("click",()=>{confirm("Remove all elements from this board?")&&(t(),s())}),h.appendChild(m);const g=document.createElement("button");g.className="btn danger",g.textContent="Reset all local data",g.addEventListener("click",()=>{confirm("Delete ALL local boards, assets and preferences? This cannot be undone.")&&(e(),s())}),h.appendChild(g),n.appendChild(h)})}class No{constructor(t,e,n,s,i,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=i,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 Z.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: ${V.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(`
|
|
10
|
-
`)}}const qt=["#93c5fd","#a5b4fc","#86efac","#fdba74","#fca5a5","#d8b4fe","#f9a8d4","#fef08a","#ffffff","#e5e7eb"],Ue=["Onboarding","Checkout","Retention","Activation","Churn risk","API gateway","User insight","Edge case","Quick win","Tech debt","Experiment","Metric","Persona","Journey","Feature flag"];function Y(o,t){return o+Math.random()*(t-o)}function Q(o){return o[Math.floor(Math.random()*o.length)]}const tt="stress";class zo{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 i=this.state.maxZIndex();const a=[];for(let r=0;r<t;r++){const c=Q(s),l=Y(-n/2,n/2),d=Y(-n/2,n/2);let h;c==="sticky"?h=D("sticky",{x:l,y:d,width:220,height:160,zIndex:++i,style:{fill:Q(Qt),borderRadius:12,shadow:!0,fontSize:14,textColor:"#1f2933"},data:{text:Q(Ue),[tt]:!0}}):c==="rectangle"||c==="ellipse"?h=D(c,{x:l,y:d,width:Y(80,300),height:Y(60,200),zIndex:++i,style:{fill:Q(qt),stroke:"#37415188",strokeWidth:1.5,borderRadius:c==="rectangle"?8:0},data:{[tt]:!0}}):c==="text"?h=D("text",{x:l,y:d,width:200,height:28,zIndex:++i,style:{fontSize:16,textColor:this.state.theme==="dark"?"#f2f2f2":"#1f2933"},data:{text:Q(Ue),[tt]:!0}}):h=D("arrow",{x:l,y:d,width:Y(-250,250),height:Y(-250,250),zIndex:++i,style:{stroke:Q(qt),strokeWidth:2,arrowEnd:!0},data:{[tt]:!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 i=D("image",{x:Y(-e/2,e/2),y:Y(-e/2,e/2),width:320,height:220,zIndex:++n,data:{assetId:"stress-placeholder-"+s,[tt]:!0}});this.state.addElement(i)}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[tt]&&(e.x=Y(-t/2,t/2),e.y=Y(-t/2,t/2));this.state.emitChange()}randomizeColors(){for(const t of this.state.elements)t.data[tt]&&(t.type==="sticky"?t.style.fill=Q(Qt):t.type==="rectangle"||t.type==="ellipse"?t.style.fill=Q(qt):t.type==="arrow"&&(t.style.stroke=Q(qt)));this.state.emitChange()}randomizeZIndex(){for(const t of this.state.elements)t.data[tt]&&(t.zIndex=Math.floor(Y(0,this.state.elements.length)));this.state.invalidateSort(),this.state.emitChange()}clearGenerated(){const t=this.state.elements.filter(e=>e.data[tt]);for(const e of t)this.state.removeElement(e.id);this.state.emitChange()}}class jo{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)},i=a=>()=>{e.generate(a),b(`Generated ${a.toLocaleString()} elements in ${e.lastGenerateMs.toFixed(0)}ms`)};s("+100",i(100)),s("+1,000",i(1e3)),s("+5,000",i(5e3)),s("+10,000",i(1e4)),s("+20,000",i(2e4)),s("+100 images",()=>{e.generateImagePlaceholders(100),b("Generated 100 image placeholders")}),s("Mixed PM board",()=>{e.generateMixedPmBoard(),b("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,b(`Viewport culling ${n.cullingEnabled?"enabled":"disabled"}`),n.state.requestRender()}),s("Toggle LOD",()=>{n.lodEnabled=!n.lodEnabled,b(`Simplified rendering ${n.lodEnabled?"enabled":"disabled"}`),n.state.requestRender()}),s("Clear generated",()=>{e.clearGenerated(),b("Cleared generated elements")},!0),t.appendChild(this.element)}toggle(){this.visible=!this.visible,this.element.classList.toggle("visible",this.visible)}}const Ho=300,Fo=1e3,_e=[{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"}],Wo=new Set(["text"]),Oo=new Set(["rectangle","ellipse","frame"]),Uo=["#0078d4","#4dabf7","#6e6e6e","#ffffff","#0f0f0f","#fef08a","#93c5fd","#fca5a5"],_o=[{label:"Black text",value:"#1f2933"},{label:"White text",value:"#ffffff"}];class Xo{constructor(t,e={}){u(this,"state",new Ps);u(this,"commands",new Ns);u(this,"surface");u(this,"renderer");u(this,"scheduler");u(this,"hitTester");u(this,"imageCache");u(this,"monitor",new Zs);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,"options");u(this,"listenerAbort",new AbortController);u(this,"root");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 qs(t,3,n),this.imageCache=new bn(Z,()=>this.state.requestRender(),e.resolveAssetUrl),this.renderer=new Ys(this.surface,this.state,this.imageCache),this.scheduler=new Ks(()=>this.renderer.render()),this.hitTester=new Gs(this.state),this.actions=new zs(this.state,this.commands),this.textEditor=new xo(this.state,this.commands),this.imageInsert=new Fs(this.state,this.commands,this.imageCache,b,e.uploadImage),this.state.onRenderNeeded(()=>{var s;this.scheduler.requestRender(),(s=this.topControls)==null||s.setZoom(this.state.viewport.scale),this.scheduleViewportSave(),this.updateContextualToolPanels()}),this.state.onChange(()=>this.scheduleBoardSave()),this.surface.onResize(()=>this.state.requestRender()),this.setupTools(t,n),this.setupUi(t),this.setupSelectionOverlays(t,n),this.setupDragDrop(t),new Ws(this.state,this.actions,this.imageInsert,n),Os({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 eo(this.surface.container,n,[new oo,new io,new co,new lo,new vt("rectangle"),new vt("ellipse"),new vt("diamond"),new vt("triangle"),new vt("pill"),new vt("chevron"),new ho,new po,new uo((s,i,a)=>void this.imageInsert.insertFiles(s,i,a))],e),this.toolController.onToolChange(s=>{var i;(i=this.toolbar)==null||i.setActiveTool(s)})}setupUi(t){this.toolbar=new wo(t,{setTool:n=>this.toolController.setTool(n),openTemplates:()=>Do(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 Co(t,{onExit:this.options.onExit?()=>this.exitToDocs():void 0,resetView:()=>this.resetView(),zoomIn:()=>{jt(this.state.viewport,window.innerWidth/2,window.innerHeight/2,this.state.viewport.scale*1.2),this.state.requestRender()},zoomOut:()=>{jt(this.state.viewport,window.innerWidth/2,window.innerHeight/2,this.state.viewport.scale/1.2),this.state.requestRender()},openStorageManager:()=>Ao(this.state,()=>this.clearBoard(),()=>void this.resetAllLocalData()),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 No(t,this.state,this.renderer,this.hitTester,this.monitor,this.imageCache);const e=new zo(this.state);this.stressPanel=new jo(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 Qt){const i=document.createElement("button");i.type="button",i.className="sticky-color-btn",i.title="Set sticky color",i.setAttribute("aria-label",`Set sticky color ${s}`),i.style.setProperty("--swatch",s),i.addEventListener("click",()=>this.setSelectedStickyColor(s)),this.stickyColorPopover.appendChild(i)}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 _e){const i=document.createElement("button");i.type="button",i.className="text-size-btn",i.textContent=s.shortLabel,i.setAttribute("aria-label",`Set text size ${s.label}`),i.addEventListener("click",()=>this.setSelectedTextSize(s)),this.textSizePopover.appendChild(i)}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 Uo){const i=document.createElement("button");i.type="button",i.className="shape-fill-btn",i.title="Set shape color",i.setAttribute("aria-label",`Set shape color ${s}`),i.style.setProperty("--swatch",s),i.addEventListener("click",()=>this.setSelectedShapeFill(s)),this.shapeStylePopover.appendChild(i)}const n=document.createElement("div");n.className="context-tool-divider",this.shapeStylePopover.appendChild(n);for(const s of _o){const i=document.createElement("button");i.type="button",i.className="shape-text-color-btn",i.textContent=s.value==="#ffffff"?"W":"B",i.setAttribute("aria-label",s.label),i.addEventListener("click",()=>this.setSelectedShapeTextColor(s.value)),this.shapeStylePopover.appendChild(i)}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 i;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=(i=e[0])==null?void 0:i.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){ro(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})),i=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(i),()=>a(s))}updateTextSizePopover(){var i,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=((i=e[0])==null?void 0:i.style.fontSize)??(((a=e[0])==null?void 0:a.type)==="sticky"?15:16);for(const r of t.children){const c=r,l=_e.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=>Wo.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})),i=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,G(l.id))}n.emitChange()};this.commands.execute("text-size",()=>a(i),()=>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,i=((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"?i==="#ffffff":i!=="#ffffff";l.classList.toggle("active",h),l.setAttribute("aria-pressed",String(h))}}}selectedShapeTargets(){return this.state.getSelected().filter(t=>Oo.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})),i=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(i),()=>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})),i=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,G(l.id))}n.emitChange()};this.commands.execute("shape-text-color",()=>a(i),()=>a(s))}openLayerMenu(t){if(t.preventDefault(),t.stopPropagation(),this.textEditor.isEditing)return;const e=$(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 i=document.createElement("div");i.className="layer-menu",i.setAttribute("role","menu");const a=document.createElement("div");a.className="layer-menu-label",a.textContent=s.length===1?"Layer":`${s.length} layers`,i.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()}),i.appendChild(h)},c=()=>{const l=document.createElement("div");l.className="layer-menu-divider",i.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(i),this.layerMenu=i,this.positionLayerMenu(i,t.clientX,t.clientY)}positionLayerMenu(t,e,n){t.style.left="0px",t.style.top="0px",requestAnimationFrame(()=>{const i=t.getBoundingClientRect(),a=Math.min(Math.max(12,e),window.innerWidth-i.width-12),r=Math.min(Math.max(12,n),window.innerHeight-i.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().finally(()=>{var t,e;return(e=(t=this.options).onExit)==null?void 0:e.call(t)})}startEmbedSelection(){Ee.begin(this.root,t=>{t&&this.copyEmbedSnippet(t)})}async copyEmbedSnippet(t){const e=this.state.viewport,n=$(t.x,t.y,e),s=$(t.x+t.w,t.y+t.h,e),i=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
|
|
11
|
-
src: /${this.state.boardId}
|
|
12
|
-
region: ${i}, ${a}, ${r}, ${c}
|
|
13
|
-
height: 360
|
|
14
|
-
title: ${this.state.title}
|
|
15
|
-
\`\`\``;try{await navigator.clipboard.writeText(l),b("Embed snippet copied — paste it into any doc")}catch{b("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(){const t=V.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.applyDocument(n),this.fitInitialRegion())}catch(n){console.error("Board file load failed",n),b("Could not load board file")}this.state.requestRender();return}const e=V.getPref("lastBoardId")??"default-board";try{const n=await V.loadBoard(e);n&&this.applyDocument(n)}catch(n){console.error("Board recovery failed",n),V.getPref("recovery")&&b("Could not load saved board — starting fresh")}this.state.requestRender()}applyDocument(t){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,i]=t;if(s<=0||i<=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/i),p=Math.max(.7,Math.min(1.1,this.state.viewport.scale*1.15)),m=xn(Math.min(h*.58,p));this.state.viewport={scale:m,offsetX:a+(l-s*m)/2-e*m,offsetY:r+(d-i*m)/2-n*m},this.topControls.setZoom(m)}scheduleBoardSave(){this.boardSaveTimer!==null&&clearTimeout(this.boardSaveTimer),this.boardSaveTimer=window.setTimeout(()=>{this.boardSaveTimer=null,this.saveBoard()},Ho)}scheduleViewportSave(){this.viewportSaveTimer===null&&(this.viewportSaveTimer=window.setTimeout(()=>{this.viewportSaveTimer=null,this.saveBoard()},Fo))}async saveBoard(){try{this.options.saveBoard?await this.options.saveBoard(Wt(this.state)):await V.saveBoard(Wt(this.state))}catch{b(this.options.saveBoard?"Save to file failed":"Save failed — browser storage may be full")}}applyTheme(t){var e;this.state.theme=t,document.documentElement.setAttribute("data-theme",t),V.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=Cn,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()}),b("Board cleared")}async resetAllLocalData(){await V.clearAllBoards(),await Z.clearAll(),V.clearPrefs(),this.imageCache.clear(),this.commands.clear(),this.state.elements=[],this.state.elementsById.clear(),this.state.clearSelection(),this.state.invalidateSort(),this.resetView(),b("All local data cleared")}confirmDeleteBoard(){F("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 i=document.createElement("button");i.className="btn",i.textContent="Cancel",i.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(),b("Board deleted"),(l=this.options.onDeleted??this.options.onExit)==null||l()}catch{a.disabled=!1,b("Could not delete board")}}),s.append(i,a),t.append(n,s)})}openExportMenu(){F("Export & import",(t,e)=>{const n=document.createElement("div");n.className="modal-actions",n.style.flexDirection="column",n.style.alignItems="stretch";const s=(i,a,r=!1)=>{const c=document.createElement("button");c.className="btn"+(r?" primary":""),c.textContent=i,c.addEventListener("click",()=>{a(),e()}),n.appendChild(c)};s("Export current view as PNG",async()=>{const i=await _s(this.state,this.imageCache,this.surface.width,this.surface.height);ze(i,this.state.title+"-view.png")},!0),s("Export full board as PNG",async()=>{const i=await Xs(this.state,this.imageCache);i?ze(i,this.state.title+"-board.png"):b("Board is empty")}),s("Export board JSON (no images)",()=>{me.exportBoardJson(Wt(this.state))}),s("Export full bundle (JSON + images)",async()=>{await me.exportFullBundle(Wt(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 me.importFromFile(e);if(!n){b("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 i of n.elements)this.state.addElement(i);this.applyTheme(n.theme),this.topControls.setTitle(n.title),this.commands.clear(),this.state.emitChange(),b("Board imported")},t.click()}}const Xe="/__api/asset?path=",Mn=new Set(["P","DIV","UL","OL","H1","H2","H3","H4","H5","H6","BLOCKQUOTE","PRE","TABLE","FIGURE","HR"]);function qo(o){const t=[];for(const e of Array.from(o.childNodes))Se(t,e);return t.map(e=>e.replace(/[ \t]+$/gm,"")).join(`
|
|
16
|
-
|
|
17
|
-
`).replace(/\n{3,}/g,`
|
|
18
|
-
|
|
19
|
-
`).trim()+`
|
|
20
|
-
`}function Se(o,t){const e=Yo(t);e!==null&&e.trim()!==""&&o.push(e)}function Yo(o){if(o.nodeType===Node.TEXT_NODE){const n=(o.textContent??"").trim();return n||null}if(o.nodeType!==Node.ELEMENT_NODE)return null;const t=o,e=t.tagName;switch(e){case"H1":case"H2":case"H3":case"H4":case"H5":case"H6":return"#".repeat(Number(e[1]))+" "+ot(t).trim();case"HR":return"---";case"P":return t.classList.contains("embed-caret-spacer")&&!Vo(t)?null:Ye(t)?Ke(t):Ce(ot(t).trim());case"BLOCKQUOTE":return qe(t).split(`
|
|
21
|
-
`).map(s=>s?`> ${s}`:">").join(`
|
|
22
|
-
`);case"UL":return te(t,!1,0);case"OL":return te(t,!0,0);case"PRE":{const n=t.querySelector("code"),s=Qo(n??t),i=(n??t).textContent??"";return"```"+s+`
|
|
23
|
-
`+i.replace(/\n$/,"")+"\n```"}case"TABLE":return Go(t);case"FIGURE":return t.classList.contains("board-embed")?Zo(t):qe(t)||null;case"DIV":return Ye(t)?Ke(t):Ce(ot(t).trim());case"BR":return null;default:return ot(t).trim()||null}}function qe(o){const t=[];for(const e of Array.from(o.childNodes))Se(t,e);return t.join(`
|
|
24
|
-
|
|
25
|
-
`)}function Ye(o){for(const t of Array.from(o.children))if(Mn.has(t.tagName))return!0;return!1}function Ke(o){const t=[];let e="";const n=()=>{const s=Ce(e.trim());s&&t.push(s),e=""};for(const s of Array.from(o.childNodes)){if(Ko(s)){n(),Se(t,s);continue}e+=Te(s)}return n(),t.join(`
|
|
26
|
-
|
|
27
|
-
`)||null}function Ko(o){return o.nodeType===Node.ELEMENT_NODE&&Mn.has(o.tagName)}function Vo(o){for(const t of Array.from(o.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 te(o,t,e){const n=[],s=" ".repeat(e);let i=0;for(const a of Array.from(o.children)){if(a.tagName!=="LI")continue;i++;const r=t?`${i}. `:"- ";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(te(p,!1,e+1));continue}if(p.tagName==="OL"){l.push(te(p,!0,e+1));continue}if(p.tagName==="INPUT"){d=p.checked?"[x] ":"[ ] ";continue}}c+=Te(h)}n.push(s+r+(d??"")+c.trim());for(const h of l)h&&n.push(h)}return n.join(`
|
|
28
|
-
`)}function Go(o){const t=Array.from(o.querySelectorAll("tr"));if(t.length===0)return"";const e=i=>Array.from(i.querySelectorAll("th,td")).map(a=>ot(a).trim()),n=e(t[0]),s=[];s.push("| "+n.join(" | ")+" |"),s.push("| "+n.map(()=>"---").join(" | ")+" |");for(let i=1;i<t.length;i++)s.push("| "+e(t[i]).join(" | ")+" |");return s.join(`
|
|
29
|
-
`)}function Zo(o){const t=["```board",`src: ${o.dataset.src??""}`];return o.dataset.region&&t.push(`region: ${o.dataset.region.split(",").map(e=>e.trim()).join(", ")}`),o.dataset.height&&t.push(`height: ${o.dataset.height}`),o.dataset.title&&t.push(`title: ${o.dataset.title}`),t.push("```"),t.join(`
|
|
30
|
-
`)}function ot(o){let t="";for(const e of Array.from(o.childNodes))t+=Te(e);return t}function Te(o){if(o.nodeType===Node.TEXT_NODE)return Ge(o.textContent??"");if(o.nodeType!==Node.ELEMENT_NODE)return"";const t=o;switch(t.tagName){case"STRONG":case"B":return Ve("**",ot(t));case"EM":case"I":return Ve("*",ot(t));case"CODE":return ti(t.textContent??"");case"A":{const e=(t.getAttribute("data-md-href")??t.getAttribute("href")??"").trim(),n=ot(t).trim()||Ge(e);return e?`[${Ze(n)}](${Je(e)})`:n}case"IMG":return`)})`;case"BR":return`
|
|
31
|
-
`;default:return ot(t)}}function Jo(o){const t=o.getAttribute("data-md-src");if(t)return t;const e=o.getAttribute("src")??"";return e.startsWith(Xe)?decodeURIComponent(e.slice(Xe.length)):e}function Qo(o){const e=(o.getAttribute("class")??"").match(/language-(\w+)/);return e?e[1]:""}function Ve(o,t){const e=t.trim();return e?`${o}${e}${o}`:""}function ti(o){if(!o.includes("`"))return"`"+o+"`";const t=Math.max(...(o.match(/`+/g)??[""]).map(n=>n.length)),e="`".repeat(t+1);return`${e} ${o} ${e}`}function Ge(o){return o.replace(/\\/g,"\\\\").replace(/\[/g,"\\[").replace(/\]/g,"\\]").replace(/\*/g,"\\*").replace(/_/g,"\\_").replace(/`/g,"\\`")}function Ce(o){return o?/^#{1,6}\s/.test(o)||/^>\s?/.test(o)||/^([-+*]|\d+\.)\s+/.test(o)||/^`{3,}/.test(o)||/^(-{3,}|\*{3,}|_{3,})$/.test(o)?"\\"+o:o:null}function Ze(o){return o.replace(/\\/g,"\\\\").replace(/\]/g,"\\]")}function Je(o){return o.trim().replace(/\s/g,"%20").replace(/\)/g,"%29").replace(/</g,"%3C").replace(/>/g,"%3E")}class Be{constructor(t){u(this,"opts");u(this,"overlay");u(this,"surface");u(this,"statusEl");u(this,"fileInput");u(this,"toolbarButtons",[]);u(this,"abort",new AbortController);u(this,"frontmatter","");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 Be(t)}build(){this.overlay=et("doc-editor");const t=et("doc-editor-bar"),e=et("doc-editor-bar-left"),n=Qe("doc-editor-done","Done");n.addEventListener("click",()=>void this.close());const s=et("doc-editor-path");s.textContent=this.opts.path,e.append(n,s),this.statusEl=et("doc-editor-status"),this.setStatus("idle"),t.append(e,this.statusEl);const i=this.buildToolbar(),a=et("doc-editor-scroll");this.surface=et("markdown-body doc-editor-surface"),this.surface.contentEditable="true",this.surface.spellcheck=!0,a.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 l;const c=(l=this.fileInput.files)==null?void 0:l[0];c&&this.insertImageFile(c),this.fileInput.value=""}),this.overlay.append(t,i,a,this.fileInput),document.body.appendChild(this.overlay);const r=this.abort.signal;this.surface.addEventListener("input",()=>this.handleInput(),{signal:r}),this.surface.addEventListener("keyup",()=>this.updateToolbarState(),{signal:r}),this.surface.addEventListener("mouseup",()=>this.updateToolbarState(),{signal:r}),this.surface.addEventListener("click",c=>this.handleSurfaceClick(c),{signal:r}),document.addEventListener("selectionchange",()=>this.updateToolbarState(),{signal:r}),this.surface.addEventListener("paste",c=>this.onPaste(c),{signal:r}),this.surface.addEventListener("dragover",c=>c.preventDefault(),{signal:r}),this.surface.addEventListener("drop",c=>this.onDrop(c),{signal:r}),window.addEventListener("keydown",c=>{if(c.key==="Escape"&&document.activeElement!==this.surface&&this.close(),(c.metaKey||c.ctrlKey)&&c.key.toLowerCase()==="s"&&(c.preventDefault(),this.save()),(c.metaKey||c.ctrlKey)&&c.key.toLowerCase()==="z"){c.preventDefault(),c.shiftKey?this.redoHistory():this.undoHistory();return}if(c.ctrlKey&&!c.metaKey&&c.key.toLowerCase()==="y"){c.preventDefault(),this.redoHistory();return}this.handleEmbedDeleteKey(c)},{signal:r})}buildToolbar(){const t=et("doc-editor-toolbar"),e=s=>{const i=et("doc-editor-tb-group");return i.append(...s),i},n=(s,i,a,r=!1)=>{const c=Qe("doc-editor-tb-btn",r?"":s);return r&&(c.innerHTML=X[s]??"",c.classList.add("icon")),c.title=i,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")),nt(n("H1","Heading 1",()=>this.formatBlock("H1")),"block","H1"),nt(n("H2","Heading 2",()=>this.formatBlock("H2")),"block","H2"),nt(n("H3","Heading 3",()=>this.formatBlock("H3")),"block","H3")]),e([nt(n("B","Bold (Cmd+B)",()=>document.execCommand("bold")),"command","bold"),nt(n("I","Italic (Cmd+I)",()=>document.execCommand("italic")),"command","italic"),nt(n("</>","Inline code",()=>this.toggleInlineCode()),"inline","CODE")]),e([nt(n("❝","Quote",()=>this.formatBlock("BLOCKQUOTE")),"block","BLOCKQUOTE"),nt(n("•","Bulleted list",()=>document.execCommand("insertUnorderedList")),"command","insertUnorderedList"),nt(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(){let t="";try{t=await k.readFile(this.opts.rootId,this.opts.path)}catch{this.surface.innerHTML="<p></p>",this.setStatus("error","Could not load file");return}const{fm:e,body:n}=ei(t);this.frontmatter=e;const{html:s}=Nt(n,{rootId:this.opts.rootId,docPath:this.opts.path});this.surface.innerHTML=s||"<p></p>",this.decorateEmbeds(),document.execCommand("defaultParagraphSeparator",!1,"p"),this.recordHistorySnapshot(!0),this.updateToolbarState()}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){ht(t.previousElementSibling)||t.before($t()),ht(t.nextElementSibling)||t.after($t())}normalizeEmbedSpacing(){const t=Array.from(this.surface.querySelectorAll("figure.board-embed"));for(const n of t)tn(n.previousElementSibling),tn(n.nextElementSibling);let e=!1;for(const n of Array.from(this.surface.children)){if(!ht(n)){e=!1;continue}if(In(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 i=t.key==="Backspace"?s.previousElementSibling:s.nextElementSibling;return s.remove(),this.clearEmbedSelection(),this.placeCaretInBlock(ht(i)?i: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=ni(n,"P");if(!s||!this.surface.contains(s)||!ht(s))return null;const i=t==="Backspace"?s.previousElementSibling:s.nextElementSibling;return i instanceof HTMLElement&&i.matches("figure.board-embed.embed-chip")?i: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(){this.saveTimer!==null&&(clearTimeout(this.saveTimer),this.saveTimer=null),this.normalizeEmbedSpacing();const t=qo(this.surface),e=this.frontmatter?`${this.frontmatter}
|
|
32
|
-
|
|
33
|
-
${t}`:t;try{this.setStatus("saving"),await k.writeFile(this.opts.rootId,this.opts.path,e),this.setStatus("saved")}catch{this.setStatus("error","Save failed")}}async close(){this.closed||(this.closed=!0,await this.save(),this.abort.abort(),this.overlay.remove(),this.opts.onClose())}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="${nn(s)}">${ri(s)}</a>`))}toggleInlineCode(){const t=window.getSelection();if(!t||t.rangeCount===0||t.isCollapsed)return;const e=t.getRangeAt(0),n=ye(e.commonAncestorContainer,"CODE");if(n){const i=n.parentNode;for(;n.firstChild;)i.insertBefore(n.firstChild,n);i.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 i=s.dataset.stateType,a=s.dataset.stateValue;let r=!1;if(n&&i==="command"&&a)try{r=document.queryCommandState(a)}catch{r=!1}else(n&&i==="block"&&a||n&&i==="inline"&&a)&&(r=!!ye(e,a));s.classList.toggle("active",r),s.setAttribute("aria-pressed",r?"true":"false")}}onPaste(t){var s,i;const e=li(((s=t.clipboardData)==null?void 0:s.getData("text/plain"))??"");if(e){t.preventDefault(),this.insertSpecialBlock(e);return}const n=(i=t.clipboardData)==null?void 0:i.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=hi(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,$t()),this.placeCaretInBlock(this.surface.lastElementChild??this.surface);return}const s=si(this.surface,n.commonAncestorContainer);if(s&&oi(s)&&!ht(s)){this.replaceBlockWithInsertedNode(s,n,t);return}n.deleteContents();const i=$t();n.insertNode(i),n.insertNode(t),this.placeCaretInBlock(i)}replaceBlockWithInsertedNode(t,e,n){const s=document.createRange();s.selectNodeContents(t),s.setEnd(e.startContainer,e.startOffset);const i=document.createRange();i.selectNodeContents(t),i.setStart(e.endContainer,e.endOffset);const a=en(t,s.cloneContents()),r=en(t,i.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??$t();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 ai(t),n=await k.uploadAsset(this.opts.rootId,t.name,e),s=xe(this.opts.path,n),i=k.assetUrl(this.opts.rootId,n),a=t.name.replace(/\.[^.]+$/,"");this.surface.focus(),document.execCommand("insertHTML",!1,`<img src="${i}" data-md-src="${s}" alt="${nn(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",error:e??"Couldn't save"};this.statusEl.textContent=n[t],this.statusEl.dataset.state=t}}function et(o){const t=document.createElement("div");return t.className=o,t}function Qe(o,t){const e=document.createElement("button");return e.className=o,e.textContent=t,e}function nt(o,t,e){return o.dataset.stateType=t,o.dataset.stateValue=e,o.setAttribute("aria-pressed","false"),o}function $t(){const o=document.createElement("p");return o.className="embed-caret-spacer",o.appendChild(document.createElement("br")),o}function ht(o){return!!o&&o.tagName==="P"&&o.classList.contains("embed-caret-spacer")}function tn(o){ht(o)&&In(o)&&o.classList.remove("embed-caret-spacer")}function In(o){for(const t of Array.from(o.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 ei(o){if(!o.startsWith("---"))return{fm:"",body:o};const t=o.indexOf(`
|
|
34
|
-
---`,3);if(t===-1)return{fm:"",body:o};const e=o.slice(0,t+4),n=o.slice(t+4).replace(/^\n+/,"");return{fm:e,body:n}}function ye(o,t){let e=o;for(;e;){if(e.nodeType===Node.ELEMENT_NODE&&e.tagName===t)return e;e=e.parentNode}return null}function ni(o,t){let e=o;for(;e;){if(e.nodeType===Node.ELEMENT_NODE&&e.matches(t))return e;e=e.parentNode}return null}function si(o,t){let e=t;for(;e&&e.parentNode!==o;)e=e.parentNode;return e instanceof HTMLElement?e:null}function oi(o){return o.tagName==="P"||o.tagName==="DIV"}function en(o,t){if(!ii(t))return null;const e=document.createElement(o.tagName);return o.tagName==="P"&&o.classList.contains("embed-caret-spacer")&&(e.className="embed-caret-spacer"),e.appendChild(t),e}function ii(o){for(const t of Array.from(o.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 ai(o){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(o)})}function nn(o){return o.replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<")}function ri(o){return o.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}const ci={board:o=>{const t=vn(o);return t.src?{kind:"board",src:t.src,region:t.region,height:t.height,title:t.title}:null}};function li(o){const t=di(o);if(!t)return null;const e=ci[t.language];return e?e(t.body):null}function di(o){const e=o.trim().match(/^(`{3,})\s*([^\s`]+)?\s*\n([\s\S]*?)\n\1\s*$/);return e?{language:(e[2]??"").toLowerCase(),body:e[3]}:null}function hi(o){switch(o.kind){case"board":return pi(o)}}function pi(o){const t=document.createElement("figure");return t.className="board-embed",t.dataset.src=o.src,t.dataset.height=String(o.height),o.region&&(t.dataset.region=o.region.join(",")),o.title&&(t.dataset.title=o.title),t}const sn="docs:theme",on="docs:sidebar-collapsed",ve=[{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 ui{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,"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,"editor");u(this,"editorHost");u(this,"editingId");u(this,"lastNonBoardHash","#/");u(this,"lastNonBoardScrollTop",0);u(this,"pendingScrollRestore");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=H(n,s)},onEditDoc:(n,s)=>this.openDocEditor({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=U(n,s)},onRenameBoard:(n,s)=>void this.promptRenameBoard(n,s),onDeleteBoard:(n,s)=>void this.promptDeleteBoard(n,s),onMoveDoc:(n,s,i)=>void this.moveDoc(n,s,i),onMoveBoard:(n,s,i)=>void this.moveBoard(n,s,i),onMoveDirectory:(n,s,i)=>void this.moveDirectory(n,s,i)};this.sidebar=new Zn(this.layout,e),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.main),this.root.appendChild(this.layout),window.addEventListener("hashchange",()=>this.route()),window.addEventListener("keydown",n=>this.handleGlobalKeydown(n)),localStorage.getItem(on)==="true"&&this.setSidebarCollapsed(!0),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 i=s.match(/^\/root\/([^/]+)\/boards\/([^?]+)(?:\?(.*))?$/);if(i){this.lastNonBoardScrollTop=this.contentScroll.scrollTop,this.updateTopbarCart(!0),this.closeCartPopover(),this.openEditor(decodeURIComponent(i[1]),decodeURIComponent(i[2]),Bi(i[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]),v=this.docsRootById(g);if(v)return void this.renderProjectWiki(v.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"),i=f("h1","home-hero-title");i.textContent="ViteBoard";const a=f("p","home-hero-sub");a.textContent="A local-first documentation workspace and infinite-canvas whiteboard. Markdown files, boards, and structured capability specs — plain files on disk, tracked by git.",s.append(i,a);const r=[{label:"Docs",kind:"doc",body:"Markdown files discovered inside any docs/ directory you register. The sidebar builds a live tree from the filesystem — create, rename, and delete docs by right-clicking any directory or file.",items:["Standard markdown: headings, lists, tables, code blocks, blockquotes, images, links","Inline board previews via fenced board blocks","Full-text search across all docs — Cmd K / Ctrl K"]},{label:"Boards",kind:"board",body:"Infinite-canvas editors saved as JSON files alongside your docs. Open from the sidebar, edit in full-screen, close to return. Boards are small, diffable, and git-native.",items:["Elements: sticky notes, tasks, shapes, text, arrows, images, frames","Embed any board region into a doc with a fenced board block",'Click "Copy embed" inside the board editor to get the exact region snippet',"Cmd/Ctrl + scroll to zoom · Shift + scroll to pan"]},{label:"Capabilities",kind:"capability",body:"Structured markdown specs with YAML frontmatter (title, summary, tags). Build a cart of specs you need for a task and copy them as a single combined prompt.",items:["Add any spec to the component cart from the sidebar or the capabilities page","Cart lives in the topbar — click to open, review, or copy the prompt","Copied prompt includes all selected specs formatted for an LLM"]},{label:"Search",kind:"",body:"Full-text search across every doc and capability in every registered project. Indexed at startup and refreshed after any content change.",items:["Cmd K / Ctrl K to open from anywhere","Ranked by title, path, headings, and body text","Arrow keys to navigate · Enter to open · Escape to close"]}];for(const y of r){const v=f("div","home-feature"),w=f("div","home-feature-header"),x=f("span",`home-feature-badge home-feature-badge-${y.kind||"neutral"}`);x.textContent=y.label,w.appendChild(x),v.appendChild(w);const S=f("p","home-feature-body");if(S.textContent=y.body,v.appendChild(S),y.items){const E=document.createElement("ul");E.className="home-feature-list";for(const C of y.items){const T=document.createElement("li");T.textContent=C,E.appendChild(T)}v.appendChild(E)}s.appendChild(v)}const c=f("div","home-shortcut-title");c.textContent="Shortcuts & interactions",s.appendChild(c);const l=[["Cmd K / Ctrl K","Open search"],["Right-click project header","Remove from workspace"],["Right-click directory","New doc · New board · New capability"],["Right-click doc","Open · Edit · Rename · Delete"],["Right-click board","Open · Rename · Delete"],["Cmd/Ctrl + scroll on board","Zoom"],["Shift + scroll on board","Pan"],["Copy embed (board toolbar)","Snippet for current view region"]],d=f("div","home-shortcut-table");for(const[y,v]of l){const w=f("div","home-shortcut-row"),x=f("kbd","home-shortcut-key");x.textContent=y;const S=f("span","home-shortcut-desc");S.textContent=v,w.append(x,S),d.appendChild(w)}s.appendChild(d),n.appendChild(s);const h=f("div","home-panel"),p=f("div","home-panel-head"),m=f("div","home-panel-title");m.textContent="Workspace";const g=f("button","btn primary home-panel-add");if(g.textContent="+ Add repo",g.addEventListener("click",()=>void this.addRepoDocs()),p.append(m,g),h.appendChild(p),this.workspace.projects.length===0){const y=f("div","home-panel-empty");y.textContent="No projects yet. Add a project root to discover docs folders.",h.appendChild(y)}else for(const y of this.workspace.projects){const v=f("div","home-panel-project"),w=f("a","home-panel-project-head");w.href=xt(y.id);const x=f("span","home-panel-project-name");x.textContent=y.label;const S=y.docsRoots.reduce((R,lt)=>{const ft=this.treesByRoot.get(lt.id)??[];return R+Rt(ft).length},0),E=y.docsRoots.reduce((R,lt)=>{const ft=this.treesByRoot.get(lt.id)??[];return R+rt(ft).length},0),C=f("span","home-panel-project-meta"),T=[`${y.docsRoots.length} root${y.docsRoots.length===1?"":"s"}`];S&&T.push(`${S} doc${S===1?"":"s"}`),E&&T.push(`${E} board${E===1?"":"s"}`),C.textContent=T.join(" · "),w.append(x,C),v.appendChild(w);const L=6,M=y.docsRoots,I=M.slice(0,L),N=f("div","home-panel-roots");for(const R of I){const lt=this.treesByRoot.get(R.id)??[],ft=be(Bt(lt)),jn=ft?H(R.id,ft):Ct(R.id),Ft=f("a","home-panel-root");Ft.href=jn;const ae=f("span","home-panel-root-path");ae.textContent=R.displayPath;const re=Rt(lt).length,ce=rt(lt).length;if(re||ce){const Me=f("span","home-panel-root-meta"),le=[];re&&le.push(String(re)),ce&&le.push(`${ce}▦`),Me.textContent=le.join(" · "),Ft.append(ae,Me)}else Ft.appendChild(ae);N.appendChild(Ft)}if(M.length>L){const R=f("a","home-panel-more");R.href=xt(y.id),R.textContent=`+ ${M.length-L} more roots`,N.appendChild(R)}v.appendChild(N),h.appendChild(v)}n.appendChild(h),e.appendChild(n),this.content.replaceChildren(e),this.contentScroll.scrollTop=t??0}async renderProjectWiki(t,e,n,s){this.disposeContent(),this.content.classList.add("content-wide");const i=this.workspace.projects.find(p=>p.id===t);if(!i){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:i.label,href:xt(i.id)});const m=s.split("/").filter(Boolean);for(let y=0;y<m.length;y++){const v=m.slice(0,y+1).join("/"),w=y===m.length-1;p.push({label:m[y],href:w?void 0:ee(n,v)})}d.appendChild(at(p));const g=f("h1","wiki-title");g.textContent=m[m.length-1]||i.label,d.appendChild(g)}else if(r&&n){const p=this.docsRootById(n),m=[{label:"Home",href:"#/"}];if(m.push({label:i.label,href:xt(i.id)}),p){const y=p.displayPath.split("/").filter(Boolean);for(let v=0;v<y.length;v++){const w=y.slice(0,v+1).join("/"),x=v===y.length-1;m.push({label:y[v],href:x?void 0:ee(n,w)})}}d.appendChild(at(m));const g=f("h1","wiki-title");g.textContent=(p==null?void 0:p.displayPath.split("/").pop())||i.label,d.appendChild(g)}else{d.appendChild(at([{label:"Home",href:"#/"},{label:i.label}]));const p=f("h1","wiki-title");p.textContent=i.label,d.appendChild(p)}if(!r&&!c){const p=i.docsRoots.reduce((v,w)=>{const x=this.treesByRoot.get(w.id)??[];return v+Rt(x).length},0),m=i.docsRoots.reduce((v,w)=>{const x=this.treesByRoot.get(w.id)??[];return v+rt(x).length},0),g=i.docsRoots.reduce((v,w)=>{const S=(this.treesByRoot.get(w.id)??[]).find(E=>E.type==="dir"&&E.name==="capabilities");return v+((S==null?void 0:S.type)==="dir"?S.children.filter(E=>E.name.endsWith(".md")).length:0)},0),y=f("div","wiki-stats");for(const[v,w]of[[String(i.docsRoots.length),"docs roots"],[String(p),"documents"],[String(m),"boards"],[String(g),"capabilities"]]){const x=f("div","wiki-stat"),S=f("div","wiki-stat-val");S.textContent=v;const E=f("div","wiki-stat-label");E.textContent=w,x.append(S,E),y.appendChild(x)}d.appendChild(y)}a.appendChild(d);const h=n?i.docsRoots.filter(p=>p.id===n):i.docsRoots;for(const p of h){const m=this.treesByRoot.get(p.id)??[];let g=m;if(s&&!l){const C=(L,M)=>{for(const I of L){if(I.type==="dir"&&I.path===M)return I;if(I.type==="dir"){const N=C(I.children,M);if(N)return N}}return null},T=C(m,s);g=(T==null?void 0:T.type)==="dir"?T.children:[]}const y=Rt(g),v=rt(g),w=g.find(C=>C.type==="dir"&&C.name==="capabilities"),x=(w==null?void 0:w.type)==="dir"?w.children.filter(C=>C.name.endsWith(".md")):[],S=f("div","wiki-root-section");if(!s||l){const C=f("div","wiki-root-head"),T=f("div","wiki-root-path");T.textContent=p.displayPath,C.appendChild(T),S.appendChild(C)}if(!y.length&&!v.length&&!x.length){const C=f("div","wiki-root-empty");C.textContent="No docs, boards, or capabilities yet. Right-click in the sidebar to add content.",S.appendChild(C),a.appendChild(S);continue}const E=f("div","wiki-root-cols");if(y.length>0){const C=f("div","wiki-col"),T=f("div","wiki-col-head");T.textContent=`Docs (${y.length})`,C.appendChild(T);const L=f("div","wiki-col-list");for(const M of y){const I=f("a","wiki-col-item wiki-col-item-doc");I.href=H(p.id,M.path),I.textContent=M.name==="index.md"?"Overview":M.name.replace(/\.md$/,"").replace(/[-_]/g," ");const N=f("span","wiki-col-item-path"),R=M.path.includes("/")?M.path.slice(0,M.path.lastIndexOf("/")):"";N.textContent=R,R&&I.appendChild(N),L.appendChild(I)}C.appendChild(L),E.appendChild(C)}if(v.length>0){const C=f("div","wiki-col"),T=f("div","wiki-col-head");T.textContent=`Boards (${v.length})`,C.appendChild(T);const L=f("div","wiki-col-list");for(const M of v){const I=it(M.path),N=f("a","wiki-col-item wiki-col-item-board");N.href=U(p.id,I),N.textContent=I.split("/").pop().replace(/[-_]/g," ");const R=f("span","wiki-col-item-path");R.textContent=I.includes("/")?I.slice(0,I.lastIndexOf("/")):p.displayPath,N.appendChild(R),L.appendChild(N)}C.appendChild(L),E.appendChild(C)}if(x.length>0){const C=f("div","wiki-col"),T=f("div","wiki-col-head");T.textContent=`Capabilities (${x.length})`,C.appendChild(T);const L=f("div","wiki-col-list");for(const M of x){const I=f("a","wiki-col-item wiki-col-item-cap");I.href=wt(p.id,M.path),I.textContent=M.name.replace(/\.md$/,"").replace(/[-_]/g," "),L.appendChild(I)}C.appendChild(L),E.appendChild(C)}S.appendChild(E),a.appendChild(S)}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 i=f("div","doc-page"),a=this.docsRootById(t),r=Yt(this.workspace,t,{filePath:e});i.appendChild(at(r));const c=f("div","");i.appendChild(c),this.content.replaceChildren(i);const{destroy:l}=Ae(c,s,{rootId:t,docPath:e});this.cleanup=l;const d=await k.getTree(t),h=Kt(t,d),p=Vt(h,H(t,e));p&&i.appendChild(p),this.contentScroll.scrollTop=n??0}catch{this.renderMessage("Document not found",e)}}openDocEditor(t){this.topbar.classList.add("editing-doc"),this.closeCartPopover(),Be.open({rootId:t.rootId,path:t.path,onClose:()=>{this.topbar.classList.remove("editing-doc"),this.route()}})}async renderCapabilityDoc(t,e,n){var 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}let s;try{s=await k.readFile(t,e),this.setEditableFile({rootId:t,path:e})}catch{this.renderMessage("Capability not found",e);return}const i=f("div","page"),a=this.docsRootById(t);a&&this.workspace.projects.find(C=>C.id===a.projectId);const r=K(((E=e.split("/").pop())==null?void 0:E.replace(/\.md$/,""))??e),c=Yt(this.workspace,t,{filePath:e,currentLabel:r});i.appendChild(at(c));const l=f("div","page-bar"),d=f("div","doc-actions capability-doc-actions"),{frontmatter:h}=Nt(s),p=f("button","btn");p.classList.add("capability-detail-btn");const m=an(t,e,s,h);this.updateCartButton(p,m.path),p.addEventListener("click",()=>{this.toggleCapabilityCart(m),this.updateCartButton(p,m.path)});const g=f("button","btn primary");g.classList.add("capability-detail-btn"),g.textContent="Copy as LLM prompt",g.addEventListener("click",async()=>{try{await navigator.clipboard.writeText(s),b("Capability copied — paste into your AI assistant")}catch{b("Could not access clipboard")}}),d.append(p,g),l.append(d),i.appendChild(l);const y=f("div","");i.appendChild(y),this.content.replaceChildren(i);const{destroy:v}=Ae(y,s,{rootId:t,docPath:e});this.cleanup=v;const w=this.treesByRoot.get(t)??[],x=Kt(t,w),S=Vt(x,wt(t,e));S&&i.appendChild(S),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=Yt(this.workspace,t,{currentLabel:"Capabilities"});n.appendChild(at(s));const i=f("h1","");i.textContent="Capability library";const a=f("p","page-lead");a.textContent="Select reusable specs into a component cart, then copy one combined prompt for your coding assistant.",n.append(i,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=d.find(y=>y.type==="dir"&&y.name==="capabilities"),p=h&&h.type==="dir"?h.children.filter(y=>y.type==="file"&&y.name.endsWith(".md")):[];for(const y of p){const v=await k.readFile(t,y.path),{frontmatter:w}=Nt(v),x=an(t,y.path,v,w),S=document.createElement("article");S.className="card",this.capabilityCart.has(x.path)&&S.classList.add("selected");const E=f("div","card-title"),C=document.createElement("a");C.href=wt(t,y.path),C.textContent=x.title,E.appendChild(C);const T=f("div","card-desc");if(T.textContent=x.summary,S.append(E,T),x.tags.length){const I=f("div","card-tags");for(const N of x.tags){const R=f("span","tag");R.textContent=N,I.appendChild(R)}S.appendChild(I)}const L=f("div","card-actions"),M=f("button","btn");M.classList.add("capability-add-btn"),this.updateCartButton(M,x.path),M.addEventListener("click",()=>{this.toggleCapabilityCart(x),this.renderCapabilitiesIndex(t,this.contentScroll.scrollTop)}),L.appendChild(M),S.appendChild(L),c.appendChild(S)}p.length===0&&c.appendChild(bt("No capabilities yet."));const m=Kt(t,d),g=Vt(m,Ln(t));g&&n.appendChild(g)}catch{c.appendChild(bt("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 i=f("div","token-actions"),a=f("button","btn");a.textContent="Select all",a.addEventListener("click",()=>{this.selectedTokens=new Set(ve.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()),i.append(a,r,c),n.append(s,i);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 ve.filter(y=>y.group===d)){const y=f("div","token-card");this.selectedTokens.has(g.variable)&&y.classList.add("selected");const v=f("label","token-select-zone"),w=document.createElement("input");w.type="checkbox",w.checked=this.selectedTokens.has(g.variable),w.addEventListener("change",()=>{w.checked?this.selectedTokens.add(g.variable):this.selectedTokens.delete(g.variable),this.renderTokenLibrary(this.contentScroll.scrollTop)});const x=f("span","token-swatch");x.style.background=g.value;const S=f("span","token-copy"),E=f("strong","");E.textContent=g.name;const C=f("code","");C.textContent=g.value;const T=f("small","");T.textContent=g.description,S.append(E,C,T),v.append(w,x,S);const L=f("button","token-copy-btn");L.type="button",L.innerHTML=X.copy,L.title=`Copy ${g.variable}`,L.setAttribute("aria-label",`Copy ${g.variable}`),L.addEventListener("click",M=>{M.stopPropagation(),this.copyToken(g)}),y.append(v,L),m.appendChild(y)}h.append(p,m),e.appendChild(h)}this.content.replaceChildren(e),this.contentScroll.scrollTop=t??0}async copySelectedTokens(){const t=ve.filter(n=>this.selectedTokens.has(n.variable));if(t.length===0)return;const e=[":root {",...t.map(n=>` ${n.variable}: ${n.value};`),"}"].join(`
|
|
35
|
-
`);try{await navigator.clipboard.writeText(e),b(`${t.length} token${t.length===1?"":"s"} copied`)}catch{b("Could not access clipboard")}}async copyToken(t){try{await navigator.clipboard.writeText(`${t.variable}: ${t.value};`),b(`${t.variable} copied`)}catch{b("Could not access clipboard")}}renderCapabilityCart(t){const e=t??(()=>{const h=mi(location.hash);h&&this.renderCapabilitiesIndex(h,this.contentScroll.scrollTop)}),n=f("aside","capability-cart"),s=f("div","capability-cart-head"),i=f("h2","");i.textContent="Component cart";const a=f("span","capability-cart-count");a.textContent=String(this.capabilityCart.size),s.append(i,a);const r=f("div","capability-cart-body");if(this.capabilityCart.size===0){const h=f("div","capability-cart-empty");h.textContent="Select specs to build a combined prompt.",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 v=f("button","capability-cart-remove");v.type="button",v.textContent="Remove",v.addEventListener("click",()=>{this.capabilityCart.delete(h.path),this.updateTopbarCart(),e()}),p.append(m,v),r.appendChild(p)}const c=f("div","capability-cart-actions"),l=f("button","btn primary");l.textContent="Copy prompt",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?"Remove from cart":"Add to cart",t.classList.toggle("primary",!n),t.classList.toggle("is-selected",n)}async copyCapabilityCartPrompt(){if(this.capabilityCart.size===0)return;const e=`Use these capability/component specs as implementation guidance for the next coding task.
|
|
36
|
-
|
|
37
|
-
`+[...this.capabilityCart.values()].map((n,s)=>`## ${s+1}. ${n.title}
|
|
38
|
-
|
|
39
|
-
${n.md.trim()}`).join(`
|
|
40
|
-
|
|
41
|
-
---
|
|
42
|
-
|
|
43
|
-
`);try{await navigator.clipboard.writeText(e),b(`${this.capabilityCart.size} spec${this.capabilityCart.size===1?"":"s"} copied`)}catch{b("Could not access clipboard")}}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=Yt(this.workspace,t,{currentLabel:"Boards"});n.appendChild(at(s));const i=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)),i.append(a,r),n.appendChild(i);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 O.list(t);for(const g of d){const y=document.createElement("a");y.className="card",y.href=U(t,g.id);const v=f("div","card-title");v.textContent=g.title;const w=f("div","card-desc");w.textContent=`${g.elementCount} element${g.elementCount===1?"":"s"} · ${g.filePath}`,y.append(v,w),l.appendChild(y)}d.length===0&&l.appendChild(bt("No boards yet — create one."));const h=await k.getTree(t),p=Kt(t,h),m=Vt(p,Ct(t));m&&n.appendChild(m)}catch{l.appendChild(bt("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"),i=[{label:"Home",href:"#/"},{label:n.label,href:xt(t)},{label:"Boards"}];s.appendChild(at(i));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=U(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(bt("No boards yet — create one."))}catch{l.appendChild(bt("Could not load boards."))}this.contentScroll.scrollTop=e??0}async listProjectBoards(t){const e=[];for(const n of t.docsRoots){const s=await O.list(n.id);for(const i of s)e.push({root:n,board:i})}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),i=(s==null?void 0:s.body)??this.sidebar.element.querySelector(".nav-repo-body");i?this.sidebar.startInlineCreate(i,"board-name",async a=>{const r=dt(a,"board");if(!r.ok){b(r.message);return}try{const c=await O.create(t,r.raw,At(),e);await this.refreshContentIndex(),location.hash=U(t,c)}catch{b("Could not create board")}}):F("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=dt(c.value,"board");if(!p.ok){b(p.message),c.focus();return}try{const m=await O.create(t,p.raw,At(),e);r(),await this.refreshContentIndex(),location.hash=U(t,m)}catch{b("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),i=(s==null?void 0:s.body)??this.sidebar.element.querySelector(".nav-repo-body");i?this.sidebar.startInlineCreate(i,"directory-name",async a=>{const r=dt(a,"directory");if(!r.ok){b(r.message);return}const c=e?`${e}/${r.slug}`:r.slug;try{await k.createDirectory(t,c),await this.refreshContentIndex()}catch{b("Could not create directory")}}):F("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=dt(c.value,"directory");if(!p.ok){b(p.message),c.focus();return}const m=e?`${e}/${p.slug}`:p.slug;try{await k.createDirectory(t,m),r(),await this.refreshContentIndex()}catch{b("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{b("Could not create docs folder");return}}async createDocsFolder(t,e){try{await k.createDocsFolder(t,e),await this.refreshContentIndex(),this.sidebar.clearBrowseMode(),b(`Created docs/ in ${e||"project root"}`)}catch{b("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),i=(s==null?void 0:s.body)??this.sidebar.element.querySelector(".nav-repo-body");i?this.sidebar.startInlineCreate(i,"document-name.md",async a=>{const r=dt(a.replace(/\.md$/i,""),"doc");if(!r.ok){b(r.message);return}const c=e?`${e}/${r.slug}.md`:`${r.slug}.md`;try{await k.writeFile(t,c,`# ${K(r.slug)}
|
|
44
|
-
`),await this.refreshContentIndex(),location.hash=H(t,c)}catch{b("Could not create doc")}}):F("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=dt(c.value.replace(/\.md$/i,""),"doc");if(!p.ok){b(p.message),c.focus();return}const m=e?`${e}/${p.slug}.md`:`${p.slug}.md`;try{await k.writeFile(t,m,`# ${K(p.slug)}
|
|
45
|
-
`),r(),await this.refreshContentIndex(),location.hash=H(t,m)}catch{b("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){F("New capability",(n,s)=>{const i=document.createElement("input");i.className="board-title",i.placeholder="Capability name (e.g. auth-flow)",i.style.width="100%",i.style.marginBottom="12px";const a=f("div","modal-actions"),r=f("button","btn primary");r.textContent="Create";const c=async()=>{const l=(i.value.trim()||"untitled").replace(/\.md$/,"").replace(/\s+/g,"-").toLowerCase(),d=e?`capabilities/${e}/${l}.md`:`capabilities/${l}.md`,h=K(l),p=`---
|
|
46
|
-
title: ${h}
|
|
47
|
-
summary:
|
|
48
|
-
tags:
|
|
49
|
-
---
|
|
50
|
-
|
|
51
|
-
# ${h}
|
|
52
|
-
`;try{await k.writeFile(t,d,p),s(),await this.refreshContentIndex(),location.hash=wt(t,d)}catch{b("Could not create capability")}};r.addEventListener("click",()=>void c()),i.addEventListener("keydown",l=>{l.stopPropagation(),l.key==="Enter"&&c()}),a.appendChild(r),n.append(i,a),i.focus()})}promptRenameDoc(t,e){const n=e.includes("/")?e.slice(0,e.lastIndexOf("/")+1):"",s=e.replace(/\.md$/,"").split("/").pop()??"",i=`doc:${t}:${e}`,a=this.sidebar.links.get(i);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=H(t,l)}catch{b("Could not rename doc")}}):F("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=H(t,g)}catch{b("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=dn(n,Pt(e));if(s!==e)try{const i=new Set(rt(await k.getTree(t)).map(c=>it(c.path))),a=await k.readFile(t,e),r=cn(a,e,s,i);await k.renameFile(t,e,s),r!==a&&await k.writeFile(t,s,r),await this.refreshContentIndex(),location.hash===H(t,e)&&(location.hash=H(t,s)),b(`Moved ${Pt(e)}`)}catch{b("Could not move doc")}}async moveBoard(t,e,n){try{const s=await O.move(t,e,n),i=await this.updateBoardEmbedReferences(t,e,s);await this.refreshContentIndex(),location.hash===U(t,e)&&(location.hash=U(t,s)),b(i>0?`Moved ${Pt(e)} and updated ${i} embed${i===1?"":"s"}`:`Moved ${Pt(e)}`)}catch(s){b(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),i=Pn(s);let a=0;for(const r of i){const c=await k.readFile(t,r.path),l=wi(c,r.path,e,n);l!==c&&(await k.writeFile(t,r.path,l),a++)}return a}async moveDirectory(t,e,n){const s=Pt(e),i=dn(n,s);if(i!==e)try{const a=await k.getTree(t),r=new Set(rt(a).map(p=>it(p.path))),c=vi(a,e),l=bi(a,e),d=await Promise.all(l.map(async p=>[p.path,await k.readFile(t,p.path)]));await k.renameFile(t,e,i);let h=0;for(const[p,m]of d){const g=`${i}${p.slice(e.length)}`,y=cn(m,p,g,r);y!==m&&(await k.writeFile(t,g,y),h++)}for(const p of c){const m=`${i}${p.slice(e.length)}`;await this.rewriteMovedBoardDocumentId(t,p,m),h+=await this.updateBoardEmbedReferences(t,p,m)}await this.refreshContentIndex(),this.rewriteRouteAfterDirectoryMove(t,e,i),b(h>0?`Moved ${s} and updated ${h} embed${h===1?"":"s"}`:`Moved ${s}`)}catch{b("Could not move directory")}}promptRenameDirectory(t,e){const n=e.includes("/")?e.slice(0,e.lastIndexOf("/")+1):"",s=e.split("/").pop()??"";F("Rename directory",(i,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(),b(`Renamed to ${h}`)}catch{b("Could not rename directory")}};l.addEventListener("click",()=>void d()),r.addEventListener("keydown",h=>{h.stopPropagation(),h.key==="Enter"&&d()}),c.appendChild(l),i.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(),b(`Deleted ${n}`)}catch{b("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),i=`#/root/${s}/docs/`,a=`#/root/${s}/boards/`,r=location.hash;if(r.startsWith(i)){const c=decodeURIComponent(r.slice(i.length));(c===e||c.startsWith(`${e}/`))&&(location.hash=H(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=U(t,`${n}${c.slice(e.length)}`))}}async promptDeleteDoc(t,e){const n=e.split("/").pop()??e;F("Delete doc",(s,i)=>{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),i(),await this.refreshContentIndex(),location.hash="#/"}catch{b("Could not delete doc")}});const l=f("button","btn");l.textContent="Cancel",l.addEventListener("click",i),r.append(c,l),s.append(a,r)})}async promptRenameBoard(t,e){const n=await O.load(t,e);if(!n){b("Board not found");return}F("Rename board",(s,i)=>{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=dt(a.value,"board");if(!d.ok){b(d.message),a.focus();return}try{const h=await O.rename(t,e,d.raw),p=await this.updateBoardEmbedReferences(t,e,h);i(),await this.refreshContentIndex(),location.hash===U(t,e)&&(location.hash=U(t,h)),p>0&&b(`Renamed board and updated ${p} embed${p===1?"":"s"}`)}catch(h){b(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){F("Delete board",(n,s)=>{const i=f("p","modal-copy");i.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 O.delete(t,e),s(),await this.refreshContentIndex(),location.hash=Ct(t)}catch{b("Could not delete board")}});const c=f("button","btn");c.textContent="Cancel",c.addEventListener("click",s),a.append(r,c),n.append(i,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 Xo(this.editorHost,{boardId:e,loadBoard:s=>O.load(t,s),saveBoard:s=>O.save(t,s),deleteBoard:s=>O.delete(t,s),uploadImage:(s,i)=>O.uploadImage(t,s,i),resolveAssetUrl:s=>k.assetUrl(t,s),initialRegion:n,onExit:()=>{this.pendingScrollRestore=this.lastNonBoardScrollTop,location.hash=this.lastNonBoardHash},onDeleted:()=>{location.hash=Ct(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(sn),e=t==="dark"||t==="light"?t:window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light";document.documentElement.setAttribute("data-theme",e)}toggleTheme(){const t=At()==="dark"?"light":"dark";document.documentElement.setAttribute("data-theme",t),localStorage.setItem(sn,t),this.updateThemeButton(),this.route({preserveScroll:!0})}createTopbar(){const t=f("div","shell-topbar"),e=f("button","topbar-icon-btn");return e.innerHTML=X.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=`${X.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.addEventListener("click",()=>this.toggleCartPopover()),this.updateTopbarCart(),this.editBtn=f("button","btn topbar-edit"),this.editBtn.type="button",this.editBtn.innerHTML=`${X.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.cartBtn,this.editBtn,this.themeBtn),t}setSidebarCollapsed(t){this.layout.classList.toggle("sidebar-collapsed",t),localStorage.setItem(on,String(t))}setEditableFile(t){this.currentEditableFile=t,this.editBtn&&(this.editBtn.hidden=!t,this.editBtn.disabled=!t)}promptRemoveProject(t,e){F("Remove from workspace",(n,s)=>{const i=f("p","modal-copy");i.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{b("Could not remove project")}});const c=f("button","btn");c.textContent="Cancel",c.addEventListener("click",s),a.append(r,c),n.append(i,a)})}async addRepoDocs(){var t;try{const e=new Set(this.allDocsRoots().map(l=>l.id)),n=await k.pickAndAddProject();if(n.cancelled)return;await this.refreshContentIndex();const s=this.allDocsRoots(),a=(n.addedProjectId?(t=this.workspace.projects.find(l=>l.id===n.addedProjectId))==null?void 0:t.docsRoots[0]:s.find(l=>!e.has(l.id)))??s[0];if(!a){b("No docs folders found under that project root.");return}const r=this.treesByRoot.get(a.id)??[],c=be(Bt(r));c?location.hash=H(a.id,c):location.hash=Ct(a.id),b(`Added ${a.projectLabel}.`)}catch(e){b(e instanceof Error?e.message:"Could not add project root.")}}updateTopbarCart(t=!1){if(!this.cartBtn)return;const e=this.capabilityCart.size;this.cartBtn.hidden=t,this.cartBtn.innerHTML=`<span>Cart</span><strong>${e}</strong>`,this.cartBtn.title=`${e} item${e===1?"":"s"} in component cart`,this.cartBtn.setAttribute("aria-label",this.cartBtn.title),this.cartBtn.disabled=e===0}toggleCartPopover(){var t;if((t=this.cartPopover)!=null&&t.classList.contains("visible")){this.closeCartPopover();return}this.openCartPopover()}openCartPopover(){this.capabilityCart.size!==0&&(this.cartPopover||(this.cartPopover=f("div","topbar-cart-popover"),this.topbar.appendChild(this.cartPopover),document.addEventListener("mousedown",t=>{var n;if(!((n=this.cartPopover)!=null&&n.classList.contains("visible")))return;const e=t.target;this.cartPopover.contains(e)||this.cartBtn.contains(e)||this.closeCartPopover()})),this.cartPopover.replaceChildren(this.renderCapabilityCart(()=>{this.updateTopbarCart(),this.capabilityCart.size===0?this.closeCartPopover():this.openCartPopover()})),this.content.classList.add("cart-popover-open"),this.cartPopover.classList.add("visible"))}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=At()==="dark";this.themeBtn.innerHTML=t?X.sun:X.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 i=e.match(/^#\/root\/([^/]+)\/boards\/(.+)$/);if(i){const r=decodeURIComponent(i[1]),c=decodeURIComponent(i[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=[];return}const t=this.allDocsRoots(),e=await Promise.all(t.map(async i=>{try{return[i.id,await k.getTree(i.id)]}catch{return[i.id,[]]}}));this.treesByRoot=new Map(e);const n=await Promise.all(this.workspace.projects.map(async i=>{try{return[i.id,await k.getProjectTree(i.id)]}catch{return[i.id,[]]}}));this.treesByProject=new Map(n),this.sidebar.refresh(this.workspace,this.treesByRoot,this.treesByProject);const s=[];for(const i of t){const a=this.treesByRoot.get(i.id)??[],r=[...Rt(a),...Wn(a,"capabilities")].filter(c=>c.type==="file"&&c.name.endsWith(".md"));for(const c of r){const l="path"in c?c.path:"";let d="";try{d=await k.readFile(i.id,l)}catch{}const{frontmatter:h}=Nt(d),p=Dn(d),m=h.title??$n(d)??K(c.name),g=h.summary??Rn(p),y=l.startsWith("capabilities/")?"Capability":"Doc",v=l.startsWith("capabilities/")?wt(i.id,l):H(i.id,l);s.push({title:m,path:`${i.projectLabel}/${i.displayPath}/${l}`,href:v,section:y,summary:g,body:p.map(w=>w.text).join(" "),chunks:p,rootId:i.id})}}this.searchIndex=s.filter(i=>i.body||i.title)}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=X.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 i,a,r,c,l;if(t.key==="Escape"){t.preventDefault(),this.closeCommandPalette();return}const e=[...((i=this.paletteResults)==null?void 0:i.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=fi(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.":"No docs indexed yet.",this.paletteResults.appendChild(s);return}for(const[s,i]of n.entries()){const{entry:a}=i,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");rn(c,a.title,e);const l=f("div","command-result-meta");l.textContent=[a.section,i.heading,a.path].filter(Boolean).join(" · ");const d=f("div","command-result-summary");rn(d,i.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(st).filter(Boolean),n=st(t.join(" "));return this.searchIndex.map(s=>{const i=st(s.title),a=st(s.path),r=st(s.summary);let c=Dt(i,e,n,10);c+=Dt(a,e,n,4),c+=Dt(r,e,n,3);let l,d=0;for(const p of s.chunks){const m=Dt(st(p.heading),e,n,5),g=Dt(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?ki(l.text,e):s.summary}}).filter(s=>s.score>0).sort((s,i)=>i.score-s.score||s.entry.title.localeCompare(i.entry.title))}shortcutLabel(){return/Mac|iPhone|iPad/.test(navigator.platform)?"⌘K":"Ctrl K"}disposeContent(){this.cleanup&&(this.cleanup(),this.cleanup=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 i=f("p","page-lead");i.textContent=e,n.append(s,i),this.content.replaceChildren(n)}}function Yt(o,t,e={}){const n=[{label:"Home",href:"#/"}];let s;for(const c of o.projects){const l=c.docsRoots.find(d=>d.id===t);if(l){n.push({label:c.label,href:xt(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:ee(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:K(r[c]),href:ee(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":K(c)})}return n}function at(o){const t=f("nav","breadcrumb");t.setAttribute("aria-label","Breadcrumb");for(let e=0;e<o.length;e++){const n=o[e],s=e===o.length-1;if(!s&&n.href){const i=document.createElement("a");i.className="breadcrumb-link",i.href=n.href,i.textContent=n.label,t.appendChild(i)}else{const i=f("span","breadcrumb-current");i.textContent=n.label,t.appendChild(i)}if(!s){const i=f("span","breadcrumb-sep");i.textContent="/",i.setAttribute("aria-hidden","true"),t.appendChild(i)}}return t}function f(o,t){const e=document.createElement(o);return t&&(e.className=t),e}function bt(o){const t=f("div","empty-note");return t.textContent=o,t}function an(o,t,e,n){return{path:`${o}:${t}`,displayPath:t,title:n.title??$n(e)??K(t.split("/").pop()??t),summary:n.summary??Rn(Dn(e)),tags:n.tags?n.tags.split(",").map(s=>s.trim()).filter(Boolean):[],md:e}}function Kt(o,t){const e=[];function n(r){const l=[...Bt(r)].sort((d,h)=>{const p=d.type==="file"&&d.name==="index.md"?-1:0,m=h.type==="file"&&h.name==="index.md"?-1:0;return p!==m?p-m:d.type!==h.type?d.type==="dir"?-1:1:d.name.localeCompare(h.name)});for(const d of l)if(d.type==="file"){const h=d.name==="index.md"?"Overview":K(d.name);e.push({label:h,href:H(o,d.path)})}else n(d.children)}n(t);const s=rt(t).sort((r,c)=>r.path.localeCompare(c.path));if(s.length>0){e.push({label:"All Boards",href:Ct(o)});for(const r of s){const c=it(r.path);e.push({label:K(c.split("/").pop()??c),href:U(o,c)})}}const i=t.find(r=>r.type==="dir"&&r.name==="capabilities"),a=(i==null?void 0:i.type)==="dir"?i.children.filter(r=>r.type==="file"&&r.name.endsWith(".md")).sort((r,c)=>r.name.localeCompare(c.name)):[];if(a.length>0){e.push({label:"All Capabilities",href:Ln(o)});for(const r of a)e.push({label:K(r.name),href:wt(o,r.path)})}return e}function Vt(o,t){const e=o.findIndex(a=>a.href===t);if(e===-1)return null;const n=e>0?o[e-1]:null,s=e<o.length-1?o[e+1]:null;if(!n&&!s)return null;const i=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>`,i.appendChild(a)}else i.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>`,i.appendChild(a)}return i}function Ln(o){return`#/root/${encodeURIComponent(o)}/capabilities`}function Bt(o){return o.filter(t=>t.type==="file"?t.name.endsWith(".md"):t.name==="capabilities"||t.name==="assets"?!1:Bt(t.children).length>0).map(t=>t.type==="file"?t:{...t,children:Bt(t.children)})}function Rt(o){const t=[],e=n=>{for(const s of Bt(n))s.type==="file"?t.push(s):e(s.children)};return e(o),t}function H(o,t){return`#/root/${encodeURIComponent(o)}/docs/${encodeURIComponent(t)}`}function wt(o,t){return`#/root/${encodeURIComponent(o)}/capabilities/${encodeURIComponent(t)}`}function Ct(o){return`#/root/${encodeURIComponent(o)}/boards`}function U(o,t){return`#/root/${encodeURIComponent(o)}/boards/${encodeURIComponent(t)}`}function xt(o){return`#/project/${encodeURIComponent(o)}`}function ee(o,t){return`#/root/${encodeURIComponent(o)}/directory/${encodeURIComponent(t)}`}function mi(o){const t=o.match(/^#\/root\/([^/]+)/);return t?decodeURIComponent(t[1]):null}function $n(o){var t,e;return((e=(t=o.match(/^#\s+(.+)$/m))==null?void 0:t[1])==null?void 0:e.trim())??null}function fi(o){return[...new Set(o.trim().split(/\s+/).map(st).filter(Boolean))]}function rn(o,t,e){const n=e.filter(Boolean).sort((a,r)=>r.length-a.length);if(n.length===0){o.textContent=t;return}const s=new RegExp(`(${n.map(gi).join("|")})`,"ig");let i=0;for(const a of t.matchAll(s)){const r=a.index??0;r>i&&o.appendChild(document.createTextNode(t.slice(i,r)));const c=document.createElement("mark");c.textContent=a[0],o.appendChild(c),i=r+a[0].length}i<t.length&&o.appendChild(document.createTextNode(t.slice(i)))}function gi(o){return o.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function Rn(o){var t;return((t=o.find(e=>e.kind==="text"&&e.text.length>0))==null?void 0:t.text.slice(0,180))??""}function Dn(o){const e=o.replace(/^---[\s\S]*?---\s*/,"").split(`
|
|
53
|
-
`),n=[];let s="",i=[];const a=(c,l="text",d=s)=>{const h=ln(c);h&&n.push({heading:d,text:h,normalized:st(h),kind:l})},r=()=>{i.length!==0&&(a(i.join(" "),"text"),i=[])};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*$"),v=[];for(c++;c<e.length&&!y.test(e[c]);)v.push(e[c]),c++;a(g==="board"?yi(v.join(`
|
|
54
|
-
`)):v.join(`
|
|
55
|
-
`),g==="board"?"board":"code");continue}const h=l.match(/^(#{1,6})\s+(.+)$/);if(h){r(),s=ln(h[2]),a(s,"heading",s);continue}if(l.trim()===""||/^(-{3,}|\*{3,}|_{3,})\s*$/.test(l)){r();continue}if(Ei(l))continue;const p=l.replace(/^>\s?/,"").replace(/^\s*([-*+]|\d+\.)\s+/,"").replace(/^\[([ xX])\]\s+/,"").replace(/\|/g," ");i.push(p)}return r(),n}function yi(o){return o.split(`
|
|
56
|
-
`).map(t=>t.replace(/#.*$/,"").trim()).filter(Boolean).map(t=>t.replace(/^(\w+)\s*:\s*/,"$1 ")).join(" ")}function Pn(o){const t=[],e=n=>{for(const s of n)s.type==="file"?s.name.endsWith(".md")&&t.push(s):e(s.children)};return e(o),t}function rt(o){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(o),t}function vi(o,t){const e=t.replace(/^\/+|\/+$/g,"");return rt(o).map(n=>it(n.path)).filter(n=>n===e||n.startsWith(`${e}/`))}function bi(o,t){const e=t.replace(/^\/+|\/+$/g,"");return Pn(o).filter(n=>n.path.startsWith(`${e}/`))}function cn(o,t,e,n){if(t===e)return o;const s=o.split(`
|
|
57
|
-
`);let i=!1,a,r=!1;const c=s.map(l=>{if(!i){const m=l.match(/^(`{3,})\s*board\s*$/i);return m&&(i=!0,a=new RegExp("^`{"+m[1].length+",}\\s*$")),l}if(a!=null&&a.test(l))return i=!1,a=void 0,l;const d=l.match(/^(\s*src\s*:\s*)(.*)$/i);if(!d)return l;const h=An(d[2]);if(!h.value)return l;const p=xi(h.value,t,e,n);return p===h.value?l:(r=!0,`${d[1]}${p}${h.suffix}`)});return r?c.join(`
|
|
58
|
-
`):o}function wi(o,t,e,n){const s=o.split(`
|
|
59
|
-
`);let i=!1,a,r=!1;const c=s.map(l=>{if(!i){const m=l.match(/^(`{3,})\s*board\s*$/i);return m&&(i=!0,a=new RegExp("^`{"+m[1].length+",}\\s*$")),l}if(a!=null&&a.test(l))return i=!1,a=void 0,l;const d=l.match(/^(\s*src\s*:\s*)(.*)$/i);if(!d)return l;const h=An(d[2]);return!h.value||!Ht(h.value,t).includes(e)?l:(r=!0,`${d[1]}${Ci(h.value,t,e,n)}${h.suffix}`)});return r?c.join(`
|
|
60
|
-
`):o}function An(o){var i;const t=o.indexOf("#"),e=t===-1?o:o.slice(0,t),n=t===-1?"":o.slice(t),s=((i=e.match(/\s*$/))==null?void 0:i[0])??"";return{value:e.trim(),suffix:`${s}${n}`}}function Ci(o,t,e,n){const s=o.trim(),i=s.startsWith("/"),a=s.startsWith("./")||s.startsWith("../"),r=s.endsWith(".board.json"),c=Ht(s,t),l=!a&&c[0]!==e&&c.includes(e);if(i){const h=`/${n}`;return r&&!h.endsWith(".board.json")?`${h}.board.json`:h}let d=a||l?Nn(xe(t,n)):n;return r&&!d.endsWith(".board.json")&&(d=`${d}.board.json`),d}function xi(o,t,e,n){const s=o.trim(),i=s.startsWith("./")||s.startsWith("../"),a=Ht(s,t),r=a[0]??"",c=a.find(p=>p!==r&&n.has(p));if(!i&&(!c||n.has(r)))return o;const l=s.endsWith(".board.json"),d=i?qn(s,t):c;if(!d)return o;let h=Nn(xe(e,d));return l&&!h.endsWith(".board.json")&&(h=`${h}.board.json`),h}function Nn(o){return!o||o.startsWith("./")||o.startsWith("../")?o:`./${o}`}function ln(o){return o.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 st(o){return o.normalize("NFKD").replace(/[\u0300-\u036f]/g,"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim()}function Dt(o,t,e,n){if(!o)return 0;let s=e&&o.includes(e)?n*4:0;for(const i of t)o===i&&(s+=n*5),o.startsWith(i)&&(s+=n*3),o.includes(i)&&(s+=n);return s}function ki(o,t){if(t.length===0)return o.slice(0,220);const e=st(o),n=t.map(l=>e.indexOf(l)).filter(l=>l>=0).sort((l,d)=>l-d)[0];if(n===void 0)return o.slice(0,220);const s=e.length===0?0:n/e.length,i=Math.round(o.length*s),a=Math.max(0,i-90),r=Math.min(o.length,i+170),c=o.slice(a,r).trim();return`${a>0?"... ":""}${c}${r<o.length?" ...":""}`}function Ei(o){return/^\s*\|?\s*:?-{1,}:?\s*(\|\s*:?-{1,}:?\s*)*\|?\s*$/.test(o)}function K(o){const t=o.replace(/\.md$/,"").replace(/[-_]/g," ");return t.charAt(0).toUpperCase()+t.slice(1)}function Si(o){return o.trim().replace(/[\\/]+/g,"-").replace(/\s+/g,"-").replace(/[^a-z0-9-_]/gi,"-").replace(/-+/g,"-").replace(/^-|-$/g,"").toLowerCase()}const Ti=new Set([".","..","assets","boards","capabilities","docs"]);function dt(o,t){const e=t==="doc"?"doc":t,n=o.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=Si(n);return s?s.length>64?{ok:!1,message:"Name is too long after formatting."}:Ti.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 Pt(o){const t=o.replace(/^\/+|\/+$/g,"");return t.split("/").pop()??t}function dn(o,t){const e=o.replace(/^\/+|\/+$/g,""),n=t.replace(/^\/+|\/+$/g,"");return e?`${e}/${n}`:n}function Bi(o){if(!o)return;const t=new URLSearchParams(o).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]]}const zn=document.getElementById("app");if(!zn)throw new Error("Missing #app root element");new ui(zn);
|