Spaces:
Sleeping
Sleeping
<div class="visualization-container"> | |
<div class="image-container"> | |
<svg width="{{ width }}" height="{{ height }}" viewBox="0 0 {{ width }} {{ height }}"> | |
<image href="data:image/png;base64,{{ image_base64 }}" width="{{ width }}" height="{{ height }}"/> | |
{% for line in lines %} | |
<a class="textline line{{loop.index}}" onmouseover="document.querySelectorAll('.line{{loop.index}}').forEach(element => {element.classList.add('highlighted')});" onmouseout="document.querySelectorAll('*').forEach(element => {element.classList.remove('highlighted')});"> | |
<path class="line-boundary" d="M {{ line.boundary|join(' L ') }} Z" fill="rgba(0, 128, 255, 0.2)" stroke="none"/> | |
<path class="line-baseline" d="M {{ line.baseline|join(' L ') }}" stroke="red" stroke-width="1" fill="none"/> | |
</a> | |
{% endfor %} | |
</svg> | |
</div> | |
<div class="transcription-container"> | |
{% for line in lines %} | |
<span class="textline line{{loop.index}}" onmouseover="document.querySelectorAll('.line{{loop.index}}').forEach(element => {element.classList.add('highlighted')});" onmouseout="document.querySelectorAll('*').forEach(element => {element.classList.remove('highlighted')});"> | |
<span class="line-number">{{ loop.index }}:</span> | |
<span class="line-text">{{ line.text }}</span> | |
{% if line.confidence %} | |
<span class="line-confidence">({{ "%.2f"|format(line.confidence) }})</span> | |
{% endif %} | |
</span> | |
<br> | |
{% endfor %} | |
</div> | |
</div> | |
<style> | |
.visualization-container { | |
display: flex; | |
gap: 20px; | |
max-height: 1000px; | |
} | |
.image-container { | |
flex: 2; | |
overflow: auto; | |
border: 1px solid #ddd; | |
border-radius: 4px; | |
} | |
.image-container svg { | |
display: block; | |
width: 100%; | |
height: auto; | |
max-width: 100%; | |
} | |
.transcription-container { | |
flex: 1; | |
overflow-y: auto; | |
padding: 10px; | |
border: 1px solid #ddd; | |
border-radius: 4px; | |
} | |
/* Synchronize scrolling between containers */ | |
.image-container, .transcription-container { | |
scroll-behavior: smooth; | |
} | |
.image-container::-webkit-scrollbar, .transcription-container::-webkit-scrollbar { | |
width: 8px; | |
} | |
.image-container::-webkit-scrollbar-track, .transcription-container::-webkit-scrollbar-track { | |
background: #f1f1f1; | |
} | |
.image-container::-webkit-scrollbar-thumb, .transcription-container::-webkit-scrollbar-thumb { | |
background: #888; | |
border-radius: 4px; | |
} | |
.image-container::-webkit-scrollbar-thumb:hover, .transcription-container::-webkit-scrollbar-thumb:hover { | |
background: #555; | |
} | |
.textline { | |
padding: 5px; | |
cursor: pointer; | |
display: inline-block; | |
unicode-bidi: bidi-override; | |
} | |
.textline:hover, | |
.textline.highlighted { | |
background-color: rgba(0, 128, 255, 0.1); | |
} | |
.textline:hover .line-boundary, | |
.textline.highlighted .line-boundary { | |
fill: rgba(0, 255, 255, 0.3); | |
} | |
.textline:hover .line-baseline, | |
.textline.highlighted .line-baseline { | |
stroke: yellow; | |
} | |
.line-number { | |
color: #666; | |
margin-right: 5px; | |
} | |
.line-confidence { | |
color: #888; | |
font-size: 0.9em; | |
margin-left: 5px; | |
} | |
/* RTL text support */ | |
.textline[dir="rtl"] { | |
text-align: right; | |
} | |
.textline[dir="ltr"] { | |
text-align: left; | |
} | |
</style> | |
<script> | |
// Synchronize scrolling between containers | |
const imageContainer = document.querySelector('.image-container'); | |
const textContainer = document.querySelector('.transcription-container'); | |
function syncScroll(source, target) { | |
const ratio = target.scrollHeight / source.scrollHeight; | |
target.scrollTop = source.scrollTop * ratio; | |
} | |
imageContainer.addEventListener('scroll', () => syncScroll(imageContainer, textContainer)); | |
textContainer.addEventListener('scroll', () => syncScroll(textContainer, imageContainer)); | |
// Function to detect text direction | |
function detectTextDirection(text) { | |
const rtlChars = /[֑-߿יִ-﷽ﹰ-ﻼ]/; | |
return rtlChars.test(text) ? 'rtl' : 'ltr'; | |
} | |
// Add direction attribute to text lines | |
function updateTextDirections() { | |
document.querySelectorAll('.textline').forEach(line => { | |
const text = line.textContent; | |
line.setAttribute('dir', detectTextDirection(text)); | |
}); | |
} | |
// Update text directions when visualization changes | |
const observer = new MutationObserver(updateTextDirections); | |
observer.observe(document.body, { childList: true, subtree: true }); | |
</script> | |