aghilTQ commited on
Commit
2d1876f
Β·
verified Β·
1 Parent(s): fde3ffd

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +376 -148
src/streamlit_app.py CHANGED
@@ -1,191 +1,419 @@
1
  import streamlit as st
2
  import pyphen
3
  import re
4
- import random
5
  from typing import List, Tuple
 
 
 
6
 
7
- # Configure page
 
 
 
 
 
 
 
 
 
8
  st.set_page_config(
9
- page_title="Text Syllabification Tool",
10
- page_icon="πŸ”€",
11
  layout="wide",
12
  initial_sidebar_state="collapsed"
13
  )
14
 
15
- # Initialize pyphen dictionary for English
16
- @st.cache_resource
17
- def get_pyphen_dict():
18
- return pyphen.Pyphen(lang='en')
19
-
20
- def generate_color_palette(num_colors: int) -> List[str]:
21
- """Generate gray shades for syllables"""
22
- # Use different shades of gray instead of bright colors
23
- base_grays = [
24
- "#2C3E50", "#34495E", "#5D6D7E", "#85929E", "#566573",
25
- "#515A5A", "#626567", "#797D7F", "#922B21", "#A93226",
26
- "#B7472A", "#C0392B", "#CD6155", "#D98880", "#E8DAEF",
27
- "#D7BDE2", "#BB8FCE", "#A569BD", "#8E44AD", "#7D3C98"
28
- ]
29
 
30
- # Generate more gray shades if needed
31
- while len(base_grays) < num_colors:
32
- # Generate random gray shades
33
- gray_val = random.randint(60, 140)
34
- color = f"#{gray_val:02x}{gray_val:02x}{gray_val:02x}"
35
- base_grays.append(color)
36
 
37
- return base_grays[:num_colors]
38
-
39
- def syllabify_word(word: str, pyphen_dict) -> List[str]:
40
- """Syllabify a single word using pyphen"""
41
- # Clean the word of punctuation for syllabification
42
- clean_word = re.sub(r'[^\w]', '', word)
43
-
44
- if not clean_word:
45
- return [word]
46
-
47
- try:
48
- # Use pyphen to get syllables
49
- syllabified = pyphen_dict.inserted(clean_word.lower())
50
- syllables = syllabified.split('-')
51
-
52
- # If no syllables found (single syllable word), return the word
53
- if len(syllables) == 1 and syllables[0] == clean_word.lower():
54
- return [clean_word]
55
-
56
- # Handle case preservation
57
- if clean_word.isupper():
58
- syllables = [syl.upper() for syl in syllables]
59
- elif clean_word[0].isupper():
60
- for i, syl in enumerate(syllables):
61
- if i == 0:
62
- syllables[i] = syl.capitalize()
63
- else:
64
- syllables[i] = syl.lower()
65
-
66
- return syllables if syllables else [clean_word]
67
- except:
68
- return [clean_word]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
 
70
- def process_text(text: str) -> List[Tuple[str, List[Tuple[str, str]]]]:
71
- """Process text and return syllable breakdown only"""
72
- pyphen_dict = get_pyphen_dict()
 
 
 
 
73
 
74
- # Split into words
75
- words = re.findall(r'\b\w+\b', text)
76
- syllable_breakdown = []
 
 
 
 
 
 
 
 
 
 
 
 
 
77
 
78
- for word in words:
79
- syllables_list = syllabify_word(word, pyphen_dict)
 
 
 
80
 
81
- # Generate gray colors for this word's syllables
82
- colors = generate_color_palette(len(syllables_list))
 
 
 
 
 
 
 
 
83
 
84
- # Add to syllable breakdown
85
- syllable_colored = [(syl, colors[i % len(colors)]) for i, syl in enumerate(syllables_list)]
86
- syllable_breakdown.append((word, syllable_colored))
 
 
 
 
 
 
 
87
 
88
- return syllable_breakdown
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
 
90
- def display_colored_text(colored_words: List[Tuple[str, str]]):
91
- """Display text with colored words"""
92
  html_parts = []
93
- for word, color in colored_words:
94
- if word.strip():
95
- html_parts.append(f'<span style="color: {color}; font-weight: bold; font-size: 16px;">{word}</span>')
 
 
 
 
96
  else:
