Update src/streamlit_app.py
Browse files- src/streamlit_app.py +178 -19
src/streamlit_app.py
CHANGED
|
@@ -39,32 +39,62 @@ st.markdown("""
|
|
| 39 |
|
| 40 |
.word-highlight {
|
| 41 |
display: inline-block;
|
| 42 |
-
padding:
|
| 43 |
-
margin: 0
|
| 44 |
border-radius: 4px;
|
| 45 |
-
transition: all 0.
|
| 46 |
cursor: pointer;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
}
|
| 48 |
|
| 49 |
.pronunciation-word {
|
| 50 |
display: inline-block;
|
| 51 |
-
padding:
|
| 52 |
-
margin: 0
|
| 53 |
border-radius: 4px;
|
| 54 |
-
transition: all 0.
|
| 55 |
cursor: pointer;
|
| 56 |
font-family: 'Courier New', monospace;
|
| 57 |
letter-spacing: 1px;
|
|
|
|
|
|
|
| 58 |
}
|
| 59 |
|
| 60 |
-
.
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
.
|
| 67 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
|
| 69 |
.pronunciation-separator {
|
| 70 |
color: #a777e3;
|
|
@@ -91,7 +121,8 @@ st.markdown("""
|
|
| 91 |
|
| 92 |
.results-text {
|
| 93 |
font-size: 1.1rem;
|
| 94 |
-
line-height: 1.
|
|
|
|
| 95 |
}
|
| 96 |
|
| 97 |
.sample-buttons {
|
|
@@ -141,7 +172,73 @@ st.markdown("""
|
|
| 141 |
text-transform: uppercase;
|
| 142 |
letter-spacing: 1px;
|
| 143 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
</style>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 145 |
""", unsafe_allow_html=True)
|
| 146 |
|
| 147 |
class PronunciationAnalyzer:
|
|
@@ -266,7 +363,7 @@ def render_highlighted_text(word_syllables: List[Tuple[str, List[str]]], analyze
|
|
| 266 |
for word, syllables in word_syllables:
|
| 267 |
if any(c.isalnum() for c in word):
|
| 268 |
color_class = analyzer.color_classes[word_index % len(analyzer.color_classes)]
|
| 269 |
-
html_parts.append(f'<span class="word-highlight {color_class}">{word}</span>')
|
| 270 |
word_index += 1
|
| 271 |
else:
|
| 272 |
html_parts.append(word)
|
|
@@ -288,7 +385,7 @@ def render_pronunciation(word_syllables: List[Tuple[str, List[str]]], analyzer:
|
|
| 288 |
|
| 289 |
# Join syllables with dots
|
| 290 |
syllable_text = '<span class="pronunciation-separator">ยท</span>'.join(syllables)
|
| 291 |
-
html_parts.append(f'<span class="pronunciation-word {color_class}">{syllable_text}</span>')
|
| 292 |
word_index += 1
|
| 293 |
else:
|
| 294 |
html_parts.append(f'<span class="pronunciation-word">{word}</span>')
|
|
@@ -398,12 +495,74 @@ def main():
|
|
| 398 |
# Original text with highlights
|
| 399 |
st.markdown("### ๐ Original Text")
|
| 400 |
original_html = render_highlighted_text(word_syllables, analyzer)
|
| 401 |
-
st.markdown(f'<div class="analysis-card"><div class="results-text">{original_html}</div></div>', unsafe_allow_html=True)
|
| 402 |
|
| 403 |
# Pronunciation breakdown
|
| 404 |
st.markdown("### ๐ค Pronunciation Breakdown")
|
| 405 |
pronunciation_html = render_pronunciation(word_syllables, analyzer)
|
| 406 |
-
st.markdown(f'<div class="analysis-card"><div class="results-text">{pronunciation_html}</div></div>', unsafe_allow_html=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 407 |
|
| 408 |
# Word-by-word breakdown
|
| 409 |
st.markdown("### ๐ Word-by-Word Analysis")
|
|
|
|
| 39 |
|
| 40 |
.word-highlight {
|
| 41 |
display: inline-block;
|
| 42 |
+
padding: 2px 4px;
|
| 43 |
+
margin: 0 1px;
|
| 44 |
border-radius: 4px;
|
| 45 |
+
transition: all 0.3s ease;
|
| 46 |
cursor: pointer;
|
| 47 |
+
color: #666;
|
| 48 |
+
background-color: transparent;
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
.word-highlight:hover {
|
| 52 |
+
transform: translateY(-1px);
|
| 53 |
+
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
| 54 |
}
|
| 55 |
|
| 56 |
.pronunciation-word {
|
| 57 |
display: inline-block;
|
| 58 |
+
padding: 2px 4px;
|
| 59 |
+
margin: 0 1px;
|
| 60 |
border-radius: 4px;
|
| 61 |
+
transition: all 0.3s ease;
|
| 62 |
cursor: pointer;
|
| 63 |
font-family: 'Courier New', monospace;
|
| 64 |
letter-spacing: 1px;
|
| 65 |
+
color: #666;
|
| 66 |
+
background-color: transparent;
|
| 67 |
}
|
| 68 |
|
| 69 |
+
.pronunciation-word:hover {
|
| 70 |
+
transform: translateY(-1px);
|
| 71 |
+
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
/* Active states when hovered/linked */
|
| 75 |
+
.word-active {
|
| 76 |
+
background-color: rgba(110, 142, 251, 0.2) !important;
|
| 77 |
+
color: #6e8efb !important;
|
| 78 |
+
transform: translateY(-1px);
|
| 79 |
+
box-shadow: 0 2px 12px rgba(110, 142, 251, 0.3);
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
.pronunciation-active {
|
| 83 |
+
background-color: rgba(110, 142, 251, 0.2) !important;
|
| 84 |
+
color: #6e8efb !important;
|
| 85 |
+
transform: translateY(-1px);
|
| 86 |
+
box-shadow: 0 2px 12px rgba(110, 142, 251, 0.3);
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
/* Color classes - only used for subtle borders */
|
| 90 |
+
.color-1 { border-left: 3px solid #6e8efb; }
|
| 91 |
+
.color-2 { border-left: 3px solid #a777e3; }
|
| 92 |
+
.color-3 { border-left: 3px solid #4facfe; }
|
| 93 |
+
.color-4 { border-left: 3px solid #00f2fe; }
|
| 94 |
+
.color-5 { border-left: 3px solid #43e97b; }
|
| 95 |
+
.color-6 { border-left: 3px solid #38f9d7; }
|
| 96 |
+
.color-7 { border-left: 3px solid #fa709a; }
|
| 97 |
+
.color-8 { border-left: 3px solid #b19709; }
|
| 98 |
|
| 99 |
.pronunciation-separator {
|
| 100 |
color: #a777e3;
|
|
|
|
| 121 |
|
| 122 |
.results-text {
|
| 123 |
font-size: 1.1rem;
|
| 124 |
+
line-height: 1.8;
|
| 125 |
+
color: #666;
|
| 126 |
}
|
| 127 |
|
| 128 |
.sample-buttons {
|
|
|
|
| 172 |
text-transform: uppercase;
|
| 173 |
letter-spacing: 1px;
|
| 174 |
}
|
| 175 |
+
|
| 176 |
+
/* Interactive hover JavaScript */
|
| 177 |
+
.hover-container {
|
| 178 |
+
position: relative;
|
| 179 |
+
}
|
| 180 |
</style>
|
| 181 |
+
|
| 182 |
+
<script>
|
| 183 |
+
function addHoverInteraction() {
|
| 184 |
+
// Add event listeners for word-syllable linking
|
| 185 |
+
setTimeout(() => {
|
| 186 |
+
const words = document.querySelectorAll('.word-highlight');
|
| 187 |
+
const pronunciations = document.querySelectorAll('.pronunciation-word');
|
| 188 |
+
|
| 189 |
+
// Clear any existing listeners
|
| 190 |
+
words.forEach(word => {
|
| 191 |
+
word.replaceWith(word.cloneNode(true));
|
| 192 |
+
});
|
| 193 |
+
pronunciations.forEach(pron => {
|
| 194 |
+
pron.replaceWith(pron.cloneNode(true));
|
| 195 |
+
});
|
| 196 |
+
|
| 197 |
+
// Re-select after cloning
|
| 198 |
+
const newWords = document.querySelectorAll('.word-highlight');
|
| 199 |
+
const newPronunciations = document.querySelectorAll('.pronunciation-word');
|
| 200 |
+
|
| 201 |
+
newWords.forEach((word, index) => {
|
| 202 |
+
word.addEventListener('mouseenter', () => {
|
| 203 |
+
// Highlight corresponding pronunciation
|
| 204 |
+
word.classList.add('word-active');
|
| 205 |
+
if (newPronunciations[index]) {
|
| 206 |
+
newPronunciations[index].classList.add('pronunciation-active');
|
| 207 |
+
}
|
| 208 |
+
});
|
| 209 |
+
|
| 210 |
+
word.addEventListener('mouseleave', () => {
|
| 211 |
+
// Remove highlights
|
| 212 |
+
word.classList.remove('word-active');
|
| 213 |
+
if (newPronunciations[index]) {
|
| 214 |
+
newPronunciations[index].classList.remove('pronunciation-active');
|
| 215 |
+
}
|
| 216 |
+
});
|
| 217 |
+
});
|
| 218 |
+
|
| 219 |
+
newPronunciations.forEach((pron, index) => {
|
| 220 |
+
pron.addEventListener('mouseenter', () => {
|
| 221 |
+
// Highlight corresponding word
|
| 222 |
+
pron.classList.add('pronunciation-active');
|
| 223 |
+
if (newWords[index]) {
|
| 224 |
+
newWords[index].classList.add('word-active');
|
| 225 |
+
}
|
| 226 |
+
});
|
| 227 |
+
|
| 228 |
+
pron.addEventListener('mouseleave', () => {
|
| 229 |
+
// Remove highlights
|
| 230 |
+
pron.classList.remove('pronunciation-active');
|
| 231 |
+
if (newWords[index]) {
|
| 232 |
+
newWords[index].classList.remove('word-active');
|
| 233 |
+
}
|
| 234 |
+
});
|
| 235 |
+
});
|
| 236 |
+
}, 100);
|
| 237 |
+
}
|
| 238 |
+
|
| 239 |
+
// Run the interaction setup when page loads
|
| 240 |
+
document.addEventListener('DOMContentLoaded', addHoverInteraction);
|
| 241 |
+
</script>
|
| 242 |
""", unsafe_allow_html=True)
|
| 243 |
|
| 244 |
class PronunciationAnalyzer:
|
|
|
|
| 363 |
for word, syllables in word_syllables:
|
| 364 |
if any(c.isalnum() for c in word):
|
| 365 |
color_class = analyzer.color_classes[word_index % len(analyzer.color_classes)]
|
| 366 |
+
html_parts.append(f'<span class="word-highlight {color_class}" data-word-index="{word_index}">{word}</span>')
|
| 367 |
word_index += 1
|
| 368 |
else:
|
| 369 |
html_parts.append(word)
|
|
|
|
| 385 |
|
| 386 |
# Join syllables with dots
|
| 387 |
syllable_text = '<span class="pronunciation-separator">ยท</span>'.join(syllables)
|
| 388 |
+
html_parts.append(f'<span class="pronunciation-word {color_class}" data-word-index="{word_index}">{syllable_text}</span>')
|
| 389 |
word_index += 1
|
| 390 |
else:
|
| 391 |
html_parts.append(f'<span class="pronunciation-word">{word}</span>')
|
|
|
|
| 495 |
# Original text with highlights
|
| 496 |
st.markdown("### ๐ Original Text")
|
| 497 |
original_html = render_highlighted_text(word_syllables, analyzer)
|
| 498 |
+
st.markdown(f'<div class="analysis-card hover-container"><div class="results-text">{original_html}</div></div>', unsafe_allow_html=True)
|
| 499 |
|
| 500 |
# Pronunciation breakdown
|
| 501 |
st.markdown("### ๐ค Pronunciation Breakdown")
|
| 502 |
pronunciation_html = render_pronunciation(word_syllables, analyzer)
|
| 503 |
+
st.markdown(f'<div class="analysis-card hover-container"><div class="results-text">{pronunciation_html}</div></div>', unsafe_allow_html=True)
|
| 504 |
+
|
| 505 |
+
# Add JavaScript for hover interaction
|
| 506 |
+
st.components.v1.html("""
|
| 507 |
+
<script>
|
| 508 |
+
function addHoverInteraction() {
|
| 509 |
+
const words = parent.document.querySelectorAll('.word-highlight');
|
| 510 |
+
const pronunciations = parent.document.querySelectorAll('.pronunciation-word');
|
| 511 |
+
|
| 512 |
+
// Clear existing event listeners by cloning nodes
|
| 513 |
+
words.forEach((word, index) => {
|
| 514 |
+
const wordIndex = word.getAttribute('data-word-index');
|
| 515 |
+
if (wordIndex !== null) {
|
| 516 |
+
const newWord = word.cloneNode(true);
|
| 517 |
+
word.parentNode.replaceChild(newWord, word);
|
| 518 |
+
|
| 519 |
+
newWord.addEventListener('mouseenter', () => {
|
| 520 |
+
newWord.classList.add('word-active');
|
| 521 |
+
const correspondingPron = parent.document.querySelector(`[data-word-index="${wordIndex}"].pronunciation-word`);
|
| 522 |
+
if (correspondingPron) {
|
| 523 |
+
correspondingPron.classList.add('pronunciation-active');
|
| 524 |
+
}
|
| 525 |
+
});
|
| 526 |
+
|
| 527 |
+
newWord.addEventListener('mouseleave', () => {
|
| 528 |
+
newWord.classList.remove('word-active');
|
| 529 |
+
const correspondingPron = parent.document.querySelector(`[data-word-index="${wordIndex}"].pronunciation-word`);
|
| 530 |
+
if (correspondingPron) {
|
| 531 |
+
correspondingPron.classList.remove('pronunciation-active');
|
| 532 |
+
}
|
| 533 |
+
});
|
| 534 |
+
}
|
| 535 |
+
});
|
| 536 |
+
|
| 537 |
+
pronunciations.forEach((pron, index) => {
|
| 538 |
+
const wordIndex = pron.getAttribute('data-word-index');
|
| 539 |
+
if (wordIndex !== null) {
|
| 540 |
+
const newPron = pron.cloneNode(true);
|
| 541 |
+
pron.parentNode.replaceChild(newPron, pron);
|
| 542 |
+
|
| 543 |
+
newPron.addEventListener('mouseenter', () => {
|
| 544 |
+
newPron.classList.add('pronunciation-active');
|
| 545 |
+
const correspondingWord = parent.document.querySelector(`[data-word-index="${wordIndex}"].word-highlight`);
|
| 546 |
+
if (correspondingWord) {
|
| 547 |
+
correspondingWord.classList.add('word-active');
|
| 548 |
+
}
|
| 549 |
+
});
|
| 550 |
+
|
| 551 |
+
newPron.addEventListener('mouseleave', () => {
|
| 552 |
+
newPron.classList.remove('pronunciation-active');
|
| 553 |
+
const correspondingWord = parent.document.querySelector(`[data-word-index="${wordIndex}"].word-highlight`);
|
| 554 |
+
if (correspondingWord) {
|
| 555 |
+
correspondingWord.classList.remove('word-active');
|
| 556 |
+
}
|
| 557 |
+
});
|
| 558 |
+
}
|
| 559 |
+
});
|
| 560 |
+
}
|
| 561 |
+
|
| 562 |
+
// Run interaction setup
|
| 563 |
+
setTimeout(addHoverInteraction, 300);
|
| 564 |
+
</script>
|
| 565 |
+
""", height=0)
|
| 566 |
|
| 567 |
# Word-by-word breakdown
|
| 568 |
st.markdown("### ๐ Word-by-Word Analysis")
|