ElMETRICO commited on
Commit
c82ddba
·
verified ·
1 Parent(s): 9afd17b

Rollback to clean ChatGPT-style UI

Browse files
Files changed (1) hide show
  1. app.py +207 -360
app.py CHANGED
@@ -260,360 +260,206 @@ def safe_search(query):
260
 
261
 
262
 
263
- custom_css = """
264
- :root {
265
- --bg: #0A0E17;
266
- --panel: #10151F;
267
- --panel-border: #1E2635;
268
- --panel-border-strong: #2A3549;
269
- --text-primary: #F8FAFC;
270
- --text-secondary: #94A3B8;
271
- --text-tertiary: #64748B;
272
- --accent-blue: #3B82F6;
273
- --accent-blue-hover: #2563EB;
274
- --accent-green: #22C55E;
275
- --input-bg: #0B1120;
276
- --radius-lg: 16px;
277
- --radius-md: 12px;
278
- --radius-sm: 8px;
279
- --space-1: 8px;
280
- --space-2: 12px;
281
- --space-3: 16px;
282
- --space-4: 24px;
283
- --space-5: 32px;
284
- }
285
-
286
- * {
287
- box-sizing: border-box;
288
- }
289
 
 
290
  .gradio-container {
291
- background: var(--bg) !important;
292
- color: var(--text-primary) !important;
293
- font-family: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif !important;
294
- }
295
-
296
- /* Gradio wraps every component in its own bordered/background "block".
297
- We already draw our own card + input borders, so strip the default
298
- wrapper chrome everywhere to avoid double-boxing labels/inputs. */
299
- .gradio-container .block,
300
- .gradio-container .form {
301
- background: transparent !important;
302
- border: none !important;
303
- box-shadow: none !important;
304
- padding: 0 !important;
305
  }
306
 
307
  #shell {
308
- max-width: 880px;
309
  margin: 0 auto;
310
- padding: var(--space-5) var(--space-3) 48px var(--space-3);
311
  }
312
 
313
- /* ---------- HERO ---------- */
 
 
 
 
 
 
 
314
 
315
- .hero {
316
- background: linear-gradient(180deg, #131A29 0%, #10151F 100%);
317
- border: 1px solid var(--panel-border);
318
- border-radius: var(--radius-lg);
319
- padding: 32px 34px;
320
- margin-bottom: var(--space-4);
321
- box-shadow: 0 20px 50px rgba(0,0,0,0.30);
322
  }
323
 
324
- .badge {
325
- display: inline-flex;
326
- align-items: center;
327
- gap: 6px;
328
- padding: 6px 12px;
329
  border-radius: 999px;
330
- background: rgba(59,130,246,0.10);
331
- border: 1px solid rgba(59,130,246,0.35);
332
- color: #93C5FD;
333
- font-weight: 650;
334
  font-size: 12px;
335
- letter-spacing: 0.02em;
336
- text-transform: uppercase;
337
- margin-bottom: var(--space-3);
 
 
 
338
  }
339
 
340
  .title {
341
- font-size: 36px;
342
- line-height: 1.15;
343
- font-weight: 800;
344
- letter-spacing: -0.03em;
345
- margin: 0 0 12px 0;
346
- color: #FFFFFF;
347
  }
348
 
349
  .subtitle {
350
- color: var(--text-secondary);
 
 
351
  font-size: 15px;
352
  line-height: 1.65;
353
- max-width: 640px;
354
- margin: 0 0 var(--space-4) 0;
355
- }
356
-
357
- .info-strip {
358
- display: flex;
359
- flex-wrap: wrap;
360
- gap: 8px;
361
- }
362
-
363
- .info-pill {
364
- padding: 8px 13px;
365
- border-radius: var(--radius-sm);
366
- background: var(--input-bg);
367
- border: 1px solid var(--panel-border);
368
- color: #CBD5E1;
369
- font-size: 12.5px;
370
- font-weight: 600;
371
  }
372
 
373
- /* ---------- CARDS ---------- */
374
-
375
- .main-card {
376
- background: var(--panel) !important;
377
- border: 1px solid var(--panel-border) !important;
378
- border-radius: var(--radius-lg) !important;
379
- padding: 28px !important;
380
- box-shadow: 0 16px 40px rgba(0,0,0,0.24);
381
- margin-bottom: var(--space-3) !important;
382
- position: relative;
383
- overflow: hidden;
384
  }
385
 
386
- .main-card::before {
387
- content: "";
388
- position: absolute;
389
- top: 0;
390
- left: 0;
391
- right: 0;
392
- height: 3px;
 
393
  }
394
 
395
- .search-card::before {
396
- background: var(--accent-blue);
397
- }
398
-
399
- .output-card::before {
400
- background: var(--accent-green);
401
- }
402
-
403
- .section-header {
404
- display: flex;
405
- align-items: baseline;
406
- justify-content: space-between;
407
  margin-bottom: 4px;
408
  }
409
 
410
- .section-title {
411
- color: var(--text-primary);
412
- font-size: 19px;
413
- font-weight: 750;
414
- letter-spacing: -0.01em;
 
 
415
  }
416
 
417
- .section-subtitle {
418
- color: var(--text-secondary);
419
- font-size: 13.5px;
420
- line-height: 1.6;
421
- margin-bottom: var(--space-3);
422
- }
423
-
424
- /* ---------- FORM CONTROLS ---------- */
425
-
426
- label span {
427
- color: var(--text-secondary) !important;
428
- font-size: 12.5px !important;
429
- font-weight: 650 !important;
430
- text-transform: uppercase;
431
- letter-spacing: 0.03em;
432
- display: inline-block;
433
- margin-bottom: 6px;
434
- }
435
-
436
- textarea, input[type="text"] {
437
- background: var(--input-bg) !important;
438
  color: #FFFFFF !important;
439
- border: 1px solid var(--panel-border-strong) !important;
440
- border-radius: var(--radius-md) !important;
441
- font-size: 15px !important;
442
- padding: 12px 14px !important;
443
- transition: border-color 0.15s ease, box-shadow 0.15s ease;
444
  }
445
 
446
- textarea:focus, input[type="text"]:focus {
447
- border-color: var(--accent-blue) !important;
448
- box-shadow: 0 0 0 3px rgba(59,130,246,0.16) !important;
449
- outline: none !important;
450
  }
451
 
452
- textarea::placeholder, input::placeholder {
453
- color: var(--text-tertiary) !important;
 
 
 
 
 
454
  }
455
 
456
- .input-wide {
457
- margin: var(--space-1) 0 var(--space-3) 0;
458
  }
459
 
460
- /* ---------- BUTTONS ---------- */
461
-
462
- .button-row {
463
- display: flex;
464
- gap: 10px;
465
- margin-bottom: var(--space-1);
466
- }
467
-
468
- button.search-button {
469
- background: var(--accent-blue) !important;
470
- border: 1px solid var(--accent-blue) !important;
471
- border-radius: var(--radius-md) !important;
472
- color: #FFFFFF !important;
473
  font-weight: 700 !important;
474
- font-size: 14.5px !important;
475
- min-height: 46px !important;
476
- flex: 2;
477
- transition: background 0.15s ease, transform 0.05s ease;
478
- }
479
-
480
- button.search-button:hover {
481
- background: var(--accent-blue-hover) !important;
482
  }
483
 
484
- button.search-button:active {
485
- transform: translateY(1px);
486
- }
487
-
488
- button.clear-button {
489
  background: transparent !important;
490
- border: 1px solid var(--panel-border-strong) !important;
491
- border-radius: var(--radius-md) !important;
492
- color: var(--text-secondary) !important;
493
- font-weight: 650 !important;
494
- font-size: 14.5px !important;
495
- min-height: 46px !important;
496
- flex: 1;
497
- transition: border-color 0.15s ease, color 0.15s ease;
498
- }
499
-
500
- button.clear-button:hover {
501
- border-color: var(--text-secondary) !important;
502
- color: #FFFFFF !important;
503
- }
504
-
505
- /* ---------- EXAMPLES ---------- */
506
-
507
- #examples-block {
508
- margin-top: var(--space-3);
509
- padding-top: var(--space-3);
510
- border-top: 1px solid var(--panel-border);
511
  }
512
 
513
- #examples-block .label-wrap span {
514
- color: var(--text-tertiary) !important;
515
- font-size: 12px !important;
516
- text-transform: uppercase;
517
- letter-spacing: 0.03em;
 
 
 
518
  }
519
 
520
- #examples-block button {
521
- background: var(--input-bg) !important;
522
- border: 1px solid var(--panel-border) !important;
523
- border-radius: 999px !important;
524
- color: #CBD5E1 !important;
525
- font-size: 13px !important;
526
- font-weight: 550 !important;
527
- padding: 6px 14px !important;
528
  }
529
 
530
- #examples-block button:hover {
531
- border-color: var(--accent-blue) !important;
532
- color: #FFFFFF !important;
533
  }
534
 
535
- /* ---------- STATUS + OUTPUT ---------- */
536
-
537
- .status-panel textarea {
538
- background: #070C16 !important;
539
- border: 1px solid var(--panel-border) !important;
540
- color: #E2E8F0 !important;
541
- font-weight: 650 !important;
542
- font-size: 13.5px !important;
543
- min-height: 40px !important;
544
- padding: 10px 14px !important;
545
  }
546
 
547
- .result-note {
548
- background: var(--input-bg);
549
- border: 1px solid var(--panel-border);
550
- border-radius: var(--radius-md);
551
- padding: 14px 16px;
552
- color: var(--text-secondary);
553
- font-size: 13.5px;
554
  line-height: 1.6;
 
555
  }
556
 
557
- /* The table has 10 columns — it must scroll horizontally rather than
558
- shrink, or headers wrap into unreadable fragments.
559
- NOTE: we target .results-table (our own elem_classes) rather than
560
- Gradio's internal implementation classes, which vary by version and
561
- silently fail to match. */
562
- .results-table {
563
- overflow-x: auto !important;
564
- border-radius: var(--radius-md) !important;
565
- border: 1px solid var(--panel-border) !important;
566
  }
567
 
568
- .results-table table {
569
- min-width: 1150px !important;
570
- width: max-content !important;
571
- border-collapse: collapse !important;
572
- table-layout: auto !important;
573
  }
574
 
575
- .results-table th {
576
- background: #0D1420 !important;
577
- color: var(--text-secondary) !important;
578
- font-size: 11.5px !important;
579
- text-transform: uppercase;
580
- letter-spacing: 0.03em;
581
- font-weight: 700 !important;
582
- white-space: nowrap !important;
583
- padding: 10px 14px !important;
584
- min-width: 90px;
585
  }
586
 
587
- .results-table td {
588
- color: #E2E8F0 !important;
589
- padding: 10px 14px !important;
590
- max-width: 220px;
591
  }
592
 
593
- /* ---------- FOOTER ---------- */
594
-
595
- .footer {
596
- text-align: center;
597
- color: var(--text-tertiary);
598
- font-size: 12.5px;
599
- line-height: 1.7;
600
- margin-top: var(--space-4);
601
- padding-top: var(--space-3);
602
- border-top: 1px solid var(--panel-border);
603
- }
604
 
605
- @media (max-width: 640px) {
606
  .title {
607
- font-size: 28px;
608
  }
609
- .main-card {
610
- padding: 20px !important;
611
- }
612
- .hero {
613
- padding: 24px 22px;
614
- }
615
- .button-row {
616
- flex-direction: column;
617
  }
618
  }
619
  """
