Drama-3 / studio.js
zhaochenyang20's picture andito's picture
andito HF Staff
Replace ZeroGPU demo with Gradio studio and H200 endpoint (#6)
e9cf2ae
Raw History Blame Contribute Delete
3.66 kB
() => {
const applyTheme = dark => {
document.body.classList.toggle('dark', dark);
const frame = document.querySelector('iframe[title="Drama 3 Workflow"]');
try { frame?.contentDocument?.body.classList.toggle('dark', dark); } catch {}
};
try { const saved = localStorage.getItem('drama3:theme'); if (saved) applyTheme(saved === 'dark'); } catch {}
window.dramaToggleTheme = () => {
const dark = !document.body.classList.contains('dark');
applyTheme(dark);
try { localStorage.setItem('drama3:theme', dark ? 'dark' : 'light'); } catch {}
};
window.dramaInsert = (text, tag, kind) => {
const field = document.querySelector('#script textarea');
const start = field?.selectionStart ?? text.length;
const end = field?.selectionEnd ?? start;
const selected = text.slice(start, end);
let snippet = kind === 'scope' ? `<${tag}>${selected || 'your words here'}</${tag}>` : `[${tag}]`;
if (kind === 'mood') return snippet + ' ' + text.replace(/^\[[^\]]+\]\s*/, '');
const before = text.slice(0, start), after = text.slice(end);
return before + (before && !/\s$/.test(before) ? ' ' : '') + snippet +
(after && !/^\s/.test(after) ? ' ' : '') + after;
};
const escape = text => text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
const highlight = text => {
let result = escape(text);
result = result.replace(/(^|\n)(Direction:|Line:)/g, '$1<span class="h-note">$2</span>');
result = result.replace(/&lt;(\/?[A-Za-z][\w-]*)&gt;/g, '<span class="h-tag">&lt;$1&gt;</span>');
return result.replace(/\[([^\[\]\n]*)\]/g, (match, inner) =>
`<span class="${inner.trim().split(/\s+/).length >= 3 || inner === 'icy calm' ? 'h-style' : 'h-point'}">${match}</span>`);
};
const installMirror = () => {
const field = document.querySelector('#script textarea');
if (!field || field.dataset.dramaMirror) return;
field.dataset.dramaMirror = 'true';
const wrapper = field.parentElement;
const mirror = document.createElement('div');
mirror.className = 'drama-script-mirror'; mirror.setAttribute('aria-hidden', 'true');
wrapper.style.position = 'relative'; wrapper.append(mirror);
field.classList.add('drama-highlighted');
const render = () => {
const style = getComputedStyle(field);
Object.assign(mirror.style, {left:field.offsetLeft+'px',top:field.offsetTop+'px',width:field.offsetWidth+'px',height:field.offsetHeight+'px',
fontFamily:style.fontFamily,fontSize:style.fontSize,fontWeight:style.fontWeight,lineHeight:style.lineHeight,letterSpacing:style.letterSpacing,
padding:style.padding,border:style.borderWidth+' solid transparent',borderRadius:style.borderRadius});
mirror.innerHTML = highlight(field.value) + '\n\u200b'; mirror.scrollTop = field.scrollTop;
};
field.addEventListener('input', render); field.addEventListener('scroll', render);
new ResizeObserver(render).observe(field);
// Gradio can replace the textbox value without firing a DOM input event.
let previous = field.value;
const tick = setInterval(() => {
if (!field.isConnected) { clearInterval(tick); return; }
if (previous !== field.value) { previous = field.value; render(); }
}, 100);
render();
};
new MutationObserver(installMirror).observe(document.body, {childList:true,subtree:true});
installMirror();
document.addEventListener('keydown', event => {
if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {
event.preventDefault(); document.querySelector(event.target.closest('#stage-note') ? '#retake' : '#perform')?.click();
}
});
}