usmannawaz commited on
Commit
261676a
·
verified ·
1 Parent(s): 0d9fe62

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +118 -268
app.py CHANGED
@@ -1,24 +1,20 @@
1
- import html
2
  import os
3
  from functools import lru_cache
4
  from pathlib import Path
5
 
6
  import gradio as gr
 
7
  import spaces
8
  import stanza
9
  from huggingface_hub import snapshot_download
10
 
11
 
12
- # ============================================================
13
- # MODEL CONFIGURATION
14
- # ============================================================
15
-
16
  MODEL_REPO_ID = "usmannawaz/old-church-slavonic-tokenizer-lemmatizer"
17
  MODEL_VARIANT = "combined"
18
 
19
  LANG = "cu"
20
- TOKENIZER_PACKAGE = "proiel"
21
  POS_PACKAGE = "proiel_nocharlm"
 
22
  LEMMATIZER_PACKAGE = "proiel_nocharlm"
23
 
24
  HF_LOCAL_DIR = "hf_models/old-church-slavonic-tokenizer-lemmatizer"
@@ -28,18 +24,14 @@ os.environ["STANZA_RESOURCES_DIR"] = STANZA_DIR
28
 
29
  EXAMPLE_TEXT = "тѣма пророчьскꙑи ликъ д͞ховьнꙑѧ благодѣти исплъни сѧ"
30
 
31
-
32
- # ============================================================
33
- # PRIVATE-USE / LEGACY CHARACTERS -> UNICODE OCS
34
- # ============================================================
35
-
36
- REPLACEMENTS = {
37
  "": "и",
38
  "": "ч",
39
  "": "ѥ",
40
  "": "н",
41
  "": ":~",
42
  "": "~",
 
43
  "": "̅",
44
  "": "҆̀",
45
  "": "ⷮ",
@@ -65,7 +57,6 @@ REPLACEMENTS = {
65
  "": "ⱉ",
66
  "": "͡",
67
  "": "҃",
68
- "ⷣ": "ⷣ҇",
69
  "ⷮ": "ⷮ҇",
70
  "ⷯ": "ⷯ҇",
71
  "": "ꙩ́",
@@ -76,17 +67,12 @@ REPLACEMENTS = {
76
  "": "҃",
77
  }
78
 
79
-
80
- # ============================================================
81
- # FONT SETUP
82
- # ============================================================
83
-
84
  FONT_DIR = Path("fonts")
85
 
86
  if FONT_DIR.exists():
87
  gr.set_static_paths(paths=[FONT_DIR])
88
 
89
- CUSTOM_CSS = r"""
90
  @font-face {
91
  font-family: "Menaion";
92
  src: url("/gradio_api/file=fonts/Menaion.otf") format("opentype");
@@ -94,22 +80,14 @@ CUSTOM_CSS = r"""
94
  font-weight: normal;
95
  font-display: swap;
96
  }
97
-
98
  body {
99
- background: linear-gradient(
100
- 135deg,
101
- #f8fafc 0%,
102
- #eef6ff 50%,
103
- #f3f7fb 100%
104
- ) !important;
105
  }
106
-
107
  .gradio-container {
108
- max-width: 1050px !important;
109
  margin: auto !important;
110
  font-family: Inter, Arial, sans-serif !important;
111
  }
112
-
113
  #hero {
114
  background: linear-gradient(135deg, #0f172a, #0f5f8f);
115
  border-radius: 28px;
@@ -117,82 +95,67 @@ body {
117
  margin-bottom: 22px;
118
  box-shadow: 0 22px 60px rgba(15, 23, 42, 0.22);
119
  }
120
-
121
  #hero h1 {
122
  color: #ffffff;
123
  font-size: 2.4rem;
124
- font-weight: 900;
125
  margin: 0 0 10px 0;
 
126
  }
127
-
128
  #hero p {
129
  color: #e0f2fe;
130
  font-size: 1.05rem;
131
  line-height: 1.55;
132
  margin: 0;
133
  }
134
-
135
- #main-card,
136
- #output-card {
137
  background: #ffffff;