@@ -635,62 +481,89 @@ EMPTY_RESULTS = pd.DataFrame(
635
  )
636
 
637
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
638
  with gr.Blocks(
639
  css=custom_css,
640
  theme=gr.themes.Base(
641
- primary_hue="blue",
642
  secondary_hue="slate",
643
- neutral_hue="slate"
644
- ),
645
- title="CrossTalk AI"
646
  ) as demo:
647
 
648
  with gr.Column(elem_id="shell"):
649
  gr.HTML(
650
  """
 
 
 
 
 
651
  <div class="hero">
652
- <div class="badge">Hybrid retrieval system</div>
653
- <h1 class="title">CrossTalk AI</h1>
654
- <p class="subtitle">
655
- A retrieval-based language identification and translation support system for
656
- low-resource ethnic languages of Bangladesh — combining verified dictionary lookup
657
- with fine-tuned E5 semantic retrieval and FAISS vector search.
658
- </p>
659
- <div class="info-strip">
660
- <div class="info-pill">12 language groups</div>
661
- <div class="info-pill">70K+ indexed entries</div>
662
- <div class="info-pill">Fine-tuned E5 retriever</div>
663
- <div class="info-pill">FAISS vector search</div>
664
- <div class="info-pill">Confidence-aware output</div>
665
  </div>
666
  </div>
667
- """
668
- )
669
 