97
  html_parts.append(word)
 
 
 
 
98
 
99
- html_content = ''.join(html_parts)
100
- st.markdown(f'<div style="font-size: 16px; line-height: 1.6; padding: 15px; border: 2px solid #ddd; border-radius: 8px; background-color: #f9f9f9;">{html_content}</div>', unsafe_allow_html=True)
101
 
102
- def display_syllable_breakdown(syllable_breakdown: List[Tuple[str, List[Tuple[str, str]]]]):
103
- """Display syllable breakdown with hyphens"""
104
  html_parts = []
 
105
 
106
- for word, syllables in syllable_breakdown:
107
- if syllables and len(syllables) > 0:
108
- # Create hyphenated syllable display
109
- syllable_spans = []
110
- for syl, color in syllables:
111
- syllable_spans.append(f'<span style="color: {color}; font-weight: bold; font-size: 16px;">{syl}</span>')
112
 
113
- # Join with hyphens for multi-syllable words
114
- if len(syllables) > 1:
115
- syllable_display = '<span style="color: #666; font-weight: bold;">-</span>'.join(syllable_spans)
116
- else:
117
- syllable_display = syllable_spans[0]
118
-
119
- html_parts.append(f'<span style="margin-right: 12px; display: inline-block; margin-bottom: 6px;">{syllable_display}</span>')
 
 
 
120
 
121
- if html_parts:
122
- html_content = ''.join(html_parts)
123
- st.markdown(f'<div style="font-size: 16px; line-height: 1.6; padding: 15px; border: 2px solid #ddd; border-radius: 8px; background-color: #f0f8ff;">{html_content}</div>', unsafe_allow_html=True)
124
 
125
- # Main app
126
  def main():
127
- st.title("πŸ”€ Text Syllabification Tool")
128
- st.markdown("*Enter text to see accurate syllable breakdown with color coding*")
129
 
130
- # Input text area
131
- input_text = st.text_area(
132
- "Enter your text:",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  placeholder="Type or paste your text here...",
134
- height=100,
135
- help="Enter any text to see how words are broken down into syllables"
136
  )
137
 
138
- # Submit button
139
- submit_clicked = st.button("πŸ” Submit", type="primary", use_container_width=True)
 
140
 
141
- if submit_clicked and input_text.strip():
142
- # Process the text
143
- syllable_breakdown = process_text(input_text)
144
-
145
- # Display syllable breakdown
146
- st.subheader("Syllable Breakdown")
147
- display_syllable_breakdown(syllable_breakdown)
148
-
149
- # Statistics
150
- total_words = len(syllable_breakdown)
151
- total_syllables = sum(len(syls) for _, syls in syllable_breakdown)
152
-
153
- st.markdown(f"**πŸ“Š {total_words} words β€’ {total_syllables} syllables**")
154
-
155
- # Show syllable counts in a more compact way
156
- if syllable_breakdown:
157
- with st.expander("πŸ“ˆ Syllable Counts per Word", expanded=False):
158
- counts_text = " β€’ ".join([f"{word}: {len(syllables)}" for word, syllables in syllable_breakdown])
159
- st.markdown(f"<small>{counts_text}</small>", unsafe_allow_html=True)
160
 
161
- elif submit_clicked and not input_text.strip():
162
- st.warning("⚠️ Please enter some text first!")
 
 
163
 
164
- elif not submit_clicked:
165
- st.info("πŸ‘† Enter some text above and click Submit to see the syllabification!")
 
 
166
 
167
- # Example
168
- st.subheader("Example")
169
- example_text = "The preparation of abstracts is an intellectual effort requiring general familiarity."
170
- st.code(example_text)
171
-
172
- if st.button("Try Example"):
173
- # Process example
174
- syllable_breakdown = process_text(example_text)
175
 
176
- st.subheader("Syllable Breakdown")
177
- display_syllable_breakdown(syllable_breakdown)
 
178
 
