CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history
markdown-preview.ts
Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.
| 6cd2f0e | 1 | /** |
| 2 | * Write/Preview tab toggle for Markdown comment textareas. | |
| 3 | * | |
| 4 | * Upgrades every `<textarea data-md-preview>` on the page with a small | |
| 5 | * tab strip above it. "Write" shows the textarea, "Preview" replaces it | |
| 6 | * with a rendered HTML panel fetched from POST /api/markdown/preview. | |
| 7 | * | |
| 8 | * Call `markdownPreviewScript()` and inject into a `<script dangerouslySetInnerHTML>`. | |
| 9 | */ | |
| 10 | ||
| 11 | export function markdownPreviewScript(): string { | |
| 12 | return `(function(){ | |
| 13 | var CSS=[ | |
| 14 | '.mdpv-wrap{position:relative;}', | |
| 15 | '.mdpv-tabs{display:flex;gap:0;margin-bottom:0;border-bottom:1px solid var(--border,#30363d);}', | |
| 16 | '.mdpv-tab{padding:6px 14px;font-size:12.5px;font-weight:600;cursor:pointer;background:none;border:none;color:var(--text-muted,#8b949e);border-bottom:2px solid transparent;margin-bottom:-1px;transition:color 120ms;}', | |
| 17 | '.mdpv-tab:hover{color:var(--text,#e6edf3);}', | |
| 18 | '.mdpv-tab.is-active{color:var(--text,#e6edf3);border-bottom-color:var(--accent,#58a6ff);}', | |
| 19 | '.mdpv-preview{min-height:80px;padding:10px 12px;border:1px solid var(--border,#30363d);border-top:none;border-radius:0 0 8px 8px;background:var(--bg-secondary,#161b22);font-size:14px;line-height:1.6;color:var(--text,#e6edf3);overflow-x:auto;}', | |
| 20 | '.mdpv-preview-loading{opacity:0.5;font-style:italic;font-size:13px;}', | |
| 21 | ].join(''); | |
| 22 | function injectStyle(){if(!document.getElementById('mdpv-style')){var s=document.createElement('style');s.id='mdpv-style';s.textContent=CSS;document.head.appendChild(s);}} | |
| 23 | function upgrade(ta){ | |
| 24 | if(ta._mdpvDone)return;ta._mdpvDone=true; | |
| 25 | injectStyle(); | |
| 26 | var parent=ta.parentElement; | |
| 27 | if(!parent)return; | |
| 28 | // Wrap textarea | |
| 29 | var wrap=document.createElement('div');wrap.className='mdpv-wrap'; | |
| 30 | parent.insertBefore(wrap,ta); | |
| 31 | wrap.appendChild(ta); | |
| 32 | // Tab strip | |
| 33 | var tabs=document.createElement('div');tabs.className='mdpv-tabs'; | |
| 34 | var writeTab=document.createElement('button');writeTab.type='button';writeTab.className='mdpv-tab is-active';writeTab.textContent='Write'; | |
| 35 | var previewTab=document.createElement('button');previewTab.type='button';previewTab.className='mdpv-tab';previewTab.textContent='Preview'; | |
| 36 | tabs.appendChild(writeTab);tabs.appendChild(previewTab); | |
| 37 | wrap.insertBefore(tabs,ta); | |
| 38 | // Preview panel | |
| 39 | var panel=document.createElement('div');panel.className='mdpv-preview';panel.style.display='none'; | |
| 40 | wrap.appendChild(panel); | |
| 41 | // Switch to write | |
| 42 | writeTab.addEventListener('click',function(){ | |
| 43 | writeTab.classList.add('is-active');previewTab.classList.remove('is-active'); | |
| 44 | ta.style.display='';panel.style.display='none'; | |
| 45 | }); | |
| 46 | // Switch to preview | |
| 47 | previewTab.addEventListener('click',function(){ | |
| 48 | previewTab.classList.add('is-active');writeTab.classList.remove('is-active'); | |
| 49 | ta.style.display='none';panel.style.display=''; | |
| 50 | panel.innerHTML='<span class="mdpv-preview-loading">Rendering…</span>'; | |
| 51 | fetch('/api/markdown/preview',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({text:ta.value})}) | |
| 52 | .then(function(r){return r.json();}) | |
| 53 | .then(function(d){panel.innerHTML=d.html||'<em style="opacity:.5">Nothing to preview</em>';}) | |
| 54 | .catch(function(){panel.innerHTML='<em style="opacity:.5">Preview unavailable</em>';}); | |
| 55 | }); | |
| 56 | } | |
| 57 | function scan(){document.querySelectorAll('textarea[data-md-preview]').forEach(upgrade);} | |
| 58 | scan(); | |
| 59 | var ob=new MutationObserver(function(){scan();}); | |
| 60 | ob.observe(document.body,{childList:true,subtree:true}); | |
| 61 | })();`; | |
| 62 | } |