670
- # SEARCH SECTION
671
- with gr.Column(elem_classes=["main-card", "search-card"]):
672
- gr.HTML(
673
- """
674
- <div class="section-header">
675
- <div class="section-title">Search</div>
676
  </div>
677
- <div class="section-subtitle">
678
- Enter a source word. Exact and base-form matches are treated as verified
679
- dictionary outputs; everything else is a semantic suggestion.
680
  </div>
681
- """
682
- )
 
 
 
 
 
683
 
 
684
  query = gr.Textbox(
685
  label="Source word",
686
- placeholder="e.g. kəkhyáŋ, Hula, Aina, aam, bajaoo",
687
  lines=1,
688
- elem_classes=["input-wide"]
689
  )
690
 
691
- with gr.Row(elem_classes=["button-row"]):
692
- submit_btn = gr.Button("Search", elem_classes=["search-button"])
693
  clear_btn = gr.Button("Clear", elem_classes=["clear-button"])
 
694
 
695
  gr.Examples(
696
  examples=[
@@ -702,78 +575,52 @@ with gr.Blocks(
702
  ["unknown tribal word"]
703
  ],
704
  inputs=query,
705
- label="Try an example",
706
  elem_id="examples-block"
707
  )
708
 
709
- # OUTPUT SECTION
710
- with gr.Column(elem_classes=["main-card", "output-card"]):
711
- gr.HTML(
712
- """
713
- <div class="section-header">
714
- <div class="section-title">Results</div>
715
- </div>
716
- <div class="section-subtitle">
717
- Verified dictionary matches appear first. Semantic fallback results are shown
718
- only when no verified match exists.
719
- </div>
720
  """
721
- )
722
-
723
- status = gr.Textbox(
724
- label="Status",
725
- lines=1,
726
- elem_classes=["status-panel"],
727
- value="Ready."
728
- )
729
-
730
- summary = gr.HTML(
731
- value="""
732
- <div class="result-note">
733
- Enter a source word above and click Search — results will appear here.
734
  </div>
735
  """
736
  )
737
 
738
  results = gr.Dataframe(
739
- label="Results table",
740
  value=EMPTY_RESULTS,
741
  interactive=False,
742
  wrap=True,
743
- max_height=460,
744
- elem_classes=["results-table"]
745
  )
746
 
747
  gr.HTML(
748
  """
749
  <div class="footer">
750
- Exact / base-form matches are verified dictionary outputs.
751
- Fine-tuned semantic fallback results are candidate suggestions only, not confirmed translations.
752
  </div>
753
  """
754
  )
755
 
756
  submit_btn.click(
757
- fn=safe_search,
758
  inputs=query,
759
- outputs=[status, summary, results]
760
  )
761
 
762
  query.submit(
763
- fn=safe_search,
764
  inputs=query,
765
- outputs=[status, summary, results]
766
  )
767
 
768
  clear_btn.click(
769
- fn=lambda: (
770
- "",
771
- "Ready.",
772
- '<div class="result-note">Enter a source word above and click Search — results will appear here.</div>',
773
- EMPTY_RESULTS
774
- ),
775
  inputs=None,
776
- outputs=[query, status, summary, results]
777
  )
778
 
779
 
 
260
 
261
 
262
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
 
264
+ custom_css = """
265
  .gradio-container {
266
+ background: #212121 !important;
267
+ color: #ECECEC !important;
268
+ font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
 
 
 
 
 
 
 
 
 
 
 
269
  }
270
 
271
  #shell {
272
+ max-width: 980px;
273
  margin: 0 auto;
274
+ padding: 34px 18px 42px 18px;
275
  }
276
 
277
+ .topbar {
278
+ display: flex;
279
+ justify-content: space-between;
280
+ align-items: center;
281
+ margin-bottom: 26px;
282
+ color: #B4B4B4;
283
+ font-size: 14px;
284
+ }
285
 
286
+ .brand {
287
+ font-weight: 700;
288
+ color: #ECECEC;
 
 
 
 
289
  }
290
 
291
+ .model-badge {
292
+ background: #2F2F2F;
293
+ border: 1px solid #3A3A3A;
294
+ color: #CFCFCF;
295
+ padding: 7px 11px;
296
  border-radius: 999px;
 
 
 
 
297
  font-size: 12px;
298
+ }
299
+
300
+ .hero {
301
+ text-align: center;
302
+ padding: 22px 10px 20px 10px;
303
+ margin-bottom: 18px;
304
  }
305
 
306
  .title {
307
+ font-size: 38px;
308
+ line-height: 1.12;
309
+ font-weight: 750;
310
+ letter-spacing: -0.035em;
311
+ margin: 0;
312
+ color: #F5F5F5;
313
  }
314
 
315
  .subtitle {
316
+ max-width: 760px;
317
+ margin: 14px auto 0 auto;
318
+ color: #B4B4B4;
319
  font-size: 15px;
320
  line-height: 1.65;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
321
  }
322
 
323
+ .capabilities {
324
+ display: grid;
325
+ grid-template-columns: repeat(3, 1fr);
326
+ gap: 12px;
327
+ margin: 26px 0 22px 0;
 
 
 
 
 
 
328
  }
329
 
330
+ .cap-card {
331
+ background: #2A2A2A;
332
+ border: 1px solid #3A3A3A;
333
+ border-radius: 16px;
334
+ padding: 14px 15px;
335
+ color: #D7D7D7;
336
+ font-size: 13px;
337
+ line-height: 1.45;
338
  }
339
 
340
+ .cap-card strong {
341
+ display: block;
342
+ color: #FFFFFF;
343
+ font-size: 14px;
 
 
 
 
 
 
 
 
344
  margin-bottom: 4px;
345
  }
346
 
347
+ .search-panel {
348
+ background: #2F2F2F !important;
349
+ border: 1px solid #454545 !important;
350
+ border-radius: 24px !important;
351
+ padding: 14px 16px 16px 16px !important;
352
+ box-shadow: 0 14px 38px rgba(0,0,0,0.20);
353
+ margin-bottom: 18px !important;
354
  }
355
 
356
+ textarea, input {
357
+ background: #2F2F2F !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
358
  color: #FFFFFF !important;
359
+ border: 1px solid #4A4A4A !important;
360
+ border-radius: 18px !important;
361
+ font-size: 16px !important;
 
 
362
  }
363
 
364
+ textarea:focus, input:focus {
365
+ border-color: #6B7280 !important;
366
+ box-shadow: 0 0 0 2px rgba(255,255,255,0.08) !important;
 
367
  }
368
 
369
+ button.send-button {
370
+ background: #ECECEC !important;
371
+ color: #111111 !important;
372
+ border: 1px solid #ECECEC !important;
373
+ border-radius: 14px !important;
374
+ font-weight: 800 !important;
375
+ min-height: 44px !important;
376
  }
377
 
378
+ button.send-button:hover {
379
+ background: #FFFFFF !important;
380
  }
381
 
382
+ button.clear-button {
383
+ background: #262626 !important;
384
+ color: #D1D5DB !important;
385
+ border: 1px solid #414141 !important;
386
+ border-radius: 14px !important;
 
 
 
 
 
 
 
 
387
  font-weight: 700 !important;
388
+ min-height: 44px !important;
 
 
 
 
 
 
 
389
  }
390
 
391
+ .output-panel {
 
 
 
 
392
  background: transparent !important;
393
+ border: none !important;
394
+ padding: 0 !important;
395
+ margin-top: 8px !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
396
  }
397
 
398
+ .answer-card {
399
+ background: #2A2A2A;
400
+ border: 1px solid #3D3D3D;
401
+ border-radius: 18px;
402
+ padding: 18px 20px;
403
+ margin-bottom: 14px;
404
+ color: #ECECEC;
405
+ line-height: 1.65;
406
  }
407
 
408
+ .answer-title {
409
+ font-weight: 800;
410
+ color: #FFFFFF;
411
+ margin-bottom: 8px;
 
 
 
 
412
  }
413
 
414
+ .answer-note {
415
+ color: #C7C7C7;
416
+ font-size: 14px;
417
  }
418
 
419
+ .dataframe {
420
+ border-radius: 16px !important;
421
+ overflow: hidden !important;
422
+ border: 1px solid #3D3D3D !important;
 
 
 
 
 
 
423
  }
424
 
425
+ .footer {
426
+ text-align: center;
427
+ color: #9CA3AF;
428
+ font-size: 12.5px;
 
 
 
429
  line-height: 1.6;
430
+ margin-top: 24px;
431
  }
432
 
433
+ #examples-block {
434
+ margin-top: 10px;
 
 
 
 
 
 
 
435
  }
436
 
437
+ #examples-block button {
438
+ border-radius: 999px !important;
439
+ background: #2A2A2A !important;
440
+ border: 1px solid #3D3D3D !important;
441
+ color: #D1D5DB !important;
442
  }
443
 
444
+ .block {
445
+ background: transparent !important;
 
 
 
 
 
 
 
 
446
  }
447
 
448
+ label {
449
+ color: #CFCFCF !important;
 
 
450
  }
451
 
452
+ @media (max-width: 900px) {
453
+ #shell {
454
+ padding: 24px 14px 34px 14px;
455
+ }
 
 
 
 
 
 
 
456
 
 
457
  .title {
458
+ font-size: 30px;
459
  }
460
+
461
+ .capabilities {
462
+ grid-template-columns: 1fr;
 
 
 
 
 
463
  }
464
  }
465
  """
 
481
  )