138
  border: 1px solid #cbd5e1;
139
  border-radius: 24px;
140
  padding: 24px;
141
  box-shadow: 0 18px 45px rgba(15, 23, 42, 0.10);
142
  }
143
-
144
  #output-card {
 
 
 
 
145
  margin-top: 22px;
 
146
  }
147
-
148
- textarea,
149
- input {
150
  border-radius: 16px !important;
151
  border: 1.5px solid #94a3b8 !important;
152
  background: #ffffff !important;
153
  color: #0f172a !important;
154
  }
155
-
156
- textarea:focus,
157
- input:focus {
158
  border-color: #0f5f8f !important;
159
  box-shadow: 0 0 0 3px rgba(14, 116, 144, 0.18) !important;
160
  }
161
-
162
  label {
163
  color: #0f172a !important;
164
  font-weight: 850 !important;
165
  }
166
-
167
  button {
168
  border-radius: 16px !important;
169
  font-weight: 900 !important;
170
  box-shadow: 0 10px 24px rgba(15, 23, 42, 0.14) !important;
171
  }
172
-
173
  button.primary {
174
  background: linear-gradient(90deg, #0f172a, #0f5f8f) !important;
175
  color: #ffffff !important;
176
  border: none !important;
177
  }
178
-
179
  button.primary:hover {
180
  background: linear-gradient(90deg, #020617, #075985) !important;
181
  transform: translateY(-1px);
182
  }
183
-
184
- /* Menaion changes only browser rendering, not the text sent to Stanza. */
185
- #ocs-input textarea,
186
- .ocs-font {
187
- font-family: "Menaion", "Noto Sans Cyrillic", serif !important;
188
- font-weight: normal !important;
189
- }
190
-
191
  #ocs-input textarea {
192
- font-size: 23px !important;
193
- line-height: 1.75 !important;
 
 
194
  }
195
-
196
  #sentence-box {
197
  background: #f8fafc;
198
  border: 1px solid #cbd5e1;
@@ -200,80 +163,46 @@ button.primary:hover {
200
  border-radius: 18px;
201
  padding: 16px;
202
  color: #0f172a;
203
- margin-bottom: 20px;
 
 
204
  }
205
-
206
- #sentence-box .label {
207
- font-family: Inter, Arial, sans-serif;
208
  font-size: 1rem;
209
- font-weight: 850;
210
  }
211
-
212
  #sentence-text {
213
  color: #0f172a;
214
- font-size: 25px;
215
- line-height: 1.85;
216
- margin-top: 10px;
217
- overflow-wrap: anywhere;
 
218
  }
219
-
220
  #table-title {
221
  color: #0f5f8f;
222
- font-family: Inter, Arial, sans-serif;
223
  font-weight: 900;
224
- font-size: 1.05rem;
225
- margin: 6px 0 12px 0;
226
- }
227
-
228
- .ocs-table-wrap {
229
- border: 1px solid #cbd5e1;
230
- border-radius: 16px;
231
- overflow: auto;
232
- }
233
-
234
- .ocs-table {
235
- width: 100%;
236
- border-collapse: collapse;
237
- table-layout: fixed;
238
- }
239
-
240
- .ocs-table th {
241
- background: #eef6ff;
242
- color: #0f172a;
243
- font-family: Inter, Arial, sans-serif;
244
- font-size: 0.95rem;
245
- text-align: left;
246
- padding: 12px;
247
- border-bottom: 1px solid #cbd5e1;
248
- }
249
-
250
- .ocs-table td {
251
- color: #0f172a;
252
- font-size: 22px;
253
- line-height: 1.6;
254
- padding: 11px 12px;
255
- border-bottom: 1px solid #e2e8f0;
256
- vertical-align: top;
257
- overflow-wrap: anywhere;
258
- }
259
-
260
- .ocs-table tr:last-child td {
261
- border-bottom: none;
262
  }
263
-
264
- .ocs-table tbody tr:nth-child(even) {
265
- background: #f8fafc;
 
 
 
 
 
 
 
 
 
266
  }
