Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
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.

markdown-preview.tsBlame62 lines · 1 contributor
6cd2f0eClaude1/**
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
11export 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}