482
 
483
 
484
+ def clean_markdown_summary(x):
485
+ x = "" if x is None else str(x)
486
+ x = x.replace('<div class="result-note">', "")
487
+ x = x.replace("</div>", "")
488
+ return x.strip()
489
+
490
+
491
+ def chat_style_search(query):
492
+ status, summary, result = safe_search(query)
493
+ summary = clean_markdown_summary(summary)
494
+
495
+ answer_html = f"""
496
+ <div class="answer-card">
497
+ <div class="answer-title">{status}</div>
498
+ <div class="answer-note">{summary}</div>
499
+ </div>
500
+ """
501
+
502
+ return answer_html, result
503
+
504
+
505
+ def clear_interface():
506
+ return "", """
507
+ <div class="answer-card">
508
+ <div class="answer-title">Ready.</div>
509
+ <div class="answer-note">Enter a source word to retrieve verified dictionary matches or semantic candidates.</div>
510
+ </div>
511
+ """, EMPTY_RESULTS
512
+
513
+
514
  with gr.Blocks(
515
  css=custom_css,
516
  theme=gr.themes.Base(
517
+ primary_hue="slate",
518
  secondary_hue="slate",
519
+ neutral_hue="zinc"
520
+ )
 
521
  ) as demo:
522
 
523
  with gr.Column(elem_id="shell"):