179
- # Show example statistics
180
- total_words = len(syllable_breakdown)
181
- total_syllables = sum(len(syls) for _, syls in syllable_breakdown)
182
- st.markdown(f"**πŸ“Š {total_words} words β€’ {total_syllables} syllables**")
183
-
184
- # About section
185
- with st.expander("About this tool"):
186
- st.markdown("""
187
- Created by @aghilalb
188
- """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
 
190
  if __name__ == "__main__":
191
  main()
 
1
  import streamlit as st
2
  import pyphen
3
  import re
 
4
  from typing import List, Tuple
5
+ import nltk
6
+ from nltk.tokenize import word_tokenize
7
+ import string
8
 
9
+ # Download required NLTK data
10
+ try:
11
+ nltk.data.find('tokenizers/punkt')
12
+ except LookupError:
13
+ nltk.download('punkt')
14
+
15
+ # Initialize pyphen for syllable splitting
16
+ dic = pyphen.Pyphen(lang='en')
17
+
18
+ # Configure Streamlit page
19
  st.set_page_config(
20
+ page_title="Text Pronunciation Analyzer",
21
+ page_icon="πŸ—£οΈ",
22
  layout="wide",
23
  initial_sidebar_state="collapsed"
24
  )
25
 
26
+ # Custom CSS for styling
27
+ st.markdown("""
28
+ <style>
29
+ .main-header {
30
+ background: linear-gradient(90deg, #6e8efb, #a777e3);
31
+ -webkit-background-clip: text;
32
+ background-clip: text;
33
+ color: transparent;
34
+ text-align: center;
35
+ font-size: 3rem;
36
+ font-weight: bold;
37
+ margin-bottom: 1rem;
38
+ }
 
39
 
40
+ .subtitle {
41
+ text-align: center;
42
+ color: #666;
43
+ font-size: 1.2rem;
44
+ margin-bottom: 2rem;
45
+ }
46
 
47
+ .word-highlight {
48
+ display: inline-block;
49
+ padding: 0 4px;
50
+ margin: 0 2px;
51
+ border-radius: 4px;
52
+ transition: all 0.2s ease;
53
+ cursor: pointer;
54
+ }
55
+
56
+ .pronunciation-word {
57
+ display: inline-block;
58
+ padding: 0 4px;
59
+ margin: 0 2px;
60
+ border-radius: 4px;
61
+ transition: all 0.2s ease;
62
+ cursor: pointer;
63
+ font-family: 'Courier New', monospace;
64
+ letter-spacing: 1px;
65
+ }
66
+
67
+ .color-1 { background-color: rgba(110, 142, 251, 0.15); color: #6e8efb; }
68
+ .color-2 { background-color: rgba(167, 119, 227, 0.15); color: #a777e3; }
69
+ .color-3 { background-color: rgba(79, 172, 254, 0.15); color: #4facfe; }
70
+ .color-4 { background-color: rgba(0, 242, 254, 0.15); color: #00f2fe; }
71
+ .color-5 { background-color: rgba(67, 233, 123, 0.15); color: #43e97b; }
72
+ .color-6 { background-color: rgba(56, 249, 215, 0.15); color: #38f9d7; }
73
+ .color-7 { background-color: rgba(250, 112, 154, 0.15); color: #fa709a; }
74
+ .color-8 { background-color: rgba(177, 151, 9, 0.15); color: #b19709; }
75
+
76
+ .pronunciation-separator {
77
+ color: #a777e3;
78
+ font-weight: bold;
79
+ margin: 0 2px;
80
+ }
81
+
82
+ .analysis-card {
83
+ background: white;
84
+ padding: 1.5rem;
85
+ border-radius: 12px;
86
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
87
+ margin: 1rem 0;
88
+ }
89
+
90
+ .section-title {
91
+ font-size: 0.9rem;
92
+ font-weight: 600;
93
+ color: #666;
94
+ text-transform: uppercase;
95
+ letter-spacing: 1px;
96
+ margin-bottom: 0.5rem;
97
+ }
98
+
99
+ .results-text {
100
+ font-size: 1.1rem;
101
+ line-height: 1.6;
102
+ }
103
+
104
+ .sample-buttons {
105
+ display: flex;
106
+ flex-wrap: wrap;
107
+ gap: 0.5rem;
108
+ justify-content: center;
109
+ margin: 1rem 0;
110
+ }
111
+
112
+ .sample-btn {
113
+ background: #f0f0f0;
114
+ border: none;
115
+ padding: 0.5rem 1rem;
116
+ border-radius: 20px;
117
+ cursor: pointer;
118
+ transition: background-color 0.2s;
119
+ }
120
+
121
+ .sample-btn:hover {
122
+ background: #e0e0e0;
123
+ }
124
+
125
+ .stats-container {
126
+ display: grid;
127
+ grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
128
+ gap: 1rem;
129
+ margin: 1rem 0;
130
+ }
131
+
132
+ .stat-box {
133
+ background: #f8f9fa;
134
+ padding: 1rem;
135
+ border-radius: 8px;
136
+ text-align: center;
137
+ }
138
+
139
+ .stat-number {
140
+ font-size: 2rem;
141
+ font-weight: bold;
142
+ color: #6e8efb;
143
+ }
144
+
145
+ .stat-label {
146
+ font-size: 0.9rem;
147
+ color: #666;
148
+ text-transform: uppercase;
149
+ letter-spacing: 1px;
150
+ }
151
+ </style>
152
+ """, unsafe_allow_html=True)
153
 
154
+ class PronunciationAnalyzer:
155
+ def __init__(self):
156
+ self.dic = pyphen.Pyphen(lang='en')
157
+ self.color_classes = [
158
+ 'color-1', 'color-2', 'color-3', 'color-4',
159
+ 'color-5', 'color-6', 'color-7', 'color-8'
160
+ ]
161
 
162
+ def get_syllables(self, word: str) -> List[str]:
163
+ """Get syllables for a word using pyphen"""
164
+ # Remove punctuation and convert to lowercase
165
+ clean_word = word.lower().strip(string.punctuation)
166
+
167
+ if not clean_word:
168
+ return [word]
169
+
170
+ # Use pyphen to split into syllables
171
+ syllables = self.dic.inserted(clean_word).split('-')
172
+
173
+ # If pyphen couldn't split (returns original word), try basic vowel-based splitting
174
+ if len(syllables) == 1 and len(clean_word) > 3:
175
+ syllables = self._basic_syllable_split(clean_word)
176
+
177
+ return syllables if syllables else [word]
178
 
179
+ def _basic_syllable_split(self, word: str) -> List[str]:
180
+ """Basic vowel-based syllable splitting as fallback"""
181
+ vowels = 'aeiouy'
182
+ syllables = []
183
+ current_syllable = ''
184
 
185
+ for i, char in enumerate(word):
186
+ current_syllable += char
187
+
188
+ # Look ahead for vowel patterns
189
+ if i < len(word) - 1:
190
+ if char in vowels and word[i + 1] not in vowels:
191
+ # Vowel followed by consonant - potential syllable break
192
+ if len(current_syllable) >= 2:
193
+ syllables.append(current_syllable)
194
+ current_syllable = ''
195
 
196
+ if current_syllable:
197
+ syllables.append(current_syllable)
198
+
199
+ return syllables if syllables else [word]
200
+
201
+ def tokenize_text(self, text: str) -> List[str]:
202
+ """Tokenize text into words while preserving punctuation"""
203
+ # Use NLTK for better tokenization
204
+ tokens = word_tokenize(text)
205
+ return tokens
206
 
207
+ def analyze_text(self, text: str) -> Tuple[List[Tuple[str, List[str]]], dict]:
208
+ """Analyze text and return word-syllable pairs and statistics"""
209
+ if not text.strip():
210
+ return [], {}
211
+
212
+ # Tokenize the text
213
+ words = self.tokenize_text(text)
214
+
215
+ # Filter out pure punctuation tokens for analysis
216
+ content_words = [word for word in words if any(c.isalnum() for c in word)]
217
+
218
+ # Get syllables for each word
219
+ word_syllables = []
220
+ total_syllables = 0
221
+
222
+ for word in words:
223
+ if any(c.isalnum() for c in word): # Only analyze words with alphanumeric characters
224
+ syllables = self.get_syllables(word)
225
+ word_syllables.append((word, syllables))
226
+ total_syllables += len(syllables)
227
+ else:
228
+ word_syllables.append((word, [word])) # Keep punctuation as-is
229
+
230
+ # Calculate statistics
231
+ stats = {
232
+ 'total_words': len(content_words),
233
+ 'total_syllables': total_syllables,
234
+ 'avg_syllables': round(total_syllables / len(content_words), 2) if content_words else 0,
235
+ 'longest_word': max(content_words, key=len) if content_words else '',
236
+ 'most_syllables': max(content_words, key=lambda w: len(self.get_syllables(w))) if content_words else ''
237
+ }
238
+
239
+ return word_syllables, stats
240
 
241
+ def render_highlighted_text(word_syllables: List[Tuple[str, List[str]]], analyzer: PronunciationAnalyzer):
242
+ """Render original text with word highlighting"""
243
  html_parts = []
244
+ word_index = 0
245
+
246
+ for word, syllables in word_syllables:
247
+ if any(c.isalnum() for c in word):
248
+ color_class = analyzer.color_classes[word_index % len(analyzer.color_classes)]
249
+ html_parts.append(f'<span class="word-highlight {color_class}">{word}</span>')
250
+ word_index += 1
251
  else:
252
  html_parts.append(word)
253
+
254
+ # Add space after word (except for punctuation that shouldn't have spaces)
255
+ if word not in '.,!?;:':
256
+ html_parts.append(' ')
257
 
258
+ return ''.join(html_parts)
 
259
 
260
+ def render_pronunciation(word_syllables: List[Tuple[str, List[str]]], analyzer: PronunciationAnalyzer):
261
+ """Render pronunciation with syllable breakdown"""
262
  html_parts = []
263
+ word_index = 0
264
 
265
+ for word, syllables in word_syllables:
266
+ if any(c.isalnum() for c in word):
267
+ color_class = analyzer.color_classes[word_index % len(analyzer.color_classes)]
 
 
 
268
 
269
+ # Join syllables with dots
270
+ syllable_text = '<span class="pronunciation-separator">Β·</span>'.join(syllables)
271
+ html_parts.append(f'<span class="pronunciation-word {color_class}">{syllable_text}</span>')
272
+ word_index += 1
273
+ else:
274
+ html_parts.append(f'<span class="pronunciation-word">{word}</span>')
275
+
276
+ # Add space after word (except for punctuation that shouldn't have spaces)
277
+ if word not in '.,!?;:':
278
+ html_parts.append(' ')
279
 
280
+ return ''.join(html_parts)
 
 
281
 
 
282
  def main():
283
+ # Initialize analyzer
284
+ analyzer = PronunciationAnalyzer()
285
 
286
+ # Header
287
+ st.markdown('<h1 class="main-header">πŸ—£οΈ Text Pronunciation Analyzer</h1>', unsafe_allow_html=True)
288
+ st.markdown('<p class="subtitle">Enter any text below to see its pronunciation breakdown with advanced syllable detection</p>', unsafe_allow_html=True)
289
+
290
+ # Input section
291
+ st.markdown("### πŸ“ Enter Your Text")
292
+
293
+ # Sample texts
294
+ sample_texts = [
295
+ "Hello world",
296
+ "Pronunciation analyzer",
297
+ "Supercalifragilisticexpialidocious",
298
+ "Linguistics and phonetics",
299
+ "The quick brown fox jumps over the lazy dog"
300
+ ]
301
+
302
+ # Sample buttons
303
+ st.markdown("**Try these samples:**")
304
+ cols = st.columns(len(sample_texts))
305
+ for i, sample in enumerate(sample_texts):
306
+ if cols[i].button(sample, key=f"sample_{i}"):
307
+ st.session_state.input_text = sample
308
+
309
+ # Text input
310
+ text_input = st.text_area(
311
+ "Text to analyze:",
312
+ value=st.session_state.get('input_text', ''),
313
+ height=120,
314
  placeholder="Type or paste your text here...",
315
+ key="text_input"
 
316
  )
317
 
318
+ # Update session state
319
+ if text_input:
320
+ st.session_state.input_text = text_input
321
 
322
+ # Analyze button
323
+ col1, col2, col3 = st.columns([1, 1, 1])
324
+ with col2:
325
+ analyze_button = st.button("πŸ” Analyze Text", type="primary", use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
326
 
327
+ # Clear button
328
+ if st.button("πŸ—‘οΈ Clear"):
329
+ st.session_state.input_text = ""
330
+ st.rerun()
331
 
332
+ # Analysis results
333
+ if analyze_button and text_input.strip():
334
+ with st.spinner("Analyzing text..."):
335
+ word_syllables, stats = analyzer.analyze_text(text_input)
336
 
337
+ if word_syllables:
338
+ st.markdown("---")
339
+ st.markdown("## πŸ“Š Analysis Results")
 
 
 
 
 
340
 
341
+ # Statistics
342
+ st.markdown("### πŸ“ˆ Text Statistics")
343
+ col1, col2, col3, col4 = st.columns(4)
344
 
345
+ with col1:
346
+ st.markdown(f"""
347
+ <div class="stat-box">
348
+ <div class="stat-number">{stats['total_words']}</div>
349
+ <div class="stat-label">Words</div>
350
+ </div>
351
+ """, unsafe_allow_html=True)
352
+
353
+ with col2:
354
+ st.markdown(f"""
355
+ <div class="stat-box">
356
+ <div class="stat-number">{stats['total_syllables']}</div>
357
+ <div class="stat-label">Syllables</div>
358
+ </div>
359
+ """, unsafe_allow_html=True)
360
+
361
+ with col3:
362
+ st.markdown(f"""
363
+ <div class="stat-box">
364
+ <div class="stat-number">{stats['avg_syllables']}</div>
365
+ <div class="stat-label">Avg/Word</div>
366
+ </div>
367
+ """, unsafe_allow_html=True)
368
+
369
+ with col4:
370
+ longest_syllables = len(analyzer.get_syllables(stats['most_syllables']))
371
+ st.markdown(f"""
372
+ <div class="stat-box">
373
+ <div class="stat-number">{longest_syllables}</div>
374
+ <div class="stat-label">Max Syllables</div>
375
+ </div>
376
+ """, unsafe_allow_html=True)
377
+
378
+ # Original text with highlights
379
+ st.markdown("### πŸ“– Original Text")
380
+ original_html = render_highlighted_text(word_syllables, analyzer)
381
+ st.markdown(f'<div class="analysis-card"><div class="results-text">{original_html}</div></div>', unsafe_allow_html=True)
382
+
383
+ # Pronunciation breakdown
384
+ st.markdown("### πŸ”€ Pronunciation Breakdown")
385
+ pronunciation_html = render_pronunciation(word_syllables, analyzer)
386
+ st.markdown(f'<div class="analysis-card"><div class="results-text">{pronunciation_html}</div></div>', unsafe_allow_html=True)
387
+
388
+ # Word-by-word breakdown
389
+ st.markdown("### πŸ“ Word-by-Word Analysis")
390
+
391
+ # Create expandable sections for detailed breakdown
392
+ content_words = [(word, syllables) for word, syllables in word_syllables if any(c.isalnum() for c in word)]
393
+
394
+ if content_words:
395
+ # Group words into rows of 3
396
+ for i in range(0, len(content_words), 3):
397
+ cols = st.columns(3)
398
+ for j, (word, syllables) in enumerate(content_words[i:i+3]):
399
+ with cols[j]:
400
+ st.markdown(f"""
401
+ <div style="background: #f8f9fa; padding: 1rem; border-radius: 8px; margin-bottom: 0.5rem;">
402
+ <div style="font-weight: bold; color: #333; margin-bottom: 0.5rem;">{word}</div>
403
+ <div style="color: #666; font-family: monospace;">{'Β·'.join(syllables)}</div>
404
+ <div style="color: #999; font-size: 0.8rem;">{len(syllables)} syllable{'s' if len(syllables) != 1 else ''}</div>
405
+ </div>
406
+ """, unsafe_allow_html=True)
407
+
408
+ elif analyze_button:
409
+ st.warning("Please enter some text to analyze.")
410
+
411
+ # Footer
412
+ st.markdown("---")
413
+ st.markdown(
414
+ '<div style="text-align: center; color: #666; padding: 2rem;">Created with ❀️ using Streamlit and pyphen</div>',
415
+ unsafe_allow_html=True
416
+ )
417
 
418
  if __name__ == "__main__":
419
  main()