`; if(b.embed_type==='pdf')return `
Failed to load PDF
Open in new tab →
`; if(b.embed_type==='download'){ var sz=b.file_size||0;var szStr=sz<1024?sz+' B':sz<1048576?(sz/1024).toFixed(1)+' KB':(sz/1048576).toFixed(1)+' MB'; return `
{{ fd_icon("paperclip",14) }}
${esc(b.file_name||'File')}
${szStr} · ${esc(b.file_mime||'')}
⬇ Download
`; } } const ph=b.type==='heading_1'?'Heading 1':b.type==='heading_2'?'Heading 2':b.type==='heading_3'?'Heading 3':b.type==='heading_4'?'Heading 4':b.type==='bulleted_list'?'List':b.type==='numbered_list'?'List':b.type==='to_do'?'To-do':b.type==='toggle'?'Toggle list':b.type==='quote'?'Empty quote':b.type==='code'?'Type code...':b.type==='callout'?'Type something...':'Press \'/\' for commands...'; let inner=''; if(b.type==='to_do')inner=`
${esc(b.content)}
`; else if(b.type==='toggle')inner=`
▶
${esc(b.content)}
`; else if(b.type==='code')inner=`
${b.language||'Plain Text'}
${esc(b.content)}
`; else if(b.type==='callout')inner=`
${b.icon||'lightbulb'}
${esc(b.content)}
`; else inner=`
${esc(b.content)}
`; const tag=b.type.startsWith('heading_')?'h'+b.type.slice(-1):b.type==='quote'?'blockquote':'div'; const cls=tag==='div'?`block-content ${b.type==='bulleted_list'?'block-bullet':b.type==='numbered_list'?'block-numbered':b.type==='to_do'?'block-todo':b.type==='toggle'?'block-toggle':b.type==='code'?'block-code':b.type==='callout'?'block-callout':''}`:`block-content block-${tag}`; const style=b.type==='callout'?` style="background:${(b.style&&b.style.bgColor)||'var(--blue-bg)'}"`:''; return `
+
<${tag}${style} class="${cls}">${inner}${tag}>
`; } window.E=null; function editorState(){ return { pid:null,pageTitle:'',blocks:[],dirty:false,saving:false,lastSaved:'',st:null, updatedAt:'{{ page.get("updated_at", "") }}', fmt:{open:false,top:0,left:0,idx:-1}, shareOpen:false,moreOpen:false,gsMoreOpen:false,accessMenuOpen:false, moveOpen:false,moveWorkspaces:[],moveLoading:false, activityOpen:false,pageCreated:'{{ page.get("created_at", "") }}', shareTab:'share',favorited:false, pagePublished:{{ page_published | tojson }}, generalAccess:'{{ page_share_mode }}',publicPerm:'viewer', pubAllowEdit:false,pubAllowComments:true, pageUrl:window.location.href,publishedUrl:'', inviteEmail:'',invitePermission:'editor',accessList:[], toastVisible:false,toastMsg:'', get timeAgo() { if (!this.updatedAt) return 'just now'; var diff = Math.floor((Date.now() - new Date(this.updatedAt + 'Z').getTime()) / 1000); if (diff < 60) return 'just now'; if (diff < 3600) return Math.floor(diff / 60) + 'm ago'; if (diff < 86400) return Math.floor(diff / 3600) + 'h ago'; return Math.floor(diff / 86400) + 'd ago'; }, formatDate(d) { if (!d) return ''; var dt = new Date(d + 'Z'); return dt.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) + ' at ' + dt.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); }, init(){ _bid=Math.floor(Math.random()*10000); const dataEl=document.getElementById('page-data'); if(dataEl){try{const data=JSON.parse(dataEl.textContent);this.pid=data.id;this.pageTitle=data.title||'';this.favorited=data.favorited||false;const fmt=data.content_format||'blocks';const raw=data.content||''; if(fmt==='file'){this.fileData=data;this.loadFileContent(data);} else if(fmt==='blocks'&&raw){try{this.blocks=JSON.parse(raw);this.blocks.forEach(b=>{if(!b.id)b.id=genId()});}catch(e){this.blocks=[];}} else if(raw&&raw.trim())this.blocks=this.md2b(raw);}catch(e){}} if(!this.blocks.length)this.blocks=[this.mkB('paragraph','')]; this.loadInlineDBs(); window.E=this;SM.init();this.render(); setTimeout(()=>{this.focusBlock();const ct=document.getElementById('_blocksCt');if(!ct)return; ct.addEventListener('keydown',e=>this.onKd(e)); ct.addEventListener('paste',e=>{e.preventDefault();document.execCommand('insertText',false,(e.clipboardData||window.clipboardData).getData('text/plain'));this.dirty=true;this.autoSave();}); if(typeof Sortable!=='undefined')Sortable.create(ct,{draggable:'.block-wrapper',handle:'.block-handle',animation:150,ghostClass:'sortable-ghost',dragClass:'sortable-drag',onEnd:evt=>{if(evt.oldIndex===evt.newIndex)return;const[item]=this.blocks.splice(evt.oldIndex,1);this.blocks.splice(evt.newIndex,0,item);this.dirty=true;this.autoSave();this.render();}}); },100); }, fileData:null, async loadFileContent(data){ const url=data.file_url||'';const mime=data.file_mime||'';const name=data.file_name||'';const self=this; if(!url){this.blocks=[this.mkB('paragraph','File not available')];this.render();return;} const ext=name.toLowerCase().split('.').pop(); if(mime.startsWith('image/')||['png','jpg','jpeg','gif','webp','svg','bmp','ico'].includes(ext)){ this.blocks=[this.mkB('image','',{src:url,alt:name})];this.render();return; } if(mime==='application/pdf'||ext==='pdf'){ this.blocks=[this.mkB('embed','',{src:url,embed_type:'pdf'})];this.render();return; } const codeExts=['py','js','ts','jsx','tsx','html','htm','css','json','xml','yaml','yml','md','markdown','sql','sh','bash','ps1','bat','cmd','rb','go','rs','java','c','cpp','h','hpp','php','swift','kt','scala','r','toml','ini','cfg','conf','env','txt','log']; const langMap={'py':'python','js':'javascript','ts':'typescript','jsx':'javascript','tsx':'typescript','html':'html','htm':'html','css':'css','json':'json','xml':'xml','yaml':'yaml','yml':'yaml','md':'markdown','markdown':'markdown','sql':'sql','sh':'bash','bash':'bash','ps1':'powershell','bat':'plaintext','cmd':'plaintext','rb':'ruby','go':'go','rs':'rust','java':'java','c':'c','cpp':'cpp','h':'c','hpp':'cpp','php':'php','swift':'swift','kt':'kotlin','scala':'scala','r':'r','toml':'toml','ini':'ini','cfg':'ini','conf':'ini','env':'ini','txt':'plaintext','log':'plaintext'}; if(mime.startsWith('text/')||codeExts.includes(ext)){ try{const r=await fetch(url);const text=await r.text(); if(ext==='md'||ext==='markdown'){this.blocks=this.md2b(text);} else{this.blocks=[this.mkB('code',text,{language:langMap[ext]||'Plain Text'})];} this.render(); }catch(e){this.blocks=[this.mkB('paragraph','Error loading file: '+e.message)];this.render();} return; } this.blocks=[this.mkB('embed','',{src:url,embed_type:'download',file_name:name,file_size:data.file_size||0,file_mime:mime})];this.render(); }, mkB(type,content,extras){const b={id:genId(),type,content:content||'',...(extras||{})};if(type==='toggle')b.expanded=true;if(type==='to_do')b.checked=false;return b;}, getEl(bid){const ct=document.getElementById('_blocksCt');return ct?ct.querySelector(`[data-bid="${bid}"]`):null;}, getIdx(bid){return this.blocks.findIndex(b=>b.id===bid);}, getActiveBlock(){const el=document.activeElement;if(!el||!el.hasAttribute('data-bid'))return null;return{el,bid:el.dataset.bid,idx:this.getIdx(el.dataset.bid)};}, focusBlock(){const b=this.blocks[0];if(!b)return;const el=this.getEl(b.id);if(el){el.focus();ce(el);}}, onCtClick(e){if(e.target===document.getElementById('_blocksCt')||e.target?.classList?.contains('blocks-empty')){if(!this.blocks.length)this.addAt(0);else{const b=this.blocks[this.blocks.length-1];const el=b&&this.getEl(b.id);if(el){el.focus();ce(el);}}}}, sync(){const ct=document.getElementById('_blocksCt');if(!ct)return;for(const b of this.blocks){if(b.type==='divider'||b.type==='image'||b.type==='embed')continue;const el=ct.querySelector(`[data-bid="${b.id}"]`);if(el)b.content=el.textContent||'';}}, render(){const ct=document.getElementById('_blocksCt');if(!ct)return;let html='';for(let i=0;i
{w.onmouseenter=()=>{w.querySelector('.block-handle')?.classList.add('visible');w.querySelector('.block-actions')?.classList.add('visible');};w.onmouseleave=()=>{w.querySelector('.block-handle')?.classList.remove('visible');w.querySelector('.block-actions')?.classList.remove('visible');};}); ct.querySelectorAll('[data-placeholder]').forEach(el=>{el.innerHTML=el.innerHTML.replace(/
]*>/gi,'').trim()!==''?el.innerHTML:'';el.classList.toggle('empty',!el.textContent.trim());}); }, replaceBlock(idx,type){const b=this.blocks[idx];if(!b)return;this.sync();b.type=type;b.content='';if(type==='toggle')b.expanded=true;if(type==='to_do')b.checked=false;this.render();this.dirty=true;this.autoSave();setTimeout(()=>{const el=this.getEl(b.id);if(el)el.focus();},60);}, addAt(idx,type,content){this.sync();const b=this.mkB(type||'paragraph',content||'');this.blocks.splice(idx,0,b);this.dirty=true;this.autoSave();this.render();setTimeout(()=>{const el=this.getEl(b.id);if(el&&type!=='divider'){el.focus();content&&ce(el);}},60);return b;}, addAfter(idx,type){return this.addAt(idx+1,type);}, removeBlock(idx){this.sync();if(this.blocks.length<=1){this.blocks[0]=this.mkB('paragraph','');}else{this.blocks.splice(idx,1);}const fi=Math.min(idx,this.blocks.length-1);this.render();this.dirty=true;this.autoSave();setTimeout(()=>{const el=this.getEl(this.blocks[fi]?.id);if(el){el.focus();ce(el);}},60);}, toggleFavorite(){ const csrf=document.cookie.match(/csrf_token=([^;]+)/); const m=this.favorited?'DELETE':'POST'; fetch(`/board/api/favorites/${this.pid}`,{method:m,headers:{'X-CSRF-Token':csrf?csrf[1]:''}}) .then(r=>r.json()).then(()=>{this.favorited=!this.favorited;this.showToast(this.favorited?'Added to favorites':'Removed from favorites'); if(window.appState&&window.appState.refreshFavorites)window.appState.refreshFavorites(); }).catch(()=>{this.showToast('Failed to toggle favorite');}); }, toggleActivityOpen(){ if(!this.activityOpen){this.activityOpen=true;this._justOpened=true;setTimeout(()=>this._justOpened=false,200);} else{this.activityOpen=false;} }, toggleShareOpen(){ if(!this.shareOpen){this.shareOpen=true;this._justOpened=true;setTimeout(()=>this._justOpened=false,200);} else{this.shareOpen=false;} }, toggleMoreOpen(){ if(!this.moreOpen){this.moreOpen=true;this._justOpened=true;setTimeout(()=>this._justOpened=false,200);} else{this.moreOpen=false;} }, _justOpened:false, togglePublish(){this.pagePublished=!this.pagePublished;this.saveShare();}, saveShare(){ const csrf=document.cookie.match(/csrf_token=([^;]+)/); fetch(`/board/api/share/${this.pid}`,{method:'POST',headers:{'Content-Type':'application/json','X-CSRF-Token':csrf?csrf[1]:''},body:JSON.stringify({mode:this.generalAccess,published:this.pagePublished})}).catch(()=>{}); }, async copyPageLink(){ console.log('copyPageLink invoked'); const url = this.pageUrl; const fallback = () => { const ta = document.createElement('textarea'); ta.value = url; ta.style.position='fixed'; ta.style.opacity='0'; document.body.appendChild(ta); ta.select(); document.execCommand('copy'); document.body.removeChild(ta); }; try { await navigator.clipboard.writeText(url); this.showToast('Link copied to clipboard'); } catch(e) { fallback(); this.showToast('Link copied to clipboard'); } }, showToast(msg){ this.toastMsg = msg; this.toastVisible = true; clearTimeout(this._toastTimer); this._toastTimer = setTimeout(() => { this.toastVisible = false; }, 2500); }, publishPage() { var self = this; var csrf = document.cookie.match(/csrf_token=([^;]+)/); fetch('/api/pages/' + this.pid + '/publish', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrf ? csrf[1] : '' } }).then(function(r){ return r.json(); }).then(function(d){ if (d.is_published) { self.pagePublished = true; self.publishedUrl = window.location.origin + '/p/' + d.publish_slug; self.showToast('Published to web — ' + self.publishedUrl); } else { self.showToast(d.detail || 'Publish failed'); } }).catch(function(){ self.showToast('Publish failed'); }); }, unpublishPage() { var self = this; var csrf = document.cookie.match(/csrf_token=([^;]+)/); fetch('/api/pages/' + this.pid + '/publish', { method: 'DELETE', headers: { 'X-CSRF-Token': csrf ? csrf[1] : '' } }).then(function(r){ return r.json(); }).then(function(d){ if (!d.is_published) { self.pagePublished = false; self.publishedUrl = ''; self.showToast('Unpublished'); } else { self.showToast(d.detail || 'Unpublish failed'); } }).catch(function(){ self.showToast('Unpublish failed'); }); }, shareInvite() { var self = this; if (!this.inviteEmail.trim()) return; var csrf = document.cookie.match(/csrf_token=([^;]+)/); fetch('/api/pages/' + this.pid + '/share', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrf ? csrf[1] : '' }, body: JSON.stringify({ email: this.inviteEmail.trim(), permission: this.invitePermission }) }).then(function(r){ return r.json(); }).then(function(d){ if (d.status === 'shared') { self.inviteEmail = ''; self.showToast('Invitation sent'); self.loadShares(); } else { self.showToast(d.detail || 'Share failed'); } }).catch(function(){ self.showToast('Share failed'); }); }, loadShares() { var self = this; fetch('/api/pages/' + this.pid + '/shares').then(function(r){ return r.json(); }).then(function(d){ self.accessList = d.shares || []; }).catch(function(){ self.accessList = []; }); }, removeShare(sid) { var self = this; var csrf = document.cookie.match(/csrf_token=([^;]+)/); fetch('/api/pages/' + this.pid + '/share/' + sid, { method: 'DELETE', headers: { 'X-CSRF-Token': csrf ? csrf[1] : '' } }).then(function(r){ return r.json(); }).then(function(d){ if (d.status === 'removed') { self.showToast('Share removed'); self.loadShares(); } else { self.showToast(d.detail || 'Remove failed'); } }).catch(function(){ self.showToast('Remove failed'); }); }, invitePerson(){if(!this.inviteEmail.trim())return;this.accessList.push({email:this.inviteEmail,permission:this.invitePermission,permOpen:false});this.inviteEmail='';}, removeAccess(email){this.accessList=this.accessList.filter(a=>a.email!==email);}, exportPage(){ this.moreOpen=false; // Export blocks as Markdown var md = '# ' + (this.pageTitle || 'Untitled') + '\n\n'; for (var i = 0; i < this.blocks.length; i++) { var b = this.blocks[i]; md += blocksToMarkdown(b) + '\n\n'; } var blob = new Blob([md], {type: 'text/markdown'}); var url = URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = (this.pageTitle || 'export') + '.md'; a.click(); URL.revokeObjectURL(url); this.showToast('Exported as Markdown'); }, duplicatePage(){this.moreOpen=false;const csrf=document.cookie.match(/csrf_token=([^;]+)/);fetch(`/board/api/pages?title=${encodeURIComponent(this.pageTitle+' copy')}§ion=Private&project=${encodeURIComponent('{{ workspace_key }}')}`,{method:'POST',headers:{'X-CSRF-Token':csrf?csrf[1]:''}}).then(r=>r.json()).then(d=>{window.location.href=`/pages/${d.id}`;}).catch(()=>{window.showToast('Duplicate failed','error');});}, movePage(){ this.moreOpen=false; this.moveOpen = true; this.moveLoading = true; var self = this; fetch('/api/workspaces') .then(function(r){ return r.json(); }) .then(function(d){ self.moveWorkspaces = d.workspaces || []; self.moveLoading = false; }) .catch(function(){ self.moveLoading = false; }); }, doMove(wsId) { this.moveOpen = false; var csrf = document.cookie.match(/csrf_token=([^;]+)/); var self = this; fetch('/api/pages/' + this.pid + '/move', { method: 'PUT', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrf ? csrf[1] : '' }, body: JSON.stringify({ workspace_id: wsId }) }).then(function(r){ return r.json(); }) .then(function(d){ if (d.status === 'ok') { self.showToast('Moved to workspace'); setTimeout(function(){ window.location.href = '/local-workspace'; }, 800); } else { self.showToast(d.detail || 'Move failed'); } }) .catch(function(){ self.showToast('Move failed'); }); }, deletePage(){this.moreOpen=false;if(!confirm('Move to Trash?'))return;const csrf=document.cookie.match(/csrf_token=([^;]+)/);fetch(`/board/api/pages/${this.pid}/trash`,{method:'POST',headers:{'X-CSRF-Token':csrf?csrf[1]:''}}).then(()=>{window.location.href='/';}).catch(()=>{window.showToast('Failed','error');});}, // ── v4.6.0: Get Started actions ── async createDatabase(){ const name=prompt('Database name:',this.pageTitle||'New Database'); if(!name||!name.trim())return; const csrf=document.cookie.match(/csrf_token=([^;]+)/); try{ // Create collection via API const r=await fetch('/db/api',{ method:'POST',headers:{'Content-Type':'application/json','X-CSRF-Token':csrf?csrf[1]:''}, body:JSON.stringify({name:name.trim()}) }); const d=await r.json(); if(d.id){ // Add inline database embed block to the page this.blocks.push(this.mkB('embed','',{embed_type:'collection',collection_id:d.id})); this.render(); this.dirty=true; this.autoSave(); this.showToast('Database created: '+d.name); }else{ this.showToast('Failed to create database'); } }catch(e){this.showToast('Error: '+e.message);} }, async createForm(){ const name=prompt('Form name:','New Form'); if(!name||!name.trim())return; const csrf=document.cookie.match(/csrf_token=([^;]+)/); try{ const r=await fetch('/db/inline/api',{ method:'POST',headers:{'Content-Type':'application/json','X-CSRF-Token':csrf?csrf[1]:''}, body:JSON.stringify({name:name.trim(),parent_page_id:this.pid}) }); const d=await r.json(); if(d.id){ this.inlineDBs.push({id:d.id,name:d.name,view_type:'form'}); this.showToast('Form created — '+d.name); this.saveDBsToPage(); this.renderInlineDBs(); }else{ this.showToast('Failed to create form'); } }catch(e){this.showToast('Error: '+e.message);} }, saveDBsToPage(){ // Store inline DB refs in the page's blocks as a special embed block if(this.inlineDBs.length===0)return; const existing=this.blocks.filter(b=>b.type==='embed'&&b.embed_type==='inline_dbs'); if(existing.length===0){ this.blocks.push(this.mkB('embed','',{embed_type:'inline_dbs',dbs:this.inlineDBs})); }else{ existing.forEach(b=>{b.dbs=this.inlineDBs;}); } this.render(); this.dirty=true;this.autoSave(); }, renderInlineDBs(){ // Re-render with inline DB blocks this.render(); }, loadInlineDBs(){ const existing=this.blocks.filter(b=>b.type==='embed'&&b.embed_type==='inline_dbs'); if(existing.length>0){ this.inlineDBs=existing[0].dbs||[]; } }, onKd(e){ const ab=this.getActiveBlock();if(!ab)return; const{el,bid,idx}=ab;const block=this.blocks[idx];if(!block)return; if(SM.isOpen){ if(e.key==='Escape'){e.preventDefault();SM.close();return;} if(e.key==='ArrowDown'||e.key==='ArrowUp'||e.key==='Enter'){e.preventDefault();SM._in.dispatchEvent(new KeyboardEvent('keydown',{key:e.key,bubbles:true}));return;} if(e.key==='Backspace'&&!SM._q&&(el.textContent||'').trim()==='/'){e.preventDefault();SM.close();return;} return; } if(e.key.length===1||e.key==='Backspace'||e.key==='Delete'){const i2=idx;setTimeout(()=>{const e2=this.getEl(this.blocks[i2]?.id);if(e2&&e2.textContent?.trim()==='/'&&!SM.isOpen)SM.open(i2,e2);},15);} if(e.key===' '){const i2=idx;setTimeout(()=>this.checkMd(i2),15);} if(e.key==='Enter'){ if(e.shiftKey){this.dirty=true;this.autoSave();return;} e.preventDefault(); const text=el.textContent||'',pos=cp(el),before=text.substring(0,pos),after=text.substring(pos); if(!before.trim()&&!after.trim()){ const nt=(block.type==='bulleted_list'||block.type==='numbered_list'||block.type==='to_do')?block.type:'paragraph'; this.sync();this.blocks.splice(idx+1,0,this.mkB(nt,''));this.render(); setTimeout(()=>{const ne=this.getEl(this.blocks[idx+1]?.id);if(ne){ne.focus();ce(ne);}},60); this.dirty=true;this.autoSave();return; } const nt=(block.type==='bulleted_list'||block.type==='numbered_list'||block.type==='to_do')?block.type:'paragraph'; this.sync();block.content=before;this.blocks.splice(idx+1,0,this.mkB(nt,after));this.render(); setTimeout(()=>{const ne=this.getEl(this.blocks[idx+1]?.id);if(ne){ne.focus();cs(ne);}},60); this.dirty=true;this.autoSave();return; } if(e.key==='ArrowUp'){if(cp(el)===0&&idx>0){e.preventDefault();const prev=this.getEl(this.blocks[idx-1]?.id);if(prev){prev.focus();ce(prev);}}return;} if(e.key==='ArrowDown'){if(cp(el)>=(el.textContent||'').length&&idx
{const ne=this.getEl(block.id);if(ne)ne.focus();},60);}else if(this.blocks.length>1){e.preventDefault();this.removeBlock(idx);}}return;} if(e.key===' '){const b2=bid;setTimeout(()=>this.checkMd(b2),15);} if((e.ctrlKey||e.metaKey)&&!e.altKey){const m={b:'bold',i:'italic',u:'underline'};if(m[e.key]){e.preventDefault();document.execCommand(m[e.key]);this.dirty=true;this.autoSave();}} if(e.key==='Tab'&&block.type==='code'){e.preventDefault();document.execCommand('insertText',false,' ');} this.dirty=true;this.autoSave(); }, checkMd(idx){ if(idx<0||idx>=this.blocks.length)return;const block=this.blocks[idx];const el=this.getEl(block.id);if(!el)return;const text=el.textContent||''; const sc={'# ':'heading_1','## ':'heading_2','### ':'heading_3','#### ':'heading_4','- ':'bulleted_list','* ':'bulleted_list','+ ':'bulleted_list','1. ':'numbered_list','[] ':'to_do','[ ] ':'to_do','> ':'quote'}; this.sync(); for(const[pfx,type] of Object.entries(sc)){if(text===pfx||text.startsWith(pfx)){block.type=type;block.content='';if(type==='toggle')block.expanded=true;if(type==='to_do')block.checked=false;this.render();setTimeout(()=>{const ne=this.getEl(block.id);if(ne&&type!=='divider')ne.focus();},60);return;}} if(text==='---'){block.type='divider';block.content='';this.addAfter(idx,'paragraph');} if(text==='```'){block.type='code';block.content='';block.language='Plain Text';this.render();setTimeout(()=>{const ne=this.getEl(block.id);if(ne)ne.focus();},60);} }, applySlash(id){ const idx=SM._i;if(idx<0||idx>=this.blocks.length)return; const block=this.blocks[idx];this.sync();block.content=''; if(id==='divider'){block.type='divider';this.addAfter(idx,'paragraph');} else{block.type=id;if(id==='toggle')block.expanded=true;if(id==='to_do')block.checked=false;if(id==='code')block.language='Plain Text';} this.render();this.dirty=true;this.autoSave(); setTimeout(()=>{const ne=this.getEl(block.id);if(ne)ne.focus();},60); }, _doToggle(bid,v){const idx=this.getIdx(bid);if(idx>=0){this.blocks[idx].checked=v;this.dirty=true;this.autoSave();}}, _doToggleExpand(bid){const idx=this.getIdx(bid);if(idx>=0){this.blocks[idx].expanded=!this.blocks[idx].expanded;this.dirty=true;this.autoSave();}}, showFmt(idx){const s=window.getSelection();if(!s.rangeCount||s.isCollapsed){this.fmt.open=false;return;}const r=s.getRangeAt(0).getBoundingClientRect();this.fmt.open=true;this.fmt.idx=idx;this.fmt.top=Math.max(r.top-44,0);this.fmt.left=Math.min(r.left+r.width/2-120,window.innerWidth-260);this._sel={range:s.getRangeAt(0).cloneRange(),idx};}, fmtApply(f){this.fmt.open=false;if(!this._sel)return;const s=window.getSelection();s.removeAllRanges();s.addRange(this._sel.range);document.execCommand(f==='strikeThrough'?'strikeThrough':f);this._sel=null;this.dirty=true;this.autoSave();}, fmtLink(){this.fmt.open=false;const u=prompt('URL:');if(u){document.execCommand('createLink',false,u);this.dirty=true;this.autoSave();}}, pastePlain(e){e.preventDefault();document.execCommand('insertText',false,(e.clipboardData||window.clipboardData).getData('text/plain'));this.dirty=true;this.autoSave();}, autoSave(){if(this.fileData)return;clearTimeout(this.st);this.st=setTimeout(()=>this.save(),1500);}, save(){ if(this.fileData)return; if(this.saving)return; this.sync(); const t=document.getElementById('_titleEl');if(t)this.pageTitle=t.textContent?.trim()||'New page'; this.saving=true; const csrf=document.cookie.match(/csrf_token=([^;]+)/); fetch(`/board/api/pages/${this.pid}/blocks`,{method:'POST',headers:{'Content-Type':'application/json','X-CSRF-Token':csrf?csrf[1]:''},body:JSON.stringify({title:this.pageTitle,blocks:this.blocks.map(b=>{const c={id:b.id,type:b.type,content:b.content};['checked','expanded','language','icon','src','alt','style'].forEach(k=>{if(b[k]!=null)c[k]=b[k];});return c;})})}) .then(r=>r.json()).then(()=>{this.saving=false;this.dirty=false;this.lastSaved=new Date().toLocaleTimeString(); document.querySelectorAll(`.sidebar-item[data-page-id="page/${this.pid}"]`).forEach(item=>{const n=item.querySelector('.page-name');if(n)n.textContent=this.pageTitle||'New page';}); }).catch(()=>{this.saving=false;}); }, md2b(md){const bl=[];let cb=null;for(const line of md.split('\n')){if(cb){if(line.trim()==='```'){bl.push(cb);cb=null;}else cb.content+=(cb.content?'\n':'')+line;continue;}const t=line.trim();if(!t)continue;if(t.startsWith('```')){cb=this.mkB('code','',{language:t.substring(3).trim()||'Plain Text'});continue;}if(t.startsWith('# ')&&!t.startsWith('## '))bl.push(this.mkB('heading_1',t.substring(2)));else if(t.startsWith('## ')&&!t.startsWith('### '))bl.push(this.mkB('heading_2',t.substring(3)));else if(t.startsWith('### ')&&!t.startsWith('#### '))bl.push(this.mkB('heading_3',t.substring(4)));else if(t.startsWith('#### '))bl.push(this.mkB('heading_4',t.substring(5)));else if(t==='---'||t==='***')bl.push(this.mkB('divider',''));else if(/^[-*+] /.test(t))bl.push(this.mkB('bulleted_list',t.substring(2)));else if(/^\d+\. /.test(t))bl.push(this.mkB('numbered_list',t.replace(/^\d+\. /,'')));else if(t.startsWith('- [ ] ')||t.startsWith('* [ ] '))bl.push(this.mkB('to_do',t.substring(6)));else if(t.startsWith('- [x] ')||t.startsWith('* [x] '))bl.push(this.mkB('to_do',t.substring(6),{checked:true}));else if(t.startsWith('> '))bl.push(this.mkB('quote',t.substring(2)));else bl.push(this.mkB('paragraph',t));}if(cb)bl.push(cb);return bl;}, }; } document.addEventListener('mouseup',()=>{setTimeout(()=>{const s=window.getSelection();if(!s||s.isCollapsed)return;const ed=document.querySelector('.page-editor-wrapper');if(!ed?.__x)return;const d=ed.__x.$data;const ct=document.getElementById('_blocksCt');if(!ct?.contains(s.anchorNode))return;const bel=s.anchorNode.parentElement?.closest('[data-bid]');if(bel){const idx=d.getIdx(bel.dataset.bid);if(idx>=0)d.showFmt(idx);}},80);}); function blocksToMarkdown(b) { var c = b.content || ''; switch(b.type) { case 'heading_1': return '# ' + c; case 'heading_2': return '## ' + c; case 'heading_3': return '### ' + c; case 'heading_4': return '#### ' + c; case 'bulleted_list': return '- ' + c; case 'numbered_list': return '1. ' + c; case 'to_do': return (b.checked ? '- [x] ' : '- [ ] ') + c; case 'quote': return '> ' + c; case 'divider': return '---'; case 'code': return '```' + (b.language || '') + '\n' + c + '\n```'; case 'toggle': return '## ' + c; default: return c; } }