524
  gr.HTML(
525
  """
526
+ <div class="topbar">
527
+ <div class="brand">CrossTalk AI</div>
528
+ <div class="model-badge">Full trained retrieval system</div>
529
+ </div>
530
+
531
  <div class="hero">
532
+ <h1 class="title">What ethnic word would you like to search?</h1>
533
+ <div class="subtitle">
534
+ CrossTalk AI identifies and retrieves meanings from low-resource ethnic language dictionaries using verified lexical matching,
535
+ fine-tuned E5 semantic retrieval, FAISS vector search, and confidence-aware output control.
 
 
 
 
 
 
 
 
 
536
  </div>
537
  </div>
 
 
538
 
539
+ <div class="capabilities">
540
+ <div class="cap-card">
541
+ <strong>Verified lookup</strong>
542
+ Exact and base-form dictionary matches are returned first.
 
 
543
  </div>
544
+ <div class="cap-card">
545
+ <strong>Semantic fallback</strong>
546
+ Fine-tuned E5 suggests candidates when verified matches are unavailable.
547
  </div>
548
+ <div class="cap-card">
549
+ <strong>Safe output</strong>
550
+ Low-confidence semantic results are not claimed as confirmed translations.
551
+ </div>
552
+ </div>
553
+ """
554
+ )
555
 
