SagarVelamuri commited on
Commit
bfbbfb4
·
verified ·
1 Parent(s): 1cac831

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +154 -153
app.py CHANGED
@@ -55,25 +55,34 @@ def _ensure_vocab_consistency(md, tok):
55
  actual_vocab = None
56
  if actual_vocab is not None:
57
  md.config.vocab_size = actual_vocab
58
- try: md.generation_config.vocab_size = actual_vocab
59
- except Exception: pass
 
 
60
  else:
61
  fallback_size = getattr(tok, "vocab_size", None)
62
  if fallback_size is None:
63
- try: fallback_size = len(tok)
64
- except Exception: fallback_size = 64000
 
 
65
  md.config.vocab_size = fallback_size
66
- try: md.generation_config.vocab_size = fallback_size
67
- except Exception: pass
 
 
68
  if not hasattr(md.config, "get_text_config") or not callable(getattr(md.config, "get_text_config", None)):
69
  def _get_text_config(self): return self
70
  md.config.get_text_config = types.MethodType(_get_text_config, md.config)
 
71
  _ensure_vocab_consistency(model, tokenizer)
72
 
73
  # Disable KV cache to avoid custom decoder's past_key_values shape bug
74
  for obj in (model.config, model.generation_config):
75
- try: setattr(obj, "use_cache", False)
76
- except Exception: pass
 
 
77
 
78
  ip = IndicProcessor(inference=True)
79
 
@@ -90,7 +99,9 @@ def _translate_to_lang(text: str, tgt_code: str, num_beams: int, max_new_tokens:
90
  return_tensors="pt",
91
  return_attention_mask=True,
92
  ).to(device)
 
93
  do_sample = (temperature is not None) and (float(temperature) > 0)
 
