File size: 5,442 Bytes
d0681c4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135

    <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>