267
-
268
- .status-box {
269
- border-radius: 16px;
270
- padding: 14px 16px;
271
- background: #fff7ed;
272
- border: 1px solid #fdba74;
273
- color: #7c2d12;
274
- font-family: Inter, Arial, sans-serif;
275
  }
276
-
277
  footer,
278
  .api,
279
  .settings,
@@ -285,19 +214,14 @@ button[aria-label="Settings"] {
285
 
286
  HERO_HTML = """
287
  <div id="hero">
288
- <h1>OCS Combined Lemmatizer</h1>
289
  <p>
290
- Pasted text is mapped from legacy/private-use characters to Unicode,
291
- processed by Stanza, and displayed using the Menaion font.
292
  </p>
293
  </div>
294
  """
295
 
296
 
297
- # ============================================================
298
- # MODEL LOADING
299
- # ============================================================
300
-
301
  @lru_cache(maxsize=1)
302
  def get_model_paths():
303
  repo_dir = snapshot_download(
@@ -307,31 +231,14 @@ def get_model_paths():
307
 
308
  model_dir = Path(repo_dir)
309
 
310
- tokenizer_model = (
311
- model_dir
312
- / "models"
313
- / MODEL_VARIANT
314
- / "tokenize"
315
- / "cu_proiel_tokenizer.pt"
316
- )
317
-
318
- lemma_model = (
319
- model_dir
320
- / "models"
321
- / MODEL_VARIANT
322
- / "lemma"
323
- / "cu_proiel_nocharlm_lemmatizer.pt"
324
- )
325
 
326
  if not tokenizer_model.exists():
327
- raise FileNotFoundError(
328
- f"Tokenizer model not found: {tokenizer_model}"
329
- )
330
 
331
  if not lemma_model.exists():
332
- raise FileNotFoundError(
333
- f"Lemmatizer model not found: {lemma_model}"
334
- )
335
 
336
  return tokenizer_model, lemma_model
337
 
@@ -365,133 +272,92 @@ def load_nlp_pipeline():
365
  )
366
 
367
 
368
- # ============================================================
369
- # TEXT PROCESSING
370
- # ============================================================
371
-
372
  def clean_text(text):
373
  return str(text or "").strip()
374
 
375
 
376
  def apply_mapping(text, mapping):
377
- ordered = sorted(
378
- mapping.items(),
379
- key=lambda item: len(item[0]),
380
- reverse=True,
381
- )
382
-
383
- for old_text, new_text in ordered:
384
- text = text.replace(old_text, new_text)
385
-
386
  return text
387
 
388
 
389
  def preprocess_text(text):
390
- return apply_mapping(text, REPLACEMENTS)
 
391
 
392
 
393
  def make_sentence_html(text):
394
- return f"""
395
- <div id="sentence-box">
396
- <div class="label">Processed Unicode OCS input</div>
397
- <div id="sentence-text" class="ocs-font">
398
- {html.escape(text)}
399
- </div>
400
- </div>
401
- """
402
-
403
-
404
- def make_results_html(rows):
405
- if not rows:
406
- return """
407
- <div class="status-box">
408
- No tokens were returned by the model.
409
- </div>
410
- """
411
-
412
- table_rows = "".join(
413
- f"""
414
- <tr>
415
- <td class="ocs-font">{html.escape(token)}</td>
416
- <td class="ocs-font">{html.escape(lemma)}</td>
417
- </tr>
418
- """
419
- for token, lemma in rows
420
  )
421
 
422
  return f"""
423
- <div id="table-title">Token and lemma output</div>
424
- <div class="ocs-table-wrap">
425
- <table class="ocs-table">
426
- <thead>
427
- <tr>
428
- <th>Token</th>
429
- <th>Lemma</th>
430
- </tr>
431
- </thead>
432
- <tbody>
433
- {table_rows}
434
- </tbody>
435
- </table>
436
  </div>
437
  """
438
 
439
 
440
  @spaces.GPU(duration=120)
441
  def analyze_text(text):
442
- raw_text = clean_text(text)
443
 
444
- if not raw_text:
 
445
  return (
446
  gr.update(visible=True),
447
- """
448
- <div class="status-box">
449
- Please paste a sentence or raw OCS text first.
450
- </div>
451
- """,
452
- "",
453
  )
454
 
455
  try:
456
- processed_text = preprocess_text(raw_text)
457
-
458
  nlp = load_nlp_pipeline()
459
- document = nlp(processed_text)
460
 
461
- rows = [
462
- (
463
- word.text or "_",
464
- word.lemma or "_",
465
- )
466
- for sentence in document.sentences
467
- for word in sentence.words
468
- ]
 
 
469
 
470
  return (
471
  gr.update(visible=True),
472
- make_sentence_html(processed_text),
473
- make_results_html(rows),
 
474
  )
475
 
476
  except Exception as error:
 
 
477
  return (
478
  gr.update(visible=True),
479
- f"""
480
- <div class="status-box">
481
- <strong>Error:</strong>
482
- {html.escape(str(error))}
483
- </div>
484
- """,
485
- "",
486
  )
487
 
488
 
489
  def clear_all():
 
 
490
  return (
491
  "",
492
  gr.update(visible=False),
493
- "",
494
- "",
 
495
  )
496
 
497
 
@@ -499,19 +365,14 @@ def load_example():
499
  return EXAMPLE_TEXT
500
 
501
 
502
- # ============================================================
503
- # GRADIO APP
504
- # Event handlers must stay inside the Blocks context.
505
- # ============================================================
506
-
507
- APP_THEME = gr.themes.Soft(
508
- primary_hue="cyan",
509
- secondary_hue="blue",
510
- neutral_hue="slate",
511
- )
512
-
513
  with gr.Blocks(
514
- title="OCS Combined Lemmatizer",
 
 
 
 
 
 
515
  ) as demo:
516
  gr.HTML(HERO_HTML)
517
 
@@ -524,29 +385,27 @@ with gr.Blocks(
524
  )
525
 
526
  with gr.Row():
527
- analyze_button = gr.Button(
528
- "Lemmatize",
529
- variant="primary",
530
- )
531
-
532
  example_button = gr.Button("Load example")
533
  clear_button = gr.Button("Clear")
534
 
535
- with gr.Column(
536
- elem_id="output-card",
537
- visible=False,
538
- ) as output_panel:
539
  sentence_output = gr.HTML()
540
- results_output = gr.HTML()
 
 
 
 
 
 
 
 
 
541
 
542
  analyze_button.click(
543
  fn=analyze_text,
544
  inputs=text_input,
545
- outputs=[
546
- output_panel,
547
- sentence_output,
548
- results_output,
549
- ],
550
  show_progress="hidden",
551
  )
552
 
@@ -560,18 +419,9 @@ with gr.Blocks(
560
  clear_button.click(
561
  fn=clear_all,
562
  inputs=None,
563
- outputs=[
564
- text_input,
565
- output_panel,
566
- sentence_output,
567
- results_output,
568
- ],
569
  show_progress="hidden",
570
  )
571
 
572
 
573
- if __name__ == "__main__":
574
- demo.launch(
575
- css=CUSTOM_CSS,
576
- theme=APP_THEME,
577
- )
 
 
1
  import os
2
  from functools import lru_cache
3
  from pathlib import Path
4
 
5
  import gradio as gr
6
+ import pandas as pd
7
  import spaces
8
  import stanza
9
  from huggingface_hub import snapshot_download
10
 
11
 
 
 
 
 
12
  MODEL_REPO_ID = "usmannawaz/old-church-slavonic-tokenizer-lemmatizer"
13
  MODEL_VARIANT = "combined"
14
 
15
  LANG = "cu"
 
16
  POS_PACKAGE = "proiel_nocharlm"
17
+ TOKENIZER_PACKAGE = "proiel"
18
  LEMMATIZER_PACKAGE = "proiel_nocharlm"
19
 
20
  HF_LOCAL_DIR = "hf_models/old-church-slavonic-tokenizer-lemmatizer"
 
24
 
25
  EXAMPLE_TEXT = "тѣма пророчьскꙑи ликъ д͞ховьнꙑѧ благодѣти исплъни сѧ"
26
 
27
+ REPLACEMENTS1 = {
 
 
 
 
 
28
  "": "и",
29
  "": "ч",
30
  "": "ѥ",
31
  "": "н",
32
  "": ":~",
33
  "": "~",
34
+ "ⷣ": "ⷣ҇",
35
  "": "̅",
36
  "": "҆̀",
37
  "": "ⷮ",
 
57
  "": "ⱉ",
58
  "": "͡",
59
  "": "҃",
 
60
  "ⷮ": "ⷮ҇",
61
  "ⷯ": "ⷯ҇",
62
  "": "ꙩ́",
 
67
  "": "҃",
68
  }
69
 
 
 
 
 
 
70
  FONT_DIR = Path("fonts")
71
 
72
  if FONT_DIR.exists():
73
  gr.set_static_paths(paths=[FONT_DIR])
74
 
75
+ CUSTOM_CSS = """
76
  @font-face {
77
  font-family: "Menaion";
78
  src: url("/gradio_api/file=fonts/Menaion.otf") format("opentype");
 
80
  font-weight: normal;
81
  font-display: swap;
82
  }
 
83
  body {
84
+ background: linear-gradient(135deg, #f8fafc 0%, #eef6ff 50%, #f3f7fb 100%) !important;
 
 
 
 
 
85
  }
 
86
  .gradio-container {
87
+ max-width: 980px !important;
88
  margin: auto !important;
89
  font-family: Inter, Arial, sans-serif !important;
90
  }
 
91
  #hero {
92
  background: linear-gradient(135deg, #0f172a, #0f5f8f);
93
  border-radius: 28px;
 
95
  margin-bottom: 22px;
96
  box-shadow: 0 22px 60px rgba(15, 23, 42, 0.22);
97
  }
 
98
  #hero h1 {
99
  color: #ffffff;
100
  font-size: 2.4rem;
 
101
  margin: 0 0 10px 0;
102
+ font-weight: 900;
103
  }
 
104
  #hero p {
105
  color: #e0f2fe;
106
  font-size: 1.05rem;
107
  line-height: 1.55;
108
  margin: 0;
109
  }
110
+ #main-card {
 
 
111
  background: #ffffff;
112
  border: 1px solid #cbd5e1;
113
  border-radius: 24px;
114
  padding: 24px;
115
  box-shadow: 0 18px 45px rgba(15, 23, 42, 0.10);
116
  }
 
117
  #output-card {
118
+ background: #ffffff;
119
+ border: 1px solid #cbd5e1;
120
+ border-radius: 24px;
121
+ padding: 22px;
122
  margin-top: 22px;
123
+ box-shadow: 0 18px 45px rgba(15, 23, 42, 0.10);
124
  }
125
+ textarea, input {
 
 
126
  border-radius: 16px !important;
127
  border: 1.5px solid #94a3b8 !important;
128
  background: #ffffff !important;
129
  color: #0f172a !important;
130
  }
131
+ textarea:focus, input:focus {
 
 
132
  border-color: #0f5f8f !important;
133
  box-shadow: 0 0 0 3px rgba(14, 116, 144, 0.18) !important;
134
  }
 
135
  label {
136
  color: #0f172a !important;
137
  font-weight: 850 !important;
138
  }
 
139
  button {
140
  border-radius: 16px !important;
141
  font-weight: 900 !important;
142
  box-shadow: 0 10px 24px rgba(15, 23, 42, 0.14) !important;
143
  }
 
144
  button.primary {
145
  background: linear-gradient(90deg, #0f172a, #0f5f8f) !important;
146
  color: #ffffff !important;
147
  border: none !important;
148
  }
 
149
  button.primary:hover {
150
  background: linear-gradient(90deg, #020617, #075985) !important;
151
  transform: translateY(-1px);
152
  }
 
 
 
 
 
 
 
 
153
  #ocs-input textarea {
154
+ font-family: "Menaion", serif !important;
155
+ font-size: 24px !important;
156
+ line-height: 1.8 !important;
157
+ font-weight: normal !important;
158
  }
 
159
  #sentence-box {
160
  background: #f8fafc;
161
  border: 1px solid #cbd5e1;
 
163
  border-radius: 18px;
164
  padding: 16px;
165
  color: #0f172a;
166
+ font-size: 1.05rem;
167
+ line-height: 1.7;
168
+ margin-bottom: 18px;
169
  }
170
+ #sentence-box b {
171
+ color: #0f172a;
172
+ font-family: Inter, Arial, sans-serif !important;
173
  font-size: 1rem;
 
174
  }
 
175
  #sentence-text {
176
  color: #0f172a;
177
+ font-family: "Menaion", serif !important;
178
+ font-size: 25px !important;
179
+ font-weight: normal !important;
180
+ line-height: 1.8 !important;
181
+ margin-top: 8px;
182
  }
 
183
  #table-title {
184
  color: #0f5f8f;
 
185
  font-weight: 900;
186
+ font-size: 1rem;
187
+ margin: 6px 0 10px 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  }
189
+ #lemma-table,
190
+ #lemma-table table,
191
+ #lemma-table td,
192
+ #lemma-table th,
193
+ #lemma-table input,
194
+ #lemma-table textarea,
195
+ #lemma-table .cell-wrap,
196
+ #lemma-table .table-wrap {
197
+ font-family: "Menaion", serif !important;
198
+ font-size: 21px !important;
199
+ font-weight: normal !important;
200
+ line-height: 1.6 !important;
201
  }
202
+ .dataframe {
203
+ border-radius: 16px !important;
204
+ overflow: hidden !important;
 
 
 
 
 
205
  }
 
206
  footer,
207
  .api,
208
  .settings,
 
214
 
215
  HERO_HTML = """
216
  <div id="hero">
217
+ <h1>Combo New Tool</h1>
218
  <p>
219
+ Paste Old Church Slavonic text. The app preprocesses the input, tokenizes it, and returns each token with its predicted lemma.
 
220
  </p>
221
  </div>
222
  """
223
 
224
 
 
 
 
 
225
  @lru_cache(maxsize=1)
226
  def get_model_paths():
227
  repo_dir = snapshot_download(
 
231
 
232
  model_dir = Path(repo_dir)
233
 
234
+ tokenizer_model = model_dir / "models" / MODEL_VARIANT / "tokenize" / "cu_proiel_tokenizer.pt"
235
+ lemma_model = model_dir / "models" / MODEL_VARIANT / "lemma" / "cu_proiel_nocharlm_lemmatizer.pt"
 
 
 
 
 
 
 
 
 
 
 
 
 
236
 
237
  if not tokenizer_model.exists():
238
+ raise FileNotFoundError(f"Tokenizer model not found: {tokenizer_model}")
 
 
239
 
240
  if not lemma_model.exists():
241
+ raise FileNotFoundError(f"Lemmatizer model not found: {lemma_model}")
 
 
242
 
243
  return tokenizer_model, lemma_model
244
 
 
272
  )
273
 
274
 
 
 
 
 
275
  def clean_text(text):
276
  return str(text or "").strip()
277
 
278
 
279
  def apply_mapping(text, mapping):
280
+ for old_char, new_char in sorted(mapping.items(), key=lambda item: len(item[0]), reverse=True):
281
+ text = text.replace(old_char, new_char)
 
 
 
 
 
 
 
282
  return text
283
 
284
 
285
  def preprocess_text(text):
286
+ text = apply_mapping(text, REPLACEMENTS1)
287
+ return text
288
 
289
 
290
  def make_sentence_html(text):
291
+ safe_text = (
292
+ text.replace("&", "&amp;")
293
+ .replace("<", "&lt;")
294
+ .replace(">", "&gt;")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
295
  )
296
 
297
  return f"""
298
+ <div id="sentence-box">
299
+ <b>Input text</b>
300
+ <div id="sentence-text">{safe_text}</div>
 
 
 
 
 
 
 
 
 
 
301
  </div>
302
  """
303
 
304
 
305
  @spaces.GPU(duration=120)
306
  def analyze_text(text):
307
+ text = clean_text(text)
308
 
309
+ if not text:
310
+ empty_table = pd.DataFrame(columns=["Token", "Lemma"])
311
  return (
312
  gr.update(visible=True),
313
+ gr.update(value="<div id='sentence-box'>Please paste a sentence or raw text first.</div>"),
314
+ gr.update(value="", visible=False),
315
+ gr.update(value=empty_table, visible=False),
 
 
 
316
  )
317
 
318
  try:
319
+ processed_text = preprocess_text(text)
 
320
  nlp = load_nlp_pipeline()
321
+ doc = nlp(processed_text)
322
 
323
+ rows = []
324
+
325
+ for sentence in doc.sentences:
326
+ for word in sentence.words:
327
+ rows.append({
328
+ "Token": word.text or "_",
329
+ "Lemma": word.lemma or "_",
330
+ })
331
+
332
+ table = pd.DataFrame(rows, columns=["Token", "Lemma"])
333
 
334
  return (
335
  gr.update(visible=True),
336
+ gr.update(value=make_sentence_html(processed_text)),
337
+ gr.update(value="<div id='table-title'>Token and lemma output</div>", visible=True),
338
+ gr.update(value=table, visible=True),
339
  )
340
 
341
  except Exception as error:
342
+ empty_table = pd.DataFrame(columns=["Token", "Lemma"])
343
+
344
  return (
345
  gr.update(visible=True),
346
+ gr.update(value=f"<div id='sentence-box'>Error: {str(error)}</div>"),
347
+ gr.update(value="", visible=False),
348
+ gr.update(value=empty_table, visible=False),
 
 
 
 
349
  )
350
 
351
 
352
  def clear_all():
353
+ empty_table = pd.DataFrame(columns=["Token", "Lemma"])
354
+
355
  return (
356
  "",
357
  gr.update(visible=False),
358
+ gr.update(value=""),
359
+ gr.update(value="", visible=False),
360
+ gr.update(value=empty_table, visible=False),
361
  )
362
 
363
 
 
365
  return EXAMPLE_TEXT
366
 
367
 
 
 
 
 
 
 
 
 
 
 
 
368
  with gr.Blocks(
369
+ title="Combo New Tool",
370
+ css=CUSTOM_CSS,
371
+ theme=gr.themes.Soft(
372
+ primary_hue="cyan",
373
+ secondary_hue="blue",
374
+ neutral_hue="slate",
375
+ ),
376
  ) as demo:
377
  gr.HTML(HERO_HTML)
378
 
 
385
  )
386
 
387
  with gr.Row():
388
+ analyze_button = gr.Button("Lemmatize", variant="primary")
 
 
 
 
389
  example_button = gr.Button("Load example")
390
  clear_button = gr.Button("Clear")
391
 
392
+ with gr.Column(elem_id="output-card", visible=False) as output_panel:
 
 
 
393
  sentence_output = gr.HTML()
394
+ table_title = gr.HTML(visible=False)
395
+
396
+ lemma_table = gr.Dataframe(
397
+ headers=["Token", "Lemma"],
398
+ interactive=False,
399
+ wrap=True,
400
+ show_label=False,
401
+ visible=False,
402
+ elem_id="lemma-table",
403
+ )
404
 
405
  analyze_button.click(
406
  fn=analyze_text,
407
  inputs=text_input,
408
+ outputs=[output_panel, sentence_output, table_title, lemma_table],
 
 
 
 
409
  show_progress="hidden",
410
  )
411
 
 
419
  clear_button.click(
420
  fn=clear_all,
421
  inputs=None,
422
+ outputs=[text_input, output_panel, sentence_output, table_title, lemma_table],
 
 
 
 
 
423
  show_progress="hidden",
424
  )
425
 
426
 
427
+ demo.launch()