556
+ with gr.Column(elem_classes=["search-panel"]):
557
  query = gr.Textbox(
558
  label="Source word",
559
+ placeholder="Message CrossTalk AI...",
560
  lines=1,
561
+ show_label=False
562
  )
563
 
564
+ with gr.Row():
 
565
  clear_btn = gr.Button("Clear", elem_classes=["clear-button"])
566
+ submit_btn = gr.Button("Search", elem_classes=["send-button"])
567
 
568
  gr.Examples(
569
  examples=[
 
575
  ["unknown tribal word"]
576
  ],
577
  inputs=query,
578
+ label="Try examples",
579
  elem_id="examples-block"
580
  )
581
 
582
+ with gr.Column(elem_classes=["output-panel"]):
583
+ answer = gr.HTML(
 
 
 
 
 
 
 
 
 
584
  """
585
+ <div class="answer-card">
586
+ <div class="answer-title">Ready.</div>
587
+ <div class="answer-note">Enter a source word to retrieve verified dictionary matches or semantic candidates.</div>
 
 
 
 
 
 
 
 
 
 
588
  </div>
589
  """
590
  )
591
 
592
  results = gr.Dataframe(
593
+ label="Retrieved results",
594
  value=EMPTY_RESULTS,
595
  interactive=False,
596
  wrap=True,
597
+ max_height=470
 
598
  )
599
 
600
  gr.HTML(
601
  """
602
  <div class="footer">
603
+ Exact/base-form matches are verified dictionary outputs. Fine-tuned semantic fallback results are candidate suggestions only, not confirmed translations.
 
604
  </div>
605
  """
606
  )
607
 
608
  submit_btn.click(
609
+ fn=chat_style_search,
610
  inputs=query,
611
+ outputs=[answer, results]
612
  )
613
 
614
  query.submit(
615
+ fn=chat_style_search,
616
  inputs=query,
617
+ outputs=[answer, results]
618
  )
619
 
620
  clear_btn.click(
621
+ fn=clear_interface,
 
 
 
 
 
622
  inputs=None,
623
+ outputs=[query, answer, results]
624
  )
625
 
626