94
  outputs = model.generate(
95
  **enc,
96
  max_new_tokens=int(max_new_tokens),
@@ -103,7 +114,10 @@ def _translate_to_lang(text: str, tgt_code: str, num_beams: int, max_new_tokens:
103
  early_stopping=False,
104
  pad_token_id=model.generation_config.pad_token_id,
105
  )
106
- decoded = tokenizer.batch_decode(outputs, skip_special_tokens=True, clean_up_tokenization_spaces=True)
 
 
 
107
  final = ip.postprocess_batch(decoded, lang=tgt_code)
108
  return final[0].strip()
109
 
@@ -140,60 +154,52 @@ THEME = gr.themes.Soft(
140
  button_primary_text_color="#ffffff",
141
  )
142
 
143
- # -------------------- LAYOUT CSS --------------------
144
  CUSTOM_CSS = """
145
- /* Fullscreen app; allow page scroll only on very short displays (<800px) */
146
- html, body { min-height: 100%; background:#0b1220; overflow-y: hidden; }
147
- @media (max-height: 800px) { html, body { overflow-y: auto; } }
148
-
149
- .gradio-container { min-height: 100vh !important; width: 100vw !important; padding: 10px 12px; margin: 0; }
150
-
151
- /* Shell: header / main / footer */
152
- #app-shell { display: grid; grid-template-rows: 64px 1fr 32px; height: 100vh; gap: 10px; }
153
- #header, #footer { display:flex; align-items:center; justify-content:center; }
154
-
155
- /* Main: two fixed columns so widths never jump */
156
- #main-grid { height: 100%; min-height: 0; display: grid; grid-template-columns: 1.06fr 0.94fr; gap: 12px; }
157
-
158
- /* Panels */
159
- .panel {
160
- border: 1px solid #223144 !important;
161
- border-radius: 12px !important;
162
- background: #0f172a !important;
163
- padding: 0 !important;
164
- min-height: 0;
165
- display: flex; flex-direction: column;
 
 
 
 
 
 
166
  }
167
 
168
- /* Panel header (clear section titles + maximize) */
 
169
  .panel-h {
170
  display:flex; align-items:center; justify-content:space-between;
171
- padding: 10px 12px; border-bottom:1px solid #243244;
172
- background:#0b1b2b; color:#eaf2ff; font-weight: 900; letter-spacing:.2px;
173
- border-top-left-radius:12px; border-top-right-radius:12px;
174
  }
175
- .max-btn { font-weight:900; padding:4px 10px; border-radius:10px; border:1px solid #3c5a86; background:#122037; color:#ffffff; }
176
- .max-btn:hover { border-color:#60a5fa; }
177
-
178
- /* Panel body fills remaining height */
179
- .panel-b { flex:1 1 auto; min-height:0; padding: 10px 12px; }
180
-
181
- /* Left column layout: Input (grow 6) / Advanced (grow 3) / Actions+Examples (grow 3) */
182
- #left-col { display:flex; flex-direction:column; gap:12px; min-height:0; }
183
- #left-input { flex: 6 1 0; min-height: 0; }
184
- #left-adv { flex: 3 1 0; min-height: 0; }
185
- #left-actions { flex: 3 1 0; min-height: 0; }
186
 
187
- /* Right column: two equal outputs */
188
- #right-col { display:flex; flex-direction:column; gap:12px; min-height:0; }
189
- #right-hi, #right-te { flex:1 1 0; min-height:0; }
 
190
 
191
- /* Text areas fill their panel body */
192
- .textwrap { height:100%; min-height:0; display:flex; }
193
- .textwrap > div { flex:1 1 auto; min-height:0; }
194
- .textwrap textarea { height:100% !important; }
195
-
196
- /* Inputs */
197
  textarea, textarea:focus {
198
  background:#0b1220 !important; color:#f9fbff !important;
199
  font-size: 17px !important; line-height:1.55 !important;
@@ -203,19 +209,14 @@ textarea::placeholder { color:#9fb4d1 !important; }
203
  textarea:hover { border-color:#6b8db6 !important; }
204
  textarea:focus { border-color:#60a5fa !important; outline:none !important; }
205
 
206
- /* Labels on sliders etc. */
207
- label { color:#ffffff !important; font-weight: 800 !important; }
208
-
209
- /* Advanced settings scroll internally if crowded */
210
- #adv-inner { height:100%; min-height:0; overflow:auto; padding-right:6px; }
211
- input[type=range] { accent-color:#3b82f6; }
212
 
213
- /* Actions + Examples */
214
- #btn-row { display:flex; gap:10px; flex-wrap:wrap; }
215
- .section-title { color:#eaf2ff; font-weight:900; margin:8px 0 6px; letter-spacing:.2px; }
216
- #examples-wrap { height: calc(100% - 48px); min-height:0; overflow:auto; border:1px dashed #2b3d55; border-radius:8px; padding:6px; }
217
 
218
- /* Modal */
219
  #modal { position: fixed; inset: 0; z-index: 9999; background: rgba(2,6,23,.88); display: none; align-items: center; justify-content: center; padding: 12px; }
220
  #modal[style*="display: block"] { display: flex !important; }
221
  .modal-card { width:min(1280px,96vw); height:min(92vh,900px); background:#0f172a; border:1px solid #335070; border-radius:14px; box-shadow:0 18px 40px rgba(2,6,23,.6); display:flex; flex-direction:column; gap:8px; padding:10px; }
@@ -223,90 +224,85 @@ input[type=range] { accent-color:#3b82f6; }
223
  #fs_box textarea { height: calc(100% - 52px) !important; }
224
  .modal-actions { display:flex; gap:8px; justify-content:flex-end; }
225
 
226
- /* Header/footer text */
227
- #app-title { font-size: 20px; font-weight: 900; color:#ffffff; margin:0; }
228
- .subtle { color:#9fb4d1; margin:0; font-size:12.5px; }
229
- .footer { color:#9fb4d1; font-size:12px; }
230
  """
231
 
232
  # -------------------- Build UI --------------------
233
  with gr.Blocks(theme=THEME, css=CUSTOM_CSS, title="EN→HI / EN→TE Translator") as demo:
234
  modal_state = gr.State(value="") # '', 'src', 'hi', 'te'
235
 
236
- with gr.Column(elem_id="app-shell"):
237
- # Header
238
- with gr.Group(elem_id="header"):
239
- gr.Markdown('<div id="app-title">English → Hindi & Telugu Translator</div>')
240
- gr.Markdown('<div class="subtle">IndicTrans2 pipeline · law-ai/InLegalTrans-En2Indic-1B</div>')
241
-
242
- # Main area
243
- with gr.Row(elem_id="main-grid"):
244
- # LEFT COLUMN
245
- with gr.Column(elem_id="left-col"):
246
- # Input pane
247
- with gr.Column(elem_classes=["panel"], elem_id="left-input"):
248
- with gr.Row(elem_classes=["panel-h"]):
249
- gr.Markdown("English Text")
250
- src_max = gr.Button("⤢", elem_classes=["max-btn"])
251
- with gr.Group(elem_classes=["panel-b", "textwrap"]):
252
- src = gr.Textbox(placeholder="Type English here…", lines=10, autofocus=True, show_label=False)
253
-
254
- # Advanced Settings pane (left)
255
- with gr.Column(elem_classes=["panel"], elem_id="left-adv"):
256
- gr.Markdown('<div class="panel-h">Advanced Settings</div>')
257
- with gr.Group(elem_classes=["panel-b"], elem_id="adv-inner"):
258
- num_beams = gr.Slider(1, 8, value=4, step=1, label="Beam search: num_beams")
259
- max_new = gr.Slider(16, 512, value=128, step=8, label="Max new tokens")
260
- temperature = gr.Slider(0.0, 1.5, value=0.0, step=0.05, label="Temperature (0 = deterministic)")
261
- top_p = gr.Slider(0.0, 1.0, value=1.0, step=0.01, label="Top-p")
262
- top_k = gr.Slider(0, 100, value=50, step=1, label="Top-k")
263
-
264
- # Actions + Examples
265
- with gr.Column(elem_classes=["panel"], elem_id="left-actions"):
266
- gr.Markdown('<div class="panel-h">Actions & Examples</div>')
267
- with gr.Group(elem_classes=["panel-b"]):
268
- with gr.Row(elem_id="btn-row"):
269
- translate_btn = gr.Button("Translate", variant="primary")
270
- clear_btn = gr.Button("Clear", variant="secondary")
271
- gr.Markdown('<div class="section-title">Examples</div>')
272
- with gr.Group(elem_id="examples-wrap"):
273
- gr.Examples(
274
- examples=[
275
- ["The Constitution guarantees fundamental rights to every citizen of India."],
276
- ["Maintenance proceedings shall commence within thirty days from the date of application."],
277
- ["The agreement shall remain in force unless terminated by mutual consent in writing."],
278
- ],
279
- inputs=[src],
280
- )
281
-
282
- # RIGHT COLUMN
283
- with gr.Column(elem_id="right-col"):
 
 
 
284
  # Hindi
285
- with gr.Column(elem_classes=["panel"], elem_id="right-hi"):
286
- with gr.Row(elem_classes=["panel-h"]):
287
- gr.Markdown("Hindi (hin_Deva)")
288
- hi_max = gr.Button("⤢", elem_classes=["max-btn"])
289
- with gr.Group(elem_classes=["panel-b", "textwrap"]):
290
- hi_out = gr.Textbox(lines=10, show_copy_button=True, show_label=False)
291
- # Telugu
292
- with gr.Column(elem_classes=["panel"], elem_id="right-te"):
293
- with gr.Row(elem_classes=["panel-h"]):
294
- gr.Markdown("Telugu (tel_Telu)")
295
- te_max = gr.Button("⤢", elem_classes=["max-btn"])
296
- with gr.Group(elem_classes=["panel-b", "textwrap"]):
297
- te_out = gr.Textbox(lines=10, show_copy_button=True, show_label=False)
298
-
299
- # Footer
300
- with gr.Group(elem_id="footer"):
301
- gr.Markdown('<div class="footer">Model: law-ai/InLegalTrans-En2Indic-1B · Tokenizer: ai4bharat/indictrans2-en-indic-1B</div>')
302
-
303
- # ---- Fullscreen modal (hidden by default) ----
304
- with gr.Group(visible=False, elem_id="modal") as modal:
305
- modal_title = gr.Markdown('<div class="modal-title">Fullscreen</div>')
306
- fs_text = gr.Textbox(lines=22, elem_id="fs_box")
307
- with gr.Row(elem_classes=["modal-actions"]):
308
- fs_save = gr.Button("Save & Close", variant="primary")
309
- fs_close = gr.Button("Close", variant="secondary")
310
 
311
  # ---- Wiring ----
312
  translate_btn.click(
@@ -317,17 +313,20 @@ with gr.Blocks(theme=THEME, css=CUSTOM_CSS, title="EN→HI / EN→TE Translator"
317
  )
318
  clear_btn.click(lambda: ("", "", ""), outputs=[src, hi_out, te_out])
319
 
320
- # Open modal (click only)
321
- def open_src(s):
322
- return gr.update(visible=True), "src", '<div class="modal-title">English Text (Fullscreen)</div>', s
323
- def open_hi(h):
324
- return gr.update(visible=True), "hi", '<div class="modal-title">Hindi (Fullscreen)</div>', h
325
- def open_te(t):
326
- return gr.update(visible=True), "te", '<div class="modal-title">Telugu (Fullscreen)</div>', t
 
 
 
327
 
328
- src_max.click(open_src, inputs=[src], outputs=[modal, modal_state, modal_title, fs_text])
329
- hi_max.click(open_hi, inputs=[hi_out], outputs=[modal, modal_state, modal_title, fs_text])
330
- te_max.click(open_te, inputs=[te_out], outputs=[modal, modal_state, modal_title, fs_text])
331
 
332
  # Save & Close / Close
333
  def save_and_close(which, fs_val, s, h, t):
@@ -335,8 +334,10 @@ with gr.Blocks(theme=THEME, css=CUSTOM_CSS, title="EN→HI / EN→TE Translator"
335
  elif which == "hi": h = fs_val
336
  elif which == "te": t = fs_val
337
  return (gr.update(visible=False), "", s, h, t)
 
338
  def close_no_save():
339
  return (gr.update(visible=False), "")
 
340
  fs_save.click(save_and_close, inputs=[modal_state, fs_text, src, hi_out, te_out],
341
  outputs=[modal, modal_state, src, hi_out, te_out])
342
  fs_close.click(close_no_save, outputs=[modal, modal_state])
 
55
  actual_vocab = None
56
  if actual_vocab is not None:
57
  md.config.vocab_size = actual_vocab
58
+ try:
59
+ md.generation_config.vocab_size = actual_vocab
60
+ except Exception:
61
+ pass
62
  else:
63
  fallback_size = getattr(tok, "vocab_size", None)
64
  if fallback_size is None:
65
+ try:
66
+ fallback_size = len(tok)
67
+ except Exception:
68
+ fallback_size = 64000
69
  md.config.vocab_size = fallback_size
70
+ try:
71
+ md.generation_config.vocab_size = fallback_size
72
+ except Exception:
73
+ pass
74
  if not hasattr(md.config, "get_text_config") or not callable(getattr(md.config, "get_text_config", None)):
75
  def _get_text_config(self): return self
76
  md.config.get_text_config = types.MethodType(_get_text_config, md.config)
77
+
78
  _ensure_vocab_consistency(model, tokenizer)
79
 
80
  # Disable KV cache to avoid custom decoder's past_key_values shape bug
81
  for obj in (model.config, model.generation_config):
82
+ try:
83
+ setattr(obj, "use_cache", False)
84
+ except Exception:
85
+ pass
86
 
87
  ip = IndicProcessor(inference=True)
88
 
 
99
  return_tensors="pt",
100
  return_attention_mask=True,
101
  ).to(device)
102
+
103
  do_sample = (temperature is not None) and (float(temperature) > 0)
104
+
105
  outputs = model.generate(
106
  **enc,
107
  max_new_tokens=int(max_new_tokens),
 
114
  early_stopping=False,
115
  pad_token_id=model.generation_config.pad_token_id,
116
  )
117
+
118
+ decoded = tokenizer.batch_decode(
119
+ outputs, skip_special_tokens=True, clean_up_tokenization_spaces=True
120
+ )
121
  final = ip.postprocess_batch(decoded, lang=tgt_code)
122
  return final[0].strip()
123
 
 
154
  button_primary_text_color="#ffffff",
155
  )
156
 
157
+ # -------------------- LAYOUT CSS (simple, robust) --------------------
158
  CUSTOM_CSS = """
159
+ /* allow natural page scroll (prevents clipping on short screens) */
160
+ html, body { background:#0b1220; }
161
+ .gradio-container { max-width: 1280px; margin: 0 auto; padding: 16px; }
162
+
163
+ /* header */
164
+ #hdr { background:#162434; border:1px solid #223144; border-radius:12px; padding:12px; text-align:center; }
165
+ #title { color:#ffffff; font-weight:900; font-size:20px; margin:0; letter-spacing:.2px; }
166
+ #subtitle { color:#9fb4d1; font-size:12.5px; margin:0; }
167
+
168
+ /* main two-column layout: left sidebar fixed width, right content flexible */
169
+ #page {
170
+ display:grid;
171
+ grid-template-columns: 320px 1fr;
172
+ gap: 16px;
173
+ margin-top: 14px;
174
+ }
175
+
176
+ /* right column layout: top= 2 columns (EN + HI), bottom = TE full width */
177
+ #right {
178
+ display:grid;
179
+ grid-template-rows: auto auto;
180
+ gap: 16px;
181
+ }
182
+ #topRow {
183
+ display:grid;
184
+ grid-template-columns: 1fr 1fr;
185
+ gap: 16px;
186
  }
187
 
188
+ /* panel shell */
189
+ .panel { border:1px solid #223144; border-radius:12px; background:#0f172a; overflow:hidden; }
190
  .panel-h {
191
  display:flex; align-items:center; justify-content:space-between;
192
+ padding:10px 12px; background:#0b1b2b; border-bottom:1px solid #243244;
193
+ color:#eaf2ff; font-weight:900; letter-spacing:.2px;
 
194
  }
195
+ .panel-b { padding:12px; }
 
 
 
 
 
 
 
 
 
 
196
 
197
+ /* maximize button */
198
+ .max { font-weight:900; padding:4px 10px; border-radius:10px; border:1px solid #3c5a86;
199
+ background:#122037; color:#ffffff; }
200
+ .max:hover { border-color:#60a5fa; }
201
 
202
+ /* inputs */
 
 
 
 
 
203
  textarea, textarea:focus {
204
  background:#0b1220 !important; color:#f9fbff !important;
205
  font-size: 17px !important; line-height:1.55 !important;
 
209
  textarea:hover { border-color:#6b8db6 !important; }
210
  textarea:focus { border-color:#60a5fa !important; outline:none !important; }
211
 
212
+ label { color:#ffffff !important; font-weight:800 !important; }
 
 
 
 
 
213
 
214
+ /* sidebar sections */
215
+ .section { margin-bottom: 16px; }
216
+ .section h3 { margin:0 0 8px 0; color:#eaf2ff; font-size:14px; font-weight:900; letter-spacing:.2px; }
217
+ #examples { border:1px dashed #2b3d55; border-radius:8px; padding:8px; }
218
 
219
+ /* modal */
220
  #modal { position: fixed; inset: 0; z-index: 9999; background: rgba(2,6,23,.88); display: none; align-items: center; justify-content: center; padding: 12px; }
221
  #modal[style*="display: block"] { display: flex !important; }
222
  .modal-card { width:min(1280px,96vw); height:min(92vh,900px); background:#0f172a; border:1px solid #335070; border-radius:14px; box-shadow:0 18px 40px rgba(2,6,23,.6); display:flex; flex-direction:column; gap:8px; padding:10px; }
 
224
  #fs_box textarea { height: calc(100% - 52px) !important; }
225
  .modal-actions { display:flex; gap:8px; justify-content:flex-end; }
226
 
227
+ /* footer */
228
+ #ftr { text-align:center; color:#9fb4d1; font-size:12px; margin-top:14px; }
 
 
229
  """
230
 
231
  # -------------------- Build UI --------------------
232
  with gr.Blocks(theme=THEME, css=CUSTOM_CSS, title="EN→HI / EN→TE Translator") as demo:
233
  modal_state = gr.State(value="") # '', 'src', 'hi', 'te'
234
 
235
+ # Header
236
+ with gr.Column(elem_id="hdr"):
237
+ gr.Markdown('<p id="title">English → Hindi & Telugu Translator</p>')
238
+ gr.Markdown('<p id="subtitle">IndicTrans2 pipeline · law-ai/InLegalTrans-En2Indic-1B</p>')
239
+
240
+ # Page grid
241
+ with gr.Row(elem_id="page"):
242
+ # LEFT SIDEBAR (fixed width)
243
+ with gr.Column():
244
+ # Advanced Settings
245
+ with gr.Column(elem_classes=["panel","section"]):
246
+ gr.Markdown('<div class="panel-h">Advanced Settings <span></span></div>')
247
+ with gr.Column(elem_classes=["panel-b"]):
248
+ num_beams = gr.Slider(1, 8, value=4, step=1, label="Beam search: num_beams")
249
+ max_new = gr.Slider(16, 512, value=128, step=8, label="Max new tokens")
250
+ temperature = gr.Slider(0.0, 1.5, value=0.0, step=0.05, label="Temperature (0 = deterministic)")
251
+ top_p = gr.Slider(0.0, 1.0, value=1.0, step=0.01, label="Top-p")
252
+ top_k = gr.Slider(0, 100, value=50, step=1, label="Top-k")
253
+
254
+ # Actions
255
+ with gr.Column(elem_classes=["panel","section"]):
256
+ gr.Markdown('<div class="panel-h">Actions <span></span></div>')
257
+ with gr.Column(elem_classes=["panel-b"]):
258
+ with gr.Row():
259
+ translate_btn = gr.Button("Translate", variant="primary")
260
+ clear_btn = gr.Button("Clear", variant="secondary")
261
+
262
+ # Examples
263
+ with gr.Column(elem_classes=["panel","section"]):
264
+ gr.Markdown('<div class="panel-h">Examples <span></span></div>')
265
+ with gr.Column(elem_classes=["panel-b"], elem_id="examples"):
266
+ gr.Examples(
267
+ examples=[
268
+ ["The Constitution guarantees fundamental rights to every citizen of India."],
269
+ ["Maintenance proceedings shall commence within thirty days from the date of application."],
270
+ ["The agreement shall remain in force unless terminated by mutual consent in writing."],
271
+ ],
272
+ inputs=[],
273
+ outputs=[],
274
+ fn=None # just displays; click-to-fill is not needed here
275
+ )
276
+
277
+ # RIGHT CONTENT (flex)
278
+ with gr.Column(elem_id="right"):
279
+ # Top row: English + Hindi
280
+ with gr.Row(elem_id="topRow"):
281
+ # English
282
+ with gr.Column(elem_classes=["panel"]):
283
+ gr.Markdown('<div class="panel-h">English Text <button class="max">⤢</button></div>')
284
+ src = gr.Textbox(lines=12, placeholder="Type English here…", show_label=False)
285
+
286
  # Hindi
287
+ with gr.Column(elem_classes=["panel"]):
288
+ gr.Markdown('<div class="panel-h">Hindi (hin_Deva) <button class="max" id="max_hi">⤢</button></div>')
289
+ hi_out = gr.Textbox(lines=12, show_copy_button=True, show_label=False)
290
+
291
+ # Bottom row: Telugu full width
292
+ with gr.Column(elem_classes=["panel"]):
293
+ gr.Markdown('<div class="panel-h">Telugu (tel_Telu) <button class="max" id="max_te">⤢</button></div>')
294
+ te_out = gr.Textbox(lines=12, show_copy_button=True, show_label=False)
295
+
296
+ # Footer
297
+ gr.Markdown('<div id="ftr">Model: law-ai/InLegalTrans-En2Indic-1B · Tokenizer: ai4bharat/indictrans2-en-indic-1B</div>')
298
+
299
+ # ---- Fullscreen modal (hidden by default) ----
300
+ with gr.Group(visible=False, elem_id="modal") as modal:
301
+ modal_title = gr.Markdown('<div class="modal-title">Fullscreen</div>')
302
+ fs_text = gr.Textbox(lines=22, elem_id="fs_box")
303
+ with gr.Row(elem_classes=["modal-actions"]):
304
+ fs_save = gr.Button("Save & Close", variant="primary")
305
+ fs_close = gr.Button("Close", variant="secondary")
 
 
 
 
 
 
306
 
307
  # ---- Wiring ----
308
  translate_btn.click(
 
313
  )
314
  clear_btn.click(lambda: ("", "", ""), outputs=[src, hi_out, te_out])
315
 
316
+ # Maximize (open modal) — use separate triggers so we know which pane
317
+ def open_src(s): return gr.update(visible=True), "src", '<div class="modal-title">English (Fullscreen)</div>', s
318
+ def open_hi(h): return gr.update(visible=True), "hi", '<div class="modal-title">Hindi (Fullscreen)</div>', h
319
+ def open_te(t): return gr.update(visible=True), "te", '<div class="modal-title">Telugu (Fullscreen)</div>', t
320
+
321
+ # Add tiny buttons for English too (we can’t attach JS to raw HTML buttons from Markdown),
322
+ # so we add hidden Gradio buttons to trigger the modal.
323
+ btn_src = gr.Button(visible=False)
324
+ btn_hi = gr.Button(visible=False)
325
+ btn_te = gr.Button(visible=False)
326
 
327
+ btn_src.click(open_src, inputs=[src], outputs=[modal, modal_state, modal_title, fs_text])
328
+ btn_hi.click(open_hi, inputs=[hi_out], outputs=[modal, modal_state, modal_title, fs_text])
329
+ btn_te.click(open_te, inputs=[te_out], outputs=[modal, modal_state, modal_title, fs_text])
330
 
331
  # Save & Close / Close
332
  def save_and_close(which, fs_val, s, h, t):
 
334
  elif which == "hi": h = fs_val
335
  elif which == "te": t = fs_val
336
  return (gr.update(visible=False), "", s, h, t)
337
+
338
  def close_no_save():
339
  return (gr.update(visible=False), "")
340
+
341
  fs_save.click(save_and_close, inputs=[modal_state, fs_text, src, hi_out, te_out],
342
  outputs=[modal, modal_state, src, hi_out, te_out])
343
  fs_close.click(close_no_save, outputs=[modal, modal_state])