← Dashboard New Page
⚙ Page Settings
Use commas to separate multiple tags
Leave empty to publish immediately when approved
Preview
vigyanllm.in › page-slug Page Title Meta description goes here and will be truncated at about one hundred and sixty characters for the search result snippet.
Word Count0
Characters0
Readability
VigyanLLM Private Limited · Sovereign Research AI WWW.VIGYANLLM.IN `; iframe.srcdoc=srcdoc; iframe.style.width=vpWidth; } function generatePreviewHTML(){ if(!editor)return''; const html=editor.getHTML(); const temp=document.createElement('div'); temp.innerHTML=html; const blocks=Array.from(temp.children); blocks.forEach((el,i)=>{ el.setAttribute('data-pos',i); }); return temp.innerHTML; } function updateSEOPreview(){ const title=document.getElementById('pageMetaTitle').value.trim()||document.getElementById('pageTitle').value.trim()||'Page Title'; const desc=document.getElementById('pageDesc').value.trim()||'Meta description goes here and will be truncated for the search result snippet.'; const slug=document.getElementById('pageSlug').value.trim()||'page-slug'; document.getElementById('seoUrl').textContent='vigyanllm.in › '+slug; document.getElementById('seoTitle').textContent=title; document.getElementById('seoDesc').textContent=desc.length>160?desc.substring(0,157)+'...':desc; } function calcReadability(text){ const words=text.trim().split(/\s+/).filter(w=>w.length>0); if(words.length<10)return '— (too short)'; const sentences=text.split(/[.!?]+/).filter(s=>s.trim().length>0).length||1; const syllableCount=words.reduce((sum,w)=>sum+countSyllables(w),0); const grade=0.39*(words.length/sentences)+11.8*(syllableCount/words.length)-15.59; const g=Math.round(grade*10)/10; let label=''; if(g<=5)label='Very Easy'; else if(g<=7)label='Fairly Easy'; else if(g<=9)label='Standard'; else if(g<=12)label='Fairly Difficult'; else if(g<=15)label='Difficult'; else label='Very Difficult'; return 'Grade '+g+' — '+label; } function countSyllables(word){ word=word.toLowerCase().replace(/[^a-z]/g,''); if(!word)return 0; if(word.length<=3)return 1; let count=0,prevVowel=false; const vowels='aeiouy'; for(let i=0;i1)count--; return Math.max(1,count); } window.addEventListener('message',function(e){ if(e.data.type==='cms-edit-pos'&&editor){ const pos=e.data.pos; const container=document.getElementById('editorContent'); const el=container?.querySelector('[data-pos="'+pos+'"]'); if(el){ el.scrollIntoView({behavior:'smooth',block:'center'}); el.classList.add('highlight-flash'); setTimeout(()=>el.classList.remove('highlight-flash'),1200); const doc=editor.state.doc; let foundPos=0,currentBlock=0; doc.forEach((node,offset)=>{ if(currentBlock===pos){foundPos=offset} currentBlock++; }); if(foundPos>0){ editor.chain().focus().setTextSelection(foundPos).run(); } } } }); document.querySelectorAll('.preview-viewport button').forEach(btn=>{ btn.addEventListener('click',function(){ document.querySelectorAll('.preview-viewport button').forEach(b=>b.classList.remove('is-active')); this.classList.add('is-active'); const iframe=document.getElementById('previewIframe'); iframe.style.width=this.dataset.vp; }); }); function updateStatusBar(p){ const sb=document.getElementById('statusBar'); sb.style.display='flex'; const status=p.status||'draft'; const updated=p.updated_at?new Date(p.updated_at).toLocaleString():'never'; sb.innerHTML='Status: '+status.replace(/_/g,' ')+'Last updated: '+updated+''; } function getContentJSON(){ return editor?editor.getJSON():{type:'doc',content:[{type:'paragraph'}]}; } function validate(){ const title=document.getElementById('pageTitle').value.trim(); const slug=document.getElementById('pageSlug').value.trim(); if(!title){showToast('Page Title is required','error');document.getElementById('pageTitle').focus();return false} if(!slug){showToast('URL Slug is required','error');document.getElementById('pageSlug').focus();return false} if(!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(slug)){showToast('Slug must be lowercase letters, numbers, and hyphens only','error');return false} return true; } function buildPageBody(status){ const sch=document.getElementById('pageSchedule').value; return { title:document.getElementById('pageTitle').value.trim(), slug:document.getElementById('pageSlug').value.trim(), description:document.getElementById('pageDesc').value.trim()||null, meta_title:document.getElementById('pageMetaTitle').value.trim()||null, tags:document.getElementById('pageTags').value.trim()||null, hero_image:document.getElementById('pageHero').value.trim()||null, content_type:document.getElementById('pageType').value, publish_schedule_at:sch?new Date(sch).toISOString():null, content_json:getContentJSON(), status:status||'draft', change_note:status==='pending_review'?'Submitted for review':'Saved as draft' }; } function saveDraft(){ if(!validate())return; const body=buildPageBody('draft'); const method=currentSlug?'PUT':'POST'; const url=currentSlug?API_BASE+'/api/v1/cms/pages/'+currentSlug:API_BASE+'/api/v1/cms/pages'; document.getElementById('editorTitle').textContent='Saving...'; authFetch(url,{method,body:JSON.stringify(body)}).then(r=>{ if(!r.ok)return r.json().then(d=>{throw new Error(d.detail||'Save failed')}); return r.json(); }).then(res=>{ const slug=res?.data?.slug||body.slug; currentSlug=slug; document.getElementById('editorTitle').textContent='Editing: '+document.getElementById('pageTitle').value; window.history.replaceState(null,'','/cms-editor?slug='+slug); const data=res?.data||res; if(data)updateStatusBar(data); showToast('Draft saved successfully','success'); }).catch(e=>{ showToast('Error: '+e.message,'error'); document.getElementById('editorTitle').textContent='Editing: '+document.getElementById('pageTitle').value; }); } function submitReview(){ if(!validate())return; if(!currentSlug){showToast('Save as draft first before submitting for review','error');return} const body=buildPageBody('pending_review'); authFetch(API_BASE+'/api/v1/cms/pages/'+currentSlug,{method:'PUT',body:JSON.stringify(body)}).then(r=>{ if(!r.ok)return r.json().then(d=>{throw new Error(d.detail||'Submit failed')}); return r.json(); }).then(res=>{ showToast('Submitted for review','success'); const data=res?.data||res; if(data)updateStatusBar(data); }).catch(e=>showToast('Error: '+e.message,'error')); } function sanitizeTiptapDoc(doc){ if(!doc||typeof doc!=='object'||!Array.isArray(doc.content))return doc; function cleanNode(node){ if(!node||typeof node!=='object')return null; var out={type:node.type}; if(node.attrs&&Object.keys(node.attrs).length)out.attrs=node.attrs; if(node.marks&&node.marks.length)out.marks=node.marks; if(node.text!==undefined){out.text=node.text;return out} if(node.content){ var cleaned=[]; (node.content||[]).forEach(function(child){ var c=cleanNode(child); if(c)cleaned.push(c); }); if(cleaned.length)out.content=cleaned; } return out; } var cleaned=doc.content.map(cleanNode).filter(Boolean); return {type:'doc',content:cleaned}; } function loadPage(slug){ currentSlug=slug; document.getElementById('editorTitle').textContent='Loading...'; authFetch(API_BASE+'/api/v1/cms/pages/'+slug).then(r=>{ if(!r.ok){window.location.href='/cms-editor';return Promise.reject('Page not found')} return r.json(); }).then(res=>{ const p=res?.data||res; if(!p)return; document.getElementById('pageTitle').value=p.title||''; document.getElementById('pageSlug').value=p.slug||''; document.getElementById('pageDesc').value=p.description||''; document.getElementById('pageMetaTitle').value=p.meta_title||''; document.getElementById('pageTags').value=p.tags||''; document.getElementById('pageType').value=p.content_type||'page'; document.getElementById('pageHero').value=p.hero_image||''; if(p.publish_schedule_at){const d=new Date(p.publish_schedule_at);document.getElementById('pageSchedule').value=d.toISOString().slice(0,16)} document.getElementById('editorTitle').textContent='Editing: '+p.title; updateStatusBar(p); if(editor&&p.content_json&&p.content_json.type==='doc'){ editor.commands.setContent(sanitizeTiptapDoc(p.content_json)); }else if(editor&&p.content_html){ editor.commands.setHTML(p.content_html); } updateSEOPreview(); updateWordCount(); }).catch(e=>{ if(e.message!=='Page not found'){ showToast('Error loading page: '+e.message,'error'); setTimeout(()=>window.location.href='/cms-editor',1500); } }); } function numberEditorBlocks(){ const container=document.getElementById('editorContent'); if(!container)return; const children=Array.from(container.children); children.forEach((el,i)=>{ if(el.tagName!=='DIV'||!el.classList.contains('spinner')){ el.setAttribute('data-pos',i); } }); } function debounce(fn,ms){ let timer; return function(...args){clearTimeout(timer);timer=setTimeout(()=>fn.apply(this,args),ms)} } const debouncedNumberBlocks=debounce(numberEditorBlocks,300); document.getElementById('pageTitle').addEventListener('input',function(){ if(!currentSlug&&!document.getElementById('pageSlug').value){ document.getElementById('pageSlug').value=this.value.toLowerCase().replace(/[^a-z0-9]+/g,'-').replace(/^-|-$/g,''); } updateSEOPreview(); updateWordCount(); }); document.getElementById('pageDesc').addEventListener('input',updateSEOPreview); document.getElementById('pageMetaTitle').addEventListener('input',updateSEOPreview); document.getElementById('pageSlug').addEventListener('input',updateSEOPreview); if(window.MutationObserver){ const observer=new MutationObserver(()=>debouncedNumberBlocks()); document.addEventListener('DOMContentLoaded',()=>{ const container=document.getElementById('editorContent'); if(container){ observer.observe(container,{childList:true,subtree:false,characterData:false}); } }); } document.addEventListener('DOMContentLoaded',()=>{ if(!getToken()){window.location.href='/cms-login';return} initEditor(); // Number blocks after initial render setTimeout(numberEditorBlocks,500); });